@api.global/typedserver
Version:
A TypeScript-based project for easy serving of static files with support for live reloading, compression, and typed requests.
1,056 lines (984 loc) • 61.2 kB
JavaScript
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
var _, done = false;
for (var i = decorators.length - 1; i >= 0; i--) {
var context = {};
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
if (kind === "accessor") {
if (result === void 0) continue;
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
if (_ = accept(result.get)) descriptor.get = _;
if (_ = accept(result.set)) descriptor.set = _;
if (_ = accept(result.init)) initializers.unshift(_);
}
else if (_ = accept(result)) {
if (kind === "field") initializers.unshift(_);
else descriptor[key] = _;
}
}
if (target) Object.defineProperty(target, contextIn.name, descriptor);
done = true;
};
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
var useValue = arguments.length > 2;
for (var i = 0; i < initializers.length; i++) {
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
}
return useValue ? value : void 0;
};
import { LitElement, html, css, property, state, customElement, DeesContextmenu } from './plugins.js';
import { sharedStyles, panelStyles, tableStyles, buttonStyles } from './sw-dash-styles.js';
/**
* TypedRequest traffic monitoring panel for sw-dash
*
* Receives logs, stats, and methods via properties from parent (sw-dash-app).
* Filtering is done locally.
* Load more and clear operations dispatch events to parent.
*/
let SwDashRequests = (() => {
let _classDecorators = [customElement('sw-dash-requests')];
let _classDescriptor;
let _classExtraInitializers = [];
let _classThis;
let _classSuper = LitElement;
let _logs_decorators;
let _logs_initializers = [];
let _logs_extraInitializers = [];
let _totalCount_decorators;
let _totalCount_initializers = [];
let _totalCount_extraInitializers = [];
let _stats_decorators;
let _stats_initializers = [];
let _stats_extraInitializers = [];
let _methods_decorators;
let _methods_initializers = [];
let _methods_extraInitializers = [];
let _directionFilter_decorators;
let _directionFilter_initializers = [];
let _directionFilter_extraInitializers = [];
let _phaseFilter_decorators;
let _phaseFilter_initializers = [];
let _phaseFilter_extraInitializers = [];
let _methodFilter_decorators;
let _methodFilter_initializers = [];
let _methodFilter_extraInitializers = [];
let _searchText_decorators;
let _searchText_initializers = [];
let _searchText_extraInitializers = [];
let _isLoadingMore_decorators;
let _isLoadingMore_initializers = [];
let _isLoadingMore_extraInitializers = [];
let _modalOpen_decorators;
let _modalOpen_initializers = [];
let _modalOpen_extraInitializers = [];
let _selectedGroup_decorators;
let _selectedGroup_initializers = [];
let _selectedGroup_extraInitializers = [];
var SwDashRequests = class extends _classSuper {
static { _classThis = this; }
static {
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
_logs_decorators = [property({ type: Array })];
_totalCount_decorators = [property({ type: Number })];
_stats_decorators = [property({ type: Object })];
_methods_decorators = [property({ type: Array })];
_directionFilter_decorators = [state()];
_phaseFilter_decorators = [state()];
_methodFilter_decorators = [state()];
_searchText_decorators = [state()];
_isLoadingMore_decorators = [state()];
_modalOpen_decorators = [state()];
_selectedGroup_decorators = [state()];
__esDecorate(this, null, _logs_decorators, { kind: "accessor", name: "logs", static: false, private: false, access: { has: obj => "logs" in obj, get: obj => obj.logs, set: (obj, value) => { obj.logs = value; } }, metadata: _metadata }, _logs_initializers, _logs_extraInitializers);
__esDecorate(this, null, _totalCount_decorators, { kind: "accessor", name: "totalCount", static: false, private: false, access: { has: obj => "totalCount" in obj, get: obj => obj.totalCount, set: (obj, value) => { obj.totalCount = value; } }, metadata: _metadata }, _totalCount_initializers, _totalCount_extraInitializers);
__esDecorate(this, null, _stats_decorators, { kind: "accessor", name: "stats", static: false, private: false, access: { has: obj => "stats" in obj, get: obj => obj.stats, set: (obj, value) => { obj.stats = value; } }, metadata: _metadata }, _stats_initializers, _stats_extraInitializers);
__esDecorate(this, null, _methods_decorators, { kind: "accessor", name: "methods", static: false, private: false, access: { has: obj => "methods" in obj, get: obj => obj.methods, set: (obj, value) => { obj.methods = value; } }, metadata: _metadata }, _methods_initializers, _methods_extraInitializers);
__esDecorate(this, null, _directionFilter_decorators, { kind: "accessor", name: "directionFilter", static: false, private: false, access: { has: obj => "directionFilter" in obj, get: obj => obj.directionFilter, set: (obj, value) => { obj.directionFilter = value; } }, metadata: _metadata }, _directionFilter_initializers, _directionFilter_extraInitializers);
__esDecorate(this, null, _phaseFilter_decorators, { kind: "accessor", name: "phaseFilter", static: false, private: false, access: { has: obj => "phaseFilter" in obj, get: obj => obj.phaseFilter, set: (obj, value) => { obj.phaseFilter = value; } }, metadata: _metadata }, _phaseFilter_initializers, _phaseFilter_extraInitializers);
__esDecorate(this, null, _methodFilter_decorators, { kind: "accessor", name: "methodFilter", static: false, private: false, access: { has: obj => "methodFilter" in obj, get: obj => obj.methodFilter, set: (obj, value) => { obj.methodFilter = value; } }, metadata: _metadata }, _methodFilter_initializers, _methodFilter_extraInitializers);
__esDecorate(this, null, _searchText_decorators, { kind: "accessor", name: "searchText", static: false, private: false, access: { has: obj => "searchText" in obj, get: obj => obj.searchText, set: (obj, value) => { obj.searchText = value; } }, metadata: _metadata }, _searchText_initializers, _searchText_extraInitializers);
__esDecorate(this, null, _isLoadingMore_decorators, { kind: "accessor", name: "isLoadingMore", static: false, private: false, access: { has: obj => "isLoadingMore" in obj, get: obj => obj.isLoadingMore, set: (obj, value) => { obj.isLoadingMore = value; } }, metadata: _metadata }, _isLoadingMore_initializers, _isLoadingMore_extraInitializers);
__esDecorate(this, null, _modalOpen_decorators, { kind: "accessor", name: "modalOpen", static: false, private: false, access: { has: obj => "modalOpen" in obj, get: obj => obj.modalOpen, set: (obj, value) => { obj.modalOpen = value; } }, metadata: _metadata }, _modalOpen_initializers, _modalOpen_extraInitializers);
__esDecorate(this, null, _selectedGroup_decorators, { kind: "accessor", name: "selectedGroup", static: false, private: false, access: { has: obj => "selectedGroup" in obj, get: obj => obj.selectedGroup, set: (obj, value) => { obj.selectedGroup = value; } }, metadata: _metadata }, _selectedGroup_initializers, _selectedGroup_extraInitializers);
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
SwDashRequests = _classThis = _classDescriptor.value;
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
}
static styles = [
sharedStyles,
panelStyles,
tableStyles,
buttonStyles,
css `
:host {
display: block;
}
.requests-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--space-4);
gap: var(--space-3);
flex-wrap: wrap;
}
.filter-group {
display: flex;
align-items: center;
gap: var(--space-2);
}
.filter-label {
font-size: 12px;
color: var(--text-tertiary);
}
.filter-select {
background: var(--bg-secondary);
border: 1px solid var(--border-default);
border-radius: var(--radius-sm);
padding: var(--space-1) var(--space-2);
color: var(--text-primary);
font-size: 12px;
}
.filter-select:focus {
outline: none;
border-color: var(--accent-primary);
}
.requests-list {
display: flex;
flex-direction: column;
gap: var(--space-2);
max-height: 600px;
overflow-y: auto;
}
.request-card {
background: var(--bg-secondary);
border: 1px solid var(--border-default);
border-radius: var(--radius-md);
padding: var(--space-3);
}
.request-card.has-error {
border-color: var(--accent-error);
}
.request-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: var(--space-2);
gap: var(--space-2);
}
.request-badges {
display: flex;
gap: var(--space-2);
align-items: center;
flex-wrap: wrap;
}
.badge {
display: inline-flex;
align-items: center;
padding: var(--space-1) var(--space-2);
border-radius: var(--radius-sm);
font-size: 11px;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.badge.direction-outgoing { background: rgba(59, 130, 246, 0.15); color: #3b82f6; }
.badge.direction-incoming { background: rgba(34, 197, 94, 0.15); color: var(--accent-success); }
.badge.phase-request { background: rgba(251, 191, 36, 0.15); color: var(--accent-warning); }
.badge.phase-response { background: rgba(99, 102, 241, 0.15); color: var(--accent-primary); }
.badge.error { background: rgba(239, 68, 68, 0.15); color: var(--accent-error); }
.method-name {
font-size: 13px;
font-weight: 600;
color: var(--text-primary);
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
}
.request-meta {
display: flex;
gap: var(--space-3);
align-items: center;
font-size: 11px;
color: var(--text-tertiary);
}
.request-time {
font-variant-numeric: tabular-nums;
}
.request-duration {
color: var(--accent-success);
}
.request-duration.slow {
color: var(--accent-warning);
}
.request-duration.very-slow {
color: var(--accent-error);
}
.request-error {
font-size: 12px;
color: var(--accent-error);
background: rgba(239, 68, 68, 0.1);
padding: var(--space-2);
border-radius: var(--radius-sm);
margin-top: var(--space-2);
}
.stats-bar {
display: flex;
gap: var(--space-4);
margin-bottom: var(--space-4);
padding: var(--space-3);
background: var(--bg-secondary);
border-radius: var(--radius-md);
border: 1px solid var(--border-default);
flex-wrap: wrap;
}
.stat-item {
display: flex;
flex-direction: column;
gap: var(--space-1);
}
.stat-value {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
font-variant-numeric: tabular-nums;
}
.stat-value.error {
color: var(--accent-error);
}
.stat-label {
font-size: 11px;
color: var(--text-tertiary);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.method-stats {
margin-bottom: var(--space-4);
}
.method-stats-title {
font-size: 12px;
font-weight: 500;
color: var(--text-secondary);
margin-bottom: var(--space-2);
}
.method-stats-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: var(--space-2);
}
.method-stat-card {
background: var(--bg-tertiary);
border-radius: var(--radius-sm);
padding: var(--space-2);
cursor: pointer;
transition: background 0.15s ease;
}
.method-stat-card:hover {
background: var(--bg-secondary);
}
.method-stat-card.active {
background: rgba(99, 102, 241, 0.15);
border: 1px solid var(--accent-primary);
}
.method-stat-name {
font-size: 11px;
font-weight: 500;
color: var(--text-primary);
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
margin-bottom: var(--space-1);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.method-stat-details {
display: flex;
gap: var(--space-3);
font-size: 10px;
color: var(--text-tertiary);
}
.empty-state {
text-align: center;
padding: var(--space-6);
color: var(--text-tertiary);
}
.clear-btn {
background: rgba(239, 68, 68, 0.1);
color: var(--accent-error);
border: 1px solid transparent;
}
.clear-btn:hover {
background: rgba(239, 68, 68, 0.2);
border-color: var(--accent-error);
}
.pagination {
display: flex;
justify-content: center;
align-items: center;
gap: var(--space-2);
margin-top: var(--space-4);
}
.page-info {
font-size: 12px;
color: var(--text-tertiary);
}
.correlation-id {
font-size: 10px;
color: var(--text-tertiary);
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
}
/* Grouped request card */
.request-card .request-response-badges {
display: flex;
gap: var(--space-2);
margin-top: var(--space-1);
}
.request-card .status-badge {
font-size: 10px;
padding: 2px 6px;
border-radius: var(--radius-sm);
}
.status-badge.has-request {
background: rgba(251, 191, 36, 0.15);
color: var(--accent-warning);
}
.status-badge.has-response {
background: rgba(99, 102, 241, 0.15);
color: var(--accent-primary);
}
.status-badge.pending {
background: rgba(156, 163, 175, 0.15);
color: var(--text-tertiary);
}
.btn-show-payload {
background: var(--bg-tertiary);
border: 1px solid var(--border-default);
color: var(--accent-primary);
font-size: 11px;
padding: var(--space-1) var(--space-2);
border-radius: var(--radius-sm);
cursor: pointer;
margin-top: var(--space-2);
}
.btn-show-payload:hover {
background: var(--accent-primary);
color: white;
}
/* Modal styles */
.payload-modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.8);
z-index: 10000;
display: flex;
align-items: center;
justify-content: center;
padding: var(--space-4);
}
.payload-modal {
background: var(--bg-primary);
border-radius: var(--radius-lg);
border: 1px solid var(--border-default);
width: 100%;
max-width: 1400px;
height: 90vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--space-3) var(--space-4);
border-bottom: 1px solid var(--border-default);
background: var(--bg-secondary);
}
.modal-title {
font-size: 14px;
font-weight: 600;
color: var(--text-primary);
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
}
.modal-subtitle {
font-size: 11px;
color: var(--text-tertiary);
margin-top: var(--space-1);
}
.modal-close {
background: transparent;
border: none;
color: var(--text-tertiary);
font-size: 24px;
cursor: pointer;
padding: var(--space-1);
line-height: 1;
}
.modal-close:hover {
color: var(--text-primary);
}
.modal-body {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1px;
flex: 1;
overflow: hidden;
background: var(--border-default);
}
.payload-panel {
background: var(--bg-primary);
display: flex;
flex-direction: column;
overflow: hidden;
}
.payload-panel-header {
padding: var(--space-2) var(--space-3);
background: var(--bg-secondary);
border-bottom: 1px solid var(--border-default);
font-size: 12px;
font-weight: 600;
display: flex;
align-items: center;
gap: var(--space-2);
}
.payload-panel-header .badge {
font-size: 10px;
}
.payload-panel-content {
flex: 1;
overflow: auto;
padding: var(--space-3);
}
.payload-json {
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
font-size: 12px;
color: var(--text-secondary);
white-space: pre-wrap;
word-break: break-all;
line-height: 1.5;
}
.payload-empty {
color: var(--text-tertiary);
font-style: italic;
font-size: 12px;
padding: var(--space-4);
text-align: center;
}
.payload-meta {
font-size: 11px;
color: var(--text-tertiary);
padding: var(--space-2) var(--space-3);
border-top: 1px solid var(--border-default);
background: var(--bg-tertiary);
}
.payload-error {
background: rgba(239, 68, 68, 0.1);
color: var(--accent-error);
padding: var(--space-2) var(--space-3);
font-size: 12px;
border-bottom: 1px solid rgba(239, 68, 68, 0.2);
}
`
];
#logs_accessor_storage = __runInitializers(this, _logs_initializers, []);
// Received from parent (sw-dash-app)
get logs() { return this.#logs_accessor_storage; }
set logs(value) { this.#logs_accessor_storage = value; }
#totalCount_accessor_storage = (__runInitializers(this, _logs_extraInitializers), __runInitializers(this, _totalCount_initializers, 0));
get totalCount() { return this.#totalCount_accessor_storage; }
set totalCount(value) { this.#totalCount_accessor_storage = value; }
#stats_accessor_storage = (__runInitializers(this, _totalCount_extraInitializers), __runInitializers(this, _stats_initializers, null));
get stats() { return this.#stats_accessor_storage; }
set stats(value) { this.#stats_accessor_storage = value; }
#methods_accessor_storage = (__runInitializers(this, _stats_extraInitializers), __runInitializers(this, _methods_initializers, []));
get methods() { return this.#methods_accessor_storage; }
set methods(value) { this.#methods_accessor_storage = value; }
#directionFilter_accessor_storage = (__runInitializers(this, _methods_extraInitializers), __runInitializers(this, _directionFilter_initializers, 'all'));
// Local state for filtering
get directionFilter() { return this.#directionFilter_accessor_storage; }
set directionFilter(value) { this.#directionFilter_accessor_storage = value; }
#phaseFilter_accessor_storage = (__runInitializers(this, _directionFilter_extraInitializers), __runInitializers(this, _phaseFilter_initializers, 'all'));
get phaseFilter() { return this.#phaseFilter_accessor_storage; }
set phaseFilter(value) { this.#phaseFilter_accessor_storage = value; }
#methodFilter_accessor_storage = (__runInitializers(this, _phaseFilter_extraInitializers), __runInitializers(this, _methodFilter_initializers, ''));
get methodFilter() { return this.#methodFilter_accessor_storage; }
set methodFilter(value) { this.#methodFilter_accessor_storage = value; }
#searchText_accessor_storage = (__runInitializers(this, _methodFilter_extraInitializers), __runInitializers(this, _searchText_initializers, ''));
get searchText() { return this.#searchText_accessor_storage; }
set searchText(value) { this.#searchText_accessor_storage = value; }
#isLoadingMore_accessor_storage = (__runInitializers(this, _searchText_extraInitializers), __runInitializers(this, _isLoadingMore_initializers, false));
get isLoadingMore() { return this.#isLoadingMore_accessor_storage; }
set isLoadingMore(value) { this.#isLoadingMore_accessor_storage = value; }
#modalOpen_accessor_storage = (__runInitializers(this, _isLoadingMore_extraInitializers), __runInitializers(this, _modalOpen_initializers, false));
// Modal state
get modalOpen() { return this.#modalOpen_accessor_storage; }
set modalOpen(value) { this.#modalOpen_accessor_storage = value; }
#selectedGroup_accessor_storage = (__runInitializers(this, _modalOpen_extraInitializers), __runInitializers(this, _selectedGroup_initializers, null));
get selectedGroup() { return this.#selectedGroup_accessor_storage; }
set selectedGroup(value) { this.#selectedGroup_accessor_storage = value; }
handleDirectionFilterChange(e) {
this.directionFilter = e.target.value;
// Local filtering - no HTTP request
}
handlePhaseFilterChange(e) {
this.phaseFilter = e.target.value;
// Local filtering - no HTTP request
}
handleMethodFilterChange(e) {
this.methodFilter = e.target.value;
// Local filtering - no HTTP request
}
setMethodFilter(method) {
// Toggle: clicking the same method clears the filter
this.methodFilter = this.methodFilter === method ? '' : method;
}
handleSearch(e) {
this.searchText = e.target.value.toLowerCase();
}
handleClear() {
if (!confirm('Are you sure you want to clear the request logs? This cannot be undone.')) {
return;
}
// Dispatch event to parent to clear via DeesComms
this.dispatchEvent(new CustomEvent('clear-requests', {
bubbles: true,
composed: true,
}));
}
loadMore() {
if (this.isLoadingMore || this.logs.length === 0)
return;
this.isLoadingMore = true;
const oldestLog = this.logs[this.logs.length - 1];
// Dispatch event to parent to load more via DeesComms
this.dispatchEvent(new CustomEvent('load-more-requests', {
detail: {
before: oldestLog.timestamp,
method: this.methodFilter || undefined,
},
bubbles: true,
composed: true,
}));
// Reset loading state after a short delay (parent will update logs prop)
setTimeout(() => {
this.isLoadingMore = false;
}, 1000);
}
openPayloadModal(group) {
this.selectedGroup = group;
this.modalOpen = true;
}
handleContextMenu(event, group) {
// Build full message object for copying
const fullMessage = {
correlationId: group.correlationId,
method: group.method,
timestamp: group.timestamp,
durationMs: group.durationMs,
request: group.request ? {
direction: group.request.direction,
phase: group.request.phase,
timestamp: group.request.timestamp,
payload: group.request.payload,
} : null,
response: group.response ? {
direction: group.response.direction,
phase: group.response.phase,
timestamp: group.response.timestamp,
durationMs: group.response.durationMs,
payload: group.response.payload,
error: group.response.error,
} : null,
};
DeesContextmenu.openContextMenuWithOptions(event, [
{
name: 'Copy Full Message',
iconName: 'copy',
action: async () => {
await navigator.clipboard.writeText(JSON.stringify(fullMessage, null, 2));
},
},
{
name: 'Copy Request Payload',
iconName: 'upload',
disabled: !group.request,
action: async () => {
if (group.request) {
await navigator.clipboard.writeText(JSON.stringify(group.request.payload, null, 2));
}
},
},
{
name: 'Copy Response Payload',
iconName: 'download',
disabled: !group.response,
action: async () => {
if (group.response) {
await navigator.clipboard.writeText(JSON.stringify(group.response.payload, null, 2));
}
},
},
{ divider: true },
{
name: 'Copy Correlation ID',
iconName: 'hash',
action: async () => {
await navigator.clipboard.writeText(group.correlationId);
},
},
{
name: 'Copy Method Name',
iconName: 'tag',
action: async () => {
await navigator.clipboard.writeText(group.method);
},
},
{ divider: true },
{
name: 'Filter by Method',
iconName: 'filter',
action: async () => {
this.setMethodFilter(group.method);
},
},
{
name: 'Show Payload',
iconName: 'eye',
action: async () => {
this.openPayloadModal(group);
},
},
]);
}
closeModal() {
this.modalOpen = false;
this.selectedGroup = null;
}
handleModalOverlayClick(e) {
if (e.target.classList.contains('payload-modal-overlay')) {
this.closeModal();
}
}
handleKeydown = (__runInitializers(this, _selectedGroup_extraInitializers), (e) => {
if (e.key === 'Escape' && this.modalOpen) {
this.closeModal();
}
});
connectedCallback() {
super.connectedCallback();
document.addEventListener('keydown', this.handleKeydown);
}
disconnectedCallback() {
super.disconnectedCallback();
document.removeEventListener('keydown', this.handleKeydown);
}
formatTimestamp(ts) {
const date = new Date(ts);
return date.toLocaleTimeString(undefined, {
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
fractionalSecondDigits: 3
});
}
getDurationClass(durationMs) {
if (!durationMs)
return '';
if (durationMs > 5000)
return 'very-slow';
if (durationMs > 1000)
return 'slow';
return '';
}
formatDuration(durationMs) {
if (!durationMs)
return '';
if (durationMs < 1000)
return `${durationMs}ms`;
return `${(durationMs / 1000).toFixed(2)}s`;
}
/**
* Filter logs locally based on direction, phase, method, and search text
*/
getFilteredLogs() {
let result = this.logs;
// Apply direction filter
if (this.directionFilter !== 'all') {
result = result.filter(l => l.direction === this.directionFilter);
}
// Apply phase filter
if (this.phaseFilter !== 'all') {
result = result.filter(l => l.phase === this.phaseFilter);
}
// Apply method filter
if (this.methodFilter) {
result = result.filter(l => l.method === this.methodFilter);
}
// Apply search
if (this.searchText) {
result = result.filter(l => l.method.toLowerCase().includes(this.searchText) ||
l.correlationId.toLowerCase().includes(this.searchText) ||
(l.error && l.error.toLowerCase().includes(this.searchText)) ||
JSON.stringify(l.payload).toLowerCase().includes(this.searchText));
}
return result;
}
/**
* Group filtered logs by correlationId to show request/response pairs together
*/
getGroupedLogs() {
const filtered = this.getFilteredLogs();
const groups = new Map();
for (const log of filtered) {
let group = groups.get(log.correlationId);
if (!group) {
group = {
correlationId: log.correlationId,
method: log.method,
timestamp: log.timestamp,
hasError: false,
};
groups.set(log.correlationId, group);
}
if (log.phase === 'request') {
group.request = log;
// Update timestamp to the earliest (request time)
if (log.timestamp < group.timestamp) {
group.timestamp = log.timestamp;
}
}
else if (log.phase === 'response') {
group.response = log;
if (log.durationMs !== undefined) {
group.durationMs = log.durationMs;
}
}
if (log.error) {
group.hasError = true;
}
}
// Convert to array and sort by timestamp (newest first)
return Array.from(groups.values()).sort((a, b) => b.timestamp - a.timestamp);
}
/**
* Render the payload modal
*/
renderModal() {
if (!this.modalOpen || !this.selectedGroup) {
return null;
}
const group = this.selectedGroup;
return html `
<div class="payload-modal-overlay" @click="${this.handleModalOverlayClick}">
<div class="payload-modal">
<div class="modal-header">
<div>
<div class="modal-title">${group.method}</div>
<div class="modal-subtitle">
Correlation ID: ${group.correlationId}
${group.durationMs !== undefined ? html ` | Duration: ${this.formatDuration(group.durationMs)}` : ''}
</div>
</div>
<button class="modal-close" @click="${this.closeModal}">×</button>
</div>
<div class="modal-body">
<!-- Request Panel (Left) -->
<div class="payload-panel">
<div class="payload-panel-header">
<span class="badge phase-request">REQUEST</span>
${group.request ? html `
<span class="badge direction-${group.request.direction}">${group.request.direction}</span>
` : ''}
</div>
${group.request ? html `
<div class="payload-meta">
Timestamp: ${this.formatTimestamp(group.request.timestamp)}
</div>
<div class="payload-panel-content">
<pre class="payload-json">${JSON.stringify(group.request.payload, null, 2)}</pre>
</div>
` : html `
<div class="payload-empty">No request data captured</div>
`}
</div>
<!-- Response Panel (Right) -->
<div class="payload-panel">
<div class="payload-panel-header">
<span class="badge phase-response">RESPONSE</span>
${group.response ? html `
<span class="badge direction-${group.response.direction}">${group.response.direction}</span>
` : ''}
</div>
${group.response?.error ? html `
<div class="payload-error">Error: ${group.response.error}</div>
` : ''}
${group.response ? html `
<div class="payload-meta">
Timestamp: ${this.formatTimestamp(group.response.timestamp)}
${group.response.durationMs !== undefined ? html ` | Duration: ${this.formatDuration(group.response.durationMs)}` : ''}
</div>
<div class="payload-panel-content">
<pre class="payload-json">${JSON.stringify(group.response.payload, null, 2)}</pre>
</div>
` : html `
<div class="payload-empty">No response yet (pending)</div>
`}
</div>
</div>
</div>
</div>
`;
}
render() {
const groupedLogs = this.getGroupedLogs();
return html `
${this.renderModal()}
<!-- Stats Bar -->
<div class="stats-bar">
<div class="stat-item">
<span class="stat-value">${this.stats?.totalRequests ?? 0}</span>
<span class="stat-label">Total Requests</span>
</div>
<div class="stat-item">
<span class="stat-value">${this.stats?.totalResponses ?? 0}</span>
<span class="stat-label">Total Responses</span>
</div>
<div class="stat-item">
<span class="stat-value ${(this.stats?.errorCount ?? 0) > 0 ? 'error' : ''}">${this.stats?.errorCount ?? 0}</span>
<span class="stat-label">Errors</span>
</div>
<div class="stat-item">
<span class="stat-value">${this.stats?.avgDurationMs ?? 0}ms</span>
<span class="stat-label">Avg Duration</span>
</div>
<div class="stat-item">
<span class="stat-value">${groupedLogs.length}</span>
<span class="stat-label">Showing</span>
</div>
</div>
<!-- Method Stats -->
${this.stats && Object.keys(this.stats.methodCounts).length > 0 ? html `
<div class="method-stats">
<div class="method-stats-title">Methods</div>
<div class="method-stats-grid">
${Object.entries(this.stats.methodCounts).slice(0, 8).map(([method, data]) => html `
<div
class="method-stat-card ${this.methodFilter === method ? 'active' : ''}"
@click="${() => this.setMethodFilter(method)}"
>
<div class="method-stat-name" title="${method}">${method}</div>
<div class="method-stat-details">
<span>${data.requests} req</span>
<span>${data.responses} res</span>
${data.errors > 0 ? html `<span style="color: var(--accent-error)">${data.errors} err</span>` : ''}
<span>${data.avgDurationMs}ms avg</span>
</div>
</div>
`)}
</div>
</div>
` : ''}
<!-- Filters -->
<div class="requests-header">
<div class="filter-group">
<span class="filter-label">Direction:</span>
<select class="filter-select" @change="${this.handleDirectionFilterChange}">
<option value="all">All</option>
<option value="outgoing">Outgoing</option>
<option value="incoming">Incoming</option>
</select>
<span class="filter-label">Phase:</span>
<select class="filter-select" @change="${this.handlePhaseFilterChange}">
<option value="all">All</option>
<option value="request">Request</option>
<option value="response">Response</option>
</select>
<span class="filter-label">Method:</span>
<select class="filter-select" .value="${this.methodFilter}" @change="${this.handleMethodFilterChange}">
<option value="">All Methods</option>
${this.methods.map(m => html `<option value="${m}" ?selected="${this.methodFilter === m}">${m}</option>`)}
</select>
<input
type="text"
class="search-input"
placeholder="Search..."
.value="${this.searchText}"
@input="${this.handleSearch}"
style="width: 150px;"
>
</div>
<button class="btn clear-btn" @click="${this.handleClear}">Clear Logs</button>
</div>
<!-- Request List (Grouped by correlationId) -->
${this.logs.length === 0 ? html `
<div class="empty-state">No request logs found. Traffic will appear here as TypedRequests are made.</div>
` : groupedLogs.length === 0 ? html `
<div class="empty-state">No logs match filter</div>
` : html `
<div class="requests-list">
${groupedLogs.map(group => html `
<div
class="request-card ${group.hasError ? 'has-error' : ''}"
@contextmenu="${(e) => this.handleContextMenu(e, group)}"
>
<div class="request-header">
<div>
<div class="request-badges">
${group.request ? html `
<span class="badge direction-${group.request.direction}">${group.request.direction}</span>
` : ''}
${group.hasError ? html `<span class="badge error">error</span>` : ''}
</div>
<div class="method-name">${group.method}</div>
<div class="correlation-id">${group.correlationId}</div>
<div class="request-response-badges">
<span class="status-badge ${group.request ? 'has-request' : 'pending'}">
${group.request ? 'REQ' : 'REQ pending'}
</span>
<span class="status-badge ${group.response ? 'has-response' : 'pending'}">
${group.response ? 'RES' : 'RES pending'}
</span>
</div>
</div>
<div class="request-meta">
<span class="request-time">${this.formatTimestamp(group.timestamp)}</span>
${group.durationMs !== undefined ? html `
<span class="request-duration ${this.getDurationClass(group.durationMs)}">
${this.formatDuration(group.durationMs)}
</span>
` : ''}
</div>
</div>
${group.response?.error ? html `
<div class="request-error">${group.response.error}</div>
` : ''}
<button class="btn-show-payload" @click="${() => this.openPayloadModal(group)}">
Show Payload
</button>
</div>
`)}
</div>
${this.logs.length < this.totalCount ? html `
<div class="pagination">
<button class="btn btn-secondary" @click="${this.loadMore}" ?disabled="${this.isLoadingMore}">
${this.isLoadingMore ? 'Loading...' : 'Load More'}
</button>
<span class="page-info">${this.logs.length} of ${this.totalCount} logs</span>
</div>
` : ''}
`}
`;
}
static {
__runInitializers(_classThis, _classExtraInitializers);
}
};
return SwDashRequests = _classThis;
})();
export { SwDashRequests };
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3ctZGFzaC1yZXF1ZXN0cy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzX3N3ZGFzaC9zdy1kYXNoLXJlcXVlc3RzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRSxHQUFHLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRSxhQUFhLEVBQUUsZUFBZSxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBRXRHLE9BQU8sRUFBRSxZQUFZLEVBQUUsV0FBVyxFQUFFLFdBQVcsRUFBRSxZQUFZLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQXFDM0Y7Ozs7OztHQU1HO0lBRVUsY0FBYzs0QkFEMUIsYUFBYSxDQUFDLGtCQUFrQixDQUFDOzs7O3NCQUNFLFVBQVU7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OEJBQWxCLFNBQVEsV0FBVTs7OztnQ0F1YjNDLFFBQVEsQ0FBQyxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsQ0FBQztzQ0FDekIsUUFBUSxDQUFDLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxDQUFDO2lDQUMxQixRQUFRLENBQUMsRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFLENBQUM7bUNBQzFCLFFBQVEsQ0FBQyxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsQ0FBQzsyQ0FHekIsS0FBSyxFQUFFO3VDQUNQLEtBQUssRUFBRTt3Q0FDUCxLQUFLLEVBQUU7c0NBQ1AsS0FBSyxFQUFFO3lDQUNQLEtBQUssRUFBRTtxQ0FHUCxLQUFLLEVBQUU7eUNBQ1AsS0FBSyxFQUFFO1lBZG1CLGlLQUFTLElBQUksNkJBQUosSUFBSSxtRkFBK0I7WUFDM0MsbUxBQVMsVUFBVSw2QkFBVixVQUFVLCtGQUFLO1lBQ3hCLG9LQUFTLEtBQUssNkJBQUwsS0FBSyxxRkFBbUM7WUFDbEQsMEtBQVMsT0FBTyw2QkFBUCxPQUFPLHlGQUFnQjtZ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