@testsmith/perfornium 0.6.4 → 0.6.5

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 (190) hide show
  1. package/dist/cli/commands/distributed.js +2 -2
  2. package/dist/cli/commands/report.js +2 -2
  3. package/dist/cli/commands/run.js +2 -0
  4. package/dist/config/parser.js +2 -2
  5. package/dist/config/types/global-config.d.ts +82 -2
  6. package/dist/config/types/scenario-config.d.ts +2 -2
  7. package/dist/core/data/data-manager.d.ts +70 -0
  8. package/dist/core/data/data-manager.js +186 -0
  9. package/dist/core/data/data-provider.d.ts +85 -0
  10. package/dist/core/data/data-provider.js +468 -0
  11. package/dist/core/data/index.d.ts +8 -0
  12. package/dist/core/data/index.js +13 -0
  13. package/dist/core/execution/check-evaluator.d.ts +10 -0
  14. package/dist/core/execution/check-evaluator.js +79 -0
  15. package/dist/core/execution/data-extractor.d.ts +6 -0
  16. package/dist/core/execution/data-extractor.js +70 -0
  17. package/dist/core/execution/index.d.ts +3 -0
  18. package/dist/core/execution/index.js +9 -0
  19. package/dist/core/execution/json-payload-processor.d.ts +7 -0
  20. package/dist/core/execution/json-payload-processor.js +140 -0
  21. package/dist/core/factories/index.d.ts +2 -0
  22. package/dist/core/factories/index.js +7 -0
  23. package/dist/core/factories/output-handler-factory.d.ts +10 -0
  24. package/dist/core/factories/output-handler-factory.js +91 -0
  25. package/dist/core/factories/protocol-handler-factory.d.ts +12 -0
  26. package/dist/core/factories/protocol-handler-factory.js +96 -0
  27. package/dist/core/index.d.ts +3 -2
  28. package/dist/core/index.js +8 -3
  29. package/dist/core/reporting/dashboard-reporter.d.ts +17 -0
  30. package/dist/core/reporting/dashboard-reporter.js +127 -0
  31. package/dist/core/reporting/index.d.ts +1 -0
  32. package/dist/core/reporting/index.js +5 -0
  33. package/dist/core/step-executor.d.ts +6 -20
  34. package/dist/core/step-executor.js +72 -366
  35. package/dist/core/strategies/index.d.ts +2 -0
  36. package/dist/core/strategies/index.js +7 -0
  37. package/dist/core/strategies/scenario-selector.d.ts +13 -0
  38. package/dist/core/strategies/scenario-selector.js +37 -0
  39. package/dist/core/strategies/think-time-strategy.d.ts +15 -0
  40. package/dist/core/strategies/think-time-strategy.js +71 -0
  41. package/dist/core/test-runner.d.ts +4 -11
  42. package/dist/core/test-runner.js +105 -312
  43. package/dist/core/virtual-user.d.ts +7 -37
  44. package/dist/core/virtual-user.js +29 -269
  45. package/dist/dashboard/routes/api.d.ts +64 -0
  46. package/dist/dashboard/routes/api.js +569 -0
  47. package/dist/dashboard/routes/index.d.ts +2 -0
  48. package/dist/dashboard/routes/index.js +7 -0
  49. package/dist/dashboard/routes/static.d.ts +6 -0
  50. package/dist/dashboard/routes/static.js +76 -0
  51. package/dist/dashboard/server.d.ts +8 -84
  52. package/dist/dashboard/server.js +76 -2007
  53. package/dist/dashboard/services/file-scanner.d.ts +7 -0
  54. package/dist/dashboard/services/file-scanner.js +114 -0
  55. package/dist/dashboard/services/index.d.ts +5 -0
  56. package/dist/dashboard/services/index.js +13 -0
  57. package/dist/dashboard/services/influxdb-service.d.ts +41 -0
  58. package/dist/dashboard/services/influxdb-service.js +329 -0
  59. package/dist/dashboard/services/metrics-parser.d.ts +12 -0
  60. package/dist/dashboard/services/metrics-parser.js +209 -0
  61. package/dist/dashboard/services/results-manager.d.ts +17 -0
  62. package/dist/dashboard/services/results-manager.js +311 -0
  63. package/dist/dashboard/services/test-executor.d.ts +41 -0
  64. package/dist/dashboard/services/test-executor.js +250 -0
  65. package/dist/dashboard/services/workers-manager.d.ts +13 -0
  66. package/dist/dashboard/services/workers-manager.js +81 -0
  67. package/dist/dashboard/templates/index.html +122 -0
  68. package/dist/dashboard/templates/scripts/main.js +3280 -0
  69. package/dist/dashboard/templates/styles.css +402 -0
  70. package/dist/dashboard/types.d.ts +168 -0
  71. package/dist/dashboard/types.js +2 -0
  72. package/dist/distributed/result-aggregator.js +1 -3
  73. package/dist/metrics/batch/batch-processor.d.ts +27 -0
  74. package/dist/metrics/batch/batch-processor.js +85 -0
  75. package/dist/metrics/batch/index.d.ts +1 -0
  76. package/dist/metrics/batch/index.js +5 -0
  77. package/dist/metrics/collector.d.ts +46 -45
  78. package/dist/metrics/collector.js +179 -640
  79. package/dist/metrics/core/error-tracker.d.ts +9 -0
  80. package/dist/metrics/core/error-tracker.js +52 -0
  81. package/dist/metrics/core/index.d.ts +3 -0
  82. package/dist/metrics/core/index.js +9 -0
  83. package/dist/metrics/core/result-storage.d.ts +19 -0
  84. package/dist/metrics/core/result-storage.js +56 -0
  85. package/dist/metrics/core/statistics-engine.d.ts +27 -0
  86. package/dist/metrics/core/statistics-engine.js +91 -0
  87. package/dist/metrics/output/file-writer.d.ts +19 -0
  88. package/dist/metrics/output/file-writer.js +129 -0
  89. package/dist/metrics/output/index.d.ts +2 -0
  90. package/dist/metrics/output/index.js +10 -0
  91. package/dist/metrics/output/influxdb-writer.d.ts +89 -0
  92. package/dist/metrics/output/influxdb-writer.js +404 -0
  93. package/dist/metrics/realtime/dispatcher.d.ts +18 -0
  94. package/dist/metrics/realtime/dispatcher.js +45 -0
  95. package/dist/metrics/realtime/endpoints/graphite.d.ts +3 -0
  96. package/dist/metrics/realtime/endpoints/graphite.js +61 -0
  97. package/dist/metrics/realtime/endpoints/influxdb.d.ts +3 -0
  98. package/dist/metrics/realtime/endpoints/influxdb.js +35 -0
  99. package/dist/metrics/realtime/endpoints/webhook.d.ts +3 -0
  100. package/dist/metrics/realtime/endpoints/webhook.js +22 -0
  101. package/dist/metrics/realtime/endpoints/websocket.d.ts +3 -0
  102. package/dist/metrics/realtime/endpoints/websocket.js +25 -0
  103. package/dist/metrics/realtime/index.d.ts +5 -0
  104. package/dist/metrics/realtime/index.js +13 -0
  105. package/dist/metrics/reporting/index.d.ts +3 -0
  106. package/dist/metrics/reporting/index.js +9 -0
  107. package/dist/metrics/reporting/step-statistics.d.ts +6 -0
  108. package/dist/metrics/reporting/step-statistics.js +59 -0
  109. package/dist/metrics/reporting/summary-generator.d.ts +16 -0
  110. package/dist/metrics/reporting/summary-generator.js +46 -0
  111. package/dist/metrics/reporting/timeline-calculator.d.ts +7 -0
  112. package/dist/metrics/reporting/timeline-calculator.js +86 -0
  113. package/dist/metrics/types.d.ts +58 -0
  114. package/dist/outputs/csv.d.ts +2 -0
  115. package/dist/outputs/csv.js +21 -2
  116. package/dist/outputs/json.js +6 -2
  117. package/dist/protocols/rest/handler.d.ts +4 -53
  118. package/dist/protocols/rest/handler.js +73 -454
  119. package/dist/protocols/rest/request/auth-handler.d.ts +4 -0
  120. package/dist/protocols/rest/request/auth-handler.js +30 -0
  121. package/dist/protocols/rest/request/body-processor.d.ts +11 -0
  122. package/dist/protocols/rest/request/body-processor.js +62 -0
  123. package/dist/protocols/rest/request/index.d.ts +2 -0
  124. package/dist/protocols/rest/request/index.js +7 -0
  125. package/dist/protocols/rest/response/checks.d.ts +6 -0
  126. package/dist/protocols/rest/response/checks.js +71 -0
  127. package/dist/protocols/rest/response/index.d.ts +2 -0
  128. package/dist/protocols/rest/response/index.js +7 -0
  129. package/dist/protocols/rest/response/size-calculator.d.ts +12 -0
  130. package/dist/protocols/rest/response/size-calculator.js +64 -0
  131. package/dist/protocols/web/browser/highlight.d.ts +7 -0
  132. package/dist/protocols/web/browser/highlight.js +47 -0
  133. package/dist/protocols/web/browser/index.d.ts +4 -0
  134. package/dist/protocols/web/browser/index.js +11 -0
  135. package/dist/protocols/web/browser/manager.d.ts +20 -0
  136. package/dist/protocols/web/browser/manager.js +189 -0
  137. package/dist/protocols/web/browser/screenshot.d.ts +8 -0
  138. package/dist/protocols/web/browser/screenshot.js +69 -0
  139. package/dist/protocols/web/browser/storage.d.ts +5 -0
  140. package/dist/protocols/web/browser/storage.js +45 -0
  141. package/dist/protocols/web/commands/index.d.ts +5 -0
  142. package/dist/protocols/web/commands/index.js +11 -0
  143. package/dist/protocols/web/commands/interaction.d.ts +13 -0
  144. package/dist/protocols/web/commands/interaction.js +68 -0
  145. package/dist/protocols/web/commands/measurement.d.ts +16 -0
  146. package/dist/protocols/web/commands/measurement.js +33 -0
  147. package/dist/protocols/web/commands/navigation.d.ts +11 -0
  148. package/dist/protocols/web/commands/navigation.js +43 -0
  149. package/dist/protocols/web/commands/types.d.ts +12 -0
  150. package/dist/protocols/web/commands/types.js +2 -0
  151. package/dist/protocols/web/commands/verification.d.ts +11 -0
  152. package/dist/protocols/web/commands/verification.js +98 -0
  153. package/dist/protocols/web/handler.d.ts +19 -30
  154. package/dist/protocols/web/handler.js +160 -650
  155. package/dist/protocols/web/network/capture.d.ts +19 -0
  156. package/dist/protocols/web/network/capture.js +225 -0
  157. package/dist/protocols/web/network/filters.d.ts +5 -0
  158. package/dist/protocols/web/network/filters.js +49 -0
  159. package/dist/protocols/web/network/index.d.ts +4 -0
  160. package/dist/protocols/web/network/index.js +9 -0
  161. package/dist/protocols/web/network/types.d.ts +13 -0
  162. package/dist/protocols/web/network/types.js +2 -0
  163. package/dist/protocols/web/network/utils.d.ts +8 -0
  164. package/dist/protocols/web/network/utils.js +29 -0
  165. package/dist/reporting/chart-data/index.d.ts +5 -0
  166. package/dist/reporting/chart-data/index.js +13 -0
  167. package/dist/reporting/chart-data/network.d.ts +25 -0
  168. package/dist/reporting/chart-data/network.js +78 -0
  169. package/dist/reporting/chart-data/scenario.d.ts +37 -0
  170. package/dist/reporting/chart-data/scenario.js +76 -0
  171. package/dist/reporting/chart-data/step-statistics.d.ts +24 -0
  172. package/dist/reporting/chart-data/step-statistics.js +94 -0
  173. package/dist/reporting/chart-data/throughput.d.ts +16 -0
  174. package/dist/reporting/chart-data/throughput.js +24 -0
  175. package/dist/reporting/chart-data/timeline.d.ts +17 -0
  176. package/dist/reporting/chart-data/timeline.js +46 -0
  177. package/dist/reporting/handlebars-helpers.d.ts +1 -0
  178. package/dist/reporting/handlebars-helpers.js +63 -0
  179. package/dist/reporting/{enhanced-html-generator.d.ts → html-generator.d.ts} +1 -1
  180. package/dist/reporting/{enhanced-html-generator.js → html-generator.js} +10 -7
  181. package/dist/reporting/templates/{enhanced-report.hbs → report.hbs} +9 -9
  182. package/dist/utils/data-utils.d.ts +17 -0
  183. package/dist/utils/data-utils.js +129 -0
  184. package/dist/utils/template.js +2 -2
  185. package/package.json +5 -2
  186. package/dist/core/csv-data-provider.d.ts +0 -47
  187. package/dist/core/csv-data-provider.js +0 -265
  188. package/dist/reporting/generator.d.ts +0 -42
  189. package/dist/reporting/generator.js +0 -1217
  190. package/dist/reporting/templates/html.hbs +0 -2453
