blaze-performance-tester 3.1.1 → 3.1.3

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
@@ -87,38 +87,6 @@ var mathUtils = {
87
87
  return sorted[Math.max(0, index)];
88
88
  }
89
89
  };
90
- async function executeTestPipeline(config) {
91
- if (!blazeCore || typeof blazeCore.runCorrelatedTransaction !== "function") {
92
- console.error("\u274C Error: runCorrelatedTransaction function is missing from the compiled binary bindings.");
93
- return;
94
- }
95
- const transactionalScenario = [
96
- {
97
- name: "Fetch Target Anti-Forgery Nonce",
98
- url: "https://httpbin.org/json",
99
- method: "GET",
100
- body: null
101
- },
102
- {
103
- name: "Perform Contextual Post Action",
104
- url: "https://httpbin.org/post?verification=${csrf_token}",
105
- method: "POST",
106
- body: JSON.stringify({
107
- data: "performance_payload",
108
- security_token: "${csrf_token}"
109
- })
110
- }
111
- ];
112
- console.log(`Launching automated script transaction analysis for: ${config.targetScript}`);
113
- const sanitizedSteps = transactionalScenario.map((step) => ({
114
- ...step,
115
- body: typeof step.body === "string" ? step.body : step.body ? JSON.stringify(step.body) : ""
116
- }));
117
- const success = await blazeCore.runCorrelatedTransaction(sanitizedSteps);
118
- if (success) {
119
- console.log("Pipeline run complete. Parameter tracking successfully mitigated breaking changes.");
120
- }
121
- }
122
90
  async function main() {
123
91
  const args = process.argv.slice(2);
124
92
  if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
@@ -126,9 +94,6 @@ async function main() {
126
94
  return;
127
95
  }
128
96
  const config = parseArguments(args);
129
- if (config.isCorrelate) {
130
- await executeTestPipeline(config);
131
- }
132
97
  if (config.isAgentic) {
133
98
  await runIntelligentAgenticStressTest(config);
134
99
  } else {
@@ -138,8 +103,7 @@ async function main() {
138
103
  function parseArguments(args) {
139
104
  const targetScript = path.resolve(args[0] || ".");
140
105
  const isAgentic = args.includes("--agentic");
141
- const isCorrelate = args.includes("--correlate");
142
- const clusterLogs = !args.includes("--no-cluster-logs");
106
+ const clusterLogs = args.includes("--cluster-logs");
143
107
  let threads = 10;
144
108
  let durationSec = 10;
145
109
  let targetApdex = 0.85;
@@ -147,25 +111,18 @@ function parseArguments(args) {
147
111
  let maxThreads = 300;
148
112
  let maxAllowedLatencyMs = 800;
149
113
  let outputHtml = "blaze-dashboard.html";
150
- let apdexT = 50;
151
114
  const threadsIdx = args.indexOf("--threads");
152
115
  if (threadsIdx !== -1) threads = parseInt(args[threadsIdx + 1], 10);
153
116
  const durationIdx = args.indexOf("--duration");
154
117
  if (durationIdx !== -1) durationSec = parseInt(args[durationIdx + 1], 10);
155
118
  const apdexIdx = args.indexOf("--target-apdex");
156
119
  if (apdexIdx !== -1) targetApdex = parseFloat(args[apdexIdx + 1]);
157
- const apdexTIdx = args.indexOf("--apdex-t");
158
- if (apdexTIdx !== -1) apdexT = parseInt(args[apdexTIdx + 1], 10);
159
120
  const errorIdx = args.indexOf("--target-error");
160
121
  if (errorIdx !== -1) targetErrorRate = parseFloat(args[errorIdx + 1]);
161
122
  const maxThreadsIdx = args.indexOf("--max-threads");
162
123
  if (maxThreadsIdx !== -1) maxThreads = parseInt(args[maxThreadsIdx + 1], 10);
163
124
  const outputIdx = args.indexOf("--output");
164
125
  if (outputIdx !== -1) outputHtml = args[outputIdx + 1];
165
- const positionalNums = args.slice(1).filter((arg) => !arg.startsWith("-")).map(Number).filter((n) => !isNaN(n));
166
- if (positionalNums.length > 0 && threadsIdx === -1 && !isAgentic) threads = positionalNums[0];
167
- if (positionalNums.length > 1 && durationIdx === -1 && !isAgentic) durationSec = positionalNums[1];
168
- if (positionalNums.length > 2 && errorIdx === -1 && !isAgentic) targetErrorRate = positionalNums[2] / 100;
169
126
  if (isAgentic) {
170
127
  const agenticIdx = args.indexOf("--agentic");
171
128
  const trailingArgs = args.slice(agenticIdx + 1).filter((arg) => !arg.startsWith("--"));
@@ -183,7 +140,6 @@ function parseArguments(args) {
183
140
  return {
184
141
  targetScript,
185
142
  isAgentic,
186
- isCorrelate,
187
143
  clusterLogs,
188
144
  threads,
189
145
  durationSec,
@@ -192,8 +148,7 @@ function parseArguments(args) {
192
148
  maxThreads,
193
149
  stepSize: 15,
194
150
  maxAllowedLatencyMs,
195
- outputHtml,
196
- apdexT
151
+ outputHtml
197
152
  };
198
153
  }
199
154
  async function runBlazeCoreEngine(config) {
@@ -209,15 +164,9 @@ async function runBlazeCoreEngine(config) {
209
164
  if (!rawRequests || rawRequests.length === 0) {
210
165
  rawRequests = generateSimulationData(config.threads, config.durationSec, config.maxAllowedLatencyMs);
211
166
  }
212
- const warmUpCutoff = Math.floor(rawRequests.length * 0.1);
213
- const steadyStateRequests = rawRequests.slice(warmUpCutoff);
214
- const metrics = processMetricsTelemetry(
215
- steadyStateRequests.length > 0 ? steadyStateRequests : rawRequests,
216
- config.durationSec,
217
- config.apdexT
218
- );
167
+ const metrics = processMetricsTelemetry(rawRequests, config.durationSec);
219
168
  const rawErrors = rawRequests.filter((r) => r.errorMessage).map((r) => r.errorMessage);
220
- resolve2({ metrics, rawErrors, rawRequests: steadyStateRequests.length > 0 ? steadyStateRequests : rawRequests });
169
+ resolve2({ metrics, rawErrors });
221
170
  }, 1e3);
222
171
  });
223
172
  }
@@ -225,22 +174,18 @@ async function runIntelligentAgenticStressTest(config) {
225
174
  console.log("\u{1F680} Initializing Intelligent Autonomous Load Agent...");
226
175
  const history = [];
227
176
  let aggregateErrorLogs = [];
228
- let lastRawRequests = [];
229
- let total1xx = 0, total2xx = 0, total3xx = 0, total4xx = 0, total5xx = 0;
177
+ let total2xx = 0, total3xx = 0, total4xx = 0, total5xx = 0;
230
178
  let globalMetricsAccumulator = { dns: [], tcp: [], tls: [], ttfb: [], latencies: [], bandwidths: [] };
231
179
  let currentThreads = Math.min(config.maxThreads, Math.max(5, Math.floor(config.maxThreads * 0.2)));
232
180
  let baseStep = config.stepSize;
233
181
  let isStable = true;
234
182
  let verdictReason = "The system successfully scaled and remained stable within parameters up to maximum configured capacity allocations.";
235
- let saturationKneePoint = null;
236
183
  while (currentThreads <= config.maxThreads && isStable) {
237
184
  console.log(`\u{1F916} [Agent Decision]: Testing execution tier at ${currentThreads} concurrent threads...`);
238
- const { metrics, rawErrors, rawRequests } = await runBlazeCoreEngine({ ...config, threads: currentThreads });
185
+ const { metrics, rawErrors } = await runBlazeCoreEngine({ ...config, threads: currentThreads });
239
186
  if (config.clusterLogs) {
240
187
  aggregateErrorLogs = aggregateErrorLogs.concat(rawErrors);
241
188
  }
242
- lastRawRequests = rawRequests;
243
- total1xx += metrics.c1xx;
244
189
  total2xx += metrics.c2xx;
245
190
  total3xx += metrics.c3xx;
246
191
  total4xx += metrics.c4xx;
@@ -268,8 +213,7 @@ async function runIntelligentAgenticStressTest(config) {
268
213
  const previous = history[history.length - 2];
269
214
  const dLatency_dThreads = (current.avgLatency - previous.avgLatency) / (current.threads - previous.threads);
270
215
  if (current.throughput <= previous.throughput * 1.02 && dLatency_dThreads > 4.5) {
271
- saturationKneePoint = currentThreads;
272
- verdictReason = `Saturation knee-point identified at ${currentThreads} VUs where throughput flattened while latency spiked rapidly.`;
216
+ verdictReason = "The primary breakdown point was identified due to infrastructure saturation where throughput flattened while latency spiked rapidly.";
273
217
  isStable = false;
274
218
  break;
275
219
  }
@@ -294,15 +238,10 @@ async function runIntelligentAgenticStressTest(config) {
294
238
  currentThreads += nextStep;
295
239
  }
296
240
  let semanticReport = null;
297
- if (config.clusterLogs) {
298
- if (aggregateErrorLogs.length === 0) {
299
- aggregateErrorLogs = getFallbackClusterLogs();
300
- }
241
+ if (config.clusterLogs && aggregateErrorLogs.length > 0) {
301
242
  const analyzer = new LogAnalyzer();
302
243
  semanticReport = analyzer.process(aggregateErrorLogs);
303
244
  }
304
- const totalTestSeconds = history.length * config.durationSec;
305
- const vuRampUpVelocity = history.length > 1 && totalTestSeconds > 0 ? (history[history.length - 1].threads - history[0].threads) / totalTestSeconds : config.threads / config.durationSec;
306
245
  const finalSummaryCards = {
307
246
  apdex: history[history.length - 1]?.apdex || 0,
308
247
  throughput: history[history.length - 1]?.throughput || 0,
@@ -311,16 +250,14 @@ async function runIntelligentAgenticStressTest(config) {
311
250
  dns: mathUtils.mean(globalMetricsAccumulator.dns),
312
251
  tcp: mathUtils.mean(globalMetricsAccumulator.tcp),
313
252
  tls: mathUtils.mean(globalMetricsAccumulator.tls),
314
- stdDev: mathUtils.stdDev(globalMetricsAccumulator.latencies, mathUtils.mean(globalMetricsAccumulator.latencies)),
315
- saturationKneePoint: saturationKneePoint || history[history.length - 1]?.threads || 50,
316
- vuRampUpVelocity
253
+ stdDev: mathUtils.stdDev(globalMetricsAccumulator.latencies, mathUtils.mean(globalMetricsAccumulator.latencies))
317
254
  };
318
255
  generateFinalAgentReport(history);
319
- exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx }, finalSummaryCards, lastRawRequests);
256
+ exportHtmlDashboard(history, config, verdictReason, semanticReport, { total2xx, total3xx, total4xx, total5xx }, finalSummaryCards);
320
257
  }
321
258
  async function runStandardStressTest(config) {
322
259
  console.log(`\u{1F3CB}\uFE0F Running Standard Stress Test [Threads: ${config.threads} | Duration: ${config.durationSec}s]`);
323
- const { metrics, rawErrors, rawRequests } = await runBlazeCoreEngine(config);
260
+ const { metrics, rawErrors } = await runBlazeCoreEngine(config);
324
261
  printMatrixDashboard(metrics, config.threads);
325
262
  const history = [{
326
263
  threads: config.threads,
@@ -333,11 +270,9 @@ async function runStandardStressTest(config) {
333
270
  failedRequests: metrics.c4xx + metrics.c5xx
334
271
  }];
335
272
  let semanticReport = null;
336
- if (config.clusterLogs) {
337
- let errsToProcess = rawErrors;
338
- if (errsToProcess.length === 0) errsToProcess = getFallbackClusterLogs();
273
+ if (config.clusterLogs && rawErrors.length > 0) {
339
274
  const analyzer = new LogAnalyzer();
340
- semanticReport = analyzer.process(errsToProcess);
275
+ semanticReport = analyzer.process(rawErrors);
341
276
  }
342
277
  const finalSummaryCards = {
343
278
  apdex: metrics.apdexScore,
@@ -347,31 +282,16 @@ async function runStandardStressTest(config) {
347
282
  dns: metrics.avgDnsMs,
348
283
  tcp: metrics.avgTcpMs,
349
284
  tls: metrics.avgTlsMs,
350
- stdDev: metrics.stdDevMs,
351
- saturationKneePoint: config.threads,
352
- vuRampUpVelocity: config.threads / config.durationSec
285
+ stdDev: metrics.stdDevMs
353
286
  };
354
287
  const isPassed = metrics.apdexScore >= config.targetApdex && metrics.errorRate <= config.targetErrorRate;
355
288
  const verdictReason = isPassed ? "Static single-wave baseline checks concluded successfully without triggering health boundaries." : "Static verification loop completed with values exceeding defined quality thresholds.";
356
289
  exportHtmlDashboard(history, config, verdictReason, semanticReport, {
357
- total1xx: metrics.c1xx,
358
290
  total2xx: metrics.c2xx,
359
291
  total3xx: metrics.c3xx,
360
292
  total4xx: metrics.c4xx,
361
293
  total5xx: metrics.c5xx
362
- }, finalSummaryCards, rawRequests);
363
- }
364
- function getFallbackClusterLogs() {
365
- const logs = [];
366
- const add = (msg, count) => {
367
- for (let i = 0; i < count; i++) logs.push(`[2026-07-08T12:08:18.000Z] ${msg}`);
368
- };
369
- add("JsonWebTokenError: jwt expired at JMT.verify (\\app\\node_modules\\jsonwebtoken\\index.js:5)", 37);
370
- add("Error: ECONNREFUSED 127.0.0.1:5432 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1)", 33);
371
- add("TypeError: Cannot read properties of undefined (reading 'config_id') at Object.execute (\\app\\dist\\handler.js:42:19)", 31);
372
- add("HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 15000ms", 25);
373
- add("HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 30000ms", 19);
374
- return logs;
294
+ }, finalSummaryCards);
375
295
  }
376
296
  function generateSimulationData(threads, durationSec, maxAllowedLatencyMs) {
377
297
  const data = [];
@@ -380,7 +300,11 @@ function generateSimulationData(threads, durationSec, maxAllowedLatencyMs) {
380
300
  const targetThresholdBreak = Math.max(5, Math.floor(maxAllowedLatencyMs * 0.05));
381
301
  const isBreached = threads > targetThresholdBreak;
382
302
  const stressFactor = isBreached ? Math.pow(threads / targetThresholdBreak, 1.5) : 1;
383
- const errorPool = getFallbackClusterLogs();
303
+ const errorPool = [
304
+ `Error: ECONNREFUSED 127.0.0.1:5432 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1605:16)`,
305
+ `HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 15000ms`,
306
+ `TypeError: Cannot read properties of undefined (reading 'config_id') at Object.execute (${path.sep}app${path.sep}dist${path.sep}handler.js:42:19)`
307
+ ];
384
308
  for (let i = 0; i < totalRequests; i++) {
385
309
  const isError = Math.random() < (isBreached ? 0.05 : 1e-3);
386
310
  const baseLatency = (25 + Math.random() * 35) * stressFactor;
@@ -388,7 +312,7 @@ function generateSimulationData(threads, durationSec, maxAllowedLatencyMs) {
388
312
  let statusCode = 200;
389
313
  if (isError) {
390
314
  statusCode = Math.random() > 0.3 ? 500 : 404;
391
- errorMessage = errorPool[Math.floor(Math.random() * errorPool.length)];
315
+ errorMessage = `[${(/* @__PURE__ */ new Date()).toISOString()}] ` + errorPool[Math.floor(Math.random() * errorPool.length)];
392
316
  }
393
317
  data.push({
394
318
  durationMs: isError ? baseLatency * 0.1 : baseLatency,
@@ -402,7 +326,7 @@ function generateSimulationData(threads, durationSec, maxAllowedLatencyMs) {
402
326
  }
403
327
  return data.sort((a, b) => a.timestampMs - b.timestampMs);
404
328
  }
405
- function processMetricsTelemetry(requests, durationSec, apdexT) {
329
+ function processMetricsTelemetry(requests, durationSec) {
406
330
  const totalRequests = requests.length;
407
331
  const durations = requests.map((r) => r.durationMs);
408
332
  const avgLatencyMs = mathUtils.mean(durations);
@@ -412,17 +336,16 @@ function processMetricsTelemetry(requests, durationSec, apdexT) {
412
336
  const avgTcpMs = mathUtils.mean(requests.map((r) => r.tcpTimeMs || 0));
413
337
  const avgTlsMs = mathUtils.mean(requests.map((r) => r.tlsTimeMs || 0));
414
338
  const avgTtfbMs = avgDnsMs + avgTcpMs + avgTlsMs + avgLatencyMs * 0.3;
415
- let c1xx = 0, c2xx = 0, c3xx = 0, c4xx = 0, c5xx = 0;
339
+ let c2xx = 0, c3xx = 0, c4xx = 0, c5xx = 0;
416
340
  requests.forEach((r) => {
417
- if (r.statusCode >= 100 && r.statusCode < 200) c1xx++;
418
- else if (r.statusCode >= 200 && r.statusCode < 300) c2xx++;
341
+ if (r.statusCode >= 200 && r.statusCode < 300) c2xx++;
419
342
  else if (r.statusCode >= 300 && r.statusCode < 400) c3xx++;
420
343
  else if (r.statusCode >= 400 && r.statusCode < 500) c4xx++;
421
344
  else if (r.statusCode >= 500) c5xx++;
422
345
  });
423
346
  const errorCount = c4xx + c5xx;
424
347
  const errorRate = totalRequests === 0 ? 0 : errorCount / totalRequests;
425
- const T = apdexT;
348
+ const T = 50;
426
349
  const satisfied = requests.filter((r) => r.durationMs <= T && r.statusCode < 400).length;
427
350
  const tolerating = requests.filter((r) => r.durationMs > T && r.durationMs <= T * 4 && r.statusCode < 400).length;
428
351
  const apdexScore = totalRequests === 0 ? 0 : (satisfied + tolerating / 2) / totalRequests;
@@ -435,7 +358,6 @@ function processMetricsTelemetry(requests, durationSec, apdexT) {
435
358
  p95LatencyMs,
436
359
  errorRate,
437
360
  apdexScore,
438
- c1xx,
439
361
  c2xx,
440
362
  c3xx,
441
363
  c4xx,
@@ -468,7 +390,7 @@ function generateFinalAgentReport(history) {
468
390
  console.log(`\u26A1 Peak Achieved Cluster Velocity : ${peakThroughput.toFixed(0)} req/sec`);
469
391
  console.log("=======================================================\n");
470
392
  }
471
- function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards, rawRequests = []) {
393
+ function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards) {
472
394
  const labels = history.map((h, i) => `${i + 1}s`);
473
395
  const successRequestsData = history.map((h) => h.successRequests);
474
396
  const failedWorkloadsData = history.map((h) => h.failedRequests);
@@ -477,37 +399,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
477
399
  const waveListItems = history.map((h) => {
478
400
  return `<div class="wave-item">Wave (${h.threads} VUs): P95 ${h.p95Latency.toFixed(1)}ms, Errors ${(h.errorRate * 100).toFixed(1)}%</div>`;
479
401
  }).join("");
480
- const estimatedMonthlyCost = (cards.throughput * 0.045 * 24 * 30).toFixed(2);
481
- const recommendedCpuCores = Math.max(2, Math.ceil(cards.saturationKneePoint / 75));
482
- const recommendedRamGb = recommendedCpuCores * 2;
483
- const firstTimestamp = rawRequests[0]?.timestampMs || Date.now();
484
- const scatterPoints = rawRequests.slice(0, 180).map((r) => ({
485
- x: Number(((r.timestampMs - firstTimestamp) / 1e3).toFixed(2)),
486
- y: Number(((r.dnsTimeMs || 0) + (r.tcpTimeMs || 0) + (r.tlsTimeMs || 0) + r.durationMs * 0.3).toFixed(1))
487
- }));
488
- const naturalLanguageAssistantHtml = `
489
- <div class="card" style="margin-top: 2rem; background: linear-gradient(135deg, #1e1b4b 0%, #111827 100%); border: 1px solid #4338ca;">
490
- <h3 style="color: #818cf8; display: flex; align-items: center; gap: 0.6rem; font-size: 1.25rem; margin-bottom: 0.5rem; border-bottom: none; padding-bottom: 0;">
491
- \u{1F52E} Ask the Test \u2014 Embedded Natural Language Assistant
492
- </h3>
493
- <div style="color: #94a3b8; font-size: 0.85rem; margin-bottom: 1.25rem;">
494
- Query metrics, log topologies, and SLA compliance profiles instantly. Try asking: <span style="color: #a5b4fc; font-family: monospace; cursor: pointer;" onclick="document.getElementById('assistantInput').value=this.innerText; runAssistantQuery();">"What was our peak throughput capacity?"</span> or <span style="color: #a5b4fc; font-family: monospace; cursor: pointer;" onclick="document.getElementById('assistantInput').value=this.innerText; runAssistantQuery();">"Summarize the server logs and confidence ratings"</span>.
495
- </div>
496
- <div style="display: flex; gap: 0.75rem; margin-bottom: 1rem;">
497
- <input type="text" id="assistantInput" placeholder="Ask a question regarding test telemetry... (e.g., 'Did we break the target Apdex limit?')"
498
- style="flex: 1; background: #030712; border: 1px solid #374151; border-radius: 6px; padding: 0.75rem 1rem; color: #f9fafb; font-size: 0.9rem; outline: none;"
499
- onkeydown="if(event.key === 'Enter') runAssistantQuery();" />
500
- <button onclick="runAssistantQuery()" style="background: #4f46e5; color: #ffffff; border: none; padding: 0 1.5rem; border-radius: 6px; font-weight: 600; font-size: 0.9rem; cursor: pointer; transition: background 0.2s;">
501
- Query Engine
502
- </button>
503
- </div>
504
- <div id="assistantResponseContainer" style="display: none; background: #030712; border: 1px solid #1f2937; border-radius: 6px; padding: 1.25rem;">
505
- <div style="font-size: 0.7rem; font-weight: bold; color: #6366f1; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.5rem; display: flex; align-items: center; gap: 0.3rem;">
506
- <span>\u{1F916} AI Assistant Response</span>
507
- </div>
508
- <div id="assistantResponseText" style="font-size: 0.9rem; line-height: 1.5; color: #e5e7eb; white-space: pre-wrap;"></div>
509
- </div>
510
- </div>`;
511
402
  let logClustersHtml = "";
512
403
  if (semanticReport) {
513
404
  const totalLogCount = semanticReport.clusters.reduce((sum, c) => sum + c.count, 0);
@@ -515,14 +406,23 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
515
406
  logClustersHtml = `
516
407
  <div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
517
408
  <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;">
518
- \u{1F9E0} Interactive Remediation Playbooks & Log Classification
409
+ \u{1F9E0} Semantic Log Clustering & Error Classification
519
410
  </h3>
520
411
  <div style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
521
- Captured <strong style="color: #f8fafc;">${totalLogCount}</strong> raw failures grouped into <strong style="color: #f8fafc;">${uniquePatternsCount}</strong> unique structural patterns. Expand a playbook to inspect automated remediation strategies and diagnostics confidence ratings.
412
+ Captured <strong style="color: #f8fafc;">${totalLogCount}</strong> raw failures grouped into <strong style="color: #f8fafc;">${uniquePatternsCount}</strong> unique structural patterns.
522
413
  </div>
523
414
 
524
- <div style="display: flex; flex-direction: column; gap: 1rem;">
525
- ${semanticReport.clusters.map((c, idx) => {
415
+ <table style="width: 100%; border-collapse: collapse;">
416
+ <thead>
417
+ <tr style="border-bottom: 1px solid #1e293b;">
418
+ <th style="width: 8%; padding: 0.75rem; text-align: left; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Count</th>
419
+ <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>
420
+ <th style="width: 10%; padding: 0.75rem; text-align: left; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Severity</th>
421
+ <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>
422
+ </tr>
423
+ </thead>
424
+ <tbody>
425
+ ${semanticReport.clusters.map((c) => {
526
426
  let catColor = "#fb923c";
527
427
  if (c.category === "Authentication_Error") catColor = "#c084fc";
528
428
  if (c.category === "Product_Error") catColor = "#f87171";
@@ -530,99 +430,27 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
530
430
  let sevBg = c.severity === "CRITICAL" ? "#dc2626" : "#ea580c";
531
431
  const rawTemplate = c.template || "";
532
432
  const cleanedTemplate = rawTemplate.replace(/\[\d{4}-\d{2}-\d{2}.*?\]\s*/, "");
533
- const clarityBonus = c.severity === "CRITICAL" ? 15 : 8;
534
- const freqScore = Math.min(25, c.count * 0.5);
535
- const confidenceScore = Math.min(99, Math.round(65 + freqScore + clarityBonus));
536
- let confColor = "#10b981";
537
- if (confidenceScore < 80) confColor = "#f59e0b";
538
- if (confidenceScore < 70) confColor = "#ef4444";
539
- let fixTitle = "Infrastructure Pool & Tuning Fix";
540
- let fixCode = "const pool = new Pool({ max: 50, idleTimeoutMillis: 30000 });";
541
- let explanation = "High concurrency is causing socket/connection starvation. Increase connection limits or enable pooling keep-alives.";
542
- if (c.category === "Authentication_Error") {
543
- fixTitle = "Auth Token / JWT Strategy";
544
- fixCode = "// Implement token caching and silent rotation\napp.use(authMiddleware({ cacheTtl: 300, allowGracePeriod: 60 }));";
545
- explanation = "Frequent token verification failures under load. Extend signature verification cache or decouple auth validation check.";
546
- } else if (c.category === "Product_Error") {
547
- fixTitle = "Application Null-Safety / Guard Fix";
548
- fixCode = "const configId = payload?.config_id ?? defaultConfigurationId;\nif (!configId) throw new ValidationError('Missing config_id');";
549
- explanation = "Runtime reference error for undefined payload field under race condition spikes. Add optional chaining and input contracts.";
550
- }
551
- const snippetId = "snippet-" + idx;
552
433
  return `
553
- <details style="background: #090d16; border: 1px solid #1e293b; border-radius: 6px; overflow: hidden;">
554
- <summary style="padding: 1rem; cursor: pointer; display: flex; align-items: center; justify-content: space-between; user-select: none;">
555
- <div style="display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;">
556
- <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>
557
- <span style="color: ${catColor}; font-weight: 600; font-size: 0.95rem;">${c.category}</span>
558
- <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>
559
- <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>
560
- </div>
561
- <div style="color: #64748b; font-size: 0.85rem; font-family: monospace;">Expand Playbook \u25BC</div>
562
- </summary>
563
- <div style="padding: 1.25rem; border-top: 1px solid #1e293b; background: #0f172a;">
564
- <div style="margin-bottom: 1rem;">
565
- <div style="color: #64748b; font-size: 0.75rem; text-transform: uppercase; font-weight: bold; margin-bottom: 0.25rem;">Detected Log Fingerprint</div>
566
- <code style="color: #cbd5e1; font-family: monospace; font-size: 0.85rem; word-break: break-all;">${cleanedTemplate}</code>
567
- </div>
568
- <div style="margin-bottom: 1.25rem;">
569
- <div style="color: #38bdf8; font-size: 0.9rem; font-weight: bold; margin-bottom: 0.25rem;">\u{1F4A1} Playbook Strategy: ${fixTitle}</div>
570
- <div style="color: #94a3b8; font-size: 0.85rem; line-height: 1.4; margin-bottom: 0.75rem;">${explanation}</div>
571
- <div style="position: relative; background: #0b111e; border: 1px solid #1e293b; border-radius: 4px; padding: 0.75rem;">
572
- <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>
573
- <pre id="${snippetId}" style="margin: 0; color: #4ade80; font-family: monospace; font-size: 0.85rem; white-space: pre-wrap;">${fixCode}</pre>
434
+ <tr style="border-bottom: 1px solid #1e293b; vertical-align: top;">
435
+ <td style="padding: 1.25rem 0.75rem; font-size: 1.2rem; font-weight: bold; color: #f8fafc;">${c.count}</td>
436
+ <td style="padding: 1.25rem 0.75rem; color: ${catColor}; font-weight: 600; font-size: 0.95rem;">${c.category}</td>
437
+ <td style="padding: 1.25rem 0.75rem;">
438
+ <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>
439
+ </td>
440
+ <td style="padding: 1.25rem 0.75rem;">
441
+ <div style="background: #090d16; border-radius: 4px; padding: 0.6rem 0.8rem; border-left: 3px solid #1e293b; margin-bottom: 0.5rem;">
442
+ <code style="color: #cbd5e1; font-family: monospace; font-size: 0.9rem; white-space: pre-wrap; word-break: break-all;">${cleanedTemplate}</code>
443
+ </div>
444
+ <div style="color: #64748b; font-size: 0.8rem; font-family: monospace; padding-left: 0.2rem; line-height: 1.4;">
445
+ <span style="color: #475569;">Real Example Trace:</span> ${rawTemplate}
574
446
  </div>
575
- </div>
576
- </div>
577
- </details>`;
447
+ </td>
448
+ </tr>`;
578
449
  }).join("")}
579
- </div>
580
- `;
450
+ </tbody>
451
+ </table>
452
+ </div>`;
581
453
  }
582
- const liveAdjusterHtml = `
583
- <div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
584
- <h3 style="color: #38bdf8; display: flex; align-items: center; gap: 0.5rem; font-size: 1.2rem; margin-bottom: 1rem;">
585
- \u{1F39B}\uFE0F Live Concurrency Adjuster (Mid-Test Simulation)
586
- </h3>
587
- <div style="display: flex; gap: 2rem; align-items: center; flex-wrap: wrap;">
588
- <div style="flex: 1; min-width: 280px;">
589
- <label for="concurrencySlider" style="display: block; font-size: 0.85rem; color: #94a3b8; margin-bottom: 0.5rem;">
590
- Scale Virtual Users (VUs): <strong id="sliderValue" style="color: #38bdf8;">${config.threads}</strong> threads
591
- </label>
592
- <input type="range" id="concurrencySlider" min="5" max="${config.maxThreads || 300}" step="5" value="${config.threads}" style="width: 100%; accent-color: #38bdf8; cursor: pointer;">
593
- </div>
594
- <div style="display: flex; gap: 1.5rem; text-align: center;">
595
- <div style="background: #090d16; padding: 0.75rem 1rem; border-radius: 6px; border: 1px solid #1e293b;">
596
- <div style="font-size: 0.7rem; color: #64748b; text-transform: uppercase;">Sim. Throughput</div>
597
- <div id="simThroughput" style="font-size: 1.2rem; font-weight: bold; color: #f97316;">${cards.throughput.toFixed(0)}/s</div>
598
- </div>
599
- <div style="background: #090d16; padding: 0.75rem 1rem; border-radius: 6px; border: 1px solid #1e293b;">
600
- <div style="font-size: 0.7rem; color: #64748b; text-transform: uppercase;">Sim. Latency</div>
601
- <div id="simLatency" style="font-size: 1.2rem; font-weight: bold; color: #10b981;">${cards.ttfb.toFixed(1)}ms</div>
602
- </div>
603
- </div>
604
- </div>
605
- </div>`;
606
- const rightSizingHtml = `
607
- <div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
608
- <h3 style="color: #38bdf8; display: flex; align-items: center; gap: 0.5rem; font-size: 1.2rem;">
609
- \u2601\uFE0F Infrastructure Right-Sizing & Cloud Cost Projections
610
- </h3>
611
- <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin-top: 1rem;">
612
- <div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
613
- <div class="card-title">Est. Monthly Cloud Cost</div>
614
- <div class="card-value green">$${estimatedMonthlyCost} <span class="unit">/mo</span></div>
615
- </div>
616
- <div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
617
- <div class="card-title">Recommended CPU Right-Sizing</div>
618
- <div class="card-value purple">${recommendedCpuCores} <span class="unit">vCores</span></div>
619
- </div>
620
- <div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
621
- <div class="card-title">Recommended Memory Allocation</div>
622
- <div class="card-value orange">${recommendedRamGb} <span class="unit">GB RAM</span></div>
623
- </div>
624
- </div>
625
- </div>`;
626
454
  const htmlContent = `<!DOCTYPE html><html lang="en"><head>
627
455
  <meta charset="UTF-8">
628
456
  <title>Blaze Core Performance Report</title>
@@ -634,7 +462,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
634
462
  h1 { color: #38bdf8; margin: 0; font-size: 2rem; }
635
463
  .meta { color: #94a3b8; font-size: 0.9rem; margin-top: 0.5rem; }
636
464
 
637
- .cards-wrapper { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin-bottom: 1.5rem; }
465
+ .cards-wrapper { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; margin-bottom: 1.5rem; }
638
466
  .metric-card { background: #131c2e; border: 1px solid #1e293b; border-radius: 6px; padding: 1.25rem; position: relative; }
639
467
  .card-title { font-size: 0.75rem; font-weight: 700; color: #64748b; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.5rem; }
640
468
  .card-value { font-size: 1.6rem; font-weight: 700; color: #ffffff; }
@@ -642,7 +470,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
642
470
  .card-value.green { color: #10b981; }
643
471
  .card-value.orange { color: #f97316; }
644
472
  .card-value.purple { color: #a855f7; }
645
- .card-value.cyan { color: #38bdf8; }
646
473
  .card-subtext { font-size: 0.8rem; color: #10b981; font-weight: 500; margin-left: 0.4rem; display: inline-block; }
647
474
 
648
475
  .top-layout-grid { display: grid; grid-template-columns: 1fr 400px; gap: 1.5rem; margin-bottom: 2rem; }
@@ -654,18 +481,18 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
654
481
 
655
482
  .matrix-box { background: #131c2e; border: 1px solid #1e293b; border-radius: 8px; padding: 1.25rem; }
656
483
  .matrix-title { font-size: 1.1rem; font-weight: bold; color: #ffffff; margin-bottom: 1rem; }
657
- .matrix-box .matrix-row { display: flex; justify-content: space-between; align-items: center; padding: 0.6rem 0; border-bottom: 1px solid #1e293b; }
658
- .matrix-box .matrix-row:last-child { border-bottom: none; }
484
+ .matrix-row { display: flex; justify-content: space-between; align-items: center; padding: 0.6rem 0; border-bottom: 1px solid #1e293b; }
485
+ .matrix-row:last-child { border-bottom: none; }
659
486
  .matrix-label { font-size: 0.9rem; color: #f8fafc; display: flex; align-items: center; gap: 0.5rem; }
660
487
  .matrix-badge { font-size: 0.7rem; font-weight: bold; padding: 0.1rem 0.3rem; border-radius: 4px; }
661
- .badge-1xx { background: rgba(56, 189, 248, 0.2); color: #38bdf8; }
662
488
  .badge-2xx { background: rgba(22, 163, 74, 0.2); color: #4ade80; }
663
489
  .badge-3xx { background: rgba(148, 163, 184, 0.2); color: #cbd5e1; }
664
490
  .badge-4xx { background: rgba(234, 179, 8, 0.2); color: #fde047; }
665
491
  .badge-5xx { background: rgba(220, 38, 38, 0.2); color: #f87171; }
666
492
  .matrix-value { font-size: 1rem; font-weight: bold; color: #ffffff; }
667
493
 
668
- .charts-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.5rem; margin-bottom: 2rem; }
494
+ /* \u{1F4CA} TWO-COLUMN CHART GRID */
495
+ .charts-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; margin-bottom: 2rem; }
669
496
  .graph-card { background: #131c2e; border: 1px solid #1e293b; border-radius: 8px; padding: 1.5rem; }
670
497
  .graph-title { font-size: 1.1rem; font-weight: 700; color: #ffffff; margin-bottom: 1.2rem; display: flex; align-items: center; gap: 0.5rem; }
671
498
 
@@ -686,7 +513,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
686
513
 
687
514
  <div class="cards-wrapper">
688
515
  <div class="metric-card">
689
- <div class="card-title">Apdex Score (T: ${config.apdexT}ms)</div>
516
+ <div class="card-title">Apdex Score (T: 50ms)</div>
690
517
  <div class="card-value green">${cards.apdex.toFixed(2)}<span class="card-subtext">(Good)</span></div>
691
518
  </div>
692
519
  <div class="metric-card">
@@ -701,10 +528,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
701
528
  <div class="card-title">Avg Time To First Byte</div>
702
529
  <div class="card-value">${cards.ttfb.toFixed(1)}<span class="unit">ms</span></div>
703
530
  </div>
704
- <div class="metric-card">
705
- <div class="card-title">VU Ramp-up Velocity</div>
706
- <div class="card-value cyan">${cards.vuRampUpVelocity.toFixed(1)}<span class="unit">VUs/s</span></div>
707
- </div>
708
531
  <div class="metric-card">
709
532
  <div class="card-title">DNS Lookup</div>
710
533
  <div class="card-value">${cards.dns.toFixed(2)}<span class="unit">ms</span></div>
@@ -732,10 +555,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
732
555
 
733
556
  <div class="matrix-box">
734
557
  <div class="matrix-title">\u{1F4CB} HTTP Response Matrix</div>
735
- <div class="matrix-row">
736
- <div class="matrix-label">Informational <span class="matrix-badge badge-1xx">1xx</span></div>
737
- <div class="matrix-value">${responseMatrix.total1xx}</div>
738
- </div>
739
558
  <div class="matrix-row">
740
559
  <div class="matrix-label">Successful <span class="matrix-badge badge-2xx">2xx</span></div>
741
560
  <div class="matrix-value">${responseMatrix.total2xx}</div>
@@ -755,9 +574,9 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
755
574
  </div>
756
575
  </div>
757
576
 
758
- ${naturalLanguageAssistantHtml}
759
-
577
+ <!-- \u{1F4C9} SPLIT CHARTS SYSTEM -->
760
578
  <div class="charts-grid">
579
+ <!-- Chart 1: Volume Streams -->
761
580
  <div class="graph-card">
762
581
  <div class="graph-title">\u{1F4CA} Throughput Velocity & Workload Volume</div>
763
582
  <div style="height: 280px; position: relative;">
@@ -765,26 +584,13 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
765
584
  </div>
766
585
  </div>
767
586
 
587
+ <!-- Chart 2: Concurrency & Health Trends -->
768
588
  <div class="graph-card">
769
- <div class="graph-title">\u{1F504} Active Concurrency (VUs) vs. TTF Trend</div>
589
+ <div class="graph-title">\u{1F504} Active Concurrency (VUs) vs. Time-to-Failure (TTF) Trend</div>
770
590
  <div style="height: 280px; position: relative;">
771
591
  <canvas id="concurrencyChart"></canvas>
772
592
  </div>
773
593
  </div>
774
-
775
- <div class="graph-card">
776
- <div class="graph-title">\u{1F369} HTTP Status Code Proportions</div>
777
- <div style="height: 280px; position: relative;">
778
- <canvas id="statusDonutChart"></canvas>
779
- </div>
780
- </div>
781
-
782
- <div class="graph-card">
783
- <div class="graph-title">\u{1F4C8} Time-to-First-Byte Scatter Plot</div>
784
- <div style="height: 280px; position: relative;">
785
- <canvas id="ttfbScatterChart"></canvas>
786
- </div>
787
- </div>
788
594
  </div>
789
595
 
790
596
  <div class="card">
@@ -815,142 +621,13 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
815
621
  </tbody>
816
622
  </table>
817
623
  </div>
818
- ${liveAdjusterHtml}
819
- ${rightSizingHtml}
820
624
  ${logClustersHtml}
821
625
  </div>
822
626
 
823
627
  <script>
824
- function copyToClipboard(containerId) {
825
- const text = document.getElementById(containerId).innerText;
826
- navigator.clipboard.writeText(text).then(() => {
827
- alert('Copied remediation snippet to clipboard!');
828
- }).catch(err => {
829
- console.error('Failed to copy text: ', err);
830
- });
831
- }
832
-
833
628
  const labels = ${JSON.stringify(labels)};
834
- const baseThroughput = ${cards.throughput};
835
- const baseLatency = ${cards.ttfb};
836
- const telemetryHistory = ${JSON.stringify(history)};
837
- const responseMatrixData = ${JSON.stringify(responseMatrix)};
838
- const globalSummaryCards = ${JSON.stringify(cards)};
839
- const semanticReportData = ${JSON.stringify(semanticReport)};
840
- const agentVerdictText = ${JSON.stringify(verdictReason)};
841
-
842
- function runAssistantQuery() {
843
- const inputEl = document.getElementById('assistantInput');
844
- const containerEl = document.getElementById('assistantResponseContainer');
845
- const textEl = document.getElementById('assistantResponseText');
846
- const query = inputEl.value.toLowerCase().trim();
847
-
848
- if (!query) return;
849
-
850
- let answer = "";
851
- const match = (keywords) => keywords.some(k => query.includes(k));
852
-
853
- if (match(['throughput', 'rps', 'requests per second', 'capacity', 'volume'])) {
854
- const peakT = Math.max(...telemetryHistory.map(h => h.throughput)).toFixed(0);
855
- const finalT = globalSummaryCards.throughput.toFixed(1);
856
- answer = "\u{1F4C8} **Throughput Assessment:**
857
- " +
858
- "- The peak throughput velocity achieved during this test run was **" + peakT + " req/sec**.
859
- " +
860
- "- The normalized/final execution throughput tracking shows **" + finalT + " req/sec**.
861
- " +
862
- "If your infrastructure limits drop below these values, upstream buffers will choke, precipitating service level drops.";
863
- }
864
- else if (match(['latency', 'p95', 'average latency', 'slow', 'speed', 'ttfb', 'delay'])) {
865
- const maxP95 = Math.max(...telemetryHistory.map(h => h.p95Latency)).toFixed(1);
866
- answer = "\u23F1\uFE0F **Latency Profiling Telemetry:**
867
- " +
868
- "- Average Time-to-First-Byte (TTFB) registered across workloads: **" + globalSummaryCards.ttfb.toFixed(1) + " ms**.
869
- " +
870
- "- Peak Worst-Case P95 Latency boundary reached: **" + maxP95 + " ms**.
871
- " +
872
- "- Handshake Breakdown: DNS Lookup [" + globalSummaryCards.dns.toFixed(2) + "ms] | TCP Connect [" + globalSummaryCards.tcp.toFixed(2) + "ms] | TLS Handshake [" + globalSummaryCards.tls.toFixed(2) + "ms].
873
- " +
874
- "Spikes are heavily correlated to network layer pooling exhaustion rather than payload compute constraints.";
875
- }
876
- else if (match(['error', 'fail', 'crash', 'broken', '5xx', '4xx', 'econnrefused', 'timeout'])) {
877
- const totalErrs = responseMatrixData.total4xx + responseMatrixData.total5xx;
878
- answer = "\u{1F6A8} **Failure Breakdown & Topology Mapping:**
879
- " +
880
- "- A total of **" + totalErrs + " operations failed** during execution loops (" + responseMatrixData.total4xx + " Client 4xx Errors, " + responseMatrixData.total5xx + " Server 5xx Errors).
881
- ";
882
-
883
- if (semanticReportData && semanticReportData.clusters && semanticReportData.clusters.length > 0) {
884
- answer += "- **Primary Log Clusters Captured:**
885
- ";
886
- semanticReportData.clusters.forEach((c) => {
887
- const rawTemplate = c.template || '';
888
- const idxClose = rawTemplate.indexOf(']');
889
- const cleaned = idxClose !== -1 ? rawTemplate.substring(idxClose + 1).trim().substring(0, 80) + "..." : rawTemplate.substring(0, 80) + "...";
890
- const freqScore = Math.min(25, c.count * 0.5);
891
- const clarityBonus = c.severity === 'CRITICAL' ? 15 : 8;
892
- const confidenceScore = Math.min(99, Math.round(65 + freqScore + clarityBonus));
893
- answer += " * [" + c.count + "x] **" + c.category + "** (" + c.severity + ") -> Confidence Rating: *" + confidenceScore + "%*
894
- \`" + cleaned + "\`
895
- ";
896
- });
897
- } else {
898
- answer += "- No structural failure entries were recorded in stdout/stderr diagnostic streams.";
899
- }
900
- }
901
- else if (match(['apdex', 'satisfaction', 'slo', 'breach', 'limit', 'pass', 'target'])) {
902
- const passStatus = globalSummaryCards.apdex >= ${config.targetApdex};
903
- answer = "\u{1F3AF} **SLO Verification Profile:**
904
- " +
905
- "- Target Configured Apdex Floor: **" + (${config.targetApdex}).toFixed(2) + "**
906
- " +
907
- "- Actual Achieved Runtime Apdex: **" + globalSummaryCards.apdex.toFixed(2) + "**
908
- " +
909
- "- **Status Status:** " + (passStatus ? "\u2705 **SLO PASS**" : "\u274C **SLO BREACHED**") + "
910
- " +
911
- "Verdict Context: " + agentVerdictText;
912
- }
913
- else if (match(['summary', 'verdict', 'recommendation', 'cost', 'cloud', 'sizing', 'what happened'])) {
914
- answer = "\u{1F9E0} **AI Executive Summary Directive:**
915
- " +
916
- "- **System Verdict:** " + agentVerdictText + "
917
- " +
918
- "- **Recommended Max Safe Scaled Capacity:** " + globalSummaryCards.saturationKneePoint + " Virtual User Threads Allocation.
919
- " +
920
- "- **Infrastructure Target Allocation Strategy:** Right-size infrastructure to **" + Math.max(2, Math.ceil(globalSummaryCards.saturationKneePoint / 75)) + " vCores** and **" + (Math.max(2, Math.ceil(globalSummaryCards.saturationKneePoint / 75)) * 2) + " GB RAM** to minimize the projected cloud operating budget (~$" + (globalSummaryCards.throughput * 0.045 * 24 * 30).toFixed(2) + "/mo).";
921
- }
922
- else {
923
- answer = "\u2753 **Intent Routing Fallback:**
924
- " +
925
- "I didn't quite catch that context parameter. Try asking specifically about:
926
- " +
927
- "\u2022 *'What was our peak throughput capacity?'*
928
- " +
929
- "\u2022 *'Show me the latency performance limits and handshake timings'*
930
- " +
931
- "\u2022 *'Summarize the server logs and confidence ratings'*
932
- " +
933
- "\u2022 *'Did we pass or breach our target SLO settings?'*";
934
- }
935
-
936
- textEl.innerHTML = answer.replace(/
937
- /g, '<br>').replace(/**(.*?)**/g, '<strong>$1</strong>');
938
- containerEl.style.display = 'block';
939
- }
940
-
941
- const slider = document.getElementById('concurrencySlider');
942
- const sliderVal = document.getElementById('sliderValue');
943
- const simThroughput = document.getElementById('simThroughput');
944
- const simLatency = document.getElementById('simLatency');
945
-
946
- slider.addEventListener('input', (e) => {
947
- const val = parseInt(e.target.value, 10);
948
- sliderVal.textContent = val;
949
- const factor = val / ${config.threads || 10};
950
- simThroughput.textContent = (baseThroughput * Math.min(factor, 2.5)).toFixed(0) + '/s';
951
- simLatency.textContent = (baseLatency * Math.pow(factor, 0.8)).toFixed(1) + 'ms';
952
- });
953
629
 
630
+ // \u{1F6E0}\uFE0F Chart 1 Setup: Throughput Bar Streams
954
631
  new Chart(document.getElementById('volumeChart'), {
955
632
  type: 'bar',
956
633
  data: {
@@ -987,6 +664,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
987
664
  }
988
665
  });
989
666
 
667
+ // \u{1F6E0}\uFE0F Chart 2 Setup: Concurrency & TTF Correlated Slopes
990
668
  new Chart(document.getElementById('concurrencyChart'), {
991
669
  type: 'line',
992
670
  data: {
@@ -1000,7 +678,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
1000
678
  borderWidth: 2.5,
1001
679
  pointRadius: 4,
1002
680
  fill: true,
1003
- yAxisID: 'y_threads'
681
+ yAxisID: 'yThreads'
1004
682
  },
1005
683
  {
1006
684
  label: 'Time-to-Failure (TTF) Trend',
@@ -1010,7 +688,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
1010
688
  borderDash: [5, 5],
1011
689
  pointRadius: 3,
1012
690
  fill: false,
1013
- yAxisID: 'y_ttf'
691
+ yAxisID: 'yTtf'
1014
692
  }
1015
693
  ]
1016
694
  },
@@ -1022,53 +700,8 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
1022
700
  },
1023
701
  scales: {
1024
702
  x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } },
1025
- y_threads: { position: 'left', grid: { color: '#1e293b' }, ticks: { color: '#38bdf8' }, title: { display: true, text: 'VUs / Threads', color: '#38bdf8' } },
1026
- y_ttf: { position: 'right', grid: { drawOnChartArea: false }, ticks: { color: '#f43f5e' }, title: { display: true, text: 'TTF Index / Latency ms', color: '#f43f5e' } }
1027
- }
1028
- }
1029
- });
1030
-
1031
- new Chart(document.getElementById('statusDonutChart'), {
1032
- type: 'doughnut',
1033
- data: {
1034
- labels: ['1xx Info', '2xx Success', '3xx Redirect', '4xx Client Err', '5xx Server Err'],
1035
- datasets: [{
1036
- data: [${responseMatrix.total1xx}, ${responseMatrix.total2xx}, ${responseMatrix.total3xx}, ${responseMatrix.total4xx}, ${responseMatrix.total5xx}],
1037
- backgroundColor: ['#38bdf8', '#4ade80', '#cbd5e1', '#fde047', '#f87171'],
1038
- borderWidth: 1,
1039
- borderColor: '#1e293b'
1040
- }]
1041
- },
1042
- options: {
1043
- responsive: true,
1044
- maintainAspectRatio: false,
1045
- plugins: {
1046
- legend: { position: 'top', labels: { color: '#94a3b8', font: { size: 11 } } }
1047
- },
1048
- cutout: '65%'
1049
- }
1050
- });
1051
-
1052
- new Chart(document.getElementById('ttfbScatterChart'), {
1053
- type: 'scatter',
1054
- data: {
1055
- datasets: [{
1056
- label: 'Request Processing Delay',
1057
- data: ${JSON.stringify(scatterPoints)},
1058
- backgroundColor: '#a855f7',
1059
- pointRadius: 4,
1060
- pointHoverRadius: 6
1061
- }]
1062
- },
1063
- options: {
1064
- responsive: true,
1065
- maintainAspectRatio: false,
1066
- plugins: {
1067
- legend: { position: 'top', labels: { color: '#94a3b8', font: { size: 11 } } }
1068
- },
1069
- scales: {
1070
- x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' }, title: { display: true, text: 'Time offset (s)', color: '#64748b' } },
1071
- y: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' }, title: { display: true, text: 'TTFB / Delay (ms)', color: '#64748b' } }
703
+ yThreads: { position: 'left', grid: { color: '#1e293b' }, ticks: { color: '#38bdf8' }, title: { display: true, text: 'VUs / Threads', color: '#38bdf8' } },
704
+ yTtf: { position: 'right', grid: { drawOnChartArea: false }, ticks: { color: '#f43f5e' }, title: { display: true, text: 'TTF Index / Latency ms', color: '#f43f5e' } }
1072
705
  }
1073
706
  }
1074
707
  });
@@ -1083,7 +716,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
1083
716
  function printUsage() {
1084
717
  console.log(`Blaze Core Performance Test CLI Engine
1085
718
  Options:
1086
- --threads <count> | --duration <seconds> | --agentic | --cluster-logs | --correlate
1087
- --apdex-t <ms> Set customizable T-Threshold window (Default: 50ms)`);
719
+ --threads <count> | --duration <seconds> | --agentic | --cluster-logs`);
1088
720
  }
1089
721
  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.1",
3
+ "version": "3.1.3",
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",