network-terminal 1.0.11 → 1.0.12
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/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -8
- package/dist/index.mjs.map +1 -1
- package/dist/vite-plugin.js +2 -1
- package/dist/vite-plugin.js.map +1 -1
- package/dist/vite-plugin.mjs +2 -8
- package/dist/vite-plugin.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -747,6 +747,7 @@ var NetworkTerminal = ({
|
|
|
747
747
|
|
|
748
748
|
// src/vite-plugin.ts
|
|
749
749
|
var import_http = require("http");
|
|
750
|
+
var import_child_process = require("child_process");
|
|
750
751
|
var networkLogs = [];
|
|
751
752
|
var sseClients = /* @__PURE__ */ new Set();
|
|
752
753
|
function broadcastLog(log) {
|
|
@@ -1223,7 +1224,7 @@ function networkTerminal(options = {}) {
|
|
|
1223
1224
|
`);
|
|
1224
1225
|
if (open) {
|
|
1225
1226
|
const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
1226
|
-
|
|
1227
|
+
(0, import_child_process.exec)(`${openCmd} http://localhost:${port}`);
|
|
1227
1228
|
}
|
|
1228
1229
|
});
|
|
1229
1230
|
dashboardServer.on("error", (err) => {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/components/NetworkTerminal.tsx","../src/components/Terminal.tsx","../src/components/TerminalHeader.tsx","../src/utils/formatters.ts","../src/utils/colors.ts","../src/components/LogEntry.tsx","../src/hooks/useNetworkInterceptor.ts","../src/vite-plugin.ts"],"sourcesContent":["export { NetworkTerminal } from './components/NetworkTerminal';\nexport { Terminal } from './components/Terminal';\nexport { TerminalHeader } from './components/TerminalHeader';\nexport { LogEntry } from './components/LogEntry';\nexport { useNetworkInterceptor } from './hooks/useNetworkInterceptor';\nexport { formatJson, truncate, formatTime } from './utils/formatters';\nexport { getStatusColor, getMethodColor } from './utils/colors';\nexport { networkTerminal } from './vite-plugin';\nexport type {\n NetworkLog,\n NetworkTerminalProps,\n TerminalProps,\n TerminalHeaderProps,\n LogEntryProps,\n UseNetworkInterceptorProps,\n} from './types';\nexport type { NetworkTerminalPluginOptions } from './vite-plugin';\n","import React, { useState, useCallback, useEffect } from 'react';\nimport { NetworkLog, NetworkTerminalProps } from '../types';\nimport { Terminal } from './Terminal';\nimport { useNetworkInterceptor } from '../hooks/useNetworkInterceptor';\n\nconst styles = {\n floatingButton: {\n position: 'fixed' as const,\n bottom: '16px',\n right: '16px',\n zIndex: 9999,\n backgroundColor: '#1f2937',\n color: '#4ade80',\n padding: '8px 16px',\n borderRadius: '8px',\n fontFamily: 'monospace',\n fontSize: '14px',\n boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1)',\n border: '1px solid #374151',\n cursor: 'pointer',\n },\n container: {\n position: 'fixed' as const,\n bottom: 0,\n left: 0,\n right: 0,\n zIndex: 9999,\n backgroundColor: '#111827',\n borderTop: '2px solid #22c55e',\n boxShadow: '0 -10px 15px -3px rgba(0, 0, 0, 0.1)',\n },\n containerTop: {\n position: 'fixed' as const,\n top: 0,\n left: 0,\n right: 0,\n zIndex: 9999,\n backgroundColor: '#111827',\n borderBottom: '2px solid #22c55e',\n boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1)',\n },\n mainHeader: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '8px 16px',\n backgroundColor: '#1f2937',\n borderBottom: '1px solid #374151',\n },\n headerLeft: {\n display: 'flex',\n alignItems: 'center',\n gap: '12px',\n },\n title: {\n color: '#4ade80',\n fontFamily: 'monospace',\n fontWeight: 'bold',\n },\n hint: {\n color: '#6b7280',\n fontFamily: 'monospace',\n fontSize: '12px',\n },\n headerRight: {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n },\n clearButton: {\n color: '#9ca3af',\n fontSize: '14px',\n fontFamily: 'monospace',\n padding: '4px 12px',\n borderRadius: '4px',\n border: 'none',\n background: 'transparent',\n cursor: 'pointer',\n },\n closeButton: {\n color: '#9ca3af',\n fontSize: '18px',\n fontWeight: 'bold',\n padding: '0 8px',\n border: 'none',\n background: 'transparent',\n cursor: 'pointer',\n borderRadius: '4px',\n },\n terminalsContainer: {\n display: 'flex',\n gap: '8px',\n padding: '8px',\n height: '450px',\n },\n terminalWrapper: {\n flex: 1,\n display: 'flex',\n flexDirection: 'column' as const,\n },\n} as const;\n\nexport const NetworkTerminal: React.FC<NetworkTerminalProps> = ({\n maxLogs = 100,\n defaultVisible = false,\n position = 'bottom',\n height = '450px',\n zIndex = 9999,\n}) => {\n const [logs, setLogs] = useState<NetworkLog[]>([]);\n const [isVisible, setIsVisible] = useState(defaultVisible);\n const [requestExpanded, setRequestExpanded] = useState(true);\n const [responseExpanded, setResponseExpanded] = useState(true);\n\n const addLog = useCallback(\n (log: NetworkLog) => {\n setLogs((prev) => [...prev.slice(-(maxLogs - 1)), log]);\n },\n [maxLogs]\n );\n\n const updateLog = useCallback((id: string, updates: Partial<NetworkLog>) => {\n setLogs((prev) =>\n prev.map((log) => (log.id === id ? { ...log, ...updates } : log))\n );\n }, []);\n\n useNetworkInterceptor({\n enabled: isVisible,\n onLogAdd: addLog,\n onLogUpdate: updateLog,\n });\n\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.ctrlKey && e.shiftKey && e.key === 'N') {\n e.preventDefault();\n setIsVisible((prev) => !prev);\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, []);\n\n const clearLogs = () => setLogs([]);\n\n if (!isVisible) {\n return (\n <button\n onClick={() => setIsVisible(true)}\n style={{\n ...styles.floatingButton,\n zIndex,\n }}\n title=\"Open Network Terminal (Ctrl+Shift+N)\"\n >\n {'>'}_ Network\n </button>\n );\n }\n\n const containerStyle =\n position === 'top'\n ? { ...styles.containerTop, zIndex }\n : { ...styles.container, zIndex };\n\n return (\n <div style={containerStyle}>\n <div style={styles.mainHeader}>\n <div style={styles.headerLeft}>\n <span style={styles.title}>{'>'}_ Network Terminal</span>\n <span style={styles.hint}>Press Ctrl+Shift+N to toggle</span>\n </div>\n <div style={styles.headerRight}>\n <button\n onClick={clearLogs}\n style={styles.clearButton}\n onMouseEnter={(e) => (e.currentTarget.style.color = '#fff')}\n onMouseLeave={(e) => (e.currentTarget.style.color = '#9ca3af')}\n >\n Clear All\n </button>\n <button\n onClick={() => setIsVisible(false)}\n style={styles.closeButton}\n onMouseEnter={(e) => (e.currentTarget.style.color = '#f87171')}\n onMouseLeave={(e) => (e.currentTarget.style.color = '#9ca3af')}\n >\n {'\\u00D7'}\n </button>\n </div>\n </div>\n\n <div style={{ ...styles.terminalsContainer, height }}>\n <div style={styles.terminalWrapper}>\n <Terminal\n title=\"Requests\"\n logs={logs}\n type=\"request\"\n onClear={clearLogs}\n expanded={requestExpanded}\n onToggleExpand={() => setRequestExpanded(!requestExpanded)}\n />\n </div>\n <div style={styles.terminalWrapper}>\n <Terminal\n title=\"Responses\"\n logs={logs}\n type=\"response\"\n onClear={clearLogs}\n expanded={responseExpanded}\n onToggleExpand={() => setResponseExpanded(!responseExpanded)}\n />\n </div>\n </div>\n </div>\n );\n};\n","import React, { useRef, useState, useEffect } from 'react';\nimport { TerminalProps } from '../types';\nimport { TerminalHeader } from './TerminalHeader';\nimport { LogEntry } from './LogEntry';\n\nconst styles = {\n container: {\n display: 'flex',\n flexDirection: 'column' as const,\n backgroundColor: '#111827',\n borderRadius: '8px',\n border: '1px solid #374151',\n overflow: 'hidden',\n transition: 'all 0.2s',\n flex: 1,\n },\n collapsed: {\n height: '48px',\n flex: 'none' as const,\n },\n body: {\n flex: 1,\n overflow: 'auto',\n padding: '16px',\n fontFamily: 'monospace',\n fontSize: '14px',\n minHeight: '200px',\n maxHeight: '400px',\n },\n empty: {\n color: '#6b7280',\n fontStyle: 'italic',\n },\n} as const;\n\nexport const Terminal: React.FC<TerminalProps> = ({\n title,\n logs,\n type,\n onClear,\n expanded,\n onToggleExpand,\n}) => {\n const terminalRef = useRef<HTMLDivElement>(null);\n const [autoScroll, setAutoScroll] = useState(true);\n\n useEffect(() => {\n if (autoScroll && terminalRef.current) {\n terminalRef.current.scrollTop = terminalRef.current.scrollHeight;\n }\n }, [logs, autoScroll]);\n\n const handleScroll = () => {\n if (terminalRef.current) {\n const { scrollTop, scrollHeight, clientHeight } = terminalRef.current;\n setAutoScroll(scrollHeight - scrollTop - clientHeight < 50);\n }\n };\n\n return (\n <div\n style={{\n ...styles.container,\n ...(expanded ? {} : styles.collapsed),\n }}\n >\n <TerminalHeader\n title={title}\n count={logs.length}\n expanded={expanded}\n onClear={onClear}\n onToggleExpand={onToggleExpand}\n />\n\n {expanded && (\n <div ref={terminalRef} onScroll={handleScroll} style={styles.body}>\n {logs.length === 0 ? (\n <div style={styles.empty}>Waiting for network requests...</div>\n ) : (\n logs.map((log) => <LogEntry key={log.id} log={log} type={type} />)\n )}\n </div>\n )}\n </div>\n );\n};\n","import React from 'react';\nimport { TerminalHeaderProps } from '../types';\n\nconst styles = {\n header: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '8px 16px',\n backgroundColor: '#1f2937',\n borderBottom: '1px solid #374151',\n },\n left: {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n },\n trafficLights: {\n display: 'flex',\n gap: '6px',\n },\n dot: {\n width: '12px',\n height: '12px',\n borderRadius: '50%',\n },\n title: {\n color: '#d1d5db',\n fontFamily: 'monospace',\n fontSize: '14px',\n marginLeft: '8px',\n },\n count: {\n color: '#6b7280',\n fontFamily: 'monospace',\n fontSize: '12px',\n },\n right: {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n },\n button: {\n color: '#9ca3af',\n fontSize: '12px',\n fontFamily: 'monospace',\n padding: '4px 8px',\n borderRadius: '4px',\n border: 'none',\n background: 'transparent',\n cursor: 'pointer',\n },\n} as const;\n\nexport const TerminalHeader: React.FC<TerminalHeaderProps> = ({\n title,\n count,\n expanded,\n onClear,\n onToggleExpand,\n}) => {\n return (\n <div style={styles.header}>\n <div style={styles.left}>\n <div style={styles.trafficLights}>\n <div style={{ ...styles.dot, backgroundColor: '#ef4444' }} />\n <div style={{ ...styles.dot, backgroundColor: '#eab308' }} />\n <div style={{ ...styles.dot, backgroundColor: '#22c55e' }} />\n </div>\n <span style={styles.title}>{title}</span>\n <span style={styles.count}>({count} entries)</span>\n </div>\n <div style={styles.right}>\n <button\n onClick={onClear}\n style={styles.button}\n onMouseEnter={(e) => (e.currentTarget.style.color = '#fff')}\n onMouseLeave={(e) => (e.currentTarget.style.color = '#9ca3af')}\n >\n Clear\n </button>\n <button\n onClick={onToggleExpand}\n style={styles.button}\n onMouseEnter={(e) => (e.currentTarget.style.color = '#fff')}\n onMouseLeave={(e) => (e.currentTarget.style.color = '#9ca3af')}\n >\n {expanded ? '\\u25BC' : '\\u25B2'}\n </button>\n </div>\n </div>\n );\n};\n","export const formatJson = (data: unknown): string => {\n if (!data) return '';\n try {\n if (typeof data === 'string') {\n const parsed = JSON.parse(data);\n return JSON.stringify(parsed, null, 2);\n }\n return JSON.stringify(data, null, 2);\n } catch {\n return String(data);\n }\n};\n\nexport const truncate = (str: string, maxLength: number): string => {\n if (str.length <= maxLength) return str;\n return str.slice(0, maxLength) + '...';\n};\n\nexport const formatTime = (date: Date): string => {\n return date.toLocaleTimeString('en-US', {\n hour12: false,\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n });\n};\n","export const getStatusColor = (status?: number): string => {\n if (!status) return '#9ca3af'; // gray-400\n if (status >= 200 && status < 300) return '#4ade80'; // green-400\n if (status >= 300 && status < 400) return '#facc15'; // yellow-400\n if (status >= 400 && status < 500) return '#fb923c'; // orange-400\n return '#f87171'; // red-400\n};\n\nexport const getMethodColor = (method: string): string => {\n const colors: Record<string, string> = {\n GET: '#22d3ee', // cyan-400\n POST: '#4ade80', // green-400\n PUT: '#facc15', // yellow-400\n PATCH: '#fb923c', // orange-400\n DELETE: '#f87171', // red-400\n };\n return colors[method.toUpperCase()] || '#9ca3af';\n};\n","import React from 'react';\nimport { LogEntryProps } from '../types';\nimport { formatJson, formatTime } from '../utils/formatters';\nimport { getStatusColor, getMethodColor } from '../utils/colors';\n\nconst styles = {\n container: {\n marginBottom: '16px',\n borderBottom: '1px solid #1f2937',\n paddingBottom: '16px',\n },\n header: {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n marginBottom: '4px',\n flexWrap: 'wrap' as const,\n },\n timestamp: {\n color: '#6b7280',\n fontSize: '12px',\n fontFamily: 'monospace',\n },\n method: {\n fontWeight: 'bold',\n fontFamily: 'monospace',\n },\n status: {\n fontWeight: 'bold',\n fontFamily: 'monospace',\n },\n duration: {\n color: '#6b7280',\n fontSize: '12px',\n fontFamily: 'monospace',\n },\n url: {\n color: '#60a5fa',\n wordBreak: 'break-all' as const,\n marginBottom: '8px',\n fontFamily: 'monospace',\n fontSize: '13px',\n },\n bodyContainer: {\n marginTop: '8px',\n },\n bodyLabel: {\n color: '#a78bfa',\n fontSize: '12px',\n marginBottom: '4px',\n fontFamily: 'monospace',\n },\n pre: {\n backgroundColor: '#1f2937',\n padding: '8px',\n borderRadius: '4px',\n color: '#86efac',\n overflowX: 'auto' as const,\n fontSize: '12px',\n whiteSpace: 'pre-wrap' as const,\n fontFamily: 'monospace',\n maxHeight: '192px',\n overflowY: 'auto' as const,\n margin: 0,\n },\n errorPre: {\n backgroundColor: '#1f2937',\n padding: '8px',\n borderRadius: '4px',\n color: '#fca5a5',\n overflowX: 'auto' as const,\n fontSize: '12px',\n whiteSpace: 'pre-wrap' as const,\n fontFamily: 'monospace',\n margin: 0,\n },\n} as const;\n\nexport const LogEntry: React.FC<LogEntryProps> = ({ log, type }) => {\n return (\n <div style={styles.container}>\n <div style={styles.header}>\n <span style={styles.timestamp}>[{formatTime(log.timestamp)}]</span>\n <span style={{ ...styles.method, color: getMethodColor(log.method) }}>\n {log.method}\n </span>\n {type === 'response' && log.status && (\n <span style={{ ...styles.status, color: getStatusColor(log.status) }}>\n {log.status} {log.statusText}\n </span>\n )}\n {log.duration !== undefined && type === 'response' && (\n <span style={styles.duration}>({log.duration}ms)</span>\n )}\n </div>\n\n <div style={styles.url}>{log.url}</div>\n\n {type === 'request' ? (\n log.requestBody ? (\n <div style={styles.bodyContainer}>\n <div style={styles.bodyLabel}>{'\\u25B8'} Payload:</div>\n <pre style={styles.pre}>{formatJson(log.requestBody)}</pre>\n </div>\n ) : null\n ) : log.error ? (\n <div style={styles.bodyContainer}>\n <div style={{ ...styles.bodyLabel, color: '#f87171' }}>\n {'\\u25B8'} Error:\n </div>\n <pre style={styles.errorPre}>{log.error}</pre>\n </div>\n ) : log.responseBody ? (\n <div style={styles.bodyContainer}>\n <div style={styles.bodyLabel}>{'\\u25B8'} Response:</div>\n <pre style={styles.pre}>{formatJson(log.responseBody)}</pre>\n </div>\n ) : null}\n </div>\n );\n};\n","import { useEffect, useRef, useCallback } from 'react';\nimport { NetworkLog, UseNetworkInterceptorProps } from '../types';\n\nexport const useNetworkInterceptor = ({\n enabled,\n onLogAdd,\n onLogUpdate,\n}: UseNetworkInterceptorProps) => {\n const originalFetch = useRef<typeof fetch | null>(null);\n const originalXHROpen = useRef<typeof XMLHttpRequest.prototype.open | null>(null);\n const originalXHRSend = useRef<typeof XMLHttpRequest.prototype.send | null>(null);\n\n const generateId = useCallback((prefix: string) => {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n }, []);\n\n // Intercept Fetch\n useEffect(() => {\n if (!enabled) return;\n\n originalFetch.current = window.fetch;\n\n window.fetch = async function (input, init) {\n const id = generateId('fetch');\n const startTime = performance.now();\n\n const url =\n typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.href\n : input.url;\n\n const method = init?.method || 'GET';\n\n let requestBody: unknown = null;\n if (init?.body) {\n try {\n requestBody =\n typeof init.body === 'string' ? JSON.parse(init.body) : init.body;\n } catch {\n requestBody = init.body;\n }\n }\n\n onLogAdd({\n id,\n timestamp: new Date(),\n method: method.toUpperCase(),\n url,\n requestBody,\n type: 'fetch',\n });\n\n try {\n const response = await originalFetch.current!(input, init);\n const duration = Math.round(performance.now() - startTime);\n\n const clonedResponse = response.clone();\n let responseBody: unknown = null;\n\n try {\n responseBody = await clonedResponse.json();\n } catch {\n try {\n responseBody = await clonedResponse.text();\n } catch {\n responseBody = '[Could not read response body]';\n }\n }\n\n onLogUpdate(id, {\n status: response.status,\n statusText: response.statusText,\n responseBody,\n duration,\n });\n\n return response;\n } catch (error: unknown) {\n const duration = Math.round(performance.now() - startTime);\n const errorMessage =\n error instanceof Error ? error.message : 'Network Error';\n onLogUpdate(id, {\n error: errorMessage,\n duration,\n });\n throw error;\n }\n };\n\n return () => {\n if (originalFetch.current) {\n window.fetch = originalFetch.current;\n }\n };\n }, [enabled, onLogAdd, onLogUpdate, generateId]);\n\n // Intercept XHR\n useEffect(() => {\n if (!enabled) return;\n\n originalXHROpen.current = XMLHttpRequest.prototype.open;\n originalXHRSend.current = XMLHttpRequest.prototype.send;\n\n XMLHttpRequest.prototype.open = function (\n method: string,\n url: string | URL\n ) {\n (this as XMLHttpRequest & { _networkTerminal: NetworkLog & { startTime: number } })._networkTerminal = {\n id: generateId('xhr'),\n method: method.toUpperCase(),\n url: url.toString(),\n startTime: 0,\n timestamp: new Date(),\n type: 'xhr',\n };\n return originalXHROpen.current!.apply(\n this,\n arguments as unknown as Parameters<typeof XMLHttpRequest.prototype.open>\n );\n };\n\n XMLHttpRequest.prototype.send = function (body?: Document | XMLHttpRequestBodyInit | null) {\n const meta = (this as XMLHttpRequest & { _networkTerminal?: NetworkLog & { startTime: number } })._networkTerminal;\n\n if (meta) {\n meta.startTime = performance.now();\n\n let requestBody: unknown = null;\n if (body) {\n try {\n requestBody = typeof body === 'string' ? JSON.parse(body) : body;\n } catch {\n requestBody = body;\n }\n }\n\n onLogAdd({\n id: meta.id,\n timestamp: new Date(),\n method: meta.method,\n url: meta.url,\n requestBody,\n type: 'xhr',\n });\n\n this.addEventListener('load', function () {\n const duration = Math.round(performance.now() - meta.startTime);\n\n let responseBody: unknown = null;\n try {\n responseBody = JSON.parse(this.responseText);\n } catch {\n responseBody = this.responseText;\n }\n\n onLogUpdate(meta.id, {\n status: this.status,\n statusText: this.statusText,\n responseBody,\n duration,\n });\n });\n\n this.addEventListener('error', function () {\n const duration = Math.round(performance.now() - meta.startTime);\n onLogUpdate(meta.id, {\n error: 'Network Error',\n duration,\n });\n });\n }\n\n return originalXHRSend.current!.apply(\n this,\n arguments as unknown as Parameters<typeof XMLHttpRequest.prototype.send>\n );\n };\n\n return () => {\n if (originalXHROpen.current) {\n XMLHttpRequest.prototype.open = originalXHROpen.current;\n }\n if (originalXHRSend.current) {\n XMLHttpRequest.prototype.send = originalXHRSend.current;\n }\n };\n }, [enabled, onLogAdd, onLogUpdate, generateId]);\n};\n","import type { Plugin, ViteDevServer } from 'vite';\nimport { createServer, IncomingMessage, ServerResponse } from 'http';\n\nexport interface NetworkTerminalPluginOptions {\n /** Port for the dashboard (default: 4001) */\n port?: number;\n /** Auto-open dashboard in browser (default: true) */\n open?: boolean;\n}\n\n// Store network logs\nconst networkLogs: any[] = [];\nconst sseClients = new Set<ServerResponse>();\n\nfunction broadcastLog(log: any) {\n const data = JSON.stringify(log);\n sseClients.forEach(client => {\n client.write(`data: ${data}\\n\\n`);\n });\n}\n\nfunction getDashboardHTML(_port: number): string {\n return `\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Network Terminal</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body {\n font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;\n background: #0a0e17;\n color: #e2e8f0;\n height: 100vh;\n overflow: hidden;\n }\n .header {\n background: #0f172a;\n padding: 12px 24px;\n border-bottom: 2px solid #4ade80;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n .title { color: #4ade80; font-size: 18px; font-weight: bold; }\n .subtitle { color: #64748b; font-size: 12px; margin-top: 4px; }\n .controls { display: flex; gap: 12px; align-items: center; }\n .btn {\n background: #1e293b;\n border: 1px solid #334155;\n color: #94a3b8;\n padding: 8px 16px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 12px;\n font-family: inherit;\n }\n .btn:hover { background: #334155; color: #e2e8f0; }\n .container { display: flex; height: calc(100vh - 80px); }\n .panel {\n flex: 1;\n display: flex;\n flex-direction: column;\n border-right: 1px solid #1e293b;\n }\n .panel:last-child { border-right: none; }\n .panel-header {\n background: #0f172a;\n padding: 12px 16px;\n border-bottom: 1px solid #1e293b;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n .panel-title { font-weight: bold; font-size: 14px; }\n .panel-title.request { color: #3b82f6; }\n .panel-title.response { color: #22c55e; }\n .panel-count {\n background: #1e293b;\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n color: #64748b;\n }\n .logs { flex: 1; overflow-y: auto; padding: 8px; }\n .log-entry {\n background: #0f172a;\n border: 1px solid #1e293b;\n border-radius: 8px;\n margin-bottom: 8px;\n overflow: hidden;\n cursor: pointer;\n transition: border-color 0.2s;\n }\n .log-entry:hover { border-color: #334155; }\n .log-entry.selected { border-color: #4ade80; }\n .log-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 12px;\n background: #1e293b;\n }\n .method {\n font-weight: bold;\n font-size: 11px;\n padding: 3px 8px;\n border-radius: 4px;\n }\n .method.GET { background: #166534; color: #4ade80; }\n .method.POST { background: #1e40af; color: #60a5fa; }\n .method.PUT { background: #a16207; color: #fcd34d; }\n .method.DELETE { background: #991b1b; color: #fca5a5; }\n .method.PATCH { background: #7c3aed; color: #c4b5fd; }\n .status { font-size: 11px; padding: 3px 8px; border-radius: 4px; }\n .status.success { background: #166534; color: #4ade80; }\n .status.error { background: #991b1b; color: #fca5a5; }\n .status.pending { background: #374151; color: #9ca3af; }\n .url {\n flex: 1;\n font-size: 12px;\n color: #94a3b8;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .duration { font-size: 11px; color: #64748b; }\n .timestamp { font-size: 10px; color: #475569; }\n .log-body { padding: 12px; font-size: 11px; max-height: 300px; overflow: auto; }\n .log-body pre {\n margin: 0;\n white-space: pre-wrap;\n word-break: break-all;\n color: #cbd5e1;\n line-height: 1.5;\n }\n .log-body.empty { color: #475569; font-style: italic; }\n .empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: #475569;\n text-align: center;\n padding: 20px;\n }\n .empty-state svg { width: 48px; height: 48px; margin-bottom: 16px; opacity: 0.5; }\n .status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #22c55e;\n animation: pulse 2s infinite;\n }\n @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }\n .status-dot.disconnected { background: #ef4444; animation: none; }\n </style>\n</head>\n<body>\n <div class=\"header\">\n <div>\n <div class=\"title\">>_ Network Terminal</div>\n <div class=\"subtitle\">Vite Plugin - Monitoring fetch/XHR requests</div>\n </div>\n <div class=\"controls\">\n <div class=\"status-dot\" id=\"statusDot\"></div>\n <button class=\"btn\" onclick=\"clearLogs()\">Clear All</button>\n </div>\n </div>\n\n <div class=\"container\">\n <div class=\"panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title request\">⬆ REQUESTS</span>\n <span class=\"panel-count\" id=\"requestCount\">0</span>\n </div>\n <div class=\"logs\" id=\"requestLogs\">\n <div class=\"empty-state\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M12 19V5M5 12l7-7 7 7\"/>\n </svg>\n <div>Waiting for requests...</div>\n <div style=\"margin-top: 8px; font-size: 11px;\">Make fetch/XHR requests in your app</div>\n </div>\n </div>\n </div>\n\n <div class=\"panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title response\">⬇ RESPONSES</span>\n <span class=\"panel-count\" id=\"responseCount\">0</span>\n </div>\n <div class=\"logs\" id=\"responseLogs\">\n <div class=\"empty-state\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M12 5v14M5 12l7 7 7-7\"/>\n </svg>\n <div>Waiting for responses...</div>\n </div>\n </div>\n </div>\n </div>\n\n <script>\n const logs = new Map();\n let selectedId = null;\n\n function formatJson(data) {\n if (!data) return null;\n if (typeof data === 'string') {\n try { data = JSON.parse(data); }\n catch (e) { return escapeHtml(data.substring(0, 2000)); }\n }\n return escapeHtml(JSON.stringify(data, null, 2));\n }\n\n function escapeHtml(str) {\n if (typeof str !== 'string') str = String(str);\n return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');\n }\n\n function formatTime(timestamp) {\n const date = new Date(timestamp);\n return date.toLocaleTimeString('en-US', { hour12: false }) + '.' +\n String(date.getMilliseconds()).padStart(3, '0');\n }\n\n function getLogsArray() {\n return Array.from(logs.values()).sort((a, b) =>\n new Date(b.timestamp) - new Date(a.timestamp)\n );\n }\n\n function renderLogs() {\n const logsArray = getLogsArray();\n const requestContainer = document.getElementById('requestLogs');\n const responseContainer = document.getElementById('responseLogs');\n\n document.getElementById('requestCount').textContent = logsArray.length;\n document.getElementById('responseCount').textContent = logsArray.filter(l => l.status).length;\n\n if (logsArray.length === 0) {\n requestContainer.innerHTML = '<div class=\"empty-state\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M12 19V5M5 12l7-7 7 7\"/></svg><div>Waiting for requests...</div></div>';\n responseContainer.innerHTML = '<div class=\"empty-state\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M12 5v14M5 12l7 7 7-7\"/></svg><div>Waiting for responses...</div></div>';\n return;\n }\n\n requestContainer.innerHTML = logsArray.map(log => {\n const isSelected = selectedId === log.id;\n const bodyContent = formatJson(log.requestBody);\n return '<div class=\"log-entry '+(isSelected ? 'selected' : '')+'\" onclick=\"selectLog(\\\\''+log.id+'\\\\')\"><div class=\"log-header\"><span class=\"method '+log.method+'\">'+log.method+'</span><span class=\"url\" title=\"'+escapeHtml(log.url)+'\">'+log.url+'</span><span class=\"timestamp\">'+formatTime(log.timestamp)+'</span></div>'+(isSelected ? '<div class=\"log-body '+(bodyContent ? '' : 'empty')+'\">'+(bodyContent ? '<pre>'+bodyContent+'</pre>' : 'No request body')+'</div>' : '')+'</div>';\n }).join('');\n\n responseContainer.innerHTML = logsArray.map(log => {\n const isSelected = selectedId === log.id;\n const statusClass = !log.status ? 'pending' : log.status < 400 ? 'success' : 'error';\n const bodyContent = formatJson(log.responseBody);\n return '<div class=\"log-entry '+(isSelected ? 'selected' : '')+'\" onclick=\"selectLog(\\\\''+log.id+'\\\\')\"><div class=\"log-header\"><span class=\"status '+statusClass+'\">'+(log.status || '...')+'</span><span class=\"url\" title=\"'+escapeHtml(log.url)+'\">'+log.url+'</span><span class=\"duration\">'+(log.duration ? log.duration + 'ms' : '')+'</span></div>'+(isSelected ? '<div class=\"log-body '+(bodyContent ? '' : 'empty')+'\">'+(bodyContent ? '<pre>'+bodyContent+'</pre>' : (log.status ? 'No response body' : 'Pending...'))+'</div>' : '')+'</div>';\n }).join('');\n }\n\n function selectLog(id) {\n selectedId = selectedId === id ? null : id;\n renderLogs();\n }\n\n function clearLogs() {\n logs.clear();\n selectedId = null;\n renderLogs();\n fetch('/clear', { method: 'POST' });\n }\n\n function addLog(log) {\n logs.set(log.id, log);\n if (logs.size > 100) {\n const oldest = getLogsArray().pop();\n if (oldest) logs.delete(oldest.id);\n }\n renderLogs();\n }\n\n function connect() {\n const evtSource = new EventSource('/events');\n evtSource.onopen = () => document.getElementById('statusDot').classList.remove('disconnected');\n evtSource.onmessage = (event) => addLog(JSON.parse(event.data));\n evtSource.onerror = () => {\n document.getElementById('statusDot').classList.add('disconnected');\n evtSource.close();\n setTimeout(connect, 2000);\n };\n }\n\n connect();\n renderLogs();\n </script>\n</body>\n</html>\n`;\n}\n\nfunction getMonitorScript(port: number): string {\n return `\n(function() {\n if (window.__networkTerminalActive) return;\n window.__networkTerminalActive = true;\n\n var DASHBOARD_URL = 'http://localhost:${port}';\n\n function sendLog(log) {\n var xhr = new XMLHttpRequest();\n xhr.open('POST', DASHBOARD_URL + '/log', true);\n xhr.setRequestHeader('Content-Type', 'application/json');\n xhr.send(JSON.stringify(log));\n }\n\n function tryParse(data) {\n if (!data) return null;\n if (typeof data === 'object') return data;\n try { return JSON.parse(data); } catch (e) { return data; }\n }\n\n var originalFetch = window.fetch;\n window.fetch = function() {\n var args = arguments;\n var startTime = Date.now();\n var input = args[0];\n var options = args[1] || {};\n var url = typeof input === 'string' ? input : (input.url || input.toString());\n var method = (options.method || 'GET').toUpperCase();\n\n if (url.includes('localhost:${port}')) {\n return originalFetch.apply(this, args);\n }\n\n var log = {\n id: Date.now().toString() + Math.random().toString(36).substr(2, 9),\n method: method,\n url: url,\n timestamp: new Date().toISOString(),\n requestBody: options.body ? tryParse(options.body) : null,\n status: null,\n responseBody: null,\n duration: null,\n };\n\n sendLog(log);\n\n return originalFetch.apply(this, args).then(function(response) {\n log.status = response.status;\n log.duration = Date.now() - startTime;\n var cloned = response.clone();\n cloned.text().then(function(text) {\n log.responseBody = tryParse(text);\n sendLog(log);\n }).catch(function() { sendLog(log); });\n return response;\n }).catch(function(error) {\n log.status = 0;\n log.duration = Date.now() - startTime;\n log.error = error.message;\n sendLog(log);\n throw error;\n });\n };\n\n var OriginalXHR = window.XMLHttpRequest;\n window.XMLHttpRequest = function() {\n var xhr = new OriginalXHR();\n var method, url, requestBody, startTime;\n\n var originalOpen = xhr.open;\n xhr.open = function(m, u) {\n method = m;\n url = u;\n return originalOpen.apply(this, arguments);\n };\n\n var originalSend = xhr.send;\n xhr.send = function(body) {\n if (url && url.includes('localhost:${port}')) {\n return originalSend.apply(this, arguments);\n }\n\n startTime = Date.now();\n requestBody = body;\n\n var log = {\n id: Date.now().toString() + Math.random().toString(36).substr(2, 9),\n method: (method || 'GET').toUpperCase(),\n url: url,\n timestamp: new Date().toISOString(),\n requestBody: tryParse(requestBody),\n status: null,\n responseBody: null,\n duration: null,\n };\n\n sendLog(log);\n\n xhr.addEventListener('loadend', function() {\n log.status = xhr.status;\n log.duration = Date.now() - startTime;\n log.responseBody = tryParse(xhr.responseText);\n sendLog(log);\n });\n\n return originalSend.apply(this, arguments);\n };\n\n return xhr;\n };\n\n console.log('[Network Terminal] ✓ Active - Dashboard: http://localhost:${port}');\n})();\n`;\n}\n\nexport function networkTerminal(options: NetworkTerminalPluginOptions = {}): Plugin {\n const { port = 4001, open = true } = options;\n let dashboardServer: ReturnType<typeof createServer> | null = null;\n\n return {\n name: 'vite-plugin-network-terminal',\n apply: 'serve',\n\n configureServer(_server: ViteDevServer) {\n // Start dashboard server\n dashboardServer = createServer((req: IncomingMessage, res: ServerResponse) => {\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n if (req.url === '/events') {\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n });\n sseClients.add(res);\n networkLogs.forEach(log => {\n res.write(`data: ${JSON.stringify(log)}\\n\\n`);\n });\n req.on('close', () => sseClients.delete(res));\n return;\n }\n\n if (req.url === '/log' && req.method === 'POST') {\n let body = '';\n req.on('data', (chunk: Buffer) => body += chunk);\n req.on('end', () => {\n try {\n const log = JSON.parse(body);\n const existingIndex = networkLogs.findIndex(l => l.id === log.id);\n if (existingIndex >= 0) {\n networkLogs[existingIndex] = log;\n } else {\n networkLogs.unshift(log);\n if (networkLogs.length > 100) networkLogs.pop();\n }\n broadcastLog(log);\n } catch (e) {}\n res.writeHead(200);\n res.end('OK');\n });\n return;\n }\n\n if (req.url === '/clear' && req.method === 'POST') {\n networkLogs.length = 0;\n res.writeHead(200);\n res.end('OK');\n return;\n }\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getDashboardHTML(port));\n });\n\n dashboardServer.listen(port, () => {\n console.log(`\\n \\x1b[32m>_ Network Terminal\\x1b[0m`);\n console.log(` Dashboard: \\x1b[36mhttp://localhost:${port}\\x1b[0m\\n`);\n\n if (open) {\n const openCmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';\n require('child_process').exec(`${openCmd} http://localhost:${port}`);\n }\n });\n\n dashboardServer.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n console.log(`\\n \\x1b[33m[Network Terminal]\\x1b[0m Port ${port} in use, dashboard disabled\\n`);\n }\n });\n },\n\n transformIndexHtml(html: string) {\n const script = `<script>${getMonitorScript(port)}</script>`;\n if (html.includes('</head>')) {\n return html.replace('</head>', script + '</head>');\n }\n return script + html;\n },\n\n buildEnd() {\n if (dashboardServer) {\n dashboardServer.close();\n }\n },\n };\n}\n\nexport default networkTerminal;\n"],"mappings":"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AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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","names":["import_react","import_jsx_runtime","styles","import_jsx_runtime","styles","import_react","import_jsx_runtime","styles"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/components/NetworkTerminal.tsx","../src/components/Terminal.tsx","../src/components/TerminalHeader.tsx","../src/utils/formatters.ts","../src/utils/colors.ts","../src/components/LogEntry.tsx","../src/hooks/useNetworkInterceptor.ts","../src/vite-plugin.ts"],"sourcesContent":["export { NetworkTerminal } from './components/NetworkTerminal';\nexport { Terminal } from './components/Terminal';\nexport { TerminalHeader } from './components/TerminalHeader';\nexport { LogEntry } from './components/LogEntry';\nexport { useNetworkInterceptor } from './hooks/useNetworkInterceptor';\nexport { formatJson, truncate, formatTime } from './utils/formatters';\nexport { getStatusColor, getMethodColor } from './utils/colors';\nexport { networkTerminal } from './vite-plugin';\nexport type {\n NetworkLog,\n NetworkTerminalProps,\n TerminalProps,\n TerminalHeaderProps,\n LogEntryProps,\n UseNetworkInterceptorProps,\n} from './types';\nexport type { NetworkTerminalPluginOptions } from './vite-plugin';\n","import React, { useState, useCallback, useEffect } from 'react';\nimport { NetworkLog, NetworkTerminalProps } from '../types';\nimport { Terminal } from './Terminal';\nimport { useNetworkInterceptor } from '../hooks/useNetworkInterceptor';\n\nconst styles = {\n floatingButton: {\n position: 'fixed' as const,\n bottom: '16px',\n right: '16px',\n zIndex: 9999,\n backgroundColor: '#1f2937',\n color: '#4ade80',\n padding: '8px 16px',\n borderRadius: '8px',\n fontFamily: 'monospace',\n fontSize: '14px',\n boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1)',\n border: '1px solid #374151',\n cursor: 'pointer',\n },\n container: {\n position: 'fixed' as const,\n bottom: 0,\n left: 0,\n right: 0,\n zIndex: 9999,\n backgroundColor: '#111827',\n borderTop: '2px solid #22c55e',\n boxShadow: '0 -10px 15px -3px rgba(0, 0, 0, 0.1)',\n },\n containerTop: {\n position: 'fixed' as const,\n top: 0,\n left: 0,\n right: 0,\n zIndex: 9999,\n backgroundColor: '#111827',\n borderBottom: '2px solid #22c55e',\n boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1)',\n },\n mainHeader: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '8px 16px',\n backgroundColor: '#1f2937',\n borderBottom: '1px solid #374151',\n },\n headerLeft: {\n display: 'flex',\n alignItems: 'center',\n gap: '12px',\n },\n title: {\n color: '#4ade80',\n fontFamily: 'monospace',\n fontWeight: 'bold',\n },\n hint: {\n color: '#6b7280',\n fontFamily: 'monospace',\n fontSize: '12px',\n },\n headerRight: {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n },\n clearButton: {\n color: '#9ca3af',\n fontSize: '14px',\n fontFamily: 'monospace',\n padding: '4px 12px',\n borderRadius: '4px',\n border: 'none',\n background: 'transparent',\n cursor: 'pointer',\n },\n closeButton: {\n color: '#9ca3af',\n fontSize: '18px',\n fontWeight: 'bold',\n padding: '0 8px',\n border: 'none',\n background: 'transparent',\n cursor: 'pointer',\n borderRadius: '4px',\n },\n terminalsContainer: {\n display: 'flex',\n gap: '8px',\n padding: '8px',\n height: '450px',\n },\n terminalWrapper: {\n flex: 1,\n display: 'flex',\n flexDirection: 'column' as const,\n },\n} as const;\n\nexport const NetworkTerminal: React.FC<NetworkTerminalProps> = ({\n maxLogs = 100,\n defaultVisible = false,\n position = 'bottom',\n height = '450px',\n zIndex = 9999,\n}) => {\n const [logs, setLogs] = useState<NetworkLog[]>([]);\n const [isVisible, setIsVisible] = useState(defaultVisible);\n const [requestExpanded, setRequestExpanded] = useState(true);\n const [responseExpanded, setResponseExpanded] = useState(true);\n\n const addLog = useCallback(\n (log: NetworkLog) => {\n setLogs((prev) => [...prev.slice(-(maxLogs - 1)), log]);\n },\n [maxLogs]\n );\n\n const updateLog = useCallback((id: string, updates: Partial<NetworkLog>) => {\n setLogs((prev) =>\n prev.map((log) => (log.id === id ? { ...log, ...updates } : log))\n );\n }, []);\n\n useNetworkInterceptor({\n enabled: isVisible,\n onLogAdd: addLog,\n onLogUpdate: updateLog,\n });\n\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.ctrlKey && e.shiftKey && e.key === 'N') {\n e.preventDefault();\n setIsVisible((prev) => !prev);\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, []);\n\n const clearLogs = () => setLogs([]);\n\n if (!isVisible) {\n return (\n <button\n onClick={() => setIsVisible(true)}\n style={{\n ...styles.floatingButton,\n zIndex,\n }}\n title=\"Open Network Terminal (Ctrl+Shift+N)\"\n >\n {'>'}_ Network\n </button>\n );\n }\n\n const containerStyle =\n position === 'top'\n ? { ...styles.containerTop, zIndex }\n : { ...styles.container, zIndex };\n\n return (\n <div style={containerStyle}>\n <div style={styles.mainHeader}>\n <div style={styles.headerLeft}>\n <span style={styles.title}>{'>'}_ Network Terminal</span>\n <span style={styles.hint}>Press Ctrl+Shift+N to toggle</span>\n </div>\n <div style={styles.headerRight}>\n <button\n onClick={clearLogs}\n style={styles.clearButton}\n onMouseEnter={(e) => (e.currentTarget.style.color = '#fff')}\n onMouseLeave={(e) => (e.currentTarget.style.color = '#9ca3af')}\n >\n Clear All\n </button>\n <button\n onClick={() => setIsVisible(false)}\n style={styles.closeButton}\n onMouseEnter={(e) => (e.currentTarget.style.color = '#f87171')}\n onMouseLeave={(e) => (e.currentTarget.style.color = '#9ca3af')}\n >\n {'\\u00D7'}\n </button>\n </div>\n </div>\n\n <div style={{ ...styles.terminalsContainer, height }}>\n <div style={styles.terminalWrapper}>\n <Terminal\n title=\"Requests\"\n logs={logs}\n type=\"request\"\n onClear={clearLogs}\n expanded={requestExpanded}\n onToggleExpand={() => setRequestExpanded(!requestExpanded)}\n />\n </div>\n <div style={styles.terminalWrapper}>\n <Terminal\n title=\"Responses\"\n logs={logs}\n type=\"response\"\n onClear={clearLogs}\n expanded={responseExpanded}\n onToggleExpand={() => setResponseExpanded(!responseExpanded)}\n />\n </div>\n </div>\n </div>\n );\n};\n","import React, { useRef, useState, useEffect } from 'react';\nimport { TerminalProps } from '../types';\nimport { TerminalHeader } from './TerminalHeader';\nimport { LogEntry } from './LogEntry';\n\nconst styles = {\n container: {\n display: 'flex',\n flexDirection: 'column' as const,\n backgroundColor: '#111827',\n borderRadius: '8px',\n border: '1px solid #374151',\n overflow: 'hidden',\n transition: 'all 0.2s',\n flex: 1,\n },\n collapsed: {\n height: '48px',\n flex: 'none' as const,\n },\n body: {\n flex: 1,\n overflow: 'auto',\n padding: '16px',\n fontFamily: 'monospace',\n fontSize: '14px',\n minHeight: '200px',\n maxHeight: '400px',\n },\n empty: {\n color: '#6b7280',\n fontStyle: 'italic',\n },\n} as const;\n\nexport const Terminal: React.FC<TerminalProps> = ({\n title,\n logs,\n type,\n onClear,\n expanded,\n onToggleExpand,\n}) => {\n const terminalRef = useRef<HTMLDivElement>(null);\n const [autoScroll, setAutoScroll] = useState(true);\n\n useEffect(() => {\n if (autoScroll && terminalRef.current) {\n terminalRef.current.scrollTop = terminalRef.current.scrollHeight;\n }\n }, [logs, autoScroll]);\n\n const handleScroll = () => {\n if (terminalRef.current) {\n const { scrollTop, scrollHeight, clientHeight } = terminalRef.current;\n setAutoScroll(scrollHeight - scrollTop - clientHeight < 50);\n }\n };\n\n return (\n <div\n style={{\n ...styles.container,\n ...(expanded ? {} : styles.collapsed),\n }}\n >\n <TerminalHeader\n title={title}\n count={logs.length}\n expanded={expanded}\n onClear={onClear}\n onToggleExpand={onToggleExpand}\n />\n\n {expanded && (\n <div ref={terminalRef} onScroll={handleScroll} style={styles.body}>\n {logs.length === 0 ? (\n <div style={styles.empty}>Waiting for network requests...</div>\n ) : (\n logs.map((log) => <LogEntry key={log.id} log={log} type={type} />)\n )}\n </div>\n )}\n </div>\n );\n};\n","import React from 'react';\nimport { TerminalHeaderProps } from '../types';\n\nconst styles = {\n header: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '8px 16px',\n backgroundColor: '#1f2937',\n borderBottom: '1px solid #374151',\n },\n left: {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n },\n trafficLights: {\n display: 'flex',\n gap: '6px',\n },\n dot: {\n width: '12px',\n height: '12px',\n borderRadius: '50%',\n },\n title: {\n color: '#d1d5db',\n fontFamily: 'monospace',\n fontSize: '14px',\n marginLeft: '8px',\n },\n count: {\n color: '#6b7280',\n fontFamily: 'monospace',\n fontSize: '12px',\n },\n right: {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n },\n button: {\n color: '#9ca3af',\n fontSize: '12px',\n fontFamily: 'monospace',\n padding: '4px 8px',\n borderRadius: '4px',\n border: 'none',\n background: 'transparent',\n cursor: 'pointer',\n },\n} as const;\n\nexport const TerminalHeader: React.FC<TerminalHeaderProps> = ({\n title,\n count,\n expanded,\n onClear,\n onToggleExpand,\n}) => {\n return (\n <div style={styles.header}>\n <div style={styles.left}>\n <div style={styles.trafficLights}>\n <div style={{ ...styles.dot, backgroundColor: '#ef4444' }} />\n <div style={{ ...styles.dot, backgroundColor: '#eab308' }} />\n <div style={{ ...styles.dot, backgroundColor: '#22c55e' }} />\n </div>\n <span style={styles.title}>{title}</span>\n <span style={styles.count}>({count} entries)</span>\n </div>\n <div style={styles.right}>\n <button\n onClick={onClear}\n style={styles.button}\n onMouseEnter={(e) => (e.currentTarget.style.color = '#fff')}\n onMouseLeave={(e) => (e.currentTarget.style.color = '#9ca3af')}\n >\n Clear\n </button>\n <button\n onClick={onToggleExpand}\n style={styles.button}\n onMouseEnter={(e) => (e.currentTarget.style.color = '#fff')}\n onMouseLeave={(e) => (e.currentTarget.style.color = '#9ca3af')}\n >\n {expanded ? '\\u25BC' : '\\u25B2'}\n </button>\n </div>\n </div>\n );\n};\n","export const formatJson = (data: unknown): string => {\n if (!data) return '';\n try {\n if (typeof data === 'string') {\n const parsed = JSON.parse(data);\n return JSON.stringify(parsed, null, 2);\n }\n return JSON.stringify(data, null, 2);\n } catch {\n return String(data);\n }\n};\n\nexport const truncate = (str: string, maxLength: number): string => {\n if (str.length <= maxLength) return str;\n return str.slice(0, maxLength) + '...';\n};\n\nexport const formatTime = (date: Date): string => {\n return date.toLocaleTimeString('en-US', {\n hour12: false,\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n });\n};\n","export const getStatusColor = (status?: number): string => {\n if (!status) return '#9ca3af'; // gray-400\n if (status >= 200 && status < 300) return '#4ade80'; // green-400\n if (status >= 300 && status < 400) return '#facc15'; // yellow-400\n if (status >= 400 && status < 500) return '#fb923c'; // orange-400\n return '#f87171'; // red-400\n};\n\nexport const getMethodColor = (method: string): string => {\n const colors: Record<string, string> = {\n GET: '#22d3ee', // cyan-400\n POST: '#4ade80', // green-400\n PUT: '#facc15', // yellow-400\n PATCH: '#fb923c', // orange-400\n DELETE: '#f87171', // red-400\n };\n return colors[method.toUpperCase()] || '#9ca3af';\n};\n","import React from 'react';\nimport { LogEntryProps } from '../types';\nimport { formatJson, formatTime } from '../utils/formatters';\nimport { getStatusColor, getMethodColor } from '../utils/colors';\n\nconst styles = {\n container: {\n marginBottom: '16px',\n borderBottom: '1px solid #1f2937',\n paddingBottom: '16px',\n },\n header: {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n marginBottom: '4px',\n flexWrap: 'wrap' as const,\n },\n timestamp: {\n color: '#6b7280',\n fontSize: '12px',\n fontFamily: 'monospace',\n },\n method: {\n fontWeight: 'bold',\n fontFamily: 'monospace',\n },\n status: {\n fontWeight: 'bold',\n fontFamily: 'monospace',\n },\n duration: {\n color: '#6b7280',\n fontSize: '12px',\n fontFamily: 'monospace',\n },\n url: {\n color: '#60a5fa',\n wordBreak: 'break-all' as const,\n marginBottom: '8px',\n fontFamily: 'monospace',\n fontSize: '13px',\n },\n bodyContainer: {\n marginTop: '8px',\n },\n bodyLabel: {\n color: '#a78bfa',\n fontSize: '12px',\n marginBottom: '4px',\n fontFamily: 'monospace',\n },\n pre: {\n backgroundColor: '#1f2937',\n padding: '8px',\n borderRadius: '4px',\n color: '#86efac',\n overflowX: 'auto' as const,\n fontSize: '12px',\n whiteSpace: 'pre-wrap' as const,\n fontFamily: 'monospace',\n maxHeight: '192px',\n overflowY: 'auto' as const,\n margin: 0,\n },\n errorPre: {\n backgroundColor: '#1f2937',\n padding: '8px',\n borderRadius: '4px',\n color: '#fca5a5',\n overflowX: 'auto' as const,\n fontSize: '12px',\n whiteSpace: 'pre-wrap' as const,\n fontFamily: 'monospace',\n margin: 0,\n },\n} as const;\n\nexport const LogEntry: React.FC<LogEntryProps> = ({ log, type }) => {\n return (\n <div style={styles.container}>\n <div style={styles.header}>\n <span style={styles.timestamp}>[{formatTime(log.timestamp)}]</span>\n <span style={{ ...styles.method, color: getMethodColor(log.method) }}>\n {log.method}\n </span>\n {type === 'response' && log.status && (\n <span style={{ ...styles.status, color: getStatusColor(log.status) }}>\n {log.status} {log.statusText}\n </span>\n )}\n {log.duration !== undefined && type === 'response' && (\n <span style={styles.duration}>({log.duration}ms)</span>\n )}\n </div>\n\n <div style={styles.url}>{log.url}</div>\n\n {type === 'request' ? (\n log.requestBody ? (\n <div style={styles.bodyContainer}>\n <div style={styles.bodyLabel}>{'\\u25B8'} Payload:</div>\n <pre style={styles.pre}>{formatJson(log.requestBody)}</pre>\n </div>\n ) : null\n ) : log.error ? (\n <div style={styles.bodyContainer}>\n <div style={{ ...styles.bodyLabel, color: '#f87171' }}>\n {'\\u25B8'} Error:\n </div>\n <pre style={styles.errorPre}>{log.error}</pre>\n </div>\n ) : log.responseBody ? (\n <div style={styles.bodyContainer}>\n <div style={styles.bodyLabel}>{'\\u25B8'} Response:</div>\n <pre style={styles.pre}>{formatJson(log.responseBody)}</pre>\n </div>\n ) : null}\n </div>\n );\n};\n","import { useEffect, useRef, useCallback } from 'react';\nimport { NetworkLog, UseNetworkInterceptorProps } from '../types';\n\nexport const useNetworkInterceptor = ({\n enabled,\n onLogAdd,\n onLogUpdate,\n}: UseNetworkInterceptorProps) => {\n const originalFetch = useRef<typeof fetch | null>(null);\n const originalXHROpen = useRef<typeof XMLHttpRequest.prototype.open | null>(null);\n const originalXHRSend = useRef<typeof XMLHttpRequest.prototype.send | null>(null);\n\n const generateId = useCallback((prefix: string) => {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n }, []);\n\n // Intercept Fetch\n useEffect(() => {\n if (!enabled) return;\n\n originalFetch.current = window.fetch;\n\n window.fetch = async function (input, init) {\n const id = generateId('fetch');\n const startTime = performance.now();\n\n const url =\n typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.href\n : input.url;\n\n const method = init?.method || 'GET';\n\n let requestBody: unknown = null;\n if (init?.body) {\n try {\n requestBody =\n typeof init.body === 'string' ? JSON.parse(init.body) : init.body;\n } catch {\n requestBody = init.body;\n }\n }\n\n onLogAdd({\n id,\n timestamp: new Date(),\n method: method.toUpperCase(),\n url,\n requestBody,\n type: 'fetch',\n });\n\n try {\n const response = await originalFetch.current!(input, init);\n const duration = Math.round(performance.now() - startTime);\n\n const clonedResponse = response.clone();\n let responseBody: unknown = null;\n\n try {\n responseBody = await clonedResponse.json();\n } catch {\n try {\n responseBody = await clonedResponse.text();\n } catch {\n responseBody = '[Could not read response body]';\n }\n }\n\n onLogUpdate(id, {\n status: response.status,\n statusText: response.statusText,\n responseBody,\n duration,\n });\n\n return response;\n } catch (error: unknown) {\n const duration = Math.round(performance.now() - startTime);\n const errorMessage =\n error instanceof Error ? error.message : 'Network Error';\n onLogUpdate(id, {\n error: errorMessage,\n duration,\n });\n throw error;\n }\n };\n\n return () => {\n if (originalFetch.current) {\n window.fetch = originalFetch.current;\n }\n };\n }, [enabled, onLogAdd, onLogUpdate, generateId]);\n\n // Intercept XHR\n useEffect(() => {\n if (!enabled) return;\n\n originalXHROpen.current = XMLHttpRequest.prototype.open;\n originalXHRSend.current = XMLHttpRequest.prototype.send;\n\n XMLHttpRequest.prototype.open = function (\n method: string,\n url: string | URL\n ) {\n (this as XMLHttpRequest & { _networkTerminal: NetworkLog & { startTime: number } })._networkTerminal = {\n id: generateId('xhr'),\n method: method.toUpperCase(),\n url: url.toString(),\n startTime: 0,\n timestamp: new Date(),\n type: 'xhr',\n };\n return originalXHROpen.current!.apply(\n this,\n arguments as unknown as Parameters<typeof XMLHttpRequest.prototype.open>\n );\n };\n\n XMLHttpRequest.prototype.send = function (body?: Document | XMLHttpRequestBodyInit | null) {\n const meta = (this as XMLHttpRequest & { _networkTerminal?: NetworkLog & { startTime: number } })._networkTerminal;\n\n if (meta) {\n meta.startTime = performance.now();\n\n let requestBody: unknown = null;\n if (body) {\n try {\n requestBody = typeof body === 'string' ? JSON.parse(body) : body;\n } catch {\n requestBody = body;\n }\n }\n\n onLogAdd({\n id: meta.id,\n timestamp: new Date(),\n method: meta.method,\n url: meta.url,\n requestBody,\n type: 'xhr',\n });\n\n this.addEventListener('load', function () {\n const duration = Math.round(performance.now() - meta.startTime);\n\n let responseBody: unknown = null;\n try {\n responseBody = JSON.parse(this.responseText);\n } catch {\n responseBody = this.responseText;\n }\n\n onLogUpdate(meta.id, {\n status: this.status,\n statusText: this.statusText,\n responseBody,\n duration,\n });\n });\n\n this.addEventListener('error', function () {\n const duration = Math.round(performance.now() - meta.startTime);\n onLogUpdate(meta.id, {\n error: 'Network Error',\n duration,\n });\n });\n }\n\n return originalXHRSend.current!.apply(\n this,\n arguments as unknown as Parameters<typeof XMLHttpRequest.prototype.send>\n );\n };\n\n return () => {\n if (originalXHROpen.current) {\n XMLHttpRequest.prototype.open = originalXHROpen.current;\n }\n if (originalXHRSend.current) {\n XMLHttpRequest.prototype.send = originalXHRSend.current;\n }\n };\n }, [enabled, onLogAdd, onLogUpdate, generateId]);\n};\n","import type { Plugin, ViteDevServer } from 'vite';\nimport { createServer, IncomingMessage, ServerResponse } from 'http';\nimport { exec } from 'child_process';\n\nexport interface NetworkTerminalPluginOptions {\n /** Port for the dashboard (default: 4001) */\n port?: number;\n /** Auto-open dashboard in browser (default: true) */\n open?: boolean;\n}\n\n// Store network logs\nconst networkLogs: any[] = [];\nconst sseClients = new Set<ServerResponse>();\n\nfunction broadcastLog(log: any) {\n const data = JSON.stringify(log);\n sseClients.forEach(client => {\n client.write(`data: ${data}\\n\\n`);\n });\n}\n\nfunction getDashboardHTML(_port: number): string {\n return `\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Network Terminal</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body {\n font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;\n background: #0a0e17;\n color: #e2e8f0;\n height: 100vh;\n overflow: hidden;\n }\n .header {\n background: #0f172a;\n padding: 12px 24px;\n border-bottom: 2px solid #4ade80;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n .title { color: #4ade80; font-size: 18px; font-weight: bold; }\n .subtitle { color: #64748b; font-size: 12px; margin-top: 4px; }\n .controls { display: flex; gap: 12px; align-items: center; }\n .btn {\n background: #1e293b;\n border: 1px solid #334155;\n color: #94a3b8;\n padding: 8px 16px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 12px;\n font-family: inherit;\n }\n .btn:hover { background: #334155; color: #e2e8f0; }\n .container { display: flex; height: calc(100vh - 80px); }\n .panel {\n flex: 1;\n display: flex;\n flex-direction: column;\n border-right: 1px solid #1e293b;\n }\n .panel:last-child { border-right: none; }\n .panel-header {\n background: #0f172a;\n padding: 12px 16px;\n border-bottom: 1px solid #1e293b;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n .panel-title { font-weight: bold; font-size: 14px; }\n .panel-title.request { color: #3b82f6; }\n .panel-title.response { color: #22c55e; }\n .panel-count {\n background: #1e293b;\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n color: #64748b;\n }\n .logs { flex: 1; overflow-y: auto; padding: 8px; }\n .log-entry {\n background: #0f172a;\n border: 1px solid #1e293b;\n border-radius: 8px;\n margin-bottom: 8px;\n overflow: hidden;\n cursor: pointer;\n transition: border-color 0.2s;\n }\n .log-entry:hover { border-color: #334155; }\n .log-entry.selected { border-color: #4ade80; }\n .log-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 12px;\n background: #1e293b;\n }\n .method {\n font-weight: bold;\n font-size: 11px;\n padding: 3px 8px;\n border-radius: 4px;\n }\n .method.GET { background: #166534; color: #4ade80; }\n .method.POST { background: #1e40af; color: #60a5fa; }\n .method.PUT { background: #a16207; color: #fcd34d; }\n .method.DELETE { background: #991b1b; color: #fca5a5; }\n .method.PATCH { background: #7c3aed; color: #c4b5fd; }\n .status { font-size: 11px; padding: 3px 8px; border-radius: 4px; }\n .status.success { background: #166534; color: #4ade80; }\n .status.error { background: #991b1b; color: #fca5a5; }\n .status.pending { background: #374151; color: #9ca3af; }\n .url {\n flex: 1;\n font-size: 12px;\n color: #94a3b8;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .duration { font-size: 11px; color: #64748b; }\n .timestamp { font-size: 10px; color: #475569; }\n .log-body { padding: 12px; font-size: 11px; max-height: 300px; overflow: auto; }\n .log-body pre {\n margin: 0;\n white-space: pre-wrap;\n word-break: break-all;\n color: #cbd5e1;\n line-height: 1.5;\n }\n .log-body.empty { color: #475569; font-style: italic; }\n .empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: #475569;\n text-align: center;\n padding: 20px;\n }\n .empty-state svg { width: 48px; height: 48px; margin-bottom: 16px; opacity: 0.5; }\n .status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #22c55e;\n animation: pulse 2s infinite;\n }\n @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }\n .status-dot.disconnected { background: #ef4444; animation: none; }\n </style>\n</head>\n<body>\n <div class=\"header\">\n <div>\n <div class=\"title\">>_ Network Terminal</div>\n <div class=\"subtitle\">Vite Plugin - Monitoring fetch/XHR requests</div>\n </div>\n <div class=\"controls\">\n <div class=\"status-dot\" id=\"statusDot\"></div>\n <button class=\"btn\" onclick=\"clearLogs()\">Clear All</button>\n </div>\n </div>\n\n <div class=\"container\">\n <div class=\"panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title request\">⬆ REQUESTS</span>\n <span class=\"panel-count\" id=\"requestCount\">0</span>\n </div>\n <div class=\"logs\" id=\"requestLogs\">\n <div class=\"empty-state\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M12 19V5M5 12l7-7 7 7\"/>\n </svg>\n <div>Waiting for requests...</div>\n <div style=\"margin-top: 8px; font-size: 11px;\">Make fetch/XHR requests in your app</div>\n </div>\n </div>\n </div>\n\n <div class=\"panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title response\">⬇ RESPONSES</span>\n <span class=\"panel-count\" id=\"responseCount\">0</span>\n </div>\n <div class=\"logs\" id=\"responseLogs\">\n <div class=\"empty-state\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M12 5v14M5 12l7 7 7-7\"/>\n </svg>\n <div>Waiting for responses...</div>\n </div>\n </div>\n </div>\n </div>\n\n <script>\n const logs = new Map();\n let selectedId = null;\n\n function formatJson(data) {\n if (!data) return null;\n if (typeof data === 'string') {\n try { data = JSON.parse(data); }\n catch (e) { return escapeHtml(data.substring(0, 2000)); }\n }\n return escapeHtml(JSON.stringify(data, null, 2));\n }\n\n function escapeHtml(str) {\n if (typeof str !== 'string') str = String(str);\n return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');\n }\n\n function formatTime(timestamp) {\n const date = new Date(timestamp);\n return date.toLocaleTimeString('en-US', { hour12: false }) + '.' +\n String(date.getMilliseconds()).padStart(3, '0');\n }\n\n function getLogsArray() {\n return Array.from(logs.values()).sort((a, b) =>\n new Date(b.timestamp) - new Date(a.timestamp)\n );\n }\n\n function renderLogs() {\n const logsArray = getLogsArray();\n const requestContainer = document.getElementById('requestLogs');\n const responseContainer = document.getElementById('responseLogs');\n\n document.getElementById('requestCount').textContent = logsArray.length;\n document.getElementById('responseCount').textContent = logsArray.filter(l => l.status).length;\n\n if (logsArray.length === 0) {\n requestContainer.innerHTML = '<div class=\"empty-state\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M12 19V5M5 12l7-7 7 7\"/></svg><div>Waiting for requests...</div></div>';\n responseContainer.innerHTML = '<div class=\"empty-state\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M12 5v14M5 12l7 7 7-7\"/></svg><div>Waiting for responses...</div></div>';\n return;\n }\n\n requestContainer.innerHTML = logsArray.map(log => {\n const isSelected = selectedId === log.id;\n const bodyContent = formatJson(log.requestBody);\n return '<div class=\"log-entry '+(isSelected ? 'selected' : '')+'\" onclick=\"selectLog(\\\\''+log.id+'\\\\')\"><div class=\"log-header\"><span class=\"method '+log.method+'\">'+log.method+'</span><span class=\"url\" title=\"'+escapeHtml(log.url)+'\">'+log.url+'</span><span class=\"timestamp\">'+formatTime(log.timestamp)+'</span></div>'+(isSelected ? '<div class=\"log-body '+(bodyContent ? '' : 'empty')+'\">'+(bodyContent ? '<pre>'+bodyContent+'</pre>' : 'No request body')+'</div>' : '')+'</div>';\n }).join('');\n\n responseContainer.innerHTML = logsArray.map(log => {\n const isSelected = selectedId === log.id;\n const statusClass = !log.status ? 'pending' : log.status < 400 ? 'success' : 'error';\n const bodyContent = formatJson(log.responseBody);\n return '<div class=\"log-entry '+(isSelected ? 'selected' : '')+'\" onclick=\"selectLog(\\\\''+log.id+'\\\\')\"><div class=\"log-header\"><span class=\"status '+statusClass+'\">'+(log.status || '...')+'</span><span class=\"url\" title=\"'+escapeHtml(log.url)+'\">'+log.url+'</span><span class=\"duration\">'+(log.duration ? log.duration + 'ms' : '')+'</span></div>'+(isSelected ? '<div class=\"log-body '+(bodyContent ? '' : 'empty')+'\">'+(bodyContent ? '<pre>'+bodyContent+'</pre>' : (log.status ? 'No response body' : 'Pending...'))+'</div>' : '')+'</div>';\n }).join('');\n }\n\n function selectLog(id) {\n selectedId = selectedId === id ? null : id;\n renderLogs();\n }\n\n function clearLogs() {\n logs.clear();\n selectedId = null;\n renderLogs();\n fetch('/clear', { method: 'POST' });\n }\n\n function addLog(log) {\n logs.set(log.id, log);\n if (logs.size > 100) {\n const oldest = getLogsArray().pop();\n if (oldest) logs.delete(oldest.id);\n }\n renderLogs();\n }\n\n function connect() {\n const evtSource = new EventSource('/events');\n evtSource.onopen = () => document.getElementById('statusDot').classList.remove('disconnected');\n evtSource.onmessage = (event) => addLog(JSON.parse(event.data));\n evtSource.onerror = () => {\n document.getElementById('statusDot').classList.add('disconnected');\n evtSource.close();\n setTimeout(connect, 2000);\n };\n }\n\n connect();\n renderLogs();\n </script>\n</body>\n</html>\n`;\n}\n\nfunction getMonitorScript(port: number): string {\n return `\n(function() {\n if (window.__networkTerminalActive) return;\n window.__networkTerminalActive = true;\n\n var DASHBOARD_URL = 'http://localhost:${port}';\n\n function sendLog(log) {\n var xhr = new XMLHttpRequest();\n xhr.open('POST', DASHBOARD_URL + '/log', true);\n xhr.setRequestHeader('Content-Type', 'application/json');\n xhr.send(JSON.stringify(log));\n }\n\n function tryParse(data) {\n if (!data) return null;\n if (typeof data === 'object') return data;\n try { return JSON.parse(data); } catch (e) { return data; }\n }\n\n var originalFetch = window.fetch;\n window.fetch = function() {\n var args = arguments;\n var startTime = Date.now();\n var input = args[0];\n var options = args[1] || {};\n var url = typeof input === 'string' ? input : (input.url || input.toString());\n var method = (options.method || 'GET').toUpperCase();\n\n if (url.includes('localhost:${port}')) {\n return originalFetch.apply(this, args);\n }\n\n var log = {\n id: Date.now().toString() + Math.random().toString(36).substr(2, 9),\n method: method,\n url: url,\n timestamp: new Date().toISOString(),\n requestBody: options.body ? tryParse(options.body) : null,\n status: null,\n responseBody: null,\n duration: null,\n };\n\n sendLog(log);\n\n return originalFetch.apply(this, args).then(function(response) {\n log.status = response.status;\n log.duration = Date.now() - startTime;\n var cloned = response.clone();\n cloned.text().then(function(text) {\n log.responseBody = tryParse(text);\n sendLog(log);\n }).catch(function() { sendLog(log); });\n return response;\n }).catch(function(error) {\n log.status = 0;\n log.duration = Date.now() - startTime;\n log.error = error.message;\n sendLog(log);\n throw error;\n });\n };\n\n var OriginalXHR = window.XMLHttpRequest;\n window.XMLHttpRequest = function() {\n var xhr = new OriginalXHR();\n var method, url, requestBody, startTime;\n\n var originalOpen = xhr.open;\n xhr.open = function(m, u) {\n method = m;\n url = u;\n return originalOpen.apply(this, arguments);\n };\n\n var originalSend = xhr.send;\n xhr.send = function(body) {\n if (url && url.includes('localhost:${port}')) {\n return originalSend.apply(this, arguments);\n }\n\n startTime = Date.now();\n requestBody = body;\n\n var log = {\n id: Date.now().toString() + Math.random().toString(36).substr(2, 9),\n method: (method || 'GET').toUpperCase(),\n url: url,\n timestamp: new Date().toISOString(),\n requestBody: tryParse(requestBody),\n status: null,\n responseBody: null,\n duration: null,\n };\n\n sendLog(log);\n\n xhr.addEventListener('loadend', function() {\n log.status = xhr.status;\n log.duration = Date.now() - startTime;\n log.responseBody = tryParse(xhr.responseText);\n sendLog(log);\n });\n\n return originalSend.apply(this, arguments);\n };\n\n return xhr;\n };\n\n console.log('[Network Terminal] ✓ Active - Dashboard: http://localhost:${port}');\n})();\n`;\n}\n\nexport function networkTerminal(options: NetworkTerminalPluginOptions = {}): Plugin {\n const { port = 4001, open = true } = options;\n let dashboardServer: ReturnType<typeof createServer> | null = null;\n\n return {\n name: 'vite-plugin-network-terminal',\n apply: 'serve',\n\n configureServer(_server: ViteDevServer) {\n // Start dashboard server\n dashboardServer = createServer((req: IncomingMessage, res: ServerResponse) => {\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n if (req.url === '/events') {\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n });\n sseClients.add(res);\n networkLogs.forEach(log => {\n res.write(`data: ${JSON.stringify(log)}\\n\\n`);\n });\n req.on('close', () => sseClients.delete(res));\n return;\n }\n\n if (req.url === '/log' && req.method === 'POST') {\n let body = '';\n req.on('data', (chunk: Buffer) => body += chunk);\n req.on('end', () => {\n try {\n const log = JSON.parse(body);\n const existingIndex = networkLogs.findIndex(l => l.id === log.id);\n if (existingIndex >= 0) {\n networkLogs[existingIndex] = log;\n } else {\n networkLogs.unshift(log);\n if (networkLogs.length > 100) networkLogs.pop();\n }\n broadcastLog(log);\n } catch (e) {}\n res.writeHead(200);\n res.end('OK');\n });\n return;\n }\n\n if (req.url === '/clear' && req.method === 'POST') {\n networkLogs.length = 0;\n res.writeHead(200);\n res.end('OK');\n return;\n }\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getDashboardHTML(port));\n });\n\n dashboardServer.listen(port, () => {\n console.log(`\\n \\x1b[32m>_ Network Terminal\\x1b[0m`);\n console.log(` Dashboard: \\x1b[36mhttp://localhost:${port}\\x1b[0m\\n`);\n\n if (open) {\n const openCmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';\n exec(`${openCmd} http://localhost:${port}`);\n }\n });\n\n dashboardServer.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n console.log(`\\n \\x1b[33m[Network Terminal]\\x1b[0m Port ${port} in use, dashboard disabled\\n`);\n }\n });\n },\n\n transformIndexHtml(html: string) {\n const script = `<script>${getMonitorScript(port)}</script>`;\n if (html.includes('</head>')) {\n return html.replace('</head>', script + '</head>');\n }\n return script + html;\n },\n\n buildEnd() {\n if (dashboardServer) {\n dashboardServer.close();\n }\n },\n };\n}\n\nexport default networkTerminal;\n"],"mappings":"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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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","names":["import_react","import_jsx_runtime","styles","import_jsx_runtime","styles","import_react","import_jsx_runtime","styles"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
-
}) : x)(function(x) {
|
|
4
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
-
});
|
|
7
|
-
|
|
8
1
|
// src/components/NetworkTerminal.tsx
|
|
9
2
|
import { useState as useState2, useCallback as useCallback2, useEffect as useEffect3 } from "react";
|
|
10
3
|
|
|
@@ -718,6 +711,7 @@ var NetworkTerminal = ({
|
|
|
718
711
|
|
|
719
712
|
// src/vite-plugin.ts
|
|
720
713
|
import { createServer } from "http";
|
|
714
|
+
import { exec } from "child_process";
|
|
721
715
|
var networkLogs = [];
|
|
722
716
|
var sseClients = /* @__PURE__ */ new Set();
|
|
723
717
|
function broadcastLog(log) {
|
|
@@ -1194,7 +1188,7 @@ function networkTerminal(options = {}) {
|
|
|
1194
1188
|
`);
|
|
1195
1189
|
if (open) {
|
|
1196
1190
|
const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
1197
|
-
|
|
1191
|
+
exec(`${openCmd} http://localhost:${port}`);
|
|
1198
1192
|
}
|
|
1199
1193
|
});
|
|
1200
1194
|
dashboardServer.on("error", (err) => {
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/NetworkTerminal.tsx","../src/components/Terminal.tsx","../src/components/TerminalHeader.tsx","../src/utils/formatters.ts","../src/utils/colors.ts","../src/components/LogEntry.tsx","../src/hooks/useNetworkInterceptor.ts","../src/vite-plugin.ts"],"sourcesContent":["import React, { useState, useCallback, useEffect } from 'react';\nimport { NetworkLog, NetworkTerminalProps } from '../types';\nimport { Terminal } from './Terminal';\nimport { useNetworkInterceptor } from '../hooks/useNetworkInterceptor';\n\nconst styles = {\n floatingButton: {\n position: 'fixed' as const,\n bottom: '16px',\n right: '16px',\n zIndex: 9999,\n backgroundColor: '#1f2937',\n color: '#4ade80',\n padding: '8px 16px',\n borderRadius: '8px',\n fontFamily: 'monospace',\n fontSize: '14px',\n boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1)',\n border: '1px solid #374151',\n cursor: 'pointer',\n },\n container: {\n position: 'fixed' as const,\n bottom: 0,\n left: 0,\n right: 0,\n zIndex: 9999,\n backgroundColor: '#111827',\n borderTop: '2px solid #22c55e',\n boxShadow: '0 -10px 15px -3px rgba(0, 0, 0, 0.1)',\n },\n containerTop: {\n position: 'fixed' as const,\n top: 0,\n left: 0,\n right: 0,\n zIndex: 9999,\n backgroundColor: '#111827',\n borderBottom: '2px solid #22c55e',\n boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1)',\n },\n mainHeader: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '8px 16px',\n backgroundColor: '#1f2937',\n borderBottom: '1px solid #374151',\n },\n headerLeft: {\n display: 'flex',\n alignItems: 'center',\n gap: '12px',\n },\n title: {\n color: '#4ade80',\n fontFamily: 'monospace',\n fontWeight: 'bold',\n },\n hint: {\n color: '#6b7280',\n fontFamily: 'monospace',\n fontSize: '12px',\n },\n headerRight: {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n },\n clearButton: {\n color: '#9ca3af',\n fontSize: '14px',\n fontFamily: 'monospace',\n padding: '4px 12px',\n borderRadius: '4px',\n border: 'none',\n background: 'transparent',\n cursor: 'pointer',\n },\n closeButton: {\n color: '#9ca3af',\n fontSize: '18px',\n fontWeight: 'bold',\n padding: '0 8px',\n border: 'none',\n background: 'transparent',\n cursor: 'pointer',\n borderRadius: '4px',\n },\n terminalsContainer: {\n display: 'flex',\n gap: '8px',\n padding: '8px',\n height: '450px',\n },\n terminalWrapper: {\n flex: 1,\n display: 'flex',\n flexDirection: 'column' as const,\n },\n} as const;\n\nexport const NetworkTerminal: React.FC<NetworkTerminalProps> = ({\n maxLogs = 100,\n defaultVisible = false,\n position = 'bottom',\n height = '450px',\n zIndex = 9999,\n}) => {\n const [logs, setLogs] = useState<NetworkLog[]>([]);\n const [isVisible, setIsVisible] = useState(defaultVisible);\n const [requestExpanded, setRequestExpanded] = useState(true);\n const [responseExpanded, setResponseExpanded] = useState(true);\n\n const addLog = useCallback(\n (log: NetworkLog) => {\n setLogs((prev) => [...prev.slice(-(maxLogs - 1)), log]);\n },\n [maxLogs]\n );\n\n const updateLog = useCallback((id: string, updates: Partial<NetworkLog>) => {\n setLogs((prev) =>\n prev.map((log) => (log.id === id ? { ...log, ...updates } : log))\n );\n }, []);\n\n useNetworkInterceptor({\n enabled: isVisible,\n onLogAdd: addLog,\n onLogUpdate: updateLog,\n });\n\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.ctrlKey && e.shiftKey && e.key === 'N') {\n e.preventDefault();\n setIsVisible((prev) => !prev);\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, []);\n\n const clearLogs = () => setLogs([]);\n\n if (!isVisible) {\n return (\n <button\n onClick={() => setIsVisible(true)}\n style={{\n ...styles.floatingButton,\n zIndex,\n }}\n title=\"Open Network Terminal (Ctrl+Shift+N)\"\n >\n {'>'}_ Network\n </button>\n );\n }\n\n const containerStyle =\n position === 'top'\n ? { ...styles.containerTop, zIndex }\n : { ...styles.container, zIndex };\n\n return (\n <div style={containerStyle}>\n <div style={styles.mainHeader}>\n <div style={styles.headerLeft}>\n <span style={styles.title}>{'>'}_ Network Terminal</span>\n <span style={styles.hint}>Press Ctrl+Shift+N to toggle</span>\n </div>\n <div style={styles.headerRight}>\n <button\n onClick={clearLogs}\n style={styles.clearButton}\n onMouseEnter={(e) => (e.currentTarget.style.color = '#fff')}\n onMouseLeave={(e) => (e.currentTarget.style.color = '#9ca3af')}\n >\n Clear All\n </button>\n <button\n onClick={() => setIsVisible(false)}\n style={styles.closeButton}\n onMouseEnter={(e) => (e.currentTarget.style.color = '#f87171')}\n onMouseLeave={(e) => (e.currentTarget.style.color = '#9ca3af')}\n >\n {'\\u00D7'}\n </button>\n </div>\n </div>\n\n <div style={{ ...styles.terminalsContainer, height }}>\n <div style={styles.terminalWrapper}>\n <Terminal\n title=\"Requests\"\n logs={logs}\n type=\"request\"\n onClear={clearLogs}\n expanded={requestExpanded}\n onToggleExpand={() => setRequestExpanded(!requestExpanded)}\n />\n </div>\n <div style={styles.terminalWrapper}>\n <Terminal\n title=\"Responses\"\n logs={logs}\n type=\"response\"\n onClear={clearLogs}\n expanded={responseExpanded}\n onToggleExpand={() => setResponseExpanded(!responseExpanded)}\n />\n </div>\n </div>\n </div>\n );\n};\n","import React, { useRef, useState, useEffect } from 'react';\nimport { TerminalProps } from '../types';\nimport { TerminalHeader } from './TerminalHeader';\nimport { LogEntry } from './LogEntry';\n\nconst styles = {\n container: {\n display: 'flex',\n flexDirection: 'column' as const,\n backgroundColor: '#111827',\n borderRadius: '8px',\n border: '1px solid #374151',\n overflow: 'hidden',\n transition: 'all 0.2s',\n flex: 1,\n },\n collapsed: {\n height: '48px',\n flex: 'none' as const,\n },\n body: {\n flex: 1,\n overflow: 'auto',\n padding: '16px',\n fontFamily: 'monospace',\n fontSize: '14px',\n minHeight: '200px',\n maxHeight: '400px',\n },\n empty: {\n color: '#6b7280',\n fontStyle: 'italic',\n },\n} as const;\n\nexport const Terminal: React.FC<TerminalProps> = ({\n title,\n logs,\n type,\n onClear,\n expanded,\n onToggleExpand,\n}) => {\n const terminalRef = useRef<HTMLDivElement>(null);\n const [autoScroll, setAutoScroll] = useState(true);\n\n useEffect(() => {\n if (autoScroll && terminalRef.current) {\n terminalRef.current.scrollTop = terminalRef.current.scrollHeight;\n }\n }, [logs, autoScroll]);\n\n const handleScroll = () => {\n if (terminalRef.current) {\n const { scrollTop, scrollHeight, clientHeight } = terminalRef.current;\n setAutoScroll(scrollHeight - scrollTop - clientHeight < 50);\n }\n };\n\n return (\n <div\n style={{\n ...styles.container,\n ...(expanded ? {} : styles.collapsed),\n }}\n >\n <TerminalHeader\n title={title}\n count={logs.length}\n expanded={expanded}\n onClear={onClear}\n onToggleExpand={onToggleExpand}\n />\n\n {expanded && (\n <div ref={terminalRef} onScroll={handleScroll} style={styles.body}>\n {logs.length === 0 ? (\n <div style={styles.empty}>Waiting for network requests...</div>\n ) : (\n logs.map((log) => <LogEntry key={log.id} log={log} type={type} />)\n )}\n </div>\n )}\n </div>\n );\n};\n","import React from 'react';\nimport { TerminalHeaderProps } from '../types';\n\nconst styles = {\n header: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '8px 16px',\n backgroundColor: '#1f2937',\n borderBottom: '1px solid #374151',\n },\n left: {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n },\n trafficLights: {\n display: 'flex',\n gap: '6px',\n },\n dot: {\n width: '12px',\n height: '12px',\n borderRadius: '50%',\n },\n title: {\n color: '#d1d5db',\n fontFamily: 'monospace',\n fontSize: '14px',\n marginLeft: '8px',\n },\n count: {\n color: '#6b7280',\n fontFamily: 'monospace',\n fontSize: '12px',\n },\n right: {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n },\n button: {\n color: '#9ca3af',\n fontSize: '12px',\n fontFamily: 'monospace',\n padding: '4px 8px',\n borderRadius: '4px',\n border: 'none',\n background: 'transparent',\n cursor: 'pointer',\n },\n} as const;\n\nexport const TerminalHeader: React.FC<TerminalHeaderProps> = ({\n title,\n count,\n expanded,\n onClear,\n onToggleExpand,\n}) => {\n return (\n <div style={styles.header}>\n <div style={styles.left}>\n <div style={styles.trafficLights}>\n <div style={{ ...styles.dot, backgroundColor: '#ef4444' }} />\n <div style={{ ...styles.dot, backgroundColor: '#eab308' }} />\n <div style={{ ...styles.dot, backgroundColor: '#22c55e' }} />\n </div>\n <span style={styles.title}>{title}</span>\n <span style={styles.count}>({count} entries)</span>\n </div>\n <div style={styles.right}>\n <button\n onClick={onClear}\n style={styles.button}\n onMouseEnter={(e) => (e.currentTarget.style.color = '#fff')}\n onMouseLeave={(e) => (e.currentTarget.style.color = '#9ca3af')}\n >\n Clear\n </button>\n <button\n onClick={onToggleExpand}\n style={styles.button}\n onMouseEnter={(e) => (e.currentTarget.style.color = '#fff')}\n onMouseLeave={(e) => (e.currentTarget.style.color = '#9ca3af')}\n >\n {expanded ? '\\u25BC' : '\\u25B2'}\n </button>\n </div>\n </div>\n );\n};\n","export const formatJson = (data: unknown): string => {\n if (!data) return '';\n try {\n if (typeof data === 'string') {\n const parsed = JSON.parse(data);\n return JSON.stringify(parsed, null, 2);\n }\n return JSON.stringify(data, null, 2);\n } catch {\n return String(data);\n }\n};\n\nexport const truncate = (str: string, maxLength: number): string => {\n if (str.length <= maxLength) return str;\n return str.slice(0, maxLength) + '...';\n};\n\nexport const formatTime = (date: Date): string => {\n return date.toLocaleTimeString('en-US', {\n hour12: false,\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n });\n};\n","export const getStatusColor = (status?: number): string => {\n if (!status) return '#9ca3af'; // gray-400\n if (status >= 200 && status < 300) return '#4ade80'; // green-400\n if (status >= 300 && status < 400) return '#facc15'; // yellow-400\n if (status >= 400 && status < 500) return '#fb923c'; // orange-400\n return '#f87171'; // red-400\n};\n\nexport const getMethodColor = (method: string): string => {\n const colors: Record<string, string> = {\n GET: '#22d3ee', // cyan-400\n POST: '#4ade80', // green-400\n PUT: '#facc15', // yellow-400\n PATCH: '#fb923c', // orange-400\n DELETE: '#f87171', // red-400\n };\n return colors[method.toUpperCase()] || '#9ca3af';\n};\n","import React from 'react';\nimport { LogEntryProps } from '../types';\nimport { formatJson, formatTime } from '../utils/formatters';\nimport { getStatusColor, getMethodColor } from '../utils/colors';\n\nconst styles = {\n container: {\n marginBottom: '16px',\n borderBottom: '1px solid #1f2937',\n paddingBottom: '16px',\n },\n header: {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n marginBottom: '4px',\n flexWrap: 'wrap' as const,\n },\n timestamp: {\n color: '#6b7280',\n fontSize: '12px',\n fontFamily: 'monospace',\n },\n method: {\n fontWeight: 'bold',\n fontFamily: 'monospace',\n },\n status: {\n fontWeight: 'bold',\n fontFamily: 'monospace',\n },\n duration: {\n color: '#6b7280',\n fontSize: '12px',\n fontFamily: 'monospace',\n },\n url: {\n color: '#60a5fa',\n wordBreak: 'break-all' as const,\n marginBottom: '8px',\n fontFamily: 'monospace',\n fontSize: '13px',\n },\n bodyContainer: {\n marginTop: '8px',\n },\n bodyLabel: {\n color: '#a78bfa',\n fontSize: '12px',\n marginBottom: '4px',\n fontFamily: 'monospace',\n },\n pre: {\n backgroundColor: '#1f2937',\n padding: '8px',\n borderRadius: '4px',\n color: '#86efac',\n overflowX: 'auto' as const,\n fontSize: '12px',\n whiteSpace: 'pre-wrap' as const,\n fontFamily: 'monospace',\n maxHeight: '192px',\n overflowY: 'auto' as const,\n margin: 0,\n },\n errorPre: {\n backgroundColor: '#1f2937',\n padding: '8px',\n borderRadius: '4px',\n color: '#fca5a5',\n overflowX: 'auto' as const,\n fontSize: '12px',\n whiteSpace: 'pre-wrap' as const,\n fontFamily: 'monospace',\n margin: 0,\n },\n} as const;\n\nexport const LogEntry: React.FC<LogEntryProps> = ({ log, type }) => {\n return (\n <div style={styles.container}>\n <div style={styles.header}>\n <span style={styles.timestamp}>[{formatTime(log.timestamp)}]</span>\n <span style={{ ...styles.method, color: getMethodColor(log.method) }}>\n {log.method}\n </span>\n {type === 'response' && log.status && (\n <span style={{ ...styles.status, color: getStatusColor(log.status) }}>\n {log.status} {log.statusText}\n </span>\n )}\n {log.duration !== undefined && type === 'response' && (\n <span style={styles.duration}>({log.duration}ms)</span>\n )}\n </div>\n\n <div style={styles.url}>{log.url}</div>\n\n {type === 'request' ? (\n log.requestBody ? (\n <div style={styles.bodyContainer}>\n <div style={styles.bodyLabel}>{'\\u25B8'} Payload:</div>\n <pre style={styles.pre}>{formatJson(log.requestBody)}</pre>\n </div>\n ) : null\n ) : log.error ? (\n <div style={styles.bodyContainer}>\n <div style={{ ...styles.bodyLabel, color: '#f87171' }}>\n {'\\u25B8'} Error:\n </div>\n <pre style={styles.errorPre}>{log.error}</pre>\n </div>\n ) : log.responseBody ? (\n <div style={styles.bodyContainer}>\n <div style={styles.bodyLabel}>{'\\u25B8'} Response:</div>\n <pre style={styles.pre}>{formatJson(log.responseBody)}</pre>\n </div>\n ) : null}\n </div>\n );\n};\n","import { useEffect, useRef, useCallback } from 'react';\nimport { NetworkLog, UseNetworkInterceptorProps } from '../types';\n\nexport const useNetworkInterceptor = ({\n enabled,\n onLogAdd,\n onLogUpdate,\n}: UseNetworkInterceptorProps) => {\n const originalFetch = useRef<typeof fetch | null>(null);\n const originalXHROpen = useRef<typeof XMLHttpRequest.prototype.open | null>(null);\n const originalXHRSend = useRef<typeof XMLHttpRequest.prototype.send | null>(null);\n\n const generateId = useCallback((prefix: string) => {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n }, []);\n\n // Intercept Fetch\n useEffect(() => {\n if (!enabled) return;\n\n originalFetch.current = window.fetch;\n\n window.fetch = async function (input, init) {\n const id = generateId('fetch');\n const startTime = performance.now();\n\n const url =\n typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.href\n : input.url;\n\n const method = init?.method || 'GET';\n\n let requestBody: unknown = null;\n if (init?.body) {\n try {\n requestBody =\n typeof init.body === 'string' ? JSON.parse(init.body) : init.body;\n } catch {\n requestBody = init.body;\n }\n }\n\n onLogAdd({\n id,\n timestamp: new Date(),\n method: method.toUpperCase(),\n url,\n requestBody,\n type: 'fetch',\n });\n\n try {\n const response = await originalFetch.current!(input, init);\n const duration = Math.round(performance.now() - startTime);\n\n const clonedResponse = response.clone();\n let responseBody: unknown = null;\n\n try {\n responseBody = await clonedResponse.json();\n } catch {\n try {\n responseBody = await clonedResponse.text();\n } catch {\n responseBody = '[Could not read response body]';\n }\n }\n\n onLogUpdate(id, {\n status: response.status,\n statusText: response.statusText,\n responseBody,\n duration,\n });\n\n return response;\n } catch (error: unknown) {\n const duration = Math.round(performance.now() - startTime);\n const errorMessage =\n error instanceof Error ? error.message : 'Network Error';\n onLogUpdate(id, {\n error: errorMessage,\n duration,\n });\n throw error;\n }\n };\n\n return () => {\n if (originalFetch.current) {\n window.fetch = originalFetch.current;\n }\n };\n }, [enabled, onLogAdd, onLogUpdate, generateId]);\n\n // Intercept XHR\n useEffect(() => {\n if (!enabled) return;\n\n originalXHROpen.current = XMLHttpRequest.prototype.open;\n originalXHRSend.current = XMLHttpRequest.prototype.send;\n\n XMLHttpRequest.prototype.open = function (\n method: string,\n url: string | URL\n ) {\n (this as XMLHttpRequest & { _networkTerminal: NetworkLog & { startTime: number } })._networkTerminal = {\n id: generateId('xhr'),\n method: method.toUpperCase(),\n url: url.toString(),\n startTime: 0,\n timestamp: new Date(),\n type: 'xhr',\n };\n return originalXHROpen.current!.apply(\n this,\n arguments as unknown as Parameters<typeof XMLHttpRequest.prototype.open>\n );\n };\n\n XMLHttpRequest.prototype.send = function (body?: Document | XMLHttpRequestBodyInit | null) {\n const meta = (this as XMLHttpRequest & { _networkTerminal?: NetworkLog & { startTime: number } })._networkTerminal;\n\n if (meta) {\n meta.startTime = performance.now();\n\n let requestBody: unknown = null;\n if (body) {\n try {\n requestBody = typeof body === 'string' ? JSON.parse(body) : body;\n } catch {\n requestBody = body;\n }\n }\n\n onLogAdd({\n id: meta.id,\n timestamp: new Date(),\n method: meta.method,\n url: meta.url,\n requestBody,\n type: 'xhr',\n });\n\n this.addEventListener('load', function () {\n const duration = Math.round(performance.now() - meta.startTime);\n\n let responseBody: unknown = null;\n try {\n responseBody = JSON.parse(this.responseText);\n } catch {\n responseBody = this.responseText;\n }\n\n onLogUpdate(meta.id, {\n status: this.status,\n statusText: this.statusText,\n responseBody,\n duration,\n });\n });\n\n this.addEventListener('error', function () {\n const duration = Math.round(performance.now() - meta.startTime);\n onLogUpdate(meta.id, {\n error: 'Network Error',\n duration,\n });\n });\n }\n\n return originalXHRSend.current!.apply(\n this,\n arguments as unknown as Parameters<typeof XMLHttpRequest.prototype.send>\n );\n };\n\n return () => {\n if (originalXHROpen.current) {\n XMLHttpRequest.prototype.open = originalXHROpen.current;\n }\n if (originalXHRSend.current) {\n XMLHttpRequest.prototype.send = originalXHRSend.current;\n }\n };\n }, [enabled, onLogAdd, onLogUpdate, generateId]);\n};\n","import type { Plugin, ViteDevServer } from 'vite';\nimport { createServer, IncomingMessage, ServerResponse } from 'http';\n\nexport interface NetworkTerminalPluginOptions {\n /** Port for the dashboard (default: 4001) */\n port?: number;\n /** Auto-open dashboard in browser (default: true) */\n open?: boolean;\n}\n\n// Store network logs\nconst networkLogs: any[] = [];\nconst sseClients = new Set<ServerResponse>();\n\nfunction broadcastLog(log: any) {\n const data = JSON.stringify(log);\n sseClients.forEach(client => {\n client.write(`data: ${data}\\n\\n`);\n });\n}\n\nfunction getDashboardHTML(_port: number): string {\n return `\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Network Terminal</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body {\n font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;\n background: #0a0e17;\n color: #e2e8f0;\n height: 100vh;\n overflow: hidden;\n }\n .header {\n background: #0f172a;\n padding: 12px 24px;\n border-bottom: 2px solid #4ade80;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n .title { color: #4ade80; font-size: 18px; font-weight: bold; }\n .subtitle { color: #64748b; font-size: 12px; margin-top: 4px; }\n .controls { display: flex; gap: 12px; align-items: center; }\n .btn {\n background: #1e293b;\n border: 1px solid #334155;\n color: #94a3b8;\n padding: 8px 16px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 12px;\n font-family: inherit;\n }\n .btn:hover { background: #334155; color: #e2e8f0; }\n .container { display: flex; height: calc(100vh - 80px); }\n .panel {\n flex: 1;\n display: flex;\n flex-direction: column;\n border-right: 1px solid #1e293b;\n }\n .panel:last-child { border-right: none; }\n .panel-header {\n background: #0f172a;\n padding: 12px 16px;\n border-bottom: 1px solid #1e293b;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n .panel-title { font-weight: bold; font-size: 14px; }\n .panel-title.request { color: #3b82f6; }\n .panel-title.response { color: #22c55e; }\n .panel-count {\n background: #1e293b;\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n color: #64748b;\n }\n .logs { flex: 1; overflow-y: auto; padding: 8px; }\n .log-entry {\n background: #0f172a;\n border: 1px solid #1e293b;\n border-radius: 8px;\n margin-bottom: 8px;\n overflow: hidden;\n cursor: pointer;\n transition: border-color 0.2s;\n }\n .log-entry:hover { border-color: #334155; }\n .log-entry.selected { border-color: #4ade80; }\n .log-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 12px;\n background: #1e293b;\n }\n .method {\n font-weight: bold;\n font-size: 11px;\n padding: 3px 8px;\n border-radius: 4px;\n }\n .method.GET { background: #166534; color: #4ade80; }\n .method.POST { background: #1e40af; color: #60a5fa; }\n .method.PUT { background: #a16207; color: #fcd34d; }\n .method.DELETE { background: #991b1b; color: #fca5a5; }\n .method.PATCH { background: #7c3aed; color: #c4b5fd; }\n .status { font-size: 11px; padding: 3px 8px; border-radius: 4px; }\n .status.success { background: #166534; color: #4ade80; }\n .status.error { background: #991b1b; color: #fca5a5; }\n .status.pending { background: #374151; color: #9ca3af; }\n .url {\n flex: 1;\n font-size: 12px;\n color: #94a3b8;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .duration { font-size: 11px; color: #64748b; }\n .timestamp { font-size: 10px; color: #475569; }\n .log-body { padding: 12px; font-size: 11px; max-height: 300px; overflow: auto; }\n .log-body pre {\n margin: 0;\n white-space: pre-wrap;\n word-break: break-all;\n color: #cbd5e1;\n line-height: 1.5;\n }\n .log-body.empty { color: #475569; font-style: italic; }\n .empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: #475569;\n text-align: center;\n padding: 20px;\n }\n .empty-state svg { width: 48px; height: 48px; margin-bottom: 16px; opacity: 0.5; }\n .status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #22c55e;\n animation: pulse 2s infinite;\n }\n @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }\n .status-dot.disconnected { background: #ef4444; animation: none; }\n </style>\n</head>\n<body>\n <div class=\"header\">\n <div>\n <div class=\"title\">>_ Network Terminal</div>\n <div class=\"subtitle\">Vite Plugin - Monitoring fetch/XHR requests</div>\n </div>\n <div class=\"controls\">\n <div class=\"status-dot\" id=\"statusDot\"></div>\n <button class=\"btn\" onclick=\"clearLogs()\">Clear All</button>\n </div>\n </div>\n\n <div class=\"container\">\n <div class=\"panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title request\">⬆ REQUESTS</span>\n <span class=\"panel-count\" id=\"requestCount\">0</span>\n </div>\n <div class=\"logs\" id=\"requestLogs\">\n <div class=\"empty-state\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M12 19V5M5 12l7-7 7 7\"/>\n </svg>\n <div>Waiting for requests...</div>\n <div style=\"margin-top: 8px; font-size: 11px;\">Make fetch/XHR requests in your app</div>\n </div>\n </div>\n </div>\n\n <div class=\"panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title response\">⬇ RESPONSES</span>\n <span class=\"panel-count\" id=\"responseCount\">0</span>\n </div>\n <div class=\"logs\" id=\"responseLogs\">\n <div class=\"empty-state\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M12 5v14M5 12l7 7 7-7\"/>\n </svg>\n <div>Waiting for responses...</div>\n </div>\n </div>\n </div>\n </div>\n\n <script>\n const logs = new Map();\n let selectedId = null;\n\n function formatJson(data) {\n if (!data) return null;\n if (typeof data === 'string') {\n try { data = JSON.parse(data); }\n catch (e) { return escapeHtml(data.substring(0, 2000)); }\n }\n return escapeHtml(JSON.stringify(data, null, 2));\n }\n\n function escapeHtml(str) {\n if (typeof str !== 'string') str = String(str);\n return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');\n }\n\n function formatTime(timestamp) {\n const date = new Date(timestamp);\n return date.toLocaleTimeString('en-US', { hour12: false }) + '.' +\n String(date.getMilliseconds()).padStart(3, '0');\n }\n\n function getLogsArray() {\n return Array.from(logs.values()).sort((a, b) =>\n new Date(b.timestamp) - new Date(a.timestamp)\n );\n }\n\n function renderLogs() {\n const logsArray = getLogsArray();\n const requestContainer = document.getElementById('requestLogs');\n const responseContainer = document.getElementById('responseLogs');\n\n document.getElementById('requestCount').textContent = logsArray.length;\n document.getElementById('responseCount').textContent = logsArray.filter(l => l.status).length;\n\n if (logsArray.length === 0) {\n requestContainer.innerHTML = '<div class=\"empty-state\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M12 19V5M5 12l7-7 7 7\"/></svg><div>Waiting for requests...</div></div>';\n responseContainer.innerHTML = '<div class=\"empty-state\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M12 5v14M5 12l7 7 7-7\"/></svg><div>Waiting for responses...</div></div>';\n return;\n }\n\n requestContainer.innerHTML = logsArray.map(log => {\n const isSelected = selectedId === log.id;\n const bodyContent = formatJson(log.requestBody);\n return '<div class=\"log-entry '+(isSelected ? 'selected' : '')+'\" onclick=\"selectLog(\\\\''+log.id+'\\\\')\"><div class=\"log-header\"><span class=\"method '+log.method+'\">'+log.method+'</span><span class=\"url\" title=\"'+escapeHtml(log.url)+'\">'+log.url+'</span><span class=\"timestamp\">'+formatTime(log.timestamp)+'</span></div>'+(isSelected ? '<div class=\"log-body '+(bodyContent ? '' : 'empty')+'\">'+(bodyContent ? '<pre>'+bodyContent+'</pre>' : 'No request body')+'</div>' : '')+'</div>';\n }).join('');\n\n responseContainer.innerHTML = logsArray.map(log => {\n const isSelected = selectedId === log.id;\n const statusClass = !log.status ? 'pending' : log.status < 400 ? 'success' : 'error';\n const bodyContent = formatJson(log.responseBody);\n return '<div class=\"log-entry '+(isSelected ? 'selected' : '')+'\" onclick=\"selectLog(\\\\''+log.id+'\\\\')\"><div class=\"log-header\"><span class=\"status '+statusClass+'\">'+(log.status || '...')+'</span><span class=\"url\" title=\"'+escapeHtml(log.url)+'\">'+log.url+'</span><span class=\"duration\">'+(log.duration ? log.duration + 'ms' : '')+'</span></div>'+(isSelected ? '<div class=\"log-body '+(bodyContent ? '' : 'empty')+'\">'+(bodyContent ? '<pre>'+bodyContent+'</pre>' : (log.status ? 'No response body' : 'Pending...'))+'</div>' : '')+'</div>';\n }).join('');\n }\n\n function selectLog(id) {\n selectedId = selectedId === id ? null : id;\n renderLogs();\n }\n\n function clearLogs() {\n logs.clear();\n selectedId = null;\n renderLogs();\n fetch('/clear', { method: 'POST' });\n }\n\n function addLog(log) {\n logs.set(log.id, log);\n if (logs.size > 100) {\n const oldest = getLogsArray().pop();\n if (oldest) logs.delete(oldest.id);\n }\n renderLogs();\n }\n\n function connect() {\n const evtSource = new EventSource('/events');\n evtSource.onopen = () => document.getElementById('statusDot').classList.remove('disconnected');\n evtSource.onmessage = (event) => addLog(JSON.parse(event.data));\n evtSource.onerror = () => {\n document.getElementById('statusDot').classList.add('disconnected');\n evtSource.close();\n setTimeout(connect, 2000);\n };\n }\n\n connect();\n renderLogs();\n </script>\n</body>\n</html>\n`;\n}\n\nfunction getMonitorScript(port: number): string {\n return `\n(function() {\n if (window.__networkTerminalActive) return;\n window.__networkTerminalActive = true;\n\n var DASHBOARD_URL = 'http://localhost:${port}';\n\n function sendLog(log) {\n var xhr = new XMLHttpRequest();\n xhr.open('POST', DASHBOARD_URL + '/log', true);\n xhr.setRequestHeader('Content-Type', 'application/json');\n xhr.send(JSON.stringify(log));\n }\n\n function tryParse(data) {\n if (!data) return null;\n if (typeof data === 'object') return data;\n try { return JSON.parse(data); } catch (e) { return data; }\n }\n\n var originalFetch = window.fetch;\n window.fetch = function() {\n var args = arguments;\n var startTime = Date.now();\n var input = args[0];\n var options = args[1] || {};\n var url = typeof input === 'string' ? input : (input.url || input.toString());\n var method = (options.method || 'GET').toUpperCase();\n\n if (url.includes('localhost:${port}')) {\n return originalFetch.apply(this, args);\n }\n\n var log = {\n id: Date.now().toString() + Math.random().toString(36).substr(2, 9),\n method: method,\n url: url,\n timestamp: new Date().toISOString(),\n requestBody: options.body ? tryParse(options.body) : null,\n status: null,\n responseBody: null,\n duration: null,\n };\n\n sendLog(log);\n\n return originalFetch.apply(this, args).then(function(response) {\n log.status = response.status;\n log.duration = Date.now() - startTime;\n var cloned = response.clone();\n cloned.text().then(function(text) {\n log.responseBody = tryParse(text);\n sendLog(log);\n }).catch(function() { sendLog(log); });\n return response;\n }).catch(function(error) {\n log.status = 0;\n log.duration = Date.now() - startTime;\n log.error = error.message;\n sendLog(log);\n throw error;\n });\n };\n\n var OriginalXHR = window.XMLHttpRequest;\n window.XMLHttpRequest = function() {\n var xhr = new OriginalXHR();\n var method, url, requestBody, startTime;\n\n var originalOpen = xhr.open;\n xhr.open = function(m, u) {\n method = m;\n url = u;\n return originalOpen.apply(this, arguments);\n };\n\n var originalSend = xhr.send;\n xhr.send = function(body) {\n if (url && url.includes('localhost:${port}')) {\n return originalSend.apply(this, arguments);\n }\n\n startTime = Date.now();\n requestBody = body;\n\n var log = {\n id: Date.now().toString() + Math.random().toString(36).substr(2, 9),\n method: (method || 'GET').toUpperCase(),\n url: url,\n timestamp: new Date().toISOString(),\n requestBody: tryParse(requestBody),\n status: null,\n responseBody: null,\n duration: null,\n };\n\n sendLog(log);\n\n xhr.addEventListener('loadend', function() {\n log.status = xhr.status;\n log.duration = Date.now() - startTime;\n log.responseBody = tryParse(xhr.responseText);\n sendLog(log);\n });\n\n return originalSend.apply(this, arguments);\n };\n\n return xhr;\n };\n\n console.log('[Network Terminal] ✓ Active - Dashboard: http://localhost:${port}');\n})();\n`;\n}\n\nexport function networkTerminal(options: NetworkTerminalPluginOptions = {}): Plugin {\n const { port = 4001, open = true } = options;\n let dashboardServer: ReturnType<typeof createServer> | null = null;\n\n return {\n name: 'vite-plugin-network-terminal',\n apply: 'serve',\n\n configureServer(_server: ViteDevServer) {\n // Start dashboard server\n dashboardServer = createServer((req: IncomingMessage, res: ServerResponse) => {\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n if (req.url === '/events') {\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n });\n sseClients.add(res);\n networkLogs.forEach(log => {\n res.write(`data: ${JSON.stringify(log)}\\n\\n`);\n });\n req.on('close', () => sseClients.delete(res));\n return;\n }\n\n if (req.url === '/log' && req.method === 'POST') {\n let body = '';\n req.on('data', (chunk: Buffer) => body += chunk);\n req.on('end', () => {\n try {\n const log = JSON.parse(body);\n const existingIndex = networkLogs.findIndex(l => l.id === log.id);\n if (existingIndex >= 0) {\n networkLogs[existingIndex] = log;\n } else {\n networkLogs.unshift(log);\n if (networkLogs.length > 100) networkLogs.pop();\n }\n broadcastLog(log);\n } catch (e) {}\n res.writeHead(200);\n res.end('OK');\n });\n return;\n }\n\n if (req.url === '/clear' && req.method === 'POST') {\n networkLogs.length = 0;\n res.writeHead(200);\n res.end('OK');\n return;\n }\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getDashboardHTML(port));\n });\n\n dashboardServer.listen(port, () => {\n console.log(`\\n \\x1b[32m>_ Network Terminal\\x1b[0m`);\n console.log(` Dashboard: \\x1b[36mhttp://localhost:${port}\\x1b[0m\\n`);\n\n if (open) {\n const openCmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';\n require('child_process').exec(`${openCmd} http://localhost:${port}`);\n }\n });\n\n dashboardServer.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n console.log(`\\n \\x1b[33m[Network Terminal]\\x1b[0m Port ${port} in use, dashboard disabled\\n`);\n }\n });\n },\n\n transformIndexHtml(html: string) {\n const script = `<script>${getMonitorScript(port)}</script>`;\n if (html.includes('</head>')) {\n return html.replace('</head>', script + '</head>');\n }\n return script + html;\n },\n\n buildEnd() {\n if (dashboardServer) {\n dashboardServer.close();\n }\n },\n };\n}\n\nexport default networkTerminal;\n"],"mappings":"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","names":["useState","useCallback","useEffect","jsx","jsxs","styles","jsx","jsxs","styles","useEffect","useRef","jsx","jsxs","styles","useState","useCallback","useEffect"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/NetworkTerminal.tsx","../src/components/Terminal.tsx","../src/components/TerminalHeader.tsx","../src/utils/formatters.ts","../src/utils/colors.ts","../src/components/LogEntry.tsx","../src/hooks/useNetworkInterceptor.ts","../src/vite-plugin.ts"],"sourcesContent":["import React, { useState, useCallback, useEffect } from 'react';\nimport { NetworkLog, NetworkTerminalProps } from '../types';\nimport { Terminal } from './Terminal';\nimport { useNetworkInterceptor } from '../hooks/useNetworkInterceptor';\n\nconst styles = {\n floatingButton: {\n position: 'fixed' as const,\n bottom: '16px',\n right: '16px',\n zIndex: 9999,\n backgroundColor: '#1f2937',\n color: '#4ade80',\n padding: '8px 16px',\n borderRadius: '8px',\n fontFamily: 'monospace',\n fontSize: '14px',\n boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1)',\n border: '1px solid #374151',\n cursor: 'pointer',\n },\n container: {\n position: 'fixed' as const,\n bottom: 0,\n left: 0,\n right: 0,\n zIndex: 9999,\n backgroundColor: '#111827',\n borderTop: '2px solid #22c55e',\n boxShadow: '0 -10px 15px -3px rgba(0, 0, 0, 0.1)',\n },\n containerTop: {\n position: 'fixed' as const,\n top: 0,\n left: 0,\n right: 0,\n zIndex: 9999,\n backgroundColor: '#111827',\n borderBottom: '2px solid #22c55e',\n boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1)',\n },\n mainHeader: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '8px 16px',\n backgroundColor: '#1f2937',\n borderBottom: '1px solid #374151',\n },\n headerLeft: {\n display: 'flex',\n alignItems: 'center',\n gap: '12px',\n },\n title: {\n color: '#4ade80',\n fontFamily: 'monospace',\n fontWeight: 'bold',\n },\n hint: {\n color: '#6b7280',\n fontFamily: 'monospace',\n fontSize: '12px',\n },\n headerRight: {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n },\n clearButton: {\n color: '#9ca3af',\n fontSize: '14px',\n fontFamily: 'monospace',\n padding: '4px 12px',\n borderRadius: '4px',\n border: 'none',\n background: 'transparent',\n cursor: 'pointer',\n },\n closeButton: {\n color: '#9ca3af',\n fontSize: '18px',\n fontWeight: 'bold',\n padding: '0 8px',\n border: 'none',\n background: 'transparent',\n cursor: 'pointer',\n borderRadius: '4px',\n },\n terminalsContainer: {\n display: 'flex',\n gap: '8px',\n padding: '8px',\n height: '450px',\n },\n terminalWrapper: {\n flex: 1,\n display: 'flex',\n flexDirection: 'column' as const,\n },\n} as const;\n\nexport const NetworkTerminal: React.FC<NetworkTerminalProps> = ({\n maxLogs = 100,\n defaultVisible = false,\n position = 'bottom',\n height = '450px',\n zIndex = 9999,\n}) => {\n const [logs, setLogs] = useState<NetworkLog[]>([]);\n const [isVisible, setIsVisible] = useState(defaultVisible);\n const [requestExpanded, setRequestExpanded] = useState(true);\n const [responseExpanded, setResponseExpanded] = useState(true);\n\n const addLog = useCallback(\n (log: NetworkLog) => {\n setLogs((prev) => [...prev.slice(-(maxLogs - 1)), log]);\n },\n [maxLogs]\n );\n\n const updateLog = useCallback((id: string, updates: Partial<NetworkLog>) => {\n setLogs((prev) =>\n prev.map((log) => (log.id === id ? { ...log, ...updates } : log))\n );\n }, []);\n\n useNetworkInterceptor({\n enabled: isVisible,\n onLogAdd: addLog,\n onLogUpdate: updateLog,\n });\n\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.ctrlKey && e.shiftKey && e.key === 'N') {\n e.preventDefault();\n setIsVisible((prev) => !prev);\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, []);\n\n const clearLogs = () => setLogs([]);\n\n if (!isVisible) {\n return (\n <button\n onClick={() => setIsVisible(true)}\n style={{\n ...styles.floatingButton,\n zIndex,\n }}\n title=\"Open Network Terminal (Ctrl+Shift+N)\"\n >\n {'>'}_ Network\n </button>\n );\n }\n\n const containerStyle =\n position === 'top'\n ? { ...styles.containerTop, zIndex }\n : { ...styles.container, zIndex };\n\n return (\n <div style={containerStyle}>\n <div style={styles.mainHeader}>\n <div style={styles.headerLeft}>\n <span style={styles.title}>{'>'}_ Network Terminal</span>\n <span style={styles.hint}>Press Ctrl+Shift+N to toggle</span>\n </div>\n <div style={styles.headerRight}>\n <button\n onClick={clearLogs}\n style={styles.clearButton}\n onMouseEnter={(e) => (e.currentTarget.style.color = '#fff')}\n onMouseLeave={(e) => (e.currentTarget.style.color = '#9ca3af')}\n >\n Clear All\n </button>\n <button\n onClick={() => setIsVisible(false)}\n style={styles.closeButton}\n onMouseEnter={(e) => (e.currentTarget.style.color = '#f87171')}\n onMouseLeave={(e) => (e.currentTarget.style.color = '#9ca3af')}\n >\n {'\\u00D7'}\n </button>\n </div>\n </div>\n\n <div style={{ ...styles.terminalsContainer, height }}>\n <div style={styles.terminalWrapper}>\n <Terminal\n title=\"Requests\"\n logs={logs}\n type=\"request\"\n onClear={clearLogs}\n expanded={requestExpanded}\n onToggleExpand={() => setRequestExpanded(!requestExpanded)}\n />\n </div>\n <div style={styles.terminalWrapper}>\n <Terminal\n title=\"Responses\"\n logs={logs}\n type=\"response\"\n onClear={clearLogs}\n expanded={responseExpanded}\n onToggleExpand={() => setResponseExpanded(!responseExpanded)}\n />\n </div>\n </div>\n </div>\n );\n};\n","import React, { useRef, useState, useEffect } from 'react';\nimport { TerminalProps } from '../types';\nimport { TerminalHeader } from './TerminalHeader';\nimport { LogEntry } from './LogEntry';\n\nconst styles = {\n container: {\n display: 'flex',\n flexDirection: 'column' as const,\n backgroundColor: '#111827',\n borderRadius: '8px',\n border: '1px solid #374151',\n overflow: 'hidden',\n transition: 'all 0.2s',\n flex: 1,\n },\n collapsed: {\n height: '48px',\n flex: 'none' as const,\n },\n body: {\n flex: 1,\n overflow: 'auto',\n padding: '16px',\n fontFamily: 'monospace',\n fontSize: '14px',\n minHeight: '200px',\n maxHeight: '400px',\n },\n empty: {\n color: '#6b7280',\n fontStyle: 'italic',\n },\n} as const;\n\nexport const Terminal: React.FC<TerminalProps> = ({\n title,\n logs,\n type,\n onClear,\n expanded,\n onToggleExpand,\n}) => {\n const terminalRef = useRef<HTMLDivElement>(null);\n const [autoScroll, setAutoScroll] = useState(true);\n\n useEffect(() => {\n if (autoScroll && terminalRef.current) {\n terminalRef.current.scrollTop = terminalRef.current.scrollHeight;\n }\n }, [logs, autoScroll]);\n\n const handleScroll = () => {\n if (terminalRef.current) {\n const { scrollTop, scrollHeight, clientHeight } = terminalRef.current;\n setAutoScroll(scrollHeight - scrollTop - clientHeight < 50);\n }\n };\n\n return (\n <div\n style={{\n ...styles.container,\n ...(expanded ? {} : styles.collapsed),\n }}\n >\n <TerminalHeader\n title={title}\n count={logs.length}\n expanded={expanded}\n onClear={onClear}\n onToggleExpand={onToggleExpand}\n />\n\n {expanded && (\n <div ref={terminalRef} onScroll={handleScroll} style={styles.body}>\n {logs.length === 0 ? (\n <div style={styles.empty}>Waiting for network requests...</div>\n ) : (\n logs.map((log) => <LogEntry key={log.id} log={log} type={type} />)\n )}\n </div>\n )}\n </div>\n );\n};\n","import React from 'react';\nimport { TerminalHeaderProps } from '../types';\n\nconst styles = {\n header: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '8px 16px',\n backgroundColor: '#1f2937',\n borderBottom: '1px solid #374151',\n },\n left: {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n },\n trafficLights: {\n display: 'flex',\n gap: '6px',\n },\n dot: {\n width: '12px',\n height: '12px',\n borderRadius: '50%',\n },\n title: {\n color: '#d1d5db',\n fontFamily: 'monospace',\n fontSize: '14px',\n marginLeft: '8px',\n },\n count: {\n color: '#6b7280',\n fontFamily: 'monospace',\n fontSize: '12px',\n },\n right: {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n },\n button: {\n color: '#9ca3af',\n fontSize: '12px',\n fontFamily: 'monospace',\n padding: '4px 8px',\n borderRadius: '4px',\n border: 'none',\n background: 'transparent',\n cursor: 'pointer',\n },\n} as const;\n\nexport const TerminalHeader: React.FC<TerminalHeaderProps> = ({\n title,\n count,\n expanded,\n onClear,\n onToggleExpand,\n}) => {\n return (\n <div style={styles.header}>\n <div style={styles.left}>\n <div style={styles.trafficLights}>\n <div style={{ ...styles.dot, backgroundColor: '#ef4444' }} />\n <div style={{ ...styles.dot, backgroundColor: '#eab308' }} />\n <div style={{ ...styles.dot, backgroundColor: '#22c55e' }} />\n </div>\n <span style={styles.title}>{title}</span>\n <span style={styles.count}>({count} entries)</span>\n </div>\n <div style={styles.right}>\n <button\n onClick={onClear}\n style={styles.button}\n onMouseEnter={(e) => (e.currentTarget.style.color = '#fff')}\n onMouseLeave={(e) => (e.currentTarget.style.color = '#9ca3af')}\n >\n Clear\n </button>\n <button\n onClick={onToggleExpand}\n style={styles.button}\n onMouseEnter={(e) => (e.currentTarget.style.color = '#fff')}\n onMouseLeave={(e) => (e.currentTarget.style.color = '#9ca3af')}\n >\n {expanded ? '\\u25BC' : '\\u25B2'}\n </button>\n </div>\n </div>\n );\n};\n","export const formatJson = (data: unknown): string => {\n if (!data) return '';\n try {\n if (typeof data === 'string') {\n const parsed = JSON.parse(data);\n return JSON.stringify(parsed, null, 2);\n }\n return JSON.stringify(data, null, 2);\n } catch {\n return String(data);\n }\n};\n\nexport const truncate = (str: string, maxLength: number): string => {\n if (str.length <= maxLength) return str;\n return str.slice(0, maxLength) + '...';\n};\n\nexport const formatTime = (date: Date): string => {\n return date.toLocaleTimeString('en-US', {\n hour12: false,\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n });\n};\n","export const getStatusColor = (status?: number): string => {\n if (!status) return '#9ca3af'; // gray-400\n if (status >= 200 && status < 300) return '#4ade80'; // green-400\n if (status >= 300 && status < 400) return '#facc15'; // yellow-400\n if (status >= 400 && status < 500) return '#fb923c'; // orange-400\n return '#f87171'; // red-400\n};\n\nexport const getMethodColor = (method: string): string => {\n const colors: Record<string, string> = {\n GET: '#22d3ee', // cyan-400\n POST: '#4ade80', // green-400\n PUT: '#facc15', // yellow-400\n PATCH: '#fb923c', // orange-400\n DELETE: '#f87171', // red-400\n };\n return colors[method.toUpperCase()] || '#9ca3af';\n};\n","import React from 'react';\nimport { LogEntryProps } from '../types';\nimport { formatJson, formatTime } from '../utils/formatters';\nimport { getStatusColor, getMethodColor } from '../utils/colors';\n\nconst styles = {\n container: {\n marginBottom: '16px',\n borderBottom: '1px solid #1f2937',\n paddingBottom: '16px',\n },\n header: {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n marginBottom: '4px',\n flexWrap: 'wrap' as const,\n },\n timestamp: {\n color: '#6b7280',\n fontSize: '12px',\n fontFamily: 'monospace',\n },\n method: {\n fontWeight: 'bold',\n fontFamily: 'monospace',\n },\n status: {\n fontWeight: 'bold',\n fontFamily: 'monospace',\n },\n duration: {\n color: '#6b7280',\n fontSize: '12px',\n fontFamily: 'monospace',\n },\n url: {\n color: '#60a5fa',\n wordBreak: 'break-all' as const,\n marginBottom: '8px',\n fontFamily: 'monospace',\n fontSize: '13px',\n },\n bodyContainer: {\n marginTop: '8px',\n },\n bodyLabel: {\n color: '#a78bfa',\n fontSize: '12px',\n marginBottom: '4px',\n fontFamily: 'monospace',\n },\n pre: {\n backgroundColor: '#1f2937',\n padding: '8px',\n borderRadius: '4px',\n color: '#86efac',\n overflowX: 'auto' as const,\n fontSize: '12px',\n whiteSpace: 'pre-wrap' as const,\n fontFamily: 'monospace',\n maxHeight: '192px',\n overflowY: 'auto' as const,\n margin: 0,\n },\n errorPre: {\n backgroundColor: '#1f2937',\n padding: '8px',\n borderRadius: '4px',\n color: '#fca5a5',\n overflowX: 'auto' as const,\n fontSize: '12px',\n whiteSpace: 'pre-wrap' as const,\n fontFamily: 'monospace',\n margin: 0,\n },\n} as const;\n\nexport const LogEntry: React.FC<LogEntryProps> = ({ log, type }) => {\n return (\n <div style={styles.container}>\n <div style={styles.header}>\n <span style={styles.timestamp}>[{formatTime(log.timestamp)}]</span>\n <span style={{ ...styles.method, color: getMethodColor(log.method) }}>\n {log.method}\n </span>\n {type === 'response' && log.status && (\n <span style={{ ...styles.status, color: getStatusColor(log.status) }}>\n {log.status} {log.statusText}\n </span>\n )}\n {log.duration !== undefined && type === 'response' && (\n <span style={styles.duration}>({log.duration}ms)</span>\n )}\n </div>\n\n <div style={styles.url}>{log.url}</div>\n\n {type === 'request' ? (\n log.requestBody ? (\n <div style={styles.bodyContainer}>\n <div style={styles.bodyLabel}>{'\\u25B8'} Payload:</div>\n <pre style={styles.pre}>{formatJson(log.requestBody)}</pre>\n </div>\n ) : null\n ) : log.error ? (\n <div style={styles.bodyContainer}>\n <div style={{ ...styles.bodyLabel, color: '#f87171' }}>\n {'\\u25B8'} Error:\n </div>\n <pre style={styles.errorPre}>{log.error}</pre>\n </div>\n ) : log.responseBody ? (\n <div style={styles.bodyContainer}>\n <div style={styles.bodyLabel}>{'\\u25B8'} Response:</div>\n <pre style={styles.pre}>{formatJson(log.responseBody)}</pre>\n </div>\n ) : null}\n </div>\n );\n};\n","import { useEffect, useRef, useCallback } from 'react';\nimport { NetworkLog, UseNetworkInterceptorProps } from '../types';\n\nexport const useNetworkInterceptor = ({\n enabled,\n onLogAdd,\n onLogUpdate,\n}: UseNetworkInterceptorProps) => {\n const originalFetch = useRef<typeof fetch | null>(null);\n const originalXHROpen = useRef<typeof XMLHttpRequest.prototype.open | null>(null);\n const originalXHRSend = useRef<typeof XMLHttpRequest.prototype.send | null>(null);\n\n const generateId = useCallback((prefix: string) => {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n }, []);\n\n // Intercept Fetch\n useEffect(() => {\n if (!enabled) return;\n\n originalFetch.current = window.fetch;\n\n window.fetch = async function (input, init) {\n const id = generateId('fetch');\n const startTime = performance.now();\n\n const url =\n typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.href\n : input.url;\n\n const method = init?.method || 'GET';\n\n let requestBody: unknown = null;\n if (init?.body) {\n try {\n requestBody =\n typeof init.body === 'string' ? JSON.parse(init.body) : init.body;\n } catch {\n requestBody = init.body;\n }\n }\n\n onLogAdd({\n id,\n timestamp: new Date(),\n method: method.toUpperCase(),\n url,\n requestBody,\n type: 'fetch',\n });\n\n try {\n const response = await originalFetch.current!(input, init);\n const duration = Math.round(performance.now() - startTime);\n\n const clonedResponse = response.clone();\n let responseBody: unknown = null;\n\n try {\n responseBody = await clonedResponse.json();\n } catch {\n try {\n responseBody = await clonedResponse.text();\n } catch {\n responseBody = '[Could not read response body]';\n }\n }\n\n onLogUpdate(id, {\n status: response.status,\n statusText: response.statusText,\n responseBody,\n duration,\n });\n\n return response;\n } catch (error: unknown) {\n const duration = Math.round(performance.now() - startTime);\n const errorMessage =\n error instanceof Error ? error.message : 'Network Error';\n onLogUpdate(id, {\n error: errorMessage,\n duration,\n });\n throw error;\n }\n };\n\n return () => {\n if (originalFetch.current) {\n window.fetch = originalFetch.current;\n }\n };\n }, [enabled, onLogAdd, onLogUpdate, generateId]);\n\n // Intercept XHR\n useEffect(() => {\n if (!enabled) return;\n\n originalXHROpen.current = XMLHttpRequest.prototype.open;\n originalXHRSend.current = XMLHttpRequest.prototype.send;\n\n XMLHttpRequest.prototype.open = function (\n method: string,\n url: string | URL\n ) {\n (this as XMLHttpRequest & { _networkTerminal: NetworkLog & { startTime: number } })._networkTerminal = {\n id: generateId('xhr'),\n method: method.toUpperCase(),\n url: url.toString(),\n startTime: 0,\n timestamp: new Date(),\n type: 'xhr',\n };\n return originalXHROpen.current!.apply(\n this,\n arguments as unknown as Parameters<typeof XMLHttpRequest.prototype.open>\n );\n };\n\n XMLHttpRequest.prototype.send = function (body?: Document | XMLHttpRequestBodyInit | null) {\n const meta = (this as XMLHttpRequest & { _networkTerminal?: NetworkLog & { startTime: number } })._networkTerminal;\n\n if (meta) {\n meta.startTime = performance.now();\n\n let requestBody: unknown = null;\n if (body) {\n try {\n requestBody = typeof body === 'string' ? JSON.parse(body) : body;\n } catch {\n requestBody = body;\n }\n }\n\n onLogAdd({\n id: meta.id,\n timestamp: new Date(),\n method: meta.method,\n url: meta.url,\n requestBody,\n type: 'xhr',\n });\n\n this.addEventListener('load', function () {\n const duration = Math.round(performance.now() - meta.startTime);\n\n let responseBody: unknown = null;\n try {\n responseBody = JSON.parse(this.responseText);\n } catch {\n responseBody = this.responseText;\n }\n\n onLogUpdate(meta.id, {\n status: this.status,\n statusText: this.statusText,\n responseBody,\n duration,\n });\n });\n\n this.addEventListener('error', function () {\n const duration = Math.round(performance.now() - meta.startTime);\n onLogUpdate(meta.id, {\n error: 'Network Error',\n duration,\n });\n });\n }\n\n return originalXHRSend.current!.apply(\n this,\n arguments as unknown as Parameters<typeof XMLHttpRequest.prototype.send>\n );\n };\n\n return () => {\n if (originalXHROpen.current) {\n XMLHttpRequest.prototype.open = originalXHROpen.current;\n }\n if (originalXHRSend.current) {\n XMLHttpRequest.prototype.send = originalXHRSend.current;\n }\n };\n }, [enabled, onLogAdd, onLogUpdate, generateId]);\n};\n","import type { Plugin, ViteDevServer } from 'vite';\nimport { createServer, IncomingMessage, ServerResponse } from 'http';\nimport { exec } from 'child_process';\n\nexport interface NetworkTerminalPluginOptions {\n /** Port for the dashboard (default: 4001) */\n port?: number;\n /** Auto-open dashboard in browser (default: true) */\n open?: boolean;\n}\n\n// Store network logs\nconst networkLogs: any[] = [];\nconst sseClients = new Set<ServerResponse>();\n\nfunction broadcastLog(log: any) {\n const data = JSON.stringify(log);\n sseClients.forEach(client => {\n client.write(`data: ${data}\\n\\n`);\n });\n}\n\nfunction getDashboardHTML(_port: number): string {\n return `\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Network Terminal</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body {\n font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;\n background: #0a0e17;\n color: #e2e8f0;\n height: 100vh;\n overflow: hidden;\n }\n .header {\n background: #0f172a;\n padding: 12px 24px;\n border-bottom: 2px solid #4ade80;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n .title { color: #4ade80; font-size: 18px; font-weight: bold; }\n .subtitle { color: #64748b; font-size: 12px; margin-top: 4px; }\n .controls { display: flex; gap: 12px; align-items: center; }\n .btn {\n background: #1e293b;\n border: 1px solid #334155;\n color: #94a3b8;\n padding: 8px 16px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 12px;\n font-family: inherit;\n }\n .btn:hover { background: #334155; color: #e2e8f0; }\n .container { display: flex; height: calc(100vh - 80px); }\n .panel {\n flex: 1;\n display: flex;\n flex-direction: column;\n border-right: 1px solid #1e293b;\n }\n .panel:last-child { border-right: none; }\n .panel-header {\n background: #0f172a;\n padding: 12px 16px;\n border-bottom: 1px solid #1e293b;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n .panel-title { font-weight: bold; font-size: 14px; }\n .panel-title.request { color: #3b82f6; }\n .panel-title.response { color: #22c55e; }\n .panel-count {\n background: #1e293b;\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n color: #64748b;\n }\n .logs { flex: 1; overflow-y: auto; padding: 8px; }\n .log-entry {\n background: #0f172a;\n border: 1px solid #1e293b;\n border-radius: 8px;\n margin-bottom: 8px;\n overflow: hidden;\n cursor: pointer;\n transition: border-color 0.2s;\n }\n .log-entry:hover { border-color: #334155; }\n .log-entry.selected { border-color: #4ade80; }\n .log-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 12px;\n background: #1e293b;\n }\n .method {\n font-weight: bold;\n font-size: 11px;\n padding: 3px 8px;\n border-radius: 4px;\n }\n .method.GET { background: #166534; color: #4ade80; }\n .method.POST { background: #1e40af; color: #60a5fa; }\n .method.PUT { background: #a16207; color: #fcd34d; }\n .method.DELETE { background: #991b1b; color: #fca5a5; }\n .method.PATCH { background: #7c3aed; color: #c4b5fd; }\n .status { font-size: 11px; padding: 3px 8px; border-radius: 4px; }\n .status.success { background: #166534; color: #4ade80; }\n .status.error { background: #991b1b; color: #fca5a5; }\n .status.pending { background: #374151; color: #9ca3af; }\n .url {\n flex: 1;\n font-size: 12px;\n color: #94a3b8;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .duration { font-size: 11px; color: #64748b; }\n .timestamp { font-size: 10px; color: #475569; }\n .log-body { padding: 12px; font-size: 11px; max-height: 300px; overflow: auto; }\n .log-body pre {\n margin: 0;\n white-space: pre-wrap;\n word-break: break-all;\n color: #cbd5e1;\n line-height: 1.5;\n }\n .log-body.empty { color: #475569; font-style: italic; }\n .empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: #475569;\n text-align: center;\n padding: 20px;\n }\n .empty-state svg { width: 48px; height: 48px; margin-bottom: 16px; opacity: 0.5; }\n .status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #22c55e;\n animation: pulse 2s infinite;\n }\n @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }\n .status-dot.disconnected { background: #ef4444; animation: none; }\n </style>\n</head>\n<body>\n <div class=\"header\">\n <div>\n <div class=\"title\">>_ Network Terminal</div>\n <div class=\"subtitle\">Vite Plugin - Monitoring fetch/XHR requests</div>\n </div>\n <div class=\"controls\">\n <div class=\"status-dot\" id=\"statusDot\"></div>\n <button class=\"btn\" onclick=\"clearLogs()\">Clear All</button>\n </div>\n </div>\n\n <div class=\"container\">\n <div class=\"panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title request\">⬆ REQUESTS</span>\n <span class=\"panel-count\" id=\"requestCount\">0</span>\n </div>\n <div class=\"logs\" id=\"requestLogs\">\n <div class=\"empty-state\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M12 19V5M5 12l7-7 7 7\"/>\n </svg>\n <div>Waiting for requests...</div>\n <div style=\"margin-top: 8px; font-size: 11px;\">Make fetch/XHR requests in your app</div>\n </div>\n </div>\n </div>\n\n <div class=\"panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title response\">⬇ RESPONSES</span>\n <span class=\"panel-count\" id=\"responseCount\">0</span>\n </div>\n <div class=\"logs\" id=\"responseLogs\">\n <div class=\"empty-state\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M12 5v14M5 12l7 7 7-7\"/>\n </svg>\n <div>Waiting for responses...</div>\n </div>\n </div>\n </div>\n </div>\n\n <script>\n const logs = new Map();\n let selectedId = null;\n\n function formatJson(data) {\n if (!data) return null;\n if (typeof data === 'string') {\n try { data = JSON.parse(data); }\n catch (e) { return escapeHtml(data.substring(0, 2000)); }\n }\n return escapeHtml(JSON.stringify(data, null, 2));\n }\n\n function escapeHtml(str) {\n if (typeof str !== 'string') str = String(str);\n return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');\n }\n\n function formatTime(timestamp) {\n const date = new Date(timestamp);\n return date.toLocaleTimeString('en-US', { hour12: false }) + '.' +\n String(date.getMilliseconds()).padStart(3, '0');\n }\n\n function getLogsArray() {\n return Array.from(logs.values()).sort((a, b) =>\n new Date(b.timestamp) - new Date(a.timestamp)\n );\n }\n\n function renderLogs() {\n const logsArray = getLogsArray();\n const requestContainer = document.getElementById('requestLogs');\n const responseContainer = document.getElementById('responseLogs');\n\n document.getElementById('requestCount').textContent = logsArray.length;\n document.getElementById('responseCount').textContent = logsArray.filter(l => l.status).length;\n\n if (logsArray.length === 0) {\n requestContainer.innerHTML = '<div class=\"empty-state\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M12 19V5M5 12l7-7 7 7\"/></svg><div>Waiting for requests...</div></div>';\n responseContainer.innerHTML = '<div class=\"empty-state\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M12 5v14M5 12l7 7 7-7\"/></svg><div>Waiting for responses...</div></div>';\n return;\n }\n\n requestContainer.innerHTML = logsArray.map(log => {\n const isSelected = selectedId === log.id;\n const bodyContent = formatJson(log.requestBody);\n return '<div class=\"log-entry '+(isSelected ? 'selected' : '')+'\" onclick=\"selectLog(\\\\''+log.id+'\\\\')\"><div class=\"log-header\"><span class=\"method '+log.method+'\">'+log.method+'</span><span class=\"url\" title=\"'+escapeHtml(log.url)+'\">'+log.url+'</span><span class=\"timestamp\">'+formatTime(log.timestamp)+'</span></div>'+(isSelected ? '<div class=\"log-body '+(bodyContent ? '' : 'empty')+'\">'+(bodyContent ? '<pre>'+bodyContent+'</pre>' : 'No request body')+'</div>' : '')+'</div>';\n }).join('');\n\n responseContainer.innerHTML = logsArray.map(log => {\n const isSelected = selectedId === log.id;\n const statusClass = !log.status ? 'pending' : log.status < 400 ? 'success' : 'error';\n const bodyContent = formatJson(log.responseBody);\n return '<div class=\"log-entry '+(isSelected ? 'selected' : '')+'\" onclick=\"selectLog(\\\\''+log.id+'\\\\')\"><div class=\"log-header\"><span class=\"status '+statusClass+'\">'+(log.status || '...')+'</span><span class=\"url\" title=\"'+escapeHtml(log.url)+'\">'+log.url+'</span><span class=\"duration\">'+(log.duration ? log.duration + 'ms' : '')+'</span></div>'+(isSelected ? '<div class=\"log-body '+(bodyContent ? '' : 'empty')+'\">'+(bodyContent ? '<pre>'+bodyContent+'</pre>' : (log.status ? 'No response body' : 'Pending...'))+'</div>' : '')+'</div>';\n }).join('');\n }\n\n function selectLog(id) {\n selectedId = selectedId === id ? null : id;\n renderLogs();\n }\n\n function clearLogs() {\n logs.clear();\n selectedId = null;\n renderLogs();\n fetch('/clear', { method: 'POST' });\n }\n\n function addLog(log) {\n logs.set(log.id, log);\n if (logs.size > 100) {\n const oldest = getLogsArray().pop();\n if (oldest) logs.delete(oldest.id);\n }\n renderLogs();\n }\n\n function connect() {\n const evtSource = new EventSource('/events');\n evtSource.onopen = () => document.getElementById('statusDot').classList.remove('disconnected');\n evtSource.onmessage = (event) => addLog(JSON.parse(event.data));\n evtSource.onerror = () => {\n document.getElementById('statusDot').classList.add('disconnected');\n evtSource.close();\n setTimeout(connect, 2000);\n };\n }\n\n connect();\n renderLogs();\n </script>\n</body>\n</html>\n`;\n}\n\nfunction getMonitorScript(port: number): string {\n return `\n(function() {\n if (window.__networkTerminalActive) return;\n window.__networkTerminalActive = true;\n\n var DASHBOARD_URL = 'http://localhost:${port}';\n\n function sendLog(log) {\n var xhr = new XMLHttpRequest();\n xhr.open('POST', DASHBOARD_URL + '/log', true);\n xhr.setRequestHeader('Content-Type', 'application/json');\n xhr.send(JSON.stringify(log));\n }\n\n function tryParse(data) {\n if (!data) return null;\n if (typeof data === 'object') return data;\n try { return JSON.parse(data); } catch (e) { return data; }\n }\n\n var originalFetch = window.fetch;\n window.fetch = function() {\n var args = arguments;\n var startTime = Date.now();\n var input = args[0];\n var options = args[1] || {};\n var url = typeof input === 'string' ? input : (input.url || input.toString());\n var method = (options.method || 'GET').toUpperCase();\n\n if (url.includes('localhost:${port}')) {\n return originalFetch.apply(this, args);\n }\n\n var log = {\n id: Date.now().toString() + Math.random().toString(36).substr(2, 9),\n method: method,\n url: url,\n timestamp: new Date().toISOString(),\n requestBody: options.body ? tryParse(options.body) : null,\n status: null,\n responseBody: null,\n duration: null,\n };\n\n sendLog(log);\n\n return originalFetch.apply(this, args).then(function(response) {\n log.status = response.status;\n log.duration = Date.now() - startTime;\n var cloned = response.clone();\n cloned.text().then(function(text) {\n log.responseBody = tryParse(text);\n sendLog(log);\n }).catch(function() { sendLog(log); });\n return response;\n }).catch(function(error) {\n log.status = 0;\n log.duration = Date.now() - startTime;\n log.error = error.message;\n sendLog(log);\n throw error;\n });\n };\n\n var OriginalXHR = window.XMLHttpRequest;\n window.XMLHttpRequest = function() {\n var xhr = new OriginalXHR();\n var method, url, requestBody, startTime;\n\n var originalOpen = xhr.open;\n xhr.open = function(m, u) {\n method = m;\n url = u;\n return originalOpen.apply(this, arguments);\n };\n\n var originalSend = xhr.send;\n xhr.send = function(body) {\n if (url && url.includes('localhost:${port}')) {\n return originalSend.apply(this, arguments);\n }\n\n startTime = Date.now();\n requestBody = body;\n\n var log = {\n id: Date.now().toString() + Math.random().toString(36).substr(2, 9),\n method: (method || 'GET').toUpperCase(),\n url: url,\n timestamp: new Date().toISOString(),\n requestBody: tryParse(requestBody),\n status: null,\n responseBody: null,\n duration: null,\n };\n\n sendLog(log);\n\n xhr.addEventListener('loadend', function() {\n log.status = xhr.status;\n log.duration = Date.now() - startTime;\n log.responseBody = tryParse(xhr.responseText);\n sendLog(log);\n });\n\n return originalSend.apply(this, arguments);\n };\n\n return xhr;\n };\n\n console.log('[Network Terminal] ✓ Active - Dashboard: http://localhost:${port}');\n})();\n`;\n}\n\nexport function networkTerminal(options: NetworkTerminalPluginOptions = {}): Plugin {\n const { port = 4001, open = true } = options;\n let dashboardServer: ReturnType<typeof createServer> | null = null;\n\n return {\n name: 'vite-plugin-network-terminal',\n apply: 'serve',\n\n configureServer(_server: ViteDevServer) {\n // Start dashboard server\n dashboardServer = createServer((req: IncomingMessage, res: ServerResponse) => {\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n if (req.url === '/events') {\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n });\n sseClients.add(res);\n networkLogs.forEach(log => {\n res.write(`data: ${JSON.stringify(log)}\\n\\n`);\n });\n req.on('close', () => sseClients.delete(res));\n return;\n }\n\n if (req.url === '/log' && req.method === 'POST') {\n let body = '';\n req.on('data', (chunk: Buffer) => body += chunk);\n req.on('end', () => {\n try {\n const log = JSON.parse(body);\n const existingIndex = networkLogs.findIndex(l => l.id === log.id);\n if (existingIndex >= 0) {\n networkLogs[existingIndex] = log;\n } else {\n networkLogs.unshift(log);\n if (networkLogs.length > 100) networkLogs.pop();\n }\n broadcastLog(log);\n } catch (e) {}\n res.writeHead(200);\n res.end('OK');\n });\n return;\n }\n\n if (req.url === '/clear' && req.method === 'POST') {\n networkLogs.length = 0;\n res.writeHead(200);\n res.end('OK');\n return;\n }\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getDashboardHTML(port));\n });\n\n dashboardServer.listen(port, () => {\n console.log(`\\n \\x1b[32m>_ Network Terminal\\x1b[0m`);\n console.log(` Dashboard: \\x1b[36mhttp://localhost:${port}\\x1b[0m\\n`);\n\n if (open) {\n const openCmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';\n exec(`${openCmd} http://localhost:${port}`);\n }\n });\n\n dashboardServer.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n console.log(`\\n \\x1b[33m[Network Terminal]\\x1b[0m Port ${port} in use, dashboard disabled\\n`);\n }\n });\n },\n\n transformIndexHtml(html: string) {\n const script = `<script>${getMonitorScript(port)}</script>`;\n if (html.includes('</head>')) {\n return html.replace('</head>', script + '</head>');\n }\n return script + html;\n },\n\n buildEnd() {\n if (dashboardServer) {\n dashboardServer.close();\n }\n },\n };\n}\n\nexport default networkTerminal;\n"],"mappings":"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","names":["useState","useCallback","useEffect","jsx","jsxs","styles","jsx","jsxs","styles","useEffect","useRef","jsx","jsxs","styles","useState","useCallback","useEffect"]}
|
package/dist/vite-plugin.js
CHANGED
|
@@ -25,6 +25,7 @@ __export(vite_plugin_exports, {
|
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(vite_plugin_exports);
|
|
27
27
|
var import_http = require("http");
|
|
28
|
+
var import_child_process = require("child_process");
|
|
28
29
|
var networkLogs = [];
|
|
29
30
|
var sseClients = /* @__PURE__ */ new Set();
|
|
30
31
|
function broadcastLog(log) {
|
|
@@ -501,7 +502,7 @@ function networkTerminal(options = {}) {
|
|
|
501
502
|
`);
|
|
502
503
|
if (open) {
|
|
503
504
|
const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
504
|
-
|
|
505
|
+
(0, import_child_process.exec)(`${openCmd} http://localhost:${port}`);
|
|
505
506
|
}
|
|
506
507
|
});
|
|
507
508
|
dashboardServer.on("error", (err) => {
|
package/dist/vite-plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/vite-plugin.ts"],"sourcesContent":["import type { Plugin, ViteDevServer } from 'vite';\nimport { createServer, IncomingMessage, ServerResponse } from 'http';\n\nexport interface NetworkTerminalPluginOptions {\n /** Port for the dashboard (default: 4001) */\n port?: number;\n /** Auto-open dashboard in browser (default: true) */\n open?: boolean;\n}\n\n// Store network logs\nconst networkLogs: any[] = [];\nconst sseClients = new Set<ServerResponse>();\n\nfunction broadcastLog(log: any) {\n const data = JSON.stringify(log);\n sseClients.forEach(client => {\n client.write(`data: ${data}\\n\\n`);\n });\n}\n\nfunction getDashboardHTML(_port: number): string {\n return `\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Network Terminal</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body {\n font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;\n background: #0a0e17;\n color: #e2e8f0;\n height: 100vh;\n overflow: hidden;\n }\n .header {\n background: #0f172a;\n padding: 12px 24px;\n border-bottom: 2px solid #4ade80;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n .title { color: #4ade80; font-size: 18px; font-weight: bold; }\n .subtitle { color: #64748b; font-size: 12px; margin-top: 4px; }\n .controls { display: flex; gap: 12px; align-items: center; }\n .btn {\n background: #1e293b;\n border: 1px solid #334155;\n color: #94a3b8;\n padding: 8px 16px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 12px;\n font-family: inherit;\n }\n .btn:hover { background: #334155; color: #e2e8f0; }\n .container { display: flex; height: calc(100vh - 80px); }\n .panel {\n flex: 1;\n display: flex;\n flex-direction: column;\n border-right: 1px solid #1e293b;\n }\n .panel:last-child { border-right: none; }\n .panel-header {\n background: #0f172a;\n padding: 12px 16px;\n border-bottom: 1px solid #1e293b;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n .panel-title { font-weight: bold; font-size: 14px; }\n .panel-title.request { color: #3b82f6; }\n .panel-title.response { color: #22c55e; }\n .panel-count {\n background: #1e293b;\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n color: #64748b;\n }\n .logs { flex: 1; overflow-y: auto; padding: 8px; }\n .log-entry {\n background: #0f172a;\n border: 1px solid #1e293b;\n border-radius: 8px;\n margin-bottom: 8px;\n overflow: hidden;\n cursor: pointer;\n transition: border-color 0.2s;\n }\n .log-entry:hover { border-color: #334155; }\n .log-entry.selected { border-color: #4ade80; }\n .log-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 12px;\n background: #1e293b;\n }\n .method {\n font-weight: bold;\n font-size: 11px;\n padding: 3px 8px;\n border-radius: 4px;\n }\n .method.GET { background: #166534; color: #4ade80; }\n .method.POST { background: #1e40af; color: #60a5fa; }\n .method.PUT { background: #a16207; color: #fcd34d; }\n .method.DELETE { background: #991b1b; color: #fca5a5; }\n .method.PATCH { background: #7c3aed; color: #c4b5fd; }\n .status { font-size: 11px; padding: 3px 8px; border-radius: 4px; }\n .status.success { background: #166534; color: #4ade80; }\n .status.error { background: #991b1b; color: #fca5a5; }\n .status.pending { background: #374151; color: #9ca3af; }\n .url {\n flex: 1;\n font-size: 12px;\n color: #94a3b8;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .duration { font-size: 11px; color: #64748b; }\n .timestamp { font-size: 10px; color: #475569; }\n .log-body { padding: 12px; font-size: 11px; max-height: 300px; overflow: auto; }\n .log-body pre {\n margin: 0;\n white-space: pre-wrap;\n word-break: break-all;\n color: #cbd5e1;\n line-height: 1.5;\n }\n .log-body.empty { color: #475569; font-style: italic; }\n .empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: #475569;\n text-align: center;\n padding: 20px;\n }\n .empty-state svg { width: 48px; height: 48px; margin-bottom: 16px; opacity: 0.5; }\n .status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #22c55e;\n animation: pulse 2s infinite;\n }\n @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }\n .status-dot.disconnected { background: #ef4444; animation: none; }\n </style>\n</head>\n<body>\n <div class=\"header\">\n <div>\n <div class=\"title\">>_ Network Terminal</div>\n <div class=\"subtitle\">Vite Plugin - Monitoring fetch/XHR requests</div>\n </div>\n <div class=\"controls\">\n <div class=\"status-dot\" id=\"statusDot\"></div>\n <button class=\"btn\" onclick=\"clearLogs()\">Clear All</button>\n </div>\n </div>\n\n <div class=\"container\">\n <div class=\"panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title request\">⬆ REQUESTS</span>\n <span class=\"panel-count\" id=\"requestCount\">0</span>\n </div>\n <div class=\"logs\" id=\"requestLogs\">\n <div class=\"empty-state\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M12 19V5M5 12l7-7 7 7\"/>\n </svg>\n <div>Waiting for requests...</div>\n <div style=\"margin-top: 8px; font-size: 11px;\">Make fetch/XHR requests in your app</div>\n </div>\n </div>\n </div>\n\n <div class=\"panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title response\">⬇ RESPONSES</span>\n <span class=\"panel-count\" id=\"responseCount\">0</span>\n </div>\n <div class=\"logs\" id=\"responseLogs\">\n <div class=\"empty-state\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M12 5v14M5 12l7 7 7-7\"/>\n </svg>\n <div>Waiting for responses...</div>\n </div>\n </div>\n </div>\n </div>\n\n <script>\n const logs = new Map();\n let selectedId = null;\n\n function formatJson(data) {\n if (!data) return null;\n if (typeof data === 'string') {\n try { data = JSON.parse(data); }\n catch (e) { return escapeHtml(data.substring(0, 2000)); }\n }\n return escapeHtml(JSON.stringify(data, null, 2));\n }\n\n function escapeHtml(str) {\n if (typeof str !== 'string') str = String(str);\n return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');\n }\n\n function formatTime(timestamp) {\n const date = new Date(timestamp);\n return date.toLocaleTimeString('en-US', { hour12: false }) + '.' +\n String(date.getMilliseconds()).padStart(3, '0');\n }\n\n function getLogsArray() {\n return Array.from(logs.values()).sort((a, b) =>\n new Date(b.timestamp) - new Date(a.timestamp)\n );\n }\n\n function renderLogs() {\n const logsArray = getLogsArray();\n const requestContainer = document.getElementById('requestLogs');\n const responseContainer = document.getElementById('responseLogs');\n\n document.getElementById('requestCount').textContent = logsArray.length;\n document.getElementById('responseCount').textContent = logsArray.filter(l => l.status).length;\n\n if (logsArray.length === 0) {\n requestContainer.innerHTML = '<div class=\"empty-state\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M12 19V5M5 12l7-7 7 7\"/></svg><div>Waiting for requests...</div></div>';\n responseContainer.innerHTML = '<div class=\"empty-state\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M12 5v14M5 12l7 7 7-7\"/></svg><div>Waiting for responses...</div></div>';\n return;\n }\n\n requestContainer.innerHTML = logsArray.map(log => {\n const isSelected = selectedId === log.id;\n const bodyContent = formatJson(log.requestBody);\n return '<div class=\"log-entry '+(isSelected ? 'selected' : '')+'\" onclick=\"selectLog(\\\\''+log.id+'\\\\')\"><div class=\"log-header\"><span class=\"method '+log.method+'\">'+log.method+'</span><span class=\"url\" title=\"'+escapeHtml(log.url)+'\">'+log.url+'</span><span class=\"timestamp\">'+formatTime(log.timestamp)+'</span></div>'+(isSelected ? '<div class=\"log-body '+(bodyContent ? '' : 'empty')+'\">'+(bodyContent ? '<pre>'+bodyContent+'</pre>' : 'No request body')+'</div>' : '')+'</div>';\n }).join('');\n\n responseContainer.innerHTML = logsArray.map(log => {\n const isSelected = selectedId === log.id;\n const statusClass = !log.status ? 'pending' : log.status < 400 ? 'success' : 'error';\n const bodyContent = formatJson(log.responseBody);\n return '<div class=\"log-entry '+(isSelected ? 'selected' : '')+'\" onclick=\"selectLog(\\\\''+log.id+'\\\\')\"><div class=\"log-header\"><span class=\"status '+statusClass+'\">'+(log.status || '...')+'</span><span class=\"url\" title=\"'+escapeHtml(log.url)+'\">'+log.url+'</span><span class=\"duration\">'+(log.duration ? log.duration + 'ms' : '')+'</span></div>'+(isSelected ? '<div class=\"log-body '+(bodyContent ? '' : 'empty')+'\">'+(bodyContent ? '<pre>'+bodyContent+'</pre>' : (log.status ? 'No response body' : 'Pending...'))+'</div>' : '')+'</div>';\n }).join('');\n }\n\n function selectLog(id) {\n selectedId = selectedId === id ? null : id;\n renderLogs();\n }\n\n function clearLogs() {\n logs.clear();\n selectedId = null;\n renderLogs();\n fetch('/clear', { method: 'POST' });\n }\n\n function addLog(log) {\n logs.set(log.id, log);\n if (logs.size > 100) {\n const oldest = getLogsArray().pop();\n if (oldest) logs.delete(oldest.id);\n }\n renderLogs();\n }\n\n function connect() {\n const evtSource = new EventSource('/events');\n evtSource.onopen = () => document.getElementById('statusDot').classList.remove('disconnected');\n evtSource.onmessage = (event) => addLog(JSON.parse(event.data));\n evtSource.onerror = () => {\n document.getElementById('statusDot').classList.add('disconnected');\n evtSource.close();\n setTimeout(connect, 2000);\n };\n }\n\n connect();\n renderLogs();\n </script>\n</body>\n</html>\n`;\n}\n\nfunction getMonitorScript(port: number): string {\n return `\n(function() {\n if (window.__networkTerminalActive) return;\n window.__networkTerminalActive = true;\n\n var DASHBOARD_URL = 'http://localhost:${port}';\n\n function sendLog(log) {\n var xhr = new XMLHttpRequest();\n xhr.open('POST', DASHBOARD_URL + '/log', true);\n xhr.setRequestHeader('Content-Type', 'application/json');\n xhr.send(JSON.stringify(log));\n }\n\n function tryParse(data) {\n if (!data) return null;\n if (typeof data === 'object') return data;\n try { return JSON.parse(data); } catch (e) { return data; }\n }\n\n var originalFetch = window.fetch;\n window.fetch = function() {\n var args = arguments;\n var startTime = Date.now();\n var input = args[0];\n var options = args[1] || {};\n var url = typeof input === 'string' ? input : (input.url || input.toString());\n var method = (options.method || 'GET').toUpperCase();\n\n if (url.includes('localhost:${port}')) {\n return originalFetch.apply(this, args);\n }\n\n var log = {\n id: Date.now().toString() + Math.random().toString(36).substr(2, 9),\n method: method,\n url: url,\n timestamp: new Date().toISOString(),\n requestBody: options.body ? tryParse(options.body) : null,\n status: null,\n responseBody: null,\n duration: null,\n };\n\n sendLog(log);\n\n return originalFetch.apply(this, args).then(function(response) {\n log.status = response.status;\n log.duration = Date.now() - startTime;\n var cloned = response.clone();\n cloned.text().then(function(text) {\n log.responseBody = tryParse(text);\n sendLog(log);\n }).catch(function() { sendLog(log); });\n return response;\n }).catch(function(error) {\n log.status = 0;\n log.duration = Date.now() - startTime;\n log.error = error.message;\n sendLog(log);\n throw error;\n });\n };\n\n var OriginalXHR = window.XMLHttpRequest;\n window.XMLHttpRequest = function() {\n var xhr = new OriginalXHR();\n var method, url, requestBody, startTime;\n\n var originalOpen = xhr.open;\n xhr.open = function(m, u) {\n method = m;\n url = u;\n return originalOpen.apply(this, arguments);\n };\n\n var originalSend = xhr.send;\n xhr.send = function(body) {\n if (url && url.includes('localhost:${port}')) {\n return originalSend.apply(this, arguments);\n }\n\n startTime = Date.now();\n requestBody = body;\n\n var log = {\n id: Date.now().toString() + Math.random().toString(36).substr(2, 9),\n method: (method || 'GET').toUpperCase(),\n url: url,\n timestamp: new Date().toISOString(),\n requestBody: tryParse(requestBody),\n status: null,\n responseBody: null,\n duration: null,\n };\n\n sendLog(log);\n\n xhr.addEventListener('loadend', function() {\n log.status = xhr.status;\n log.duration = Date.now() - startTime;\n log.responseBody = tryParse(xhr.responseText);\n sendLog(log);\n });\n\n return originalSend.apply(this, arguments);\n };\n\n return xhr;\n };\n\n console.log('[Network Terminal] ✓ Active - Dashboard: http://localhost:${port}');\n})();\n`;\n}\n\nexport function networkTerminal(options: NetworkTerminalPluginOptions = {}): Plugin {\n const { port = 4001, open = true } = options;\n let dashboardServer: ReturnType<typeof createServer> | null = null;\n\n return {\n name: 'vite-plugin-network-terminal',\n apply: 'serve',\n\n configureServer(_server: ViteDevServer) {\n // Start dashboard server\n dashboardServer = createServer((req: IncomingMessage, res: ServerResponse) => {\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n if (req.url === '/events') {\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n });\n sseClients.add(res);\n networkLogs.forEach(log => {\n res.write(`data: ${JSON.stringify(log)}\\n\\n`);\n });\n req.on('close', () => sseClients.delete(res));\n return;\n }\n\n if (req.url === '/log' && req.method === 'POST') {\n let body = '';\n req.on('data', (chunk: Buffer) => body += chunk);\n req.on('end', () => {\n try {\n const log = JSON.parse(body);\n const existingIndex = networkLogs.findIndex(l => l.id === log.id);\n if (existingIndex >= 0) {\n networkLogs[existingIndex] = log;\n } else {\n networkLogs.unshift(log);\n if (networkLogs.length > 100) networkLogs.pop();\n }\n broadcastLog(log);\n } catch (e) {}\n res.writeHead(200);\n res.end('OK');\n });\n return;\n }\n\n if (req.url === '/clear' && req.method === 'POST') {\n networkLogs.length = 0;\n res.writeHead(200);\n res.end('OK');\n return;\n }\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getDashboardHTML(port));\n });\n\n dashboardServer.listen(port, () => {\n console.log(`\\n \\x1b[32m>_ Network Terminal\\x1b[0m`);\n console.log(` Dashboard: \\x1b[36mhttp://localhost:${port}\\x1b[0m\\n`);\n\n if (open) {\n const openCmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';\n require('child_process').exec(`${openCmd} http://localhost:${port}`);\n }\n });\n\n dashboardServer.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n console.log(`\\n \\x1b[33m[Network Terminal]\\x1b[0m Port ${port} in use, dashboard disabled\\n`);\n }\n });\n },\n\n transformIndexHtml(html: string) {\n const script = `<script>${getMonitorScript(port)}</script>`;\n if (html.includes('</head>')) {\n return html.replace('</head>', script + '</head>');\n }\n return script + html;\n },\n\n buildEnd() {\n if (dashboardServer) {\n dashboardServer.close();\n }\n },\n };\n}\n\nexport default networkTerminal;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/vite-plugin.ts"],"sourcesContent":["import type { Plugin, ViteDevServer } from 'vite';\nimport { createServer, IncomingMessage, ServerResponse } from 'http';\nimport { exec } from 'child_process';\n\nexport interface NetworkTerminalPluginOptions {\n /** Port for the dashboard (default: 4001) */\n port?: number;\n /** Auto-open dashboard in browser (default: true) */\n open?: boolean;\n}\n\n// Store network logs\nconst networkLogs: any[] = [];\nconst sseClients = new Set<ServerResponse>();\n\nfunction broadcastLog(log: any) {\n const data = JSON.stringify(log);\n sseClients.forEach(client => {\n client.write(`data: ${data}\\n\\n`);\n });\n}\n\nfunction getDashboardHTML(_port: number): string {\n return `\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Network Terminal</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body {\n font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;\n background: #0a0e17;\n color: #e2e8f0;\n height: 100vh;\n overflow: hidden;\n }\n .header {\n background: #0f172a;\n padding: 12px 24px;\n border-bottom: 2px solid #4ade80;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n .title { color: #4ade80; font-size: 18px; font-weight: bold; }\n .subtitle { color: #64748b; font-size: 12px; margin-top: 4px; }\n .controls { display: flex; gap: 12px; align-items: center; }\n .btn {\n background: #1e293b;\n border: 1px solid #334155;\n color: #94a3b8;\n padding: 8px 16px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 12px;\n font-family: inherit;\n }\n .btn:hover { background: #334155; color: #e2e8f0; }\n .container { display: flex; height: calc(100vh - 80px); }\n .panel {\n flex: 1;\n display: flex;\n flex-direction: column;\n border-right: 1px solid #1e293b;\n }\n .panel:last-child { border-right: none; }\n .panel-header {\n background: #0f172a;\n padding: 12px 16px;\n border-bottom: 1px solid #1e293b;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n .panel-title { font-weight: bold; font-size: 14px; }\n .panel-title.request { color: #3b82f6; }\n .panel-title.response { color: #22c55e; }\n .panel-count {\n background: #1e293b;\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n color: #64748b;\n }\n .logs { flex: 1; overflow-y: auto; padding: 8px; }\n .log-entry {\n background: #0f172a;\n border: 1px solid #1e293b;\n border-radius: 8px;\n margin-bottom: 8px;\n overflow: hidden;\n cursor: pointer;\n transition: border-color 0.2s;\n }\n .log-entry:hover { border-color: #334155; }\n .log-entry.selected { border-color: #4ade80; }\n .log-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 12px;\n background: #1e293b;\n }\n .method {\n font-weight: bold;\n font-size: 11px;\n padding: 3px 8px;\n border-radius: 4px;\n }\n .method.GET { background: #166534; color: #4ade80; }\n .method.POST { background: #1e40af; color: #60a5fa; }\n .method.PUT { background: #a16207; color: #fcd34d; }\n .method.DELETE { background: #991b1b; color: #fca5a5; }\n .method.PATCH { background: #7c3aed; color: #c4b5fd; }\n .status { font-size: 11px; padding: 3px 8px; border-radius: 4px; }\n .status.success { background: #166534; color: #4ade80; }\n .status.error { background: #991b1b; color: #fca5a5; }\n .status.pending { background: #374151; color: #9ca3af; }\n .url {\n flex: 1;\n font-size: 12px;\n color: #94a3b8;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .duration { font-size: 11px; color: #64748b; }\n .timestamp { font-size: 10px; color: #475569; }\n .log-body { padding: 12px; font-size: 11px; max-height: 300px; overflow: auto; }\n .log-body pre {\n margin: 0;\n white-space: pre-wrap;\n word-break: break-all;\n color: #cbd5e1;\n line-height: 1.5;\n }\n .log-body.empty { color: #475569; font-style: italic; }\n .empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: #475569;\n text-align: center;\n padding: 20px;\n }\n .empty-state svg { width: 48px; height: 48px; margin-bottom: 16px; opacity: 0.5; }\n .status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #22c55e;\n animation: pulse 2s infinite;\n }\n @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }\n .status-dot.disconnected { background: #ef4444; animation: none; }\n </style>\n</head>\n<body>\n <div class=\"header\">\n <div>\n <div class=\"title\">>_ Network Terminal</div>\n <div class=\"subtitle\">Vite Plugin - Monitoring fetch/XHR requests</div>\n </div>\n <div class=\"controls\">\n <div class=\"status-dot\" id=\"statusDot\"></div>\n <button class=\"btn\" onclick=\"clearLogs()\">Clear All</button>\n </div>\n </div>\n\n <div class=\"container\">\n <div class=\"panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title request\">⬆ REQUESTS</span>\n <span class=\"panel-count\" id=\"requestCount\">0</span>\n </div>\n <div class=\"logs\" id=\"requestLogs\">\n <div class=\"empty-state\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M12 19V5M5 12l7-7 7 7\"/>\n </svg>\n <div>Waiting for requests...</div>\n <div style=\"margin-top: 8px; font-size: 11px;\">Make fetch/XHR requests in your app</div>\n </div>\n </div>\n </div>\n\n <div class=\"panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title response\">⬇ RESPONSES</span>\n <span class=\"panel-count\" id=\"responseCount\">0</span>\n </div>\n <div class=\"logs\" id=\"responseLogs\">\n <div class=\"empty-state\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M12 5v14M5 12l7 7 7-7\"/>\n </svg>\n <div>Waiting for responses...</div>\n </div>\n </div>\n </div>\n </div>\n\n <script>\n const logs = new Map();\n let selectedId = null;\n\n function formatJson(data) {\n if (!data) return null;\n if (typeof data === 'string') {\n try { data = JSON.parse(data); }\n catch (e) { return escapeHtml(data.substring(0, 2000)); }\n }\n return escapeHtml(JSON.stringify(data, null, 2));\n }\n\n function escapeHtml(str) {\n if (typeof str !== 'string') str = String(str);\n return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');\n }\n\n function formatTime(timestamp) {\n const date = new Date(timestamp);\n return date.toLocaleTimeString('en-US', { hour12: false }) + '.' +\n String(date.getMilliseconds()).padStart(3, '0');\n }\n\n function getLogsArray() {\n return Array.from(logs.values()).sort((a, b) =>\n new Date(b.timestamp) - new Date(a.timestamp)\n );\n }\n\n function renderLogs() {\n const logsArray = getLogsArray();\n const requestContainer = document.getElementById('requestLogs');\n const responseContainer = document.getElementById('responseLogs');\n\n document.getElementById('requestCount').textContent = logsArray.length;\n document.getElementById('responseCount').textContent = logsArray.filter(l => l.status).length;\n\n if (logsArray.length === 0) {\n requestContainer.innerHTML = '<div class=\"empty-state\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M12 19V5M5 12l7-7 7 7\"/></svg><div>Waiting for requests...</div></div>';\n responseContainer.innerHTML = '<div class=\"empty-state\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M12 5v14M5 12l7 7 7-7\"/></svg><div>Waiting for responses...</div></div>';\n return;\n }\n\n requestContainer.innerHTML = logsArray.map(log => {\n const isSelected = selectedId === log.id;\n const bodyContent = formatJson(log.requestBody);\n return '<div class=\"log-entry '+(isSelected ? 'selected' : '')+'\" onclick=\"selectLog(\\\\''+log.id+'\\\\')\"><div class=\"log-header\"><span class=\"method '+log.method+'\">'+log.method+'</span><span class=\"url\" title=\"'+escapeHtml(log.url)+'\">'+log.url+'</span><span class=\"timestamp\">'+formatTime(log.timestamp)+'</span></div>'+(isSelected ? '<div class=\"log-body '+(bodyContent ? '' : 'empty')+'\">'+(bodyContent ? '<pre>'+bodyContent+'</pre>' : 'No request body')+'</div>' : '')+'</div>';\n }).join('');\n\n responseContainer.innerHTML = logsArray.map(log => {\n const isSelected = selectedId === log.id;\n const statusClass = !log.status ? 'pending' : log.status < 400 ? 'success' : 'error';\n const bodyContent = formatJson(log.responseBody);\n return '<div class=\"log-entry '+(isSelected ? 'selected' : '')+'\" onclick=\"selectLog(\\\\''+log.id+'\\\\')\"><div class=\"log-header\"><span class=\"status '+statusClass+'\">'+(log.status || '...')+'</span><span class=\"url\" title=\"'+escapeHtml(log.url)+'\">'+log.url+'</span><span class=\"duration\">'+(log.duration ? log.duration + 'ms' : '')+'</span></div>'+(isSelected ? '<div class=\"log-body '+(bodyContent ? '' : 'empty')+'\">'+(bodyContent ? '<pre>'+bodyContent+'</pre>' : (log.status ? 'No response body' : 'Pending...'))+'</div>' : '')+'</div>';\n }).join('');\n }\n\n function selectLog(id) {\n selectedId = selectedId === id ? null : id;\n renderLogs();\n }\n\n function clearLogs() {\n logs.clear();\n selectedId = null;\n renderLogs();\n fetch('/clear', { method: 'POST' });\n }\n\n function addLog(log) {\n logs.set(log.id, log);\n if (logs.size > 100) {\n const oldest = getLogsArray().pop();\n if (oldest) logs.delete(oldest.id);\n }\n renderLogs();\n }\n\n function connect() {\n const evtSource = new EventSource('/events');\n evtSource.onopen = () => document.getElementById('statusDot').classList.remove('disconnected');\n evtSource.onmessage = (event) => addLog(JSON.parse(event.data));\n evtSource.onerror = () => {\n document.getElementById('statusDot').classList.add('disconnected');\n evtSource.close();\n setTimeout(connect, 2000);\n };\n }\n\n connect();\n renderLogs();\n </script>\n</body>\n</html>\n`;\n}\n\nfunction getMonitorScript(port: number): string {\n return `\n(function() {\n if (window.__networkTerminalActive) return;\n window.__networkTerminalActive = true;\n\n var DASHBOARD_URL = 'http://localhost:${port}';\n\n function sendLog(log) {\n var xhr = new XMLHttpRequest();\n xhr.open('POST', DASHBOARD_URL + '/log', true);\n xhr.setRequestHeader('Content-Type', 'application/json');\n xhr.send(JSON.stringify(log));\n }\n\n function tryParse(data) {\n if (!data) return null;\n if (typeof data === 'object') return data;\n try { return JSON.parse(data); } catch (e) { return data; }\n }\n\n var originalFetch = window.fetch;\n window.fetch = function() {\n var args = arguments;\n var startTime = Date.now();\n var input = args[0];\n var options = args[1] || {};\n var url = typeof input === 'string' ? input : (input.url || input.toString());\n var method = (options.method || 'GET').toUpperCase();\n\n if (url.includes('localhost:${port}')) {\n return originalFetch.apply(this, args);\n }\n\n var log = {\n id: Date.now().toString() + Math.random().toString(36).substr(2, 9),\n method: method,\n url: url,\n timestamp: new Date().toISOString(),\n requestBody: options.body ? tryParse(options.body) : null,\n status: null,\n responseBody: null,\n duration: null,\n };\n\n sendLog(log);\n\n return originalFetch.apply(this, args).then(function(response) {\n log.status = response.status;\n log.duration = Date.now() - startTime;\n var cloned = response.clone();\n cloned.text().then(function(text) {\n log.responseBody = tryParse(text);\n sendLog(log);\n }).catch(function() { sendLog(log); });\n return response;\n }).catch(function(error) {\n log.status = 0;\n log.duration = Date.now() - startTime;\n log.error = error.message;\n sendLog(log);\n throw error;\n });\n };\n\n var OriginalXHR = window.XMLHttpRequest;\n window.XMLHttpRequest = function() {\n var xhr = new OriginalXHR();\n var method, url, requestBody, startTime;\n\n var originalOpen = xhr.open;\n xhr.open = function(m, u) {\n method = m;\n url = u;\n return originalOpen.apply(this, arguments);\n };\n\n var originalSend = xhr.send;\n xhr.send = function(body) {\n if (url && url.includes('localhost:${port}')) {\n return originalSend.apply(this, arguments);\n }\n\n startTime = Date.now();\n requestBody = body;\n\n var log = {\n id: Date.now().toString() + Math.random().toString(36).substr(2, 9),\n method: (method || 'GET').toUpperCase(),\n url: url,\n timestamp: new Date().toISOString(),\n requestBody: tryParse(requestBody),\n status: null,\n responseBody: null,\n duration: null,\n };\n\n sendLog(log);\n\n xhr.addEventListener('loadend', function() {\n log.status = xhr.status;\n log.duration = Date.now() - startTime;\n log.responseBody = tryParse(xhr.responseText);\n sendLog(log);\n });\n\n return originalSend.apply(this, arguments);\n };\n\n return xhr;\n };\n\n console.log('[Network Terminal] ✓ Active - Dashboard: http://localhost:${port}');\n})();\n`;\n}\n\nexport function networkTerminal(options: NetworkTerminalPluginOptions = {}): Plugin {\n const { port = 4001, open = true } = options;\n let dashboardServer: ReturnType<typeof createServer> | null = null;\n\n return {\n name: 'vite-plugin-network-terminal',\n apply: 'serve',\n\n configureServer(_server: ViteDevServer) {\n // Start dashboard server\n dashboardServer = createServer((req: IncomingMessage, res: ServerResponse) => {\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n if (req.url === '/events') {\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n });\n sseClients.add(res);\n networkLogs.forEach(log => {\n res.write(`data: ${JSON.stringify(log)}\\n\\n`);\n });\n req.on('close', () => sseClients.delete(res));\n return;\n }\n\n if (req.url === '/log' && req.method === 'POST') {\n let body = '';\n req.on('data', (chunk: Buffer) => body += chunk);\n req.on('end', () => {\n try {\n const log = JSON.parse(body);\n const existingIndex = networkLogs.findIndex(l => l.id === log.id);\n if (existingIndex >= 0) {\n networkLogs[existingIndex] = log;\n } else {\n networkLogs.unshift(log);\n if (networkLogs.length > 100) networkLogs.pop();\n }\n broadcastLog(log);\n } catch (e) {}\n res.writeHead(200);\n res.end('OK');\n });\n return;\n }\n\n if (req.url === '/clear' && req.method === 'POST') {\n networkLogs.length = 0;\n res.writeHead(200);\n res.end('OK');\n return;\n }\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getDashboardHTML(port));\n });\n\n dashboardServer.listen(port, () => {\n console.log(`\\n \\x1b[32m>_ Network Terminal\\x1b[0m`);\n console.log(` Dashboard: \\x1b[36mhttp://localhost:${port}\\x1b[0m\\n`);\n\n if (open) {\n const openCmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';\n exec(`${openCmd} http://localhost:${port}`);\n }\n });\n\n dashboardServer.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n console.log(`\\n \\x1b[33m[Network Terminal]\\x1b[0m Port ${port} in use, dashboard disabled\\n`);\n }\n });\n },\n\n transformIndexHtml(html: string) {\n const script = `<script>${getMonitorScript(port)}</script>`;\n if (html.includes('</head>')) {\n return html.replace('</head>', script + '</head>');\n }\n return script + html;\n },\n\n buildEnd() {\n if (dashboardServer) {\n dashboardServer.close();\n }\n },\n };\n}\n\nexport default networkTerminal;\n"],"mappings":"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","names":[]}
|
package/dist/vite-plugin.mjs
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
-
}) : x)(function(x) {
|
|
4
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
-
});
|
|
7
|
-
|
|
8
1
|
// src/vite-plugin.ts
|
|
9
2
|
import { createServer } from "http";
|
|
3
|
+
import { exec } from "child_process";
|
|
10
4
|
var networkLogs = [];
|
|
11
5
|
var sseClients = /* @__PURE__ */ new Set();
|
|
12
6
|
function broadcastLog(log) {
|
|
@@ -483,7 +477,7 @@ function networkTerminal(options = {}) {
|
|
|
483
477
|
`);
|
|
484
478
|
if (open) {
|
|
485
479
|
const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
486
|
-
|
|
480
|
+
exec(`${openCmd} http://localhost:${port}`);
|
|
487
481
|
}
|
|
488
482
|
});
|
|
489
483
|
dashboardServer.on("error", (err) => {
|
package/dist/vite-plugin.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/vite-plugin.ts"],"sourcesContent":["import type { Plugin, ViteDevServer } from 'vite';\nimport { createServer, IncomingMessage, ServerResponse } from 'http';\n\nexport interface NetworkTerminalPluginOptions {\n /** Port for the dashboard (default: 4001) */\n port?: number;\n /** Auto-open dashboard in browser (default: true) */\n open?: boolean;\n}\n\n// Store network logs\nconst networkLogs: any[] = [];\nconst sseClients = new Set<ServerResponse>();\n\nfunction broadcastLog(log: any) {\n const data = JSON.stringify(log);\n sseClients.forEach(client => {\n client.write(`data: ${data}\\n\\n`);\n });\n}\n\nfunction getDashboardHTML(_port: number): string {\n return `\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Network Terminal</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body {\n font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;\n background: #0a0e17;\n color: #e2e8f0;\n height: 100vh;\n overflow: hidden;\n }\n .header {\n background: #0f172a;\n padding: 12px 24px;\n border-bottom: 2px solid #4ade80;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n .title { color: #4ade80; font-size: 18px; font-weight: bold; }\n .subtitle { color: #64748b; font-size: 12px; margin-top: 4px; }\n .controls { display: flex; gap: 12px; align-items: center; }\n .btn {\n background: #1e293b;\n border: 1px solid #334155;\n color: #94a3b8;\n padding: 8px 16px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 12px;\n font-family: inherit;\n }\n .btn:hover { background: #334155; color: #e2e8f0; }\n .container { display: flex; height: calc(100vh - 80px); }\n .panel {\n flex: 1;\n display: flex;\n flex-direction: column;\n border-right: 1px solid #1e293b;\n }\n .panel:last-child { border-right: none; }\n .panel-header {\n background: #0f172a;\n padding: 12px 16px;\n border-bottom: 1px solid #1e293b;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n .panel-title { font-weight: bold; font-size: 14px; }\n .panel-title.request { color: #3b82f6; }\n .panel-title.response { color: #22c55e; }\n .panel-count {\n background: #1e293b;\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n color: #64748b;\n }\n .logs { flex: 1; overflow-y: auto; padding: 8px; }\n .log-entry {\n background: #0f172a;\n border: 1px solid #1e293b;\n border-radius: 8px;\n margin-bottom: 8px;\n overflow: hidden;\n cursor: pointer;\n transition: border-color 0.2s;\n }\n .log-entry:hover { border-color: #334155; }\n .log-entry.selected { border-color: #4ade80; }\n .log-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 12px;\n background: #1e293b;\n }\n .method {\n font-weight: bold;\n font-size: 11px;\n padding: 3px 8px;\n border-radius: 4px;\n }\n .method.GET { background: #166534; color: #4ade80; }\n .method.POST { background: #1e40af; color: #60a5fa; }\n .method.PUT { background: #a16207; color: #fcd34d; }\n .method.DELETE { background: #991b1b; color: #fca5a5; }\n .method.PATCH { background: #7c3aed; color: #c4b5fd; }\n .status { font-size: 11px; padding: 3px 8px; border-radius: 4px; }\n .status.success { background: #166534; color: #4ade80; }\n .status.error { background: #991b1b; color: #fca5a5; }\n .status.pending { background: #374151; color: #9ca3af; }\n .url {\n flex: 1;\n font-size: 12px;\n color: #94a3b8;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .duration { font-size: 11px; color: #64748b; }\n .timestamp { font-size: 10px; color: #475569; }\n .log-body { padding: 12px; font-size: 11px; max-height: 300px; overflow: auto; }\n .log-body pre {\n margin: 0;\n white-space: pre-wrap;\n word-break: break-all;\n color: #cbd5e1;\n line-height: 1.5;\n }\n .log-body.empty { color: #475569; font-style: italic; }\n .empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: #475569;\n text-align: center;\n padding: 20px;\n }\n .empty-state svg { width: 48px; height: 48px; margin-bottom: 16px; opacity: 0.5; }\n .status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #22c55e;\n animation: pulse 2s infinite;\n }\n @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }\n .status-dot.disconnected { background: #ef4444; animation: none; }\n </style>\n</head>\n<body>\n <div class=\"header\">\n <div>\n <div class=\"title\">>_ Network Terminal</div>\n <div class=\"subtitle\">Vite Plugin - Monitoring fetch/XHR requests</div>\n </div>\n <div class=\"controls\">\n <div class=\"status-dot\" id=\"statusDot\"></div>\n <button class=\"btn\" onclick=\"clearLogs()\">Clear All</button>\n </div>\n </div>\n\n <div class=\"container\">\n <div class=\"panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title request\">⬆ REQUESTS</span>\n <span class=\"panel-count\" id=\"requestCount\">0</span>\n </div>\n <div class=\"logs\" id=\"requestLogs\">\n <div class=\"empty-state\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M12 19V5M5 12l7-7 7 7\"/>\n </svg>\n <div>Waiting for requests...</div>\n <div style=\"margin-top: 8px; font-size: 11px;\">Make fetch/XHR requests in your app</div>\n </div>\n </div>\n </div>\n\n <div class=\"panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title response\">⬇ RESPONSES</span>\n <span class=\"panel-count\" id=\"responseCount\">0</span>\n </div>\n <div class=\"logs\" id=\"responseLogs\">\n <div class=\"empty-state\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M12 5v14M5 12l7 7 7-7\"/>\n </svg>\n <div>Waiting for responses...</div>\n </div>\n </div>\n </div>\n </div>\n\n <script>\n const logs = new Map();\n let selectedId = null;\n\n function formatJson(data) {\n if (!data) return null;\n if (typeof data === 'string') {\n try { data = JSON.parse(data); }\n catch (e) { return escapeHtml(data.substring(0, 2000)); }\n }\n return escapeHtml(JSON.stringify(data, null, 2));\n }\n\n function escapeHtml(str) {\n if (typeof str !== 'string') str = String(str);\n return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');\n }\n\n function formatTime(timestamp) {\n const date = new Date(timestamp);\n return date.toLocaleTimeString('en-US', { hour12: false }) + '.' +\n String(date.getMilliseconds()).padStart(3, '0');\n }\n\n function getLogsArray() {\n return Array.from(logs.values()).sort((a, b) =>\n new Date(b.timestamp) - new Date(a.timestamp)\n );\n }\n\n function renderLogs() {\n const logsArray = getLogsArray();\n const requestContainer = document.getElementById('requestLogs');\n const responseContainer = document.getElementById('responseLogs');\n\n document.getElementById('requestCount').textContent = logsArray.length;\n document.getElementById('responseCount').textContent = logsArray.filter(l => l.status).length;\n\n if (logsArray.length === 0) {\n requestContainer.innerHTML = '<div class=\"empty-state\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M12 19V5M5 12l7-7 7 7\"/></svg><div>Waiting for requests...</div></div>';\n responseContainer.innerHTML = '<div class=\"empty-state\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M12 5v14M5 12l7 7 7-7\"/></svg><div>Waiting for responses...</div></div>';\n return;\n }\n\n requestContainer.innerHTML = logsArray.map(log => {\n const isSelected = selectedId === log.id;\n const bodyContent = formatJson(log.requestBody);\n return '<div class=\"log-entry '+(isSelected ? 'selected' : '')+'\" onclick=\"selectLog(\\\\''+log.id+'\\\\')\"><div class=\"log-header\"><span class=\"method '+log.method+'\">'+log.method+'</span><span class=\"url\" title=\"'+escapeHtml(log.url)+'\">'+log.url+'</span><span class=\"timestamp\">'+formatTime(log.timestamp)+'</span></div>'+(isSelected ? '<div class=\"log-body '+(bodyContent ? '' : 'empty')+'\">'+(bodyContent ? '<pre>'+bodyContent+'</pre>' : 'No request body')+'</div>' : '')+'</div>';\n }).join('');\n\n responseContainer.innerHTML = logsArray.map(log => {\n const isSelected = selectedId === log.id;\n const statusClass = !log.status ? 'pending' : log.status < 400 ? 'success' : 'error';\n const bodyContent = formatJson(log.responseBody);\n return '<div class=\"log-entry '+(isSelected ? 'selected' : '')+'\" onclick=\"selectLog(\\\\''+log.id+'\\\\')\"><div class=\"log-header\"><span class=\"status '+statusClass+'\">'+(log.status || '...')+'</span><span class=\"url\" title=\"'+escapeHtml(log.url)+'\">'+log.url+'</span><span class=\"duration\">'+(log.duration ? log.duration + 'ms' : '')+'</span></div>'+(isSelected ? '<div class=\"log-body '+(bodyContent ? '' : 'empty')+'\">'+(bodyContent ? '<pre>'+bodyContent+'</pre>' : (log.status ? 'No response body' : 'Pending...'))+'</div>' : '')+'</div>';\n }).join('');\n }\n\n function selectLog(id) {\n selectedId = selectedId === id ? null : id;\n renderLogs();\n }\n\n function clearLogs() {\n logs.clear();\n selectedId = null;\n renderLogs();\n fetch('/clear', { method: 'POST' });\n }\n\n function addLog(log) {\n logs.set(log.id, log);\n if (logs.size > 100) {\n const oldest = getLogsArray().pop();\n if (oldest) logs.delete(oldest.id);\n }\n renderLogs();\n }\n\n function connect() {\n const evtSource = new EventSource('/events');\n evtSource.onopen = () => document.getElementById('statusDot').classList.remove('disconnected');\n evtSource.onmessage = (event) => addLog(JSON.parse(event.data));\n evtSource.onerror = () => {\n document.getElementById('statusDot').classList.add('disconnected');\n evtSource.close();\n setTimeout(connect, 2000);\n };\n }\n\n connect();\n renderLogs();\n </script>\n</body>\n</html>\n`;\n}\n\nfunction getMonitorScript(port: number): string {\n return `\n(function() {\n if (window.__networkTerminalActive) return;\n window.__networkTerminalActive = true;\n\n var DASHBOARD_URL = 'http://localhost:${port}';\n\n function sendLog(log) {\n var xhr = new XMLHttpRequest();\n xhr.open('POST', DASHBOARD_URL + '/log', true);\n xhr.setRequestHeader('Content-Type', 'application/json');\n xhr.send(JSON.stringify(log));\n }\n\n function tryParse(data) {\n if (!data) return null;\n if (typeof data === 'object') return data;\n try { return JSON.parse(data); } catch (e) { return data; }\n }\n\n var originalFetch = window.fetch;\n window.fetch = function() {\n var args = arguments;\n var startTime = Date.now();\n var input = args[0];\n var options = args[1] || {};\n var url = typeof input === 'string' ? input : (input.url || input.toString());\n var method = (options.method || 'GET').toUpperCase();\n\n if (url.includes('localhost:${port}')) {\n return originalFetch.apply(this, args);\n }\n\n var log = {\n id: Date.now().toString() + Math.random().toString(36).substr(2, 9),\n method: method,\n url: url,\n timestamp: new Date().toISOString(),\n requestBody: options.body ? tryParse(options.body) : null,\n status: null,\n responseBody: null,\n duration: null,\n };\n\n sendLog(log);\n\n return originalFetch.apply(this, args).then(function(response) {\n log.status = response.status;\n log.duration = Date.now() - startTime;\n var cloned = response.clone();\n cloned.text().then(function(text) {\n log.responseBody = tryParse(text);\n sendLog(log);\n }).catch(function() { sendLog(log); });\n return response;\n }).catch(function(error) {\n log.status = 0;\n log.duration = Date.now() - startTime;\n log.error = error.message;\n sendLog(log);\n throw error;\n });\n };\n\n var OriginalXHR = window.XMLHttpRequest;\n window.XMLHttpRequest = function() {\n var xhr = new OriginalXHR();\n var method, url, requestBody, startTime;\n\n var originalOpen = xhr.open;\n xhr.open = function(m, u) {\n method = m;\n url = u;\n return originalOpen.apply(this, arguments);\n };\n\n var originalSend = xhr.send;\n xhr.send = function(body) {\n if (url && url.includes('localhost:${port}')) {\n return originalSend.apply(this, arguments);\n }\n\n startTime = Date.now();\n requestBody = body;\n\n var log = {\n id: Date.now().toString() + Math.random().toString(36).substr(2, 9),\n method: (method || 'GET').toUpperCase(),\n url: url,\n timestamp: new Date().toISOString(),\n requestBody: tryParse(requestBody),\n status: null,\n responseBody: null,\n duration: null,\n };\n\n sendLog(log);\n\n xhr.addEventListener('loadend', function() {\n log.status = xhr.status;\n log.duration = Date.now() - startTime;\n log.responseBody = tryParse(xhr.responseText);\n sendLog(log);\n });\n\n return originalSend.apply(this, arguments);\n };\n\n return xhr;\n };\n\n console.log('[Network Terminal] ✓ Active - Dashboard: http://localhost:${port}');\n})();\n`;\n}\n\nexport function networkTerminal(options: NetworkTerminalPluginOptions = {}): Plugin {\n const { port = 4001, open = true } = options;\n let dashboardServer: ReturnType<typeof createServer> | null = null;\n\n return {\n name: 'vite-plugin-network-terminal',\n apply: 'serve',\n\n configureServer(_server: ViteDevServer) {\n // Start dashboard server\n dashboardServer = createServer((req: IncomingMessage, res: ServerResponse) => {\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n if (req.url === '/events') {\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n });\n sseClients.add(res);\n networkLogs.forEach(log => {\n res.write(`data: ${JSON.stringify(log)}\\n\\n`);\n });\n req.on('close', () => sseClients.delete(res));\n return;\n }\n\n if (req.url === '/log' && req.method === 'POST') {\n let body = '';\n req.on('data', (chunk: Buffer) => body += chunk);\n req.on('end', () => {\n try {\n const log = JSON.parse(body);\n const existingIndex = networkLogs.findIndex(l => l.id === log.id);\n if (existingIndex >= 0) {\n networkLogs[existingIndex] = log;\n } else {\n networkLogs.unshift(log);\n if (networkLogs.length > 100) networkLogs.pop();\n }\n broadcastLog(log);\n } catch (e) {}\n res.writeHead(200);\n res.end('OK');\n });\n return;\n }\n\n if (req.url === '/clear' && req.method === 'POST') {\n networkLogs.length = 0;\n res.writeHead(200);\n res.end('OK');\n return;\n }\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getDashboardHTML(port));\n });\n\n dashboardServer.listen(port, () => {\n console.log(`\\n \\x1b[32m>_ Network Terminal\\x1b[0m`);\n console.log(` Dashboard: \\x1b[36mhttp://localhost:${port}\\x1b[0m\\n`);\n\n if (open) {\n const openCmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';\n require('child_process').exec(`${openCmd} http://localhost:${port}`);\n }\n });\n\n dashboardServer.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n console.log(`\\n \\x1b[33m[Network Terminal]\\x1b[0m Port ${port} in use, dashboard disabled\\n`);\n }\n });\n },\n\n transformIndexHtml(html: string) {\n const script = `<script>${getMonitorScript(port)}</script>`;\n if (html.includes('</head>')) {\n return html.replace('</head>', script + '</head>');\n }\n return script + html;\n },\n\n buildEnd() {\n if (dashboardServer) {\n dashboardServer.close();\n }\n },\n };\n}\n\nexport default networkTerminal;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/vite-plugin.ts"],"sourcesContent":["import type { Plugin, ViteDevServer } from 'vite';\nimport { createServer, IncomingMessage, ServerResponse } from 'http';\nimport { exec } from 'child_process';\n\nexport interface NetworkTerminalPluginOptions {\n /** Port for the dashboard (default: 4001) */\n port?: number;\n /** Auto-open dashboard in browser (default: true) */\n open?: boolean;\n}\n\n// Store network logs\nconst networkLogs: any[] = [];\nconst sseClients = new Set<ServerResponse>();\n\nfunction broadcastLog(log: any) {\n const data = JSON.stringify(log);\n sseClients.forEach(client => {\n client.write(`data: ${data}\\n\\n`);\n });\n}\n\nfunction getDashboardHTML(_port: number): string {\n return `\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Network Terminal</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body {\n font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;\n background: #0a0e17;\n color: #e2e8f0;\n height: 100vh;\n overflow: hidden;\n }\n .header {\n background: #0f172a;\n padding: 12px 24px;\n border-bottom: 2px solid #4ade80;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n .title { color: #4ade80; font-size: 18px; font-weight: bold; }\n .subtitle { color: #64748b; font-size: 12px; margin-top: 4px; }\n .controls { display: flex; gap: 12px; align-items: center; }\n .btn {\n background: #1e293b;\n border: 1px solid #334155;\n color: #94a3b8;\n padding: 8px 16px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 12px;\n font-family: inherit;\n }\n .btn:hover { background: #334155; color: #e2e8f0; }\n .container { display: flex; height: calc(100vh - 80px); }\n .panel {\n flex: 1;\n display: flex;\n flex-direction: column;\n border-right: 1px solid #1e293b;\n }\n .panel:last-child { border-right: none; }\n .panel-header {\n background: #0f172a;\n padding: 12px 16px;\n border-bottom: 1px solid #1e293b;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n .panel-title { font-weight: bold; font-size: 14px; }\n .panel-title.request { color: #3b82f6; }\n .panel-title.response { color: #22c55e; }\n .panel-count {\n background: #1e293b;\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n color: #64748b;\n }\n .logs { flex: 1; overflow-y: auto; padding: 8px; }\n .log-entry {\n background: #0f172a;\n border: 1px solid #1e293b;\n border-radius: 8px;\n margin-bottom: 8px;\n overflow: hidden;\n cursor: pointer;\n transition: border-color 0.2s;\n }\n .log-entry:hover { border-color: #334155; }\n .log-entry.selected { border-color: #4ade80; }\n .log-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 12px;\n background: #1e293b;\n }\n .method {\n font-weight: bold;\n font-size: 11px;\n padding: 3px 8px;\n border-radius: 4px;\n }\n .method.GET { background: #166534; color: #4ade80; }\n .method.POST { background: #1e40af; color: #60a5fa; }\n .method.PUT { background: #a16207; color: #fcd34d; }\n .method.DELETE { background: #991b1b; color: #fca5a5; }\n .method.PATCH { background: #7c3aed; color: #c4b5fd; }\n .status { font-size: 11px; padding: 3px 8px; border-radius: 4px; }\n .status.success { background: #166534; color: #4ade80; }\n .status.error { background: #991b1b; color: #fca5a5; }\n .status.pending { background: #374151; color: #9ca3af; }\n .url {\n flex: 1;\n font-size: 12px;\n color: #94a3b8;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .duration { font-size: 11px; color: #64748b; }\n .timestamp { font-size: 10px; color: #475569; }\n .log-body { padding: 12px; font-size: 11px; max-height: 300px; overflow: auto; }\n .log-body pre {\n margin: 0;\n white-space: pre-wrap;\n word-break: break-all;\n color: #cbd5e1;\n line-height: 1.5;\n }\n .log-body.empty { color: #475569; font-style: italic; }\n .empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: #475569;\n text-align: center;\n padding: 20px;\n }\n .empty-state svg { width: 48px; height: 48px; margin-bottom: 16px; opacity: 0.5; }\n .status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #22c55e;\n animation: pulse 2s infinite;\n }\n @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }\n .status-dot.disconnected { background: #ef4444; animation: none; }\n </style>\n</head>\n<body>\n <div class=\"header\">\n <div>\n <div class=\"title\">>_ Network Terminal</div>\n <div class=\"subtitle\">Vite Plugin - Monitoring fetch/XHR requests</div>\n </div>\n <div class=\"controls\">\n <div class=\"status-dot\" id=\"statusDot\"></div>\n <button class=\"btn\" onclick=\"clearLogs()\">Clear All</button>\n </div>\n </div>\n\n <div class=\"container\">\n <div class=\"panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title request\">⬆ REQUESTS</span>\n <span class=\"panel-count\" id=\"requestCount\">0</span>\n </div>\n <div class=\"logs\" id=\"requestLogs\">\n <div class=\"empty-state\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M12 19V5M5 12l7-7 7 7\"/>\n </svg>\n <div>Waiting for requests...</div>\n <div style=\"margin-top: 8px; font-size: 11px;\">Make fetch/XHR requests in your app</div>\n </div>\n </div>\n </div>\n\n <div class=\"panel\">\n <div class=\"panel-header\">\n <span class=\"panel-title response\">⬇ RESPONSES</span>\n <span class=\"panel-count\" id=\"responseCount\">0</span>\n </div>\n <div class=\"logs\" id=\"responseLogs\">\n <div class=\"empty-state\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M12 5v14M5 12l7 7 7-7\"/>\n </svg>\n <div>Waiting for responses...</div>\n </div>\n </div>\n </div>\n </div>\n\n <script>\n const logs = new Map();\n let selectedId = null;\n\n function formatJson(data) {\n if (!data) return null;\n if (typeof data === 'string') {\n try { data = JSON.parse(data); }\n catch (e) { return escapeHtml(data.substring(0, 2000)); }\n }\n return escapeHtml(JSON.stringify(data, null, 2));\n }\n\n function escapeHtml(str) {\n if (typeof str !== 'string') str = String(str);\n return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');\n }\n\n function formatTime(timestamp) {\n const date = new Date(timestamp);\n return date.toLocaleTimeString('en-US', { hour12: false }) + '.' +\n String(date.getMilliseconds()).padStart(3, '0');\n }\n\n function getLogsArray() {\n return Array.from(logs.values()).sort((a, b) =>\n new Date(b.timestamp) - new Date(a.timestamp)\n );\n }\n\n function renderLogs() {\n const logsArray = getLogsArray();\n const requestContainer = document.getElementById('requestLogs');\n const responseContainer = document.getElementById('responseLogs');\n\n document.getElementById('requestCount').textContent = logsArray.length;\n document.getElementById('responseCount').textContent = logsArray.filter(l => l.status).length;\n\n if (logsArray.length === 0) {\n requestContainer.innerHTML = '<div class=\"empty-state\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M12 19V5M5 12l7-7 7 7\"/></svg><div>Waiting for requests...</div></div>';\n responseContainer.innerHTML = '<div class=\"empty-state\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M12 5v14M5 12l7 7 7-7\"/></svg><div>Waiting for responses...</div></div>';\n return;\n }\n\n requestContainer.innerHTML = logsArray.map(log => {\n const isSelected = selectedId === log.id;\n const bodyContent = formatJson(log.requestBody);\n return '<div class=\"log-entry '+(isSelected ? 'selected' : '')+'\" onclick=\"selectLog(\\\\''+log.id+'\\\\')\"><div class=\"log-header\"><span class=\"method '+log.method+'\">'+log.method+'</span><span class=\"url\" title=\"'+escapeHtml(log.url)+'\">'+log.url+'</span><span class=\"timestamp\">'+formatTime(log.timestamp)+'</span></div>'+(isSelected ? '<div class=\"log-body '+(bodyContent ? '' : 'empty')+'\">'+(bodyContent ? '<pre>'+bodyContent+'</pre>' : 'No request body')+'</div>' : '')+'</div>';\n }).join('');\n\n responseContainer.innerHTML = logsArray.map(log => {\n const isSelected = selectedId === log.id;\n const statusClass = !log.status ? 'pending' : log.status < 400 ? 'success' : 'error';\n const bodyContent = formatJson(log.responseBody);\n return '<div class=\"log-entry '+(isSelected ? 'selected' : '')+'\" onclick=\"selectLog(\\\\''+log.id+'\\\\')\"><div class=\"log-header\"><span class=\"status '+statusClass+'\">'+(log.status || '...')+'</span><span class=\"url\" title=\"'+escapeHtml(log.url)+'\">'+log.url+'</span><span class=\"duration\">'+(log.duration ? log.duration + 'ms' : '')+'</span></div>'+(isSelected ? '<div class=\"log-body '+(bodyContent ? '' : 'empty')+'\">'+(bodyContent ? '<pre>'+bodyContent+'</pre>' : (log.status ? 'No response body' : 'Pending...'))+'</div>' : '')+'</div>';\n }).join('');\n }\n\n function selectLog(id) {\n selectedId = selectedId === id ? null : id;\n renderLogs();\n }\n\n function clearLogs() {\n logs.clear();\n selectedId = null;\n renderLogs();\n fetch('/clear', { method: 'POST' });\n }\n\n function addLog(log) {\n logs.set(log.id, log);\n if (logs.size > 100) {\n const oldest = getLogsArray().pop();\n if (oldest) logs.delete(oldest.id);\n }\n renderLogs();\n }\n\n function connect() {\n const evtSource = new EventSource('/events');\n evtSource.onopen = () => document.getElementById('statusDot').classList.remove('disconnected');\n evtSource.onmessage = (event) => addLog(JSON.parse(event.data));\n evtSource.onerror = () => {\n document.getElementById('statusDot').classList.add('disconnected');\n evtSource.close();\n setTimeout(connect, 2000);\n };\n }\n\n connect();\n renderLogs();\n </script>\n</body>\n</html>\n`;\n}\n\nfunction getMonitorScript(port: number): string {\n return `\n(function() {\n if (window.__networkTerminalActive) return;\n window.__networkTerminalActive = true;\n\n var DASHBOARD_URL = 'http://localhost:${port}';\n\n function sendLog(log) {\n var xhr = new XMLHttpRequest();\n xhr.open('POST', DASHBOARD_URL + '/log', true);\n xhr.setRequestHeader('Content-Type', 'application/json');\n xhr.send(JSON.stringify(log));\n }\n\n function tryParse(data) {\n if (!data) return null;\n if (typeof data === 'object') return data;\n try { return JSON.parse(data); } catch (e) { return data; }\n }\n\n var originalFetch = window.fetch;\n window.fetch = function() {\n var args = arguments;\n var startTime = Date.now();\n var input = args[0];\n var options = args[1] || {};\n var url = typeof input === 'string' ? input : (input.url || input.toString());\n var method = (options.method || 'GET').toUpperCase();\n\n if (url.includes('localhost:${port}')) {\n return originalFetch.apply(this, args);\n }\n\n var log = {\n id: Date.now().toString() + Math.random().toString(36).substr(2, 9),\n method: method,\n url: url,\n timestamp: new Date().toISOString(),\n requestBody: options.body ? tryParse(options.body) : null,\n status: null,\n responseBody: null,\n duration: null,\n };\n\n sendLog(log);\n\n return originalFetch.apply(this, args).then(function(response) {\n log.status = response.status;\n log.duration = Date.now() - startTime;\n var cloned = response.clone();\n cloned.text().then(function(text) {\n log.responseBody = tryParse(text);\n sendLog(log);\n }).catch(function() { sendLog(log); });\n return response;\n }).catch(function(error) {\n log.status = 0;\n log.duration = Date.now() - startTime;\n log.error = error.message;\n sendLog(log);\n throw error;\n });\n };\n\n var OriginalXHR = window.XMLHttpRequest;\n window.XMLHttpRequest = function() {\n var xhr = new OriginalXHR();\n var method, url, requestBody, startTime;\n\n var originalOpen = xhr.open;\n xhr.open = function(m, u) {\n method = m;\n url = u;\n return originalOpen.apply(this, arguments);\n };\n\n var originalSend = xhr.send;\n xhr.send = function(body) {\n if (url && url.includes('localhost:${port}')) {\n return originalSend.apply(this, arguments);\n }\n\n startTime = Date.now();\n requestBody = body;\n\n var log = {\n id: Date.now().toString() + Math.random().toString(36).substr(2, 9),\n method: (method || 'GET').toUpperCase(),\n url: url,\n timestamp: new Date().toISOString(),\n requestBody: tryParse(requestBody),\n status: null,\n responseBody: null,\n duration: null,\n };\n\n sendLog(log);\n\n xhr.addEventListener('loadend', function() {\n log.status = xhr.status;\n log.duration = Date.now() - startTime;\n log.responseBody = tryParse(xhr.responseText);\n sendLog(log);\n });\n\n return originalSend.apply(this, arguments);\n };\n\n return xhr;\n };\n\n console.log('[Network Terminal] ✓ Active - Dashboard: http://localhost:${port}');\n})();\n`;\n}\n\nexport function networkTerminal(options: NetworkTerminalPluginOptions = {}): Plugin {\n const { port = 4001, open = true } = options;\n let dashboardServer: ReturnType<typeof createServer> | null = null;\n\n return {\n name: 'vite-plugin-network-terminal',\n apply: 'serve',\n\n configureServer(_server: ViteDevServer) {\n // Start dashboard server\n dashboardServer = createServer((req: IncomingMessage, res: ServerResponse) => {\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n if (req.url === '/events') {\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n });\n sseClients.add(res);\n networkLogs.forEach(log => {\n res.write(`data: ${JSON.stringify(log)}\\n\\n`);\n });\n req.on('close', () => sseClients.delete(res));\n return;\n }\n\n if (req.url === '/log' && req.method === 'POST') {\n let body = '';\n req.on('data', (chunk: Buffer) => body += chunk);\n req.on('end', () => {\n try {\n const log = JSON.parse(body);\n const existingIndex = networkLogs.findIndex(l => l.id === log.id);\n if (existingIndex >= 0) {\n networkLogs[existingIndex] = log;\n } else {\n networkLogs.unshift(log);\n if (networkLogs.length > 100) networkLogs.pop();\n }\n broadcastLog(log);\n } catch (e) {}\n res.writeHead(200);\n res.end('OK');\n });\n return;\n }\n\n if (req.url === '/clear' && req.method === 'POST') {\n networkLogs.length = 0;\n res.writeHead(200);\n res.end('OK');\n return;\n }\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getDashboardHTML(port));\n });\n\n dashboardServer.listen(port, () => {\n console.log(`\\n \\x1b[32m>_ Network Terminal\\x1b[0m`);\n console.log(` Dashboard: \\x1b[36mhttp://localhost:${port}\\x1b[0m\\n`);\n\n if (open) {\n const openCmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';\n exec(`${openCmd} http://localhost:${port}`);\n }\n });\n\n dashboardServer.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n console.log(`\\n \\x1b[33m[Network Terminal]\\x1b[0m Port ${port} in use, dashboard disabled\\n`);\n }\n });\n },\n\n transformIndexHtml(html: string) {\n const script = `<script>${getMonitorScript(port)}</script>`;\n if (html.includes('</head>')) {\n return html.replace('</head>', script + '</head>');\n }\n return script + html;\n },\n\n buildEnd() {\n if (dashboardServer) {\n dashboardServer.close();\n }\n },\n };\n}\n\nexport default networkTerminal;\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "network-terminal",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.12",
|
|
4
4
|
"description": "A browser-based terminal UI for monitoring Fetch/XHR requests with real-time display of routes, payloads, and responses",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|