@@ -0,0 +1,19 @@
1
+ import { Page } from 'playwright';
2
+ import { NetworkCaptureConfig } from '../../../config';
3
+ import { CapturedNetworkCall } from '../../../metrics/types';
4
+ import { CurrentContext } from './types';
5
+ export type NetworkCallCallback = (call: CapturedNetworkCall) => void;
6
+ export declare class NetworkCaptureManager {
7
+ private networkCalls;
8
+ private pendingRequests;
9
+ private currentContext;
10
+ private onNetworkCall?;
11
+ constructor(onNetworkCall?: NetworkCallCallback);
12
+ setNetworkCallCallback(callback: NetworkCallCallback): void;
13
+ updateContext(vuId: number, context: CurrentContext): void;
14
+ setupNetworkCapture(page: Page, vuId: number, config: NetworkCaptureConfig): void;
15
+ getAndClearNetworkCalls(vuId: number): CapturedNetworkCall[];
16
+ getNetworkCalls(vuId: number): CapturedNetworkCall[];
17
+ clearVU(vuId: number): void;
18
+ clearAll(): void;
19
+ }
@@ -0,0 +1,225 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NetworkCaptureManager = void 0;
4
+ const logger_1 = require("../../../utils/logger");
5
+ const filters_1 = require("./filters");
6
+ const utils_1 = require("./utils");
7
+ class NetworkCaptureManager {
8
+ constructor(onNetworkCall) {
9
+ this.networkCalls = new Map();
10
+ this.pendingRequests = new Map();
11
+ this.currentContext = new Map();
12
+ this.onNetworkCall = onNetworkCall;
13
+ }
14
+ setNetworkCallCallback(callback) {
15
+ this.onNetworkCall = callback;
16
+ }
17
+ updateContext(vuId, context) {
18
+ this.currentContext.set(vuId, context);
19
+ }
20
+ setupNetworkCapture(page, vuId, config) {
21
+ if (!this.networkCalls.has(vuId)) {
22
+ this.networkCalls.set(vuId, []);
23
+ }
24
+ const capturedBodies = new Map();
25
+ logger_1.logger.info(`VU ${vuId}: Setting up network capture with config: ${JSON.stringify(config)}`);
26
+ // Use route interception to capture response bodies
27
+ page.route('**/*', async (route) => {
28
+ const request = route.request();
29
+ const url = request.url();
30
+ const shouldCapture = filters_1.NetworkFilters.shouldCaptureUrl(url, config) && config.capture_response_body;
31
+ if (!shouldCapture) {
32
+ await route.continue();
33
+ return;
34
+ }
35
+ try {
36
+ const response = await route.fetch();
37
+ const contentType = response.headers()['content-type'] || '';
38
+ let bodyText;
39
+ if (filters_1.NetworkFilters.shouldCaptureBodyByContentType(contentType, config)) {
40
+ try {
41
+ bodyText = await response.text();
42
+ }
43
+ catch {
44
+ // Body not available as text
45
+ }
46
+ }
47
+ if (bodyText !== undefined) {
48
+ capturedBodies.set(url, { body: bodyText, headers: response.headers() });
49
+ }
50
+ await route.fulfill({ response });
51
+ }
52
+ catch (error) {
53
+ await route.continue();
54
+ }
55
+ });
56
+ // Capture request start
57
+ page.on('request', (request) => {
58
+ const url = request.url();
59
+ if (!filters_1.NetworkFilters.shouldCaptureUrl(url, config))
60
+ return;
61
+ const requestId = utils_1.NetworkUtils.generateRequestId();
62
+ const startTime = Date.now();
63
+ const currentCtx = this.currentContext.get(vuId);
64
+ const call = {
65
+ id: requestId,
66
+ vu_id: vuId,
67
+ timestamp: startTime,
68
+ request_url: url,
69
+ request_method: request.method(),
70
+ request_headers: utils_1.NetworkUtils.captureHeaders(request.headers(), config),
71
+ request_body: utils_1.NetworkUtils.captureRequestBody(request.postData(), config),
72
+ request_body_truncated: utils_1.NetworkUtils.isBodyTruncated(request.postData(), config),
73
+ start_time: startTime,
74
+ resource_type: request.resourceType(),
75
+ scenario: currentCtx?.scenario,
76
+ step_name: currentCtx?.step_name,
77
+ success: false
78
+ };
79
+ this.pendingRequests.set(request, { call, vuId });
80
+ });
81
+ // Capture response
82
+ page.on('response', async (response) => {
83
+ const request = response.request();
84
+ const url = request.url();
85
+ if (!filters_1.NetworkFilters.shouldCaptureUrl(url, config))
86
+ return;
87
+ const pending = this.pendingRequests.get(request);
88
+ if (!pending) {
89
+ logger_1.logger.debug(`VU ${vuId}: Response without matching request for ${url}`);
90
+ return;
91
+ }
92
+ this.pendingRequests.delete(request);
93
+ const endTime = Date.now();
94
+ try {
95
+ const status = response.status();
96
+ const headers = response.headers();
97
+ let body;
98
+ let bodyTruncated = false;
99
+ const captured = capturedBodies.get(url);
100
+ if (captured && config.capture_response_body) {
101
+ body = utils_1.NetworkUtils.truncateBody(captured.body, config);
102
+ bodyTruncated = utils_1.NetworkUtils.isBodyTruncated(captured.body, config);
103
+ capturedBodies.delete(url);
104
+ }
105
+ else if (config.capture_response_body && filters_1.NetworkFilters.shouldCaptureBodyByContentType(headers['content-type'], config)) {
106
+ try {
107
+ const bodyText = await response.text();
108
+ body = utils_1.NetworkUtils.truncateBody(bodyText, config);
109
+ bodyTruncated = utils_1.NetworkUtils.isBodyTruncated(bodyText, config);
110
+ }
111
+ catch (bodyError) {
112
+ logger_1.logger.debug(`Failed to capture response body for ${url}: ${bodyError.message}`);
113
+ }
114
+ }
115
+ const completedCall = {
116
+ ...pending.call,
117
+ response_status: status,
118
+ response_status_text: response.statusText(),
119
+ response_headers: utils_1.NetworkUtils.captureHeaders(headers, config),
120
+ response_body: body,
121
+ response_body_truncated: bodyTruncated,
122
+ response_size: body?.length,
123
+ end_time: endTime,
124
+ duration: endTime - pending.call.start_time,
125
+ success: status >= 200 && status < 400
126
+ };
127
+ const vuCalls = this.networkCalls.get(vuId);
128
+ vuCalls.push(completedCall);
129
+ // Invoke callback for real-time processing (e.g., InfluxDB storage)
130
+ if (this.onNetworkCall) {
131
+ this.onNetworkCall(completedCall);
132
+ }
133
+ if (config.store_separate !== false) {
134
+ console.log(`[NETWORK] ${JSON.stringify({
135
+ id: completedCall.id,
136
+ vu: vuId,
137
+ url: url,
138
+ method: request.method(),
139
+ status: status,
140
+ statusText: completedCall.response_status_text,
141
+ duration: completedCall.duration,
142
+ size: completedCall.response_size || 0,
143
+ type: request.resourceType(),
144
+ success: completedCall.success,
145
+ requestHeaders: completedCall.request_headers,
146
+ requestBody: completedCall.request_body,
147
+ responseHeaders: completedCall.response_headers,
148
+ responseBody: completedCall.response_body
149
+ })}`);
150
+ }
151
+ }
152
+ catch (error) {
153
+ logger_1.logger.debug(`VU ${vuId}: Failed to capture response for ${url}: ${error.message}`);
154
+ }
155
+ });
156
+ // Capture request failures
157
+ page.on('requestfailed', (request) => {
158
+ const url = request.url();
159
+ if (!filters_1.NetworkFilters.shouldCaptureUrl(url, config))
160
+ return;
161
+ const pending = this.pendingRequests.get(request);
162
+ this.pendingRequests.delete(request);
163
+ const endTime = Date.now();
164
+ const currentCtx = this.currentContext.get(vuId);
165
+ const failedCall = {
166
+ id: pending?.call.id || utils_1.NetworkUtils.generateRequestId(),
167
+ vu_id: vuId,
168
+ timestamp: pending?.call.start_time || endTime,
169
+ request_url: url,
170
+ request_method: request.method(),
171
+ request_headers: pending?.call.request_headers,
172
+ request_body: pending?.call.request_body,
173
+ start_time: pending?.call.start_time || endTime,
174
+ end_time: endTime,
175
+ duration: endTime - (pending?.call.start_time || endTime),
176
+ resource_type: request.resourceType(),
177
+ scenario: pending?.call.scenario || currentCtx?.scenario,
178
+ step_name: pending?.call.step_name || currentCtx?.step_name,
179
+ success: false,
180
+ error: request.failure()?.errorText || 'Request failed'
181
+ };
182
+ const vuCalls = this.networkCalls.get(vuId);
183
+ vuCalls.push(failedCall);
184
+ // Invoke callback for real-time processing (e.g., InfluxDB storage)
185
+ if (this.onNetworkCall) {
186
+ this.onNetworkCall(failedCall);
187
+ }
188
+ if (config.store_separate !== false) {
189
+ console.log(`[NETWORK] ${JSON.stringify({
190
+ id: failedCall.id,
191
+ vu: vuId,
192
+ url: url,
193
+ method: request.method(),
194
+ status: 0,
195
+ statusText: 'Failed',
196
+ duration: failedCall.duration,
197
+ size: 0,
198
+ type: request.resourceType(),
199
+ success: false,
200
+ error: failedCall.error,
201
+ requestHeaders: failedCall.request_headers,
202
+ requestBody: failedCall.request_body
203
+ })}`);
204
+ }
205
+ });
206
+ }
207
+ getAndClearNetworkCalls(vuId) {
208
+ const calls = this.networkCalls.get(vuId) || [];
209
+ this.networkCalls.set(vuId, []);
210
+ return calls;
211
+ }
212
+ getNetworkCalls(vuId) {
213
+ return this.networkCalls.get(vuId) || [];
214
+ }
215
+ clearVU(vuId) {
216
+ this.networkCalls.delete(vuId);
217
+ this.currentContext.delete(vuId);
218
+ }
219
+ clearAll() {
220
+ this.networkCalls.clear();
221
+ this.pendingRequests.clear();
222
+ this.currentContext.clear();
223
+ }
224
+ }
225
+ exports.NetworkCaptureManager = NetworkCaptureManager;
@@ -0,0 +1,5 @@
1
+ import { NetworkCaptureConfig } from '../../../config';
2
+ export declare class NetworkFilters {
3
+ static shouldCaptureUrl(url: string, config: NetworkCaptureConfig): boolean;
4
+ static shouldCaptureBodyByContentType(contentType: string | undefined, config: NetworkCaptureConfig): boolean;
5
+ }
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NetworkFilters = void 0;
4
+ const minimatch_1 = require("minimatch");
5
+ const logger_1 = require("../../../utils/logger");
6
+ class NetworkFilters {
7
+ static shouldCaptureUrl(url, config) {
8
+ let pathname = url;
9
+ try {
10
+ pathname = new URL(url).pathname;
11
+ }
12
+ catch {
13
+ // Keep full URL if parsing fails
14
+ }
15
+ // Check exclude patterns first
16
+ if (config.exclude_patterns?.length) {
17
+ for (const pattern of config.exclude_patterns) {
18
+ const corePattern = pattern.replace(/\*\*/g, '').replace(/\*/g, '').replace(/^\/+|\/+$/g, '');
19
+ const simpleMatch = corePattern && (url.includes(corePattern) || pathname.includes(corePattern));
20
+ if ((0, minimatch_1.minimatch)(url, pattern) || (0, minimatch_1.minimatch)(pathname, pattern) || simpleMatch) {
21
+ return false;
22
+ }
23
+ }
24
+ }
25
+ // Check include patterns (if specified, URL must match at least one)
26
+ if (config.include_patterns?.length) {
27
+ for (const pattern of config.include_patterns) {
28
+ const corePattern = pattern.replace(/\*\*/g, '').replace(/\*/g, '').replace(/^\/+|\/+$/g, '');
29
+ const simpleMatch = corePattern && (url.includes(corePattern) || pathname.includes(corePattern));
30
+ const globMatch = (0, minimatch_1.minimatch)(url, pattern) || (0, minimatch_1.minimatch)(pathname, pattern);
31
+ if (globMatch || simpleMatch) {
32
+ logger_1.logger.debug(`URL captured: ${url} matches pattern ${pattern}`);
33
+ return true;
34
+ }
35
+ }
36
+ return false;
37
+ }
38
+ return true;
39
+ }
40
+ static shouldCaptureBodyByContentType(contentType, config) {
41
+ if (!contentType)
42
+ return false;
43
+ if (!config.content_type_filters?.length)
44
+ return true;
45
+ const lowerContentType = contentType.toLowerCase();
46
+ return config.content_type_filters.some(filter => lowerContentType.includes(filter.toLowerCase()));
47
+ }
48
+ }
49
+ exports.NetworkFilters = NetworkFilters;
@@ -0,0 +1,4 @@
1
+ export { NetworkCaptureManager, NetworkCallCallback } from './capture';
2
+ export { NetworkFilters } from './filters';
3
+ export { NetworkUtils } from './utils';
4
+ export { PendingRequest, CurrentContext, CapturedBody } from './types';
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NetworkUtils = exports.NetworkFilters = exports.NetworkCaptureManager = void 0;
4
+ var capture_1 = require("./capture");
5
+ Object.defineProperty(exports, "NetworkCaptureManager", { enumerable: true, get: function () { return capture_1.NetworkCaptureManager; } });
6
+ var filters_1 = require("./filters");
7
+ Object.defineProperty(exports, "NetworkFilters", { enumerable: true, get: function () { return filters_1.NetworkFilters; } });
8
+ var utils_1 = require("./utils");
9
+ Object.defineProperty(exports, "NetworkUtils", { enumerable: true, get: function () { return utils_1.NetworkUtils; } });
@@ -0,0 +1,13 @@
1
+ import { CapturedNetworkCall } from '../../../metrics/types';
2
+ export interface PendingRequest {
3
+ call: Partial<CapturedNetworkCall>;
4
+ vuId: number;
5
+ }
6
+ export interface CurrentContext {
7
+ scenario?: string;
8
+ step_name?: string;
9
+ }
10
+ export interface CapturedBody {
11
+ body: string;
12
+ headers: Record<string, string>;
13
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,8 @@
1
+ import { NetworkCaptureConfig } from '../../../config';
2
+ export declare class NetworkUtils {
3
+ static captureHeaders(headers: Record<string, string>, config: NetworkCaptureConfig): Record<string, string> | undefined;
4
+ static captureRequestBody(body: string | null | undefined, config: NetworkCaptureConfig): string | undefined;
5
+ static truncateBody(body: string | null | undefined, config: NetworkCaptureConfig): string | undefined;
6
+ static isBodyTruncated(body: string | null | undefined, config: NetworkCaptureConfig): boolean;
7
+ static generateRequestId(): string;
8
+ }
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NetworkUtils = void 0;
4
+ class NetworkUtils {
5
+ static captureHeaders(headers, config) {
6
+ return headers;
7
+ }
8
+ static captureRequestBody(body, config) {
9
+ if (!config.capture_request_body || !body)
10
+ return undefined;
11
+ return NetworkUtils.truncateBody(body, config);
12
+ }
13
+ static truncateBody(body, config) {
14
+ if (!body)
15
+ return undefined;
16
+ const maxSize = config.max_body_size || 10240;
17
+ return body.length > maxSize ? body.substring(0, maxSize) : body;
18
+ }
19
+ static isBodyTruncated(body, config) {
20
+ if (!body)
21
+ return false;
22
+ const maxSize = config.max_body_size || 10240;
23
+ return body.length > maxSize;
24
+ }
25
+ static generateRequestId() {
26
+ return `req_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
27
+ }
28
+ }
29
+ exports.NetworkUtils = NetworkUtils;
@@ -0,0 +1,5 @@
1
+ export { StepStatisticsCalculator, StepStatistics, StepResponseTime } from './step-statistics';
2
+ export { ThroughputCalculator, RequestsPerSecond, ResponsesPerSecond } from './throughput';
3
+ export { TimelineCalculator, VURampupDataPoint, TimelineDataPoint } from './timeline';
4
+ export { ScenarioCalculator, ScenarioData, ScenarioStatistics, ChartData } from './scenario';
5
+ export { NetworkStatisticsCalculator, NetworkStatistics, EndpointStats } from './network';
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NetworkStatisticsCalculator = exports.ScenarioCalculator = exports.TimelineCalculator = exports.ThroughputCalculator = exports.StepStatisticsCalculator = void 0;
4
+ var step_statistics_1 = require("./step-statistics");
5
+ Object.defineProperty(exports, "StepStatisticsCalculator", { enumerable: true, get: function () { return step_statistics_1.StepStatisticsCalculator; } });
6
+ var throughput_1 = require("./throughput");
7
+ Object.defineProperty(exports, "ThroughputCalculator", { enumerable: true, get: function () { return throughput_1.ThroughputCalculator; } });
8
+ var timeline_1 = require("./timeline");
9
+ Object.defineProperty(exports, "TimelineCalculator", { enumerable: true, get: function () { return timeline_1.TimelineCalculator; } });
10
+ var scenario_1 = require("./scenario");
11
+ Object.defineProperty(exports, "ScenarioCalculator", { enumerable: true, get: function () { return scenario_1.ScenarioCalculator; } });
12
+ var network_1 = require("./network");
13
+ Object.defineProperty(exports, "NetworkStatisticsCalculator", { enumerable: true, get: function () { return network_1.NetworkStatisticsCalculator; } });
@@ -0,0 +1,25 @@
1
+ import { TestResult } from '../../metrics/types';
2
+ export interface EndpointStats {
3
+ endpoint: string;
4
+ count: number;
5
+ avg_duration: number;
6
+ success_rate: number;
7
+ total_size: number;
8
+ status_distribution: Record<number, number>;
9
+ }
10
+ export interface NetworkStatistics {
11
+ total_calls: number;
12
+ successful_calls: number;
13
+ failed_calls: number;
14
+ success_rate: number;
15
+ avg_duration: number;
16
+ total_size: number;
17
+ by_endpoint: EndpointStats[];
18
+ by_type: Array<{
19
+ type: string;
20
+ count: number;
21
+ }>;
22
+ }
23
+ export declare class NetworkStatisticsCalculator {
24
+ static calculateNetworkStatistics(results: TestResult[]): NetworkStatistics | null;
25
+ }
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NetworkStatisticsCalculator = void 0;
4
+ class NetworkStatisticsCalculator {
5
+ static calculateNetworkStatistics(results) {
6
+ const allCalls = [];
7
+ results.forEach(result => {
8
+ if (result.custom_metrics?.network_calls) {
9
+ allCalls.push(...result.custom_metrics.network_calls);
10
+ }
11
+ });
12
+ if (allCalls.length === 0)
13
+ return null;
14
+ const successfulCalls = allCalls.filter(c => c.success);
15
+ const failedCalls = allCalls.filter(c => !c.success);
16
+ const durations = allCalls.filter(c => c.duration).map(c => c.duration);
17
+ const avgDuration = durations.length > 0
18
+ ? durations.reduce((a, b) => a + b, 0) / durations.length
19
+ : 0;
20
+ const totalSize = allCalls.reduce((sum, c) => sum + (c.response_size || 0), 0);
21
+ const byEndpoint = new Map();
22
+ allCalls.forEach(call => {
23
+ try {
24
+ const url = new URL(call.request_url);
25
+ const endpoint = url.pathname;
26
+ if (!byEndpoint.has(endpoint)) {
27
+ byEndpoint.set(endpoint, []);
28
+ }
29
+ byEndpoint.get(endpoint).push(call);
30
+ }
31
+ catch {
32
+ // Invalid URL, skip
33
+ }
34
+ });
35
+ const endpointStats = Array.from(byEndpoint.entries())
36
+ .map(([endpoint, calls]) => {
37
+ const successful = calls.filter(c => c.success);
38
+ const callDurations = calls.filter(c => c.duration).map(c => c.duration);
39
+ const avgDur = callDurations.length > 0
40
+ ? callDurations.reduce((a, b) => a + b, 0) / callDurations.length
41
+ : 0;
42
+ const statusDist = {};
43
+ calls.forEach(c => {
44
+ const status = c.response_status || 0;
45
+ statusDist[status] = (statusDist[status] || 0) + 1;
46
+ });
47
+ return {
48
+ endpoint,
49
+ count: calls.length,
50
+ avg_duration: Math.round(avgDur),
51
+ success_rate: calls.length > 0 ? (successful.length / calls.length) * 100 : 0,
52
+ total_size: calls.reduce((sum, c) => sum + (c.response_size || 0), 0),
53
+ status_distribution: statusDist
54
+ };
55
+ })
56
+ .sort((a, b) => b.count - a.count)
57
+ .slice(0, 20);
58
+ const byType = new Map();
59
+ allCalls.forEach(call => {
60
+ const type = call.resource_type || 'unknown';
61
+ byType.set(type, (byType.get(type) || 0) + 1);
62
+ });
63
+ const typeStats = Array.from(byType.entries())
64
+ .map(([type, count]) => ({ type, count }))
65
+ .sort((a, b) => b.count - a.count);
66
+ return {
67
+ total_calls: allCalls.length,
68
+ successful_calls: successfulCalls.length,
69
+ failed_calls: failedCalls.length,
70
+ success_rate: allCalls.length > 0 ? (successfulCalls.length / allCalls.length) * 100 : 0,
71
+ avg_duration: Math.round(avgDuration),
72
+ total_size: totalSize,
73
+ by_endpoint: endpointStats,
74
+ by_type: typeStats
75
+ };
76
+ }
77
+ }
78
+ exports.NetworkStatisticsCalculator = NetworkStatisticsCalculator;
@@ -0,0 +1,37 @@
1
+ import { TestResult } from '../../metrics/types';
2
+ export interface ScenarioData {
3
+ name: string;
4
+ total: number;
5
+ success: number;
6
+ errors: number;
7
+ avgResponseTime: number;
8
+ responseTimes: number[];
9
+ successRate: number;
10
+ }
11
+ export interface ScenarioStatistics extends ScenarioData {
12
+ percentiles: Record<number, number>;
13
+ minResponseTime: number;
14
+ maxResponseTime: number;
15
+ p50: number;
16
+ p90: number;
17
+ p95: number;
18
+ p99: number;
19
+ }
20
+ export interface ChartData {
21
+ responseTimes: Array<{
22
+ timestamp: number;
23
+ duration: number;
24
+ scenario: string;
25
+ }>;
26
+ errors: Array<{
27
+ timestamp: number;
28
+ error: string;
29
+ scenario: string;
30
+ }>;
31
+ scenarios: ScenarioData[];
32
+ }
33
+ export declare class ScenarioCalculator {
34
+ static groupByScenario(results: TestResult[]): ScenarioData[];
35
+ static prepareChartData(results: TestResult[]): ChartData;
36
+ static calculateScenarioStatistics(results: TestResult[]): ScenarioStatistics[];
37
+ }
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ScenarioCalculator = void 0;
4
+ const statistics_1 = require("../statistics");
5
+ class ScenarioCalculator {
6
+ static groupByScenario(results) {
7
+ const scenarios = {};
8
+ results.forEach(result => {
9
+ if (!scenarios[result.scenario]) {
10
+ scenarios[result.scenario] = {
11
+ name: result.scenario,
12
+ total: 0,
13
+ success: 0,
14
+ errors: 0,
15
+ avgResponseTime: 0,
16
+ responseTimes: [],
17
+ successRate: 0
18
+ };
19
+ }
20
+ const scenario = scenarios[result.scenario];
21
+ scenario.total++;
22
+ if (result.success) {
23
+ scenario.success++;
24
+ scenario.responseTimes.push(result.duration);
25
+ }
26
+ else {
27
+ scenario.errors++;
28
+ }
29
+ });
30
+ Object.values(scenarios).forEach((scenario) => {
31
+ if (scenario.responseTimes.length > 0) {
32
+ scenario.avgResponseTime = scenario.responseTimes.reduce((a, b) => a + b, 0) / scenario.responseTimes.length;
33
+ }
34
+ scenario.successRate = scenario.total > 0 ? (scenario.success / scenario.total) * 100 : 0;
35
+ });
36
+ return Object.values(scenarios);
37
+ }
38
+ static prepareChartData(results) {
39
+ const responseTimes = results
40
+ .filter(r => r.success)
41
+ .map(r => ({
42
+ timestamp: r.timestamp,
43
+ duration: r.duration,
44
+ scenario: r.scenario
45
+ }));
46
+ const errors = results
47
+ .filter(r => !r.success)
48
+ .map(r => ({
49
+ timestamp: r.timestamp,
50
+ error: r.error || 'Unknown error',
51
+ scenario: r.scenario
52
+ }));
53
+ return {
54
+ responseTimes,
55
+ errors,
56
+ scenarios: ScenarioCalculator.groupByScenario(results)
57
+ };
58
+ }
59
+ static calculateScenarioStatistics(results) {
60
+ const scenarioGroups = ScenarioCalculator.groupByScenario(results);
61
+ return scenarioGroups.map((scenario) => {
62
+ const percentiles = statistics_1.StatisticsCalculator.calculatePercentiles(scenario.responseTimes, [50, 90, 95, 99]);
63
+ return {
64
+ ...scenario,
65
+ percentiles,
66
+ minResponseTime: scenario.responseTimes.length > 0 ? Math.min(...scenario.responseTimes) : 0,
67
+ maxResponseTime: scenario.responseTimes.length > 0 ? Math.max(...scenario.responseTimes) : 0,
68
+ p50: percentiles[50] || 0,
69
+ p90: percentiles[90] || 0,
70
+ p95: percentiles[95] || 0,
71
+ p99: percentiles[99] || 0
72
+ };
73
+ });
74
+ }
75
+ }
76
+ exports.ScenarioCalculator = ScenarioCalculator;
@@ -0,0 +1,24 @@
1
+ import { TestResult, StepStatistics } from '../../metrics/types';
2
+ export type { StepStatistics } from '../../metrics/types';
3
+ export interface StepResponseTime {
4
+ step_name: string;
5
+ count: number;
6
+ avg: number;
7
+ min: number;
8
+ max: number;
9
+ p50: number;
10
+ p90: number;
11
+ p95: number;
12
+ p99: number;
13
+ response_times: number[];
14
+ timeline_data: Array<{
15
+ duration: number;
16
+ timestamp: number;
17
+ vu_id: number;
18
+ iteration: number;
19
+ }>;
20
+ }
21
+ export declare class StepStatisticsCalculator {
22
+ static calculateStepStatistics(results: TestResult[]): StepStatistics[];
23
+ static calculateStepResponseTimes(results: TestResult[]): StepResponseTime[];
24
+ }