viberadar 0.3.208 → 0.3.209
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/server/index.d.ts.map +1 -1
- package/dist/server/index.js +237 -46
- package/dist/server/index.js.map +1 -1
- package/dist/ui/dashboard.html +313 -172
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAO7B,OAAO,EAAE,UAAU,EAA4H,MAAM,YAAY,CAAC;AAOlK,UAAU,aAAa;IACrB,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;CACrB;AA0vED,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAO7B,OAAO,EAAE,UAAU,EAA4H,MAAM,YAAY,CAAC;AAOlK,UAAU,aAAa;IACrB,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;CACrB;AA0vED,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAgvG1G"}
|
package/dist/server/index.js
CHANGED
|
@@ -2095,7 +2095,7 @@ function startServer({ data: initialData, port, projectRoot }) {
|
|
|
2095
2095
|
let loadRunning = false;
|
|
2096
2096
|
let loadProc = null;
|
|
2097
2097
|
let loadState = {
|
|
2098
|
-
status: 'idle', startTime: 0, buckets: [], totalRequests: 0,
|
|
2098
|
+
runId: null, status: 'idle', startTime: 0, buckets: [], totalRequests: 0,
|
|
2099
2099
|
totalErrors: 0, logs: [], script: '', config: null, summary: null,
|
|
2100
2100
|
};
|
|
2101
2101
|
let probeRunning = false;
|
|
@@ -2184,36 +2184,170 @@ function startServer({ data: initialData, port, projectRoot }) {
|
|
|
2184
2184
|
probeRunning = false;
|
|
2185
2185
|
}
|
|
2186
2186
|
}
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
return parseFloat(m[1]);
|
|
2193
|
-
if ((m = s.match(/^([\d.]+)s$/)))
|
|
2194
|
-
return parseFloat(m[1]) * 1000;
|
|
2195
|
-
if ((m = s.match(/^(\d+)m([\d.]+)s$/)))
|
|
2196
|
-
return parseInt(m[1]) * 60000 + parseFloat(m[2]) * 1000;
|
|
2197
|
-
return 0;
|
|
2187
|
+
const loadRunsDir = path.join(projectRoot, '.viberadar', 'load-runs');
|
|
2188
|
+
const MAX_LOAD_RUN_HISTORY = 50;
|
|
2189
|
+
function sanitizeLoadScriptName(name) {
|
|
2190
|
+
const raw = typeof name === 'string' && name.trim() ? name.trim() : 'Без названия';
|
|
2191
|
+
return raw.replace(/[^a-zA-Zа-яА-ЯёЁ0-9_\- .]/g, '_').slice(0, 80);
|
|
2198
2192
|
}
|
|
2199
|
-
function
|
|
2200
|
-
const
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
s.p90Duration = parseK6Dur(dur[2]);
|
|
2205
|
-
s.p95Duration = parseK6Dur(dur[3]);
|
|
2206
|
-
}
|
|
2207
|
-
const reqs = text.match(/\bhttp_reqs[^:]*:\s+(\d+)\s+([\d.]+)\/s/);
|
|
2208
|
-
if (reqs) {
|
|
2209
|
-
s.totalRequests = parseInt(reqs[1]);
|
|
2210
|
-
s.rps = parseFloat(reqs[2]);
|
|
2211
|
-
}
|
|
2212
|
-
const fail = text.match(/http_req_failed[^:]*:\s+([\d.]+)%/);
|
|
2213
|
-
if (fail)
|
|
2214
|
-
s.errorPct = parseFloat(fail[1]);
|
|
2215
|
-
return s;
|
|
2193
|
+
function normalizeLoadDuration(value) {
|
|
2194
|
+
const raw = typeof value === 'string' && value.trim() ? value.trim() : '30s';
|
|
2195
|
+
if (!/^(\d+(ms|s|m|h))+$/.test(raw))
|
|
2196
|
+
return '30s';
|
|
2197
|
+
return raw;
|
|
2216
2198
|
}
|
|
2199
|
+
function normalizeLoadVus(value) {
|
|
2200
|
+
const n = typeof value === 'number' ? value : parseInt(String(value || '10'), 10);
|
|
2201
|
+
if (!Number.isFinite(n) || n < 1)
|
|
2202
|
+
return 10;
|
|
2203
|
+
return Math.min(Math.floor(n), 10000);
|
|
2204
|
+
}
|
|
2205
|
+
function sanitizeLoadEnvVars(value) {
|
|
2206
|
+
if (!value || typeof value !== 'object')
|
|
2207
|
+
return {};
|
|
2208
|
+
const out = {};
|
|
2209
|
+
for (const [key, val] of Object.entries(value)) {
|
|
2210
|
+
const envKey = key.trim();
|
|
2211
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(envKey))
|
|
2212
|
+
continue;
|
|
2213
|
+
if (val == null)
|
|
2214
|
+
continue;
|
|
2215
|
+
out[envKey] = String(val);
|
|
2216
|
+
}
|
|
2217
|
+
return out;
|
|
2218
|
+
}
|
|
2219
|
+
function redactLoadEnvVars(envVars) {
|
|
2220
|
+
const out = {};
|
|
2221
|
+
for (const key of Object.keys(envVars)) {
|
|
2222
|
+
out[key] = /token|secret|password|key/i.test(key) ? '***' : envVars[key];
|
|
2223
|
+
}
|
|
2224
|
+
return out;
|
|
2225
|
+
}
|
|
2226
|
+
function buildLoadConfig(cfg) {
|
|
2227
|
+
const envVars = sanitizeLoadEnvVars(cfg.envVars);
|
|
2228
|
+
const baseUrl = typeof cfg.baseUrl === 'string' && cfg.baseUrl.trim() ? cfg.baseUrl.trim() : 'http://localhost:5000';
|
|
2229
|
+
envVars.BASE_URL = baseUrl;
|
|
2230
|
+
const config = {
|
|
2231
|
+
vus: normalizeLoadVus(cfg.vus),
|
|
2232
|
+
duration: normalizeLoadDuration(cfg.duration),
|
|
2233
|
+
baseUrl,
|
|
2234
|
+
scriptName: sanitizeLoadScriptName(cfg.scriptName),
|
|
2235
|
+
envVars: redactLoadEnvVars(envVars),
|
|
2236
|
+
};
|
|
2237
|
+
return { config, envVars };
|
|
2238
|
+
}
|
|
2239
|
+
function flattenK6Checks(group) {
|
|
2240
|
+
let passes = 0;
|
|
2241
|
+
let fails = 0;
|
|
2242
|
+
for (const check of group?.checks || []) {
|
|
2243
|
+
passes += Number(check.passes || 0);
|
|
2244
|
+
fails += Number(check.fails || 0);
|
|
2245
|
+
}
|
|
2246
|
+
for (const child of group?.groups || []) {
|
|
2247
|
+
const nested = flattenK6Checks(child);
|
|
2248
|
+
passes += nested.passes;
|
|
2249
|
+
fails += nested.fails;
|
|
2250
|
+
}
|
|
2251
|
+
return { passes, fails };
|
|
2252
|
+
}
|
|
2253
|
+
function normalizeK6Summary(raw, exitCode) {
|
|
2254
|
+
const metrics = raw?.metrics || {};
|
|
2255
|
+
const duration = metrics.http_req_duration?.values || {};
|
|
2256
|
+
const reqs = metrics.http_reqs?.values || {};
|
|
2257
|
+
const failed = metrics.http_req_failed?.values || {};
|
|
2258
|
+
const checks = flattenK6Checks(raw?.root_group);
|
|
2259
|
+
let thresholdsPassed = 0;
|
|
2260
|
+
let thresholdsFailed = 0;
|
|
2261
|
+
for (const metric of Object.values(metrics)) {
|
|
2262
|
+
for (const threshold of Object.values(metric?.thresholds || {})) {
|
|
2263
|
+
if (threshold?.ok === false)
|
|
2264
|
+
thresholdsFailed++;
|
|
2265
|
+
else if (threshold?.ok === true)
|
|
2266
|
+
thresholdsPassed++;
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
return {
|
|
2270
|
+
totalRequests: typeof reqs.count === 'number' ? reqs.count : undefined,
|
|
2271
|
+
rps: typeof reqs.rate === 'number' ? reqs.rate : undefined,
|
|
2272
|
+
avgDuration: typeof duration.avg === 'number' ? duration.avg : undefined,
|
|
2273
|
+
p90Duration: typeof duration['p(90)'] === 'number' ? duration['p(90)'] : undefined,
|
|
2274
|
+
p95Duration: typeof duration['p(95)'] === 'number' ? duration['p(95)'] : undefined,
|
|
2275
|
+
p99Duration: typeof duration['p(99)'] === 'number' ? duration['p(99)'] : undefined,
|
|
2276
|
+
errorPct: typeof failed.rate === 'number' ? failed.rate * 100 : undefined,
|
|
2277
|
+
testRunDurationMs: typeof raw?.state?.testRunDurationMs === 'number' ? raw.state.testRunDurationMs : undefined,
|
|
2278
|
+
checksPassed: checks.passes,
|
|
2279
|
+
checksFailed: checks.fails,
|
|
2280
|
+
thresholdsPassed,
|
|
2281
|
+
thresholdsFailed,
|
|
2282
|
+
exitCode,
|
|
2283
|
+
};
|
|
2284
|
+
}
|
|
2285
|
+
function readK6Summary(summaryPath, exitCode) {
|
|
2286
|
+
try {
|
|
2287
|
+
if (!fs.existsSync(summaryPath))
|
|
2288
|
+
return { exitCode };
|
|
2289
|
+
return normalizeK6Summary(JSON.parse(fs.readFileSync(summaryPath, 'utf-8')), exitCode);
|
|
2290
|
+
}
|
|
2291
|
+
catch {
|
|
2292
|
+
return { exitCode };
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
function readLoadRunIndex() {
|
|
2296
|
+
try {
|
|
2297
|
+
const p = path.join(loadRunsDir, 'index.json');
|
|
2298
|
+
const parsed = JSON.parse(fs.readFileSync(p, 'utf-8'));
|
|
2299
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
2300
|
+
}
|
|
2301
|
+
catch {
|
|
2302
|
+
return [];
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
function writeLoadRunIndex(items) {
|
|
2306
|
+
fs.mkdirSync(loadRunsDir, { recursive: true });
|
|
2307
|
+
fs.writeFileSync(path.join(loadRunsDir, 'index.json'), JSON.stringify(items, null, 2), 'utf-8');
|
|
2308
|
+
}
|
|
2309
|
+
function saveLoadRun() {
|
|
2310
|
+
if (!loadState.runId)
|
|
2311
|
+
return;
|
|
2312
|
+
fs.mkdirSync(loadRunsDir, { recursive: true });
|
|
2313
|
+
const record = {
|
|
2314
|
+
...loadState,
|
|
2315
|
+
scriptName: loadState.config?.scriptName,
|
|
2316
|
+
createdAt: new Date(loadState.startTime || Date.now()).toISOString(),
|
|
2317
|
+
};
|
|
2318
|
+
fs.writeFileSync(path.join(loadRunsDir, `${loadState.runId}.json`), JSON.stringify(record, null, 2), 'utf-8');
|
|
2319
|
+
const index = readLoadRunIndex().filter(i => i.runId !== loadState.runId);
|
|
2320
|
+
index.unshift({
|
|
2321
|
+
runId: loadState.runId,
|
|
2322
|
+
scriptName: loadState.config?.scriptName || 'Без названия',
|
|
2323
|
+
createdAt: record.createdAt,
|
|
2324
|
+
status: loadState.status,
|
|
2325
|
+
startTime: loadState.startTime,
|
|
2326
|
+
endTime: loadState.endTime,
|
|
2327
|
+
config: loadState.config,
|
|
2328
|
+
summary: loadState.summary,
|
|
2329
|
+
});
|
|
2330
|
+
const compact = index.slice(0, MAX_LOAD_RUN_HISTORY);
|
|
2331
|
+
writeLoadRunIndex(compact);
|
|
2332
|
+
for (const old of index.slice(MAX_LOAD_RUN_HISTORY)) {
|
|
2333
|
+
try {
|
|
2334
|
+
fs.unlinkSync(path.join(loadRunsDir, `${old.runId}.json`));
|
|
2335
|
+
}
|
|
2336
|
+
catch { }
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
function loadLastRunIntoState() {
|
|
2340
|
+
const latest = readLoadRunIndex()[0];
|
|
2341
|
+
if (!latest)
|
|
2342
|
+
return;
|
|
2343
|
+
try {
|
|
2344
|
+
const record = JSON.parse(fs.readFileSync(path.join(loadRunsDir, `${latest.runId}.json`), 'utf-8'));
|
|
2345
|
+
if (record && typeof record === 'object')
|
|
2346
|
+
loadState = record;
|
|
2347
|
+
}
|
|
2348
|
+
catch { }
|
|
2349
|
+
}
|
|
2350
|
+
loadLastRunIntoState();
|
|
2217
2351
|
// ── SSE clients ────────────────────────────────────────────────────────────
|
|
2218
2352
|
const sseClients = new Set();
|
|
2219
2353
|
function broadcast(event, payload = {}) {
|
|
@@ -4911,7 +5045,7 @@ a{color:var(--blue)}
|
|
|
4911
5045
|
req.on('data', (d) => { body += d; });
|
|
4912
5046
|
req.on('end', () => {
|
|
4913
5047
|
try {
|
|
4914
|
-
const { name, script } = JSON.parse(body);
|
|
5048
|
+
const { name, script, vus, duration, baseUrl } = JSON.parse(body);
|
|
4915
5049
|
if (!name || !script) {
|
|
4916
5050
|
res.writeHead(400, jsonH);
|
|
4917
5051
|
res.end(JSON.stringify({ error: 'name and script required' }));
|
|
@@ -4921,7 +5055,15 @@ a{color:var(--blue)}
|
|
|
4921
5055
|
const safeName = name.replace(/[^a-zA-Zа-яА-ЯёЁ0-9_\- ]/g, '_').slice(0, 80);
|
|
4922
5056
|
const date = new Date().toISOString().slice(0, 16).replace('T', ' ');
|
|
4923
5057
|
const fileName = `${Date.now()}-${safeName.replace(/\s+/g, '_')}.json`;
|
|
4924
|
-
const entry = {
|
|
5058
|
+
const entry = {
|
|
5059
|
+
name: safeName,
|
|
5060
|
+
date,
|
|
5061
|
+
script,
|
|
5062
|
+
fileName,
|
|
5063
|
+
vus: normalizeLoadVus(vus),
|
|
5064
|
+
duration: normalizeLoadDuration(duration),
|
|
5065
|
+
baseUrl: typeof baseUrl === 'string' && baseUrl.trim() ? baseUrl.trim() : 'http://localhost:5000',
|
|
5066
|
+
};
|
|
4925
5067
|
// overwrite if same name exists
|
|
4926
5068
|
const existing = fs.readdirSync(scriptsDir).find(f => {
|
|
4927
5069
|
try {
|
|
@@ -5011,6 +5153,31 @@ a{color:var(--blue)}
|
|
|
5011
5153
|
res.end(JSON.stringify(loadState));
|
|
5012
5154
|
return;
|
|
5013
5155
|
}
|
|
5156
|
+
if (url === '/api/load/runs' && req.method === 'GET') {
|
|
5157
|
+
res.writeHead(200, jsonH);
|
|
5158
|
+
res.end(JSON.stringify(readLoadRunIndex()));
|
|
5159
|
+
return;
|
|
5160
|
+
}
|
|
5161
|
+
const loadRunMatch = url.match(/^\/api\/load\/runs\/([^/]+)$/);
|
|
5162
|
+
if (loadRunMatch && req.method === 'GET') {
|
|
5163
|
+
try {
|
|
5164
|
+
const runId = decodeURIComponent(loadRunMatch[1]);
|
|
5165
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(runId)) {
|
|
5166
|
+
res.writeHead(400, jsonH);
|
|
5167
|
+
res.end(JSON.stringify({ error: 'Bad run id' }));
|
|
5168
|
+
return;
|
|
5169
|
+
}
|
|
5170
|
+
const runPath = path.join(loadRunsDir, `${runId}.json`);
|
|
5171
|
+
const record = JSON.parse(fs.readFileSync(runPath, 'utf-8'));
|
|
5172
|
+
res.writeHead(200, jsonH);
|
|
5173
|
+
res.end(JSON.stringify(record));
|
|
5174
|
+
}
|
|
5175
|
+
catch {
|
|
5176
|
+
res.writeHead(404, jsonH);
|
|
5177
|
+
res.end(JSON.stringify({ error: 'Run not found' }));
|
|
5178
|
+
}
|
|
5179
|
+
return;
|
|
5180
|
+
}
|
|
5014
5181
|
if (url === '/api/load/stop' && req.method === 'POST') {
|
|
5015
5182
|
if (loadProc) {
|
|
5016
5183
|
try {
|
|
@@ -5024,7 +5191,7 @@ a{color:var(--blue)}
|
|
|
5024
5191
|
loadState.status = 'stopped';
|
|
5025
5192
|
loadState.endTime = Date.now();
|
|
5026
5193
|
}
|
|
5027
|
-
broadcast('load-done', { status: loadState.status, summary: loadState.summary });
|
|
5194
|
+
broadcast('load-done', { runId: loadState.runId, status: loadState.status, summary: loadState.summary });
|
|
5028
5195
|
res.writeHead(200, jsonH);
|
|
5029
5196
|
res.end(JSON.stringify({ ok: true }));
|
|
5030
5197
|
return;
|
|
@@ -5053,8 +5220,11 @@ a{color:var(--blue)}
|
|
|
5053
5220
|
res.end(JSON.stringify({ error: 'No script provided' }));
|
|
5054
5221
|
return;
|
|
5055
5222
|
}
|
|
5223
|
+
const { config: loadConfig, envVars } = buildLoadConfig(cfg);
|
|
5224
|
+
const runId = `load-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
5056
5225
|
const scriptPath = path.join(os.tmpdir(), `viberadar-k6-${Date.now()}.js`);
|
|
5057
5226
|
const jsonOutPath = path.join(os.tmpdir(), `viberadar-k6-out-${Date.now()}.ndjson`);
|
|
5227
|
+
const summaryPath = path.join(os.tmpdir(), `viberadar-k6-summary-${Date.now()}.json`);
|
|
5058
5228
|
try {
|
|
5059
5229
|
fs.writeFileSync(scriptPath, script, 'utf-8');
|
|
5060
5230
|
}
|
|
@@ -5065,22 +5235,27 @@ a{color:var(--blue)}
|
|
|
5065
5235
|
}
|
|
5066
5236
|
loadRunning = true;
|
|
5067
5237
|
loadState = {
|
|
5068
|
-
status: 'running', startTime: Date.now(), buckets: [], totalRequests: 0,
|
|
5069
|
-
totalErrors: 0, logs: [], script, config:
|
|
5238
|
+
runId, status: 'running', startTime: Date.now(), buckets: [], totalRequests: 0,
|
|
5239
|
+
totalErrors: 0, logs: [], script, config: loadConfig, summary: null,
|
|
5070
5240
|
};
|
|
5071
|
-
broadcast('load-started', { config:
|
|
5241
|
+
broadcast('load-started', { runId, config: loadConfig });
|
|
5072
5242
|
res.writeHead(200, jsonH);
|
|
5073
|
-
res.end(JSON.stringify({ ok: true }));
|
|
5074
|
-
// Build --env flags from cfg.envVars (e.g. { TOKEN: 'abc', BASE_URL: '...' })
|
|
5075
|
-
const envVars = (typeof cfg.envVars === 'object' && cfg.envVars !== null)
|
|
5076
|
-
? cfg.envVars
|
|
5077
|
-
: {};
|
|
5243
|
+
res.end(JSON.stringify({ ok: true, runId }));
|
|
5078
5244
|
const envFlags = [];
|
|
5079
5245
|
for (const [k, v] of Object.entries(envVars)) {
|
|
5080
5246
|
if (k && v !== undefined && v !== '')
|
|
5081
5247
|
envFlags.push('--env', `${k}=${v}`);
|
|
5082
5248
|
}
|
|
5083
|
-
|
|
5249
|
+
const args = [
|
|
5250
|
+
'run',
|
|
5251
|
+
'--vus', String(loadConfig.vus),
|
|
5252
|
+
'--duration', loadConfig.duration,
|
|
5253
|
+
...envFlags,
|
|
5254
|
+
'--summary-export', summaryPath,
|
|
5255
|
+
'--out', `json=${jsonOutPath}`,
|
|
5256
|
+
scriptPath,
|
|
5257
|
+
];
|
|
5258
|
+
loadProc = (0, child_process_1.spawn)('k6', args, {
|
|
5084
5259
|
cwd: projectRoot, env: { ...process.env }, shell: WIN, stdio: 'pipe',
|
|
5085
5260
|
});
|
|
5086
5261
|
const addLog = (line) => {
|
|
@@ -5156,7 +5331,7 @@ a{color:var(--blue)}
|
|
|
5156
5331
|
}
|
|
5157
5332
|
if (changed) {
|
|
5158
5333
|
const slice = loadState.buckets.slice(-30);
|
|
5159
|
-
broadcast('load-progress', { buckets: slice, total: loadState.totalRequests, errors: loadState.totalErrors });
|
|
5334
|
+
broadcast('load-progress', { runId: loadState.runId, buckets: slice, total: loadState.totalRequests, errors: loadState.totalErrors });
|
|
5160
5335
|
}
|
|
5161
5336
|
}
|
|
5162
5337
|
catch { }
|
|
@@ -5166,11 +5341,17 @@ a{color:var(--blue)}
|
|
|
5166
5341
|
loadRunning = false;
|
|
5167
5342
|
loadProc = null;
|
|
5168
5343
|
if (loadState.status === 'running') {
|
|
5169
|
-
loadState.status =
|
|
5344
|
+
loadState.status = code === 0 ? 'done' : 'error';
|
|
5170
5345
|
}
|
|
5171
5346
|
loadState.endTime = Date.now();
|
|
5172
|
-
loadState.summary =
|
|
5173
|
-
|
|
5347
|
+
loadState.summary = readK6Summary(summaryPath, code);
|
|
5348
|
+
if (loadState.summary?.totalRequests != null)
|
|
5349
|
+
loadState.totalRequests = loadState.summary.totalRequests;
|
|
5350
|
+
if (loadState.summary?.errorPct != null && loadState.summary.totalRequests != null) {
|
|
5351
|
+
loadState.totalErrors = Math.round(loadState.summary.totalRequests * (loadState.summary.errorPct / 100));
|
|
5352
|
+
}
|
|
5353
|
+
saveLoadRun();
|
|
5354
|
+
broadcast('load-done', { runId: loadState.runId, status: loadState.status, summary: loadState.summary });
|
|
5174
5355
|
try {
|
|
5175
5356
|
fs.unlinkSync(scriptPath);
|
|
5176
5357
|
}
|
|
@@ -5179,6 +5360,10 @@ a{color:var(--blue)}
|
|
|
5179
5360
|
fs.unlinkSync(jsonOutPath);
|
|
5180
5361
|
}
|
|
5181
5362
|
catch { }
|
|
5363
|
+
try {
|
|
5364
|
+
fs.unlinkSync(summaryPath);
|
|
5365
|
+
}
|
|
5366
|
+
catch { }
|
|
5182
5367
|
});
|
|
5183
5368
|
loadProc.on('error', (err) => {
|
|
5184
5369
|
clearInterval(watchInterval);
|
|
@@ -5187,11 +5372,17 @@ a{color:var(--blue)}
|
|
|
5187
5372
|
loadState.status = 'error';
|
|
5188
5373
|
loadState.endTime = Date.now();
|
|
5189
5374
|
addLog(`❌ k6 не запустился: ${err.message}`);
|
|
5190
|
-
|
|
5375
|
+
loadState.summary = { exitCode: null };
|
|
5376
|
+
saveLoadRun();
|
|
5377
|
+
broadcast('load-done', { runId: loadState.runId, status: 'error', summary: loadState.summary });
|
|
5191
5378
|
try {
|
|
5192
5379
|
fs.unlinkSync(scriptPath);
|
|
5193
5380
|
}
|
|
5194
5381
|
catch { }
|
|
5382
|
+
try {
|
|
5383
|
+
fs.unlinkSync(summaryPath);
|
|
5384
|
+
}
|
|
5385
|
+
catch { }
|
|
5195
5386
|
});
|
|
5196
5387
|
});
|
|
5197
5388
|
return;
|