visilog
Version:
Stream browser console logs to files for LLM debugging. Zero-config setup with simple imports. No MCP required - just tell your LLM to read the log files.
2 lines • 12 kB
JavaScript
(()=>{"use strict";var e={335:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.WebSocketLogger=void 0,t.WebSocketLogger=class{constructor(e={}){this.ws=null,this.sessionId=null,this.messageQueue=[],this.reconnectAttempts=0,this.reconnectTimer=null,this.isConnecting=!1,this.heartbeatInterval=null,this.logHandlers=[],this.errorHandlers=[],this.config={enableWebSocket:!0,enableConsole:!0,minLevel:0,websocketUrl:"ws://localhost:3001",maxRetries:5,retryInterval:2e3,autoConnect:!0,namespace:void 0},this.levels=[{name:"debug",priority:0,color:"#888888"},{name:"log",priority:1,color:"#000000"},{name:"info",priority:2,color:"#0066cc"},{name:"warn",priority:3,color:"#ff9900"},{name:"error",priority:4,color:"#cc0000"}],this.originalConsole={log:console.log.bind(console),info:console.info.bind(console),warn:console.warn.bind(console),error:console.error.bind(console),debug:console.debug.bind(console)},this.consoleOverridden=!1,this.config={...this.config,...e},this.config.autoConnect&&this.init()}init(){this.config.enableWebSocket&&this.connectWebSocket(),this.log("🔌 WebSocketLogger initialized",{config:this.sanitizeConfig(this.config),url:this.getUrl(),userAgent:this.getUserAgent()})}enableConsoleOverride(){this.consoleOverridden||(console.log=(...e)=>{this.handleLog("log",e)},console.info=(...e)=>{this.handleLog("info",e)},console.warn=(...e)=>{this.handleLog("warn",e)},console.error=(...e)=>{this.handleLog("error",e)},console.debug=(...e)=>{this.handleLog("debug",e)},this.consoleOverridden=!0)}disableConsoleOverride(){this.consoleOverridden&&(console.log=this.originalConsole.log,console.info=this.originalConsole.info,console.warn=this.originalConsole.warn,console.error=this.originalConsole.error,console.debug=this.originalConsole.debug,this.consoleOverridden=!1)}connectWebSocket(){if(!(this.isConnecting||this.ws&&this.ws.readyState===WebSocket.OPEN)){this.isConnecting=!0;try{this.ws=new WebSocket(this.config.websocketUrl),this.ws.onopen=()=>{this.isConnecting=!1,this.reconnectAttempts=0,this.originalConsole.log("🔌 Connected to logging server"),this.startHeartbeat(),this.processMessageQueue()},this.ws.onmessage=e=>{try{const t=JSON.parse(e.data);this.handleServerMessage(t)}catch(e){this.handleError(e,"Failed to parse server message")}},this.ws.onclose=()=>{this.isConnecting=!1,this.stopHeartbeat(),this.originalConsole.warn("🔌 Disconnected from logging server"),this.scheduleReconnect()},this.ws.onerror=e=>{this.isConnecting=!1,this.stopHeartbeat(),this.handleError(new Error("WebSocket connection error"),"WebSocket error"),this.scheduleReconnect()}}catch(e){this.isConnecting=!1,this.handleError(e,"Failed to connect to logging server"),this.scheduleReconnect()}}}handleServerMessage(e){switch(e.type){case"session-init":this.sessionId=e.sessionId||null,this.originalConsole.log(`📱 Session initialized: ${this.sessionId}`);break;case"ping":this.sendMessage({type:"pong"});break;case"error":this.handleError(new Error(e.message||"Server error"),"Server error")}}scheduleReconnect(){if(this.reconnectAttempts>=this.config.maxRetries)return this.originalConsole.warn(`🔌 Max reconnection attempts (${this.config.maxRetries}) reached. Switching to console-only mode.`),void(this.config.enableWebSocket=!1);this.reconnectTimer&&clearTimeout(this.reconnectTimer);const e=this.config.retryInterval*Math.pow(2,this.reconnectAttempts);this.reconnectTimer=setTimeout((()=>{this.reconnectAttempts++,this.originalConsole.log(`🔌 Reconnection attempt ${this.reconnectAttempts}/${this.config.maxRetries}`),this.connectWebSocket()}),e)}startHeartbeat(){this.heartbeatInterval=setInterval((()=>{this.ws&&this.ws.readyState===WebSocket.OPEN&&this.sendMessage({type:"ping"})}),3e4)}stopHeartbeat(){this.heartbeatInterval&&(clearInterval(this.heartbeatInterval),this.heartbeatInterval=null)}processMessageQueue(){for(;this.messageQueue.length>0;){const e=this.messageQueue.shift();e&&this.sendLogMessage(e)}}sendMessage(e){if(this.ws&&this.ws.readyState===WebSocket.OPEN)try{this.ws.send(JSON.stringify(e))}catch(e){this.handleError(e,"Failed to send message")}}sendLogMessage(e){if(this.config.enableWebSocket)if(this.ws&&this.ws.readyState===WebSocket.OPEN)try{this.sendMessage({type:"log",log:e})}catch(t){this.handleError(t,"Failed to send log message"),this.messageQueue.push(e)}else this.messageQueue.push(e),this.messageQueue.length>1e3&&this.messageQueue.shift()}createLogMessage(e,t,o){const n={level:e,message:t,timestamp:(new Date).toISOString(),sessionId:this.sessionId||"unknown",url:this.getUrl(),userAgent:this.getUserAgent(),data:o};return this.config.namespace&&(n.namespace=this.config.namespace),n}handleLog(e,t){const o=this.levels.find((t=>t.name===e));if(!o)return;if(o.priority<this.config.minLevel)return;const n=t.map((e=>{if("object"==typeof e)try{return JSON.stringify(e,null,2)}catch{return String(e)}return String(e)})).join(" "),s=t.filter((e=>"object"==typeof e&&null!==e)),i=s.length>0?s:void 0;this.config.enableConsole&&this.originalConsole[e](...t);const r=this.createLogMessage(e,n,i);this.logHandlers.forEach((e=>{try{e(r)}catch(e){this.handleError(e,"Log handler error")}})),this.sendLogMessage(r)}handleError(e,t){this.originalConsole.error(`❌ WebSocketLogger Error${t?` (${t})`:""}:`,e),this.errorHandlers.forEach((o=>{try{const n=t?{operation:t,component:"WebSocketLogger",timestamp:(new Date).toISOString()}:void 0;o(e,n)}catch(e){this.originalConsole.error("❌ Error handler failed:",e)}}))}getUrl(){return"undefined"!=typeof window&&window.location?window.location.href:"unknown"}getUserAgent(){return"undefined"!=typeof navigator&&navigator.userAgent?navigator.userAgent:"unknown"}sanitizeConfig(e){const{websocketUrl:t,...o}=e;return{...o,websocketUrl:"***"}}log(e,t){this.handleLog("log",[e,t].filter(Boolean))}info(e,t){this.handleLog("info",[e,t].filter(Boolean))}warn(e,t){this.handleLog("warn",[e,t].filter(Boolean))}error(e,t){this.handleLog("error",[e,t].filter(Boolean))}debug(e,t){this.handleLog("debug",[e,t].filter(Boolean))}setMinLevel(e){const t=this.levels.find((t=>t.name===e));t&&(this.config.minLevel=t.priority,this.log(`🔧 Log level set to: ${e}`))}enableWebSocketLogging(e){this.config.enableWebSocket=e,e&&!this.ws?this.connectWebSocket():!e&&this.ws&&this.disconnect(),this.log("🔧 WebSocket logging "+(e?"enabled":"disabled"))}enableConsoleLogging(e){this.config.enableConsole=e,this.log("🔧 Console logging "+(e?"enabled":"disabled"))}updateConfig(e){const t=this.config.websocketUrl;this.config={...this.config,...e},e.websocketUrl&&e.websocketUrl!==t&&this.ws&&(this.disconnect(),this.connectWebSocket())}onLog(e){return this.logHandlers.push(e),()=>{const t=this.logHandlers.indexOf(e);t>-1&&this.logHandlers.splice(t,1)}}onError(e){return this.errorHandlers.push(e),()=>{const t=this.errorHandlers.indexOf(e);t>-1&&this.errorHandlers.splice(t,1)}}getSessionId(){return this.sessionId}getQueueSize(){return this.messageQueue.length}getConnectionStatus(){if(!this.config.enableWebSocket)return"disabled";if(!this.ws)return"disconnected";switch(this.ws.readyState){case WebSocket.CONNECTING:return"connecting";case WebSocket.OPEN:return"connected";case WebSocket.CLOSING:return"closing";case WebSocket.CLOSED:return"closed";default:return"unknown"}}isConnected(){return this.ws?.readyState===WebSocket.OPEN}getConfig(){return{...this.config}}connect(){this.config.enableWebSocket||(this.config.enableWebSocket=!0),this.connectWebSocket()}disconnect(){this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.stopHeartbeat(),this.ws&&(this.ws.close(),this.ws=null),this.sessionId=null,this.reconnectAttempts=0}destroy(){this.disconnect(),this.disableConsoleOverride(),this.logHandlers=[],this.errorHandlers=[],this.messageQueue=[]}}},491:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.EnvironmentDetector=void 0;class o{static detect(){if(this.cachedInfo)return this.cachedInfo;const e={isDevelopment:this.isDevelopment(),isProduction:this.isProduction(),isTest:this.isTest(),isBrowser:this.isBrowser(),isNode:this.isNode(),buildTool:this.detectBuildTool(),framework:this.detectFramework()};return this.cachedInfo=e,e}static isDevelopment(){return("undefined"==typeof process||!process.env)&&"undefined"!=typeof window&&("localhost"===window.location.hostname||"127.0.0.1"===window.location.hostname||window.location.hostname.includes("dev")||""!==window.location.port)}static isProduction(){return!("undefined"==typeof process||!process.env)||"undefined"!=typeof window&&!this.isDevelopment()}static isTest(){return"undefined"!=typeof process&&process.env?!!process.env.JEST_WORKER_ID||!!process.env.VITEST||!!process.env.CYPRESS:"undefined"!=typeof window&&!!(window.__karma__||window.jasmine||window.mocha||window.QUnit)}static isBrowser(){return"undefined"!=typeof window&&"undefined"!=typeof document&&"undefined"!=typeof navigator}static isNode(){return"undefined"!=typeof process&&null!=process.versions&&null!=process.versions.node}static detectBuildTool(){if("undefined"!=typeof window&&window.__vite_plugin_react_preamble_installed__)return"vite";if("undefined"!=typeof process&&process.env.VITE)return"vite";if(void 0!==globalThis.__webpack_require__)return"webpack";if("undefined"!=typeof process&&process.env.WEBPACK)return"webpack";if("undefined"!=typeof process&&process.env.ROLLUP)return"rollup";if("undefined"!=typeof process&&process.env.ESBUILD)return"esbuild";if("undefined"!=typeof window){const e=window;if(e.__webpack_public_path__)return"webpack";if(e.__vite__)return"vite"}return"unknown"}static detectFramework(){if("undefined"==typeof window)return"unknown";const e=window;return e.React||e.__REACT_DEVTOOLS_GLOBAL_HOOK__||document.querySelector("[data-reactroot]")||document.querySelector("[data-react-checksum]")?"react":e.Vue||e.__VUE__||document.querySelector("[data-v-]")||document.querySelector(".vue-component")?"vue":e.ng||e.angular||e.getAllAngularRootElements||document.querySelector("[ng-app]")||document.querySelector("[ng-controller]")||document.querySelector("app-root")?"angular":e.__svelte||document.querySelector("[data-svelte]")||document.querySelector(".svelte-component")?"svelte":"unknown"}static clearCache(){this.cachedInfo=null}static getDescription(e){const t=e||this.detect(),o=[];return t.isDevelopment?o.push("Development"):t.isProduction?o.push("Production"):t.isTest&&o.push("Test"),t.isBrowser&&o.push("Browser"),t.isNode&&o.push("Node.js"),t.buildTool&&"unknown"!==t.buildTool&&o.push(t.buildTool.charAt(0).toUpperCase()+t.buildTool.slice(1)),t.framework&&"unknown"!==t.framework&&o.push(t.framework.charAt(0).toUpperCase()+t.framework.slice(1)),o.join(" + ")||"Unknown Environment"}}t.EnvironmentDetector=o,o.cachedInfo=null}},t={};function o(n){var s=t[n];if(void 0!==s)return s.exports;var i=t[n]={exports:{}};return e[n](i,i.exports,o),i.exports}var n={};(()=>{var e=n;Object.defineProperty(e,"__esModule",{value:!0});const t=o(335);if(o(491).EnvironmentDetector.detect().isDevelopment){const e=()=>"undefined"!=typeof window?`${"https:"===window.location.protocol?"wss:":"ws:"}//${window.location.hostname}:${process.env.VISILOG_PORT||"3001"}`:"ws://localhost:3001";try{const o=new t.WebSocketLogger({enableWebSocket:!0,enableConsole:!0,websocketUrl:e(),autoConnect:!0,maxRetries:3,retryInterval:2e3});o.enableConsoleOverride(),"undefined"!=typeof window&&(window.__visilog=o),console.info("🔌 VisiLog auto-setup complete - console logs will be captured"),"undefined"!=typeof window&&"undefined"!=typeof document&&document.addEventListener("keydown",(e=>{if(e.ctrlKey&&e.shiftKey&&"L"===e.key){const e=o.getConfig().enableWebSocket;o.updateConfig({enableWebSocket:!e}),console.info("🔌 VisiLog "+(e?"disabled":"enabled"))}}))}catch(e){console.warn("VisiLog auto-setup failed:",e)}}else console.debug("VisiLog auto-setup skipped (not in development environment)");e.default={}})(),module.exports["visilog-auto"]=n})();
//# sourceMappingURL=auto.js.map