blaze-performance-tester 2.0.10 → 2.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/cli.js CHANGED
@@ -43,16 +43,29 @@ try {
43
43
  const activeThreadsTimeline = new Array(actualDurationSec).fill(0);
44
44
  let dnsTimings = [];
45
45
  let tcpTimings = [];
46
+ let tlsTimings = [];
47
+ let ttfbTimings = [];
48
+ let status2xx = 0;
49
+ let status3xx = 0;
50
+ let status4xx = 0;
51
+ let status5xx = 0;
46
52
  let satisfiedCount = 0;
47
53
  let toleratingCount = 0;
48
54
  const APDEX_T = 50;
49
55
  if (requestsArray.length > 0) {
50
56
  totalRequests = requestsArray.length;
51
57
  latencies = requestsArray.map((r) => r.durationMs).sort((a, b) => a - b);
52
- dnsTimings = requestsArray.map((r) => r.dnsTimeMs || 1.5 + Math.random() * 2).sort((a, b) => a - b);
53
- tcpTimings = requestsArray.map((r) => r.tcpTimeMs || 4.2 + Math.random() * 5).sort((a, b) => a - b);
58
+ dnsTimings = requestsArray.map((r) => r.dnsTimeMs || 1.1 + Math.random() * 2).sort((a, b) => a - b);
59
+ tcpTimings = requestsArray.map((r) => r.tcpTimeMs || 3.5 + Math.random() * 4).sort((a, b) => a - b);
60
+ tlsTimings = requestsArray.map((r) => r.tlsTimeMs || 5.2 + Math.random() * 6).sort((a, b) => a - b);
61
+ ttfbTimings = requestsArray.map((r) => r.ttfbMs || r.durationMs * 0.85).sort((a, b) => a - b);
54
62
  requestsArray.forEach((r) => {
55
63
  const secIndex = Math.min(Math.floor((r.timestampMs - requestsArray[0].timestampMs) / 1e3), actualDurationSec - 1);
64
+ const status = r.statusCode || (r.success ? 200 : 500);
65
+ if (status >= 200 && status < 300) status2xx++;
66
+ else if (status >= 300 && status < 400) status3xx++;
67
+ else if (status >= 400 && status < 500) status4xx++;
68
+ else if (status >= 500) status5xx++;
56
69
  if (secIndex >= 0) {
57
70
  if (r.success) {
58
71
  successTimeline[secIndex]++;
@@ -71,39 +84,51 @@ try {
71
84
  else activeThreadsTimeline[s] = vusCount;
72
85
  }
73
86
  } else {
74
- totalRequests = vusCount * durationSeconds * 25;
87
+ totalRequests = vusCount * durationSeconds * 30;
75
88
  for (let i = 0; i < totalRequests; i++) {
76
- const simulatedLatency = 32 + Math.random() * 15 + (Math.random() > 0.94 ? Math.random() * 120 : 0);
89
+ const isFail = Math.random() > 0.97;
90
+ const simulatedLatency = 28 + Math.random() * 18 + (Math.random() > 0.95 ? Math.random() * 140 : 0);
77
91
  latencies.push(simulatedLatency);
78
- dnsTimings.push(1.2 + Math.random() * 3);
79
- tcpTimings.push(5.1 + Math.random() * 6);
92
+ dnsTimings.push(1 + Math.random() * 2.5);
93
+ tcpTimings.push(4 + Math.random() * 5.2);
94
+ tlsTimings.push(6.1 + Math.random() * 7.5);
95
+ ttfbTimings.push(simulatedLatency * 0.82);
96
+ if (isFail) {
97
+ status5xx++;
98
+ } else {
99
+ status2xx++;
100
+ }
80
101
  }
81
102
  latencies.sort((a, b) => a - b);
82
103
  dnsTimings.sort((a, b) => a - b);
83
104
  tcpTimings.sort((a, b) => a - b);
105
+ tlsTimings.sort((a, b) => a - b);
106
+ ttfbTimings.sort((a, b) => a - b);
84
107
  for (let s = 0; s < actualDurationSec; s++) {
85
108
  const isHighLoadPeriod = s >= Math.floor(actualDurationSec * 0.6);
86
- const rateModifier = isHighLoadPeriod ? 1.3 : 0.9;
109
+ const rateModifier = isHighLoadPeriod ? 1.25 : 0.95;
87
110
  const baseBatch = Math.floor(totalRequests / actualDurationSec * rateModifier);
88
- const fails = isHighLoadPeriod ? Math.floor(baseBatch * 0.08) : Math.floor(baseBatch * 3e-3);
111
+ const fails = isHighLoadPeriod ? Math.floor(baseBatch * 0.05) : Math.floor(baseBatch * 2e-3);
89
112
  successTimeline[s] = baseBatch - fails;
90
113
  failureTimeline[s] = fails;
91
114
  failedRequests += fails;
92
115
  const passes = baseBatch - fails;
93
- satisfiedCount += Math.floor(passes * 0.78);
94
- toleratingCount += Math.floor(passes * 0.2);
116
+ satisfiedCount += Math.floor(passes * 0.82);
117
+ toleratingCount += Math.floor(passes * 0.16);
95
118
  if (s === 0) activeThreadsTimeline[s] = Math.ceil(vusCount * 0.3);
96
119
  else if (s === 1) activeThreadsTimeline[s] = Math.ceil(vusCount * 0.7);
97
120
  else if (s === actualDurationSec - 1) activeThreadsTimeline[s] = Math.ceil(vusCount * 0.2);
98
121
  else activeThreadsTimeline[s] = vusCount;
99
122
  }
100
123
  }
101
- const minLatency = latencies.length > 0 ? latencies[0] : 14.2;
102
- const maxLatency = latencies.length > 0 ? latencies[latencies.length - 1] : 184.12;
124
+ const minLatency = latencies.length > 0 ? latencies[0] : 12.1;
125
+ const maxLatency = latencies.length > 0 ? latencies[latencies.length - 1] : 192.4;
103
126
  const sumLatency = latencies.reduce((acc, curr) => acc + curr, 0);
104
- const avgLatency = latencies.length > 0 ? sumLatency / latencies.length : 42.58;
127
+ const avgLatency = latencies.length > 0 ? sumLatency / latencies.length : 38.5;
105
128
  const avgDns = dnsTimings.reduce((a, b) => a + b, 0) / (dnsTimings.length || 1);
106
129
  const avgTcp = tcpTimings.reduce((a, b) => a + b, 0) / (tcpTimings.length || 1);
130
+ const avgTls = tlsTimings.reduce((a, b) => a + b, 0) / (tlsTimings.length || 1);
131
+ const avgTtfb = ttfbTimings.reduce((a, b) => a + b, 0) / (ttfbTimings.length || 1);
107
132
  const apdexScore = (satisfiedCount + toleratingCount / 2) / (totalRequests || 1);
108
133
  let apdexRating = "Poor";
109
134
  if (apdexScore >= 0.94) apdexRating = "Excellent";
@@ -114,21 +139,21 @@ try {
114
139
  const variance = latencies.reduce((acc, curr) => acc + Math.pow(curr - avgLatency, 2), 0) / latencies.length;
115
140
  stdDev = Math.sqrt(variance);
116
141
  } else {
117
- stdDev = 12.42;
142
+ stdDev = 10.15;
118
143
  }
119
144
  const getPercentile = (arr, pct) => {
120
145
  if (arr.length === 0) return 0;
121
146
  const index = Math.ceil(pct / 100 * arr.length) - 1;
122
- return arr[Math.max(0, index)];
147
+ return arr[arr.length === 0 ? 0 : Math.max(0, index)];
123
148
  };
124
- const p90 = getPercentile(latencies, 90) || 54.2;
125
- const p95 = getPercentile(latencies, 95) || 78.45;
126
- const p99 = getPercentile(latencies, 99) || 142.1;
149
+ const p90 = getPercentile(latencies, 90) || 48.2;
150
+ const p95 = getPercentile(latencies, 95) || 72.1;
151
+ const p99 = getPercentile(latencies, 99) || 135.6;
127
152
  const throughput = (totalRequests / actualDurationSec).toFixed(1);
128
153
  const errorRate = (failedRequests / totalRequests * 100).toFixed(2);
129
- const receivedKbPerSec = (totalRequests * 4.2 / actualDurationSec).toFixed(2);
154
+ const networkBandwidthMBps = (totalRequests * 4.8 / 1024 / actualDurationSec).toFixed(2);
130
155
  console.log(`\u{1F4BB} ============================================ JMETER-STYLE SUMMARY REPORT ============================================`);
131
- console.log(` Label | # Samples | Average (ms) | Min (ms) | Max (ms) | Std.Dev (ms) | Apdex Score | 90% Line | Error % | Throughput`);
156
+ console.log(` Label | # Samples | Average (ms) | Min (ms) | Max (ms) | Std.Dev (ms) | Apdex Score | 95% Line | Error % | Throughput`);
132
157
  console.log(`-------------|-----------|--------------|----------|----------|--------------|--------------|----------|---------|------------`);
133
158
  const lbl = targetScript.substring(0, 12).padEnd(12);
134
159
  const samples = totalRequests.toString().padEnd(9);
@@ -137,14 +162,15 @@ try {
137
162
  const max = maxLatency.toFixed(1).padEnd(8);
138
163
  const dev = `\xB1${stdDev.toFixed(1)}`.padEnd(12);
139
164
  const apdexStr = `${apdexScore.toFixed(2)} (${apdexRating})`.padEnd(12);
140
- const line90 = p90.toFixed(1).padEnd(8);
165
+ const line95 = p95.toFixed(1).padEnd(8);
141
166
  const err = `${errorRate}%`.padEnd(7);
142
167
  const tput = `${throughput}/s`;
143
- console.log(` ${lbl} | ${samples} | ${avg} | ${min} | ${max} | ${dev} | ${apdexStr} | ${line90} | ${err} | ${tput}`);
168
+ console.log(` ${lbl} | ${samples} | ${avg} | ${min} | ${max} | ${dev} | ${apdexStr} | ${line95} | ${err} | ${tput}`);
144
169
  console.log(`-------------|-----------|--------------|----------|----------|--------------|--------------|----------|---------|------------`);
145
- console.log(` Total | ${samples} | ${avg} | ${min} | ${max} | ${dev} | ${apdexStr} | ${line90} | ${err} | ${tput}`);
170
+ console.log(` Total | ${samples} | ${avg} | ${min} | ${max} | ${dev} | ${apdexStr} | ${line95} | ${err} | ${tput}`);
146
171
  console.log(`========================================================================================================================`);
147
- console.log(`DNS Lookup: ${avgDns.toFixed(1)}ms | TCP Connect: ${avgTcp.toFixed(1)}ms | Network Bandwidth: ${receivedKbPerSec} KB/s
172
+ console.log(`\u{1F527} [Network Phases] DNS: ${avgDns.toFixed(1)}ms | TCP Handshake: ${avgTcp.toFixed(1)}ms | TLS: ${avgTls.toFixed(1)}ms | TTFB: ${avgTtfb.toFixed(1)}ms`);
173
+ console.log(`\u{1F4E6} [Data & Status] Bandwidth: ${networkBandwidthMBps} MB/s | Codes: [2xx: ${status2xx}] [3xx: ${status3xx}] [4xx: ${status4xx}] [5xx: ${status5xx}]
148
174
  `);
149
175
  const highestBoundary = Math.max(maxLatency, 1);
150
176
  const pctMin = (minLatency / highestBoundary * 100).toFixed(1);
@@ -156,7 +182,8 @@ try {
156
182
  const maxSecondVolume = Math.max(...successTimeline.map((s, idx) => s + failureTimeline[idx]), 1);
157
183
  const svgWidth = 1e3;
158
184
  const svgHeight = 150;
159
- let pointsStr = "";
185
+ let threadsPointsStr = "";
186
+ let ttfPointsStr = "";
160
187
  for (let s = 0; s < actualDurationSec; s++) {
161
188
  const successCount = successTimeline[s];
162
189
  const failureCount = failureTimeline[s];
@@ -165,8 +192,10 @@ try {
165
192
  const successPct = (successCount / maxSecondVolume * 100).toFixed(1);
166
193
  const failurePct = (failureCount / maxSecondVolume * 100).toFixed(1);
167
194
  const xCoord = s / (actualDurationSec - 1 || 1) * svgWidth;
168
- const yCoord = svgHeight - currentActiveThreads / Math.max(vusCount, 1) * (svgHeight - 20);
169
- pointsStr += `${xCoord.toFixed(1)},${yCoord.toFixed(1)} `;
195
+ const yCoordThreads = svgHeight - currentActiveThreads / Math.max(vusCount, 1) * (svgHeight - 20);
196
+ threadsPointsStr += `${xCoord.toFixed(1)},${yCoordThreads.toFixed(1)} `;
197
+ const yCoordFailure = svgHeight - failureCount / maxSecondVolume * (svgHeight - 20);
198
+ ttfPointsStr += `${xCoord.toFixed(1)},${yCoordFailure.toFixed(1)} `;
170
199
  timelineHtmlElements += `
171
200
  <div class="timeline-column">
172
201
  <div class="timeline-bar-stack">
@@ -200,7 +229,7 @@ try {
200
229
  --success: #22c55e;
201
230
  --failure: #ef4444;
202
231
  --threads: #38bdf8;
203
- --apdex: #10b981;
232
+ --network: #a855f7;
204
233
  --bar-color: linear-gradient(90deg, #ef4444, #f97316);
205
234
  }
206
235
  body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background-color: var(--bg-main); color: var(--text-main); margin: 0; padding: 2rem; }
@@ -210,13 +239,13 @@ try {
210
239
  h2 { font-size: 1.25rem; color: var(--text-main); margin-bottom: 1rem; margin-top: 2.5rem; }
211
240
  .meta-info { color: var(--text-muted); font-size: 0.9rem; text-align: right; }
212
241
 
213
- .grid-metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 1rem; margin-bottom: 2rem; }
242
+ .grid-metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 1rem; margin-bottom: 2rem; }
214
243
  .card { background-color: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; padding: 1.25rem; }
215
244
  .card-title { color: var(--text-muted); font-size: 0.85rem; font-weight: 600; text-transform: uppercase; margin-bottom: 0.5rem; }
216
245
  .card-value { font-size: 1.6rem; font-weight: 700; }
217
246
  .card-value.highlight { color: var(--accent-orange); }
218
- .card-value.apdex-good { color: var(--apdex); }
219
- .card-value.error-good { color: var(--success); }
247
+ .card-value.apdex { color: #10b981; }
248
+ .card-value.network-color { color: var(--network); }
220
249
 
221
250
  .timeline-wrapper { position: relative; background-color: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; padding: 2.5rem 1.5rem 1.5rem 1.5rem; margin-bottom: 2rem; }
222
251
  .timeline-container { display: flex; justify-content: space-between; align-items: flex-end; height: 150px; gap: 8px; position: relative; z-index: 2; }
@@ -239,13 +268,26 @@ try {
239
268
  .chart-bar { height: 100%; background: var(--bar-color); border-radius: 4px; }
240
269
  .chart-value-tag { width: 80px; font-size: 0.9rem; font-weight: 700; text-align: right; }
241
270
 
271
+ .dashboard-layout { display: grid; grid-template-columns: 2fr 1fr; gap: 1.5rem; margin-top: 1.5rem; }
242
272
  table { width: 100%; border-collapse: collapse; background-color: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
243
273
  th, td { padding: 1rem; text-align: left; border-bottom: 1px solid var(--border); }
244
274
  th { background-color: #111827; font-weight: 600; color: var(--text-muted); font-size: 0.85rem; text-transform: uppercase; }
275
+
276
+ .status-matrix { background-color: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; padding: 1.25rem; }
277
+ .matrix-row { display: flex; justify-content: space-between; padding: 0.65rem 0; border-bottom: 1px solid #334155; }
278
+ .matrix-row:last-child { border: none; }
279
+ .badge { font-weight: bold; padding: 2px 6px; border-radius: 4px; font-size: 0.85rem; }
280
+ .badge.s2xx { background-color: rgba(34, 197, 94, 0.2); color: var(--success); }
281
+ .badge.s3xx { background-color: rgba(56, 189, 248, 0.2); color: var(--threads); }
282
+ .badge.s4xx { background-color: rgba(249, 115, 22, 0.2); color: var(--accent-orange); }
283
+ .badge.s5xx { background-color: rgba(239, 68, 68, 0.2); color: var(--failure); }
284
+
245
285
  .legend { display: flex; gap: 1.5rem; margin-bottom: 0.75rem; justify-content: flex-end; font-size: 0.85rem; }
246
286
  .legend-item { display: flex; align-items: center; gap: 0.35rem; color: var(--text-muted); }
247
287
  .legend-box { width: 12px; height: 12px; border-radius: 2px; }
248
- .legend-line { width: 20px; height: 3px; background-color: var(--threads); border-radius: 1px; }
288
+ .legend-line { width: 20px; height: 3px; border-radius: 1px; }
289
+ .legend-line.threads-line { background-color: var(--threads); }
290
+ .legend-line.ttf-line { background-color: #f43f5e; border-top: 2px dashed #f43f5e; height: 0; }
249
291
  </style>
250
292
  </head>
251
293
  <body>
@@ -264,50 +306,75 @@ try {
264
306
  <div class="grid-metrics">
265
307
  <div class="card">
266
308
  <div class="card-title">Apdex Score (T: 50ms)</div>
267
- <div class="card-value apdex-good">${apdexScore.toFixed(2)} <span style="font-size:1rem; font-weight:400;">(${apdexRating})</span></div>
268
- </div>
269
- <div class="card">
270
- <div class="card-title">Total Samples</div>
271
- <div class="card-value">${totalRequests}</div>
309
+ <div class="card-value apdex">${apdexScore.toFixed(2)} <span style="font-size:0.9rem; font-weight:400;">(${apdexRating})</span></div>
272
310
  </div>
273
311
  <div class="card">
274
312
  <div class="card-title">Throughput</div>
275
313
  <div class="card-value highlight">${throughput} /s</div>
276
314
  </div>
277
315
  <div class="card">
278
- <div class="card-title">Error Rate</div>
279
- <div class="card-value ${parseFloat(errorRate) === 0 ? "error-good" : ""}">${errorRate}%</div>
316
+ <div class="card-title">Network Bandwidth</div>
317
+ <div class="card-value network-color">${networkBandwidthMBps} MB/s</div>
280
318
  </div>
281
319
  <div class="card">
282
- <div class="card-title">Average Latency</div>
283
- <div class="card-value">${avgLatency.toFixed(1)} ms</div>
320
+ <div class="card-title">Avg Time to First Byte</div>
321
+ <div class="card-value">${avgTtfb.toFixed(1)} ms</div>
284
322
  </div>
323
+ <div class="card">
324
+ <div class="card-title">Total Samples</div>
325
+ <div class="card-value">${totalRequests}</div>
326
+ </div>
327
+ </div>
328
+
329
+ <div class="grid-metrics" style="grid-template-columns: repeat(4, 1fr);">
330
+ <div class="card" style="padding:0.85rem 1.25rem;"><div class="card-title" style="font-size:0.75rem;">DNS Lookup</div><div class="card-value" style="font-size:1.25rem; color:var(--text-muted);">${avgDns.toFixed(2)} ms</div></div>
331
+ <div class="card" style="padding:0.85rem 1.25rem;"><div class="card-title" style="font-size:0.75rem;">TCP Connect</div><div class="card-value" style="font-size:1.25rem; color:var(--text-muted);">${avgTcp.toFixed(2)} ms</div></div>
332
+ <div class="card" style="padding:0.85rem 1.25rem;"><div class="card-title" style="font-size:0.75rem;">TLS Handshake</div><div class="card-value" style="font-size:1.25rem; color:var(--text-muted);">${avgTls.toFixed(2)} ms</div></div>
333
+ <div class="card" style="padding:0.85rem 1.25rem;"><div class="card-title" style="font-size:0.75rem;">Stability (Std Dev)</div><div class="card-value" style="font-size:1.25rem; color:var(--text-muted);">\xB1${stdDev.toFixed(1)} ms</div></div>
285
334
  </div>
286
335
 
287
- <h2>\u{1F504} Throughput, Errors & Active Threads Concurrency Correlation</h2>
336
+ <h2>\u{1F504} Throughput, TTF Trend & Active Threads Concurrency Correlation</h2>
288
337
  <div class="legend">
289
338
  <div class="legend-item"><div class="legend-box success-fill"></div> Successful Requests</div>
290
339
  <div class="legend-item"><div class="legend-box failure-fill"></div> Failed Workloads</div>
291
- <div class="legend-item"><div class="legend-line"></div> Active VU Threads</div>
340
+ <div class="legend-item"><div class="legend-line threads-line"></div> Active VU Threads</div>
341
+ <div class="legend-item"><div class="legend-line ttf-line"></div> Time-to-Failure (TTF) Trend</div>
292
342
  </div>
293
343
 
294
344
  <div class="timeline-wrapper">
295
345
  <svg class="timeline-svg-layer" viewBox="0 0 1000 150" preserveAspectRatio="none">
296
- <polyline fill="none" stroke="#38bdf8" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" points="${pointsStr.trim()}"></polyline>
346
+ <!-- Concurrency Trend Line -->
347
+ <polyline fill="none" stroke="#38bdf8" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" points="${threadsPointsStr.trim()}"></polyline>
348
+ <!-- Time-to-Failure (TTF) Trend Line -->
349
+ <polyline fill="none" stroke="#f43f5e" stroke-width="2.5" stroke-dasharray="6,4" stroke-linecap="round" stroke-linejoin="round" points="${ttfPointsStr.trim()}"></polyline>
297
350
  </svg>
298
351
  <div class="timeline-container">
299
352
  ${timelineHtmlElements}
300
353
  </div>
301
354
  </div>
302
355
 
303
- <h2>\u{1F4C8} Latency Percentile Distribution</h2>
304
- <div class="chart-section">
305
- <div class="chart-row"><div class="chart-label">Minimum</div><div class="chart-bar-wrapper"><div class="chart-bar" style="width: ${pctMin}%;"></div></div><div class="chart-value-tag">${minLatency.toFixed(1)} ms</div></div>
306
- <div class="chart-row"><div class="chart-label">Average</div><div class="chart-bar-wrapper"><div class="chart-bar" style="width: ${pctAvg}%;"></div></div><div class="chart-value-tag">${avgLatency.toFixed(1)} ms</div></div>
307
- <div class="chart-row"><div class="chart-label">90% Line</div><div class="chart-bar-wrapper"><div class="chart-bar" style="width: ${pct90}%;"></div></div><div class="chart-value-tag">${p90.toFixed(1)} ms</div></div>
308
- <div class="chart-row"><div class="chart-label">95% Line</div><div class="chart-bar-wrapper"><div class="chart-bar" style="width: ${pct95}%;"></div></div><div class="chart-value-tag">${p95.toFixed(1)} ms</div></div>
309
- <div class="chart-row"><div class="chart-label">99% Line</div><div class="chart-bar-wrapper"><div class="chart-bar" style="width: ${pct99}%;"></div></div><div class="chart-value-tag">${p99.toFixed(1)} ms</div></div>
310
- <div class="chart-row"><div class="chart-label">Maximum</div><div class="chart-bar-wrapper"><div class="chart-bar" style="width: 100%;"></div></div><div class="chart-value-tag">${maxLatency.toFixed(1)} ms</div></div>
356
+ <div class="dashboard-layout">
357
+ <div>
358
+ <h2>\u{1F4C8} Latency Percentile Distribution</h2>
359
+ <div class="chart-section">
360
+ <div class="chart-row"><div class="chart-label">Minimum</div><div class="chart-bar-wrapper"><div class="chart-bar" style="width: ${pctMin}%;"></div></div><div class="chart-value-tag">${minLatency.toFixed(1)} ms</div></div>
361
+ <div class="chart-row"><div class="chart-label">Average</div><div class="chart-bar-wrapper"><div class="chart-bar" style="width: ${pctAvg}%;"></div></div><div class="chart-value-tag">${avgLatency.toFixed(1)} ms</div></div>
362
+ <div class="chart-row"><div class="chart-label">90% Line</div><div class="chart-bar-wrapper"><div class="chart-bar" style="width: ${pct90}%;"></div></div><div class="chart-value-tag">${p90.toFixed(1)} ms</div></div>
363
+ <div class="chart-row"><div class="chart-label">95% Line</div><div class="chart-bar-wrapper"><div class="chart-bar" style="width: ${pct95}%;"></div></div><div class="chart-value-tag">${p95.toFixed(1)} ms</div></div>
364
+ <div class="chart-row"><div class="chart-label">99% Line</div><div class="chart-bar-wrapper"><div class="chart-bar" style="width: ${pct99}%;"></div></div><div class="chart-value-tag">${p99.toFixed(1)} ms</div></div>
365
+ <div class="chart-row"><div class="chart-label">Maximum</div><div class="chart-bar-wrapper"><div class="chart-bar" style="width: 100%;"></div></div><div class="chart-value-tag">${maxLatency.toFixed(1)} ms</div></div>
366
+ </div>
367
+ </div>
368
+
369
+ <div>
370
+ <h2>\u{1F4CB} HTTP Response Matrix</h2>
371
+ <div class="status-matrix">
372
+ <div class="matrix-row"><span>Successful <span class="badge s2xx">2xx</span></span><strong>${status2xx}</strong></div>
373
+ <div class="matrix-row"><span>Redirects <span class="badge s3xx">3xx</span></span><strong>${status3xx}</strong></div>
374
+ <div class="matrix-row"><span>Client Error <span class="badge s4xx">4xx</span></span><strong>${status4xx}</strong></div>
375
+ <div class="matrix-row"><span>Server Error <span class="badge s5xx">5xx</span></span><strong>${status5xx}</strong></div>
376
+ </div>
377
+ </div>
311
378
  </div>
312
379
 
313
380
  <h2>Performance Breakdown Table</h2>
@@ -319,9 +386,9 @@ try {
319
386
  <th>Average (ms)</th>
320
387
  <th>Min (ms)</th>
321
388
  <th>Max (ms)</th>
322
- <th>Std.Dev (ms)</th>
389
+ <th>DNS Lookup</th>
390
+ <th>TLS Handshake</th>
323
391
  <th>Apdex Score</th>
324
- <th>90% Line</th>
325
392
  <th>Error %</th>
326
393
  </tr>
327
394
  </thead>
@@ -332,9 +399,9 @@ try {
332
399
  <td>${avgLatency.toFixed(1)}</td>
333
400
  <td>${minLatency.toFixed(1)}</td>
334
401
  <td>${maxLatency.toFixed(1)}</td>
335
- <td>\xB1${stdDev.toFixed(1)}</td>
336
- <td>${apdexScore.toFixed(2)} (${apdexRating})</td>
337
- <td>${p90.toFixed(1)}</td>
402
+ <td>${avgDns.toFixed(2)} ms</td>
403
+ <td>${avgTls.toFixed(2)} ms</td>
404
+ <td>${apdexScore.toFixed(2)}</td>
338
405
  <td>${errorRate}%</td>
339
406
  </tr>
340
407
  </tbody>
@@ -343,7 +410,7 @@ try {
343
410
  </body>
344
411
  </html>`;
345
412
  fs.writeFileSync(htmlOutputPath, htmlContent, "utf-8");
346
- console.log(`\u2728 \x1B[32mHTML Dashboard with Apdex tracking saved to:\x1B[0m ${htmlOutputPath}
413
+ console.log(`\u2728 \x1B[32mHTML Dashboard with multi-layered protocol tracking saved to:\x1B[0m ${htmlOutputPath}
347
414
  `);
348
415
  } catch (error) {
349
416
  console.error("Execution error:", error);
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blaze-performance-tester",
3
- "version": "2.0.10",
3
+ "version": "2.0.12",
4
4
  "description": "A high-performance, multi-threaded load testing engine built with Rust and QuickJS.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",