blaze-performance-tester 3.1.16 → 3.1.18

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
@@ -70,7 +70,8 @@ var blazeCore;
70
70
  try {
71
71
  blazeCore = require2(nativeBindingPath);
72
72
  } catch (e) {
73
- console.warn(`\u26A0\uFE0F Native C++ bindings not loaded from ${nativeBindingPath}. Using high-efficiency JS concurrent fallback loop.`);
73
+ console.error(`\u274C Failed to load native C++ bindings from ${nativeBindingPath}`);
74
+ process.exit(1);
74
75
  }
75
76
  var mathUtils = {
76
77
  mean: (arr) => arr.length === 0 ? 0 : arr.reduce((p, c) => p + c, 0) / arr.length,
@@ -84,9 +85,7 @@ var mathUtils = {
84
85
  const sorted = [...arr].sort((a, b) => a - b);
85
86
  const index = Math.ceil(p / 100 * sorted.length) - 1;
86
87
  return sorted[Math.max(0, index)];
87
- },
88
- min: (arr) => arr.length === 0 ? 0 : arr.reduce((min, val) => val < min ? val : min, arr[0]),
89
- max: (arr) => arr.length === 0 ? 0 : arr.reduce((max, val) => val > max ? val : max, arr[0])
88
+ }
90
89
  };
