blaze-performance-tester 3.2.32 → 3.2.34

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 +182 -7
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -5747,7 +5747,7 @@ var i18n = {
5747
5747
  good: "(\u0905\u091A\u094D\u091B\u093E)"
5748
5748
  },
5749
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 \u0915\u094D\u0935\u093E\u0932\u093F\u091F\u0940 \u0907\u0902\u091C\u0928 \u0930\u0947\u091F\u093F\u0902\u0917" },
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
5751
  matrix: {
5752
5752
  title: "\u{1F4CB} HTTP \u0930\u093F\u0938\u094D\u092A\u0949\u0928\u094D\u0938 \u092E\u0948\u091F\u094D\u0930\u093F\u0915\u094D\u0938",
5753
5753
  info: "\u0938\u0942\u091A\u0928\u093E\u0924\u094D\u092E\u0915",
@@ -5976,6 +5976,34 @@ ${JSON.stringify(metrics, null, 2)}
5976
5976
  return null;
5977
5977
  }
5978
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
+ }
5979
6007
  async function generateGeminiRCA(history, finalSummary, errorLogs) {
5980
6008
  const apiKey = process.env.GEMINI_API_KEY;
5981
6009
  if (!apiKey) {
@@ -6424,6 +6452,7 @@ async function runIntelligentAgenticStressTest(config) {
6424
6452
  };
6425
6453
  generateFinalAgentReport(history, healthScore);
6426
6454
  const geminiAnalysisHtml = await generateGeminiRCA(history, finalSummaryCards, aggregateErrorLogs);
6455
+ const wittyBriefingText = await generateWittyAudioBriefing(finalSummaryCards);
6427
6456
  if (config.optionValue === "elif") {
6428
6457
  const plainEnglishSummary = await generateElifExplanation(finalSummaryCards);
6429
6458
  if (plainEnglishSummary) {
@@ -6443,7 +6472,7 @@ async function runIntelligentAgenticStressTest(config) {
6443
6472
  breakdownRates[code] = totalRequestsAccumulator === 0 ? 0 : globalCodeCounts[code] / totalRequestsAccumulator * 100;
6444
6473
  });
6445
6474
  const baselineData = loadBaselineData(config.baselinePath);
6446
- exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx, breakdownRates }, finalSummaryCards, lastRawRequests, geminiAnalysisHtml, baselineData);
6475
+ exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx, breakdownRates }, finalSummaryCards, lastRawRequests, geminiAnalysisHtml, wittyBriefingText, baselineData);
6447
6476
  }
6448
6477
  async function runStandardStressTest(config) {
6449
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]`);
@@ -6486,6 +6515,7 @@ async function runStandardStressTest(config) {
6486
6515
  const isPassed = metrics.apdexScore >= config.targetApdex && metrics.errorRate <= config.targetErrorRate;
6487
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.";
6488
6517
  const geminiAnalysisHtml = await generateGeminiRCA(history, finalSummaryCards, rawErrors.length > 0 ? rawErrors : getFallbackClusterLogs());
6518
+ const wittyBriefingText = await generateWittyAudioBriefing(finalSummaryCards);
6489
6519
  if (config.optionValue === "elif") {
6490
6520
  const plainEnglishSummary = await generateElifExplanation(finalSummaryCards);
6491
6521
  if (plainEnglishSummary) {
@@ -6511,7 +6541,7 @@ async function runStandardStressTest(config) {
6511
6541
  total4xx: metrics.c4xx,
6512
6542
  total5xx: metrics.c5xx,
6513
6543
  breakdownRates
6514
- }, finalSummaryCards, rawRequests, geminiAnalysisHtml, baselineData);
6544
+ }, finalSummaryCards, rawRequests, geminiAnalysisHtml, wittyBriefingText, baselineData);
6515
6545
  }
6516
6546
  function getFallbackClusterLogs() {
6517
6547
  const logs = [];
@@ -6576,7 +6606,7 @@ function processMetricsTelemetry(requests, durationSec) {
6576
6606
  const avgTlsMs = mathUtils.mean(requests.map((r) => r.tlsTimeMs || 0));
6577
6607
  const avgTtfbMs = avgDnsMs + avgTcpMs + avgTlsMs + avgLatencyMs * 0.3;
6578
6608
  let c1xx = 0, c2xx = 0, c3xx = 0, c4xx = 0, c5xx = 0;
6579
- const codeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500: 0, 502: 0, 503: 0, 504: 0 };
6609
+ const codeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500: 502, 503: 0, 504: 0 };
6580
6610
  [400, 401, 403, 404, 429, 500, 502, 503, 504].forEach((c) => codeCounts[c] = 0);
6581
6611
  requests.forEach((r) => {
6582
6612
  if (r.statusCode >= 100 && r.statusCode < 200)
@@ -6644,7 +6674,7 @@ function generateFinalAgentReport(history, score) {
6644
6674
  console.log(`\u26A1 Peak Achieved Cluster Velocity : ${peakThroughput.toFixed(0)} req/sec`);
6645
6675
  console.log("=======================================================\n");
6646
6676
  }
6647
- function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards, rawRequests = [], geminiAnalysisHtml = "", baselineData = null) {
6677
+ function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards, rawRequests = [], geminiAnalysisHtml = "", wittyBriefingText = "", baselineData = null) {
6648
6678
  const dict = i18n[config.locale] || i18n["en-US"];
6649
6679
  const dir = dict.rtl ? "rtl" : "ltr";
6650
6680
  const formatter = new Intl.DateTimeFormat(config.locale || "en-US", {
@@ -6749,9 +6779,14 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
6749
6779
  </table>
6750
6780
  </div>`;
