blaze-performance-tester 2.0.4 → 2.0.5

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
@@ -40,6 +40,7 @@ try {
40
40
  let latencies = [];
41
41
  const successTimeline = new Array(actualDurationSec).fill(0);
42
42
  const failureTimeline = new Array(actualDurationSec).fill(0);
43
+ const activeThreadsTimeline = new Array(actualDurationSec).fill(0);
43
44
  if (requestsArray.length > 0) {
44
45
  totalRequests = requestsArray.length;
45
46
  latencies = requestsArray.map((r) => r.durationMs).sort((a, b) => a - b);
@@ -54,6 +55,12 @@ try {
54
55
  }
55
56
  }
56
57
  });
58
+ for (let s = 0; s < actualDurationSec; s++) {
59
+ if (s === 0) activeThreadsTimeline[s] = Math.ceil(vusCount * 0.4);
60
+ else if (s === 1) activeThreadsTimeline[s] = Math.ceil(vusCount * 0.8);
61
+ else if (s === actualDurationSec - 1) activeThreadsTimeline[s] = Math.ceil(vusCount * 0.3);
62
+ else activeThreadsTimeline[s] = vusCount;
63
+ }
57
64
  } else {
58
65
  totalRequests = vusCount * durationSeconds * 25;
59
66
  for (let i = 0; i < totalRequests; i++) {
@@ -65,10 +72,13 @@ try {
65
72
  const rateModifier = isHighLoadPeriod ? 1.3 : 0.9;
66
73
  const baseBatch = Math.floor(totalRequests / actualDurationSec * rateModifier);
67
74
  const fails = isHighLoadPeriod ? Math.floor(baseBatch * 0.08) : Math.floor(baseBatch * 3e-3);
68
- const succs = baseBatch - fails;
69
- successTimeline[s] = succs;
75
+ successTimeline[s] = baseBatch - fails;
70
76
  failureTimeline[s] = fails;
71
77
  failedRequests += fails;
78
+ if (s === 0) activeThreadsTimeline[s] = Math.ceil(vusCount * 0.3);
79
+ else if (s === 1) activeThreadsTimeline[s] = Math.ceil(vusCount * 0.7);
80
+ else if (s === actualDurationSec - 1) activeThreadsTimeline[s] = Math.ceil(vusCount * 0.2);
81
+ else activeThreadsTimeline[s] = vusCount;
72
82
  }
73
83
  }
74
84
  const getPercentile = (arr, pct) => {
@@ -114,12 +124,19 @@ try {
114
124
  const pct99 = (p99 / highestBoundary * 100).toFixed(1);
115
125
  let timelineHtmlElements = "";
116
126
  const maxSecondVolume = Math.max(...successTimeline.map((s, idx) => s + failureTimeline[idx]), 1);
127
+ const svgWidth = 1e3;
128
+ const svgHeight = 150;
129
+ let pointsStr = "";
117
130
  for (let s = 0; s < actualDurationSec; s++) {
118
131
  const successCount = successTimeline[s];
119
132
  const failureCount = failureTimeline[s];
133
+ const currentActiveThreads = activeThreadsTimeline[s];
120
134
  const totalSecRequests = successCount + failureCount;
121
135
  const successPct = (successCount / maxSecondVolume * 100).toFixed(1);
122
136
  const failurePct = (failureCount / maxSecondVolume * 100).toFixed(1);
137
+ const xCoord = s / (actualDurationSec - 1 || 1) * svgWidth;
138
+ const yCoord = svgHeight - currentActiveThreads / Math.max(vusCount, 1) * (svgHeight - 20);
139
+ pointsStr += `${xCoord.toFixed(1)},${yCoord.toFixed(1)} `;
123
140
  timelineHtmlElements += `
124
141
  <div class="timeline-column">
125
142
  <div class="timeline-bar-stack">
@@ -128,7 +145,8 @@ try {
128
145
  </div>
129
146
  <div class="timeline-tick-label">${s + 1}s</div>
130
147
  <div class="timeline-hover-metrics">
131
- <strong>Sec ${s + 1}</strong><br/>
148
+ <strong>Second ${s + 1}</strong><br/>
149
+ \u{1F465} Active VUs: ${currentActiveThreads}<br/>
132
150
  \u{1F7E2} Pass: ${successCount}<br/>
133
151
  \u{1F534} Fail: ${failureCount}<br/>
134
152
  \u{1F4CA} Tput: ${totalSecRequests}/s
@@ -151,6 +169,7 @@ try {
151
169
  --border: #334155;
152
170
  --success: #22c55e;
153
171
  --failure: #ef4444;
172
+ --threads: #38bdf8;
154
173
  --bar-color: linear-gradient(90deg, #ef4444, #f97316);
155
174
  }
156
175
  body {
@@ -185,18 +204,25 @@ try {
185
204
  .card-value.highlight { color: var(--accent-orange); }
186
205
  .card-value.error-good { color: var(--success); }
187
206
 
188
- /* TIMELINE CORRELATION STYLING */
207
+ /* INTEGRATED TIMELINE MATRIX & SVG LAYOVER WRAPPER */
208
+ .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; }
189
209
  .timeline-container {
190
- background-color: var(--bg-card);
191
- border: 1px solid var(--border);
192
- border-radius: 8px;
193
- padding: 2rem 1.5rem 1.5rem 1.5rem;
194
210
  display: flex;
195
211
  justify-content: space-between;
196
212
  align-items: flex-end;
197
- height: 220px;
213
+ height: 150px;
198
214
  gap: 8px;
199
- margin-bottom: 2rem;
215
+ position: relative;
216
+ z-index: 2;
217
+ }
218
+ .timeline-svg-layer {
219
+ position: absolute;
220
+ top: 2.5rem;
221
+ left: 1.5rem;
222
+ width: calc(100% - 3rem);
223
+ height: 150px;
224
+ z-index: 3;
225
+ pointer-events: none;
200
226
  }
201
227
  .timeline-column {
202
228
  flex: 1;
@@ -217,29 +243,32 @@ try {
217
243
  flex-direction: column;
218
244
  justify-content: flex-end;
219
245
  overflow: hidden;
246
+ opacity: 0.75;
247
+ transition: opacity 0.2s;
220
248
  }
249
+ .timeline-column:hover .timeline-bar-stack { opacity: 1; border: 1px solid var(--text-muted); }
221
250
  .stack-fill { width: 100%; transition: height 0.3s ease; }
222
251
  .success-fill { background-color: var(--success); }
223
252
  .failure-fill { background-color: var(--failure); }
224
253
  .timeline-tick-label { font-size: 0.75rem; color: var(--text-muted); margin-top: 0.5rem; font-weight: 600; }
225
254
 
226
- /* HOVER DETAILS METRIC POPUP */
255
+ /* POPUP FLOATER DETAILS */
227
256
  .timeline-column:hover .timeline-hover-metrics { display: block; }
228
257
  .timeline-hover-metrics {
229
258
  display: none;
230
259
  position: absolute;
231
- bottom: 105%;
260
+ bottom: 110%;
232
261
  background-color: #030712;
233
- border: 1px solid var(--border);
262
+ border: 1px solid var(--text-muted);
234
263
  border-radius: 4px;
235
- padding: 0.5rem;
264
+ padding: 0.6rem;
236
265
  font-size: 0.75rem;
237
266
  white-space: nowrap;
238
- z-index: 10;
239
- box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.5);
267
+ z-index: 20;
268
+ box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.7);
240
269
  }
241
270
 
242
- /* LATENCY DISTRIBUTION STYLING */
271
+ /* LATENCY DISTRIBUTION PANEL */
243
272
  .chart-section { background-color: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; padding: 1.5rem; margin-bottom: 2rem; }
244
273
  .chart-row { display: flex; align-items: center; margin-bottom: 0.85rem; }
245
274
  .chart-label { width: 100px; font-size: 0.9rem; color: var(--text-muted); font-weight: 600; }
@@ -250,11 +279,11 @@ try {
250
279
  table { width: 100%; border-collapse: collapse; background-color: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
251
280
  th, td { padding: 1rem; text-align: left; border-bottom: 1px solid var(--border); }
252
281
  th { background-color: #111827; font-weight: 600; color: var(--text-muted); font-size: 0.85rem; text-transform: uppercase; }
253
- tr:last-child td { border-bottom: none; }
254
282
  .total-row { font-weight: 700; background-color: #2a3342; }
255
- .legend { display: flex; gap: 1.5rem; margin-bottom: 0.5rem; justify-content: flex-end; font-size: 0.85rem; }
283
+ .legend { display: flex; gap: 1.5rem; margin-bottom: 0.75rem; justify-content: flex-end; font-size: 0.85rem; }
256
284
  .legend-item { display: flex; align-items: center; gap: 0.35rem; color: var(--text-muted); }
257
285
  .legend-box { width: 12px; height: 12px; border-radius: 2px; }
286
+ .legend-line { width: 20px; height: 3px; background-color: var(--threads); border-radius: 1px; }
258
287
  </style>
259
288
  </head>
260
289
  <body>
@@ -266,7 +295,7 @@ try {
266
295
  </div>
267
296
  <div class="meta-info">
268
297
  <div>Elapsed Duration: ${actualDurationSec}s</div>
269
- <div>Concurrent VUs: ${vusCount} Parallel Threads</div>
298
+ <div>Peak Capacity VUs: ${vusCount} Threads</div>
270
299
  </div>
271
300
  </header>
272
301
 
@@ -289,13 +318,22 @@ try {
289
318
  </div>
290
319
  </div>
291
320
 
292
- <h2>\u{1F504} Throughput & Error Correlation Timeline</h2>
321
+ <h2>\u{1F504} Throughput, Errors & Active Threads Concurrency Correlation</h2>
293
322
  <div class="legend">
294
323
  <div class="legend-item"><div class="legend-box success-fill"></div> Successful Requests</div>
295
324
  <div class="legend-item"><div class="legend-box failure-fill"></div> Failed Workloads</div>
325
+ <div class="legend-item"><div class="legend-line"></div> Active VU Threads</div>
296
326
  </div>
297
- <div class="timeline-container">
298
- ${timelineHtmlElements}
327
+
328
+ <div class="timeline-wrapper">
329
+ <!-- PURE CSS SVG POLYLINE SCALED OVERLAY LAYER FOR ACTIVE THREADS TIMELINE -->
330
+ <svg class="timeline-svg-layer" viewBox="0 0 1000 150" preserveAspectRatio="none">
331
+ <polyline fill="none" stroke="#38bdf8" stroke-width="3" stroke-dasharray="1200" stroke-linecap="round" stroke-linejoin="round" points="${pointsStr.trim()}"></polyline>
332
+ </svg>
333
+
334
+ <div class="timeline-container">
335
+ ${timelineHtmlElements}
336
+ </div>
299
337
  </div>
300
338
 
301
339
  <h2>\u{1F4C8} Latency Percentile Distribution</h2>
@@ -305,7 +343,7 @@ try {
305
343
  <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>
306
344
  <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>
307
345
  <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>
308
- <div class="chart-row"><div class="chart-label">Maximum</div><div class="chart-bar-wrapper"><div class="chart-bar" style="style-width: 100%;"></div></div><div class="chart-value-tag">${maxLatency.toFixed(1)} ms</div></div>
346
+ <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>
309
347
  </div>
310
348
 
311
349
  <h2>Performance Breakdown Table</h2>
@@ -341,7 +379,7 @@ try {
341
379
  </body>
342
380
  </html>`;
343
381
  fs.writeFileSync(htmlOutputPath, htmlContent, "utf-8");
344
- console.log(`\u2728 \x1B[32mHTML Dashboard with Correlation Timeline saved to:\x1B[0m ${htmlOutputPath}
382
+ console.log(`\u2728 \x1B[32mHTML Dashboard with Integrated Active Threads Overlay saved to:\x1B[0m ${htmlOutputPath}
345
383
  `);
346
384
  } catch (error) {
347
385
  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.4",
3
+ "version": "2.0.5",
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",