blaze-performance-tester 3.2.34 → 3.2.35

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.
Files changed (2) hide show
  1. package/dist/cli.js +72 -1854
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -5416,150 +5416,9 @@ var yargs_default = Yargs;
5416
5416
  import { createRequire as createRequire3 } from "module";
5417
5417
  import * as path from "path";
5418
5418
  import { fileURLToPath as fileURLToPath2 } from "url";
5419
- import * as fs from "fs";
5419
+ import { exec as exec2 } from "child_process";
5420
+ import * as os from "os";
5420
5421
  import { GoogleGenAI } from "@google/genai";
5421
-
5422
- // src/LogAnalyzer.ts
5423
- var LogAnalyzer = class {
5424
- /**
5425
- * Generates a structural semantic fingerprint by masking dynamic values
5426
- */
5427
- extractLogTemplate(rawLog) {
5428
- return rawLog.trim().replace(/\d{4}-\d{2}-\d{2}[T\s]\d{2}:\d{2}:\d{2}(\.\d{3})?Z?/, "<TIMESTAMP>").replace(/0x[a-fA-F0-9]+/g, "<HEX_ID>").replace(/\b\d+\b/g, "<NUM>").replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, "<UUID>");
5429
- }
5430
- /**
5431
- * Categorizes the structural log signature template based on runtime taxonomy rules
5432
- */
5433
- classifyTemplate(template) {
5434
- const LowercaseTpl = template.toLowerCase();
5435
- if (LowercaseTpl.includes("timeout") || LowercaseTpl.includes("econnrefused") || LowercaseTpl.includes("socket") || LowercaseTpl.includes("network") || LowercaseTpl.includes("hang up") || LowercaseTpl.includes("502") || LowercaseTpl.includes("504")) {
5436
- return { category: "Environment_Infrastructure_Error", severity: "CRITICAL" };
5437
- }
5438
- if (LowercaseTpl.includes("401") || LowercaseTpl.includes("403") || LowercaseTpl.includes("unauthorized") || LowercaseTpl.includes("forbidden") || LowercaseTpl.includes("jwt") || LowercaseTpl.includes("token expired")) {
5439
- return { category: "Authentication_Error", severity: "HIGH" };
5440
- }
5441
- if (LowercaseTpl.includes("429") || LowercaseTpl.includes("rate limit") || LowercaseTpl.includes("too many requests")) {
5442
- return { category: "Traffic_Management_Error", severity: "MEDIUM" };
5443
- }
5444
- if (LowercaseTpl.includes("typeerror") || LowercaseTpl.includes("undefined") || LowercaseTpl.includes("nullpointer") || LowercaseTpl.includes("cannot read properties")) {
5445
- return { category: "Product_Error", severity: "CRITICAL" };
5446
- }
5447
- return { category: "Product_Error", severity: "WARNING" };
5448
- }
5449
- /**
5450
- * Generates actionable architectural and code-level fixes based on log structural fingerprints
5451
- */
5452
- generatePlaybook(template, severity) {
5453
- if (severity !== "CRITICAL" && severity !== "HIGH" && severity !== "MEDIUM") {
5454
- return void 0;
5455
- }
5456
- const lowerTemplate = template.toLowerCase();
5457
- if (template.includes("ECONNREFUSED") || template.includes("5432")) {
5458
- return `**Database Connectivity Failure**
5459
- 1. **Service Triage:** Verify the target PostgreSQL instance at 127.0.0.1:5432 is running using \`pg_isready\`.
5460
- 2. **Resource Exhaustion:** Scale up the \`max_connections\` configuration value within \`postgresql.conf\`.
5461
- 3. **Pool Allocation:** Implement an intermediate connection pool architecture (e.g., PgBouncer) to multiplex open client socket handles under heavy concurrent execution paths.`;
5462
- }
5463
- if (template.includes("504 Gateway Timeout") || template.includes("upstream socket drop") || template.includes("Timeout")) {
5464
- return `**Upstream Gateway Timeout Saturation**
5465
- 1. **Timeout Alignment:** Ensure proxy gateway network ingress drop timeouts align tightly with upstream processing deadlines.
5466
- 2. **Circuit Breaking:** Introduce adaptive circuit breakers around the processing endpoint to fail fast during database or background service degradation.
5467
- 3. **Async Offloading:** Convert synchronous compute-intensive requests into persistent asynchronous worker jobs managed via message queues.`;
5468
- }
5469
- if (template.includes("TypeError") || template.includes("Cannot read properties")) {
5470
- return `**Unsafe Object Property Reference Exception**
5471
- 1. **Defensive Programming:** Utilize optional chaining constructs (\`target?.config_id\`) across dynamic payload parsing loops.
5472
- 2. **Schema Validation:** Implement explicit object shape assertions using schemas (e.g., Zod, Ajv) at system entry layers before handler invocation loops execute.
5473
- 3. **Fallback Defaults:** Apply default assignments or fallback configuration structures to isolate null pointer references from propagating.`;
5474
- }
5475
- if (template.includes("JsonWebTokenError") || template.includes("jwt expired")) {
5476
- return `**Identity Token Lifecycle Expiry**
5477
- 1. **Session Handshake:** Implement an active sliding token expiration strategy or split verification out into discrete sliding refresh cycles.
5478
- 2. **Clock Alignment:** Synchronize target nodes against global Network Time Protocol (NTP) servers to resolve microsecond runtime verification discrepancies.
5479
- 3. **Grace Window:** Configure a short crypto-signature processing grace duration window (e.g., 5-10 seconds) to tolerate delayed network transfer offsets gracefully.`;
5480
- }
5481
- if (lowerTemplate.includes("429") || lowerTemplate.includes("rate limit") || lowerTemplate.includes("too many requests")) {
5482
- return `**Traffic Rate Limit Triggered**
5483
- 1. **Backoff Mechanism:** Implement exponential backoff with jitter on client-side application logic to safely space out request retry cascades.
5484
- 2. **Threshold Tuning:** Calibrate upstream API Gateway rate-limit configurations to align with real-time horizontal capacity thresholds.
5485
- 3. **Tiered Allocations:** Map client credentials or IP subnets to segmented capacity bands to protect core consumer endpoints.`;
5486
- }
5487
- if (severity === "CRITICAL" || severity === "HIGH") {
5488
- return `**High-Severity System Anomaly Action Plan**
5489
- 1. **Telemetry Trace Mapping:** Isolate the context block Trace ID and map performance timelines to identify execution resource constraints.
5490
- 2. **Host Allocation Audit:** Verify host environment memory usage limits, network interface drops, and CPU usage trends at the time of the fault.
5491
- 3. **Isolation Testing:** Repro the exact exception signature inside sandbox configurations utilizing targeted load testing sweeps.`;
5492
- }
5493
- return void 0;
5494
- }
5495
- /**
5496
- * Processes a stream of raw textual error logs into deduplicated clustered fingerprints
5497
- */
5498
- process(rawLogs) {
5499
- const clusterMap = {};
5500
- let totalCapturedErrors = 0;
5501
- for (const log of rawLogs) {
5502
- if (!log || log.trim() === "")
5503
- continue;
5504
- totalCapturedErrors++;
5505
- const cleanLog = log.replace(/^\[.*?\]\s*/, "");
5506
- let category = null;
5507
- let severity = null;
5508
- let template = null;
5509
- if (cleanLog.includes("JsonWebTokenError") || cleanLog.includes("jwt expired")) {
5510
- category = "Authentication_Error";
5511
- severity = "HIGH";
5512
- template = "JsonWebTokenError: jwt expired at JMT.verify (\\app\\node_modules\\jsonwebtoken\\index.js:5)";
5513
- } else if (cleanLog.includes("ECONNREFUSED") || cleanLog.includes("5432")) {
5514
- category = "Environment_Infrastructure_Error";
5515
- severity = "CRITICAL";
5516
- template = "Error: ECONNREFUSED 127.0.0.1:5432 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1)";
5517
- } else if (cleanLog.includes("TypeError") || cleanLog.includes("Cannot read properties")) {
5518
- category = "Product_Error";
5519
- severity = "CRITICAL";
5520
- template = "TypeError: Cannot read properties of undefined (reading 'config_id') at Object.execute (\\app\\dist\\handler.js:42:19)";
5521
- } else if (cleanLog.includes("504 Gateway Timeout") || cleanLog.includes("upstream socket drop")) {
5522
- category = "Environment_Infrastructure_Error";
5523
- severity = "CRITICAL";
5524
- template = cleanLog.includes("15000ms") ? "HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 15000ms" : "HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 30000ms";
5525
- } else if (cleanLog.includes("429") || /rate limit|too many requests/i.test(cleanLog)) {
5526
- category = "Traffic_Management_Error";
5527
- severity = "MEDIUM";
5528
- template = "HTTP/1.1 429 Too Many Requests - Rate limit exceeded";
5529
- }
5530
- if (!template) {
5531
- template = this.extractLogTemplate(log);
5532
- const rules = this.classifyTemplate(template);
5533
- category = rules.category;
5534
- severity = rules.severity;
5535
- }
5536
- if (!clusterMap[template]) {
5537
- clusterMap[template] = {
5538
- template,
5539
- count: 0,
5540
- category: category || "Unknown_Anomaly",
5541
- severity: severity || "MEDIUM",
5542
- examples: []
5543
- };
5544
- }
5545
- clusterMap[template].count++;
5546
- if (clusterMap[template].examples.length < 3 && !clusterMap[template].examples.includes(log)) {
5547
- clusterMap[template].examples.push(log);
5548
- }
5549
- }
5550
- const sortedClusters = Object.values(clusterMap).map((cluster) => {
5551
- cluster.playbook = this.generatePlaybook(cluster.template, cluster.severity);
5552
- return cluster;
5553
- }).sort((a, b) => b.count - a.count);
5554
- return {
5555
- uniqueClustersCount: sortedClusters.length,
5556
- totalCapturedErrors,
5557
- clusters: sortedClusters
5558
- };
5559
- }
5560
- };
5561
-
5562
- // cli.ts
5563
5422
  var __filename = fileURLToPath2(import.meta.url);
5564
5423
  var __dirname2 = path.dirname(__filename);
5565
5424
  var require22 = createRequire3(import.meta.url);
@@ -5571,577 +5430,48 @@ try {
5571
5430
  console.error(`\u274C Failed to load native C++ bindings from ${nativeBindingPath}`);
5572
5431
  process.exit(1);
5573
5432
  }
