/**
* ds-network-monitor.js
* Network request monitoring and debugging
*
* REFACTORED: DSS-compliant version using DSBaseTool + table-template.js
* - Extends DSBaseTool for Shadow DOM, AbortController, and standardized lifecycle
* - Uses table-template.js for DSS-compliant table rendering (NO inline events/styles)
* - Event delegation pattern for all interactions
* - Logger utility instead of console.*
*
* Reference: .knowledge/dss-coding-standards.json
*/
import DSBaseTool from '../base/ds-base-tool.js';
import toolBridge from '../../services/tool-bridge.js';
import { ComponentHelpers } from '../../utils/component-helpers.js';
import { logger } from '../../utils/logger.js';
import { createTableView, setupTableEvents, createStatsCard } from '../../templates/table-template.js';
class DSNetworkMonitor extends DSBaseTool {
constructor() {
super();
this.requests = [];
this.filteredRequests = [];
this.filterUrl = '';
this.filterType = 'all';
this.autoRefresh = false;
this.refreshInterval = null;
}
connectedCallback() {
super.connectedCallback();
this.loadRequests();
}
disconnectedCallback() {
if (this.refreshInterval) {
clearInterval(this.refreshInterval);
}
super.disconnectedCallback();
}
/**
* Render the component (required by DSBaseTool)
*/
render() {
this.shadowRoot.innerHTML = `
`;
}
/**
* Setup event listeners (required by DSBaseTool)
*/
setupEventListeners() {
// EVENT-002: Event delegation
this.delegateEvents('.network-monitor-container', 'click', (action, e) => {
if (action === 'refresh') {
this.loadRequests();
}
});
// Filter input with debounce
const filterInput = this.$('#network-filter');
if (filterInput) {
const debouncedFilter = ComponentHelpers.debounce((term) => {
this.filterUrl = term.toLowerCase();
this.applyFilters();
}, 300);
this.bindEvent(filterInput, 'input', (e) => debouncedFilter(e.target.value));
}
// Type filter
const typeFilter = this.$('#network-type-filter');
if (typeFilter) {
this.bindEvent(typeFilter, 'change', (e) => {
this.filterType = e.target.value;
this.applyFilters();
});
}
// Auto-refresh toggle
const autoRefreshToggle = this.$('#auto-refresh-toggle');
if (autoRefreshToggle) {
this.bindEvent(autoRefreshToggle, 'change', (e) => {
this.autoRefresh = e.target.checked;
if (this.autoRefresh) {
this.refreshInterval = setInterval(() => this.loadRequests(), 2000);
logger.debug('[DSNetworkMonitor] Auto-refresh enabled');
} else {
if (this.refreshInterval) {
clearInterval(this.refreshInterval);
this.refreshInterval = null;
logger.debug('[DSNetworkMonitor] Auto-refresh disabled');
}
}
});
}
}
async loadRequests() {
const content = this.$('#network-content');
if (!content) return;
// Only show loading on first load
if (this.requests.length === 0) {
content.innerHTML = '⏳
Loading network requests...
';
}
try {
const result = await toolBridge.getNetworkRequests(null, 100);
if (result && result.requests) {
this.requests = result.requests;
this.updateTypeFilter();
this.applyFilters();
logger.debug('[DSNetworkMonitor] Loaded requests', { count: this.requests.length });
} else {
this.requests = [];
content.innerHTML = '🌐
No network requests captured
';
}
} catch (error) {
logger.error('[DSNetworkMonitor] Failed to load network requests', error);
content.innerHTML = ComponentHelpers.renderError('Failed to load network requests', error);
}
}
updateTypeFilter() {
const typeFilter = this.$('#network-type-filter');
if (!typeFilter) return;
const types = this.getResourceTypes();
const currentValue = typeFilter.value;
typeFilter.innerHTML = `
${types.map(type => ``).join('')}
`;
}
applyFilters() {
let filtered = [...this.requests];
// Filter by URL
if (this.filterUrl) {
filtered = filtered.filter(req =>
req.url.toLowerCase().includes(this.filterUrl) ||
req.method.toLowerCase().includes(this.filterUrl)
);
}
// Filter by type
if (this.filterType !== 'all') {
filtered = filtered.filter(req => req.resourceType === this.filterType);
}
this.filteredRequests = filtered;
this.renderRequests();
}
getResourceTypes() {
if (!this.requests) return [];
const types = new Set(this.requests.map(r => r.resourceType).filter(Boolean));
return Array.from(types).sort();
}
getStatusColor(status) {
if (status >= 200 && status < 300) return 'success';
if (status >= 300 && status < 400) return 'info';
if (status >= 400 && status < 500) return 'warning';
if (status >= 500) return 'error';
return 'info';
}
renderRequests() {
const content = this.$('#network-content');
if (!content) return;
if (!this.filteredRequests || this.filteredRequests.length === 0) {
content.innerHTML = `
🔍
${this.filterUrl ? 'No requests match your filter' : 'No network requests captured yet'}
`;
return;
}
// Render info count
const infoHtml = `
Showing ${this.filteredRequests.length} of ${this.requests.length} requests
${this.autoRefresh ? '• Auto-refreshing every 2s' : ''}
`;
// Use table-template.js for DSS-compliant rendering
const { html: tableHtml, styles: tableStyles } = createTableView({
columns: [
{ header: 'Method', key: 'method', width: '80px', align: 'left' },
{ header: 'Status', key: 'status', width: '80px', align: 'left' },
{ header: 'URL', key: 'url', align: 'left' },
{ header: 'Type', key: 'resourceType', width: '100px', align: 'left' },
{ header: 'Time', key: 'timing', width: '80px', align: 'left' }
],
rows: this.filteredRequests,
renderCell: (col, row) => this.renderCell(col, row),
renderDetails: (row) => this.renderDetails(row),
emptyMessage: 'No network requests',
emptyIcon: '🌐'
});
// Adopt table styles
this.adoptStyles(tableStyles);
// Render table
content.innerHTML = infoHtml + tableHtml + '💡 Click any row to view full request details
';
// Setup table event handlers
setupTableEvents(this.shadowRoot);
logger.debug('[DSNetworkMonitor] Rendered requests', { count: this.filteredRequests.length });
}
renderCell(col, row) {
const method = row.method || 'GET';
const status = row.status || '-';
const statusColor = this.getStatusColor(status);
const resourceType = row.resourceType || 'other';
const url = row.url || 'Unknown URL';
const timing = row.timing ? `${Math.round(row.timing)}ms` : '-';
switch (col.key) {
case 'method':
const methodColor = method === 'GET' ? 'info' : method === 'POST' ? 'success' : 'warning';
return `${this.escapeHtml(method)}`;
case 'status':
return `${this.escapeHtml(String(status))}`;
case 'url':
return `${this.escapeHtml(url)}`;
case 'resourceType':
return `${this.escapeHtml(resourceType)}`;
case 'timing':
return `${timing}`;
default:
return this.escapeHtml(String(row[col.key] || '-'));
}
}
renderDetails(row) {
const method = row.method || 'GET';
const status = row.status || '-';
const url = row.url || 'Unknown URL';
const resourceType = row.resourceType || 'other';
return `
URL:
${this.escapeHtml(url)}
Method:
${this.escapeHtml(method)}
Status:
${this.escapeHtml(String(status))}
Type:
${this.escapeHtml(resourceType)}
${row.headers ? `
Headers:
${this.escapeHtml(JSON.stringify(row.headers, null, 2))}
` : ''}
`;
}
}
customElements.define('ds-network-monitor', DSNetworkMonitor);
export default DSNetworkMonitor;