From 9923befe712b0d174f6fad0f87c999b96bd80ebb Mon Sep 17 00:00:00 2001 From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com> Date: Thu, 3 Apr 2025 18:12:38 +0300 Subject: [PATCH 01/17] safeStringify & %c support --- src/addons/consoleCatcher.ts | 102 +++++++++++++++++++++++++++++------ 1 file changed, 86 insertions(+), 16 deletions(-) diff --git a/src/addons/consoleCatcher.ts b/src/addons/consoleCatcher.ts index fc8fa13..244a747 100644 --- a/src/addons/consoleCatcher.ts +++ b/src/addons/consoleCatcher.ts @@ -2,7 +2,7 @@ * @file Module for intercepting console logs with stack trace capture */ -import type { ConsoleLogEvent } from '@hawk.so/types'; +import type { ConsoleLogEvent } from "@hawk.so/types"; const createConsoleCatcher = (): { initConsoleCatcher: () => void; @@ -13,6 +13,66 @@ const createConsoleCatcher = (): { const consoleOutput: ConsoleLogEvent[] = []; let isInitialized = false; + /** + * Safely stringifies objects handling circular references + */ + const safeStringify = (obj: unknown): string => { + const seen = new WeakSet(); + return JSON.stringify(obj, (key, value) => { + if (typeof value === "object" && value !== null) { + if (seen.has(value)) { + return "[Circular]"; + } + seen.add(value); + } + return value; + }); + }; + + /** + * Formats console arguments handling %c directives + */ + const formatConsoleArgs = ( + args: unknown[] + ): { message: string; styles: string[] } => { + if (args.length === 0) return { message: "", styles: [] }; + + const firstArg = args[0]; + if (typeof firstArg !== "string" || !firstArg.includes("%c")) { + return { + message: args + .map((arg) => (typeof arg === "string" ? arg : safeStringify(arg))) + .join(" "), + styles: [], + }; + } + + // Handle %c formatting + const message = args[0] as string; + const styles: string[] = []; + + // Extract styles from arguments + let styleIndex = 0; + for (let i = 1; i < args.length; i++) { + const arg = args[i]; + if (typeof arg === "string" && message.indexOf("%c", styleIndex) !== -1) { + styles.push(arg); + styleIndex = message.indexOf("%c", styleIndex) + 2; + } + } + + // Add remaining arguments that aren't styles + const remainingArgs = args + .slice(styles.length + 1) + .map((arg) => (typeof arg === "string" ? arg : safeStringify(arg))) + .join(" "); + + return { + message: message + (remainingArgs ? " " + remainingArgs : ""), + styles, + }; + }; + const addToConsoleOutput = (logEvent: ConsoleLogEvent): void => { if (consoleOutput.length >= MAX_LOGS) { consoleOutput.shift(); @@ -25,24 +85,24 @@ const createConsoleCatcher = (): { ): ConsoleLogEvent => { if (event instanceof ErrorEvent) { return { - method: 'error', + method: "error", timestamp: new Date(), - type: event.error?.name || 'Error', + type: event.error?.name || "Error", message: event.error?.message || event.message, - stack: event.error?.stack || '', + stack: event.error?.stack || "", fileLine: event.filename ? `${event.filename}:${event.lineno}:${event.colno}` - : '', + : "", }; } return { - method: 'error', + method: "error", timestamp: new Date(), - type: 'UnhandledRejection', + type: "UnhandledRejection", message: event.reason?.message || String(event.reason), - stack: event.reason?.stack || '', - fileLine: '', + stack: event.reason?.stack || "", + fileLine: "", }; }; @@ -53,25 +113,34 @@ const createConsoleCatcher = (): { } isInitialized = true; - const consoleMethods: string[] = ['log', 'warn', 'error', 'info', 'debug']; + const consoleMethods: string[] = [ + "log", + "warn", + "error", + "info", + "debug", + ]; consoleMethods.forEach((method) => { - if (typeof window.console[method] !== 'function') { + if (typeof window.console[method] !== "function") { return; } const oldFunction = window.console[method].bind(window.console); window.console[method] = function (...args: unknown[]): void { - const stack = new Error().stack?.split('\n').slice(2).join('\n') || ''; + const stack = + new Error().stack?.split("\n").slice(2).join("\n") || ""; + const { message, styles } = formatConsoleArgs(args); const logEvent: ConsoleLogEvent = { method, timestamp: new Date(), type: method, - message: args.map((arg) => typeof arg === 'string' ? arg : JSON.stringify(arg)).join(' '), + message, stack, - fileLine: stack.split('\n')[0]?.trim(), + fileLine: stack.split("\n")[0]?.trim(), + styles, }; addToConsoleOutput(logEvent); @@ -87,10 +156,11 @@ const createConsoleCatcher = (): { }, getConsoleLogStack(): ConsoleLogEvent[] { - return [ ...consoleOutput ]; + return [...consoleOutput]; }, }; }; const consoleCatcher = createConsoleCatcher(); -export const { initConsoleCatcher, getConsoleLogStack, addErrorEvent } = consoleCatcher; +export const { initConsoleCatcher, getConsoleLogStack, addErrorEvent } = + consoleCatcher; From 60b16302d76dd5e7e0c3395d4b2d0bb072f1cfeb Mon Sep 17 00:00:00 2001 From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com> Date: Thu, 3 Apr 2025 18:34:50 +0300 Subject: [PATCH 02/17] fix lint --- src/addons/consoleCatcher.ts | 59 +++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/src/addons/consoleCatcher.ts b/src/addons/consoleCatcher.ts index 244a747..024039b 100644 --- a/src/addons/consoleCatcher.ts +++ b/src/addons/consoleCatcher.ts @@ -2,7 +2,7 @@ * @file Module for intercepting console logs with stack trace capture */ -import type { ConsoleLogEvent } from "@hawk.so/types"; +import type { ConsoleLogEvent } from '@hawk.so/types'; const createConsoleCatcher = (): { initConsoleCatcher: () => void; @@ -18,13 +18,15 @@ const createConsoleCatcher = (): { */ const safeStringify = (obj: unknown): string => { const seen = new WeakSet(); + return JSON.stringify(obj, (key, value) => { - if (typeof value === "object" && value !== null) { + if (typeof value === 'object' && value !== null) { if (seen.has(value)) { - return "[Circular]"; + return '[Circular]'; } seen.add(value); } + return value; }); }; @@ -38,11 +40,12 @@ const createConsoleCatcher = (): { if (args.length === 0) return { message: "", styles: [] }; const firstArg = args[0]; - if (typeof firstArg !== "string" || !firstArg.includes("%c")) { + if (typeof firstArg !== 'string' || !firstArg.includes('%c')) { + return { message: args - .map((arg) => (typeof arg === "string" ? arg : safeStringify(arg))) - .join(" "), + .map((arg) => (typeof arg === 'string' ? arg : safeStringify(arg))) + .join(' '), styles: [], }; } @@ -55,20 +58,20 @@ const createConsoleCatcher = (): { let styleIndex = 0; for (let i = 1; i < args.length; i++) { const arg = args[i]; - if (typeof arg === "string" && message.indexOf("%c", styleIndex) !== -1) { + if (typeof arg === 'string' && message.indexOf('%c', styleIndex) !== -1) { styles.push(arg); - styleIndex = message.indexOf("%c", styleIndex) + 2; + styleIndex = message.indexOf('%c', styleIndex) + 2; } } // Add remaining arguments that aren't styles const remainingArgs = args .slice(styles.length + 1) - .map((arg) => (typeof arg === "string" ? arg : safeStringify(arg))) - .join(" "); + .map((arg) => (typeof arg === 'string' ? arg : safeStringify(arg))) + .join(' '); return { - message: message + (remainingArgs ? " " + remainingArgs : ""), + message: message + (remainingArgs ? ' ' + remainingArgs : ''), styles, }; }; @@ -84,25 +87,26 @@ const createConsoleCatcher = (): { event: ErrorEvent | PromiseRejectionEvent ): ConsoleLogEvent => { if (event instanceof ErrorEvent) { + return { - method: "error", + method: 'error', timestamp: new Date(), - type: event.error?.name || "Error", + type: event.error?.name || 'Error', message: event.error?.message || event.message, - stack: event.error?.stack || "", + stack: event.error?.stack || '', fileLine: event.filename ? `${event.filename}:${event.lineno}:${event.colno}` - : "", + : '', }; } return { - method: "error", + method: 'error', timestamp: new Date(), - type: "UnhandledRejection", + type: 'UnhandledRejection', message: event.reason?.message || String(event.reason), - stack: event.reason?.stack || "", - fileLine: "", + stack: event.reason?.stack || '', + fileLine: '', }; }; @@ -114,15 +118,15 @@ const createConsoleCatcher = (): { isInitialized = true; const consoleMethods: string[] = [ - "log", - "warn", - "error", - "info", - "debug", + 'log', + 'warn', + 'error', + 'info', + 'debug', ]; consoleMethods.forEach((method) => { - if (typeof window.console[method] !== "function") { + if (typeof window.console[method] !== 'function') { return; } @@ -130,7 +134,7 @@ const createConsoleCatcher = (): { window.console[method] = function (...args: unknown[]): void { const stack = - new Error().stack?.split("\n").slice(2).join("\n") || ""; + new Error().stack?.split('\n').slice(2).join('\n') || ''; const { message, styles } = formatConsoleArgs(args); const logEvent: ConsoleLogEvent = { @@ -139,7 +143,7 @@ const createConsoleCatcher = (): { type: method, message, stack, - fileLine: stack.split("\n")[0]?.trim(), + fileLine: stack.split('\n')[0]?.trim(), styles, }; @@ -156,6 +160,7 @@ const createConsoleCatcher = (): { }, getConsoleLogStack(): ConsoleLogEvent[] { + return [...consoleOutput]; }, }; From 3bcb512cb4409b22c3f038db8bf4cb62aedb4ea0 Mon Sep 17 00:00:00 2001 From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com> Date: Thu, 3 Apr 2025 18:36:54 +0300 Subject: [PATCH 03/17] fix lint --- src/addons/consoleCatcher.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/addons/consoleCatcher.ts b/src/addons/consoleCatcher.ts index 024039b..a4cde4c 100644 --- a/src/addons/consoleCatcher.ts +++ b/src/addons/consoleCatcher.ts @@ -37,7 +37,7 @@ const createConsoleCatcher = (): { const formatConsoleArgs = ( args: unknown[] ): { message: string; styles: string[] } => { - if (args.length === 0) return { message: "", styles: [] }; + if (args.length === 0) return { message: '', styles: [] }; const firstArg = args[0]; if (typeof firstArg !== 'string' || !firstArg.includes('%c')) { @@ -56,8 +56,10 @@ const createConsoleCatcher = (): { // Extract styles from arguments let styleIndex = 0; + for (let i = 1; i < args.length; i++) { const arg = args[i]; + if (typeof arg === 'string' && message.indexOf('%c', styleIndex) !== -1) { styles.push(arg); styleIndex = message.indexOf('%c', styleIndex) + 2; From a4fb9d80bfd22b7f380d52e9b3475b7262d1aeb6 Mon Sep 17 00:00:00 2001 From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com> Date: Thu, 3 Apr 2025 18:41:16 +0300 Subject: [PATCH 04/17] fix lint --- src/addons/consoleCatcher.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/addons/consoleCatcher.ts b/src/addons/consoleCatcher.ts index a4cde4c..77dd45b 100644 --- a/src/addons/consoleCatcher.ts +++ b/src/addons/consoleCatcher.ts @@ -20,7 +20,9 @@ const createConsoleCatcher = (): { const seen = new WeakSet(); return JSON.stringify(obj, (key, value) => { + if (typeof value === 'object' && value !== null) { + if (seen.has(value)) { return '[Circular]'; } @@ -88,6 +90,7 @@ const createConsoleCatcher = (): { const createConsoleEventFromError = ( event: ErrorEvent | PromiseRejectionEvent ): ConsoleLogEvent => { + if (event instanceof ErrorEvent) { return { @@ -136,7 +139,8 @@ const createConsoleCatcher = (): { window.console[method] = function (...args: unknown[]): void { const stack = - new Error().stack?.split('\n').slice(2).join('\n') || ''; + new Error().stack?.split('\n').slice(2) + .join('\n') || ''; const { message, styles } = formatConsoleArgs(args); const logEvent: ConsoleLogEvent = { @@ -163,7 +167,7 @@ const createConsoleCatcher = (): { getConsoleLogStack(): ConsoleLogEvent[] { - return [...consoleOutput]; + return [ ...consoleOutput ]; }, }; }; From a2abb83a9214766272a08fbe73afbb73c02c16fe Mon Sep 17 00:00:00 2001 From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com> Date: Tue, 8 Apr 2025 15:30:02 +0300 Subject: [PATCH 05/17] safe-stringify package --- package.json | 1 + src/addons/consoleCatcher.ts | 81 +++++++++++++----------------------- yarn.lock | 5 +++ 3 files changed, 34 insertions(+), 53 deletions(-) diff --git a/package.json b/package.json index 894332a..66f74b1 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "dependencies": { "@hawk.so/types": "^0.1.20", "error-stack-parser": "^2.1.4", + "safe-stringify": "^1.1.1", "vite-plugin-dts": "^4.2.4" } } diff --git a/src/addons/consoleCatcher.ts b/src/addons/consoleCatcher.ts index 77dd45b..b283e43 100644 --- a/src/addons/consoleCatcher.ts +++ b/src/addons/consoleCatcher.ts @@ -1,8 +1,8 @@ /** * @file Module for intercepting console logs with stack trace capture */ - -import type { ConsoleLogEvent } from '@hawk.so/types'; +import safeStringify from "safe-stringify"; +import type { ConsoleLogEvent } from "@hawk.so/types"; const createConsoleCatcher = (): { initConsoleCatcher: () => void; @@ -13,41 +13,20 @@ const createConsoleCatcher = (): { const consoleOutput: ConsoleLogEvent[] = []; let isInitialized = false; - /** - * Safely stringifies objects handling circular references - */ - const safeStringify = (obj: unknown): string => { - const seen = new WeakSet(); - - return JSON.stringify(obj, (key, value) => { - - if (typeof value === 'object' && value !== null) { - - if (seen.has(value)) { - return '[Circular]'; - } - seen.add(value); - } - - return value; - }); - }; - /** * Formats console arguments handling %c directives */ const formatConsoleArgs = ( args: unknown[] ): { message: string; styles: string[] } => { - if (args.length === 0) return { message: '', styles: [] }; + if (args.length === 0) return { message: "", styles: [] }; const firstArg = args[0]; - if (typeof firstArg !== 'string' || !firstArg.includes('%c')) { - + if (typeof firstArg !== "string" || !firstArg.includes("%c")) { return { message: args - .map((arg) => (typeof arg === 'string' ? arg : safeStringify(arg))) - .join(' '), + .map((arg) => (typeof arg === "string" ? arg : safeStringify(arg))) + .join(" "), styles: [], }; } @@ -62,20 +41,20 @@ const createConsoleCatcher = (): { for (let i = 1; i < args.length; i++) { const arg = args[i]; - if (typeof arg === 'string' && message.indexOf('%c', styleIndex) !== -1) { + if (typeof arg === "string" && message.indexOf("%c", styleIndex) !== -1) { styles.push(arg); - styleIndex = message.indexOf('%c', styleIndex) + 2; + styleIndex = message.indexOf("%c", styleIndex) + 2; } } // Add remaining arguments that aren't styles const remainingArgs = args .slice(styles.length + 1) - .map((arg) => (typeof arg === 'string' ? arg : safeStringify(arg))) - .join(' '); + .map((arg) => (typeof arg === "string" ? arg : safeStringify(arg))) + .join(" "); return { - message: message + (remainingArgs ? ' ' + remainingArgs : ''), + message: message + (remainingArgs ? " " + remainingArgs : ""), styles, }; }; @@ -90,28 +69,26 @@ const createConsoleCatcher = (): { const createConsoleEventFromError = ( event: ErrorEvent | PromiseRejectionEvent ): ConsoleLogEvent => { - if (event instanceof ErrorEvent) { - return { - method: 'error', + method: "error", timestamp: new Date(), - type: event.error?.name || 'Error', + type: event.error?.name || "Error", message: event.error?.message || event.message, - stack: event.error?.stack || '', + stack: event.error?.stack || "", fileLine: event.filename ? `${event.filename}:${event.lineno}:${event.colno}` - : '', + : "", }; } return { - method: 'error', + method: "error", timestamp: new Date(), - type: 'UnhandledRejection', + type: "UnhandledRejection", message: event.reason?.message || String(event.reason), - stack: event.reason?.stack || '', - fileLine: '', + stack: event.reason?.stack || "", + fileLine: "", }; }; @@ -123,15 +100,15 @@ const createConsoleCatcher = (): { isInitialized = true; const consoleMethods: string[] = [ - 'log', - 'warn', - 'error', - 'info', - 'debug', + "log", + "warn", + "error", + "info", + "debug", ]; consoleMethods.forEach((method) => { - if (typeof window.console[method] !== 'function') { + if (typeof window.console[method] !== "function") { return; } @@ -139,8 +116,7 @@ const createConsoleCatcher = (): { window.console[method] = function (...args: unknown[]): void { const stack = - new Error().stack?.split('\n').slice(2) - .join('\n') || ''; + new Error().stack?.split("\n").slice(2).join("\n") || ""; const { message, styles } = formatConsoleArgs(args); const logEvent: ConsoleLogEvent = { @@ -149,7 +125,7 @@ const createConsoleCatcher = (): { type: method, message, stack, - fileLine: stack.split('\n')[0]?.trim(), + fileLine: stack.split("\n")[0]?.trim(), styles, }; @@ -166,8 +142,7 @@ const createConsoleCatcher = (): { }, getConsoleLogStack(): ConsoleLogEvent[] { - - return [ ...consoleOutput ]; + return [...consoleOutput]; }, }; }; diff --git a/yarn.lock b/yarn.lock index a3a87c2..cf1e367 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2619,6 +2619,11 @@ safe-regex-test@^1.0.3: es-errors "^1.3.0" is-regex "^1.1.4" +safe-stringify@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/safe-stringify/-/safe-stringify-1.1.1.tgz#f4240f506d041f58374d6106e2a5850f6b1ce576" + integrity sha512-YSzQLuwp06fuvJD1h6+vVNFYZoXmDs5UUNPUbTvQK7Ap+L0qD4Vp+sN434C+pdS3prVVlUfQdNeiEIgxox/kUQ== + semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" From e21d320164c96149488f7af793dc169170efd2db Mon Sep 17 00:00:00 2001 From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com> Date: Tue, 8 Apr 2025 15:40:32 +0300 Subject: [PATCH 06/17] fix lint --- src/addons/consoleCatcher.ts | 54 ++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/addons/consoleCatcher.ts b/src/addons/consoleCatcher.ts index b283e43..d94b6f1 100644 --- a/src/addons/consoleCatcher.ts +++ b/src/addons/consoleCatcher.ts @@ -1,8 +1,8 @@ /** * @file Module for intercepting console logs with stack trace capture */ -import safeStringify from "safe-stringify"; -import type { ConsoleLogEvent } from "@hawk.so/types"; +import safeStringify from 'safe-stringify'; +import type { ConsoleLogEvent } from '@hawk.so/types'; const createConsoleCatcher = (): { initConsoleCatcher: () => void; @@ -19,14 +19,14 @@ const createConsoleCatcher = (): { const formatConsoleArgs = ( args: unknown[] ): { message: string; styles: string[] } => { - if (args.length === 0) return { message: "", styles: [] }; + if (args.length === 0) return { message: '', styles: [] }; const firstArg = args[0]; - if (typeof firstArg !== "string" || !firstArg.includes("%c")) { + if (typeof firstArg !== 'string' || !firstArg.includes('%c')) { return { message: args - .map((arg) => (typeof arg === "string" ? arg : safeStringify(arg))) - .join(" "), + .map((arg) => (typeof arg === 'string' ? arg : safeStringify(arg))) + .join(' '), styles: [], }; } @@ -41,20 +41,20 @@ const createConsoleCatcher = (): { for (let i = 1; i < args.length; i++) { const arg = args[i]; - if (typeof arg === "string" && message.indexOf("%c", styleIndex) !== -1) { + if (typeof arg === 'string' && message.indexOf('%c', styleIndex) !== -1) { styles.push(arg); - styleIndex = message.indexOf("%c", styleIndex) + 2; + styleIndex = message.indexOf('%c', styleIndex) + 2; } } // Add remaining arguments that aren't styles const remainingArgs = args .slice(styles.length + 1) - .map((arg) => (typeof arg === "string" ? arg : safeStringify(arg))) - .join(" "); + .map((arg) => (typeof arg === 'string' ? arg : safeStringify(arg))) + .join(' '); return { - message: message + (remainingArgs ? " " + remainingArgs : ""), + message: message + (remainingArgs ? ' ' + remainingArgs : ''), styles, }; }; @@ -71,24 +71,24 @@ const createConsoleCatcher = (): { ): ConsoleLogEvent => { if (event instanceof ErrorEvent) { return { - method: "error", + method: 'error', timestamp: new Date(), - type: event.error?.name || "Error", + type: event.error?.name || 'Error', message: event.error?.message || event.message, - stack: event.error?.stack || "", + stack: event.error?.stack || '', fileLine: event.filename ? `${event.filename}:${event.lineno}:${event.colno}` - : "", + : '', }; } return { - method: "error", + method: 'error', timestamp: new Date(), - type: "UnhandledRejection", + type: 'UnhandledRejection', message: event.reason?.message || String(event.reason), - stack: event.reason?.stack || "", - fileLine: "", + stack: event.reason?.stack || '', + fileLine: '', }; }; @@ -100,15 +100,15 @@ const createConsoleCatcher = (): { isInitialized = true; const consoleMethods: string[] = [ - "log", - "warn", - "error", - "info", - "debug", + 'log', + 'warn', + 'error', + 'info', + 'debug', ]; consoleMethods.forEach((method) => { - if (typeof window.console[method] !== "function") { + if (typeof window.console[method] !== 'function') { return; } @@ -116,7 +116,7 @@ const createConsoleCatcher = (): { window.console[method] = function (...args: unknown[]): void { const stack = - new Error().stack?.split("\n").slice(2).join("\n") || ""; + new Error().stack?.split('\n').slice(2).join('\n') || ''; const { message, styles } = formatConsoleArgs(args); const logEvent: ConsoleLogEvent = { @@ -125,7 +125,7 @@ const createConsoleCatcher = (): { type: method, message, stack, - fileLine: stack.split("\n")[0]?.trim(), + fileLine: stack.split('\n')[0]?.trim(), styles, }; From 1edfe1dec1e46e2874ee113ae9310675466cbcd3 Mon Sep 17 00:00:00 2001 From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com> Date: Tue, 8 Apr 2025 15:43:53 +0300 Subject: [PATCH 07/17] fix --- src/addons/consoleCatcher.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/addons/consoleCatcher.ts b/src/addons/consoleCatcher.ts index d94b6f1..10cdc1b 100644 --- a/src/addons/consoleCatcher.ts +++ b/src/addons/consoleCatcher.ts @@ -19,9 +19,11 @@ const createConsoleCatcher = (): { const formatConsoleArgs = ( args: unknown[] ): { message: string; styles: string[] } => { + if (args.length === 0) return { message: '', styles: [] }; const firstArg = args[0]; + if (typeof firstArg !== 'string' || !firstArg.includes('%c')) { return { message: args @@ -60,6 +62,7 @@ const createConsoleCatcher = (): { }; const addToConsoleOutput = (logEvent: ConsoleLogEvent): void => { + if (consoleOutput.length >= MAX_LOGS) { consoleOutput.shift(); } @@ -69,6 +72,7 @@ const createConsoleCatcher = (): { const createConsoleEventFromError = ( event: ErrorEvent | PromiseRejectionEvent ): ConsoleLogEvent => { + if (event instanceof ErrorEvent) { return { method: 'error', @@ -94,6 +98,7 @@ const createConsoleCatcher = (): { return { initConsoleCatcher(): void { + if (isInitialized) { return; } @@ -108,6 +113,7 @@ const createConsoleCatcher = (): { ]; consoleMethods.forEach((method) => { + if (typeof window.console[method] !== 'function') { return; } @@ -116,7 +122,8 @@ const createConsoleCatcher = (): { window.console[method] = function (...args: unknown[]): void { const stack = - new Error().stack?.split('\n').slice(2).join('\n') || ''; + new Error().stack?.split('\n').slice(2) + .join('\n') || ''; const { message, styles } = formatConsoleArgs(args); const logEvent: ConsoleLogEvent = { @@ -142,7 +149,7 @@ const createConsoleCatcher = (): { }, getConsoleLogStack(): ConsoleLogEvent[] { - return [...consoleOutput]; + return [ ...consoleOutput ]; }, }; }; From 6e4aca8fcaaa4f7e2f2bc4401907da184b0fbc84 Mon Sep 17 00:00:00 2001 From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com> Date: Tue, 8 Apr 2025 15:47:09 +0300 Subject: [PATCH 08/17] fix --- src/addons/consoleCatcher.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/addons/consoleCatcher.ts b/src/addons/consoleCatcher.ts index 10cdc1b..26ec209 100644 --- a/src/addons/consoleCatcher.ts +++ b/src/addons/consoleCatcher.ts @@ -25,6 +25,7 @@ const createConsoleCatcher = (): { const firstArg = args[0]; if (typeof firstArg !== 'string' || !firstArg.includes('%c')) { + return { message: args .map((arg) => (typeof arg === 'string' ? arg : safeStringify(arg))) @@ -74,6 +75,7 @@ const createConsoleCatcher = (): { ): ConsoleLogEvent => { if (event instanceof ErrorEvent) { + return { method: 'error', timestamp: new Date(), @@ -149,6 +151,7 @@ const createConsoleCatcher = (): { }, getConsoleLogStack(): ConsoleLogEvent[] { + return [ ...consoleOutput ]; }, }; From 6249c1f8e9c5f562dfb51247d136eeb3b477477d Mon Sep 17 00:00:00 2001 From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com> Date: Tue, 8 Apr 2025 15:50:39 +0300 Subject: [PATCH 09/17] fix --- src/addons/consoleCatcher.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/addons/consoleCatcher.ts b/src/addons/consoleCatcher.ts index 26ec209..786d49c 100644 --- a/src/addons/consoleCatcher.ts +++ b/src/addons/consoleCatcher.ts @@ -124,8 +124,7 @@ const createConsoleCatcher = (): { window.console[method] = function (...args: unknown[]): void { const stack = - new Error().stack?.split('\n').slice(2) - .join('\n') || ''; + new Error().stack?.split('\n').slice(2).join('\n') || ''; const { message, styles } = formatConsoleArgs(args); const logEvent: ConsoleLogEvent = { From 173096e13a073e98addd6a48fd8a98dd063e6852 Mon Sep 17 00:00:00 2001 From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com> Date: Tue, 8 Apr 2025 15:58:01 +0300 Subject: [PATCH 10/17] args --- src/addons/consoleCatcher.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/addons/consoleCatcher.ts b/src/addons/consoleCatcher.ts index 786d49c..cc6048c 100644 --- a/src/addons/consoleCatcher.ts +++ b/src/addons/consoleCatcher.ts @@ -53,7 +53,12 @@ const createConsoleCatcher = (): { // Add remaining arguments that aren't styles const remainingArgs = args .slice(styles.length + 1) - .map((arg) => (typeof arg === 'string' ? arg : safeStringify(arg))) + .map((arg) => { + if (typeof arg === 'string') return arg; + if (typeof arg === 'number' || typeof arg === 'boolean') + return String(arg); + return safeStringify(arg); + }) .join(' '); return { From 9d539b0fdb192dd573ee24ff3da21cd5ec4d5fa7 Mon Sep 17 00:00:00 2001 From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com> Date: Tue, 8 Apr 2025 16:13:30 +0300 Subject: [PATCH 11/17] fix --- src/addons/consoleCatcher.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/addons/consoleCatcher.ts b/src/addons/consoleCatcher.ts index cc6048c..83911a1 100644 --- a/src/addons/consoleCatcher.ts +++ b/src/addons/consoleCatcher.ts @@ -28,8 +28,13 @@ const createConsoleCatcher = (): { return { message: args - .map((arg) => (typeof arg === 'string' ? arg : safeStringify(arg))) - .join(' '), + .map((arg) => { + if (typeof arg === 'string') return arg; + if (typeof arg === 'number' || typeof arg === 'boolean') + return String(arg); + return safeStringify(arg); + }) + .join(''), styles: [], }; } From 47cd3d9faa4b01b716ca39f4025f4d73de9cccae Mon Sep 17 00:00:00 2001 From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com> Date: Tue, 8 Apr 2025 16:15:45 +0300 Subject: [PATCH 12/17] fix --- src/addons/consoleCatcher.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/addons/consoleCatcher.ts b/src/addons/consoleCatcher.ts index 83911a1..1b66a44 100644 --- a/src/addons/consoleCatcher.ts +++ b/src/addons/consoleCatcher.ts @@ -27,8 +27,7 @@ const createConsoleCatcher = (): { if (typeof firstArg !== 'string' || !firstArg.includes('%c')) { return { - message: args - .map((arg) => { + message: args.map((arg) => { if (typeof arg === 'string') return arg; if (typeof arg === 'number' || typeof arg === 'boolean') return String(arg); From 0bb8a8c1e4da87c7c0b137dbc373efd6c1c51bc5 Mon Sep 17 00:00:00 2001 From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com> Date: Tue, 8 Apr 2025 16:16:55 +0300 Subject: [PATCH 13/17] fix --- src/addons/consoleCatcher.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/addons/consoleCatcher.ts b/src/addons/consoleCatcher.ts index 1b66a44..02a5df9 100644 --- a/src/addons/consoleCatcher.ts +++ b/src/addons/consoleCatcher.ts @@ -32,8 +32,7 @@ const createConsoleCatcher = (): { if (typeof arg === 'number' || typeof arg === 'boolean') return String(arg); return safeStringify(arg); - }) - .join(''), + }).join(''), styles: [], }; } From 76730de43aae451a34177cb50b8da64fc0bed8e5 Mon Sep 17 00:00:00 2001 From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com> Date: Tue, 8 Apr 2025 16:55:57 +0300 Subject: [PATCH 14/17] lint --- src/addons/consoleCatcher.ts | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/addons/consoleCatcher.ts b/src/addons/consoleCatcher.ts index 02a5df9..899e7c1 100644 --- a/src/addons/consoleCatcher.ts +++ b/src/addons/consoleCatcher.ts @@ -15,22 +15,29 @@ const createConsoleCatcher = (): { /** * Formats console arguments handling %c directives + * + * @param args - Console arguments that may include %c style directives */ const formatConsoleArgs = ( args: unknown[] ): { message: string; styles: string[] } => { - - if (args.length === 0) return { message: '', styles: [] }; + if (args.length === 0) { + return { message: '', + styles: [] }; + } const firstArg = args[0]; if (typeof firstArg !== 'string' || !firstArg.includes('%c')) { - return { message: args.map((arg) => { - if (typeof arg === 'string') return arg; - if (typeof arg === 'number' || typeof arg === 'boolean') + if (typeof arg === 'string') { + return arg; + } + if (typeof arg === 'number' || typeof arg === 'boolean') { return String(arg); + } + return safeStringify(arg); }).join(''), styles: [], @@ -57,9 +64,13 @@ const createConsoleCatcher = (): { const remainingArgs = args .slice(styles.length + 1) .map((arg) => { - if (typeof arg === 'string') return arg; - if (typeof arg === 'number' || typeof arg === 'boolean') + if (typeof arg === 'string') { + return arg; + } + if (typeof arg === 'number' || typeof arg === 'boolean') { return String(arg); + } + return safeStringify(arg); }) .join(' '); @@ -71,7 +82,6 @@ const createConsoleCatcher = (): { }; const addToConsoleOutput = (logEvent: ConsoleLogEvent): void => { - if (consoleOutput.length >= MAX_LOGS) { consoleOutput.shift(); } @@ -81,9 +91,7 @@ const createConsoleCatcher = (): { const createConsoleEventFromError = ( event: ErrorEvent | PromiseRejectionEvent ): ConsoleLogEvent => { - if (event instanceof ErrorEvent) { - return { method: 'error', timestamp: new Date(), @@ -108,7 +116,6 @@ const createConsoleCatcher = (): { return { initConsoleCatcher(): void { - if (isInitialized) { return; } @@ -123,7 +130,6 @@ const createConsoleCatcher = (): { ]; consoleMethods.forEach((method) => { - if (typeof window.console[method] !== 'function') { return; } @@ -132,7 +138,8 @@ const createConsoleCatcher = (): { window.console[method] = function (...args: unknown[]): void { const stack = - new Error().stack?.split('\n').slice(2).join('\n') || ''; + new Error().stack?.split('\n').slice(2) + .join('\n') || ''; const { message, styles } = formatConsoleArgs(args); const logEvent: ConsoleLogEvent = { @@ -158,12 +165,12 @@ const createConsoleCatcher = (): { }, getConsoleLogStack(): ConsoleLogEvent[] { - return [ ...consoleOutput ]; }, }; }; const consoleCatcher = createConsoleCatcher(); + export const { initConsoleCatcher, getConsoleLogStack, addErrorEvent } = consoleCatcher; From a1244bc4d605d17291f9552d83a8abd10c024f9a Mon Sep 17 00:00:00 2001 From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com> Date: Tue, 8 Apr 2025 18:06:51 +0300 Subject: [PATCH 15/17] stringifyArg --- src/addons/consoleCatcher.ts | 38 +++++++++++++++++------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/src/addons/consoleCatcher.ts b/src/addons/consoleCatcher.ts index 899e7c1..9975a0b 100644 --- a/src/addons/consoleCatcher.ts +++ b/src/addons/consoleCatcher.ts @@ -13,6 +13,22 @@ const createConsoleCatcher = (): { const consoleOutput: ConsoleLogEvent[] = []; let isInitialized = false; + /** + * Converts any argument to its string representation + * + * @param arg - Console arguments + */ + const stringifyArg = (arg: unknown): string => { + if (typeof arg === 'string') { + return arg; + } + if (typeof arg === 'number' || typeof arg === 'boolean') { + return String(arg); + } + + return safeStringify(arg); + }; + /** * Formats console arguments handling %c directives * @@ -30,16 +46,7 @@ const createConsoleCatcher = (): { if (typeof firstArg !== 'string' || !firstArg.includes('%c')) { return { - message: args.map((arg) => { - if (typeof arg === 'string') { - return arg; - } - if (typeof arg === 'number' || typeof arg === 'boolean') { - return String(arg); - } - - return safeStringify(arg); - }).join(''), + message: args.map(stringifyArg).join(' '), styles: [], }; } @@ -63,16 +70,7 @@ const createConsoleCatcher = (): { // Add remaining arguments that aren't styles const remainingArgs = args .slice(styles.length + 1) - .map((arg) => { - if (typeof arg === 'string') { - return arg; - } - if (typeof arg === 'number' || typeof arg === 'boolean') { - return String(arg); - } - - return safeStringify(arg); - }) + .map(stringifyArg) .join(' '); return { From 991f49bd9357263aaf43548964ca9dd824e689b3 Mon Sep 17 00:00:00 2001 From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com> Date: Tue, 8 Apr 2025 19:20:57 +0300 Subject: [PATCH 16/17] named func --- src/addons/consoleCatcher.ts | 152 ++++++++++++++++++++--------------- 1 file changed, 88 insertions(+), 64 deletions(-) diff --git a/src/addons/consoleCatcher.ts b/src/addons/consoleCatcher.ts index 9975a0b..997a30c 100644 --- a/src/addons/consoleCatcher.ts +++ b/src/addons/consoleCatcher.ts @@ -4,11 +4,14 @@ import safeStringify from 'safe-stringify'; import type { ConsoleLogEvent } from '@hawk.so/types'; -const createConsoleCatcher = (): { +/** + * Creates a console interceptor that captures and formats console output + */ +function createConsoleCatcher(): { initConsoleCatcher: () => void; addErrorEvent: (event: ErrorEvent | PromiseRejectionEvent) => void; getConsoleLogStack: () => ConsoleLogEvent[]; -} => { + } { const MAX_LOGS = 20; const consoleOutput: ConsoleLogEvent[] = []; let isInitialized = false; @@ -16,9 +19,9 @@ const createConsoleCatcher = (): { /** * Converts any argument to its string representation * - * @param arg - Console arguments + * @param arg - Value to convert to string */ - const stringifyArg = (arg: unknown): string => { + function stringifyArg(arg: unknown): string { if (typeof arg === 'string') { return arg; } @@ -27,19 +30,22 @@ const createConsoleCatcher = (): { } return safeStringify(arg); - }; + } /** * Formats console arguments handling %c directives * - * @param args - Console arguments that may include %c style directives + * @param args - Console arguments that may include style directives */ - const formatConsoleArgs = ( - args: unknown[] - ): { message: string; styles: string[] } => { + function formatConsoleArgs(args: unknown[]): { + message: string; + styles: string[]; + } { if (args.length === 0) { - return { message: '', - styles: [] }; + return { + message: '', + styles: [], + }; } const firstArg = args[0]; @@ -77,18 +83,28 @@ const createConsoleCatcher = (): { message: message + (remainingArgs ? ' ' + remainingArgs : ''), styles, }; - }; + } - const addToConsoleOutput = (logEvent: ConsoleLogEvent): void => { + /** + * Adds a console log event to the output buffer + * + * @param logEvent - The console log event to be added to the output buffer + */ + function addToConsoleOutput(logEvent: ConsoleLogEvent): void { if (consoleOutput.length >= MAX_LOGS) { consoleOutput.shift(); } consoleOutput.push(logEvent); - }; + } - const createConsoleEventFromError = ( + /** + * Creates a console log event from an error or promise rejection + * + * @param event - The error event or promise rejection event to convert + */ + function createConsoleEventFromError( event: ErrorEvent | PromiseRejectionEvent - ): ConsoleLogEvent => { + ): ConsoleLogEvent { if (event instanceof ErrorEvent) { return { method: 'error', @@ -110,63 +126,71 @@ const createConsoleCatcher = (): { stack: event.reason?.stack || '', fileLine: '', }; - }; + } - return { - initConsoleCatcher(): void { - if (isInitialized) { + /** + * Initializes the console interceptor by overriding default console methods + */ + function initConsoleCatcher(): void { + if (isInitialized) { + return; + } + + isInitialized = true; + const consoleMethods: string[] = ['log', 'warn', 'error', 'info', 'debug']; + + consoleMethods.forEach(function overrideConsoleMethod(method) { + if (typeof window.console[method] !== 'function') { return; } - isInitialized = true; - const consoleMethods: string[] = [ - 'log', - 'warn', - 'error', - 'info', - 'debug', - ]; - - consoleMethods.forEach((method) => { - if (typeof window.console[method] !== 'function') { - return; - } - - const oldFunction = window.console[method].bind(window.console); - - window.console[method] = function (...args: unknown[]): void { - const stack = - new Error().stack?.split('\n').slice(2) - .join('\n') || ''; - const { message, styles } = formatConsoleArgs(args); - - const logEvent: ConsoleLogEvent = { - method, - timestamp: new Date(), - type: method, - message, - stack, - fileLine: stack.split('\n')[0]?.trim(), - styles, - }; - - addToConsoleOutput(logEvent); - oldFunction(...args); + const oldFunction = window.console[method].bind(window.console); + + window.console[method] = function (...args: unknown[]): void { + const stack = new Error().stack?.split('\n').slice(2) + .join('\n') || ''; + const { message, styles } = formatConsoleArgs(args); + + const logEvent: ConsoleLogEvent = { + method, + timestamp: new Date(), + type: method, + message, + stack, + fileLine: stack.split('\n')[0]?.trim(), + styles, }; - }); - }, - addErrorEvent(event: ErrorEvent | PromiseRejectionEvent): void { - const logEvent = createConsoleEventFromError(event); + addToConsoleOutput(logEvent); + oldFunction(...args); + }; + }); + } - addToConsoleOutput(logEvent); - }, + /** + * Handles error events by converting them to console log events + * + * @param event - The error or promise rejection event to handle + */ + function addErrorEvent(event: ErrorEvent | PromiseRejectionEvent): void { + const logEvent = createConsoleEventFromError(event); - getConsoleLogStack(): ConsoleLogEvent[] { - return [ ...consoleOutput ]; - }, + addToConsoleOutput(logEvent); + } + + /** + * Returns the current console output buffer + */ + function getConsoleLogStack(): ConsoleLogEvent[] { + return [ ...consoleOutput ]; + } + + return { + initConsoleCatcher, + addErrorEvent, + getConsoleLogStack, }; -}; +} const consoleCatcher = createConsoleCatcher(); From 8ba75e19c47d3b0e9d560e928aed0d7a8ac8bccb Mon Sep 17 00:00:00 2001 From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com> Date: Tue, 8 Apr 2025 19:33:20 +0300 Subject: [PATCH 17/17] patch version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 66f74b1..87e029b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@hawk.so/javascript", "type": "commonjs", - "version": "3.2.2", + "version": "3.2.3", "description": "JavaScript errors tracking for Hawk.so", "files": [ "dist"