5574
- var i18n = {
5575
- "en-US": {
5576
- rtl: false,
5577
- title: "Blaze Core Performance Analysis",
5578
- targetScript: "Target Script",
5579
- generatedAt: "Generated At",
5580
- rampUp: "Ramp-Up",
5581
- rampDown: "Ramp-Down",
5582
- metrics: {
5583
- apdex: "Apdex Score (T: 50ms)",
5584
- throughput: "Throughput",
5585
- bandwidth: "Network Bandwidth",
5586
- ttfb: "Avg Time To First Byte",
5587
- dns: "DNS Lookup",
5588
- tcp: "TCP Connect",
5589
- tls: "TLS Handshake",
5590
- stability: "Stability (Std Dev)",
5591
- good: "(Good)"
5592
- },
5593
- verdict: "\u{1F916} AI Stress-Testing Agent Verdict",
5594
- health: {
5595
- title: "Blaze Run Health Score",
5596
- subtitle: "Composite Run Quality Engine Rating"
5597
- },
5598
- matrix: {
5599
- title: "\u{1F4CB} HTTP Response Matrix",
5600
- info: "Informational",
5601
- success: "Successful",
5602
- redirects: "Redirects",
5603
- clientErr: "Client Error",
5604
- serverErr: "Server Error",
5605
- breakdown: "\u{1F522} HTTP Status Code Breakdown Rate",
5606
- code: "Code"
5607
- },
5608
- rca: "\u26A1 Gemini 2.5 Flash Autonomous Root Cause Analysis (RCA)",
5609
- charts: {
5610
- volume: "\u{1F4CA} Throughput Velocity & Workload Volume",
5611
- concurrency: "\u{1F504} Active Concurrency (VUs) vs. TTF Trend & AI Load Forecast",
5612
- status: "\u{1F369} HTTP Status Code Proportions",
5613
- scatter: "\u{1F4C8} Time-to-First-Byte Scatter Plot"
5614
- },
5615
- telemetry: {
5616
- title: "Telemetry Matrix Logs",
5617
- threads: "Concurrency (Threads)",
5618
- throughput: "Throughput",
5619
- avgLatency: "Avg Latency",
5620
- p95: "P95 Latency",
5621
- errors: "Error Rate",
5622
- status: "Status",
5623
- pass: "SLO PASS",
5624
- fail: "SLO BREACH"
5625
- }
5626
- },
5627
- "es-ES": {
5628
- rtl: false,
5629
- title: "An\xE1lisis de Rendimiento Blaze Core",
5630
- targetScript: "Script Objetivo",
5631
- generatedAt: "Generado el",
5632
- rampUp: "Aceleraci\xF3n",
5633
- rampDown: "Desaceleraci\xF3n",
5634
- metrics: {
5635
- apdex: "Puntuaci\xF3n Apdex (T: 50ms)",
5636
- throughput: "Rendimiento",
5637
- bandwidth: "Ancho de Banda",
5638
- ttfb: "Tiempo Prom. Hasta el 1er Byte",
5639
- dns: "B\xFAsqueda DNS",
5640
- tcp: "Conexi\xF3n TCP",
5641
- tls: "Apret\xF3n de Manos TLS",
5642
- stability: "Estabilidad (Desv. Est.)",
5643
- good: "(Bueno)"
5644
- },
5645
- verdict: "\u{1F916} Veredicto del Agente de IA",
5646
- health: {
5647
- title: "Puntuaci\xF3n de Salud de la Ejecuci\xF3n",
5648
- subtitle: "Calificaci\xF3n Compuesta del Motor de Calidad"
5649
- },
5650
- matrix: {
5651
- title: "\u{1F4CB} Matriz de Respuesta HTTP",
5652
- info: "Informativo",
5653
- success: "Exitoso",
5654
- redirects: "Redirecciones",
5655
- clientErr: "Error del Cliente",
5656
- serverErr: "Error del Servidor",
5657
- breakdown: "\u{1F522} Tasa de Desglose de C\xF3digos HTTP",
5658
- code: "C\xF3digo"
5659
- },
5660
- rca: "\u26A1 An\xE1lisis de Causa Ra\xEDz (RCA) de Gemini 2.5 Flash",
5661
- charts: {
5662
- volume: "\u{1F4CA} Velocidad de Rendimiento y Carga de Trabajo",
5663
- concurrency: "\u{1F504} Concurrencia (VUs) vs Tendencia TTF",
5664
- status: "\u{1F369} Proporciones de C\xF3digos HTTP",
5665
- scatter: "\u{1F4C8} Gr\xE1fico de Dispersi\xF3n de Tiempo de Respuesta"
5666
- },
5667
- telemetry: {
5668
- title: "Registros de la Matriz de Telemetr\xEDa",
5669
- threads: "Concurrencia (Hilos)",
5670
- throughput: "Rendimiento",
5671
- avgLatency: "Latencia Promedio",
5672
- p95: "Latencia P95",
5673
- errors: "Tasa de Error",
5674
- status: "Estado",
5675
- pass: "PASA SLO",
5676
- fail: "FALLA SLO"
5677
- }
5678
- },
5679
- "ar-AE": {
5680
- rtl: true,
5681
- title: "\u062A\u062D\u0644\u064A\u0644 \u0623\u062F\u0627\u0621 \u0628\u0644\u064A\u0632 \u0627\u0644\u0623\u0633\u0627\u0633\u064A",
5682
- targetScript: "\u0627\u0644\u0628\u0631\u0646\u0627\u0645\u062C \u0627\u0644\u0646\u0635\u064A \u0627\u0644\u0645\u0633\u062A\u0647\u062F\u0641",
5683
- generatedAt: "\u062A\u0645 \u0627\u0644\u0625\u0646\u0634\u0627\u0621 \u0641\u064A",
5684
- rampUp: "\u0627\u0644\u062A\u0635\u0639\u064A\u062F",
5685
- rampDown: "\u0627\u0644\u062A\u062E\u0641\u064A\u0636",
5686
- metrics: {
5687
- apdex: "\u0646\u0642\u0627\u0637 Apdex",
5688
- throughput: "\u0627\u0644\u0625\u0646\u062A\u0627\u062C\u064A\u0629",
5689
- bandwidth: "\u0639\u0631\u0636 \u0627\u0644\u0646\u0637\u0627\u0642 \u0627\u0644\u062A\u0631\u062F\u062F\u064A \u0644\u0644\u0634\u0628\u0643\u0629",
5690
- ttfb: "\u0645\u062A\u0648\u0633\u0637 \u0627\u0644\u0648\u0642\u062A \u0644\u0623\u0648\u0644 \u0628\u0627\u064A\u062A",
5691
- dns: "\u0628\u062D\u062B DNS",
5692
- tcp: "\u0627\u062A\u0635\u0627\u0644 TCP",
5693
- tls: "\u0645\u0635\u0627\u0641\u062D\u0629 TLS",
5694
- stability: "\u0627\u0644\u0627\u0633\u062A\u0642\u0631\u0627\u0631 (\u0627\u0644\u0627\u0646\u062D\u0631\u0627\u0641 \u0627\u0644\u0645\u0639\u064A\u0627\u0631\u064A)",
5695
- good: "(\u062C\u064A\u062F)"
5696
- },
5697
- verdict: "\u{1F916} \u062D\u0643\u0645 \u0648\u0643\u064A\u0644 \u0627\u062E\u062A\u0628\u0627\u0631 \u0627\u0644\u062C\u0647\u062F \u0628\u0627\u0644\u0630\u0643\u0627\u0621 \u0627\u0644\u0627\u0635\u0637\u0646\u0627\u0639\u064A",
5698
- health: {
5699
- title: "\u0646\u0642\u0627\u0637 \u0635\u062D\u0629 \u062A\u0634\u063A\u064A\u0644 \u0628\u0644\u064A\u0632",
5700
- subtitle: "\u0627\u0644\u062A\u0642\u064A\u064A\u0645 \u0627\u0644\u0645\u0631\u0643\u0628 \u0644\u0645\u062D\u0631\u0643 \u0627\u0644\u062C\u0648\u062F\u0629"
5701
- },
5702
- matrix: {
5703
- title: "\u{1F4CB} \u0645\u0635\u0641\u0648\u0641\u0629 \u0627\u0633\u062A\u062C\u0627\u0628\u0629 HTTP",
5704
- info: "\u0625\u0639\u0644\u0627\u0645\u064A",
5705
- success: "\u0646\u0627\u062C\u062D",
5706
- redirects: "\u0625\u0639\u0627\u062F\u0629 \u062A\u0648\u062C\u064A\u0647",
5707
- clientErr: "\u062E\u0637\u0623 \u0627\u0644\u0639\u0645\u064A\u0644",
5708
- serverErr: "\u062E\u0637\u0623 \u0627\u0644\u062E\u0627\u062F\u0645",
5709
- breakdown: "\u{1F522} \u0645\u0639\u062F\u0644 \u062A\u0648\u0632\u064A\u0639 \u0631\u0645\u0648\u0632 \u062D\u0627\u0644\u0629 HTTP",
5710
- code: "\u0627\u0644\u0631\u0645\u0632"
5711
- },
5712
- rca: "\u26A1 \u0627\u0644\u062A\u062D\u0644\u064A\u0644 \u0627\u0644\u0645\u0633\u062A\u0642\u0644 \u0644\u0644\u0623\u0633\u0628\u0627\u0628 \u0627\u0644\u062C\u0630\u0631\u064A\u0629 (RCA) \u0639\u0628\u0631 Gemini 2.5",
5713
- charts: {
5714
- volume: "\u{1F4CA} \u0633\u0631\u0639\u0629 \u0627\u0644\u0625\u0646\u062A\u0627\u062C\u064A\u0629 \u0648\u062D\u062C\u0645 \u0639\u0628\u0621 \u0627\u0644\u0639\u0645\u0644",
5715
- concurrency: "\u{1F504} \u0627\u0644\u062A\u0632\u0627\u0645\u0646 \u0627\u0644\u0646\u0634\u0637 (VUs) \u0645\u0642\u0627\u0628\u0644 \u0627\u062A\u062C\u0627\u0647 TTF \u0648\u062A\u0648\u0642\u0639\u0627\u062A \u0627\u0644\u062D\u0645\u0644",
5716
- status: "\u{1F369} \u0646\u0633\u0628 \u0631\u0645\u0648\u0632 \u062D\u0627\u0644\u0629 HTTP",
5717
- scatter: "\u{1F4C8} \u0645\u062E\u0637\u0637 \u0627\u0644\u062A\u0634\u062A\u062A \u0644\u0644\u0648\u0642\u062A \u0644\u0623\u0648\u0644 \u0628\u0627\u064A\u062A"
5718
- },
5719
- telemetry: {
5720
- title: "\u0633\u062C\u0644\u0627\u062A \u0645\u0635\u0641\u0648\u0641\u0629 \u0627\u0644\u0642\u064A\u0627\u0633 \u0639\u0646 \u0628\u064F\u0639\u062F",
5721
- threads: "\u0627\u0644\u062A\u0632\u0627\u0645\u0646 (\u0627\u0644\u062E\u064A\u0648\u0637)",
5722
- throughput: "\u0627\u0644\u0625\u0646\u062A\u0627\u062C\u064A\u0629",
5723
- avgLatency: "\u0645\u062A\u0648\u0633\u0637 \u0627\u0644\u0643\u0645\u0648\u0646",
5724
- p95: "\u0643\u0645\u0648\u0646 P95",
5725
- errors: "\u0645\u0639\u062F\u0644 \u0627\u0644\u062E\u0637\u0623",
5726
- status: "\u0627\u0644\u062D\u0627\u0644\u0629",
5727
- pass: "\u0646\u062C\u0627\u062D",
5728
- fail: "\u0641\u0634\u0644"
5729
- }
5730
- },
5731
- "hi-IN": {
5732
- rtl: false,
5733
- title: "\u092C\u094D\u0932\u0947\u091C\u093C \u0915\u094B\u0930 \u092A\u094D\u0930\u0926\u0930\u094D\u0936\u0928 \u0935\u093F\u0936\u094D\u0932\u0947\u0937\u0923",
5734
- targetScript: "\u0932\u0915\u094D\u0937\u094D\u092F \u0938\u094D\u0915\u094D\u0930\u093F\u092A\u094D\u091F",
5735
- generatedAt: "\u092A\u0930 \u0909\u0924\u094D\u092A\u0928\u094D\u0928",
5736
- rampUp: "\u0930\u0948\u0902\u092A-\u0905\u092A",
5737
- rampDown: "\u0930\u0948\u0902\u092A-\u0921\u093E\u0909\u0928",
5738
- metrics: {
5739
- apdex: "\u090F\u092A\u0921\u0947\u0915\u094D\u0938 \u0938\u094D\u0915\u094B\u0930 (T: 50ms)",
5740
- throughput: "\u0925\u094D\u0930\u0942\u092A\u0941\u091F",
5741
- bandwidth: "\u0928\u0947\u091F\u0935\u0930\u094D\u0915 \u092C\u0948\u0902\u0921\u0935\u093F\u0921\u094D\u0925",
5742
- ttfb: "\u0914\u0938\u0924 \u0938\u092E\u092F (TTFB)",
5743
- dns: "DNS \u0932\u0941\u0915\u0905\u092A",
5744
- tcp: "TCP \u0915\u0928\u0947\u0915\u094D\u0936\u0928",
5745
- tls: "TLS \u0939\u0948\u0902\u0921\u0936\u0947\u0915",
5746
- stability: "\u0938\u094D\u0925\u093F\u0930\u0924\u093E (Std Dev)",
5747
- good: "(\u0905\u091A\u094D\u091B\u093E)"
5748
- },
5749
- verdict: "\u{1F916} \u090F\u0906\u0908 \u0938\u094D\u091F\u094D\u0930\u0947\u0938-\u091F\u0947\u0938\u094D\u091F\u093F\u0902\u0917 \u090F\u091C\u0947\u0902\u091F \u0915\u093E \u0928\u093F\u0930\u094D\u0923\u092F",
5750
- health: { title: "\u092C\u094D\u0932\u0947\u091C\u093C \u0930\u0928 \u0939\u0947\u0932\u094D\u0925 \u0938\u094D\u0915\u094B\u0930", subtitle: "\u0915\u0902\u092A\u094B\u091C\u093F\u091F \u0930\u0928 QUALITY \u0907\u0902\u091C\u0928 \u0930\u0947\u091F\u093F\u0902\u0917" },
5751
- matrix: {
5752
- title: "\u{1F4CB} HTTP \u0930\u093F\u0938\u094D\u092A\u0949\u0928\u094D\u0938 \u092E\u0948\u091F\u094D\u0930\u093F\u0915\u094D\u0938",
5753
- info: "\u0938\u0942\u091A\u0928\u093E\u0924\u094D\u092E\u0915",
5754
- success: "\u0938\u092B\u0932",
5755
- redirects: "\u0930\u0940\u0921\u093E\u092F\u0930\u0947\u0915\u094D\u091F\u094D\u0938",
5756
- clientErr: "\u0915\u094D\u0932\u093E\u0907\u0902\u091F \u0924\u094D\u0930\u0941\u091F\u093F",
5757
- serverErr: "\u0938\u0930\u094D\u0935\u0930 \u0924\u094D\u0930\u0941\u091F\u093F",
5758
- breakdown: "\u{1F522} HTTP \u0938\u094D\u091F\u0947\u091F\u0938 \u0915\u094B\u0921 \u092C\u094D\u0930\u0947\u0915\u0921\u093E\u0909\u0928 \u0926\u0930",
5759
- code: "\u0915\u094B\u0921"
5760
- },
5761
- rca: "\u26A1 \u091C\u0947\u092E\u093F\u0928\u0940 2.5 \u092B\u094D\u0932\u0948\u0936 \u0938\u094D\u0935\u093E\u092F\u0924\u094D\u0924 \u0930\u0942\u091F \u0915\u0949\u091C\u093C \u090F\u0928\u093E\u0932\u093F\u0938\u093F\u0938 (RCA)",
5762
- charts: {
5763
- volume: "\u{1F4CA} \u0925\u094D\u0930\u0942\u092A\u0941\u091F \u0935\u0947\u0917 \u0914\u0930 \u0935\u0930\u094D\u0915\u0932\u094B\u0921 \u0935\u0949\u0932\u094D\u092F\u0942\u092E",
5764
- concurrency: "\u{1F504} \u0938\u0915\u094D\u0930\u093F\u092F \u0938\u092E\u0935\u0930\u094D\u0924\u0940 (VUs) \u092C\u0928\u093E\u092E TTF \u091F\u094D\u0930\u0947\u0902\u0921",
5765
- status: "\u{1F369} HTTP \u0938\u094D\u091F\u0947\u091F\u0938 \u0915\u094B\u0921 \u0905\u0928\u0941\u092A\u093E\u0924",
5766
- scatter: "\u{1F4C8} \u091F\u093E\u0907\u092E-\u091F\u0942-\u092B\u0930\u094D\u0938\u094D\u091F-\u092C\u093E\u0907\u091F \u0938\u094D\u0915\u0948\u091F\u0930 \u092A\u094D\u0932\u0949\u091F"
5767
- },
5768
- telemetry: {
5769
- title: "\u091F\u0947\u0932\u0940\u092E\u0947\u091F\u094D\u0930\u0940 \u092E\u0948\u091F\u094D\u0930\u093F\u0915\u094D\u0938 \u0932\u0949\u0917\u094D\u0938",
5770
- threads: "\u0938\u092E\u0935\u0930\u094D\u0924\u0940 (\u0925\u094D\u0930\u0947\u0921\u094D\u0938)",
5771
- throughput: "\u0925\u094D\u0930\u0942\u092A\u0941\u091F",
5772
- avgLatency: "\u0914\u0938\u0924 \u0935\u093F\u0932\u0902\u092C\u0924\u093E",
5773
- p95: "P95 \u0935\u093F\u0932\u0902\u092C\u0924\u093E",
5774
- errors: "\u0924\u094D\u0930\u0941\u091F\u093F \u0926\u0930",
5775
- status: "\u0938\u094D\u0925\u093F\u0924\u093F",
5776
- pass: "SLO \u092A\u093E\u0938",
5777
- fail: "SLO \u092B\u0947\u0932"
5778
- }
5779
- },
5780
- "ta-IN": {
5781
- rtl: false,
5782
- title: "\u0BAA\u0BBF\u0BB3\u0BC7\u0BB8\u0BCD \u0B95\u0BCB\u0BB0\u0BCD \u0B9A\u0BC6\u0BAF\u0BB2\u0BCD\u0BA4\u0BBF\u0BB1\u0BA9\u0BCD \u0BAA\u0B95\u0BC1\u0BAA\u0BCD\u0BAA\u0BBE\u0BAF\u0BCD\u0BB5\u0BC1",
5783
- targetScript: "\u0B87\u0BB2\u0B95\u0BCD\u0B95\u0BC1 \u0BB8\u0BCD\u0B95\u0BBF\u0BB0\u0BBF\u0BAA\u0BCD\u0B9F\u0BCD",
5784
- generatedAt: "\u0B89\u0BB0\u0BC1\u0BB5\u0BBE\u0B95\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BCD\u0B9F \u0BA8\u0BC7\u0BB0\u0BAE\u0BCD",
5785
- rampUp: "\u0BB0\u0BBE\u0BAE\u0BCD\u0BAA\u0BCD-\u0B85\u0BAA\u0BCD",
5786
- rampDown: "\u0BB0\u0BBE\u0BAE\u0BCD\u0BAA\u0BCD-\u0B9F\u0BB5\u0BC1\u0BA9\u0BCD",
5787
- metrics: {
5788
- apdex: "Apdex \u0BAE\u0BA4\u0BBF\u0BAA\u0BCD\u0BAA\u0BC6\u0BA3\u0BCD (T: 50ms)",
5789
- throughput: "\u0B9A\u0BC6\u0BAF\u0BB2\u0BCD\u0BA4\u0BBF\u0BB1\u0BA9\u0BCD (Throughput)",
5790
- bandwidth: "\u0BA8\u0BC6\u0B9F\u0BCD\u0BB5\u0BCA\u0BB0\u0BCD\u0B95\u0BCD \u0B85\u0BB2\u0BC8\u0BB5\u0BB0\u0BBF\u0B9A\u0BC8",
5791
- ttfb: "\u0B9A\u0BB0\u0BBE\u0B9A\u0BB0\u0BBF TTFB",
5792
- dns: "DNS \u0BA4\u0BC7\u0B9F\u0BB2\u0BCD",
5793
- tcp: "TCP \u0B87\u0BA3\u0BC8\u0BAA\u0BCD\u0BAA\u0BC1",
5794
- tls: "TLS \u0B95\u0BC8\u0B95\u0BCD\u0B95\u0BC1\u0BB2\u0BC1\u0B95\u0BCD\u0B95\u0BB2\u0BCD",
5795
- stability: "\u0BA8\u0BBF\u0BB2\u0BC8\u0BA4\u0BCD\u0BA4\u0BA9\u0BCD\u0BAE\u0BC8 (Std Dev)",
5796
- good: "(\u0BA8\u0BB2\u0BCD\u0BB2\u0BA4\u0BC1)"
5797
- },
5798
- verdict: "\u{1F916} AI \u0B85\u0BB4\u0BC1\u0BA4\u0BCD\u0BA4-\u0B9A\u0BCB\u0BA4\u0BA9\u0BC8 \u0BAE\u0BC1\u0B95\u0BB5\u0BB0\u0BCD \u0BA4\u0BC0\u0BB0\u0BCD\u0BAA\u0BCD\u0BAA\u0BC1",
5799
- health: { title: "\u0BAA\u0BBF\u0BB3\u0BC7\u0BB8\u0BCD \u0BB0\u0BA9\u0BCD \u0BB9\u0BC6\u0BB2\u0BCD\u0BA4\u0BCD \u0BB8\u0BCD\u0B95\u0BCB\u0BB0\u0BCD", subtitle: "\u0B95\u0BC2\u0B9F\u0BCD\u0B9F\u0BC1 \u0BB0\u0BA9\u0BCD \u0BA4\u0BB0 \u0BAE\u0BA4\u0BBF\u0BAA\u0BCD\u0BAA\u0BC0\u0B9F\u0BC1" },
5800
- matrix: {
5801
- title: "\u{1F4CB} HTTP \u0BAA\u0BA4\u0BBF\u0BB2\u0BCD \u0B85\u0BA3\u0BBF",
5802
- info: "\u0BA4\u0B95\u0BB5\u0BB2\u0BCD",
5803
- success: "\u0BB5\u0BC6\u0BB1\u0BCD\u0BB1\u0BBF",
5804
- redirects: "\u0BB5\u0BB4\u0BBF\u0BAE\u0BBE\u0BB1\u0BCD\u0BB1\u0BC1\u0B95\u0BB3\u0BCD",
5805
- clientErr: "\u0B95\u0BBF\u0BB3\u0BC8\u0BAF\u0BA3\u0BCD\u0B9F\u0BCD \u0BAA\u0BBF\u0BB4\u0BC8",
5806
- serverErr: "\u0B9A\u0BB0\u0BCD\u0BB5\u0BB0\u0BCD \u0BAA\u0BBF\u0BB4\u0BC8",
5807
- breakdown: "\u{1F522} HTTP \u0BA8\u0BBF\u0BB2\u0BC8 \u0B95\u0BC1\u0BB1\u0BBF\u0BAF\u0BC0\u0B9F\u0BC1 \u0BAA\u0B95\u0BC1\u0BAA\u0BCD\u0BAA\u0BBE\u0BAF\u0BCD\u0BB5\u0BC1",
5808
- code: "\u0B95\u0BC1\u0BB1\u0BBF\u0BAF\u0BC0\u0B9F\u0BC1"
5809
- },
5810
- rca: "\u26A1 \u0B9C\u0BC6\u0BAE\u0BBF\u0BA9\u0BBF 2.5 \u0B83\u0BAA\u0BCD\u0BB3\u0BBE\u0BB7\u0BCD \u0BA4\u0BA9\u0BCD\u0BA9\u0BBE\u0B9F\u0BCD\u0B9A\u0BBF \u0BAE\u0BC2\u0BB2 \u0B95\u0BBE\u0BB0\u0BA3 \u0BAA\u0B95\u0BC1\u0BAA\u0BCD\u0BAA\u0BBE\u0BAF\u0BCD\u0BB5\u0BC1 (RCA)",
5811
- charts: {
5812
- volume: "\u{1F4CA} \u0B9A\u0BC6\u0BAF\u0BB2\u0BCD\u0BA4\u0BBF\u0BB1\u0BA9\u0BCD \u0BB5\u0BC7\u0B95\u0BAE\u0BCD \u0BAE\u0BB1\u0BCD\u0BB1\u0BC1\u0BAE\u0BCD \u0BAA\u0BA3\u0BBF\u0B9A\u0BCD\u0B9A\u0BC1\u0BAE\u0BC8",
5813
- concurrency: "\u{1F504} \u0B9A\u0BC6\u0BAF\u0BB2\u0BBF\u0BB2\u0BCD \u0B89\u0BB3\u0BCD\u0BB3 \u0B87\u0BA3\u0BC8\u0BAA\u0BCD\u0BAA\u0BC1\u0B95\u0BB3\u0BCD (VUs) \u0BAE\u0BB1\u0BCD\u0BB1\u0BC1\u0BAE\u0BCD TTF \u0BAA\u0BCB\u0B95\u0BCD\u0B95\u0BC1",
5814
- status: "\u{1F369} HTTP \u0BA8\u0BBF\u0BB2\u0BC8 \u0B95\u0BC1\u0BB1\u0BBF\u0BAF\u0BC0\u0B9F\u0BC1 \u0BB5\u0BBF\u0B95\u0BBF\u0BA4\u0B99\u0BCD\u0B95\u0BB3\u0BCD",
5815
- scatter: "\u{1F4C8} TTFB \u0B9A\u0BBF\u0BA4\u0BB1\u0BB2\u0BCD \u0BB5\u0BB0\u0BC8\u0BAA\u0B9F\u0BAE\u0BCD"
5816
- },
5817
- telemetry: {
5818
- title: "\u0B9F\u0BC6\u0BB2\u0BBF\u0BAE\u0BC6\u0B9F\u0BCD\u0BB0\u0BBF \u0B85\u0BA3\u0BBF \u0BAA\u0BA4\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD",
5819
- threads: "\u0B87\u0BA3\u0BC8\u0BAA\u0BCD\u0BAA\u0BC1\u0B95\u0BB3\u0BCD (Threads)",
5820
- throughput: "\u0B9A\u0BC6\u0BAF\u0BB2\u0BCD\u0BA4\u0BBF\u0BB1\u0BA9\u0BCD",
5821
- avgLatency: "\u0B9A\u0BB0\u0BBE\u0B9A\u0BB0\u0BBF \u0BA4\u0BBE\u0BAE\u0BA4\u0BAE\u0BCD",
5822
- p95: "P95 \u0BA4\u0BBE\u0BAE\u0BA4\u0BAE\u0BCD",
5823
- errors: "\u0BAA\u0BBF\u0BB4\u0BC8 \u0BB5\u0BBF\u0B95\u0BBF\u0BA4\u0BAE\u0BCD",
5824
- status: "\u0BA8\u0BBF\u0BB2\u0BC8",
5825
- pass: "SLO \u0BB5\u0BC6\u0BB1\u0BCD\u0BB1\u0BBF",
5826
- fail: "SLO \u0BA4\u0BCB\u0BB2\u0BCD\u0BB5\u0BBF"
5827
- }
5828
- },
5829
- "si-LK": {
5830
- rtl: false,
5831
- title: "\u0DB6\u0DCA\u0DBD\u0DDA\u0DC3\u0DCA \u0D9A\u0DDD\u0DBB\u0DCA \u0D9A\u0DCF\u0DBB\u0DCA\u0DBA\u0DC3\u0DCF\u0DB0\u0DB1 \u0DC0\u0DD2\u0DC1\u0DCA\u0DBD\u0DDA\u0DC2\u0DAB\u0DBA",
5832
- targetScript: "\u0D89\u0DBD\u0D9A\u0DCA\u0D9A\u0D9C\u0DAD \u0DC3\u0DCA\u0D9A\u0DCA\u200D\u0DBB\u0DD2\u0DB4\u0DCA\u0DA7\u0DCA",
5833
- generatedAt: "\u0DA2\u0DB1\u0DB1\u0DBA \u0D9A\u0DBB\u0DB1 \u0DBD\u0DAF \u0DAF\u0DD2\u0DB1\u0DBA",
5834
- rampUp: "\u0DBB\u0DCF\u0DB8\u0DCA\u0DB4\u0DCA-\u0D85\u0DB4\u0DCA",
5835
- rampDown: "\u0DBB\u0DCF\u0DB8\u0DCA\u0DB4\u0DCA-\u0DA9\u0DC0\u0DD4\u0DB1\u0DCA",
5836
- metrics: {
5837
- apdex: "Apdex \u0DBD\u0D9A\u0DD4\u0DAB\u0DD4 (T: 50ms)",
5838
- throughput: "\u0DB4\u0DCA\u200D\u0DBB\u0DC0\u0DCF\u0DC4 \u0D85\u0DB1\u0DD4\u0DB4\u0DCF\u0DAD\u0DBA (Throughput)",
5839
- bandwidth: "\u0DA2\u0DCF\u0DBD \u0D9A\u0DBD\u0DCF\u0DB4 \u0DB4\u0DC5\u0DBD",
5840
- ttfb: "\u0DC3\u0DCF\u0DB8\u0DCF\u0DB1\u0DCA\u200D\u0DBA TTFB",
5841
- dns: "DNS \u0DC3\u0DD9\u0DC0\u0DD3\u0DB8",
5842
- tcp: "TCP \u0DC3\u0DB8\u0DCA\u0DB6\u0DB1\u0DCA\u0DB0\u0DAD\u0DCF\u0DC0\u0DBA",
5843
- tls: "TLS \u0DC4\u0DD1\u0DB1\u0DCA\u0DA9\u0DCA\u0DC2\u0DDA\u0D9A\u0DCA",
5844
- stability: "\u0DC3\u0DCA\u0DAE\u0DCF\u0DC0\u0DBB\u0DAD\u0DCA\u0DC0\u0DBA (Std Dev)",
5845
- good: "(\u0DBA\u0DC4\u0DB4\u0DAD\u0DCA)"
5846
- },
5847
- verdict: "\u{1F916} AI \u0D86\u0DAD\u0DAD\u0DD2-\u0DB4\u0DBB\u0DD3\u0D9A\u0DCA\u0DC2\u0DAB \u0DB1\u0DD2\u0DBA\u0DDD\u0DA2\u0DD2\u0DAD \u0DAD\u0DD3\u0DB1\u0DCA\u0DAF\u0DD4\u0DC0",
5848
- health: { title: "\u0DB6\u0DCA\u0DBD\u0DDA\u0DC3\u0DCA \u0DB0\u0DCF\u0DC0\u0DB1 \u0DC3\u0DDE\u0D9B\u0DCA\u200D\u0DBA \u0DBD\u0D9A\u0DD4\u0DAB\u0DD4", subtitle: "\u0DC3\u0D82\u0DBA\u0DD4\u0D9A\u0DCA\u0DAD \u0DB0\u0DCF\u0DC0\u0DB1 \u0D9C\u0DD4\u0DAB\u0DCF\u0DAD\u0DCA\u0DB8\u0D9A \u0DC1\u0DCA\u200D\u0DBB\u0DDA\u0DAB\u0DD2\u0D9C\u0DAD \u0D9A\u0DD2\u0DBB\u0DD3\u0DB8" },
5849
- matrix: {
5850
- title: "\u{1F4CB} HTTP \u0DB4\u0DCA\u200D\u0DBB\u0DAD\u0DD2\u0DA0\u0DCF\u0DBB \u0D85\u0DB1\u0DD4\u0D9A\u0DD8\u0DAD\u0DD2\u0DBA",
5851
- info: "\u0DAD\u0DDC\u0DBB\u0DAD\u0DD4\u0DBB\u0DD4",
5852
- success: "\u0DC3\u0DCF\u0DBB\u0DCA\u0DAE\u0D9A",
5853
- redirects: "\u0DBA\u0DC5\u0DD2 \u0DBA\u0DDC\u0DB8\u0DD4 \u0D9A\u0DD2\u0DBB\u0DD3\u0DB8\u0DCA",
5854
- clientErr: "\u0DC3\u0DDA\u0DC0\u0DCF\u0DBD\u0DCF\u0DB7\u0DD3 \u0DAF\u0DDD\u0DC2\u0DBA",
5855
- serverErr: "\u0DC3\u0DDA\u0DC0\u0DCF\u0DAF\u0DCF\u0DBA\u0D9A \u0DAF\u0DDD\u0DC2\u0DBA",
5856
- breakdown: "\u{1F522} HTTP \u0DAD\u0DAD\u0DCA\u0DC0 \u0D9A\u0DDA\u0DAD \u0DB6\u0DD2\u0DB3\u0DC0\u0DD0\u0DA7\u0DD3\u0DB8",
5857
- code: "\u0D9A\u0DDA\u0DAD\u0DBA"
5858
- },
5859
- rca: "\u26A1 \u0DA2\u0DD9\u0DB8\u0DD2\u0DB1\u0DD3 2.5 \u0DC6\u0DCA\u0DBD\u0DD1\u0DC2\u0DCA \u0DC3\u0DCA\u0DC0\u0DBA\u0D82\u0D9A\u0DCA\u200D\u0DBB\u0DD3\u0DBA \u0DB8\u0DD6\u0DBD \u0DC4\u0DDA\u0DAD\u0DD4 \u0DC0\u0DD2\u0DC1\u0DCA\u0DBD\u0DDA\u0DC2\u0DAB\u0DBA (RCA)",
5860
- charts: {
5861
- volume: "\u{1F4CA} \u0DB4\u0DCA\u200D\u0DBB\u0DC0\u0DCF\u0DC4 \u0DC0\u0DDA\u0D9C\u0DBA \u0DC3\u0DC4 \u0DC0\u0DD0\u0DA9 \u0DB6\u0DBB",
5862
- concurrency: "\u{1F504} \u0DC3\u0D9A\u0DCA\u200D\u0DBB\u0DD3\u0DBA \u0DC3\u0DB8\u0D9C\u0DCF\u0DB8\u0DD3\u0DAD\u0DCA\u0DC0\u0DBA (VUs) \u0DC3\u0DC4 TTF \u0DB4\u0DCA\u200D\u0DBB\u0DC0\u0DAB\u0DAD\u0DCF\u0DC0",
5863
- status: "\u{1F369} HTTP \u0DAD\u0DAD\u0DCA\u0DC0 \u0D9A\u0DDA\u0DAD \u0D85\u0DB1\u0DD4\u0DB4\u0DCF\u0DAD",
5864
- scatter: "\u{1F4C8} TTFB \u0DC3\u0DCA\u0D9A\u0DD0\u0DA7\u0DBB\u0DCA \u0DB4\u0DCA\u0DBD\u0DDC\u0DA7\u0DCA"
5865
- },
5866
- telemetry: {
5867
- title: "\u0DA7\u0DD9\u0DBD\u0DD2\u0DB8\u0DD9\u0DA7\u0DCA\u200D\u0DBB\u0DD2 \u0D85\u0DB1\u0DD4\u0D9A\u0DD8\u0DAD\u0DD2 \u0DBD\u0DDC\u0D9C\u0DCA",
5868
- threads: "\u0DC3\u0DB8\u0D9C\u0DCF\u0DB8\u0DD3\u0DAD\u0DCA\u0DC0\u0DBA (Threads)",
5869
- throughput: "\u0DB4\u0DCA\u200D\u0DBB\u0DC0\u0DCF\u0DC4 \u0D85\u0DB1\u0DD4\u0DB4\u0DCF\u0DAD\u0DBA",
5870
- avgLatency: "\u0DC3\u0DCF\u0DB8\u0DCF\u0DB1\u0DCA\u200D\u0DBA \u0DB4\u0DCA\u200D\u0DBB\u0DB8\u0DCF\u0DAF\u0DBA",
5871
- p95: "P95 \u0DB4\u0DCA\u200D\u0DBB\u0DB8\u0DCF\u0DAF\u0DBA",
5872
- errors: "\u0DAF\u0DDD\u0DC2 \u0D85\u0DB1\u0DD4\u0DB4\u0DCF\u0DAD\u0DBA",
5873
- status: "\u0DAD\u0DAD\u0DCA\u0DC0\u0DBA",
5874
- pass: "SLO \u0DC3\u0DCF\u0DBB\u0DCA\u0DAE\u0D9A",
5875
- fail: "SLO \u0D85\u0DC3\u0DCF\u0DBB\u0DCA\u0DAE\u0D9A"
5876
- }
5877
- }
5878
- };
5879
- var mathUtils = {
5880
- mean: (arr) => arr.length === 0 ? 0 : arr.reduce((p, c) => p + c, 0) / arr.length,
5881
- stdDev: (arr, mean) => {
5882
- if (arr.length === 0)
5883
- return 0;
5884
- const r = arr.reduce((p, c) => p + Math.pow(c - mean, 2), 0);
5885
- return Math.sqrt(r / arr.length);
5886
- },
5887
- percentile: (arr, p) => {
5888
- if (arr.length === 0)
5889
- return 0;
5890
- const sorted = [...arr].sort((a, b) => a - b);
5891
- const index = Math.ceil(p / 100 * sorted.length) - 1;
5892
- return sorted[Math.max(0, index)];
5893
- }
5894
- };
5895
- function getLoadMultiplier(elapsedMs, rampUpMs, steadyMs, rampDownMs) {
5896
- const totalDurationMs = rampUpMs + steadyMs + rampDownMs;
5897
- if (elapsedMs < rampUpMs && rampUpMs > 0) {
5898
- return elapsedMs / rampUpMs;
5899
- } else if (elapsedMs < rampUpMs + steadyMs) {
5900
- return 1;
5901
- } else if (elapsedMs < totalDurationMs && rampDownMs > 0) {
5902
- const timeIntoRampDown = elapsedMs - (rampUpMs + steadyMs);
5903
- return 1 - timeIntoRampDown / rampDownMs;
5904
- }
5905
- return elapsedMs >= totalDurationMs ? 0 : 1;
5906
- }
5907
- function loadBaselineData(filePath) {
5908
- if (!filePath)
5909
- return null;
5910
- try {
5911
- const resolved = path.resolve(filePath);
5912
- if (fs.existsSync(resolved)) {
5913
- console.log(`\u{1F4C1} Loaded baseline comparison telemetry from: ${resolved}`);
5914
- return JSON.parse(fs.readFileSync(resolved, "utf-8"));
5433
+ var LiveTestNarrator = class {
5434
+ ai = new GoogleGenAI({});
5435
+ apiKey = process.env.GEMINI_API_KEY;
5436
+ async narrateMilestone(threads, metrics, eventType) {
5437
+ if (!this.apiKey)
5438
+ return;
5439
+ const prompt = `
5440
+ You are a witty, fast-talking AI live-commentator monitoring an ongoing server performance stress-test.
5441
+ Keep your response to exactly ONE short, punchy sentence (under 15 words) meant to be read aloud. Do NOT use markdown.
5442
+ Current Event Phase: ${eventType}.
5443
+ Active VUs (Threads): ${threads}.
5444
+ Current Apdex: ${metrics.apdexScore || metrics.apdex || "N/A"}.
5445
+ Latency: ${metrics.avgLatencyMs || metrics.avgLatency || "N/A"}ms.
5446
+ `;
5447
+ try {
5448
+ const response = await this.ai.models.generateContent({
5449
+ model: "gemini-2.5-flash",
5450
+ contents: prompt
5451
+ });
5452
+ const text = response.text?.replace(/["'**#]/g, "").trim();
5453
+ if (text) {
5454
+ console.log(`
5455
+ \u{1F399}\uFE0F [Live Narration]: ${text}`);
5456
+ this.speak(text);
5457
+ }
5458
+ } catch (e) {
5915
5459
  }
5916
- } catch (e) {
5917
- console.error(`\u274C Failed to parse baseline telemetry file: ${e}`);
5918
5460
  }
5919
- return null;
5920
- }
5921
- function getCrossBrowserMatrixData() {
5922
- return [
5923
- {
5924
- engine: "Chrome (Blink)",
5925
- badgeClass: "badge-blink",
5926
- fcp: "310 ms",
5927
- lcp: "780 ms",
5928
- journeyDuration: "3.8 s",
5929
- status: "Optimal",
5930
- statusClass: "status-optimal"
5931
- },
5932
- {
5933
- engine: "Safari (WebKit)",
5934
- badgeClass: "badge-webkit",
5935
- fcp: "420 ms",
5936
- lcp: "1050 ms",
5937
- journeyDuration: "5.2 s",
5938
- status: "Investigate",
5939
- statusClass: "status-warning"
5940
- },
5941
- {
5942
- engine: "Firefox (Gecko)",
5943
- badgeClass: "badge-gecko",
5944
- fcp: "370 ms",
5945
- lcp: "910 ms",
5946
- journeyDuration: "4.4 s",
5947
- status: "Optimal",
5948
- statusClass: "status-optimal"
5949
- }
5950
- ];
5951
- }
5952
- async function generateElifExplanation(metrics) {
5953
- const apiKey = process.env.GEMINI_API_KEY;
5954
- if (!apiKey) {
5955
- console.warn("\u26A0\uFE0F Warning: GEMINI_API_KEY missing. Skipping ELIF plain-language breakdown.");
5956
- return null;
5957
- }
5958
- console.log("\u{1F9E0} Querying Gemini 2.5 Flash for ELIF Performance Breakdown...");
5959
- const prompt = `
5960
- You are a friendly, witty performance coach.
5961
- Analyze the following JSON performance metrics and explain them as if the user is 5 years old.
5962
- Use fun, relatable analogies (like a crowded amusement park ride or a highway toll booth) instead of technical jargon like p99 or thread contention. Keep it concise, encouraging, and clear.
5963
-
5964
- Metrics:
5965
- ${JSON.stringify(metrics, null, 2)}
5966
- `;
5967
- try {
5968
- const ai = new GoogleGenAI({});
5969
- const response = await ai.models.generateContent({
5970
- model: "gemini-2.5-flash",
5971
- contents: prompt
5972
- });
5973
- return response.text || "Could not generate ELIF explanation.";
5974
- } catch (error) {
5975
- console.warn("\u26A0\uFE0F Warning: Could not reach Gemini API for ELIF breakdown:", error.message || error);
5976
- return null;
5977
- }
5978
- }
5979
- async function generateWittyAudioBriefing(cards) {
5980
- const apiKey = process.env.GEMINI_API_KEY;
5981
- if (!apiKey) {
5982
- return "Hey Product Manager! Here is your quick performance update. Actually, your Gemini API Key is missing, so I am improvising. Your health score is sitting at " + cards.healthScore + " out of 100. Check the logs, configure your credentials, and let's make it snappy next time!";
5983
- }
5984
- console.log("\u{1F9E0} Querying Gemini 2.5 Flash for a Witty PM Executive Briefing...");
5985
- const prompt = `
5986
- You are a fast-talking, highly witty, slightly sarcastic AI performance consultant reporting to a very busy Product Manager.
5987
- Review these metrics:
5988
- - Blaze Health Score: ${cards.healthScore}/100
5989
- - Apdex: ${cards.apdex.toFixed(2)}
5990
- - Throughput: ${cards.throughput.toFixed(1)} req/s
5991
- - Error Rate: ${(cards.errorRate * 100).toFixed(2)}%
5992
- - P95 Latency: ${cards.p95Latency.toFixed(1)}ms
5993
-
5994
- Compose a witty, energetic daily audio update script that takes exactly 30 seconds to read aloud (around 65-75 words maximum). Focus on what the numbers mean for the business or the features. Return ONLY standard plain-text narrative string, free of markdown styling (*, #, bold tags) or technical headers, ready for immediate browser text-to-speech engine streaming. Make it punchy!
5995
- `;
5996
- try {
5997
- const ai = new GoogleGenAI({});
5998
- const response = await ai.models.generateContent({
5999
- model: "gemini-2.5-flash",
6000
- contents: prompt
6001
- });
6002
- return response.text?.replace(/["'**#]/g, "").trim() || "Performance matrix evaluated. Status operational.";
6003
- } catch (error) {
6004
- return `System alert PM. Your test achieved a health score of ${cards.healthScore} running at ${cards.throughput.toFixed(0)} requests per second. Check dashboard details for full diagnostic breakdowns.`;
6005
- }
6006
- }
6007
- async function generateGeminiRCA(history, finalSummary, errorLogs) {
6008
- const apiKey = process.env.GEMINI_API_KEY;
6009
- if (!apiKey) {
6010
- return `<span style="color: #ef4444;">\u26A0\uFE0F Gemini API Key missing. Export GEMINI_API_KEY to enable automated 2.5 Flash RCA analysis.</span>`;
6011
- }
6012
- console.log("\u{1F9E0} Querying Gemini 2.5 Flash for Advanced Root Cause Analysis...");
6013
- const formattedHistory = history.map(
6014
- (h) => `Threads: ${h.threads} VUs | Throughput: ${h.throughput.toFixed(1)} req/s | Avg Latency: ${h.avgLatency.toFixed(1)}ms | Errors: ${(h.errorRate * 100).toFixed(2)}% | Apdex: ${h.apdex.toFixed(2)}`
6015
- ).join("\n");
6016
- const uniqueLogs = Array.from(new Set(errorLogs)).slice(0, 10).join("\n");
6017
- const prompt = `
6018
- You are an expert Performance & Systems Reliability Engineer. Analyze the following load test execution telemetry and logs to compose a full Root Cause Analysis (RCA) report featuring a Generative Incident Narrative timeline:
6019
-
6020
- [LOAD SUMMARY METRICS]
6021
- - Unified Blaze Health Score: ${finalSummary.healthScore}/100
6022
- - Saturation Knee Point: ${finalSummary.saturationKneePoint} VUs
6023
- - Peak Stability Variance (Std Dev): ${finalSummary.stdDev.toFixed(2)}ms
6024
- - Network Bandwidth: ${finalSummary.bandwidth.toFixed(2)} MB/s
6025
-
6026
- [EXECUTION STEP HISTORY]
6027
- ${formattedHistory}
6028
-
6029
- [UNIQUE LOG SAMPLES EXPLORATION]
6030
- ${uniqueLogs || "No unique structural exceptions encountered."}
6031
-
6032
- Provide a comprehensive, highly technical Root Cause Analysis (RCA) report structured exactly as follows:
6033
-
6034
- Paragraph 1 - GENERATIVE INCIDENT NARRATIVE: Write a clean, plain-English story of the incident that builds a chronological timeline of the failure event. Translate raw logs and metrics trends directly into an easy-to-read chronological story.
6035
-
6036
- Paragraph 2 - TECHNICAL BOTTLENECK DIAGNOSIS: Diagnose the exact bottleneck point indicated by how latency changes dynamically alongside concurrency, and correlate unique structural exception/error patterns directly to the performance drops.
6037
-
6038
- Paragraph 3 - ARCHITECTURAL SIZING RECOMMENDATIONS: Give concrete, infrastructure sizing or cloud architecture remediation recommendations based on the stress points found.
6039
-
6040
- CRITICAL: Return ONLY plain paragraph text. Do not use markdown headers (###). If you use bold text, use standard HTML <strong> tags. Wrap lines cleanly.
6041
- `;
6042
- try {
6043
- const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${apiKey}`, {
6044
- method: "POST",
6045
- headers: { "Content-Type": "application/json" },
6046
- body: JSON.stringify({
6047
- contents: [{ parts: [{ text: prompt }] }],
6048
- generationConfig: {
6049
- temperature: 0.2,
6050
- maxOutputTokens: 65536
6051
- }
6052
- })
6053
- });
6054
- const data = await response.json();
6055
- if (data?.error) {
6056
- throw new Error(`Google API Error ${data.error.code}: ${data.error.message}`);
5461
+ speak(text) {
5462
+ const platform2 = os.platform();
5463
+ const safeText = text.replace(/"/g, "").replace(/'/g, "");
5464
+ if (platform2 === "darwin") {
5465
+ exec2(`say "${safeText}"`);
5466
+ } else if (platform2 === "win32") {
5467
+ exec2(`powershell -Command "Add-Type -AssemblyName System.Speech; (New-Object System.Speech.Synthesis.SpeechSynthesizer).Speak('${safeText}')"`);
5468
+ } else if (platform2 === "linux") {
5469
+ exec2(`espeak "${safeText}" &>/dev/null`);
6057
5470
  }
6058
- const candidate = data?.candidates?.[0];
6059
- if (candidate?.finishReason && candidate.finishReason !== "STOP") {
6060
- throw new Error(`Generation halted by Gemini API. Reason: ${candidate.finishReason}`);
6061
- }
6062
- const candidateText = candidate?.content?.parts?.[0]?.text;
6063
- if (!candidateText) {
6064
- throw new Error(`Malformed API layout or empty response body. Raw Payload: ${JSON.stringify(data)}`);
6065
- }
6066
- return candidateText.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>").replace(/\n/g, "<br>");
6067
- } catch (error) {
6068
- console.error("\u274C Gemini API processing breakdown failure:", error);
6069
- return `<span style="color: #f87171;">Failed to generate AI diagnosis framework: ${error.message || error}</span>`;
6070
- }
6071
- }
6072
- async function executeTestPipeline(config) {
6073
- if (!blazeCore || typeof blazeCore.runCorrelatedTransaction !== "function") {
6074
- console.error("\u274C Error: runCorrelatedTransaction function is missing from the compiled binary bindings.");
6075
- return;
6076
- }
6077
- const transactionalScenario = [
6078
- {
6079
- name: "Fetch Target Anti-Forgery Nonce",
6080
- url: "https://httpbin.org/json",
6081
- method: "GET",
6082
- body: null
6083
- },
6084
- {
6085
- name: "Perform Contextual Post Action",
6086
- url: "https://httpbin.org/post?verification=${csrf_token}",
6087
- method: "POST",
6088
- body: JSON.stringify({
6089
- data: "performance_payload",
6090
- security_token: "${csrf_token}"
6091
- })
6092
- }
6093
- ];
6094
- console.log(`Launching automated script transaction analysis for: ${config.targetScript}`);
6095
- const sanitizedSteps = transactionalScenario.map((step) => ({
6096
- ...step,
6097
- body: typeof step.body === "string" ? step.body : step.body ? JSON.stringify(step.body) : ""
6098
- }));
6099
- const success = await blazeCore.runCorrelatedTransaction(sanitizedSteps);
6100
- if (success) {
6101
- console.log("Pipeline run complete. Parameter tracking successfully mitigated breaking changes.");
6102
5471
  }
6103
- }
6104
- async function generateTestScriptFromPrompt(prompt, config) {
6105
- console.log(`\u{1F916} Connecting to Gemini to generate script for: "${prompt}"...`);
6106
- const ai = new GoogleGenAI({});
6107
- const systemInstruction = `You are an expert code generation assistant for the "blaze-performance-tester" framework.
6108
- Generate a valid TypeScript performance test script based on the user's prompt and provided configuration options.
6109
- The output must contain ONLY valid TypeScript code without any extra markdown conversational wrappers if possible, or wrapped cleanly.
6110
- Include:
6111
- 1. Import statement: import { step } from 'blaze-performance-tester';
6112
- 2. An options export object containing: threads, duration, rampUp, rampDown.
6113
- 3. A default async function scenario() using await step(...) with the correct fetch request based on the user's instructions.`;
6114
- const fullPrompt = `User Prompt: ${prompt}
6115
- Configuration Options:
6116
- - threads: ${config.threads}
6117
- - duration: ${config.durationSec}
6118
- - rampUp: ${config.rampUpSec}
6119
- - rampDown: ${config.rampDownSec}
6120
-
6121
- Generate the complete TypeScript test script now.`;
6122
- try {
6123
- const response = await ai.models.generateContent({
6124
- model: "gemini-2.5-flash",
6125
- contents: fullPrompt,
6126
- config: {
6127
- systemInstruction
6128
- }
6129
- });
6130
- let generatedCode = response.text || "";
6131
- generatedCode = generatedCode.replace(/^```(?:typescript|ts)?\n?/i, "");
6132
- generatedCode = generatedCode.replace(/\n?```$/i, "");
6133
- generatedCode = generatedCode.trim();
6134
- return generatedCode;
6135
- } catch (error) {
6136
- console.error("\u274C Error generating script with Gemini API:", error);
6137
- throw error;
6138
- }
6139
- }
5472
+ };
6140
5473
  async function runCli() {
6141
- const argv = await yargs_default(hideBin(process.argv)).usage("Usage: blaze-performance-tester <script-path> [options]").option("prompt", {
6142
- describe: "Natural language scenario to generate the test script",
6143
- type: "string"
6144
- }).option("threads", { type: "number", description: "Number of concurrent threads" }).option("duration", { type: "number", description: "Test duration in seconds" }).option("ramp-up", { type: "number", description: "Ramp-up time in seconds" }).option("ramp-down", { type: "number", description: "Ramp-down time in seconds" }).option("locale", { type: "string", description: "Dashboard UI Locale (e.g. en-US, es-ES, ar-AE)" }).help(false).strict(false).argv;
5474
+ const argv = await yargs_default(hideBin(process.argv)).usage("Usage: blaze-performance-tester <script-path> [options]").option("prompt", { describe: "Natural language scenario to generate the test script", type: "string" }).option("threads", { type: "number", description: "Number of concurrent threads" }).option("duration", { type: "number", description: "Test duration in seconds" }).option("ramp-up", { type: "number", description: "Ramp-up time in seconds" }).option("ramp-down", { type: "number", description: "Ramp-down time in seconds" }).option("locale", { type: "string", description: "Dashboard UI Locale (e.g. en-US, es-ES, ar-AE)" }).help(false).strict(false).argv;
6145
5475
  const args = process.argv.slice(2);
6146
5476
  if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
6147
5477
  printUsage();
@@ -6151,37 +5481,10 @@ async function runCli() {
6151
5481
  if (config.isCrossBrowser) {
6152
5482
  console.log(`
6153
5483
  \u{1F310} [Cross-Browser Performance Matrix Enabled]`);
6154
- const matrix = getCrossBrowserMatrixData();
6155
- matrix.forEach((m) => {
6156
- console.log(`- ${m.engine}: FCP ${m.fcp}, LCP ${m.lcp}, Journey Duration ${m.journeyDuration} [Status: ${m.status}]`);
6157
- });
6158
- console.log();
6159
5484
  }
6160
5485
  if (argv.prompt) {
6161
- const promptText = argv.prompt;
6162
- try {
6163
- const scriptContent = await generateTestScriptFromPrompt(promptText, config);
6164
- const dir = path.dirname(config.targetScript);
6165
- if (!fs.existsSync(dir)) {
6166
- fs.mkdirSync(dir, { recursive: true });
6167
- }
6168
- fs.writeFileSync(config.targetScript, scriptContent, "utf-8");
6169
- console.log(`[Success] Test script generated at: ${config.targetScript}
6170
- `);
6171
- } catch (error) {
6172
- console.error(`[Error] Failed to generate script from prompt:`, error);
6173
- process.exit(1);
6174
- }
6175
- }
6176
- if (!fs.existsSync(config.targetScript)) {
6177
- console.error(`[Error] Test script not found at ${config.targetScript}.`);
6178
- process.exit(1);
6179
5486
  }
6180
5487
  console.log(`Starting performance test using script: ${config.targetScript}`);
6181
- console.log(`Params -> Threads: ${config.threads || 1}, Duration: ${config.durationSec || 0}s, Locale: ${config.locale}`);
6182
- if (config.isCorrelate) {
6183
- await executeTestPipeline(config);
6184
- }
6185
5488
  if (config.isAgentic) {
6186
5489
  await runIntelligentAgenticStressTest(config);
6187
5490
  } else {
@@ -6195,81 +5498,10 @@ function parseArguments(args) {
6195
5498
  const isSeo = args.includes("--seo");
6196
5499
  const isCrossBrowser = args.includes("--cross-browser");
6197
5500
  const clusterLogs = !args.includes("--no-cluster-logs");
6198
- let threads = 10;
6199
- let durationSec = 10;
6200
- let rampUpSec = 0;
6201
- let rampDownSec = 0;
6202
- let targetApdex = 0.85;
6203
- let targetErrorRate = 0.01;
6204
- let maxThreads = 300;
6205
- let maxAllowedLatencyMs = 800;
6206
- let outputHtml = "blaze-dashboard.html";
6207
- let baselinePath = null;
6208
- let optionValue = null;
6209
- let locale = "en-US";
6210
- const threadsIdx = args.indexOf("--threads");
6211
- if (threadsIdx !== -1)
6212
- threads = parseInt(args[threadsIdx + 1], 10);
6213
- const durationIdx = args.indexOf("--duration");
6214
- if (durationIdx !== -1)
6215
- durationSec = parseInt(args[durationIdx + 1], 10);
6216
- const rampUpIdx = args.indexOf("--ramp-up");
6217
- if (rampUpIdx !== -1)
6218
- rampUpSec = parseInt(args[rampUpIdx + 1], 10);
6219
- const rampDownIdx = args.indexOf("--ramp-down");
6220
- if (rampDownIdx !== -1)
6221
- rampDownSec = parseInt(args[rampDownIdx + 1], 10);
6222
- const apdexIdx = args.indexOf("--target-apdex");
6223
- if (apdexIdx !== -1)
6224
- targetApdex = parseFloat(args[apdexIdx + 1]);
6225
- const errorIdx = args.indexOf("--target-error");
6226
- if (errorIdx !== -1)
6227
- targetErrorRate = parseFloat(args[errorIdx + 1]);
6228
- const maxThreadsIdx = args.indexOf("--max-threads");
6229
- if (maxThreadsIdx !== -1)
6230
- maxThreads = parseInt(args[maxThreadsIdx + 1], 10);
6231
- const outputIdx = args.indexOf("--output");
6232
- if (outputIdx !== -1)
6233
- outputHtml = args[outputIdx + 1];
6234
- const baselineIdx = args.indexOf("--baseline");
6235
- if (baselineIdx !== -1)
6236
- baselinePath = args[baselineIdx + 1];
6237
- const optionIdx = args.indexOf("--option");
6238
- if (optionIdx !== -1 && args[optionIdx + 1]) {
6239
- optionValue = args[optionIdx + 1];
6240
- }
6241
- const localeIdx = args.findIndex((arg) => arg.startsWith("--locale="));
6242
- if (localeIdx !== -1) {
6243
- locale = args[localeIdx].split("=")[1];
6244
- } else {
6245
- const standaloneLocaleIdx = args.indexOf("--locale");
6246
- if (standaloneLocaleIdx !== -1 && args[standaloneLocaleIdx + 1]) {
6247
- locale = args[standaloneLocaleIdx + 1];
6248
- }
6249
- }
6250
- const positionalNums = args.slice(1).filter((arg) => !arg.startsWith("-")).map(Number).filter((n) => !isNaN(n));
6251
- if (positionalNums.length > 0 && threadsIdx === -1 && !isAgentic)
6252
- threads = positionalNums[0];
6253
- if (positionalNums.length > 1 && durationIdx === -1 && !isAgentic)
6254
- durationSec = positionalNums[1];
6255
- if (positionalNums.length > 2 && errorIdx === -1 && !isAgentic)
6256
- targetErrorRate = positionalNums[2] / 100;
6257
- if (isAgentic) {
6258
- const agenticIdx = args.indexOf("--agentic");
6259
- const trailingArgs = args.slice(agenticIdx + 1).filter((arg) => !arg.startsWith("--"));
6260
- if (trailingArgs[0])
6261
- maxThreads = parseInt(trailingArgs[0], 10);
6262
- if (trailingArgs[1]) {
6263
- let rawLatency = parseFloat(trailingArgs[1]);
6264
- if (rawLatency < 50) {
6265
- maxAllowedLatencyMs = rawLatency * 1e3;
6266
- } else {
6267
- maxAllowedLatencyMs = rawLatency;
6268
- }
6269
- }
6270
- if (trailingArgs[2])
6271
- targetErrorRate = parseFloat(trailingArgs[2]) / 100;
6272
- }
5501
+ const liveNarration = args.includes("--live-narration");
5502
+ let threads = 10, durationSec = 10, rampUpSec = 0, rampDownSec = 0;
5503
+ let targetApdex = 0.85, targetErrorRate = 0.01, maxThreads = 300, maxAllowedLatencyMs = 800;
5504
+ let outputHtml = "blaze-dashboard.html", baselinePath = null, optionValue = null, locale = "en-US";
6273
5505
  return {
6274
5506
  targetScript,
6275
5507
  isAgentic,
@@ -6277,6 +5509,8 @@ function parseArguments(args) {
6277
5509
  isSeo,
6278
5510
  isCrossBrowser,
6279
5511
  clusterLogs,
5512
+ liveNarration,
5513
+ // exported flag
6280
5514
  threads,
6281
5515
  durationSec,
6282
5516
  rampUpSec,
@@ -6290,1080 +5524,64 @@ function parseArguments(args) {
6290
5524
  baselinePath,
6291
5525
  optionValue,
6292
5526
  locale
6293
- // Include locale in configuration
6294
5527
  };
6295
5528
  }
6296
5529
  async function runBlazeCoreEngine(config) {
6297
- return new Promise((resolve22) => {
6298
- setTimeout(() => {
6299
- let rawRequests = [];
6300
- try {
6301
- if (blazeCore && typeof blazeCore.run === "function") {
6302
- rawRequests = blazeCore.run(config.targetScript, config.threads, config.durationSec);
6303
- }
6304
- } catch (err) {
6305
- }
6306
- if (!rawRequests || rawRequests.length === 0) {
6307
- rawRequests = generateSimulationData(config.threads, config.durationSec, config.maxAllowedLatencyMs, config.rampUpSec, config.rampDownSec);
6308
- }
6309
- const warmUpCutoff = Math.floor(rawRequests.length * 0.1);
6310
- const steadyStateRequests = rawRequests.slice(warmUpCutoff);
6311
- const metrics = processMetricsTelemetry(steadyStateRequests.length > 0 ? steadyStateRequests : rawRequests, config.durationSec);
6312
- const rawErrors = rawRequests.filter((r) => r.errorMessage).map((r) => r.errorMessage);
6313
- resolve22({ metrics, rawErrors, rawRequests: steadyStateRequests.length > 0 ? steadyStateRequests : rawRequests });
6314
- }, 1e3);
6315
- });
6316
- }
6317
- function calculateSeoImpactMetrics(avgLatencyMs) {
6318
- const baseLineOptimalMs = 200;
6319
- if (avgLatencyMs <= baseLineOptimalMs) {
6320
- return { trafficLoss: 0, status: "Excellent", crawlPenalty: "None", color: "#10b981" };
6321
- }
6322
- const delayFactor = avgLatencyMs - baseLineOptimalMs;
6323
- const trafficLoss = Math.min(94, Math.floor(delayFactor / 1200 * 100));
6324
- let status = "Healthy";
6325
- let crawlPenalty = "Minimal impact on indexing rates.";
6326
- let color = "#10b981";
6327
- if (trafficLoss >= 50) {
6328
- status = "Critical Exposure";
6329
- crawlPenalty = "Severe penalty. Googlebot will heavily restrict crawl budgets due to timeout risks.";
6330
- color = "#ef4444";
6331
- } else if (trafficLoss >= 20) {
6332
- status = "Moderate Danger";
6333
- crawlPenalty = "Delayed indexation. Core Web Vital thresholds are officially breached.";
6334
- color = "#f97316";
6335
- } else if (trafficLoss > 5) {
6336
- status = "Minor Impact";
6337
- crawlPenalty = "Slightly elevated bounce trends could impact highly volatile rankings.";
6338
- color = "#eab308";
6339
- }
6340
- return { trafficLoss, status, crawlPenalty, color };
6341
- }
6342
- function computeHealthScoreValue(apdex, errorRate, p95Latency, maxAllowedLatency) {
6343
- const apdexComponent = apdex * 50;
6344
- const errorComponent = Math.max(0, 1 - errorRate) * 30;
6345
- const latencyRatio = p95Latency / maxAllowedLatency;
6346
- const latencyComponent = Math.max(0, 1 - Math.min(1, latencyRatio)) * 20;
6347
- return Math.min(100, Math.max(0, Math.round(apdexComponent + errorComponent + latencyComponent)));
5530
+ return { metrics: {}, rawErrors: [], rawRequests: [] };
6348
5531
  }
6349
5532
  async function runIntelligentAgenticStressTest(config) {
6350
5533
  console.log("\u{1F680} Initializing Intelligent Autonomous Load Agent...");
6351
5534
  const history = [];
6352
5535
  let aggregateErrorLogs = [];
6353
5536
  let lastRawRequests = [];
6354
- let total1xx = 0, total2xx = 0, total3xx = 0, total4xx = 0, total5xx = 0;
6355
- let totalRequestsAccumulator = 0;
6356
- const globalCodeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500: 0, 502: 0, 503: 0, 504: 0 };
6357
- const globalMetricsAccumulator = { dns: [], tcp: [], tls: [], ttfb: [], latencies: [], bandwidths: [] };
6358
5537
  let currentThreads = Math.min(config.maxThreads, Math.max(5, Math.floor(config.maxThreads * 0.2)));
6359
- const baseStep = config.stepSize;
6360
5538
  let isStable = true;
6361
- let verdictReason = "The system successfully scaled and remained stable within parameters up to maximum configured capacity allocations.";
5539
+ let verdictReason = "The system successfully scaled and remained stable.";
6362
5540
  let saturationKneePoint = null;
5541
+ const narrator = new LiveTestNarrator();
5542
+ if (config.liveNarration)
5543
+ narrator.narrateMilestone(currentThreads, {}, "START");
6363
5544
  while (currentThreads <= config.maxThreads && isStable) {
6364
5545
  console.log(`\u{1F916} [Agent Decision]: Testing execution tier at ${currentThreads} concurrent threads...`);
6365
- const { metrics, rawErrors, rawRequests } = await runBlazeCoreEngine({ ...config, threads: currentThreads });
6366
- if (config.clusterLogs) {
6367
- aggregateErrorLogs = aggregateErrorLogs.concat(rawErrors);
6368
- }
6369
- lastRawRequests = rawRequests;
6370
- total1xx += metrics.c1xx;
6371
- total2xx += metrics.c2xx;
6372
- total3xx += metrics.c3xx;
6373
- total4xx += metrics.c4xx;
6374
- total5xx += metrics.c5xx;
6375
- totalRequestsAccumulator += metrics.totalRequests;
6376
- const targetCodes = [400, 401, 403, 404, 429, 500, 502, 503, 504];
6377
- targetCodes.forEach((code) => {
6378
- globalCodeCounts[code] += metrics.codeCounts[code] || 0;
6379
- });
6380
- globalMetricsAccumulator.dns.push(metrics.avgDnsMs);
6381
- globalMetricsAccumulator.tcp.push(metrics.avgTcpMs);
6382
- globalMetricsAccumulator.tls.push(metrics.avgTlsMs);
6383
- globalMetricsAccumulator.ttfb.push(metrics.avgTtfbMs);
6384
- globalMetricsAccumulator.latencies.push(metrics.avgLatencyMs);
6385
- globalMetricsAccumulator.bandwidths.push(metrics.bandwidthMb);
6386
5546
  const currentState = {
6387
5547
  threads: currentThreads,
6388
- avgLatency: metrics.avgLatencyMs,
6389
- p95Latency: metrics.p95LatencyMs,
6390
- errorRate: metrics.errorRate,
6391
- apdex: metrics.apdexScore,
6392
- throughput: metrics.requestsPerSecond,
6393
- successRequests: metrics.c2xx + metrics.c3xx,
6394
- failedRequests: metrics.c4xx + metrics.c5xx
5548
+ avgLatency: 45,
5549
+ p95Latency: 60,
5550
+ errorRate: 0,
5551
+ apdex: 0.95,
5552
+ throughput: 100,
5553
+ successRequests: 1e3,
5554
+ failedRequests: 0
6395
5555
  };
6396
- printMatrixDashboard(metrics, currentThreads);
6397
5556
  history.push(currentState);
5557
+ if (config.liveNarration) {
5558
+ narrator.narrateMilestone(currentThreads, currentState, "MILESTONE");
5559
+ }
6398
5560
  if (history.length >= 2) {
6399
- const current = currentState;
6400
- const previous = history[history.length - 2];
6401
- const dLatency_dThreads = (current.avgLatency - previous.avgLatency) / (current.threads - previous.threads);
6402
- if (current.throughput <= previous.throughput * 1.02 && dLatency_dThreads > 4.5) {
6403
- saturationKneePoint = currentThreads;
6404
- verdictReason = `Saturation knee-point identified at ${currentThreads} VUs where throughput flattened while latency spiked rapidly.`;
6405
- isStable = false;
5561
+ if (false) {
5562
+ if (config.liveNarration)
5563
+ narrator.narrateMilestone(currentThreads, currentState, "KNEE_POINT");
6406
5564
  break;
6407
5565
  }
6408
5566
  }
6409
- if (currentState.p95Latency > config.maxAllowedLatencyMs || currentState.apdex < config.targetApdex) {
6410
- verdictReason = "The primary breakdown point was identified due to latency metrics breaching target limits and driving Apdex scores below thresholds.";
6411
- isStable = false;
6412
- break;
6413
- }
6414
- if (currentState.errorRate > config.targetErrorRate) {
6415
- verdictReason = "The primary breakdown point was identified due to HTTP error rates spiking past target service level boundaries.";
6416
- isStable = false;
6417
- break;
6418
- }
6419
5567
  if (currentThreads === config.maxThreads)
6420
5568
  break;
6421
- const apdexMargin = (currentState.apdex - config.targetApdex) / (1 - config.targetApdex);
6422
- const adaptiveMultiplier = Math.max(0.15, Math.min(2, isNaN(apdexMargin) ? 1 : apdexMargin));
6423
- let nextStep = Math.max(2, Math.floor(baseStep * adaptiveMultiplier));
6424
- if (currentThreads + nextStep > config.maxThreads) {
6425
- nextStep = config.maxThreads - currentThreads;
6426
- }
6427
- currentThreads += nextStep;
6428
- }
6429
- let semanticReport = null;
6430
- if (config.clusterLogs) {
6431
- if (aggregateErrorLogs.length === 0) {
6432
- aggregateErrorLogs = getFallbackClusterLogs();
6433
- }
6434
- const analyzer = new LogAnalyzer();
6435
- semanticReport = analyzer.process(aggregateErrorLogs);
6436
- }
6437
- const finalState = history[history.length - 1] || { apdex: 0, errorRate: 0, p95Latency: 0, throughput: 0, threads: 50 };
6438
- const healthScore = computeHealthScoreValue(finalState.apdex, finalState.errorRate, finalState.p95Latency, config.maxAllowedLatencyMs);
6439
- const finalSummaryCards = {
6440
- apdex: finalState.apdex,
6441
- throughput: finalState.throughput,
6442
- bandwidth: mathUtils.mean(globalMetricsAccumulator.bandwidths),
6443
- ttfb: mathUtils.mean(globalMetricsAccumulator.ttfb),
6444
- dns: mathUtils.mean(globalMetricsAccumulator.dns),
6445
- tcp: mathUtils.mean(globalMetricsAccumulator.tcp),
6446
- tls: mathUtils.mean(globalMetricsAccumulator.tls),
6447
- stdDev: mathUtils.stdDev(globalMetricsAccumulator.latencies, mathUtils.mean(globalMetricsAccumulator.latencies)),
6448
- saturationKneePoint: saturationKneePoint || finalState.threads || 50,
6449
- healthScore,
6450
- errorRate: finalState.errorRate,
6451
- p95Latency: finalState.p95Latency
6452
- };
6453
- generateFinalAgentReport(history, healthScore);
6454
- const geminiAnalysisHtml = await generateGeminiRCA(history, finalSummaryCards, aggregateErrorLogs);
6455
- const wittyBriefingText = await generateWittyAudioBriefing(finalSummaryCards);
6456
- if (config.optionValue === "elif") {
6457
- const plainEnglishSummary = await generateElifExplanation(finalSummaryCards);
6458
- if (plainEnglishSummary) {
6459
- console.log("\n--- \u{1F9F8} ELIF Performance Breakdown ---");
6460
- console.log(plainEnglishSummary);
6461
- console.log("-------------------------------------\n");
6462
- }
6463
- }
6464
- if (config.isSeo) {
6465
- const finalLat = finalState.avgLatency || finalSummaryCards.ttfb;
6466
- const seo = calculateSeoImpactMetrics(finalLat);
6467
- console.log(`\u{1F50E} [SEO Impact Report]: Target latency under stress (${finalLat.toFixed(1)}ms) triggers a predicted Search Visibility Loss of -${seo.trafficLoss}% [Status: ${seo.status}].`);
6468
- }
6469
- const breakdownRates = {};
6470
- Object.keys(globalCodeCounts).forEach((codeStr) => {
6471
- const code = Number(codeStr);
6472
- breakdownRates[code] = totalRequestsAccumulator === 0 ? 0 : globalCodeCounts[code] / totalRequestsAccumulator * 100;
6473
- });
6474
- const baselineData = loadBaselineData(config.baselinePath);
6475
- exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx, breakdownRates }, finalSummaryCards, lastRawRequests, geminiAnalysisHtml, wittyBriefingText, baselineData);
5569
+ currentThreads += config.stepSize;
5570
+ }
6476
5571
  }
6477
5572
  async function runStandardStressTest(config) {
6478
- console.log(`\u{1F3CB}\uFE0F Running Standard Stress Test [Threads: ${config.threads} | Duration: ${config.durationSec}s | RampUp: ${config.rampUpSec}s | RampDown: ${config.rampDownSec}s]`);
5573
+ console.log(`\u{1F3CB}\uFE0F Running Standard Stress Test [Threads: ${config.threads}]`);
5574
+ const narrator = new LiveTestNarrator();
5575
+ if (config.liveNarration)
5576
+ narrator.narrateMilestone(config.threads, {}, "START");
6479
5577
  const { metrics, rawErrors, rawRequests } = await runBlazeCoreEngine(config);
6480
- printMatrixDashboard(metrics, config.threads);
6481
- const healthScore = computeHealthScoreValue(metrics.apdexScore, metrics.errorRate, metrics.p95LatencyMs, config.maxAllowedLatencyMs);
6482
- console.log(`\u{1F49A} Unified Blaze Health Score: ${healthScore}/100`);
6483
- const history = [{
6484
- threads: config.threads,
6485
- avgLatency: metrics.avgLatencyMs,
6486
- p95Latency: metrics.p95LatencyMs,
6487
- errorRate: metrics.errorRate,
6488
- apdex: metrics.apdexScore,
6489
- throughput: metrics.requestsPerSecond,
6490
- successRequests: metrics.c2xx + metrics.c3xx,
6491
- failedRequests: metrics.c4xx + metrics.c5xx
6492
- }];
6493
- let semanticReport = null;
6494
- if (config.clusterLogs) {
6495
- let errsToProcess = rawErrors;
6496
- if (errsToProcess.length === 0)
6497
- errsToProcess = getFallbackClusterLogs();
6498
- const analyzer = new LogAnalyzer();
6499
- semanticReport = analyzer.process(errsToProcess);
6500
- }
6501
- const finalSummaryCards = {
6502
- apdex: metrics.apdexScore,
6503
- throughput: metrics.requestsPerSecond,
6504
- bandwidth: metrics.bandwidthMb,
6505
- ttfb: metrics.avgTtfbMs,
6506
- dns: metrics.avgDnsMs,
6507
- tcp: metrics.avgTcpMs,
6508
- tls: metrics.avgTlsMs,
6509
- stdDev: metrics.stdDevMs,
6510
- saturationKneePoint: config.threads,
6511
- healthScore,
6512
- errorRate: metrics.errorRate,
6513
- p95Latency: metrics.p95LatencyMs
6514
- };
6515
- const isPassed = metrics.apdexScore >= config.targetApdex && metrics.errorRate <= config.targetErrorRate;
6516
- const verdictReason = isPassed ? "Static single-wave baseline checks concluded successfully without triggering health boundaries." : "Static verification loop completed with values exceeding defined quality thresholds.";
6517
- const geminiAnalysisHtml = await generateGeminiRCA(history, finalSummaryCards, rawErrors.length > 0 ? rawErrors : getFallbackClusterLogs());
6518
- const wittyBriefingText = await generateWittyAudioBriefing(finalSummaryCards);
6519
- if (config.optionValue === "elif") {
6520
- const plainEnglishSummary = await generateElifExplanation(finalSummaryCards);
6521
- if (plainEnglishSummary) {
6522
- console.log("\n--- \u{1F9F8} ELIF Performance Breakdown ---");
6523
- console.log(plainEnglishSummary);
6524
- console.log("-------------------------------------\n");
6525
- }
6526
- }
6527
- if (config.isSeo) {
6528
- const seo = calculateSeoImpactMetrics(metrics.avgLatencyMs);
6529
- console.log(`\u{1F50E} [SEO Impact Report]: Target latency (${metrics.avgLatencyMs.toFixed(1)}ms) triggers a predicted Search Visibility Loss of -${seo.trafficLoss}% [Status: ${seo.status}].`);
6530
- }
6531
- const breakdownRates = {};
6532
- Object.keys(metrics.codeCounts).forEach((codeStr) => {
6533
- const code = Number(codeStr);
6534
- breakdownRates[code] = metrics.totalRequests === 0 ? 0 : metrics.codeCounts[code] / metrics.totalRequests * 100;
6535
- });
6536
- const baselineData = loadBaselineData(config.baselinePath);
6537
- exportHtmlDashboard(history, config, verdictReason, semanticReport, {
6538
- total1xx: metrics.c1xx,
6539
- total2xx: metrics.c2xx,
6540
- total3xx: metrics.c3xx,
6541
- total4xx: metrics.c4xx,
6542
- total5xx: metrics.c5xx,
6543
- breakdownRates
6544
- }, finalSummaryCards, rawRequests, geminiAnalysisHtml, wittyBriefingText, baselineData);
6545
- }
6546
- function getFallbackClusterLogs() {
6547
- const logs = [];
6548
- const add = (msg, count) => {
6549
- for (let i = 0; i < count; i++)
6550
- logs.push(`[2026-07-08T12:08:18.000Z] ${msg}`);
6551
- };
6552
- add("JsonWebTokenError: jwt expired at JMT.verify (\\app\\node_modules\\jsonwebtoken\\index.js:5)", 37);
6553
- add("Error: ECONNREFUSED 127.0.0.1:5432 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1)", 33);
6554
- add("TypeError: Cannot read properties of undefined (reading 'config_id') at Object.execute (\\app\\dist\\handler.js:42:19)", 31);
6555
- add("HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 15000ms", 25);
6556
- add("HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 30000ms", 19);
6557
- return logs;
6558
- }
6559
- function generateSimulationData(threads, durationSec, maxAllowedLatencyMs, rampUpSec = 0, rampDownSec = 0) {
6560
- const data = [];
6561
- const totalRequests = Math.max(15, threads * durationSec * 30);
6562
- const startTime = Date.now();
6563
- const rampUpMs = rampUpSec * 1e3;
6564
- const steadyMs = durationSec * 1e3;
6565
- const rampDownMs = rampDownSec * 1e3;
6566
- const totalTestMs = rampUpMs + steadyMs + rampDownMs;
6567
- const targetThresholdBreak = Math.max(5, Math.floor(maxAllowedLatencyMs * 0.05));
6568
- const errorPool = getFallbackClusterLogs();
6569
- for (let i = 0; i < totalRequests; i++) {
6570
- const offsetMs = Math.random() * totalTestMs;
6571
- const loadFactor = getLoadMultiplier(offsetMs, rampUpMs, steadyMs, rampDownMs);
6572
- if (loadFactor <= 0)
6573
- continue;
6574
- const effectiveThreads = Math.max(1, threads * loadFactor);
6575
- const isBreached = effectiveThreads > targetThresholdBreak;
6576
- const stressFactor = isBreached ? Math.pow(effectiveThreads / targetThresholdBreak, 1.5) : 1;
6577
- const isError = Math.random() < (isBreached ? 0.05 : 1e-3);
6578
- const baseLatency = (25 + Math.random() * 35) * stressFactor;
6579
- let errorMessage;
6580
- let statusCode = 200;
6581
- if (isError) {
6582
- const targetResponseCodes = [400, 401, 403, 404, 429, 500, 502, 503, 504];
6583
- statusCode = targetResponseCodes[Math.floor(Math.random() * targetResponseCodes.length)];
6584
- errorMessage = errorPool[Math.floor(Math.random() * errorPool.length)];
6585
- }
6586
- data.push({
6587
- durationMs: isError ? baseLatency * 0.1 : baseLatency,
6588
- timestampMs: startTime + offsetMs,
6589
- dnsTimeMs: 1 + Math.random() * 0.9,
6590
- tcpTimeMs: 4 + Math.random() * 1.5,
6591
- tlsTimeMs: 6 + Math.random() * 1.8,
6592
- statusCode,
6593
- errorMessage
6594
- });
6595
- }
6596
- return data.sort((a, b) => a.timestampMs - b.timestampMs);
6597
- }
6598
- function processMetricsTelemetry(requests, durationSec) {
6599
- const totalRequests = requests.length;
6600
- const durations = requests.map((r) => r.durationMs);
6601
- const avgLatencyMs = mathUtils.mean(durations);
6602
- const stdDevMs = mathUtils.stdDev(durations, avgLatencyMs);
6603
- const p95LatencyMs = mathUtils.percentile(durations, 95);
6604
- const avgDnsMs = mathUtils.mean(requests.map((r) => r.dnsTimeMs || 0));
6605
- const avgTcpMs = mathUtils.mean(requests.map((r) => r.tcpTimeMs || 0));
6606
- const avgTlsMs = mathUtils.mean(requests.map((r) => r.tlsTimeMs || 0));
6607
- const avgTtfbMs = avgDnsMs + avgTcpMs + avgTlsMs + avgLatencyMs * 0.3;
6608
- let c1xx = 0, c2xx = 0, c3xx = 0, c4xx = 0, c5xx = 0;
6609
- const codeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500: 502, 503: 0, 504: 0 };
6610
- [400, 401, 403, 404, 429, 500, 502, 503, 504].forEach((c) => codeCounts[c] = 0);
6611
- requests.forEach((r) => {
6612
- if (r.statusCode >= 100 && r.statusCode < 200)
6613
- c1xx++;
6614
- else if (r.statusCode >= 200 && r.statusCode < 300)
6615
- c2xx++;
6616
- else if (r.statusCode >= 300 && r.statusCode < 400)
6617
- c3xx++;
6618
- else if (r.statusCode >= 400 && r.statusCode < 500)
6619
- c4xx++;
6620
- else if (r.statusCode >= 500)
6621
- c5xx++;
6622
- if (r.statusCode in codeCounts) {
6623
- codeCounts[r.statusCode]++;
6624
- }
6625
- });
6626
- const errorCount = c4xx + c5xx;
6627
- const errorRate = totalRequests === 0 ? 0 : errorCount / totalRequests;
6628
- const T = 50;
6629
- const satisfied = requests.filter((r) => r.durationMs <= T && r.statusCode < 400).length;
6630
- const tolerating = requests.filter((r) => r.durationMs > T && r.durationMs <= T * 4 && r.statusCode < 400).length;
6631
- const apdexScore = totalRequests === 0 ? 0 : (satisfied + tolerating / 2) / totalRequests;
6632
- const bandwidthMb = totalRequests * 1.2 / durationSec / 10;
6633
- return {
6634
- totalRequests,
6635
- requestsPerSecond: totalRequests / durationSec,
6636
- avgLatencyMs,
6637
- stdDevMs,
6638
- p95LatencyMs,
6639
- errorRate,
6640
- apdexScore,
6641
- c1xx,
6642
- c2xx,
6643
- c3xx,
6644
- c4xx,
6645
- c5xx,
6646
- avgDnsMs,
6647
- avgTcpMs,
6648
- avgTlsMs,
6649
- avgTtfbMs,
6650
- bandwidthMb,
6651
- codeCounts
6652
- };
6653
- }
6654
- function printMatrixDashboard(m, threads) {
6655
- const pad = (val, size) => String(val).padEnd(size).substring(0, size);
6656
- console.log(`-----------------------------------------------------------------------------------------`);
6657
- console.log(`| Threads | Samples | Avg Latency | P95 Latency | Throughput | Error Rate | Apdex |`);
6658
- console.log(`-----------------------------------------------------------------------------------------`);
6659
- 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)} |`);
6660
- console.log(`-----------------------------------------------------------------------------------------`);
6661
- }
6662
- function generateFinalAgentReport(history, score) {
6663
- if (history.length === 0)
6664
- return;
6665
- const peakThroughput = Math.max(...history.map((h) => h.throughput));
6666
- const cleanRuns = history.filter((h) => h.apdex >= 0.85 && h.errorRate <= 0.01);
6667
- const maxSafeThreads = cleanRuns.length > 0 ? cleanRuns[cleanRuns.length - 1].threads : history[0].threads;
6668
- console.log("\n=======================================================");
6669
- console.log("\u{1F9E0} INTELLIGENT AUTONOMOUS LOAD AGENT FINAL REPORT");
6670
- console.log("=======================================================");
6671
- console.log(`\u{1F49A} Unified Blaze Health Score : ${score}/100`);
6672
- console.log(`\u{1F3C6} Recommended Max Safe Concurrency : ${maxSafeThreads} allocation threads`);
6673
- console.log(`\u{1F4A5} Infrastructure Failure Boundary : ${history[history.length - 1].threads} concurrency allocation`);
6674
- console.log(`\u26A1 Peak Achieved Cluster Velocity : ${peakThroughput.toFixed(0)} req/sec`);
6675
- console.log("=======================================================\n");
6676
- }
6677
- function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards, rawRequests = [], geminiAnalysisHtml = "", wittyBriefingText = "", baselineData = null) {
6678
- const dict = i18n[config.locale] || i18n["en-US"];
6679
- const dir = dict.rtl ? "rtl" : "ltr";
6680
- const formatter = new Intl.DateTimeFormat(config.locale || "en-US", {
6681
- year: "numeric",
6682
- month: "long",
6683
- day: "numeric",
6684
- hour: "2-digit",
6685
- minute: "2-digit",
6686
- second: "2-digit",
6687
- timeZoneName: "short"
6688
- });
6689
- const formattedDate = formatter.format(/* @__PURE__ */ new Date());
6690
- const summaryArtifactPath = config.outputHtml.replace(/\.html$/, "-summary.json");
6691
- try {
6692
- fs.writeFileSync(summaryArtifactPath, JSON.stringify(cards, null, 2), "utf-8");
6693
- } catch (e) {
6694
- }
6695
- const labels = history.map((h, i) => `${i + 1}s`);
6696
- const successRequestsData = history.map((h) => h.successRequests);
6697
- const failedWorkloadsData = history.map((h) => h.failedRequests);
6698
- const activeThreadsData = history.map((h) => h.threads);
6699
- const ttfTrendData = history.map((h) => h.avgLatency * 1.1);
6700
- const waveListItems = history.map((h) => {
6701
- return `<div class="wave-item">Wave (${h.threads} VUs): P95 ${h.p95Latency.toFixed(1)}ms, Errors ${(h.errorRate * 100).toFixed(1)}%</div>`;
6702
- }).join("");
6703
- const estimatedMonthlyCost = (cards.throughput * 0.045 * 24 * 30).toFixed(2);
6704
- const recommendedCpuCores = Math.max(2, Math.ceil(cards.saturationKneePoint / 75));
6705
- const recommendedRamGb = recommendedCpuCores * 2;
6706
- const firstTimestamp = rawRequests[0]?.timestampMs || Date.now();
6707
- const scatterPoints = rawRequests.slice(0, 180).map((r) => ({
6708
- x: Number(((r.timestampMs - firstTimestamp) / 1e3).toFixed(2)),
6709
- y: Number(((r.dnsTimeMs || 0) + (r.tcpTimeMs || 0) + (r.tlsTimeMs || 0) + r.durationMs * 0.3).toFixed(1))
6710
- }));
6711
- let baselineComparisonHtml = "";
6712
- if (baselineData) {
6713
- const diffApdex = cards.apdex - baselineData.apdex;
6714
- const diffThroughput = (cards.throughput - baselineData.throughput) / (baselineData.throughput || 1) * 100;
6715
- const diffTtfb = (cards.ttfb - baselineData.ttfb) / (baselineData.ttfb || 1) * 100;
6716
- const diffHealth = cards.healthScore - baselineData.healthScore;
6717
- const formatDelta = (val, isInverse = false) => {
6718
- const good = isInverse ? val <= 0 : val >= 0;
6719
- const color = good ? "#10b981" : "#ef4444";
6720
- const sign = val > 0 ? "+" : "";
6721
- return `<span style="color: ${color}; font-weight: bold;">${sign}${val.toFixed(1)}%</span>`;
6722
- };
6723
- baselineComparisonHtml = `
6724
- <div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #7c3aed;">
6725
- <h3 style="color: #a78bfa; display: flex; align-items: center; gap: 0.6rem; font-size: 1.2rem; border-bottom: 1px solid #1e293b; padding-bottom: 0.5rem;">
6726
- \u{1F4CA} Baseline Regression Comparison (Delta Engine)
6727
- </h3>
6728
- <div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem;">
6729
- <div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
6730
- <div class="card-title">Apdex Shift</div>
6731
- <div style="font-size: 1.4rem; font-weight: bold; color: #ffffff;">${cards.apdex.toFixed(2)}</div>
6732
- <div style="font-size: 0.8rem; margin-top: 0.2rem;">Delta: <span style="color: ${diffApdex >= 0 ? "#10b981" : "#ef4444"}; font-weight: bold;">${diffApdex >= 0 ? "+" : ""}${diffApdex.toFixed(2)}</span></div>
6733
- </div>
6734
- <div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
6735
- <div class="card-title">Throughput Delta</div>
6736
- <div style="font-size: 1.4rem; font-weight: bold; color: #ffffff;">${cards.throughput.toFixed(0)}/s</div>
6737
- <div style="font-size: 0.8rem; margin-top: 0.2rem;">Delta: ${formatDelta(diffThroughput, false)}</div>
6738
- </div>
6739
- <div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
6740
- <div class="card-title">TTFB Latency Shift</div>
6741
- <div style="font-size: 1.4rem; font-weight: bold; color: #ffffff;">${cards.ttfb.toFixed(1)}ms</div>
6742
- <div style="font-size: 0.8rem; margin-top: 0.2rem;">Delta: ${formatDelta(diffTtfb, true)}</div>
6743
- </div>
6744
- <div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
6745
- <div class="card-title">Health Score Diff</div>
6746
- <div style="font-size: 1.4rem; font-weight: bold; color: #ffffff;">${cards.healthScore}/100</div>
6747
- <div style="font-size: 0.8rem; margin-top: 0.2rem;">Delta: <span style="color: ${diffHealth >= 0 ? "#10b981" : "#ef4444"}; font-weight: bold;">${diffHealth >= 0 ? "+" : ""}${diffHealth} pts</span></div>
6748
- </div>
6749
- </div>
6750
- </div>`;
6751
- }
6752
- const crossBrowserRows = getCrossBrowserMatrixData().map((m) => `
6753
- <tr>
6754
- <td><span class="engine-badge ${m.badgeClass}">${m.engine}</span></td>
6755
- <td>${m.fcp}</td>
6756
- <td>${m.lcp}</td>
6757
- <td>${m.journeyDuration}</td>
6758
- <td><span class="${m.statusClass}">${m.status}</span></td>
6759
- </tr>
6760
- `).join("");
6761
- const crossBrowserMatrixSectionHtml = `
6762
- <div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #38bdf8;">
6763
- <h3 style="color: #38bdf8; display: flex; align-items: center; gap: 0.6rem; font-size: 1.2rem; border-bottom: 1px solid #1e293b; padding-bottom: 0.5rem;">
6764
- \u{1F310} Cross-Browser Rendering Performance Matrix
6765
- </h3>
6766
- <table class="matrix-table" style="width: 100%; border-collapse: collapse; text-align: start; font-size: 14px;">
6767
- <thead>
6768
- <tr style="border-bottom: 1px solid #1e293b;">
6769
- <th style="padding: 12px 14px; background: #090d16; color: #94a3b8; font-size: 12px; text-transform: uppercase;">Browser Engine</th>
6770
- <th style="padding: 12px 14px; background: #090d16; color: #94a3b8; font-size: 12px; text-transform: uppercase;">FCP</th>
6771
- <th style="padding: 12px 14px; background: #090d16; color: #94a3b8; font-size: 12px; text-transform: uppercase;">LCP</th>
6772
- <th style="padding: 12px 14px; background: #090d16; color: #94a3b8; font-size: 12px; text-transform: uppercase;">Journey Duration</th>
6773
- <th style="padding: 12px 14px; background: #090d16; color: #94a3b8; font-size: 12px; text-transform: uppercase;">Status</th>
6774
- </tr>
6775
- </thead>
6776
- <tbody>
6777
- ${crossBrowserRows}
6778
- </tbody>
6779
- </table>
6780
- </div>`;
6781
- let logClustersHtml = "";
6782
- let formattedTicketLogs = "No structural error codes encountered.";
6783
- if (semanticReport) {
6784
- const totalLogCount = semanticReport.clusters.reduce((sum, c) => sum + c.count, 0);
6785
- formattedTicketLogs = semanticReport.clusters.slice(0, 5).map((c) => {
6786
- const cleanBlueprint = c.template.replace(/\[\d{4}-\d{2}-\d{2}.*?\]\s*/, "");
6787
- return `[${c.severity}] Count: ${c.count} | Cat: ${c.category}
6788
- \u21B3 ${cleanBlueprint}`;
6789
- }).join("\n\n");
6790
- logClustersHtml = `
6791
- <div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
6792
- <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;">
6793
- \u{1F9E0} Semantic Log Clustering
6794
- </h3>
6795
- <div style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
6796
- Captured <strong style="color: #f8fafc;">${totalLogCount}</strong> raw failures.
6797
- </div>
6798
-
6799
- <table style="width: 100%; border-collapse: collapse;">
6800
- <thead>
6801
- <tr style="border-bottom: 1px solid #1e293b;">
6802
- <th style="width: 8%; padding: 0.75rem; text-align: start; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Count</th>
6803
- <th style="width: 22%; padding: 0.75rem; text-align: start; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Category</th>
6804
- <th style="width: 10%; padding: 0.75rem; text-align: start; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Severity</th>
6805
- <th style="width: 60%; padding: 0.75rem; text-align: start; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Structural Blueprint</th>
6806
- </tr>
6807
- </thead>
6808
- <tbody>
6809
- ${semanticReport.clusters.map((c) => {
6810
- let catColor = "#fb923c";
6811
- if (c.category === "Authentication_Error")
6812
- catColor = "#c084fc";
6813
- if (c.category === "Product_Error")
6814
- catColor = "#f87171";
6815
- let sevBg = c.severity === "CRITICAL" ? "#dc2626" : "#ea580c";
6816
- const rawTemplate = c.template || "";
6817
- const cleanedTemplate = rawTemplate.replace(/\[\d{4}-\d{2}-\d{2}.*?\]\s*/, "");
6818
- return `
6819
- <tr style="border-bottom: 1px solid #1e293b; vertical-align: top;">
6820
- <td style="padding: 1.25rem 0.75rem; font-size: 1.2rem; font-weight: bold; color: #f8fafc;">${c.count}</td>
6821
- <td style="padding: 1.25rem 0.75rem; color: ${catColor}; font-weight: 600; font-size: 0.95rem;">${c.category}</td>
6822
- <td style="padding: 1.25rem 0.75rem;">
6823
- <span style="background: ${sevBg}; color: #ffffff; padding: 0.2rem 0.5rem; border-radius: 4px; font-size: 0.7rem; font-weight: bold;">${c.severity}</span>
6824
- </td>
6825
- <td style="padding: 1.25rem 0.75rem;">
6826
- <div style="background: #090d16; border-radius: 4px; padding: 0.6rem 0.8rem; border-inline-start: 3px solid #1e293b; margin-bottom: 0.5rem;">
6827
- <code style="color: #cbd5e1; font-family: monospace; font-size: 0.9rem; white-space: pre-wrap; word-break: break-all;">${cleanedTemplate}</code>
6828
- </div>
6829
- </td>
6830
- </tr>`;
6831
- }).join("")}
6832
- </tbody>
6833
- </table>
6834
- </div>`;
6835
- }
6836
- const generatedTicketMarkdown = `## \u{1F6A8} Performance Degradation Ticket (Blaze Incident Framework)
6837
-
6838
- ### \u{1F4CA} 1. System Telemetry Metrics
6839
- - **Blaze Core Health Score:** ${cards.healthScore}/100
6840
- - **Target Threshold Apdex:** ${config.targetApdex} (Realized Apdex: ${cards.apdex.toFixed(2)})
6841
- - **Peak Aggregated Throughput:** ${cards.throughput.toFixed(1)} req/sec
6842
- - **P95 Latency Window:** ${cards.p95Latency.toFixed(1)} ms
6843
- - **Observed System Error Rate:** ${(cards.errorRate * 100).toFixed(2)}%
6844
-
6845
- ### \u{1F9E0} 2. Architectural Sizing Recommendations
6846
- - **Recommended Remediation Profile:** Upgrade capacity to a minimum configuration tier of **${recommendedCpuCores} CPU Cores** and **${recommendedRamGb} GB RAM**.
6847
- - **Estimated OpEx Budget Shift:** ~$${estimatedMonthlyCost}/month based on system concurrency profiles.
6848
-
6849
- ### \u274C 3. Structural Log Blueprint Failures
6850
- \`\`\`
6851
- ${formattedTicketLogs}
6852
- \`\`\`
6853
-
6854
- *Ticket generated autonomously via Blaze Fix-It Ticket Engine pipeline. Status: Pending Triage.*`;
6855
- const breakdownRates = responseMatrix.breakdownRates || {};
6856
- const breakdownGridHtml = [400, 401, 403, 404, 429, 500, 502, 503, 504].map((code) => {
6857
- const rate = breakdownRates[code] || 0;
6858
- const displayColor = rate > 0 ? code >= 500 ? "#f87171" : "#fde047" : "#64748b";
6859
- return `
6860
- <div style="background: #090d16; border: 1px solid #1e293b; border-radius: 4px; padding: 0.4rem 0.2rem; text-align: center;">
6861
- <div style="font-size: 0.7rem; color: #64748b; font-weight: bold;">${dict.matrix.code} ${code}</div>
6862
- <div style="font-size: 0.85rem; font-weight: bold; color: ${displayColor};">${rate.toFixed(2)}%</div>
6863
- </div>
6864
- `;
6865
- }).join("");
6866
- const gaugeColor = cards.healthScore >= 90 ? "#10b981" : cards.healthScore >= 70 ? "#f59e0b" : "#ef4444";
6867
- const htmlContent = `<!DOCTYPE html>
6868
- <html lang="${config.locale}" dir="${dir}">
6869
- <head>
6870
- <meta charset="UTF-8">
6871
- <title>${dict.title}</title>
6872
- <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
6873
- <style>
6874
- body { font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #0b111e; color: #f8fafc; margin: 0; padding: 2rem; }
6875
- .container { max-width: 1300px; margin: 0 auto; }
6876
- .header { border-bottom: 1px solid #1e293b; padding-bottom: 1rem; margin-bottom: 1.5rem; }
6877
- h1 { color: #38bdf8; margin: 0; font-size: 2rem; }
6878
- .meta { color: #94a3b8; font-size: 0.9rem; margin-top: 0.5rem; }
6879
-
6880
- .cards-wrapper { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; margin-bottom: 1.5rem; }
6881
- .metric-card { background: #131c2e; border: 1px solid #1e293b; border-radius: 6px; padding: 1.25rem; position: relative; }
6882
- .card-title { font-size: 0.75rem; font-weight: 700; color: #64748b; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.5rem; }
6883
- .card-value { font-size: 1.6rem; font-weight: 700; color: #ffffff; }
6884
- .card-value .unit { font-size: 0.9rem; font-weight: 500; color: #94a3b8; margin-inline-start: 0.2rem; display: inline-block; }
6885
- .card-value.green { color: #10b981; }
6886
- .card-value.orange { color: #f97316; }
6887
- .card-value.purple { color: #a855f7; }
6888
- .card-subtext { font-size: 0.8rem; color: #10b981; font-weight: 500; margin-inline-start: 0.4rem; display: inline-block; }
6889
-
6890
- .top-layout-grid { display: grid; grid-template-columns: 1fr 280px 380px; gap: 1.5rem; margin-bottom: 2rem; }
6891
- .verdict-box { background: #0f172a; border: 1px dashed #ea580c; border-radius: 8px; padding: 1.25rem; }
6892
- .verdict-title { text-transform: uppercase; font-size: 0.85rem; font-weight: 700; color: #f97316; margin-bottom: 0.5rem; }
6893
- .verdict-text { font-size: 0.95rem; line-height: 1.5; color: #e2e8f0; margin-bottom: 0.75rem; }
6894
- .verdict-waves { background: #1f2937; border-radius: 6px; padding: 0.75rem 1rem; font-family: monospace; color: #9ca3af; font-size: 0.9rem; }
6895
- .wave-item { margin: 0.25rem 0; }
6896
-
6897
- .gauge-container-box { background: #131c2e; border: 1px solid #1e293b; border-radius: 8px; padding: 1.25rem; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; }
6898
- .gauge-title { font-size: 0.85rem; font-weight: bold; color: #94a3b8; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 1rem; }
6899
-
6900
- .matrix-box { background: #131c2e; border: 1px solid #1e293b; border-radius: 8px; padding: 1.25rem; }
6901
- .matrix-title { font-size: 1.1rem; font-weight: bold; color: #ffffff; margin-bottom: 1rem; }
6902
- .matrix-box .matrix-row { display: flex; justify-content: space-between; align-items: center; padding: 0.6rem 0; border-bottom: 1px solid #1e293b; }
6903
- .matrix-box .matrix-row:last-child { border-bottom: none; }
6904
- .matrix-label { font-size: 0.9rem; color: #f8fafc; display: flex; align-items: center; gap: 0.5rem; }
6905
- .matrix-badge { font-size: 0.7rem; font-weight: bold; padding: 0.1rem 0.3rem; border-radius: 4px; }
6906
- .badge-1xx { background: rgba(56, 189, 248, 0.2); color: #38bdf8; }
6907
- .badge-2xx { background: rgba(22, 163, 74, 0.2); color: #4ade80; }
6908
- .badge-3xx { background: rgba(148, 163, 184, 0.2); color: #cbd5e1; }
6909
- .badge-4xx { background: rgba(234, 179, 8, 0.2); color: #fde047; }
6910
- .badge-5xx { background: rgba(220, 38, 38, 0.2); color: #f87171; }
6911
- .matrix-value { font-size: 1rem; font-weight: bold; color: #ffffff; }
6912
-
6913
- .engine-badge { display: inline-block; padding: 4px 10px; border-radius: 6px; font-weight: 600; font-size: 12px; }
6914
- .badge-blink { background: #dbeafe; color: #1e40af; }
6915
- .badge-webkit { background: #fef3c7; color: #92400e; }
6916
- .badge-gecko { background: #fee2e2; color: #991b1b; }
6917
- .status-optimal { color: #16a34a; font-weight: 600; }
6918
- .status-warning { color: #ca8a04; font-weight: 600; }
6919
-
6920
- .charts-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.5rem; margin-bottom: 2rem; }
6921
- .graph-card { background: #131c2e; border: 1px solid #1e293b; border-radius: 8px; padding: 1.5rem; }
6922
- .graph-title { font-size: 1.1rem; font-weight: 700; color: #ffffff; margin-bottom: 1.2rem; display: flex; align-items: center; gap: 0.5rem; }
6923
-
6924
- .card { background: #131c2e; border: 1px solid #1e293b; border-radius: 8px; padding: 1.5rem; }
6925
- h3 { margin-top: 0; color: #cbd5e1; border-bottom: 1px solid #1e293b; padding-bottom: 0.5rem; }
6926
- table { width: 100%; border-collapse: collapse; margin-top: 1rem; }
6927
- th, td { padding: 0.75rem; text-align: start; border-bottom: 1px solid #1e293b; }
6928
- th { color: #94a3b8; font-weight: 600; }
6929
- .badge { padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.8rem; font-weight: bold; }
6930
- .badge-success { background: #16a34a; color: #f0fdf4; }
6931
- .badge-fail { background: #dc2626; color: #fef2f2; }
6932
- </style></head><body>
6933
- <div class="container">
6934
- <div class="header">
6935
- <h1>\u{1F525} ${dict.title}</h1>
6936
- <div class="meta">${dict.targetScript}: <code>${config.targetScript}</code> | ${dict.rampUp}: ${config.rampUpSec}s | ${dict.rampDown}: ${config.rampDownSec}s | ${dict.generatedAt}: ${formattedDate}</div>
6937
- </div>
6938
-
6939
- <!-- \u{1F399}\uFE0F Automated Executive Summary Audio Player Dashboard Widget Component -->
6940
- <div class="card" style="margin-bottom: 2rem; background: #1e1b4b; border: 1px solid #6366f1; display: flex; align-items: center; gap: 1.5rem; padding: 1rem 1.5rem; border-radius: 8px;">
6941
- <div style="background: #312e81; border-radius: 50%; width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; cursor: pointer; border: 2px solid #818cf8; transition: transform 0.2s; flex-shrink: 0;" id="audioPlayBtn" onclick="toggleBriefingAudio()">
6942
- <svg id="playIcon" width="18" height="18" viewBox="0 0 24 24" fill="#818cf8"><path d="M8 5v14l11-7z"/></svg>
6943
- <svg id="pauseIcon" width="18" height="18" viewBox="0 0 24 24" fill="#818cf8" style="display:none;"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>
6944
- </div>
6945
- <div style="flex-grow: 1; min-width: 0;">
6946
- <div style="font-size: 0.85rem; font-weight: bold; color: #818cf8; text-transform: uppercase; letter-spacing: 0.05em; display: flex; align-items: center; gap: 0.5rem;">
6947
- <span>\u{1F399}\uFE0F Executive Summary Briefing</span>
6948
- <span id="audioStatus" style="font-size: 0.75rem; color: #94a3b8; text-transform: none; font-weight: normal;">(Ready to play)</span>
6949
- </div>
6950
- <div style="font-size: 0.9rem; color: #cbd5e1; font-style: italic; margin-top: 0.25rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;" id="briefingTextPreview"></div>
6951
- </div>
6952
- <div style="display: flex; align-items: flex-end; gap: 3px; height: 20px; flex-shrink: 0;" id="visualizerBars">
6953
- <div style="width: 3px; height: 8px; background: #6366f1; border-radius: 1px; transition: height 0.15s ease;"></div>
6954
- <div style="width: 3px; height: 14px; background: #6366f1; border-radius: 1px; transition: height 0.15s ease;"></div>
6955
- <div style="width: 3px; height: 6px; background: #6366f1; border-radius: 1px; transition: height 0.15s ease;"></div>
6956
- <div style="width: 3px; height: 18px; background: #6366f1; border-radius: 1px; transition: height 0.15s ease;"></div>
6957
- <div style="width: 3px; height: 10px; background: #6366f1; border-radius: 1px; transition: height 0.15s ease;"></div>
6958
- </div>
6959
- </div>
6960
-
6961
- <div class="cards-wrapper">
6962
- <div class="metric-card">
6963
- <div class="card-title">${dict.metrics.apdex}</div>
6964
- <div class="card-value green">${cards.apdex.toFixed(2)}<span class="card-subtext">${dict.metrics.good}</span></div>
6965
- </div>
6966
- <div class="metric-card">
6967
- <div class="card-title">${dict.metrics.throughput}</div>
6968
- <div class="card-value orange">${cards.throughput.toFixed(1)}<span class="unit">/s</span></div>
6969
- </div>
6970
- <div class="metric-card">
6971
- <div class="card-title">${dict.metrics.bandwidth}</div>
6972
- <div class="card-value purple">${cards.bandwidth.toFixed(2)}<span class="unit">MB/s</span></div>
6973
- </div>
6974
- <div class="metric-card">
6975
- <div class="card-title">${dict.metrics.ttfb}</div>
6976
- <div class="card-value">${cards.ttfb.toFixed(1)}<span class="unit">ms</span></div>
6977
- </div>
6978
- <div class="metric-card">
6979
- <div class="card-title">${dict.metrics.dns}</div>
6980
- <div class="card-value">${cards.dns.toFixed(2)}<span class="unit">ms</span></div>
6981
- </div>
6982
- <div class="metric-card">
6983
- <div class="card-title">${dict.metrics.tcp}</div>
6984
- <div class="card-value">${cards.tcp.toFixed(2)}<span class="unit">ms</span></div>
6985
- </div>
6986
- <div class="metric-card">
6987
- <div class="card-title">${dict.metrics.tls}</div>
6988
- <div class="card-value">${cards.tls.toFixed(2)}<span class="unit">ms</span></div>
6989
- </div>
6990
- <div class="metric-card">
6991
- <div class="card-title">${dict.metrics.stability}</div>
6992
- <div class="card-value">&plusmn;${cards.stdDev.toFixed(1)}<span class="unit">ms</span></div>
6993
- </div>
6994
- </div>
6995
-
6996
- ${baselineComparisonHtml}
6997
- ${crossBrowserMatrixSectionHtml}
6998
-
6999
- <div class="top-layout-grid" style="margin-top: 2rem;">
7000
- <div class="verdict-box">
7001
- <div class="verdict-title">${dict.verdict}</div>
7002
- <div class="verdict-text">${verdictReason}</div>
7003
- <div class="verdict-waves">${waveListItems}</div>
7004
- </div>
7005
-
7006
- <div class="gauge-container-box">
7007
- <div class="gauge-title">${dict.health.title}</div>
7008
- <div style="position: relative; width: 140px; height: 140px; display: flex; align-items: center; justify-content: center;">
7009
- <svg width="140" height="140" viewBox="0 0 140 140" style="transform: rotate(-90deg);">
7010
- <circle cx="70" cy="70" r="58" stroke="#1e293b" stroke-width="12" fill="transparent"/>
7011
- <circle cx="70" cy="70" r="58" stroke="${gaugeColor}" stroke-width="12" fill="transparent"
7012
- stroke-dasharray="364.4" stroke-dashoffset="${364.4 - 364.4 * cards.healthScore / 100}"
7013
- stroke-linecap="round" style="transition: stroke-dashoffset 1s ease-out;"/>
7014
- </svg>
7015
- <div style="position: absolute; text-align: center;">
7016
- <div style="font-size: 2.2rem; font-weight: 800; color: #ffffff; line-height: 1;">${cards.healthScore}</div>
7017
- <div style="font-size: 0.75rem; color: #64748b; font-weight: bold; margin-top: 0.2rem; text-transform: uppercase;">/ 100</div>
7018
- </div>
7019
- </div>
7020
- <div style="margin-top: 0.75rem; font-size: 0.8rem; color: #94a3b8; font-weight: 500;">
7021
- ${dict.health.subtitle}
7022
- </div>
7023
- </div>
7024
-
7025
- <div class="matrix-box">
7026
- <div class="matrix-title">${dict.matrix.title}</div>
7027
- <div class="matrix-row">
7028
- <div class="matrix-label">${dict.matrix.info} <span class="matrix-badge badge-1xx">1xx</span></div>
7029
- <div class="matrix-value">${responseMatrix.total1xx}</div>
7030
- </div>
7031
- <div class="matrix-row">
7032
- <div class="matrix-label">${dict.matrix.success} <span class="matrix-badge badge-2xx">2xx</span></div>
7033
- <div class="matrix-value">${responseMatrix.total2xx}</div>
7034
- </div>
7035
- <div class="matrix-row">
7036
- <div class="matrix-label">${dict.matrix.redirects} <span class="matrix-badge badge-3xx">3xx</span></div>
7037
- <div class="matrix-value">${responseMatrix.total3xx}</div>
7038
- </div>
7039
- <div class="matrix-row">
7040
- <div class="matrix-label">${dict.matrix.clientErr} <span class="matrix-badge badge-4xx">4xx</span></div>
7041
- <div class="matrix-value">${responseMatrix.total4xx}</div>
7042
- </div>
7043
- <div class="matrix-row">
7044
- <div class="matrix-label">${dict.matrix.serverErr} <span class="matrix-badge badge-5xx">5xx</span></div>
7045
- <div class="matrix-value">${responseMatrix.total5xx}</div>
7046
- </div>
7047
-
7048
- <div class="matrix-title" style="margin-top: 1.5rem; font-size: 0.85rem; border-top: 1px solid #1e293b; padding-top: 1rem; color: #94a3b8; text-transform: uppercase; letter-spacing: 0.05em;">
7049
- ${dict.matrix.breakdown}
7050
- </div>
7051
- <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.5rem; margin-top: 0.5rem;">
7052
- ${breakdownGridHtml}
7053
- </div>
7054
- </div>
7055
- </div>
7056
-
7057
- <div class="card" style="margin-top: 2rem; background: #0c172b; border: 1px solid #7c3aed;">
7058
- <h3 style="color: #a78bfa; display: flex; align-items: center; gap: 0.6rem; font-size: 1.2rem; border-bottom: 1px solid #1e293b; padding-bottom: 0.5rem;">
7059
- ${dict.rca}
7060
- </h3>
7061
- <div style="font-size: 0.95rem; line-height: 1.6; color: #cbd5e1; padding: 0.5rem 0; white-space: pre-wrap; word-break: break-word;">
7062
- ${geminiAnalysisHtml}
7063
- </div>
7064
- </div>
7065
-
7066
- <!-- \u{1F3AB} Generative Fix-It Tickets Feature UI Component -->
7067
- <div class="card" style="margin-top: 2rem; background: #0c1c18; border: 1px solid #10b981;">
7068
- <h3 style="color: #34d399; display: flex; align-items: center; justify-content: space-between; font-size: 1.2rem; border-bottom: 1px solid #1f2937; padding-bottom: 0.5rem;">
7069
- <span>\u{1F3AB} Generative Fix-It Ticket Engine</span>
7070
- <button id="btnCopyTicket" onclick="copyFixItTicketMarkdown()" style="background: #10b981; color: #0c1c18; border: none; padding: 0.4rem 0.9rem; border-radius: 4px; cursor: pointer; font-weight: bold; font-size: 0.85rem; transition: background 0.2s;">
7071
- \u{1F4CB} Copy Ticket Markdown
7072
- </button>
7073
- </h3>
7074
- <div style="background: #040d0a; border: 1px solid #14532d; border-radius: 6px; padding: 1rem; max-height: 300px; overflow-y: auto;">
7075
- <pre id="ticketBodyPreview" style="margin: 0; color: #a7f3d0; font-family: monospace; font-size: 0.85rem; white-space: pre-wrap; word-break: break-all;"></pre>
7076
- </div>
7077
- </div>
7078
-
7079
- <div class="charts-grid" style="margin-top: 2rem;">
7080
- <div class="graph-card">
7081
- <div class="graph-title">${dict.charts.volume}</div>
7082
- <div style="height: 280px; position: relative;">
7083
- <canvas id="volumeChart"></canvas>
7084
- </div>
7085
- </div>
7086
-
7087
- <div class="graph-card">
7088
- <div class="graph-title">${dict.charts.concurrency}</div>
7089
- <div style="height: 280px; position: relative;">
7090
- <canvas id="concurrencyChart"></canvas>
7091
- </div>
7092
- </div>
7093
-
7094
- <div class="graph-card">
7095
- <div class="graph-title">${dict.charts.status}</div>
7096
- <div style="height: 280px; position: relative;">
7097
- <canvas id="statusDonutChart"></canvas>
7098
- </div>
7099
- </div>
7100
-
7101
- <div class="graph-card">
7102
- <div class="graph-title">${dict.charts.scatter}</div>
7103
- <div style="height: 280px; position: relative;">
7104
- <canvas id="ttfbScatterChart"></canvas>
7105
- </div>
7106
- </div>
7107
- </div>
7108
-
7109
- <div class="card" style="margin-top: 2rem;">
7110
- <h3>${dict.telemetry.title}</h3>
7111
- <table>
7112
- <thead>
7113
- <tr>
7114
- <th>${dict.telemetry.threads}</th>
7115
- <th>${dict.telemetry.throughput}</th>
7116
- <th>${dict.telemetry.avgLatency}</th>
7117
- <th>${dict.telemetry.p95}</th>
7118
- <th>${dict.telemetry.errors}</th>
7119
- <th>${dict.telemetry.status}</th>
7120
- </tr>
7121
- </thead>
7122
- <tbody id="telemetryTableBody">
7123
- ${history.map((h) => {
7124
- const isPassed = h.apdex >= config.targetApdex && h.errorRate <= config.targetErrorRate;
7125
- return `<tr>
7126
- <td><strong>${h.threads}</strong></td>
7127
- <td>${h.throughput.toFixed(0)} /s</td>
7128
- <td>${h.avgLatency.toFixed(1)} ms</td>
7129
- <td>${h.p95Latency.toFixed(1)} ms</td>
7130
- <td>${(h.errorRate * 100).toFixed(2)}%</td>
7131
- <td><span class="badge ${isPassed ? "badge-success" : "badge-fail"}">${isPassed ? dict.telemetry.pass : dict.telemetry.fail}</span></td>
7132
- </tr>`;
7133
- }).join("")}
7134
- </tbody>
7135
- </table>
7136
- </div>
7137
- ${logClustersHtml}
7138
- </div>
7139
-
7140
- <script>
7141
- // Inject the generated Briefing Text payload
7142
- const textBriefingPayload = ${JSON.stringify(wittyBriefingText)};
7143
- document.getElementById('briefingTextPreview').textContent = '"' + textBriefingPayload + '"';
7144
-
7145
- let synthesisEngine = window.speechSynthesis;
7146
- let textUtterance = null;
7147
- let audioStateActive = false;
7148
- let visualBarsTimer = null;
7149
-
7150
- function toggleBriefingAudio() {
7151
- if (!synthesisEngine) {
7152
- alert("Speech synthesis metrics streaming unsupported on this browser platform.");
7153
- return;
7154
- }
7155
-
7156
- if (audioStateActive) {
7157
- synthesisEngine.cancel();
7158
- terminateAudioVisuals();
7159
- } else {
7160
- synthesisEngine.cancel();
7161
- textUtterance = new SpeechSynthesisUtterance(textBriefingPayload);
7162
-
7163
- // Match specialized high quality voice parameters if dynamically present
7164
- let targetVoices = synthesisEngine.getVoices();
7165
- let activeVoice = targetVoices.find(v => v.lang.startsWith('en') && v.name.includes('Google')) || targetVoices.find(v => v.lang.startsWith('en'));
7166
- if (activeVoice) textUtterance.voice = activeVoice;
7167
-
7168
- textUtterance.rate = 1.08; // Snappy presentation delivery mapping
7169
- textUtterance.pitch = 1.0;
7170
-
7171
- textUtterance.onend = () => terminateAudioVisuals();
7172
- textUtterance.onerror = () => terminateAudioVisuals();
7173
-
7174
- synthesisEngine.speak(textUtterance);
7175
- initializeAudioVisuals();
7176
- }
7177
- }
7178
-
7179
- function initializeAudioVisuals() {
7180
- audioStateActive = true;
7181
- document.getElementById('playIcon').style.display = 'none';
7182
- document.getElementById('pauseIcon').style.display = 'block';
7183
- document.getElementById('audioStatus').textContent = '(Briefing playing...)';
7184
- document.getElementById('audioPlayBtn').style.transform = 'scale(1.05)';
7185
-
7186
- const bars = document.getElementById('visualizerBars').children;
7187
- visualBarsTimer = setInterval(() => {
7188
- for (let bar of bars) {
7189
- bar.style.height = (Math.floor(Math.random() * 16) + 4) + 'px';
7190
- }
7191
- }, 120);
7192
- }
7193
-
7194
- function terminateAudioVisuals() {
7195
- audioStateActive = false;
7196
- document.getElementById('playIcon').style.display = 'block';
7197
- document.getElementById('pauseIcon').style.display = 'none';
7198
- document.getElementById('audioStatus').textContent = '(Paused / Finished)';
7199
- document.getElementById('audioPlayBtn').style.transform = 'scale(1)';
7200
- clearInterval(visualBarsTimer);
7201
-
7202
- const defaultHeights = [8, 14, 6, 18, 10];
7203
- const bars = document.getElementById('visualizerBars').children;
7204
- for (let i = 0; i < bars.length; i++) {
7205
- if(bars[i]) bars[i].style.height = defaultHeights[i] + 'px';
7206
- }
7207
- }
7208
-
7209
- // Inject and expose the formatted Fix-It Ticket Markdown
7210
- const rawTicketMarkdown = ${JSON.stringify(generatedTicketMarkdown)};
7211
- document.getElementById('ticketBodyPreview').textContent = rawTicketMarkdown;
7212
-
7213
- function copyFixItTicketMarkdown() {
7214
- navigator.clipboard.writeText(rawTicketMarkdown).then(() => {
7215
- const btn = document.getElementById('btnCopyTicket');
7216
- btn.textContent = '\u2705 Copied!';
7217
- btn.style.background = '#34d399';
7218
- setTimeout(() => {
7219
- btn.textContent = '\u{1F4CB} Copy Ticket Markdown';
7220
- btn.style.background = '#10b981';
7221
- }, 2000);
7222
- }).catch(err => {
7223
- console.error('Failed to copy ticket layout text blueprint: ', err);
7224
- });
7225
- }
7226
-
7227
- const labels = ${JSON.stringify(labels)};
7228
- const successRequestsData = ${JSON.stringify(successRequestsData)};
7229
- const failedWorkloadsData = ${JSON.stringify(failedWorkloadsData)};
7230
-
7231
- new Chart(document.getElementById('volumeChart'), {
7232
- type: 'bar',
7233
- data: {
7234
- labels: labels,
7235
- datasets: [
7236
- {
7237
- label: '${dict.matrix.success}',
7238
- data: successRequestsData,
7239
- backgroundColor: '#10b981',
7240
- stack: 'requests',
7241
- barPercentage: 0.95,
7242
- categoryPercentage: 0.95
7243
- },
7244
- {
7245
- label: '${dict.matrix.serverErr}',
7246
- data: failedWorkloadsData,
7247
- backgroundColor: '#ef4444',
7248
- stack: 'requests',
7249
- barPercentage: 0.95,
7250
- categoryPercentage: 0.95
7251
- }
7252
- ]
7253
- },
7254
- options: {
7255
- responsive: true,
7256
- maintainAspectRatio: false,
7257
- plugins: {
7258
- legend: { position: 'top', labels: { color: '#94a3b8', font: { size: 11 } } }
7259
- },
7260
- scales: {
7261
- x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } },
7262
- y: { stacked: true, grid: { color: '#1e293b' }, ticks: { color: '#64748b' } }
7263
- }
7264
- }
7265
- });
7266
-
7267
- const baseActiveThreads = ${JSON.stringify(activeThreadsData)};
7268
- const baseTtfTrend = ${JSON.stringify(ttfTrendData)};
7269
-
7270
- new Chart(document.getElementById('concurrencyChart'), {
7271
- type: 'line',
7272
- data: {
7273
- labels: labels,
7274
- datasets: [
7275
- {
7276
- label: 'VUs',
7277
- data: baseActiveThreads,
7278
- borderColor: '#38bdf8',
7279
- backgroundColor: 'rgba(56, 189, 248, 0.1)',
7280
- borderWidth: 2.5,
7281
- pointRadius: 4,
7282
- fill: true,
7283
- yAxisID: 'yThreads'
7284
- },
7285
- {
7286
- label: 'TTF',
7287
- data: baseTtfTrend,
7288
- borderColor: '#f43f5e',
7289
- borderWidth: 2,
7290
- borderDash: [5, 5],
7291
- pointRadius: 3,
7292
- fill: false,
7293
- yAxisID: 'yTtf'
7294
- }
7295
- ]
7296
- },
7297
- options: {
7298
- responsive: true,
7299
- maintainAspectRatio: false,
7300
- plugins: {
7301
- legend: { position: 'top', labels: { color: '#94a3b8', font: { size: 11 } } }
7302
- },
7303
- scales: {
7304
- x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } },
7305
- yThreads: { position: 'left', grid: { color: '#1e293b' }, ticks: { color: '#38bdf8' } },
7306
- yTtf: { position: 'right', grid: { drawOnChartArea: false }, ticks: { color: '#f43f5e' } }
7307
- }
7308
- }
7309
- });
7310
-
7311
- new Chart(document.getElementById('statusDonutChart'), {
7312
- type: 'doughnut',
7313
- data: {
7314
- labels: ['1xx', '2xx', '3xx', '4xx', '5xx'],
7315
- datasets: [{
7316
- data: [${responseMatrix.total1xx}, ${responseMatrix.total2xx}, ${responseMatrix.total3xx}, ${responseMatrix.total4xx}, ${responseMatrix.total5xx}],
7317
- backgroundColor: ['#38bdf8', '#4ade80', '#cbd5e1', '#fde047', '#f87171'],
7318
- borderWidth: 1,
7319
- borderColor: '#1e293b'
7320
- }]
7321
- },
7322
- options: {
7323
- responsive: true,
7324
- maintainAspectRatio: false,
7325
- plugins: {
7326
- legend: { position: 'top', labels: { color: '#94a3b8', font: { size: 11 } } },
7327
- cutout: '65%'
7328
- }
7329
- }
7330
- });
7331
-
7332
- new Chart(document.getElementById('ttfbScatterChart'), {
7333
- type: 'scatter',
7334
- data: {
7335
- datasets: [{
7336
- label: 'Delay',
7337
- data: ${JSON.stringify(scatterPoints)},
7338
- backgroundColor: '#a855f7',
7339
- pointRadius: 4,
7340
- pointHoverRadius: 6
7341
- }]
7342
- },
7343
- options: {
7344
- responsive: true,
7345
- maintainAspectRatio: false,
7346
- plugins: {
7347
- legend: { position: 'top', labels: { color: '#94a3b8', font: { size: 11 } } }
7348
- },
7349
- scales: {
7350
- x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } },
7351
- y: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } }
7352
- }
7353
- }
7354
- });
7355
- </script></body></html>`;
7356
- try {
7357
- fs.writeFileSync(config.outputHtml, htmlContent, "utf-8");
7358
- console.log(`\u{1F4CA} Standalone Localized Dashboard exported successfully to: ${path.resolve(config.outputHtml)}`);
7359
- } catch (err) {
7360
- console.error(`\u274C Failed to write HTML output dashboard: ${err}`);
7361
- }
5578
+ if (config.liveNarration)
5579
+ narrator.narrateMilestone(config.threads, metrics, "END");
7362
5580
  }
7363
5581
  function printUsage() {
7364
5582
  console.log(`Blaze Core Performance Test CLI Engine
7365
5583
  Options:
7366
- --threads <count> | --duration <seconds> | --baseline <json_file> | --output <html_file> | --cross-browser | --locale <lang_code> | --option elif`);
5584
+ --threads <count> | --duration <seconds> | --live-narration`);
7367
5585
  }
7368
5586
  runCli().catch(console.error);
7369
5587
  export {