6751
6781
  let logClustersHtml = "";
6782
+ let formattedTicketLogs = "No structural error codes encountered.";
6752
6783
  if (semanticReport) {
6753
6784
  const totalLogCount = semanticReport.clusters.reduce((sum, c) => sum + c.count, 0);
6754
- const uniquePatternsCount = semanticReport.clusters.length;
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");
6755
6790
  logClustersHtml = `
6756
6791
  <div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
6757
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;">
@@ -6798,6 +6833,25 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
6798
6833
  </table>
6799
6834
  </div>`;
6800
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.*`;
6801
6855
  const breakdownRates = responseMatrix.breakdownRates || {};
6802
6856
  const breakdownGridHtml = [400, 401, 403, 404, 429, 500, 502, 503, 504].map((code) => {
6803
6857
  const rate = breakdownRates[code] || 0;
@@ -6827,7 +6881,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
6827
6881
  .metric-card { background: #131c2e; border: 1px solid #1e293b; border-radius: 6px; padding: 1.25rem; position: relative; }
6828
6882
  .card-title { font-size: 0.75rem; font-weight: 700; color: #64748b; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.5rem; }
6829
6883
  .card-value { font-size: 1.6rem; font-weight: 700; color: #ffffff; }
6830
- .card-value .unit { font-size: 0.9rem; font-weight: 500; color: #94a3b8; margin-inline-start: 0.2rem; }
6884
+ .card-value .unit { font-size: 0.9rem; font-weight: 500; color: #94a3b8; margin-inline-start: 0.2rem; display: inline-block; }
6831
6885
  .card-value.green { color: #10b981; }
6832
6886
  .card-value.orange { color: #f97316; }
6833
6887
  .card-value.purple { color: #a855f7; }
@@ -6881,6 +6935,28 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
6881
6935
  <h1>\u{1F525} ${dict.title}</h1>
6882
6936
  <div class="meta">${dict.targetScript}: <code>${config.targetScript}</code> | ${dict.rampUp}: ${config.rampUpSec}s | ${dict.rampDown}: ${config.rampDownSec}s | ${dict.generatedAt}: ${formattedDate}</div>
6883
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>
6884
6960
 
6885
6961
  <div class="cards-wrapper">
6886
6962
  <div class="metric-card">
@@ -6987,6 +7063,19 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
6987
7063
  </div>
6988
7064
  </div>
6989
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
+
6990
7079
  <div class="charts-grid" style="margin-top: 2rem;">
6991
7080
  <div class="graph-card">
6992
7081
  <div class="graph-title">${dict.charts.volume}</div>
@@ -7049,6 +7138,92 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
7049
7138
  </div>
7050
7139
 
7051
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
+
7052
7227
  const labels = ${JSON.stringify(labels)};
7053
7228
  const successRequestsData = ${JSON.stringify(successRequestsData)};
7054
7229
  const failedWorkloadsData = ${JSON.stringify(failedWorkloadsData)};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blaze-performance-tester",
3
- "version": "3.2.32",
3
+ "version": "3.2.34",
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",