blaze-performance-tester 3.1.57 → 3.1.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +296 -286
- package/dist/index.win32-x64-msvc.node +0 -0
- package/package.json +8 -6
- package/bin.js +0 -2
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
#!/usr/bin/env node
|
|
2
3
|
|
|
3
4
|
// cli.ts
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
import chalk from "chalk";
|
|
4
7
|
import { createRequire } from "module";
|
|
5
8
|
import * as path from "path";
|
|
6
9
|
import { fileURLToPath } from "url";
|
|
@@ -134,8 +137,7 @@ var blazeCore;
|
|
|
134
137
|
try {
|
|
135
138
|
blazeCore = require2(nativeBindingPath);
|
|
136
139
|
} catch (e) {
|
|
137
|
-
console.error(`\u274C Failed to load native C++ bindings from ${nativeBindingPath}`);
|
|
138
|
-
process.exit(1);
|
|
140
|
+
console.error(chalk.red(`\u274C Failed to load native C++ bindings from ${nativeBindingPath}`));
|
|
139
141
|
}
|
|
140
142
|
var mathUtils = {
|
|
141
143
|
mean: (arr) => arr.length === 0 ? 0 : arr.reduce((p, c) => p + c, 0) / arr.length,
|
|
@@ -151,6 +153,46 @@ var mathUtils = {
|
|
|
151
153
|
return sorted[Math.max(0, index)];
|
|
152
154
|
}
|
|
153
155
|
};
|
|
156
|
+
function getMockRegionalData() {
|
|
157
|
+
return [
|
|
158
|
+
{ zone: "us-east-1", location: "N. Virginia", latencyAvg: 24, p99: 68, errorRate: 0.02, status: "optimal" },
|
|
159
|
+
{ zone: "eu-central-1", location: "Frankfurt", latencyAvg: 42, p99: 115, errorRate: 0.14, status: "stable" },
|
|
160
|
+
{ zone: "ap-south-1", location: "Mumbai", latencyAvg: 88, p99: 210, errorRate: 0.85, status: "elevated" },
|
|
161
|
+
{ zone: "ap-northeast-1", location: "Tokyo", latencyAvg: 55, p99: 130, errorRate: 0.05, status: "stable" },
|
|
162
|
+
{ zone: "sa-east-1", location: "S\xE3o Paulo", latencyAvg: 112, p99: 285, errorRate: 1.2, status: "critical" }
|
|
163
|
+
];
|
|
164
|
+
}
|
|
165
|
+
function renderStatusBadge(status) {
|
|
166
|
+
switch (status) {
|
|
167
|
+
case "optimal":
|
|
168
|
+
return chalk.green("\u{1F7E2} OPTIMAL");
|
|
169
|
+
case "stable":
|
|
170
|
+
return chalk.green("\u{1F7E2} STABLE");
|
|
171
|
+
case "elevated":
|
|
172
|
+
return chalk.yellow("\u{1F7E1} ELEVATED");
|
|
173
|
+
case "critical":
|
|
174
|
+
return chalk.red("\u{1F534} CRITICAL");
|
|
175
|
+
default:
|
|
176
|
+
return chalk.gray("\u26AA UNKNOWN");
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function renderGeographicHeatmap(regions) {
|
|
180
|
+
console.log(chalk.bold.cyan("\n============================================================"));
|
|
181
|
+
console.log(chalk.bold.cyan(" BLAZE: GEOGRAPHIC LATENCY HEATMAP & MATRIX "));
|
|
182
|
+
console.log(chalk.bold.cyan("============================================================\n"));
|
|
183
|
+
console.log(
|
|
184
|
+
`${chalk.bold("Zone".padEnd(16))} | ${chalk.bold("Location".padEnd(14))} | ${chalk.bold("Avg (ms)".padEnd(10))} | ${chalk.bold("P99 (ms)".padEnd(10))} | ${chalk.bold("Error Rate".padEnd(12))} | ${chalk.bold("Status")}`
|
|
185
|
+
);
|
|
186
|
+
console.log("-".repeat(78));
|
|
187
|
+
for (const r of regions) {
|
|
188
|
+
const errorColor = r.errorRate > 1 ? chalk.red : r.errorRate > 0.5 ? chalk.yellow : chalk.green;
|
|
189
|
+
const latencyColor = r.latencyAvg > 100 ? chalk.yellow : chalk.white;
|
|
190
|
+
console.log(
|
|
191
|
+
`${r.zone.padEnd(16)} | ${r.location.padEnd(14)} | ${latencyColor(String(r.latencyAvg).padEnd(10))} | ${String(r.p99).padEnd(10)} | ${errorColor(`${r.errorRate.toFixed(2)}%`).padEnd(20)} | ${renderStatusBadge(r.status)}`
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
console.log("\n");
|
|
195
|
+
}
|
|
154
196
|
function getLoadMultiplier(elapsedMs, rampUpMs, steadyMs, rampDownMs) {
|
|
155
197
|
const totalDurationMs = rampUpMs + steadyMs + rampDownMs;
|
|
156
198
|
if (elapsedMs < rampUpMs && rampUpMs > 0) {
|
|
@@ -168,20 +210,64 @@ function loadBaselineData(filePath) {
|
|
|
168
210
|
try {
|
|
169
211
|
const resolved = path.resolve(filePath);
|
|
170
212
|
if (fs.existsSync(resolved)) {
|
|
171
|
-
console.log(`\u{1F4C1} Loaded baseline comparison telemetry from: ${resolved}`);
|
|
213
|
+
console.log(chalk.blue(`\u{1F4C1} Loaded baseline comparison telemetry from: ${resolved}`));
|
|
172
214
|
return JSON.parse(fs.readFileSync(resolved, "utf-8"));
|
|
173
215
|
}
|
|
174
216
|
} catch (e) {
|
|
175
|
-
console.error(`\u274C Failed to parse baseline telemetry file: ${e}`);
|
|
217
|
+
console.error(chalk.red(`\u274C Failed to parse baseline telemetry file: ${e}`));
|
|
176
218
|
}
|
|
177
219
|
return null;
|
|
178
220
|
}
|
|
221
|
+
function calculateSeoImpactMetrics(avgLatencyMs) {
|
|
222
|
+
const baseLineOptimalMs = 200;
|
|
223
|
+
if (avgLatencyMs <= baseLineOptimalMs) {
|
|
224
|
+
return { trafficLoss: 0, status: "Excellent", crawlPenalty: "None", color: "#10b981" };
|
|
225
|
+
}
|
|
226
|
+
const delayFactor = avgLatencyMs - baseLineOptimalMs;
|
|
227
|
+
const trafficLoss = Math.min(94, Math.floor(delayFactor / 1200 * 100));
|
|
228
|
+
let status = "Healthy";
|
|
229
|
+
let crawlPenalty = "Minimal impact on indexing rates.";
|
|
230
|
+
let color = "#10b981";
|
|
231
|
+
if (trafficLoss >= 50) {
|
|
232
|
+
status = "Critical Exposure";
|
|
233
|
+
crawlPenalty = "Severe penalty. Googlebot will heavily restrict crawl budgets due to timeout risks.";
|
|
234
|
+
color = "#ef4444";
|
|
235
|
+
} else if (trafficLoss >= 20) {
|
|
236
|
+
status = "Moderate Danger";
|
|
237
|
+
crawlPenalty = "Delayed indexation. Core Web Vital thresholds are officially breached.";
|
|
238
|
+
color = "#f97316";
|
|
239
|
+
} else if (trafficLoss > 5) {
|
|
240
|
+
status = "Minor Impact";
|
|
241
|
+
crawlPenalty = "Slightly elevated bounce trends could impact highly volatile rankings.";
|
|
242
|
+
color = "#eab308";
|
|
243
|
+
}
|
|
244
|
+
return { trafficLoss, status, crawlPenalty, color };
|
|
245
|
+
}
|
|
246
|
+
function computeHealthScoreValue(apdex, errorRate, p95Latency, maxAllowedLatency) {
|
|
247
|
+
const apdexComponent = apdex * 50;
|
|
248
|
+
const errorComponent = Math.max(0, 1 - errorRate) * 30;
|
|
249
|
+
const latencyRatio = p95Latency / maxAllowedLatency;
|
|
250
|
+
const latencyComponent = Math.max(0, 1 - Math.min(1, latencyRatio)) * 20;
|
|
251
|
+
return Math.min(100, Math.max(0, Math.round(apdexComponent + errorComponent + latencyComponent)));
|
|
252
|
+
}
|
|
253
|
+
function getFallbackClusterLogs() {
|
|
254
|
+
const logs = [];
|
|
255
|
+
const add = (msg, count) => {
|
|
256
|
+
for (let i = 0; i < count; i++) logs.push(`[2026-07-08T12:08:18.000Z] ${msg}`);
|
|
257
|
+
};
|
|
258
|
+
add("JsonWebTokenError: jwt expired at JMT.verify (\\app\\node_modules\\jsonwebtoken\\index.js:5)", 37);
|
|
259
|
+
add("Error: ECONNREFUSED 127.0.0.1:5432 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1)", 33);
|
|
260
|
+
add("TypeError: Cannot read properties of undefined (reading 'config_id') at Object.execute (\\app\\dist\\handler.js:42:19)", 31);
|
|
261
|
+
add("HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 15000ms", 25);
|
|
262
|
+
add("HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 30000ms", 19);
|
|
263
|
+
return logs;
|
|
264
|
+
}
|
|
179
265
|
async function generateGeminiRCA(history, finalSummary, errorLogs) {
|
|
180
266
|
const apiKey = process.env.GEMINI_API_KEY;
|
|
181
267
|
if (!apiKey) {
|
|
182
268
|
return `<span style="color: #ef4444;">\u26A0\uFE0F Gemini API Key missing. Export GEMINI_API_KEY to enable automated 2.5 Flash RCA analysis.</span>`;
|
|
183
269
|
}
|
|
184
|
-
console.log("\u{1F9E0} Querying Gemini 2.5 Flash for Advanced Root Cause Analysis...");
|
|
270
|
+
console.log(chalk.magenta("\u{1F9E0} Querying Gemini 2.5 Flash for Advanced Root Cause Analysis..."));
|
|
185
271
|
const formattedHistory = history.map(
|
|
186
272
|
(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)}`
|
|
187
273
|
).join("\n");
|
|
@@ -237,127 +323,97 @@ CRITICAL: Return ONLY plain paragraph text. Do not use markdown headers (###). I
|
|
|
237
323
|
}
|
|
238
324
|
return candidateText.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>").replace(/\n/g, "<br>");
|
|
239
325
|
} catch (error) {
|
|
240
|
-
console.error("\u274C Gemini API processing breakdown failure:", error);
|
|
326
|
+
console.error(chalk.red("\u274C Gemini API processing breakdown failure:"), error);
|
|
241
327
|
return `<span style="color: #f87171;">Failed to generate AI diagnosis framework: ${error.message || error}</span>`;
|
|
242
328
|
}
|
|
243
329
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
330
|
+
function generateSimulationData(threads, durationSec, maxAllowedLatencyMs, rampUpSec = 0, rampDownSec = 0) {
|
|
331
|
+
const data = [];
|
|
332
|
+
const totalRequests = Math.max(15, threads * durationSec * 30);
|
|
333
|
+
const startTime = Date.now();
|
|
334
|
+
const rampUpMs = rampUpSec * 1e3;
|
|
335
|
+
const steadyMs = durationSec * 1e3;
|
|
336
|
+
const rampDownMs = rampDownSec * 1e3;
|
|
337
|
+
const totalTestMs = rampUpMs + steadyMs + rampDownMs;
|
|
338
|
+
const targetThresholdBreak = Math.max(5, Math.floor(maxAllowedLatencyMs * 0.05));
|
|
339
|
+
const errorPool = getFallbackClusterLogs();
|
|
340
|
+
for (let i = 0; i < totalRequests; i++) {
|
|
341
|
+
const offsetMs = Math.random() * totalTestMs;
|
|
342
|
+
const loadFactor = getLoadMultiplier(offsetMs, rampUpMs, steadyMs, rampDownMs);
|
|
343
|
+
if (loadFactor <= 0) continue;
|
|
344
|
+
const effectiveThreads = Math.max(1, threads * loadFactor);
|
|
345
|
+
const isBreached = effectiveThreads > targetThresholdBreak;
|
|
346
|
+
const stressFactor = isBreached ? Math.pow(effectiveThreads / targetThresholdBreak, 1.5) : 1;
|
|
347
|
+
const isError = Math.random() < (isBreached ? 0.05 : 1e-3);
|
|
348
|
+
const baseLatency = (25 + Math.random() * 35) * stressFactor;
|
|
349
|
+
let errorMessage;
|
|
350
|
+
let statusCode = 200;
|
|
351
|
+
if (isError) {
|
|
352
|
+
const targetResponseCodes = [400, 401, 403, 404, 429, 500, 502, 503, 504];
|
|
353
|
+
statusCode = targetResponseCodes[Math.floor(Math.random() * targetResponseCodes.length)];
|
|
354
|
+
errorMessage = errorPool[Math.floor(Math.random() * errorPool.length)];
|
|
264
355
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
async function main() {
|
|
277
|
-
const args = process.argv.slice(2);
|
|
278
|
-
if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
|
|
279
|
-
printUsage();
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
const config = parseArguments(args);
|
|
283
|
-
if (config.isCorrelate) {
|
|
284
|
-
await executeTestPipeline(config);
|
|
285
|
-
}
|
|
286
|
-
if (config.isAgentic) {
|
|
287
|
-
await runIntelligentAgenticStressTest(config);
|
|
288
|
-
} else {
|
|
289
|
-
await runStandardStressTest(config);
|
|
356
|
+
data.push({
|
|
357
|
+
durationMs: isError ? baseLatency * 0.1 : baseLatency,
|
|
358
|
+
timestampMs: startTime + offsetMs,
|
|
359
|
+
dnsTimeMs: 1 + Math.random() * 0.9,
|
|
360
|
+
tcpTimeMs: 4 + Math.random() * 1.5,
|
|
361
|
+
tlsTimeMs: 6 + Math.random() * 1.8,
|
|
362
|
+
statusCode,
|
|
363
|
+
errorMessage
|
|
364
|
+
});
|
|
290
365
|
}
|
|
366
|
+
return data.sort((a, b) => a.timestampMs - b.timestampMs);
|
|
291
367
|
}
|
|
292
|
-
function
|
|
293
|
-
const
|
|
294
|
-
const
|
|
295
|
-
const
|
|
296
|
-
const
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
let
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
if (rampUpIdx !== -1) rampUpSec = parseInt(args[rampUpIdx + 1], 10);
|
|
314
|
-
const rampDownIdx = args.indexOf("--ramp-down");
|
|
315
|
-
if (rampDownIdx !== -1) rampDownSec = parseInt(args[rampDownIdx + 1], 10);
|
|
316
|
-
const apdexIdx = args.indexOf("--target-apdex");
|
|
317
|
-
if (apdexIdx !== -1) targetApdex = parseFloat(apdexIdx + 1);
|
|
318
|
-
const errorIdx = args.indexOf("--target-error");
|
|
319
|
-
if (errorIdx !== -1) targetErrorRate = parseFloat(args[errorIdx + 1]);
|
|
320
|
-
const maxThreadsIdx = args.indexOf("--max-threads");
|
|
321
|
-
if (maxThreadsIdx !== -1) maxThreads = parseInt(args[maxThreadsIdx + 1], 10);
|
|
322
|
-
const outputIdx = args.indexOf("--output");
|
|
323
|
-
if (outputIdx !== -1) outputHtml = args[outputIdx + 1];
|
|
324
|
-
const baselineIdx = args.indexOf("--baseline");
|
|
325
|
-
if (baselineIdx !== -1) baselinePath = args[baselineIdx + 1];
|
|
326
|
-
const positionalNums = args.slice(1).filter((arg) => !arg.startsWith("-")).map(Number).filter((n) => !isNaN(n));
|
|
327
|
-
if (positionalNums.length > 0 && threadsIdx === -1 && !isAgentic) threads = positionalNums[0];
|
|
328
|
-
if (positionalNums.length > 1 && durationIdx === -1 && !isAgentic) durationSec = positionalNums[1];
|
|
329
|
-
if (positionalNums.length > 2 && errorIdx === -1 && !isAgentic) targetErrorRate = positionalNums[2] / 100;
|
|
330
|
-
if (isAgentic) {
|
|
331
|
-
const agenticIdx = args.indexOf("--agentic");
|
|
332
|
-
const trailingArgs = args.slice(agenticIdx + 1).filter((arg) => !arg.startsWith("--"));
|
|
333
|
-
if (trailingArgs[0]) maxThreads = parseInt(trailingArgs[0], 10);
|
|
334
|
-
if (trailingArgs[1]) {
|
|
335
|
-
let rawLatency = parseFloat(trailingArgs[1]);
|
|
336
|
-
if (rawLatency < 50) {
|
|
337
|
-
maxAllowedLatencyMs = rawLatency * 1e3;
|
|
338
|
-
} else {
|
|
339
|
-
maxAllowedLatencyMs = rawLatency;
|
|
340
|
-
}
|
|
368
|
+
function processMetricsTelemetry(requests, durationSec) {
|
|
369
|
+
const totalRequests = requests.length;
|
|
370
|
+
const durations = requests.map((r) => r.durationMs);
|
|
371
|
+
const avgLatencyMs = mathUtils.mean(durations);
|
|
372
|
+
const stdDevMs = mathUtils.stdDev(durations, avgLatencyMs);
|
|
373
|
+
const p95LatencyMs = mathUtils.percentile(durations, 95);
|
|
374
|
+
const avgDnsMs = mathUtils.mean(requests.map((r) => r.dnsTimeMs || 0));
|
|
375
|
+
const avgTcpMs = mathUtils.mean(requests.map((r) => r.tcpTimeMs || 0));
|
|
376
|
+
const avgTlsMs = mathUtils.mean(requests.map((r) => r.tlsTimeMs || 0));
|
|
377
|
+
const avgTtfbMs = avgDnsMs + avgTcpMs + avgTlsMs + avgLatencyMs * 0.3;
|
|
378
|
+
let c1xx = 0, c2xx = 0, c3xx = 0, c4xx = 0, c5xx = 0;
|
|
379
|
+
const codeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500: 0, 502: 0, 503: 0, 504: 0 };
|
|
380
|
+
[400, 401, 403, 404, 429, 500, 502, 503, 504].forEach((c) => codeCounts[c] = 0);
|
|
381
|
+
requests.forEach((r) => {
|
|
382
|
+
if (r.statusCode >= 100 && r.statusCode < 200) c1xx++;
|
|
383
|
+
else if (r.statusCode >= 200 && r.statusCode < 300) c2xx++;
|
|
384
|
+
else if (r.statusCode >= 300 && r.statusCode < 400) c3xx++;
|
|
385
|
+
else if (r.statusCode >= 400 && r.statusCode < 500) c4xx++;
|
|
386
|
+
else if (r.statusCode >= 500) c5xx++;
|
|
387
|
+
if (r.statusCode in codeCounts) {
|
|
388
|
+
codeCounts[r.statusCode]++;
|
|
341
389
|
}
|
|
342
|
-
|
|
343
|
-
|
|
390
|
+
});
|
|
391
|
+
const errorCount = c4xx + c5xx;
|
|
392
|
+
const errorRate = totalRequests === 0 ? 0 : errorCount / totalRequests;
|
|
393
|
+
const T = 50;
|
|
394
|
+
const satisfied = requests.filter((r) => r.durationMs <= T && r.statusCode < 400).length;
|
|
395
|
+
const tolerating = requests.filter((r) => r.durationMs > T && r.durationMs <= T * 4 && r.statusCode < 400).length;
|
|
396
|
+
const apdexScore = totalRequests === 0 ? 0 : (satisfied + tolerating / 2) / totalRequests;
|
|
397
|
+
const bandwidthMb = totalRequests * 1.2 / durationSec / 10;
|
|
344
398
|
return {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
399
|
+
totalRequests,
|
|
400
|
+
requestsPerSecond: totalRequests / durationSec,
|
|
401
|
+
avgLatencyMs,
|
|
402
|
+
stdDevMs,
|
|
403
|
+
p95LatencyMs,
|
|
404
|
+
errorRate,
|
|
405
|
+
apdexScore,
|
|
406
|
+
c1xx,
|
|
407
|
+
c2xx,
|
|
408
|
+
c3xx,
|
|
409
|
+
c4xx,
|
|
410
|
+
c5xx,
|
|
411
|
+
avgDnsMs,
|
|
412
|
+
avgTcpMs,
|
|
413
|
+
avgTlsMs,
|
|
414
|
+
avgTtfbMs,
|
|
415
|
+
bandwidthMb,
|
|
416
|
+
codeCounts
|
|
361
417
|
};
|
|
362
418
|
}
|
|
363
419
|
async function runBlazeCoreEngine(config) {
|
|
@@ -381,40 +437,62 @@ async function runBlazeCoreEngine(config) {
|
|
|
381
437
|
}, 1e3);
|
|
382
438
|
});
|
|
383
439
|
}
|
|
384
|
-
function
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
440
|
+
function printMatrixDashboard(m, threads) {
|
|
441
|
+
const pad = (val, size) => String(val).padEnd(size).substring(0, size);
|
|
442
|
+
console.log(`-----------------------------------------------------------------------------------------`);
|
|
443
|
+
console.log(`| Threads | Samples | Avg Latency | P95 Latency | Throughput | Error Rate | Apdex |`);
|
|
444
|
+
console.log(`-----------------------------------------------------------------------------------------`);
|
|
445
|
+
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)} |`);
|
|
446
|
+
console.log(`-----------------------------------------------------------------------------------------`);
|
|
447
|
+
}
|
|
448
|
+
function generateFinalAgentReport(history, score) {
|
|
449
|
+
if (history.length === 0) return;
|
|
450
|
+
const peakThroughput = Math.max(...history.map((h) => h.throughput));
|
|
451
|
+
const cleanRuns = history.filter((h) => h.apdex >= 0.85 && h.errorRate <= 0.01);
|
|
452
|
+
const maxSafeThreads = cleanRuns.length > 0 ? cleanRuns[cleanRuns.length - 1].threads : history[0].threads;
|
|
453
|
+
console.log(chalk.bold.green("\n======================================================="));
|
|
454
|
+
console.log(chalk.bold.cyan("\u{1F9E0} INTELLIGENT AUTONOMOUS LOAD AGENT FINAL REPORT"));
|
|
455
|
+
console.log(chalk.bold.green("======================================================="));
|
|
456
|
+
console.log(`\u{1F7E2} Unified Blaze Health Score : ${score}/100`);
|
|
457
|
+
console.log(`\u{1F3C6} Recommended Max Safe Concurrency : ${maxSafeThreads} allocation threads`);
|
|
458
|
+
console.log(`\u{1F4A5} Infrastructure Failure Boundary : ${history[history.length - 1].threads} concurrency allocation`);
|
|
459
|
+
console.log(`\u26A1 Peak Achieved Cluster Velocity : ${peakThroughput.toFixed(0)} req/sec`);
|
|
460
|
+
console.log(chalk.bold.green("=======================================================\n"));
|
|
461
|
+
}
|
|
462
|
+
async function executeTestPipeline(config) {
|
|
463
|
+
if (!blazeCore || typeof blazeCore.runCorrelatedTransaction !== "function") {
|
|
464
|
+
console.error(chalk.red("\u274C Error: runCorrelatedTransaction function is missing from the compiled binary bindings."));
|
|
465
|
+
return;
|
|
388
466
|
}
|
|
389
|
-
const
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
467
|
+
const transactionalScenario = [
|
|
468
|
+
{
|
|
469
|
+
name: "Fetch Target Anti-Forgery Nonce",
|
|
470
|
+
url: "https://httpbin.org/json",
|
|
471
|
+
method: "GET",
|
|
472
|
+
body: null
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
name: "Perform Contextual Post Action",
|
|
476
|
+
url: "https://httpbin.org/post?verification=${csrf_token}",
|
|
477
|
+
method: "POST",
|
|
478
|
+
body: JSON.stringify({
|
|
479
|
+
data: "performance_payload",
|
|
480
|
+
security_token: "${csrf_token}"
|
|
481
|
+
})
|
|
482
|
+
}
|
|
483
|
+
];
|
|
484
|
+
console.log(chalk.blue(`Launching automated script transaction analysis for: ${config.targetScript}`));
|
|
485
|
+
const sanitizedSteps = transactionalScenario.map((step) => ({
|
|
486
|
+
...step,
|
|
487
|
+
body: typeof step.body === "string" ? step.body : step.body ? JSON.stringify(step.body) : ""
|
|
488
|
+
}));
|
|
489
|
+
const success = await blazeCore.runCorrelatedTransaction(sanitizedSteps);
|
|
490
|
+
if (success) {
|
|
491
|
+
console.log(chalk.green("Pipeline run complete. Parameter tracking successfully mitigated breaking changes."));
|
|
406
492
|
}
|
|
407
|
-
return { trafficLoss, status, crawlPenalty, color };
|
|
408
|
-
}
|
|
409
|
-
function computeHealthScoreValue(apdex, errorRate, p95Latency, maxAllowedLatency) {
|
|
410
|
-
const apdexComponent = apdex * 50;
|
|
411
|
-
const errorComponent = Math.max(0, 1 - errorRate) * 30;
|
|
412
|
-
const latencyRatio = p95Latency / maxAllowedLatency;
|
|
413
|
-
const latencyComponent = Math.max(0, 1 - Math.min(1, latencyRatio)) * 20;
|
|
414
|
-
return Math.min(100, Math.max(0, Math.round(apdexComponent + errorComponent + latencyComponent)));
|
|
415
493
|
}
|
|
416
494
|
async function runIntelligentAgenticStressTest(config) {
|
|
417
|
-
console.log("\u{1F680} Initializing Intelligent Autonomous Load Agent...");
|
|
495
|
+
console.log(chalk.magenta("\u{1F680} Initializing Intelligent Autonomous Load Agent..."));
|
|
418
496
|
const history = [];
|
|
419
497
|
let aggregateErrorLogs = [];
|
|
420
498
|
let lastRawRequests = [];
|
|
@@ -428,7 +506,7 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
428
506
|
let verdictReason = "The system successfully scaled and remained stable within parameters up to maximum configured capacity allocations.";
|
|
429
507
|
let saturationKneePoint = null;
|
|
430
508
|
while (currentThreads <= config.maxThreads && isStable) {
|
|
431
|
-
console.log(`\u{1F916} [Agent Decision]: Testing execution tier at ${currentThreads} concurrent threads...`);
|
|
509
|
+
console.log(chalk.yellow(`\u{1F916} [Agent Decision]: Testing execution tier at ${currentThreads} concurrent threads...`));
|
|
432
510
|
const { metrics, rawErrors, rawRequests } = await runBlazeCoreEngine({ ...config, threads: currentThreads });
|
|
433
511
|
if (config.clusterLogs) {
|
|
434
512
|
aggregateErrorLogs = aggregateErrorLogs.concat(rawErrors);
|
|
@@ -521,7 +599,7 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
521
599
|
if (config.isSeo) {
|
|
522
600
|
const finalLat = finalState.avgLatency || finalSummaryCards.ttfb;
|
|
523
601
|
const seo = calculateSeoImpactMetrics(finalLat);
|
|
524
|
-
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}].`);
|
|
602
|
+
console.log(chalk.bold.yellow(`\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}].`));
|
|
525
603
|
}
|
|
526
604
|
const breakdownRates = {};
|
|
527
605
|
Object.keys(globalCodeCounts).forEach((codeStr) => {
|
|
@@ -532,11 +610,11 @@ async function runIntelligentAgenticStressTest(config) {
|
|
|
532
610
|
exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx, breakdownRates }, finalSummaryCards, lastRawRequests, geminiAnalysisHtml, baselineData);
|
|
533
611
|
}
|
|
534
612
|
async function runStandardStressTest(config) {
|
|
535
|
-
console.log(`\u{1F3CB}\uFE0F Running Standard Stress Test [Threads: ${config.threads} | Duration: ${config.durationSec}s | RampUp: ${config.rampUpSec}s | RampDown: ${config.rampDownSec}s]`);
|
|
613
|
+
console.log(chalk.blue(`\u{1F3CB}\uFE0F Running Standard Stress Test [Threads: ${config.threads} | Duration: ${config.durationSec}s | RampUp: ${config.rampUpSec}s | RampDown: ${config.rampDownSec}s]`));
|
|
536
614
|
const { metrics, rawErrors, rawRequests } = await runBlazeCoreEngine(config);
|
|
537
615
|
printMatrixDashboard(metrics, config.threads);
|
|
538
616
|
const healthScore = computeHealthScoreValue(metrics.apdexScore, metrics.errorRate, metrics.p95LatencyMs, config.maxAllowedLatencyMs);
|
|
539
|
-
console.log(`\u{1F49A} Unified Blaze Health Score: ${healthScore}/100`);
|
|
617
|
+
console.log(chalk.green(`\u{1F49A} Unified Blaze Health Score: ${healthScore}/100`));
|
|
540
618
|
const history = [{
|
|
541
619
|
threads: config.threads,
|
|
542
620
|
avgLatency: metrics.avgLatencyMs,
|
|
@@ -573,7 +651,7 @@ async function runStandardStressTest(config) {
|
|
|
573
651
|
const geminiAnalysisHtml = await generateGeminiRCA(history, finalSummaryCards, rawErrors.length > 0 ? rawErrors : getFallbackClusterLogs());
|
|
574
652
|
if (config.isSeo) {
|
|
575
653
|
const seo = calculateSeoImpactMetrics(metrics.avgLatencyMs);
|
|
576
|
-
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}].`);
|
|
654
|
+
console.log(chalk.bold.yellow(`\u{1F50E} [SEO Impact Report]: Target latency (${metrics.avgLatencyMs.toFixed(1)}ms) triggers a predicted Search Visibility Loss of -${seo.trafficLoss}% [Status: ${seo.status}].`));
|
|
577
655
|
}
|
|
578
656
|
const breakdownRates = {};
|
|
579
657
|
Object.keys(metrics.codeCounts).forEach((codeStr) => {
|
|
@@ -590,129 +668,6 @@ async function runStandardStressTest(config) {
|
|
|
590
668
|
breakdownRates
|
|
591
669
|
}, finalSummaryCards, rawRequests, geminiAnalysisHtml, baselineData);
|
|
592
670
|
}
|
|
593
|
-
function getFallbackClusterLogs() {
|
|
594
|
-
const logs = [];
|
|
595
|
-
const add = (msg, count) => {
|
|
596
|
-
for (let i = 0; i < count; i++) logs.push(`[2026-07-08T12:08:18.000Z] ${msg}`);
|
|
597
|
-
};
|
|
598
|
-
add("JsonWebTokenError: jwt expired at JMT.verify (\\app\\node_modules\\jsonwebtoken\\index.js:5)", 37);
|
|
599
|
-
add("Error: ECONNREFUSED 127.0.0.1:5432 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1)", 33);
|
|
600
|
-
add("TypeError: Cannot read properties of undefined (reading 'config_id') at Object.execute (\\app\\dist\\handler.js:42:19)", 31);
|
|
601
|
-
add("HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 15000ms", 25);
|
|
602
|
-
add("HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 30000ms", 19);
|
|
603
|
-
return logs;
|
|
604
|
-
}
|
|
605
|
-
function generateSimulationData(threads, durationSec, maxAllowedLatencyMs, rampUpSec = 0, rampDownSec = 0) {
|
|
606
|
-
const data = [];
|
|
607
|
-
const totalRequests = Math.max(15, threads * durationSec * 30);
|
|
608
|
-
const startTime = Date.now();
|
|
609
|
-
const rampUpMs = rampUpSec * 1e3;
|
|
610
|
-
const steadyMs = durationSec * 1e3;
|
|
611
|
-
const rampDownMs = rampDownSec * 1e3;
|
|
612
|
-
const totalTestMs = rampUpMs + steadyMs + rampDownMs;
|
|
613
|
-
const targetThresholdBreak = Math.max(5, Math.floor(maxAllowedLatencyMs * 0.05));
|
|
614
|
-
const errorPool = getFallbackClusterLogs();
|
|
615
|
-
for (let i = 0; i < totalRequests; i++) {
|
|
616
|
-
const offsetMs = Math.random() * totalTestMs;
|
|
617
|
-
const loadFactor = getLoadMultiplier(offsetMs, rampUpMs, steadyMs, rampDownMs);
|
|
618
|
-
if (loadFactor <= 0) continue;
|
|
619
|
-
const effectiveThreads = Math.max(1, threads * loadFactor);
|
|
620
|
-
const isBreached = effectiveThreads > targetThresholdBreak;
|
|
621
|
-
const stressFactor = isBreached ? Math.pow(effectiveThreads / targetThresholdBreak, 1.5) : 1;
|
|
622
|
-
const isError = Math.random() < (isBreached ? 0.05 : 1e-3);
|
|
623
|
-
const baseLatency = (25 + Math.random() * 35) * stressFactor;
|
|
624
|
-
let errorMessage;
|
|
625
|
-
let statusCode = 200;
|
|
626
|
-
if (isError) {
|
|
627
|
-
const targetResponseCodes = [400, 401, 403, 404, 429, 500, 502, 503, 504];
|
|
628
|
-
statusCode = targetResponseCodes[Math.floor(Math.random() * targetResponseCodes.length)];
|
|
629
|
-
errorMessage = errorPool[Math.floor(Math.random() * errorPool.length)];
|
|
630
|
-
}
|
|
631
|
-
data.push({
|
|
632
|
-
durationMs: isError ? baseLatency * 0.1 : baseLatency,
|
|
633
|
-
timestampMs: startTime + offsetMs,
|
|
634
|
-
dnsTimeMs: 1 + Math.random() * 0.9,
|
|
635
|
-
tcpTimeMs: 4 + Math.random() * 1.5,
|
|
636
|
-
tlsTimeMs: 6 + Math.random() * 1.8,
|
|
637
|
-
statusCode,
|
|
638
|
-
errorMessage
|
|
639
|
-
});
|
|
640
|
-
}
|
|
641
|
-
return data.sort((a, b) => a.timestampMs - b.timestampMs);
|
|
642
|
-
}
|
|
643
|
-
function processMetricsTelemetry(requests, durationSec) {
|
|
644
|
-
const totalRequests = requests.length;
|
|
645
|
-
const durations = requests.map((r) => r.durationMs);
|
|
646
|
-
const avgLatencyMs = mathUtils.mean(durations);
|
|
647
|
-
const stdDevMs = mathUtils.stdDev(durations, avgLatencyMs);
|
|
648
|
-
const p95LatencyMs = mathUtils.percentile(durations, 95);
|
|
649
|
-
const avgDnsMs = mathUtils.mean(requests.map((r) => r.dnsTimeMs || 0));
|
|
650
|
-
const avgTcpMs = mathUtils.mean(requests.map((r) => r.tcpTimeMs || 0));
|
|
651
|
-
const avgTlsMs = mathUtils.mean(requests.map((r) => r.tlsTimeMs || 0));
|
|
652
|
-
const avgTtfbMs = avgDnsMs + avgTcpMs + avgTlsMs + avgLatencyMs * 0.3;
|
|
653
|
-
let c1xx = 0, c2xx = 0, c3xx = 0, c4xx = 0, c5xx = 0;
|
|
654
|
-
const codeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500: 0, 502: 0, 503: 0, 504: 0 };
|
|
655
|
-
[400, 401, 403, 404, 429, 500, 502, 503, 504].forEach((c) => codeCounts[c] = 0);
|
|
656
|
-
requests.forEach((r) => {
|
|
657
|
-
if (r.statusCode >= 100 && r.statusCode < 200) c1xx++;
|
|
658
|
-
else if (r.statusCode >= 200 && r.statusCode < 300) c2xx++;
|
|
659
|
-
else if (r.statusCode >= 300 && r.statusCode < 400) c3xx++;
|
|
660
|
-
else if (r.statusCode >= 400 && r.statusCode < 500) c4xx++;
|
|
661
|
-
else if (r.statusCode >= 500) c5xx++;
|
|
662
|
-
if (r.statusCode in codeCounts) {
|
|
663
|
-
codeCounts[r.statusCode]++;
|
|
664
|
-
}
|
|
665
|
-
});
|
|
666
|
-
const errorCount = c4xx + c5xx;
|
|
667
|
-
const errorRate = totalRequests === 0 ? 0 : errorCount / totalRequests;
|
|
668
|
-
const T = 50;
|
|
669
|
-
const satisfied = requests.filter((r) => r.durationMs <= T && r.statusCode < 400).length;
|
|
670
|
-
const tolerating = requests.filter((r) => r.durationMs > T && r.durationMs <= T * 4 && r.statusCode < 400).length;
|
|
671
|
-
const apdexScore = totalRequests === 0 ? 0 : (satisfied + tolerating / 2) / totalRequests;
|
|
672
|
-
const bandwidthMb = totalRequests * 1.2 / durationSec / 10;
|
|
673
|
-
return {
|
|
674
|
-
totalRequests,
|
|
675
|
-
requestsPerSecond: totalRequests / durationSec,
|
|
676
|
-
avgLatencyMs,
|
|
677
|
-
stdDevMs,
|
|
678
|
-
p95LatencyMs,
|
|
679
|
-
errorRate,
|
|
680
|
-
apdexScore,
|
|
681
|
-
c1xx,
|
|
682
|
-
c2xx,
|
|
683
|
-
c3xx,
|
|
684
|
-
c4xx,
|
|
685
|
-
c5xx,
|
|
686
|
-
avgDnsMs,
|
|
687
|
-
avgTcpMs,
|
|
688
|
-
avgTlsMs,
|
|
689
|
-
avgTtfbMs,
|
|
690
|
-
bandwidthMb,
|
|
691
|
-
codeCounts
|
|
692
|
-
};
|
|
693
|
-
}
|
|
694
|
-
function printMatrixDashboard(m, threads) {
|
|
695
|
-
const pad = (val, size) => String(val).padEnd(size).substring(0, size);
|
|
696
|
-
console.log(`-----------------------------------------------------------------------------------------`);
|
|
697
|
-
console.log(`| Threads | Samples | Avg Latency | P95 Latency | Throughput | Error Rate | Apdex |`);
|
|
698
|
-
console.log(`-----------------------------------------------------------------------------------------`);
|
|
699
|
-
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)} |`);
|
|
700
|
-
console.log(`-----------------------------------------------------------------------------------------`);
|
|
701
|
-
}
|
|
702
|
-
function generateFinalAgentReport(history, score) {
|
|
703
|
-
if (history.length === 0) return;
|
|
704
|
-
const peakThroughput = Math.max(...history.map((h) => h.throughput));
|
|
705
|
-
const cleanRuns = history.filter((h) => h.apdex >= 0.85 && h.errorRate <= 0.01);
|
|
706
|
-
const maxSafeThreads = cleanRuns.length > 0 ? cleanRuns[cleanRuns.length - 1].threads : history[0].threads;
|
|
707
|
-
console.log("\n=======================================================");
|
|
708
|
-
console.log("\u{1F9E0} INTELLIGENT AUTONOMOUS LOAD AGENT FINAL REPORT");
|
|
709
|
-
console.log("=======================================================");
|
|
710
|
-
console.log(`\u{1F49A} Unified Blaze Health Score : ${score}/100`);
|
|
711
|
-
console.log(`\u{1F3C6} Recommended Max Safe Concurrency : ${maxSafeThreads} allocation threads`);
|
|
712
|
-
console.log(`\u{1F4A5} Infrastructure Failure Boundary : ${history[history.length - 1].threads} concurrency allocation`);
|
|
713
|
-
console.log(`\u26A1 Peak Achieved Cluster Velocity : ${peakThroughput.toFixed(0)} req/sec`);
|
|
714
|
-
console.log("=======================================================\n");
|
|
715
|
-
}
|
|
716
671
|
function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards, rawRequests = [], geminiAnalysisHtml = "", baselineData = null) {
|
|
717
672
|
const summaryArtifactPath = config.outputHtml.replace(/\.html$/, "-summary.json");
|
|
718
673
|
try {
|
|
@@ -1203,7 +1158,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
1203
1158
|
const feedback = document.getElementById('nlpQueryFeedback');
|
|
1204
1159
|
|
|
1205
1160
|
let filterFn = (row, data) => true;
|
|
1206
|
-
const numMatch = q.match(/(d+(
|
|
1161
|
+
const numMatch = q.match(/(\\d+(?:\\.\\d+)?)/);
|
|
1207
1162
|
const targetNum = numMatch ? parseFloat(numMatch[1]) : null;
|
|
1208
1163
|
|
|
1209
1164
|
const isGreater = q.includes('>') || q.includes('greater') || q.includes('above') || q.includes('more');
|
|
@@ -1431,14 +1386,69 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
1431
1386
|
</script></body></html>`;
|
|
1432
1387
|
try {
|
|
1433
1388
|
fs.writeFileSync(config.outputHtml, htmlContent, "utf-8");
|
|
1434
|
-
console.log(`\u{1F4CA} Standalone Dashboard exported successfully to: ${path.resolve(config.outputHtml)}`);
|
|
1389
|
+
console.log(chalk.bold.green(`\u{1F4CA} Standalone Dashboard exported successfully to: ${path.resolve(config.outputHtml)}`));
|
|
1435
1390
|
} catch (err) {
|
|
1436
|
-
console.error(`\u274C Failed to write HTML output dashboard: ${err}`);
|
|
1391
|
+
console.error(chalk.red(`\u274C Failed to write HTML output dashboard: ${err}`));
|
|
1437
1392
|
}
|
|
1438
1393
|
}
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1394
|
+
var program = new Command();
|
|
1395
|
+
program.name("blaze").description("Blaze CLI - High-performance distributed load and latency tester").version("1.0.0");
|
|
1396
|
+
program.command("run").description("Run a distributed load or local performance/stress test pipeline").argument("[script]", "Target script or directory path to evaluate", ".").option("-u, --url <url>", "Target simulation URL for distributed load routing", "https://api.example.com/health").option("-r, --regions <zones>", "Comma-separated list of target AWS/Cloud zones to simulate (e.g. us-east-1,eu-central-1,ap-south-1)").option("-t, --threads <threads>", "Number of parallel virtual users (VUs)", "10").option("-d, --duration <seconds>", "Test duration in seconds", "10").option("--ramp-up <seconds>", "Ramp-up duration in seconds", "0").option("--ramp-down <seconds>", "Ramp-down duration in seconds", "0").option("--target-apdex <score>", "Target Apdex satisfaction index (0.0 to 1.0)", "0.85").option("--target-error <rate>", "Target maximum allowed error rate (e.g. 0.01 for 1%)", "0.01").option("--max-threads <max>", "Maximum parallel threads for dynamic agentic scaling tests", "300").option("--max-allowed-latency <ms>", "SLA violation latency threshold (in milliseconds)", "800").option("-o, --output <filename>", "Destination path of the exported interactive HTML telemetry dashboard", "blaze-dashboard.html").option("--baseline <filepath>", "Reference JSON summary data to run automatic regression comparisons").option("--agentic", "Deploy the autonomous cognitive agent to auto-scale load until failure limits are hit", false).option("--correlate", "Enable transaction tracking pipelines (mitigates CSRF/nonce state mutations)", false).option("--seo", "Model SEO bounce rate, googlebot budget indexing penalties, and visibility impact", false).option("--no-cluster-logs", "Disable structural failure pattern clustering").action(async (script, options) => {
|
|
1397
|
+
const parsedThreads = parseInt(options.threads, 10);
|
|
1398
|
+
const parsedDuration = parseInt(options.duration, 10);
|
|
1399
|
+
const parsedRampUp = parseInt(options.rampUp, 10) || 0;
|
|
1400
|
+
const parsedRampDown = parseInt(options.rampDown, 10) || 0;
|
|
1401
|
+
const parsedApdex = parseFloat(options.targetApdex);
|
|
1402
|
+
const parsedError = parseFloat(options.targetError);
|
|
1403
|
+
const parsedMaxThreads = parseInt(options.maxThreads, 10);
|
|
1404
|
+
const parsedMaxLatencyRaw = parseFloat(options.maxAllowedLatency || "800");
|
|
1405
|
+
const parsedMaxLatency = parsedMaxLatencyRaw < 50 ? parsedMaxLatencyRaw * 1e3 : parsedMaxLatencyRaw;
|
|
1406
|
+
const config = {
|
|
1407
|
+
targetScript: path.resolve(script || "."),
|
|
1408
|
+
isAgentic: !!options.agentic,
|
|
1409
|
+
isCorrelate: !!options.correlate,
|
|
1410
|
+
isSeo: !!options.seo,
|
|
1411
|
+
clusterLogs: options.clusterLogs !== false,
|
|
1412
|
+
threads: parsedThreads,
|
|
1413
|
+
durationSec: parsedDuration,
|
|
1414
|
+
rampUpSec: parsedRampUp,
|
|
1415
|
+
rampDownSec: parsedRampDown,
|
|
1416
|
+
targetApdex: parsedApdex,
|
|
1417
|
+
targetErrorRate: parsedError,
|
|
1418
|
+
maxThreads: parsedMaxThreads,
|
|
1419
|
+
stepSize: 15,
|
|
1420
|
+
maxAllowedLatencyMs: parsedMaxLatency,
|
|
1421
|
+
outputHtml: options.output,
|
|
1422
|
+
baselinePath: options.baseline || null
|
|
1423
|
+
};
|
|
1424
|
+
if (options.regions) {
|
|
1425
|
+
console.log(chalk.blue(`
|
|
1426
|
+
Initializing Blaze load test simulation against ${chalk.bold(options.url)}...`));
|
|
1427
|
+
console.log(chalk.gray(`Target regions: ${options.regions}`));
|
|
1428
|
+
console.log(chalk.gray(`Duration: ${options.duration}s
|
|
1429
|
+
`));
|
|
1430
|
+
await new Promise((resolve2) => {
|
|
1431
|
+
setTimeout(() => {
|
|
1432
|
+
const allRegions = getMockRegionalData();
|
|
1433
|
+
const requestedZones = options.regions.split(",").map((z) => z.trim());
|
|
1434
|
+
const filteredRegions = allRegions.filter((r) => requestedZones.includes(r.zone));
|
|
1435
|
+
renderGeographicHeatmap(filteredRegions.length > 0 ? filteredRegions : allRegions);
|
|
1436
|
+
console.log(chalk.gray(`Executing localized system stress verification pipelines...
|
|
1437
|
+
`));
|
|
1438
|
+
resolve2(null);
|
|
1439
|
+
}, 1e3);
|
|
1440
|
+
});
|
|
1441
|
+
}
|
|
1442
|
+
if (config.isCorrelate) {
|
|
1443
|
+
await executeTestPipeline(config);
|
|
1444
|
+
}
|
|
1445
|
+
if (config.isAgentic) {
|
|
1446
|
+
await runIntelligentAgenticStressTest(config);
|
|
1447
|
+
} else {
|
|
1448
|
+
await runStandardStressTest(config);
|
|
1449
|
+
}
|
|
1450
|
+
});
|
|
1451
|
+
program.command("heatmap").description("Display real-time geographic latency heatmap breakdown").action(() => {
|
|
1452
|
+
renderGeographicHeatmap(getMockRegionalData());
|
|
1453
|
+
});
|
|
1454
|
+
program.parse(process.argv);
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blaze-performance-tester",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.60",
|
|
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",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"author": "Kushan Shalindra Amarsiri <lkkushan@yahoo.com>",
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"bin": {
|
|
11
|
-
"blaze": "./
|
|
11
|
+
"blaze": "./dist/cli.js"
|
|
12
12
|
},
|
|
13
13
|
"files": [
|
|
14
14
|
"dist",
|
|
@@ -18,16 +18,18 @@
|
|
|
18
18
|
"blaze.win32-x64-msvc.node"
|
|
19
19
|
],
|
|
20
20
|
"scripts": {
|
|
21
|
-
"prebuild": "node -e \"if (!fs.existsSync('dist')) fs.mkdirSync('dist')\"",
|
|
21
|
+
"prebuild": "node -e \"const fs = require('fs'); if (!fs.existsSync('dist')) fs.mkdirSync('dist')\"",
|
|
22
22
|
"build:rust": "napi build --platform --release --output-dir dist --js index.js --dts index.d.ts",
|
|
23
|
-
"build:cli": "esbuild cli.ts --bundle --platform=node --format=esm --packages=external --outfile=dist/cli.js",
|
|
23
|
+
"build:cli": "esbuild cli.ts --bundle --platform=node --format=esm --packages=external --banner:js=\"#!/usr/bin/env node\" --outfile=dist/cli.js",
|
|
24
24
|
"build": "npm run prebuild && npm run build:rust && npm run build:cli"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"
|
|
27
|
+
"chalk": "^5.3.0",
|
|
28
|
+
"commander": "^12.1.0"
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {
|
|
30
31
|
"@napi-rs/cli": "^2.16.0",
|
|
32
|
+
"esbuild": "^0.20.0",
|
|
31
33
|
"typescript": "^5.0.0"
|
|
32
34
|
}
|
|
33
|
-
}
|
|
35
|
+
}
|
package/bin.js
DELETED