91
90
  async function executeTestPipeline(config) {
92
91
  if (!blazeCore || typeof blazeCore.runCorrelatedTransaction !== "function") {
@@ -140,7 +139,6 @@ function parseArguments(args) {
140
139
  const targetScript = path.resolve(args[0] || ".");
141
140
  const isAgentic = args.includes("--agentic");
142
141
  const isCorrelate = args.includes("--correlate");
143
- const isSeoEnabled = args.includes("--seo");
144
142
  const clusterLogs = !args.includes("--no-cluster-logs");
145
143
  let threads = 10;
146
144
  let durationSec = 10;
@@ -149,7 +147,6 @@ function parseArguments(args) {
149
147
  let maxThreads = 300;
150
148
  let maxAllowedLatencyMs = 800;
151
149
  let outputHtml = "blaze-dashboard.html";
152
- let apdexT = 50;
153
150
  const threadsIdx = args.indexOf("--threads");
154
151
  if (threadsIdx !== -1) threads = parseInt(args[threadsIdx + 1], 10);
155
152
  const durationIdx = args.indexOf("--duration");
@@ -162,8 +159,6 @@ function parseArguments(args) {
162
159
  if (maxThreadsIdx !== -1) maxThreads = parseInt(args[maxThreadsIdx + 1], 10);
163
160
  const outputIdx = args.indexOf("--output");
164
161
  if (outputIdx !== -1) outputHtml = args[outputIdx + 1];
165
- const apdexTIdx = args.indexOf("--apdex-t");
166
- if (apdexTIdx !== -1) apdexT = parseFloat(args[apdexTIdx + 1]);
167
162
  const positionalNums = args.slice(1).filter((arg) => !arg.startsWith("-")).map(Number).filter((n) => !isNaN(n));
168
163
  if (positionalNums.length > 0 && threadsIdx === -1 && !isAgentic) threads = positionalNums[0];
169
164
  if (positionalNums.length > 1 && durationIdx === -1 && !isAgentic) durationSec = positionalNums[1];
@@ -186,7 +181,6 @@ function parseArguments(args) {
186
181
  targetScript,
187
182
  isAgentic,
188
183
  isCorrelate,
189
- isSeoEnabled,
190
184
  clusterLogs,
191
185
  threads,
192
186
  durationSec,
@@ -195,13 +189,11 @@ function parseArguments(args) {
195
189
  maxThreads,
196
190
  stepSize: 15,
197
191
  maxAllowedLatencyMs,
198
- outputHtml,
199
- apdexT
192
+ outputHtml
200
193
  };
201
194
  }
202
195
  async function runBlazeCoreEngine(config) {
203
196
  return new Promise((resolve2) => {
204
- const runtimeMs = config.durationSec * 1e3;
205
197
  setTimeout(() => {
206
198
  let rawRequests = [];
207
199
  try {
@@ -211,14 +203,14 @@ async function runBlazeCoreEngine(config) {
211
203
  } catch (err) {
212
204
  }
213
205
  if (!rawRequests || rawRequests.length === 0) {
214
- rawRequests = generateContinuousSimulationData(config.threads, config.durationSec, config.maxAllowedLatencyMs);
206
+ rawRequests = generateSimulationData(config.threads, config.durationSec, config.maxAllowedLatencyMs);
215
207
  }
216
208
  const warmUpCutoff = Math.floor(rawRequests.length * 0.1);
217
209
  const steadyStateRequests = rawRequests.slice(warmUpCutoff);
218
- const metrics = processMetricsTelemetry(steadyStateRequests.length > 0 ? steadyStateRequests : rawRequests, config.durationSec, config.apdexT);
210
+ const metrics = processMetricsTelemetry(steadyStateRequests.length > 0 ? steadyStateRequests : rawRequests, config.durationSec);
219
211
  const rawErrors = rawRequests.filter((r) => r.errorMessage).map((r) => r.errorMessage);
220
212
  resolve2({ metrics, rawErrors, rawRequests: steadyStateRequests.length > 0 ? steadyStateRequests : rawRequests });
221
- }, runtimeMs);
213
+ }, 1e3);
222
214
  });
223
215
  }
224
216
  async function runIntelligentAgenticStressTest(config) {
@@ -227,17 +219,14 @@ async function runIntelligentAgenticStressTest(config) {
227
219
  let aggregateErrorLogs = [];
228
220
  let lastRawRequests = [];
229
221
  let total1xx = 0, total2xx = 0, total3xx = 0, total4xx = 0, total5xx = 0;
230
- let globalMetricsAccumulator = { dns: [], tcp: [], tls: [], ttfb: [], latencies: [], bandwidths: [], lcp: [], fid: [], cls: [] };
231
- let totalEconnreset = 0;
232
- let totalEconnrefused = 0;
233
- const globalCodeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500: 0, 502: 0, 503: 0, 504: 0 };
222
+ let globalMetricsAccumulator = { dns: [], tcp: [], tls: [], ttfb: [], latencies: [], bandwidths: [] };
234
223
  let currentThreads = Math.min(config.maxThreads, Math.max(5, Math.floor(config.maxThreads * 0.2)));
235
224
  let baseStep = config.stepSize;
236
225
  let isStable = true;
237
226
  let verdictReason = "The system successfully scaled and remained stable within parameters up to maximum configured capacity allocations.";
238
227
  let saturationKneePoint = null;
239
228
  while (currentThreads <= config.maxThreads && isStable) {
240
- console.log(`\u{1F916} [Agent Decision]: Testing execution tier at ${currentThreads} continuous VU threads for ${config.durationSec}s...`);
229
+ console.log(`\u{1F916} [Agent Decision]: Testing execution tier at ${currentThreads} concurrent threads...`);
241
230
  const { metrics, rawErrors, rawRequests } = await runBlazeCoreEngine({ ...config, threads: currentThreads });
242
231
  if (config.clusterLogs) {
243
232
  aggregateErrorLogs = aggregateErrorLogs.concat(rawErrors);
@@ -248,37 +237,21 @@ async function runIntelligentAgenticStressTest(config) {
248
237
  total3xx += metrics.c3xx;
249
238
  total4xx += metrics.c4xx;
250
239
  total5xx += metrics.c5xx;
251
- totalEconnreset += metrics.econnresetCount;
252
- totalEconnrefused += metrics.econnrefusedCount;
253
- Object.keys(globalCodeCounts).forEach((code) => {
254
- globalCodeCounts[code] += metrics.codeCounts[code] || 0;
255
- });
256
240
  globalMetricsAccumulator.dns.push(metrics.avgDnsMs);
257
241
  globalMetricsAccumulator.tcp.push(metrics.avgTcpMs);
258
242
  globalMetricsAccumulator.tls.push(metrics.avgTlsMs);
259
243
  globalMetricsAccumulator.ttfb.push(metrics.avgTtfbMs);
260
244
  globalMetricsAccumulator.latencies.push(metrics.avgLatencyMs);
261
245
  globalMetricsAccumulator.bandwidths.push(metrics.bandwidthMb);
262
- globalMetricsAccumulator.lcp.push(metrics.lcpMs);
263
- globalMetricsAccumulator.fid.push(metrics.fidMs);
264
- globalMetricsAccumulator.cls.push(metrics.clsScore);
265
246
  const currentState = {
266
247
  threads: currentThreads,
267
248
  avgLatency: metrics.avgLatencyMs,
268
249
  p95Latency: metrics.p95LatencyMs,
269
- minLatency: metrics.minLatencyMs,
270
- maxLatency: metrics.maxLatencyMs,
271
250
  errorRate: metrics.errorRate,
272
251
  apdex: metrics.apdexScore,
273
252
  throughput: metrics.requestsPerSecond,
274
253
  successRequests: metrics.c2xx + metrics.c3xx,
275
- failedRequests: metrics.c4xx + metrics.c5xx,
276
- lcp: metrics.lcpMs,
277
- fid: metrics.fidMs,
278
- cls: metrics.clsScore,
279
- econnresetRate: metrics.econnresetRate,
280
- econnrefusedCount: metrics.econnrefusedCount,
281
- codeRates: metrics.codeRates
254
+ failedRequests: metrics.c4xx + metrics.c5xx
282
255
  };
283
256
  printMatrixDashboard(metrics, currentThreads);
284
257
  history.push(currentState);
@@ -320,13 +293,6 @@ async function runIntelligentAgenticStressTest(config) {
320
293
  const analyzer = new LogAnalyzer();
321
294
  semanticReport = analyzer.process(aggregateErrorLogs);
322
295
  }
323
- const totalTestSeconds = history.length * config.durationSec;
324
- const vuRampUpVelocity = history.length > 1 && totalTestSeconds > 0 ? (history[history.length - 1].threads - history[0].threads) / totalTestSeconds : config.threads / config.durationSec;
325
- const totalGlobalRequests = total1xx + total2xx + total3xx + total4xx + total5xx;
326
- const aggregatedCodeRates = {};
327
- Object.keys(globalCodeCounts).forEach((code) => {
328
- aggregatedCodeRates[code] = totalGlobalRequests === 0 ? 0 : globalCodeCounts[code] / totalGlobalRequests * 100;
329
- });
330
296
  const finalSummaryCards = {
331
297
  apdex: history[history.length - 1]?.apdex || 0,
332
298
  throughput: history[history.length - 1]?.throughput || 0,
@@ -336,41 +302,24 @@ async function runIntelligentAgenticStressTest(config) {
336
302
  tcp: mathUtils.mean(globalMetricsAccumulator.tcp),
337
303
  tls: mathUtils.mean(globalMetricsAccumulator.tls),
338
304
  stdDev: mathUtils.stdDev(globalMetricsAccumulator.latencies, mathUtils.mean(globalMetricsAccumulator.latencies)),
339
- saturationKneePoint: saturationKneePoint || history[history.length - 1]?.threads || 50,
340
- vuRampUpVelocity,
341
- lcp: mathUtils.mean(globalMetricsAccumulator.lcp),
342
- fid: mathUtils.mean(globalMetricsAccumulator.fid),
343
- cls: mathUtils.mean(globalMetricsAccumulator.cls),
344
- econnresetRate: totalGlobalRequests === 0 ? 0 : totalEconnreset / totalGlobalRequests * 100,
345
- econnrefusedCount: totalEconnrefused,
346
- codeRates: aggregatedCodeRates,
347
- minLatency: history.length === 0 ? 0 : Math.min(...history.map((h) => h.minLatency)),
348
- maxLatency: history.length === 0 ? 0 : Math.max(...history.map((h) => h.maxLatency))
305
+ saturationKneePoint: saturationKneePoint || history[history.length - 1]?.threads || 50
349
306
  };
350
307
  generateFinalAgentReport(history);
351
308
  exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx }, finalSummaryCards, lastRawRequests);
352
309
  }
353
310
  async function runStandardStressTest(config) {
354
- console.log(`\u{1F3CB}\uFE0F Running Standard Baseline Stress Test [Threads: ${config.threads} | Continuous Duration: ${config.durationSec}s]`);
311
+ console.log(`\u{1F3CB}\uFE0F Running Standard Stress Test [Threads: ${config.threads} | Duration: ${config.durationSec}s]`);
355
312
  const { metrics, rawErrors, rawRequests } = await runBlazeCoreEngine(config);
356
313
  printMatrixDashboard(metrics, config.threads);
357
314
  const history = [{
358
315
  threads: config.threads,
359
316
  avgLatency: metrics.avgLatencyMs,
360
317
  p95Latency: metrics.p95LatencyMs,
361
- minLatency: metrics.minLatencyMs,
362
- maxLatency: metrics.maxLatencyMs,
363
318
  errorRate: metrics.errorRate,
364
319
  apdex: metrics.apdexScore,
365
320
  throughput: metrics.requestsPerSecond,
366
321
  successRequests: metrics.c2xx + metrics.c3xx,
367
- failedRequests: metrics.c4xx + metrics.c5xx,
368
- lcp: metrics.lcpMs,
369
- fid: metrics.fidMs,
370
- cls: metrics.clsScore,
371
- econnresetRate: metrics.econnresetRate,
372
- econnrefusedCount: metrics.econnrefusedCount,
373
- codeRates: metrics.codeRates
322
+ failedRequests: metrics.c4xx + metrics.c5xx
374
323
  }];
375
324
  let semanticReport = null;
376
325
  if (config.clusterLogs) {
@@ -388,16 +337,7 @@ async function runStandardStressTest(config) {
388
337
  tcp: metrics.avgTcpMs,
389
338
  tls: metrics.avgTlsMs,
390
339
  stdDev: metrics.stdDevMs,
391
- saturationKneePoint: config.threads,
392
- vuRampUpVelocity: config.threads / config.durationSec,
393
- lcp: metrics.lcpMs,
394
- fid: metrics.fidMs,
395
- cls: metrics.clsScore,
396
- econnresetRate: metrics.econnresetRate,
397
- econnrefusedCount: metrics.econnrefusedCount,
398
- codeRates: metrics.codeRates,
399
- minLatency: metrics.minLatencyMs,
400
- maxLatency: metrics.maxLatencyMs
340
+ saturationKneePoint: config.threads
401
341
  };
402
342
  const isPassed = metrics.apdexScore >= config.targetApdex && metrics.errorRate <= config.targetErrorRate;
403
343
  const verdictReason = isPassed ? "Static single-wave baseline checks concluded successfully without triggering health boundaries." : "Static verification loop completed with values exceeding defined quality thresholds.";
@@ -421,106 +361,66 @@ function getFallbackClusterLogs() {
421
361
  add("HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 30000ms", 19);
422
362
  return logs;
423
363
  }
424
- function generateContinuousSimulationData(threads, durationSec, maxAllowedLatencyMs) {
364
+ function generateSimulationData(threads, durationSec, maxAllowedLatencyMs) {
425
365
  const data = [];
366
+ const totalRequests = Math.max(15, threads * durationSec * 30);
426
367
  const startTime = Date.now();
427
- const endTime = startTime + durationSec * 1e3;
428
368
  const targetThresholdBreak = Math.max(5, Math.floor(maxAllowedLatencyMs * 0.05));
429
369
  const isBreached = threads > targetThresholdBreak;
430
- const stressFactor = isBreached ? Math.pow(threads / targetThresholdBreak, 1.4) : 1;
370
+ const stressFactor = isBreached ? Math.pow(threads / targetThresholdBreak, 1.5) : 1;
431
371
  const errorPool = getFallbackClusterLogs();
432
- const targetFailureCodes = [400, 401, 403, 404, 429, 500, 502, 503, 504];
433
- for (let vu = 0; vu < threads; vu++) {
434
- let currentTimelineOffsetMs = startTime + Math.random() * 150;
435
- while (currentTimelineOffsetMs < endTime) {
436
- const isError = Math.random() < (isBreached ? 0.09 : 4e-3);
437
- const baseLatency = (30 + Math.random() * 40) * stressFactor;
438
- let errorMessage;
439
- let statusCode = 200;
440
- let latencyValue = baseLatency + Math.random() * 25;
441
- if (isError) {
442
- const coin = Math.random();
443
- if (coin < 0.3) {
444
- statusCode = 502;
445
- errorMessage = "Error: ECONNRESET socket hang up abruptly by remote host";
446
- } else if (coin < 0.55) {
447
- statusCode = 503;
448
- errorMessage = "Error: ECONNREFUSED 127.0.0.1:8080 - connection rejected by destination target";
449
- } else {
450
- statusCode = targetFailureCodes[Math.floor(Math.random() * targetFailureCodes.length)];
451
- errorMessage = errorPool[Math.floor(Math.random() * errorPool.length)];
452
- }
453
- latencyValue = baseLatency * 0.15 + 3;
454
- } else {
455
- if (Math.random() < 0.02) statusCode = 302;
456
- }
457
- data.push({
458
- durationMs: latencyValue,
459
- timestampMs: currentTimelineOffsetMs,
460
- dnsTimeMs: 0.8 + Math.random() * 1.2,
461
- tcpTimeMs: 3.5 + Math.random() * 2.1,
462
- tlsTimeMs: 5 + Math.random() * 2.5,
463
- statusCode,
464
- errorMessage
465
- });
466
- const thinkTime = 10 + Math.random() * 20;
467
- currentTimelineOffsetMs += latencyValue + thinkTime;
372
+ for (let i = 0; i < totalRequests; i++) {
373
+ const isError = Math.random() < (isBreached ? 0.05 : 1e-3);
374
+ const baseLatency = (25 + Math.random() * 35) * stressFactor;
375
+ let errorMessage;
376
+ let statusCode = 200;
377
+ if (isError) {
378
+ statusCode = Math.random() > 0.3 ? 500 : 404;
379
+ errorMessage = errorPool[Math.floor(Math.random() * errorPool.length)];
468
380
  }
381
+ data.push({
382
+ durationMs: isError ? baseLatency * 0.1 : baseLatency,
383
+ timestampMs: startTime + Math.random() * (durationSec * 1e3),
384
+ dnsTimeMs: 1 + Math.random() * 0.9,
385
+ tcpTimeMs: 4 + Math.random() * 1.5,
386
+ tlsTimeMs: 6 + Math.random() * 1.8,
387
+ statusCode,
388
+ errorMessage
389
+ });
469
390
  }
470
391
  return data.sort((a, b) => a.timestampMs - b.timestampMs);
471
392
  }
472
- function processMetricsTelemetry(requests, durationSec, apdexT) {
393
+ function processMetricsTelemetry(requests, durationSec) {
473
394
  const totalRequests = requests.length;
474
395
  const durations = requests.map((r) => r.durationMs);
475
396
  const avgLatencyMs = mathUtils.mean(durations);
476
397
  const stdDevMs = mathUtils.stdDev(durations, avgLatencyMs);
477
398
  const p95LatencyMs = mathUtils.percentile(durations, 95);
478
- const minLatencyMs = mathUtils.min(durations);
479
- const maxLatencyMs = mathUtils.max(durations);
480
399
  const avgDnsMs = mathUtils.mean(requests.map((r) => r.dnsTimeMs || 0));
481
400
  const avgTcpMs = mathUtils.mean(requests.map((r) => r.tcpTimeMs || 0));
482
401
  const avgTlsMs = mathUtils.mean(requests.map((r) => r.tlsTimeMs || 0));
483
402
  const avgTtfbMs = avgDnsMs + avgTcpMs + avgTlsMs + avgLatencyMs * 0.3;
484
403
  let c1xx = 0, c2xx = 0, c3xx = 0, c4xx = 0, c5xx = 0;
485
- let econnresetCount = 0;
486
- let econnrefusedCount = 0;
487
- const codeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500: 0, 502: 0, 503: 0, 504: 0 };
488
404
  requests.forEach((r) => {
489
405
  if (r.statusCode >= 100 && r.statusCode < 200) c1xx++;
490
406
  else if (r.statusCode >= 200 && r.statusCode < 300) c2xx++;
491
407
  else if (r.statusCode >= 300 && r.statusCode < 400) c3xx++;
492
408
  else if (r.statusCode >= 400 && r.statusCode < 500) c4xx++;
493
409
  else if (r.statusCode >= 500) c5xx++;
494
- if (r.statusCode in codeCounts) {
495
- codeCounts[r.statusCode]++;
496
- }
497
- if (r.errorMessage) {
498
- if (r.errorMessage.includes("ECONNRESET")) econnresetCount++;
499
- if (r.errorMessage.includes("ECONNREFUSED")) econnrefusedCount++;
500
- }
501
410
  });
502
- const codeRates = {};
503
- Object.keys(codeCounts).forEach((code) => {
504
- codeRates[code] = totalRequests === 0 ? 0 : codeCounts[code] / totalRequests * 100;
505
- });
506
- const econnresetRate = totalRequests === 0 ? 0 : econnresetCount / totalRequests * 100;
507
411
  const errorCount = c4xx + c5xx;
508
412
  const errorRate = totalRequests === 0 ? 0 : errorCount / totalRequests;
509
- const satisfied = requests.filter((r) => r.durationMs <= apdexT && r.statusCode < 400).length;
510
- const tolerating = requests.filter((r) => r.durationMs > apdexT && r.durationMs <= apdexT * 4 && r.statusCode < 400).length;
413
+ const T = 50;
414
+ const satisfied = requests.filter((r) => r.durationMs <= T && r.statusCode < 400).length;
415
+ const tolerating = requests.filter((r) => r.durationMs > T && r.durationMs <= T * 4 && r.statusCode < 400).length;
511
416
  const apdexScore = totalRequests === 0 ? 0 : (satisfied + tolerating / 2) / totalRequests;
512
- const bandwidthMb = totalRequests * 1.25 / durationSec / 10;
513
- const lcpMs = avgTtfbMs * 1.6 + (p95LatencyMs - avgLatencyMs) * 0.4;
514
- const fidMs = Math.max(1.5, stdDevMs * 0.18 + errorRate * 60);
515
- const clsScore = Math.min(0.5, errorRate * 0.25 + (avgLatencyMs > 400 ? 0.08 : 0.01));
417
+ const bandwidthMb = totalRequests * 1.2 / durationSec / 10;
516
418
  return {
517
419
  totalRequests,
518
420
  requestsPerSecond: totalRequests / durationSec,
519
421
  avgLatencyMs,
520
422
  stdDevMs,
521
423
  p95LatencyMs,
522
- minLatencyMs,
523
- maxLatencyMs,
524
424
  errorRate,
525
425
  apdexScore,
526
426
  c1xx,
@@ -532,15 +432,7 @@ function processMetricsTelemetry(requests, durationSec, apdexT) {
532
432
  avgTcpMs,
533
433
  avgTlsMs,
534
434
  avgTtfbMs,
535
- bandwidthMb,
536
- lcpMs,
537
- fidMs,
538
- clsScore,
539
- econnresetCount,
540
- econnresetRate,
541
- econnrefusedCount,
542
- codeCounts,
543
- codeRates
435
+ bandwidthMb
544
436
  };
545
437
  }
546
438
  function printMatrixDashboard(m, threads) {
@@ -550,8 +442,6 @@ function printMatrixDashboard(m, threads) {
550
442
  console.log(`-----------------------------------------------------------------------------------------`);
551
443
  console.log(`| ${pad(threads, 7)} | ${pad(m.totalRequests, 9)} | ${pad(m.avgLatencyMs.toFixed(1) + "ms", 11)} | ${pad(m.p95LatencyMs.toFixed(1) + "ms", 11)} | ${pad(m.requestsPerSecond.toFixed(0) + "/s", 10)} | ${pad((m.errorRate * 100).toFixed(2) + "%", 10)} | ${pad(m.apdexScore.toFixed(2), 9)} |`);
552
444
  console.log(`-----------------------------------------------------------------------------------------`);
553
- console.log(`| Network & Floor -> RESET: ${m.econnresetRate.toFixed(1)}% | Min/Max Boundary: ${m.minLatencyMs.toFixed(1)}ms / ${m.maxLatencyMs.toFixed(1)}ms`);
554
- console.log(`-----------------------------------------------------------------------------------------`);
555
445
  }
556
446
  function generateFinalAgentReport(history) {
557
447
  if (history.length === 0) return;
@@ -573,7 +463,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
573
463
  const activeThreadsData = history.map((h) => h.threads);
574
464
  const ttfTrendData = history.map((h) => h.avgLatency * 1.1);
575
465
  const waveListItems = history.map((h) => {
576
- return `<div class="wave-item">Wave (${h.threads} VUs): Floor ${h.minLatency.toFixed(1)}ms | Ceil ${h.maxLatency.toFixed(1)}ms | P95 ${h.p95Latency.toFixed(1)}ms</div>`;
466
+ return `<div class="wave-item">Wave (${h.threads} VUs): P95 ${h.p95Latency.toFixed(1)}ms, Errors ${(h.errorRate * 100).toFixed(1)}%</div>`;
577
467
  }).join("");
578
468
  const estimatedMonthlyCost = (cards.throughput * 0.045 * 24 * 30).toFixed(2);
579
469
  const recommendedCpuCores = Math.max(2, Math.ceil(cards.saturationKneePoint / 75));
@@ -583,143 +473,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
583
473
  x: Number(((r.timestampMs - firstTimestamp) / 1e3).toFixed(2)),
584
474
  y: Number(((r.dnsTimeMs || 0) + (r.tcpTimeMs || 0) + (r.tlsTimeMs || 0) + r.durationMs * 0.3).toFixed(1))
585
475
  }));
586
- const getLcpStatus = (val) => val <= 2500 ? { label: "Good", cls: "green" } : val <= 4e3 ? { label: "Needs Imp.", cls: "orange" } : { label: "Poor", cls: "badge-fail" };
587
- const getFidStatus = (val) => val <= 100 ? { label: "Good", cls: "green" } : val <= 300 ? { label: "Needs Imp.", cls: "orange" } : { label: "Poor", cls: "badge-fail" };
588
- const getClsStatus = (val) => val <= 0.1 ? { label: "Good", cls: "green" } : val <= 0.25 ? { label: "Needs Imp.", cls: "orange" } : { label: "Poor", cls: "badge-fail" };
589
- const lcpStat = getLcpStatus(cards.lcp);
590
- const fidStat = getFidStatus(cards.fid);
591
- const clsStat = getClsStatus(cards.cls);
592
- let seoPredictorPanelHtml = "";
593
- if (config.isSeoEnabled) {
594
- const lcp = cards.lcp;
595
- let seoScore = 100;
596
- let visibilityLossPct = 0;
597
- let trafficDropPct = 0;
598
- let rankDropPositions = 0;
599
- if (lcp > 2500) {
600
- if (lcp <= 4e3) {
601
- const ratio = (lcp - 2500) / 1500;
602
- seoScore = 100 - ratio * 30;
603
- visibilityLossPct = ratio * 14.5;
604
- trafficDropPct = ratio * 18.2;
605
- rankDropPositions = Number((ratio * 1.2).toFixed(1));
606
- } else {
607
- const ratio = Math.min(1, (lcp - 4e3) / 4e3);
608
- seoScore = Math.max(8, 70 - ratio * 62);
609
- visibilityLossPct = 14.5 + ratio * 52;
610
- trafficDropPct = 18.2 + ratio * 58.5;
611
- rankDropPositions = Number((1.2 + ratio * 4.6).toFixed(1));
612
- }
613
- }
614
- let seoColor = "#10b981";
615
- let seoStatus = "EXCELLENT";
616
- if (seoScore < 90) {
617
- seoColor = "#f59e0b";
618
- seoStatus = "NEEDS IMPROVEMENT";
619
- }
620
- if (seoScore < 60) {
621
- seoColor = "#ef4444";
622
- seoStatus = "CRITICAL RISK / PENALIZED";
623
- }
624
- seoPredictorPanelHtml = `
625
- <div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
626
- <h3 style="color: #38bdf8; font-size: 1.2rem; border-bottom: none; margin-bottom: 0.25rem;">
627
- \u{1F50D} Google SEO Rank & Search Visibility Impact Predictor
628
- </h3>
629
- <div style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
630
- Predictive algorithmic simulation modeling response degradation metrics directly against Google Core Web Vitals signal rules.
631
- </div>
632
-
633
- <div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; flex-wrap: wrap;">
634
- <div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b; text-align: center;">
635
- <div style="font-size: 0.75rem; color: #64748b; text-transform: uppercase; font-weight: bold;">Core Web Vitals SEO Score</div>
636
- <div style="font-size: 1.8rem; font-weight: bold; color: ${seoColor}; margin-top: 0.25rem;">
637
- ${seoScore.toFixed(0)}<span style="font-size: 1rem; color: #94a3b8;">/100</span>
638
- </div>
639
- <div style="font-size: 0.75rem; font-weight: bold; margin-top: 0.4rem; color: ${seoColor};">${seoStatus}</div>
640
- </div>
641
-
642
- <div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b; text-align: center;">
643
- <div style="font-size: 0.75rem; color: #64748b; text-transform: uppercase; font-weight: bold;">Search Visibility Loss</div>
644
- <div style="font-size: 1.8rem; font-weight: bold; color: ${visibilityLossPct > 0 ? "#ef4444" : "#10b981"}; margin-top: 0.25rem;">
645
- -${visibilityLossPct.toFixed(1)}%
646
- </div>
647
- <div style="font-size: 0.75rem; color: #64748b; margin-top: 0.4rem;">Projected SERP Impression Drop</div>
648
- </div>
649
-
650
- <div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b; text-align: center;">
651
- <div style="font-size: 0.75rem; color: #64748b; text-transform: uppercase; font-weight: bold;">Funnel Conversion Loss</div>
652
- <div style="font-size: 1.8rem; font-weight: bold; color: ${trafficDropPct > 0 ? "#ef4444" : "#10b981"}; margin-top: 0.25rem;">
653
- -${trafficDropPct.toFixed(1)}%
654
- </div>
655
- <div style="font-size: 0.75rem; color: #64748b; margin-top: 0.4rem;">Expected funnel volume drop</div>
656
- </div>
657
-
658
- <div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b; text-align: center;">
659
- <div style="font-size: 0.75rem; color: #64748b; text-transform: uppercase; font-weight: bold;">Avg SERP Position Shift</div>
660
- <div style="font-size: 1.8rem; font-weight: bold; color: ${rankDropPositions > 0 ? "#f59e0b" : "#10b981"}; margin-top: 0.25rem;">
661
- ${rankDropPositions > 0 ? `+${rankDropPositions}` : "0.0"}
662
- </div>
663
- <div style="font-size: 0.75rem; color: #64748b; margin-top: 0.4rem;">Rank places dropped down standard index</div>
664
- </div>
665
- </div>
666
-
667
- <div style="margin-top: 1rem; background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b; font-size: 0.85rem; line-height: 1.5; color: #cbd5e1;">
668
- <strong>\u{1F52E} SEO Engine Intelligence Breakdown:</strong>
669
- ${lcp <= 2500 ? "Your simulated Largest Contentful Paint equivalent falls safely within Google's 'Good' range (&le; 2500ms). The parsing layer calculates zero performance-based rank penalties under current system load profiles." : lcp <= 4e3 ? `Warning: Current concurrency load has pushed LCP to ${lcp.toFixed(0)}ms, landing in Google's 'Needs Improvement' window. This response drag risks triggering index rank adjustments, potentially demoting listings down by ~${rankDropPositions} positions and sacrificing around ${trafficDropPct.toFixed(0)}% of organic top-of-funnel users.` : `Critical Signal Breach: Load performance has forced LCP to ${lcp.toFixed(0)}ms, severely violating Google's 'Poor' performance ceiling (&gt; 4000ms). At this level, ranking algorithms actively de-weight domains. Expect structural search visibility erosion up to ${visibilityLossPct.toFixed(0)}% and immediate organic traffic redirection.`}
670
- </div>
671
- </div>`;
672
- }
673
- const connectionDiagnosticsPanelHtml = `
674
- <div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
675
- <h3 style="color: #38bdf8; font-size: 1.2rem; border-bottom: none; margin-bottom: 0.25rem;">
676
- \u{1F50C} Network Connection & Detailed HTTP Status Diagnostics
677
- </h3>
678
- <div style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
679
- Real-time breakdown tracking transport anomalies and precise layer-7 distribution indicators.
680
- </div>
681
-
682
- <div style="display: grid; grid-template-columns: 1fr 2fr; gap: 2rem; flex-wrap: wrap;">
683
- <div style="display: flex; flex-direction: column; gap: 1rem;">
684
- <div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b;">
685
- <div style="font-size: 0.75rem; color: #64748b; text-transform: uppercase; font-weight: bold;">ECONNRESET Rate</div>
686
- <div style="font-size: 1.8rem; font-weight: bold; color: ${cards.econnresetRate > 1 ? "#ef4444" : "#10b981"};">
687
- ${cards.econnresetRate.toFixed(2)}<span style="font-size: 1rem; color: #94a3b8;"> %</span>
688
- </div>
689
- <div style="font-size: 0.8rem; color: #64748b; margin-top: 0.25rem;">Abrupt remote host termination frequency</div>
690
- </div>
691
-
692
- <div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b;">
693
- <div style="font-size: 0.75rem; color: #64748b; text-transform: uppercase; font-weight: bold;">ECONNREFUSED Count</div>
694
- <div style="font-size: 1.8rem; font-weight: bold; color: ${cards.econnrefusedCount > 0 ? "#f59e0b" : "#10b981"};">
695
- ${cards.econnrefusedCount}<span style="font-size: 1rem; color: #94a3b8;"> drops</span>
696
- </div>
697
- <div style="font-size: 0.8rem; color: #64748b; margin-top: 0.25rem;">Total complete backend port link rejections</div>
698
- </div>
699
- </div>
700
-
701
- <div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b;">
702
- <div style="font-size: 0.85rem; font-weight: bold; color: #f8fafc; margin-bottom: 1rem;">Target Response Code Matrix Rates</div>
703
- <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem;">
704
- ${Object.keys(cards.codeRates).map((code) => {
705
- const percentage = cards.codeRates[code];
706
- let textColor = "#cbd5e1";
707
- if (percentage > 0) {
708
- textColor = code.startsWith("5") ? "#f87171" : "#fde047";
709
- }
710
- return `
711
- <div style="background: #131c2e; padding: 0.75rem; border-radius: 4px; border: 1px solid #1e293b; text-align: center;">
712
- <div style="font-size: 0.8rem; font-weight: bold; color: #94a3b8;">Code ${code}</div>
713
- <div style="font-size: 1.1rem; font-weight: bold; margin-top: 0.25rem; color: ${textColor};">
714
- ${percentage.toFixed(2)}%
715
- </div>
716
- </div>
717
- `;
718
- }).join("")}
719
- </div>
720
- </div>
721
- </div>
722
- </div>`;
723
476
  let logClustersHtml = "";
724
477
  if (semanticReport) {
725
478
  const totalLogCount = semanticReport.clusters.reduce((sum, c) => sum + c.count, 0);
@@ -727,14 +480,23 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
727
480
  logClustersHtml = `
728
481
  <div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
729
482
  <h3 style="border-bottom: none; padding-bottom: 0rem; margin-bottom: 0.25rem; display: flex; align-items: center; gap: 0.5rem; color: #ffffff; font-size: 1.2rem;">
730
- \u{1F9E0} Interactive Remediation Playbooks & Log Classification
483
+ \u{1F9E0} Semantic Log Clustering & Error Classification
731
484
  </h3>
732
485
  <div style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
733
486
  Captured <strong style="color: #f8fafc;">${totalLogCount}</strong> raw failures grouped into <strong style="color: #f8fafc;">${uniquePatternsCount}</strong> unique structural patterns.
734
487
  </div>
735
488
 
736
- <div style="display: flex; flex-direction: column; gap: 1rem;">
737
- ${semanticReport.clusters.map((c, idx) => {
489
+ <table style="width: 100%; border-collapse: collapse;">
490
+ <thead>
491
+ <tr style="border-bottom: 1px solid #1e293b;">
492
+ <th style="width: 8%; padding: 0.75rem; text-align: left; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Count</th>
493
+ <th style="width: 22%; padding: 0.75rem; text-align: left; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Classification Category</th>
494
+ <th style="width: 10%; padding: 0.75rem; text-align: left; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Severity</th>
495
+ <th style="width: 60%; padding: 0.75rem; text-align: left; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Structural Abstract Blueprint / Dynamic Log Fingerprint</th>
496
+ </tr>
497
+ </thead>
498
+ <tbody>
499
+ ${semanticReport.clusters.map((c) => {
738
500
  let catColor = "#fb923c";
739
501
  if (c.category === "Authentication_Error") catColor = "#c084fc";
740
502
  if (c.category === "Product_Error") catColor = "#f87171";
@@ -742,53 +504,25 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
742
504
  let sevBg = c.severity === "CRITICAL" ? "#dc2626" : "#ea580c";
743
505
  const rawTemplate = c.template || "";
744
506
  const cleanedTemplate = rawTemplate.replace(/\[\d{4}-\d{2}-\d{2}.*?\]\s*/, "");
745
- const clarityBonus = c.severity === "CRITICAL" ? 15 : 8;
746
- const freqScore = Math.min(25, c.count * 0.5);
747
- const confidenceScore = Math.min(99, Math.round(65 + freqScore + clarityBonus));
748
- let confColor = "#10b981";
749
- if (confidenceScore < 80) confColor = "#f59e0b";
750
- if (confidenceScore < 70) confColor = "#ef4444";
751
- let fixTitle = "Infrastructure Pool & Tuning Fix";
752
- let fixCode = "const pool = new Pool({ max: 50, idleTimeoutMillis: 30000 });";
753
- let explanation = "High concurrency is causing socket/connection starvation. Increase connection limits or enable pooling keep-alives.";
754
- if (c.category === "Authentication_Error") {
755
- fixTitle = "Auth Token / JWT Strategy";
756
- fixCode = "// Implement token caching and silent rotation\\napp.use(authMiddleware({ cacheTtl: 300, allowGracePeriod: 60 }));";
757
- explanation = "Frequent token verification failures under load. Extend signature verification cache or decouple auth validation check.";
758
- } else if (c.category === "Product_Error") {
759
- fixTitle = "Application Null-Safety / Guard Fix";
760
- fixCode = "const configId = payload?.config_id ?? defaultConfigurationId;\\nif (!configId) throw new ValidationError('Missing config_id');";
761
- explanation = "Runtime reference error for undefined payload field under race condition spikes. Add optional chaining and input contracts.";
762
- }
763
- const snippetId = `snippet-${idx}`;
764
507
  return `
765
- <details style="background: #090d16; border: 1px solid #1e293b; border-radius: 6px; overflow: hidden;">
766
- <summary style="padding: 1rem; cursor: pointer; display: flex; align-items: center; justify-content: space-between; user-select: none;">
767
- <div style="display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;">
768
- <span style="font-size: 1.1rem; font-weight: bold; color: #f8fafc; background: #131c2e; padding: 0.2rem 0.6rem; border-radius: 4px;">${c.count}x</span>
769
- <span style="color: ${catColor}; font-weight: 600; font-size: 0.95rem;">${c.category}</span>
770
- <span style="background: ${sevBg}; color: #ffffff; padding: 0.15rem 0.4rem; border-radius: 4px; font-size: 0.65rem; font-weight: bold; letter-spacing: 0.05em;">${c.severity}</span>
771
- <span style="background: rgba(16, 185, 129, 0.15); color: ${confColor}; border: 1px solid ${confColor}40; padding: 0.15rem 0.5rem; border-radius: 4px; font-size: 0.7rem; font-weight: bold;">AI Confidence: ${confidenceScore}%</span>
772
- </div>
773
- <div style="color: #64748b; font-size: 0.85rem; font-family: monospace;">Expand Playbook \u25BC</div>
774
- </summary>
775
- <div style="padding: 1.25rem; border-top: 1px solid #1e293b; background: #0f172a;">
776
- <div style="margin-bottom: 1rem;">
777
- <div style="color: #64748b; font-size: 0.75rem; text-transform: uppercase; font-weight: bold; margin-bottom: 0.25rem;">Detected Log Fingerprint</div>
778
- <code style="color: #cbd5e1; font-family: monospace; font-size: 0.85rem; word-break: break-all;">${cleanedTemplate}</code>
779
- </div>
780
- <div style="margin-bottom: 1.25rem;">
781
- <div style="color: #38bdf8; font-size: 0.9rem; font-weight: bold; margin-bottom: 0.25rem;">\u{1F4A1} Playbook Strategy: ${fixTitle}</div>
782
- <div style="color: #94a3b8; font-size: 0.85rem; line-height: 1.4; margin-bottom: 0.75rem;">${explanation}</div>
783
- <div style="position: relative; background: #0b111e; border: 1px solid #1e293b; border-radius: 4px; padding: 0.75rem;">
784
- <button onclick="copyToClipboard('${snippetId}')" style="position: absolute; top: 0.5rem; right: 0.5rem; background: #1e293b; color: #38bdf8; border: none; padding: 0.2rem 0.5rem; border-radius: 3px; font-size: 0.7rem; cursor: pointer;">Copy Fix</button>
785
- <pre id="${snippetId}" style="margin: 0; color: #4ade80; font-family: monospace; font-size: 0.85rem; white-space: pre-wrap;">${fixCode}</pre>
508
+ <tr style="border-bottom: 1px solid #1e293b; vertical-align: top;">
509
+ <td style="padding: 1.25rem 0.75rem; font-size: 1.2rem; font-weight: bold; color: #f8fafc;">${c.count}</td>
510
+ <td style="padding: 1.25rem 0.75rem; color: ${catColor}; font-weight: 600; font-size: 0.95rem;">${c.category}</td>
511
+ <td style="padding: 1.25rem 0.75rem;">
512
+ <span style="background: ${sevBg}; color: #ffffff; padding: 0.2rem 0.5rem; border-radius: 4px; font-size: 0.7rem; font-weight: bold; letter-spacing: 0.05em;">${c.severity}</span>
513
+ </td>
514
+ <td style="padding: 1.25rem 0.75rem;">
515
+ <div style="background: #090d16; border-radius: 4px; padding: 0.6rem 0.8rem; border-left: 3px solid #1e293b; margin-bottom: 0.5rem;">
516
+ <code style="color: #cbd5e1; font-family: monospace; font-size: 0.9rem; white-space: pre-wrap; word-break: break-all;">${cleanedTemplate}</code>
786
517
  </div>
787
- </div>
788
- </div>
789
- </details>`;
518
+ <div style="color: #64748b; font-size: 0.8rem; font-family: monospace; padding-left: 0.2rem; line-height: 1.4;">
519
+ <span style="color: #475569;">Real Example Trace:</span> ${rawTemplate}
520
+ </div>
521
+ </td>
522
+ </tr>`;
790
523
  }).join("")}
791
- </div>
524
+ </tbody>
525
+ </table>
792
526
  </div>`;
793
527
  }
794
528
  const liveAdjusterHtml = `
@@ -846,9 +580,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
846
580
  h1 { color: #38bdf8; margin: 0; font-size: 2rem; }
847
581
  .meta { color: #94a3b8; font-size: 0.9rem; margin-top: 0.5rem; }
848
582
 
849
- .section-title { font-size: 1.2rem; color: #cbd5e1; font-weight: bold; margin: 2rem 0 1rem 0; padding-bottom: 0.4rem; border-bottom: 1px solid #1e293b; }
850
583
  .cards-wrapper { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; margin-bottom: 1.5rem; }
851
- @media (max-width: 1024px) { .cards-wrapper { grid-template-columns: repeat(2, 1fr); } }
852
584
  .metric-card { background: #131c2e; border: 1px solid #1e293b; border-radius: 6px; padding: 1.25rem; position: relative; }
853
585
  .card-title { font-size: 0.75rem; font-weight: 700; color: #64748b; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.5rem; }
854
586
  .card-value { font-size: 1.6rem; font-weight: 700; color: #ffffff; }
@@ -856,11 +588,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
856
588
  .card-value.green { color: #10b981; }
857
589
  .card-value.orange { color: #f97316; }
858
590
  .card-value.purple { color: #a855f7; }
859
- .card-value.cyan { color: #38bdf8; }
860
- .card-value.red { color: #f87171; }
861
- .card-subtext { font-size: 0.8rem; font-weight: 500; margin-left: 0.4rem; display: inline-block; }
862
- .card-subtext.green { color: #10b981; }
863
- .card-subtext.orange { color: #f97316; }
591
+ .card-subtext { font-size: 0.8rem; color: #10b981; font-weight: 500; margin-left: 0.4rem; display: inline-block; }
864
592
 
865
593
  .top-layout-grid { display: grid; grid-template-columns: 1fr 400px; gap: 1.5rem; margin-bottom: 2rem; }
866
594
  .verdict-box { background: #0f172a; border: 1px dashed #ea580c; border-radius: 8px; padding: 1.25rem; }
@@ -901,24 +629,15 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
901
629
  <div class="meta">Target Script: <code>${config.targetScript}</code></div>
902
630
  </div>
903
631
 
904
- <div class="section-title">\u{1F4CA} Core Server Metrics & Apdex Verification</div>
905
632
  <div class="cards-wrapper">
906
633
  <div class="metric-card">
907
- <div class="card-title">Apdex Score (T: ${config.apdexT}ms)</div>
908
- <div class="card-value green">${cards.apdex.toFixed(2)}<span class="card-subtext green">(Verified)</span></div>
634
+ <div class="card-title">Apdex Score (T: 50ms)</div>
635
+ <div class="card-value green">${cards.apdex.toFixed(2)}<span class="card-subtext">(Good)</span></div>
909
636
  </div>
910
637
  <div class="metric-card">
911
638
  <div class="card-title">Throughput</div>
912
639
  <div class="card-value orange">${cards.throughput.toFixed(1)}<span class="unit">/s</span></div>
913
640
  </div>
914
- <div class="metric-card">
915
- <div class="card-title">Minimum Response Time</div>
916
- <div class="card-value green">${cards.minLatency.toFixed(1)}<span class="unit">ms</span></div>
917
- </div>
918
- <div class="metric-card">
919
- <div class="card-title">Maximum Response Time</div>
920
- <div class="card-value red">${cards.maxLatency.toFixed(1)}<span class="unit">ms</span></div>
921
- </div>
922
641
  <div class="metric-card">
923
642
  <div class="card-title">Network Bandwidth</div>
924
643
  <div class="card-value purple">${cards.bandwidth.toFixed(2)}<span class="unit">MB/s</span></div>
@@ -928,32 +647,23 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
928
647
  <div class="card-value">${cards.ttfb.toFixed(1)}<span class="unit">ms</span></div>
929
648
  </div>
930
649
  <div class="metric-card">
931
- <div class="card-title">VU Ramp-up Velocity</div>
932
- <div class="card-value cyan">${cards.vuRampUpVelocity.toFixed(1)}<span class="unit">VUs/s</span></div>
933
- </div>
934
- <div class="metric-card">
935
- <div class="card-title">Stability (Std Dev)</div>
936
- <div class="card-value">&plusmn;${cards.stdDev.toFixed(1)}<span class="unit">ms</span></div>
650
+ <div class="card-title">DNS Lookup</div>
651
+ <div class="card-value">${cards.dns.toFixed(2)}<span class="unit">ms</span></div>
937
652
  </div>
938
- </div>
939
-
940
- <div class="section-title">\u{1F310} Simulated Browser Core Web Vitals Equivalent</div>
941
- <div class="cards-wrapper">
942
653
  <div class="metric-card">
943
- <div class="card-title">Largest Contentful Paint (LCP)</div>
944
- <div class="card-value">${cards.lcp.toFixed(0)}<span class="unit">ms</span><span class="card-subtext ${lcpStat.cls}">(${lcpStat.label})</span></div>
654
+ <div class="card-title">TCP Connect</div>
655
+ <div class="card-value">${cards.tcp.toFixed(2)}<span class="unit">ms</span></div>
945
656
  </div>
946
657
  <div class="metric-card">
947
- <div class="card-title">First Input Delay (FID)</div>
948
- <div class="card-value">${cards.fid.toFixed(1)}<span class="unit">ms</span><span class="card-subtext ${fidStat.cls}">(${fidStat.label})</span></div>
658
+ <div class="card-title">TLS Handshake</div>
659
+ <div class="card-value">${cards.tls.toFixed(2)}<span class="unit">ms</span></div>
949
660
  </div>
950
661
  <div class="metric-card">
951
- <div class="card-title">Cumulative Layout Shift (CLS)</div>
952
- <div class="card-value">${cards.cls.toFixed(3)}<span class="card-subtext ${clsStat.cls}">(${clsStat.label})</span></div>
662
+ <div class="card-title">Stability (Std Dev)</div>
663
+ <div class="card-value">&plusmn;${cards.stdDev.toFixed(1)}<span class="unit">ms</span></div>
953
664
  </div>
954
665
  </div>
955
666
 
956
- <div class="section-title">\u26A1 Diagnostic Breakdowns</div>
957
667
  <div class="top-layout-grid">
958
668
  <div class="verdict-box">
959
669
  <div class="verdict-title">\u{1F916} AI Stress-Testing Agent Verdict</div>
@@ -1025,8 +735,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
1025
735
  <th>Throughput</th>
1026
736
  <th>Avg Latency</th>
1027
737
  <th>P95 Latency</th>
1028
- <th>Min / Max Floor</th>
1029
- <th>RESET Rate</th>
738
+ <th>Error Rate</th>
1030
739
  <th>Status</th>
1031
740
  </tr>
1032
741
  </thead>
@@ -1038,35 +747,24 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
1038
747
  <td>${h.throughput.toFixed(0)} req/sec</td>
1039
748
  <td>${h.avgLatency.toFixed(1)} ms</td>
1040
749
  <td>${h.p95Latency.toFixed(1)} ms</td>
1041
- <td><small>${h.minLatency.toFixed(1)}ms / ${h.maxLatency.toFixed(1)}ms</small></td>
1042
- <td>${h.econnresetRate.toFixed(2)}%</td>
750
+ <td>${(h.errorRate * 100).toFixed(2)}%</td>
1043
751
  <td><span class="badge ${isPassed ? "badge-success" : "badge-fail"}">${isPassed ? "SLO PASS" : "SLO BREACH"}</span></td>
1044
752
  </tr>`;
1045
753
  }).join("")}
1046
754
  </tbody>
1047
755
  </table>
1048
756
  </div>
1049
- ${seoPredictorPanelHtml}
1050
- ${connectionDiagnosticsPanelHtml}
1051
757
  ${liveAdjusterHtml}
1052
758
  ${rightSizingHtml}
1053
759
  ${logClustersHtml}
1054
760
  </div>
1055
761
 
1056
762
  <script>
1057
- function copyToClipboard(containerId) {
1058
- const text = document.getElementById(containerId).innerText;
1059
- navigator.clipboard.writeText(text).then(() => {
1060
- alert('Copied remediation snippet to clipboard!');
1061
- }).catch(err => {
1062
- console.error('Failed to copy text: ', err);
1063
- });
1064
- }
1065
-
1066
763
  const labels = ${JSON.stringify(labels)};
1067
764
  const baseThroughput = ${cards.throughput};
1068
765
  const baseLatency = ${cards.ttfb};
1069
766
 
767
+ // Live Concurrency Adjuster interactive client logic
1070
768
  const slider = document.getElementById('concurrencySlider');
1071
769
  const sliderVal = document.getElementById('sliderValue');
1072
770
  const simThroughput = document.getElementById('simThroughput');
@@ -1151,8 +849,8 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
1151
849
  },
1152
850
  scales: {
1153
851
  x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } },
1154
- yThreads: { position: 'left', grid: { color: '#1e293b' }, ticks: { color: '#38bdf8' }, title: { display: true, text: 'VUs / Threads', color: '#38bdf8' } },
1155
- yTtf: { position: 'right', grid: { drawOnChartArea: false }, ticks: { color: '#f43f5e' }, title: { display: true, text: 'TTF Index / Latency ms', color: '#f43f5e' } }
852
+ y_threads: { position: 'left', grid: { color: '#1e293b' }, ticks: { color: '#38bdf8' }, title: { display: true, text: 'VUs / Threads', color: '#38bdf8' } },
853
+ y_ttf: { position: 'right', grid: { drawOnChartArea: false }, ticks: { color: '#f43f5e' }, title: { display: true, text: 'TTF Index / Latency ms', color: '#f43f5e' } }
1156
854
  }
1157
855
  }
1158
856
  });
@@ -1212,6 +910,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
1212
910
  function printUsage() {
1213
911
  console.log(`Blaze Core Performance Test CLI Engine
1214
912
  Options:
1215
- --threads <count> | --duration <seconds> | --agentic | --cluster-logs | --correlate | --apdex-t <ms> | --seo`);
913
+ --threads <count> | --duration <seconds> | --agentic | --cluster-logs | --correlate`);
1216
914
  }
1217
915
  main().catch(console.error);
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blaze-performance-tester",
3
- "version": "3.1.16",
3
+ "version": "3.1.18",
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",