blaze-performance-tester 3.2.23 → 3.2.25

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 +260 -317
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -5571,6 +5571,164 @@ try {
5571
5571
  console.error(`\u274C Failed to load native C++ bindings from ${nativeBindingPath}`);
5572
5572
  process.exit(1);
5573
5573
  }
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
+ };
5574
5732
  var mathUtils = {
5575
5733
  mean: (arr) => arr.length === 0 ? 0 : arr.reduce((p, c) => p + c, 0) / arr.length,
5576
5734
  stdDev: (arr, mean) => {
@@ -5808,7 +5966,7 @@ async function runCli() {
5808
5966
  const argv = await yargs_default(hideBin(process.argv)).usage("Usage: blaze-performance-tester <script-path> [options]").option("prompt", {
5809
5967
  describe: "Natural language scenario to generate the test script",
5810
5968
  type: "string"
5811
- }).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" }).help(false).strict(false).argv;
5969
+ }).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;
5812
5970
  const args = process.argv.slice(2);
5813
5971
  if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
5814
5972
  printUsage();
@@ -5845,7 +6003,7 @@ async function runCli() {
5845
6003
  process.exit(1);
5846
6004
  }
5847
6005
  console.log(`Starting performance test using script: ${config.targetScript}`);
5848
- console.log(`Params -> Threads: ${config.threads || 1}, Duration: ${config.durationSec || 0}s`);
6006
+ console.log(`Params -> Threads: ${config.threads || 1}, Duration: ${config.durationSec || 0}s, Locale: ${config.locale}`);
5849
6007
  if (config.isCorrelate) {
5850
6008
  await executeTestPipeline(config);
5851
6009
  }
@@ -5873,6 +6031,7 @@ function parseArguments(args) {
5873
6031
  let outputHtml = "blaze-dashboard.html";
5874
6032
  let baselinePath = null;
5875
6033
  let optionValue = null;
6034
+ let locale = "en-US";
5876
6035
  const threadsIdx = args.indexOf("--threads");
5877
6036
  if (threadsIdx !== -1)
5878
6037
  threads = parseInt(args[threadsIdx + 1], 10);
@@ -5887,7 +6046,7 @@ function parseArguments(args) {
5887
6046
  rampDownSec = parseInt(args[rampDownIdx + 1], 10);
5888
6047
  const apdexIdx = args.indexOf("--target-apdex");
5889
6048
  if (apdexIdx !== -1)
5890
- targetApdex = parseFloat(apdexIdx + 1);
6049
+ targetApdex = parseFloat(args[apdexIdx + 1]);
5891
6050
  const errorIdx = args.indexOf("--target-error");
5892
6051
  if (errorIdx !== -1)
5893
6052
  targetErrorRate = parseFloat(args[errorIdx + 1]);
@@ -5904,6 +6063,15 @@ function parseArguments(args) {
5904
6063
  if (optionIdx !== -1 && args[optionIdx + 1]) {
5905
6064
  optionValue = args[optionIdx + 1];
5906
6065
  }
6066
+ const localeIdx = args.findIndex((arg) => arg.startsWith("--locale="));
6067
+ if (localeIdx !== -1) {
6068
+ locale = args[localeIdx].split("=")[1];
6069
+ } else {
6070
+ const standaloneLocaleIdx = args.indexOf("--locale");
6071
+ if (standaloneLocaleIdx !== -1 && args[standaloneLocaleIdx + 1]) {
6072
+ locale = args[standaloneLocaleIdx + 1];
6073
+ }
6074
+ }
5907
6075
  const positionalNums = args.slice(1).filter((arg) => !arg.startsWith("-")).map(Number).filter((n) => !isNaN(n));
5908
6076
  if (positionalNums.length > 0 && threadsIdx === -1 && !isAgentic)
5909
6077
  threads = positionalNums[0];
@@ -5933,7 +6101,6 @@ function parseArguments(args) {
5933
6101
  isCorrelate,
5934
6102
  isSeo,
5935
6103
  isCrossBrowser,
5936
- // Include in configuration object
5937
6104
  clusterLogs,
5938
6105
  threads,
5939
6106
  durationSec,
@@ -5946,7 +6113,9 @@ function parseArguments(args) {
5946
6113
  maxAllowedLatencyMs,
5947
6114
  outputHtml,
5948
6115
  baselinePath,
5949
- optionValue
6116
+ optionValue,
6117
+ locale
6118
+ // Include locale in configuration
5950
6119
  };
5951
6120
  }
5952
6121
  async function runBlazeCoreEngine(config) {
@@ -6329,6 +6498,18 @@ function generateFinalAgentReport(history, score) {
6329
6498
  console.log("=======================================================\n");
6330
6499
  }
6331
6500
  function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards, rawRequests = [], geminiAnalysisHtml = "", baselineData = null) {
6501
+ const dict = i18n[config.locale] || i18n["en-US"];
6502
+ const dir = dict.rtl ? "rtl" : "ltr";
6503
+ const formatter = new Intl.DateTimeFormat(config.locale || "en-US", {
6504
+ year: "numeric",
6505
+ month: "long",
6506
+ day: "numeric",
6507
+ hour: "2-digit",
6508
+ minute: "2-digit",
6509
+ second: "2-digit",
6510
+ timeZoneName: "short"
6511
+ });
6512
+ const formattedDate = formatter.format(/* @__PURE__ */ new Date());
6332
6513
  const summaryArtifactPath = config.outputHtml.replace(/\.html$/, "-summary.json");
6333
6514
  try {
6334
6515
  fs.writeFileSync(summaryArtifactPath, JSON.stringify(cards, null, 2), "utf-8");
@@ -6367,9 +6548,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
6367
6548
  <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;">
6368
6549
  \u{1F4CA} Baseline Regression Comparison (Delta Engine)
6369
6550
  </h3>
6370
- <p style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
6371
- Comparing current execution metrics directly against reference payload from baseline source.
6372
- </p>
6373
6551
  <div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem;">
6374
6552
  <div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
6375
6553
  <div class="card-title">Apdex Shift</div>
@@ -6408,17 +6586,14 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
6408
6586
  <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;">
6409
6587
  \u{1F310} Cross-Browser Rendering Performance Matrix
6410
6588
  </h3>
6411
- <p style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
6412
- Synthetic user journey benchmarks across Blink, WebKit, and Gecko execution contexts.
6413
- </p>
6414
- <table class="matrix-table" style="width: 100%; border-collapse: collapse; text-align: left; font-size: 14px;">
6589
+ <table class="matrix-table" style="width: 100%; border-collapse: collapse; text-align: start; font-size: 14px;">
6415
6590
  <thead>
6416
6591
  <tr style="border-bottom: 1px solid #1e293b;">
6417
6592
  <th style="padding: 12px 14px; background: #090d16; color: #94a3b8; font-size: 12px; text-transform: uppercase;">Browser Engine</th>
6418
6593
  <th style="padding: 12px 14px; background: #090d16; color: #94a3b8; font-size: 12px; text-transform: uppercase;">FCP</th>
6419
6594
  <th style="padding: 12px 14px; background: #090d16; color: #94a3b8; font-size: 12px; text-transform: uppercase;">LCP</th>
6420
6595
  <th style="padding: 12px 14px; background: #090d16; color: #94a3b8; font-size: 12px; text-transform: uppercase;">Journey Duration</th>
6421
- <th style="padding: 12px 14px; background: #090d16; color: #94a3b8; font-size: 12px; text-transform: uppercase;">Performance Status</th>
6596
+ <th style="padding: 12px 14px; background: #090d16; color: #94a3b8; font-size: 12px; text-transform: uppercase;">Status</th>
6422
6597
  </tr>
6423
6598
  </thead>
6424
6599
  <tbody>
@@ -6433,19 +6608,19 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
6433
6608
  logClustersHtml = `
6434
6609
  <div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
6435
6610
  <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;">
6436
- \u{1F9E0} Semantic Log Clustering & Error Classification
6611
+ \u{1F9E0} Semantic Log Clustering
6437
6612
  </h3>
6438
6613
  <div style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
6439
- Captured <strong style="color: #f8fafc;">${totalLogCount}</strong> raw failures grouped into <strong style="color: #f8fafc;">${uniquePatternsCount}</strong> unique structural patterns.
6614
+ Captured <strong style="color: #f8fafc;">${totalLogCount}</strong> raw failures.
6440
6615
  </div>
6441
6616
 
6442
6617
  <table style="width: 100%; border-collapse: collapse;">
6443
6618
  <thead>
6444
6619
  <tr style="border-bottom: 1px solid #1e293b;">
6445
- <th style="width: 8%; padding: 0.75rem; text-align: left; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Count</th>
6446
- <th style="width: 22%; padding: 0.75rem; text-align: left; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Classification Category</th>
6447
- <th style="width: 10%; padding: 0.75rem; text-align: left; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Severity</th>
6448
- <th style="width: 60%; padding: 0.75rem; text-align: left; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Structural Abstract Blueprint / Dynamic Log Fingerprint</th>
6620
+ <th style="width: 8%; padding: 0.75rem; text-align: start; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Count</th>
6621
+ <th style="width: 22%; padding: 0.75rem; text-align: start; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Category</th>
6622
+ <th style="width: 10%; padding: 0.75rem; text-align: start; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Severity</th>
6623
+ <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>
6449
6624
  </tr>
6450
6625
  </thead>
6451
6626
  <tbody>
@@ -6455,8 +6630,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
6455
6630
  catColor = "#c084fc";
6456
6631
  if (c.category === "Product_Error")
6457
6632
  catColor = "#f87171";
6458
- if (c.category === "Environment_Infrastructure_Error")
6459
- catColor = "#fb923c";
6460
6633
  let sevBg = c.severity === "CRITICAL" ? "#dc2626" : "#ea580c";
6461
6634
  const rawTemplate = c.template || "";
6462
6635
  const cleanedTemplate = rawTemplate.replace(/\[\d{4}-\d{2}-\d{2}.*?\]\s*/, "");
@@ -6465,15 +6638,12 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
6465
6638
  <td style="padding: 1.25rem 0.75rem; font-size: 1.2rem; font-weight: bold; color: #f8fafc;">${c.count}</td>
6466
6639
  <td style="padding: 1.25rem 0.75rem; color: ${catColor}; font-weight: 600; font-size: 0.95rem;">${c.category}</td>
6467
6640
  <td style="padding: 1.25rem 0.75rem;">
6468
- <span style="background: ${sevBg}; color: #ffffff; padding: 0.2rem 0.5rem; border-radius: 4px; font-size: 0.7rem; font-weight: bold; letter-spacing: 0.05em;">${c.severity}</span>
6641
+ <span style="background: ${sevBg}; color: #ffffff; padding: 0.2rem 0.5rem; border-radius: 4px; font-size: 0.7rem; font-weight: bold;">${c.severity}</span>
6469
6642
  </td>
6470
6643
  <td style="padding: 1.25rem 0.75rem;">
6471
- <div style="background: #090d16; border-radius: 4px; padding: 0.6rem 0.8rem; border-left: 3px solid #1e293b; margin-bottom: 0.5rem;">
6644
+ <div style="background: #090d16; border-radius: 4px; padding: 0.6rem 0.8rem; border-inline-start: 3px solid #1e293b; margin-bottom: 0.5rem;">
6472
6645
  <code style="color: #cbd5e1; font-family: monospace; font-size: 0.9rem; white-space: pre-wrap; word-break: break-all;">${cleanedTemplate}</code>
6473
6646
  </div>
6474
- <div style="color: #64748b; font-size: 0.8rem; font-family: monospace; padding-left: 0.2rem; line-height: 1.4;">
6475
- <span style="color: #475569;">Real Example Trace:</span> ${rawTemplate}
6476
- </div>
6477
6647
  </td>
6478
6648
  </tr>`;
6479
6649
  }).join("")}
@@ -6481,124 +6651,26 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
6481
6651
  </table>
6482
6652
  </div>`;
6483
6653
  }
6484
- let seoImpactSectionHtml = "";
6485
- if (config.isSeo) {
6486
- const finalLatency = history[history.length - 1]?.avgLatency || cards.ttfb;
6487
- const seoMetrics = calculateSeoImpactMetrics(finalLatency);
6488
- seoImpactSectionHtml = `
6489
- <div class="card" style="margin-top: 2rem; background: #111a2e; border: 1px solid #1e3a8a;">
6490
- <h3 style="color: #f43f5e; display: flex; align-items: center; gap: 0.6rem; font-size: 1.2rem; border-bottom: 1px solid #1e293b; padding-bottom: 0.5rem;">
6491
- \u{1F50E} SEO Rank Impact & Visibility Loss Predictor
6492
- </h3>
6493
- <p style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
6494
- Translates response time infrastructure load stress into Core Web Vitals ranking drops and organic search volume degradation metrics.
6495
- </p>
6496
- <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem;">
6497
- <div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b; text-align: center;">
6498
- <div class="card-title" style="color: #94a3b8;">Est. Organic Traffic Loss</div>
6499
- <div style="font-size: 2rem; font-weight: bold; color: ${seoMetrics.color};">
6500
- ${seoMetrics.trafficLoss > 0 ? "-" : ""}${seoMetrics.trafficLoss}%
6501
- </div>
6502
- <div style="font-size: 0.8rem; color: #64748b; margin-top: 0.25rem;">compared to 200ms sweet-spot</div>
6503
- </div>
6504
- <div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b;">
6505
- <div class="card-title">Search Visibility Status</div>
6506
- <div style="font-size: 1.25rem; font-weight: bold; color: ${seoMetrics.color}; margin-top: 0.4rem;">${seoMetrics.status}</div>
6507
- <div style="font-size: 0.8rem; color: #94a3b8; margin-top: 0.25rem;">Target Latency: ${finalLatency.toFixed(1)}ms</div>
6508
- </div>
6509
- <div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b;">
6510
- <div class="card-title">Googlebot Crawl Budget Impact</div>
6511
- <div style="font-size: 0.85rem; color: #e2e8f0; line-height: 1.5; margin-top: 0.4rem;">${seoMetrics.crawlPenalty}</div>
6512
- </div>
6513
- </div>
6514
- </div>`;
6515
- }
6516
- const liveAdjusterHtml = `
6517
- <div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
6518
- <h3 style="color: #38bdf8; display: flex; align-items: center; gap: 0.5rem; font-size: 1.2rem; margin-bottom: 1rem;">
6519
- \u{1F39B}\uFE0F Live Concurrency Adjuster (Mid-Test Simulation)
6520
- </h3>
6521
- <div style="display: flex; gap: 2rem; align-items: center; flex-wrap: wrap;">
6522
- <div style="flex: 1; min-width: 280px;">
6523
- <label for="concurrencySlider" style="display: block; font-size: 0.85rem; color: #94a3b8; margin-bottom: 0.5rem;">
6524
- Scale Virtual Users (VUs): <strong id="sliderValue" style="color: #38bdf8;">${config.threads}</strong> threads
6525
- </label>
6526
- <input type="range" id="concurrencySlider" min="5" max="${config.maxThreads || 300}" step="5" value="${config.threads}" style="width: 100%; accent-color: #38bdf8; cursor: pointer;">
6527
- </div>
6528
- <div style="display: flex; gap: 1.5rem; text-align: center;">
6529
- <div style="background: #090d16; padding: 0.75rem 1rem; border-radius: 6px; border: 1px solid #1e293b;">
6530
- <div style="font-size: 0.7rem; color: #64748b; text-transform: uppercase;">Sim. Throughput</div>
6531
- <div id="simThroughput" style="font-size: 1.2rem; font-weight: bold; color: #f97316;">${cards.throughput.toFixed(0)}/s</div>
6532
- </div>
6533
- <div style="background: #090d16; padding: 0.75rem 1rem; border-radius: 6px; border: 1px solid #1e293b;">
6534
- <div style="font-size: 0.7rem; color: #64748b; text-transform: uppercase;">Sim. Latency</div>
6535
- <div id="simLatency" style="font-size: 1.2rem; font-weight: bold; color: #10b981;">${cards.ttfb.toFixed(1)}ms</div>
6536
- </div>
6537
- </div>
6538
- </div>
6539
- </div>`;
6540
- const rightSizingHtml = `
6541
- <div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
6542
- <h3 style="color: #38bdf8; display: flex; align-items: center; gap: 0.5rem; font-size: 1.2rem;">
6543
- \u2601\uFE0F Infrastructure Right-Sizing & Cloud Cost Projections
6544
- </h3>
6545
- <div style="grid-template-columns: repeat(3, 1fr); display: grid; gap: 1rem; margin-top: 1rem;">
6546
- <div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
6547
- <div class="card-title">Est. Monthly Cloud Cost</div>
6548
- <div class="card-value green">$${estimatedMonthlyCost} <span class="unit">/mo</span></div>
6549
- </div>
6550
- <div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
6551
- <div class="card-title">Recommended CPU Right-Sizing</div>
6552
- <div class="card-value purple">${recommendedCpuCores} <span class="unit">vCores</span></div>
6553
- </div>
6554
- <div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
6555
- <div class="card-title">Recommended Memory Allocation</div>
6556
- <div class="card-value orange">${recommendedRamGb} <span class="unit">GB RAM</span></div>
6557
- </div>
6558
- </div>
6559
- </div>`;
6560
- const nlpQueryEngineHtml = `
6561
- <div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #0ea5e9; border-left: 4px solid #0ea5e9;">
6562
- <h3 style="color: #0ea5e9; display: flex; align-items: center; gap: 0.5rem; font-size: 1.2rem; margin-bottom: 0.5rem; border-bottom: none; padding-bottom: 0;">
6563
- \u{1F916} Natural Language Telemetry Insights Query Engine
6564
- </h3>
6565
- <p style="color: #94a3b8; font-size: 0.9rem; margin: 0 0 1rem 0;">
6566
- Ask questions of your load test results using natural phrasing to isolate and filter metric rows dynamically.
6567
- </p>
6568
- <div style="display: flex; gap: 0.75rem; margin-bottom: 0.75rem;">
6569
- <input type="text" id="nlpQueryInput" placeholder="e.g., show me tiers where latency > 40ms or error rate > 0%"
6570
- style="flex: 1; background: #090d16; border: 1px solid #1e293b; border-radius: 6px; padding: 0.75rem 1rem; color: #ffffff; font-size: 0.95rem; outline: none; transition: border-color 0.2s;">
6571
- <button id="nlpQueryBtn" style="background: #0ea5e9; color: #ffffff; border: none; border-radius: 6px; padding: 0.75rem 1.5rem; font-weight: 600; cursor: pointer; transition: background 0.2s;">
6572
- Query Matrix
6573
- </button>
6574
- </div>
6575
- <div style="display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center;">
6576
- <span style="font-size: 0.8rem; color: #64748b; font-weight: bold; text-transform: uppercase; letter-spacing: 0.05em;">Suggestions:</span>
6577
- <button onclick="setNlpQuery('Show P95 latency > 50ms')" style="background: #1e293b; color: #cbd5e1; border: 1px solid #334155; border-radius: 4px; padding: 0.25rem 0.5rem; font-size: 0.8rem; cursor: pointer;">P95 Latency > 50ms</button>
6578
- <button onclick="setNlpQuery('Find tiers with errors > 0%')" style="background: #1e293b; color: #cbd5e1; border: 1px solid #334155; border-radius: 4px; padding: 0.25rem 0.5rem; font-size: 0.8rem; cursor: pointer;">Errors > 0%</button>
6579
- <button onclick="setNlpQuery('High concurrency > 25 VUs')" style="background: #1e293b; color: #cbd5e1; border: 1px solid #334155; border-radius: 4px; padding: 0.25rem 0.5rem; font-size: 0.8rem; cursor: pointer;">Concurrency > 25 VUs</button>
6580
- <button onclick="setNlpQuery('')" style="background: #1c1917; color: #f43f5e; border: 1px solid #44403c; border-radius: 4px; padding: 0.25rem 0.5rem; font-size: 0.8rem; cursor: pointer;">Reset Filter</button>
6581
- </div>
6582
- <div id="nlpQueryFeedback" style="margin-top: 0.75rem; font-size: 0.85rem; color: #38bdf8;"></div>
6583
- </div>`;
6584
6654
  const breakdownRates = responseMatrix.breakdownRates || {};
6585
6655
  const breakdownGridHtml = [400, 401, 403, 404, 429, 500, 502, 503, 504].map((code) => {
6586
6656
  const rate = breakdownRates[code] || 0;
6587
6657
  const displayColor = rate > 0 ? code >= 500 ? "#f87171" : "#fde047" : "#64748b";
6588
6658
  return `
6589
6659
  <div style="background: #090d16; border: 1px solid #1e293b; border-radius: 4px; padding: 0.4rem 0.2rem; text-align: center;">
6590
- <div style="font-size: 0.7rem; color: #64748b; font-weight: bold;">Code ${code}</div>
6660
+ <div style="font-size: 0.7rem; color: #64748b; font-weight: bold;">${dict.matrix.code} ${code}</div>
6591
6661
  <div style="font-size: 0.85rem; font-weight: bold; color: ${displayColor};">${rate.toFixed(2)}%</div>
6592
6662
  </div>
6593
6663
  `;
6594
6664
  }).join("");
6595
6665
  const gaugeColor = cards.healthScore >= 90 ? "#10b981" : cards.healthScore >= 70 ? "#f59e0b" : "#ef4444";
6596
- const htmlContent = `<!DOCTYPE html><html lang="en"><head>
6666
+ const htmlContent = `<!DOCTYPE html>
6667
+ <html lang="${config.locale}" dir="${dir}">
6668
+ <head>
6597
6669
  <meta charset="UTF-8">
6598
- <title>Blaze Core Performance Report</title>
6670
+ <title>${dict.title}</title>
6599
6671
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
6600
6672
  <style>
6601
- body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #0b111e; color: #f8fafc; margin: 0; padding: 2rem; }
6673
+ body { font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #0b111e; color: #f8fafc; margin: 0; padding: 2rem; }
6602
6674
  .container { max-width: 1300px; margin: 0 auto; }
6603
6675
  .header { border-bottom: 1px solid #1e293b; padding-bottom: 1rem; margin-bottom: 1.5rem; }
6604
6676
  h1 { color: #38bdf8; margin: 0; font-size: 2rem; }
@@ -6608,11 +6680,11 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
6608
6680
  .metric-card { background: #131c2e; border: 1px solid #1e293b; border-radius: 6px; padding: 1.25rem; position: relative; }
6609
6681
  .card-title { font-size: 0.75rem; font-weight: 700; color: #64748b; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.5rem; }
6610
6682
  .card-value { font-size: 1.6rem; font-weight: 700; color: #ffffff; }
6611
- .card-value .unit { font-size: 0.9rem; font-weight: 500; color: #94a3b8; margin-left: 0.2rem; }
6683
+ .card-value .unit { font-size: 0.9rem; font-weight: 500; color: #94a3b8; margin-inline-start: 0.2rem; }
6612
6684
  .card-value.green { color: #10b981; }
6613
6685
  .card-value.orange { color: #f97316; }
6614
6686
  .card-value.purple { color: #a855f7; }
6615
- .card-subtext { font-size: 0.8rem; color: #10b981; font-weight: 500; margin-left: 0.4rem; display: inline-block; }
6687
+ .card-subtext { font-size: 0.8rem; color: #10b981; font-weight: 500; margin-inline-start: 0.4rem; display: inline-block; }
6616
6688
 
6617
6689
  .top-layout-grid { display: grid; grid-template-columns: 1fr 280px 380px; gap: 1.5rem; margin-bottom: 2rem; }
6618
6690
  .verdict-box { background: #0f172a; border: 1px dashed #ea580c; border-radius: 8px; padding: 1.25rem; }
@@ -6637,7 +6709,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
6637
6709
  .badge-5xx { background: rgba(220, 38, 38, 0.2); color: #f87171; }
6638
6710
  .matrix-value { font-size: 1rem; font-weight: bold; color: #ffffff; }
6639
6711
 
6640
- /* Cross-browser engine badge utility styles */
6641
6712
  .engine-badge { display: inline-block; padding: 4px 10px; border-radius: 6px; font-weight: 600; font-size: 12px; }
6642
6713
  .badge-blink { background: #dbeafe; color: #1e40af; }
6643
6714
  .badge-webkit { background: #fef3c7; color: #92400e; }
@@ -6652,7 +6723,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
6652
6723
  .card { background: #131c2e; border: 1px solid #1e293b; border-radius: 8px; padding: 1.5rem; }
6653
6724
  h3 { margin-top: 0; color: #cbd5e1; border-bottom: 1px solid #1e293b; padding-bottom: 0.5rem; }
6654
6725
  table { width: 100%; border-collapse: collapse; margin-top: 1rem; }
6655
- th, td { padding: 0.75rem; text-align: left; border-bottom: 1px solid #1e293b; }
6726
+ th, td { padding: 0.75rem; text-align: start; border-bottom: 1px solid #1e293b; }
6656
6727
  th { color: #94a3b8; font-weight: 600; }
6657
6728
  .badge { padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.8rem; font-weight: bold; }
6658
6729
  .badge-success { background: #16a34a; color: #f0fdf4; }
@@ -6660,41 +6731,41 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
6660
6731
  </style></head><body>
6661
6732
  <div class="container">
6662
6733
  <div class="header">
6663
- <h1>\u{1F525} Blaze Core Performance Analysis</h1>
6664
- <div class="meta">Target Script: <code>${config.targetScript}</code> | Ramp-Up: ${config.rampUpSec}s | Ramp-Down: ${config.rampDownSec}s</div>
6734
+ <h1>\u{1F525} ${dict.title}</h1>
6735
+ <div class="meta">${dict.targetScript}: <code>${config.targetScript}</code> | ${dict.rampUp}: ${config.rampUpSec}s | ${dict.rampDown}: ${config.rampDownSec}s | ${dict.generatedAt}: ${formattedDate}</div>
6665
6736
  </div>
6666
6737
 
6667
6738
  <div class="cards-wrapper">
6668
6739
  <div class="metric-card">
6669
- <div class="card-title">Apdex Score (T: 50ms)</div>
6670
- <div class="card-value green">${cards.apdex.toFixed(2)}<span class="card-subtext">(Good)</span></div>
6740
+ <div class="card-title">${dict.metrics.apdex}</div>
6741
+ <div class="card-value green">${cards.apdex.toFixed(2)}<span class="card-subtext">${dict.metrics.good}</span></div>
6671
6742
  </div>
6672
6743
  <div class="metric-card">
6673
- <div class="card-title">Throughput</div>
6744
+ <div class="card-title">${dict.metrics.throughput}</div>
6674
6745
  <div class="card-value orange">${cards.throughput.toFixed(1)}<span class="unit">/s</span></div>
6675
6746
  </div>
6676
6747
  <div class="metric-card">
6677
- <div class="card-title">Network Bandwidth</div>
6748
+ <div class="card-title">${dict.metrics.bandwidth}</div>
6678
6749
  <div class="card-value purple">${cards.bandwidth.toFixed(2)}<span class="unit">MB/s</span></div>
6679
6750
  </div>
6680
6751
  <div class="metric-card">
6681
- <div class="card-title">Avg Time To First Byte</div>
6752
+ <div class="card-title">${dict.metrics.ttfb}</div>
6682
6753
  <div class="card-value">${cards.ttfb.toFixed(1)}<span class="unit">ms</span></div>
6683
6754
  </div>
6684
6755
  <div class="metric-card">
6685
- <div class="card-title">DNS Lookup</div>
6756
+ <div class="card-title">${dict.metrics.dns}</div>
6686
6757
  <div class="card-value">${cards.dns.toFixed(2)}<span class="unit">ms</span></div>
6687
6758
  </div>
6688
6759
  <div class="metric-card">
6689
- <div class="card-title">TCP Connect</div>
6760
+ <div class="card-title">${dict.metrics.tcp}</div>
6690
6761
  <div class="card-value">${cards.tcp.toFixed(2)}<span class="unit">ms</span></div>
6691
6762
  </div>
6692
6763
  <div class="metric-card">
6693
- <div class="card-title">TLS Handshake</div>
6764
+ <div class="card-title">${dict.metrics.tls}</div>
6694
6765
  <div class="card-value">${cards.tls.toFixed(2)}<span class="unit">ms</span></div>
6695
6766
  </div>
6696
6767
  <div class="metric-card">
6697
- <div class="card-title">Stability (Std Dev)</div>
6768
+ <div class="card-title">${dict.metrics.stability}</div>
6698
6769
  <div class="card-value">&plusmn;${cards.stdDev.toFixed(1)}<span class="unit">ms</span></div>
6699
6770
  </div>
6700
6771
  </div>
@@ -6704,13 +6775,13 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
6704
6775
 
6705
6776
  <div class="top-layout-grid" style="margin-top: 2rem;">
6706
6777
  <div class="verdict-box">
6707
- <div class="verdict-title">\u{1F916} AI Stress-Testing Agent Verdict</div>
6778
+ <div class="verdict-title">${dict.verdict}</div>
6708
6779
  <div class="verdict-text">${verdictReason}</div>
6709
6780
  <div class="verdict-waves">${waveListItems}</div>
6710
6781
  </div>
6711
6782
 
6712
6783
  <div class="gauge-container-box">
6713
- <div class="gauge-title">Blaze Run Health Score</div>
6784
+ <div class="gauge-title">${dict.health.title}</div>
6714
6785
  <div style="position: relative; width: 140px; height: 140px; display: flex; align-items: center; justify-content: center;">
6715
6786
  <svg width="140" height="140" viewBox="0 0 140 140" style="transform: rotate(-90deg);">
6716
6787
  <circle cx="70" cy="70" r="58" stroke="#1e293b" stroke-width="12" fill="transparent"/>
@@ -6724,35 +6795,35 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
6724
6795
  </div>
6725
6796
  </div>
6726
6797
  <div style="margin-top: 0.75rem; font-size: 0.8rem; color: #94a3b8; font-weight: 500;">
6727
- Composite Run Quality Engine Rating
6798
+ ${dict.health.subtitle}
6728
6799
  </div>
6729
6800
  </div>
6730
6801
 
6731
6802
  <div class="matrix-box">
6732
- <div class="matrix-title">\u{1F4CB} HTTP Response Matrix</div>
6803
+ <div class="matrix-title">${dict.matrix.title}</div>
6733
6804
  <div class="matrix-row">
6734
- <div class="matrix-label">Informational <span class="matrix-badge badge-1xx">1xx</span></div>
6805
+ <div class="matrix-label">${dict.matrix.info} <span class="matrix-badge badge-1xx">1xx</span></div>
6735
6806
  <div class="matrix-value">${responseMatrix.total1xx}</div>
6736
6807
  </div>
6737
6808
  <div class="matrix-row">
6738
- <div class="matrix-label">Successful <span class="matrix-badge badge-2xx">2xx</span></div>
6809
+ <div class="matrix-label">${dict.matrix.success} <span class="matrix-badge badge-2xx">2xx</span></div>
6739
6810
  <div class="matrix-value">${responseMatrix.total2xx}</div>
6740
6811
  </div>
6741
6812
  <div class="matrix-row">
6742
- <div class="matrix-label">Redirects <span class="matrix-badge badge-3xx">3xx</span></div>
6813
+ <div class="matrix-label">${dict.matrix.redirects} <span class="matrix-badge badge-3xx">3xx</span></div>
6743
6814
  <div class="matrix-value">${responseMatrix.total3xx}</div>
6744
6815
  </div>
6745
6816
  <div class="matrix-row">
6746
- <div class="matrix-label">Client Error <span class="matrix-badge badge-4xx">4xx</span></div>
6817
+ <div class="matrix-label">${dict.matrix.clientErr} <span class="matrix-badge badge-4xx">4xx</span></div>
6747
6818
  <div class="matrix-value">${responseMatrix.total4xx}</div>
6748
6819
  </div>
6749
6820
  <div class="matrix-row">
6750
- <div class="matrix-label">Server Error <span class="matrix-badge badge-5xx">5xx</span></div>
6821
+ <div class="matrix-label">${dict.matrix.serverErr} <span class="matrix-badge badge-5xx">5xx</span></div>
6751
6822
  <div class="matrix-value">${responseMatrix.total5xx}</div>
6752
6823
  </div>
6753
6824
 
6754
6825
  <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;">
6755
- \u{1F522} HTTP Status Code Breakdown Rate
6826
+ ${dict.matrix.breakdown}
6756
6827
  </div>
6757
6828
  <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.5rem; margin-top: 0.5rem;">
6758
6829
  ${breakdownGridHtml}
@@ -6762,7 +6833,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
6762
6833
 
6763
6834
  <div class="card" style="margin-top: 2rem; background: #0c172b; border: 1px solid #7c3aed;">
6764
6835
  <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;">
6765
- \u26A1 Gemini 2.5 Flash Autonomous Root Cause Analysis (RCA)
6836
+ ${dict.rca}
6766
6837
  </h3>
6767
6838
  <div style="font-size: 0.95rem; line-height: 1.6; color: #cbd5e1; padding: 0.5rem 0; white-space: pre-wrap; word-break: break-word;">
6768
6839
  ${geminiAnalysisHtml}
@@ -6771,47 +6842,45 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
6771
6842
 
6772
6843
  <div class="charts-grid" style="margin-top: 2rem;">
6773
6844
  <div class="graph-card">
6774
- <div class="graph-title">\u{1F4CA} Throughput Velocity & Workload Volume</div>
6845
+ <div class="graph-title">${dict.charts.volume}</div>
6775
6846
  <div style="height: 280px; position: relative;">
6776
6847
  <canvas id="volumeChart"></canvas>
6777
6848
  </div>
6778
6849
  </div>
6779
6850
 
6780
6851
  <div class="graph-card">
6781
- <div class="graph-title">\u{1F504} Active Concurrency (VUs) vs. TTF Trend & AI Load Forecast</div>
6852
+ <div class="graph-title">${dict.charts.concurrency}</div>
6782
6853
  <div style="height: 280px; position: relative;">
6783
6854
  <canvas id="concurrencyChart"></canvas>
6784
6855
  </div>
6785
6856
  </div>
6786
6857
 
6787
6858
  <div class="graph-card">
6788
- <div class="graph-title">\u{1F369} HTTP Status Code Proportions</div>
6859
+ <div class="graph-title">${dict.charts.status}</div>
6789
6860
  <div style="height: 280px; position: relative;">
6790
6861
  <canvas id="statusDonutChart"></canvas>
6791
6862
  </div>
6792
6863
  </div>
6793
6864
 
6794
6865
  <div class="graph-card">
6795
- <div class="graph-title">\u{1F4C8} Time-to-First-Byte Scatter Plot</div>
6866
+ <div class="graph-title">${dict.charts.scatter}</div>
6796
6867
  <div style="height: 280px; position: relative;">
6797
6868
  <canvas id="ttfbScatterChart"></canvas>
6798
6869
  </div>
6799
6870
  </div>
6800
6871
  </div>
6801
-
6802
- ${nlpQueryEngineHtml}
6803
6872
 
6804
6873
  <div class="card" style="margin-top: 2rem;">
6805
- <h3>Telemetry Matrix Logs</h3>
6874
+ <h3>${dict.telemetry.title}</h3>
6806
6875
  <table>
6807
6876
  <thead>
6808
6877
  <tr>
6809
- <th>Concurrency (Threads)</th>
6810
- <th>Throughput</th>
6811
- <th>Avg Latency</th>
6812
- <th>P95 Latency</th>
6813
- <th>Error Rate</th>
6814
- <th>Status</th>
6878
+ <th>${dict.telemetry.threads}</th>
6879
+ <th>${dict.telemetry.throughput}</th>
6880
+ <th>${dict.telemetry.avgLatency}</th>
6881
+ <th>${dict.telemetry.p95}</th>
6882
+ <th>${dict.telemetry.errors}</th>
6883
+ <th>${dict.telemetry.status}</th>
6815
6884
  </tr>
6816
6885
  </thead>
6817
6886
  <tbody id="telemetryTableBody">
@@ -6819,103 +6888,23 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
6819
6888
  const isPassed = h.apdex >= config.targetApdex && h.errorRate <= config.targetErrorRate;
6820
6889
  return `<tr>
6821
6890
  <td><strong>${h.threads}</strong></td>
6822
- <td>${h.throughput.toFixed(0)} req/sec</td>
6891
+ <td>${h.throughput.toFixed(0)} /s</td>
6823
6892
  <td>${h.avgLatency.toFixed(1)} ms</td>
6824
6893
  <td>${h.p95Latency.toFixed(1)} ms</td>
6825
6894
  <td>${(h.errorRate * 100).toFixed(2)}%</td>
6826
- <td><span class="badge ${isPassed ? "badge-success" : "badge-fail"}">${isPassed ? "SLO PASS" : "SLO BREACH"}</span></td>
6895
+ <td><span class="badge ${isPassed ? "badge-success" : "badge-fail"}">${isPassed ? dict.telemetry.pass : dict.telemetry.fail}</span></td>
6827
6896
  </tr>`;
6828
6897
  }).join("")}
6829
6898
  </tbody>
6830
6899
  </table>
6831
6900
  </div>
6832
- ${seoImpactSectionHtml}
6833
- ${liveAdjusterHtml}
6834
- ${rightSizingHtml}
6835
6901
  ${logClustersHtml}
6836
6902
  </div>
6837
6903
 
6838
6904
  <script>
6839
- const telemetryHistory = ${JSON.stringify(history)};
6840
6905
  const labels = ${JSON.stringify(labels)};
6841
- const baseThroughput = ${cards.throughput};
6842
- const baseLatency = ${cards.ttfb};
6843
-
6844
- const slider = document.getElementById('concurrencySlider');
6845
- const sliderVal = document.getElementById('sliderValue');
6846
- const simThroughput = document.getElementById('simThroughput');
6847
- const simLatency = document.getElementById('simLatency');
6848
-
6849
- if (slider) {
6850
- slider.addEventListener('input', (e) => {
6851
- const val = parseInt(e.target.value, 10);
6852
- sliderVal.textContent = val;
6853
- const factor = val / ${config.threads || 10};
6854
- simThroughput.textContent = (baseThroughput * Math.min(factor, 2.5)).toFixed(0) + '/s';
6855
- simLatency.textContent = (baseLatency * Math.pow(factor, 0.8)).toFixed(1) + 'ms';
6856
- });
6857
- }
6858
-
6859
- function executeNlpQuery(queryString) {
6860
- const q = queryString.toLowerCase().trim();
6861
- const rows = document.querySelectorAll('#telemetryTableBody tr');
6862
- const feedback = document.getElementById('nlpQueryFeedback');
6863
-
6864
- let filterFn = (row, data) => true;
6865
- const numMatch = q.match(/(d+(?:.d+)?)/);
6866
- const targetNum = numMatch ? parseFloat(numMatch[1]) : null;
6867
-
6868
- const isGreater = q.includes('>') || q.includes('greater') || q.includes('above') || q.includes('more');
6869
- const isLess = q.includes('<') || q.includes('less') || q.includes('below') || q.includes('under');
6870
-
6871
- if (q.includes('latency') || q.includes('p95') || q.includes('ms')) {
6872
- if (targetNum !== null) {
6873
- filterFn = (row, data) => isLess ? (data.p95Latency < targetNum) : (data.p95Latency > targetNum);
6874
- } else {
6875
- filterFn = (row, data) => data.p95Latency > 0;
6876
- }
6877
- } else if (q.includes('error') || q.includes('fail') || q.includes('%')) {
6878
- if (targetNum !== null) {
6879
- const searchVal = q.includes('%') ? targetNum / 100 : targetNum;
6880
- filterFn = (row, data) => isLess ? (data.errorRate < searchVal) : (data.errorRate > searchVal);
6881
- } else {
6882
- filterFn = (row, data) => data.errorRate > 0;
6883
- }
6884
- } else if (q.includes('concurrency') || q.includes('thread') || q.includes('vu')) {
6885
- if (targetNum !== null) {
6886
- filterFn = (row, data) => isLess ? (data.threads < targetNum) : (data.threads > targetNum);
6887
- }
6888
- }
6889
-
6890
- let matchCount = 0;
6891
- rows.forEach((row, idx) => {
6892
- const data = telemetryHistory[idx];
6893
- if (!data) return;
6894
- if (filterFn(row, data)) {
6895
- row.style.display = '';
6896
- row.style.background = q !== '' ? 'rgba(14, 165, 233, 0.15)' : '';
6897
- matchCount++;
6898
- } else {
6899
- row.style.display = 'none';
6900
- }
6901
- });
6902
-
6903
- feedback.innerHTML = q === '' ? '' : '\u{1F50D} Match verification resolved <strong>' + matchCount + '</strong> parameters inside telemetry array.';
6904
- }
6905
-
6906
- function setNlpQuery(str) {
6907
- const input = document.getElementById('nlpQueryInput');
6908
- input.value = str;
6909
- executeNlpQuery(str);
6910
- }
6911
-
6912
- document.getElementById('nlpQueryBtn').addEventListener('click', () => {
6913
- executeNlpQuery(document.getElementById('nlpQueryInput').value);
6914
- });
6915
-
6916
- document.getElementById('nlpQueryInput').addEventListener('keyup', (e) => {
6917
- if (e.key === 'Enter') executeNlpQuery(e.target.value);
6918
- });
6906
+ const successRequestsData = ${JSON.stringify(successRequestsData)};
6907
+ const failedWorkloadsData = ${JSON.stringify(failedWorkloadsData)};
6919
6908
 
6920
6909
  new Chart(document.getElementById('volumeChart'), {
6921
6910
  type: 'bar',
@@ -6923,16 +6912,16 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
6923
6912
  labels: labels,
6924
6913
  datasets: [
6925
6914
  {
6926
- label: 'Successful Requests',
6927
- data: ${JSON.stringify(successRequestsData)},
6915
+ label: '${dict.matrix.success}',
6916
+ data: successRequestsData,
6928
6917
  backgroundColor: '#10b981',
6929
6918
  stack: 'requests',
6930
6919
  barPercentage: 0.95,
6931
6920
  categoryPercentage: 0.95
6932
6921
  },
6933
6922
  {
6934
- label: 'Failed Workloads',
6935
- data: ${JSON.stringify(failedWorkloadsData)},
6923
+ label: '${dict.matrix.serverErr}',
6924
+ data: failedWorkloadsData,
6936
6925
  backgroundColor: '#ef4444',
6937
6926
  stack: 'requests',
6938
6927
  barPercentage: 0.95,
@@ -6955,50 +6944,15 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
6955
6944
 
6956
6945
  const baseActiveThreads = ${JSON.stringify(activeThreadsData)};
6957
6946
  const baseTtfTrend = ${JSON.stringify(ttfTrendData)};
6958
- const historicalCount = baseTtfTrend.length;
6959
-
6960
- let extendedLabels = [...labels];
6961
- let extendedThreads = [...baseActiveThreads];
6962
- let extendedTtf = [...baseTtfTrend];
6963
- let aiForecastData = [];
6964
-
6965
- if (historicalCount >= 2) {
6966
- let sumX = 0, sumY = 0, sumXY = 0, sumXX = 0;
6967
- for (let i = 0; i < historicalCount; i++) {
6968
- sumX += i;
6969
- sumY += baseTtfTrend[i];
6970
- sumXY += i * baseTtfTrend[i];
6971
- sumXX += i * i;
6972
- }
6973
- const denominator = (historicalCount * sumXX - sumX * sumX);
6974
- const slope = denominator === 0 ? 0 : (historicalCount * sumXY - sumX * sumY) / denominator;
6975
- const intercept = (sumY - slope * sumX) / historicalCount;
6976
-
6977
- for (let i = 0; i < historicalCount; i++) {
6978
- aiForecastData.push(Number((slope * i + intercept).toFixed(1)));
6979
- }
6980
-
6981
- const lastThreads = baseActiveThreads[historicalCount - 1] || 10;
6982
- const threadStep = ${config.stepSize || 15};
6983
-
6984
- for (let i = 1; i <= 3; i++) {
6985
- extendedLabels.push('+' + (i * 5) + 's (AI Forecast)');
6986
- extendedThreads.push(lastThreads + (threadStep * i));
6987
- extendedTtf.push(null);
6988
- aiForecastData.push(Number((slope * (historicalCount + i - 1) + intercept).toFixed(1)));
6989
- }
6990
- } else {
6991
- aiForecastData = [...baseTtfTrend];
6992
- }
6993
-
6947
+
6994
6948
  new Chart(document.getElementById('concurrencyChart'), {
6995
6949
  type: 'line',
6996
6950
  data: {
6997
- labels: extendedLabels,
6951
+ labels: labels,
6998
6952
  datasets: [
6999
6953
  {
7000
- label: 'Active VU Threads',
7001
- data: extendedThreads,
6954
+ label: 'VUs',
6955
+ data: baseActiveThreads,
7002
6956
  borderColor: '#38bdf8',
7003
6957
  backgroundColor: 'rgba(56, 189, 248, 0.1)',
7004
6958
  borderWidth: 2.5,
@@ -7007,25 +6961,14 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
7007
6961
  yAxisID: 'yThreads'
7008
6962
  },
7009
6963
  {
7010
- label: 'Time-to-Failure (TTF) Trend',
7011
- data: extendedTtf,
6964
+ label: 'TTF',
6965
+ data: baseTtfTrend,
7012
6966
  borderColor: '#f43f5e',
7013
6967
  borderWidth: 2,
7014
6968
  borderDash: [5, 5],
7015
6969
  pointRadius: 3,
7016
6970
  fill: false,
7017
6971
  yAxisID: 'yTtf'
7018
- },
7019
- {
7020
- label: '\u{1F52E} AI Capacity Forecast Line',
7021
- data: aiForecastData,
7022
- borderColor: '#a855f7',
7023
- borderWidth: 2,
7024
- borderDash: [3, 3],
7025
- pointRadius: 4,
7026
- pointBackgroundColor: '#a855f7',
7027
- fill: false,
7028
- yAxisID: 'yTtf'
7029
6972
  }
7030
6973
  ]
7031
6974
  },
@@ -7037,8 +6980,8 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
7037
6980
  },
7038
6981
  scales: {
7039
6982
  x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } },
7040
- yThreads: { position: 'left', grid: { color: '#1e293b' }, ticks: { color: '#38bdf8' }, title: { display: true, text: 'VUs / Threads', color: '#38bdf8' } },
7041
- yTtf: { position: 'right', grid: { drawOnChartArea: false }, ticks: { color: '#f43f5e' }, title: { display: true, text: 'TTF Index / Latency ms', color: '#f43f5e' } }
6983
+ yThreads: { position: 'left', grid: { color: '#1e293b' }, ticks: { color: '#38bdf8' } },
6984
+ yTtf: { position: 'right', grid: { drawOnChartArea: false }, ticks: { color: '#f43f5e' } }
7042
6985
  }
7043
6986
  }
7044
6987
  });
@@ -7046,7 +6989,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
7046
6989
  new Chart(document.getElementById('statusDonutChart'), {
7047
6990
  type: 'doughnut',
7048
6991
  data: {
7049
- labels: ['1xx Info', '2xx Success', '3xx Redirect', '4xx Client Err', '5xx Server Err'],
6992
+ labels: ['1xx', '2xx', '3xx', '4xx', '5xx'],
7050
6993
  datasets: [{
7051
6994
  data: [${responseMatrix.total1xx}, ${responseMatrix.total2xx}, ${responseMatrix.total3xx}, ${responseMatrix.total4xx}, ${responseMatrix.total5xx}],
7052
6995
  backgroundColor: ['#38bdf8', '#4ade80', '#cbd5e1', '#fde047', '#f87171'],
@@ -7068,7 +7011,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
7068
7011
  type: 'scatter',
7069
7012
  data: {
7070
7013
  datasets: [{
7071
- label: 'Request Processing Delay',
7014
+ label: 'Delay',
7072
7015
  data: ${JSON.stringify(scatterPoints)},
7073
7016
  backgroundColor: '#a855f7',
7074
7017
  pointRadius: 4,
@@ -7082,15 +7025,15 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
7082
7025
  legend: { position: 'top', labels: { color: '#94a3b8', font: { size: 11 } } }
7083
7026
  },
7084
7027
  scales: {
7085
- x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' }, title: { display: true, text: 'Time offset (s)', color: '#64748b' } },
7086
- y: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' }, title: { display: true, text: 'TTFB / Delay (ms)', color: '#64748b' } }
7028
+ x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } },
7029
+ y: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } }
7087
7030
  }
7088
7031
  }
7089
7032
  });
7090
7033
  </script></body></html>`;
7091
7034
  try {
7092
7035
  fs.writeFileSync(config.outputHtml, htmlContent, "utf-8");
7093
- console.log(`\u{1F4CA} Standalone Dashboard exported successfully to: ${path.resolve(config.outputHtml)}`);
7036
+ console.log(`\u{1F4CA} Standalone Localized Dashboard exported successfully to: ${path.resolve(config.outputHtml)}`);
7094
7037
  } catch (err) {
7095
7038
  console.error(`\u274C Failed to write HTML output dashboard: ${err}`);
7096
7039
  }
@@ -7098,7 +7041,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
7098
7041
  function printUsage() {
7099
7042
  console.log(`Blaze Core Performance Test CLI Engine
7100
7043
  Options:
7101
- --threads <count> | --duration <seconds> | --baseline <json_file> | --output <html_file> | --cross-browser | --option elif`);
7044
+ --threads <count> | --duration <seconds> | --baseline <json_file> | --output <html_file> | --cross-browser | --locale <lang_code> | --option elif`);
7102
7045
  }
7103
7046
  runCli().catch(console.error);
7104
7047
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blaze-performance-tester",
3
- "version": "3.2.23",
3
+ "version": "3.2.25",
4
4
  "description": "A high-performance, multi-threaded load testing engine built with Rust and QuickJS.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",