automation_model 1.0.795-dev → 1.0.795-stage

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 (44) hide show
  1. package/lib/api.js +40 -12
  2. package/lib/api.js.map +1 -1
  3. package/lib/auto_page.d.ts +1 -1
  4. package/lib/auto_page.js +59 -16
  5. package/lib/auto_page.js.map +1 -1
  6. package/lib/browser_manager.d.ts +2 -3
  7. package/lib/browser_manager.js +115 -71
  8. package/lib/browser_manager.js.map +1 -1
  9. package/lib/bruno.js.map +1 -1
  10. package/lib/check_performance.d.ts +1 -0
  11. package/lib/check_performance.js +57 -0
  12. package/lib/check_performance.js.map +1 -0
  13. package/lib/command_common.d.ts +2 -2
  14. package/lib/command_common.js +30 -23
  15. package/lib/command_common.js.map +1 -1
  16. package/lib/file_checker.js +7 -0
  17. package/lib/file_checker.js.map +1 -1
  18. package/lib/index.js +1 -0
  19. package/lib/index.js.map +1 -1
  20. package/lib/init_browser.d.ts +1 -2
  21. package/lib/init_browser.js +137 -128
  22. package/lib/init_browser.js.map +1 -1
  23. package/lib/locator_log.js.map +1 -1
  24. package/lib/network.d.ts +2 -2
  25. package/lib/network.js +286 -203
  26. package/lib/network.js.map +1 -1
  27. package/lib/route.d.ts +64 -2
  28. package/lib/route.js +493 -194
  29. package/lib/route.js.map +1 -1
  30. package/lib/scripts/axe.mini.js +23978 -1
  31. package/lib/snapshot_validation.js +3 -0
  32. package/lib/snapshot_validation.js.map +1 -1
  33. package/lib/stable_browser.d.ts +14 -8
  34. package/lib/stable_browser.js +453 -87
  35. package/lib/stable_browser.js.map +1 -1
  36. package/lib/table_helper.js +14 -0
  37. package/lib/table_helper.js.map +1 -1
  38. package/lib/test_context.d.ts +1 -0
  39. package/lib/test_context.js +1 -0
  40. package/lib/test_context.js.map +1 -1
  41. package/lib/utils.d.ts +7 -3
  42. package/lib/utils.js +157 -25
  43. package/lib/utils.js.map +1 -1
  44. package/package.json +18 -11
package/lib/network.js CHANGED
@@ -3,6 +3,60 @@ import fs from "fs";
3
3
  import { _stepNameToTemplate } from "./route.js";
4
4
  import crypto from "crypto";
5
5
  import { tmpdir } from "os";
6
+ import createDebug from "debug";
7
+ const debug = createDebug("automation_model:network");
8
+ class SaveQueue {
9
+ queue = [];
10
+ isProcessing = false;
11
+ maxRetries = 3;
12
+ async enqueueSave(current) {
13
+ this.queue.push({ current, retryCount: 0 });
14
+ if (!this.isProcessing) {
15
+ await this.processQueue();
16
+ }
17
+ }
18
+ async processQueue() {
19
+ this.isProcessing = true;
20
+ while (this.queue.length > 0) {
21
+ const item = this.queue.shift();
22
+ try {
23
+ await this.saveSafely(item.current);
24
+ }
25
+ catch (error) {
26
+ console.error(`Save failed for ${item.current ? "current" : "previous"} step:`, error);
27
+ // Retry logic
28
+ if (item.retryCount < this.maxRetries) {
29
+ item.retryCount++;
30
+ this.queue.unshift(item); // Put it back at the front
31
+ await new Promise((resolve) => setTimeout(resolve, 100 * item.retryCount)); // Exponential backoff
32
+ }
33
+ }
34
+ }
35
+ this.isProcessing = false;
36
+ }
37
+ async saveSafely(current) {
38
+ const stepHash = current ? executionState.currentStepHash : executionState.previousStepHash;
39
+ if (!stepHash)
40
+ return;
41
+ const file = path.join(detailedNetworkFolder, `${stepHash}.json`);
42
+ const entries = current
43
+ ? Array.from(executionState.liveRequestsMap.values())
44
+ : Array.from(executionState.liveRequestsMapPrevious.values());
45
+ // Ensure all entries are JSON-serializable
46
+ const validEntries = entries.filter((entry) => {
47
+ try {
48
+ JSON.stringify(entry);
49
+ return true;
50
+ }
51
+ catch {
52
+ console.warn(`Skipping non-serializable entry: ${entry.requestId}`);
53
+ return false;
54
+ }
55
+ });
56
+ const jsonString = JSON.stringify(validEntries, null, 2);
57
+ await fs.promises.writeFile(file, jsonString, "utf8");
58
+ }
59
+ }
6
60
  function _getNetworkFile(world = null, web = null, context = null) {
7
61
  let networkFile = null;
8
62
  if (world && world.reportFolder) {
@@ -50,7 +104,12 @@ function registerNetworkEvents(world, web, context, page) {
50
104
  const networkFile = _getNetworkFile(world, web, context);
51
105
  function saveNetworkData() {
52
106
  if (context && context.networkData) {
53
- fs.writeFileSync(networkFile, JSON.stringify(context.networkData, null, 2), "utf8");
107
+ try {
108
+ fs.writeFileSync(networkFile, JSON.stringify(context.networkData, null, 2), "utf8");
109
+ }
110
+ catch (error) {
111
+ console.error("Error saving network data:", error);
112
+ }
54
113
  }
55
114
  }
56
115
  if (!context) {
@@ -65,109 +124,99 @@ function registerNetworkEvents(world, web, context, page) {
65
124
  const networkData = context.networkData;
66
125
  // Event listener for when a request is made
67
126
  page.on("request", (request) => {
68
- // console.log("Request started:", request.url());
69
- const requestId = requestIdCounter++;
70
- request.requestId = requestId; // Assign a unique ID to the request
71
- handleRequest(request);
72
- const startTime = Date.now();
73
- requestTimes.set(requestId, startTime);
74
- // Initialize data for this request
75
- networkData.push({
76
- requestId,
77
- requestStart: startTime,
78
- requestUrl: request.url(),
79
- method: request.method(),
80
- status: "Pending",
81
- responseTime: null,
82
- responseReceived: null,
83
- responseEnd: null,
84
- size: null,
85
- });
86
- saveNetworkData();
87
- });
88
- // Event listener for when a response is received
89
- page.on("response", async (response) => {
90
- const request = response.request();
91
- const requestId = request.requestId;
92
- const receivedTime = Date.now();
93
- // await handleRequestFinishedOrFailed(request, false);
94
- // Find the corresponding data object
95
- const data = networkData.find((item) => item.requestId === requestId);
96
- if (data) {
97
- data.status = response.status();
98
- data.responseReceived = receivedTime;
127
+ try {
128
+ // console.log("Request started:", request.url());
129
+ const requestId = requestIdCounter++;
130
+ request.requestId = requestId; // Assign a unique ID to the request
131
+ handleRequest(request, context);
132
+ const startTime = Date.now();
133
+ requestTimes.set(requestId, startTime);
134
+ // Initialize data for this request
135
+ networkData.push({
136
+ requestId,
137
+ requestStart: startTime,
138
+ requestUrl: request.url(),
139
+ method: request.method(),
140
+ status: "Pending",
141
+ responseTime: null,
142
+ responseReceived: null,
143
+ responseEnd: null,
144
+ size: null,
145
+ });
99
146
  saveNetworkData();
100
147
  }
101
- else {
102
- // console.error("No data found for request ID", requestId);
148
+ catch (error) {
149
+ // console.error("Error handling request:", error);
103
150
  }
104
151
  });
105
- // Event listener for when a request is finished
106
- page.on("requestfinished", async (request) => {
107
- const requestId = request.requestId;
108
- const endTime = Date.now();
109
- const startTime = requestTimes.get(requestId);
110
- await handleRequestFinishedOrFailed(request, false);
111
- const response = await request.response();
112
- const timing = request.timing();
113
- // Find the corresponding data object
114
- const data = networkData.find((item) => item.requestId === requestId);
115
- if (data) {
116
- data.responseEnd = endTime;
117
- data.responseTime = endTime - startTime;
118
- // Get response size
119
- try {
120
- const body = await response.body();
121
- data.size = body.length;
122
- }
123
- catch (e) {
124
- data.size = 0;
152
+ // Event listener for when a response is received
153
+ page.on("response", async (response) => {
154
+ try {
155
+ const request = response.request();
156
+ const requestId = request.requestId;
157
+ const receivedTime = Date.now();
158
+ // await handleRequestFinishedOrFailed(request, false);
159
+ // Find the corresponding data object
160
+ const data = networkData.find((item) => item.requestId === requestId);
161
+ if (data) {
162
+ data.status = response.status();
163
+ data.responseReceived = receivedTime;
164
+ saveNetworkData();
125
165
  }
126
- const type = request.resourceType();
127
- /*
128
- domainLookupStart: 80.655,
129
- domainLookupEnd: 80.668,
130
- connectStart: 80.668,
131
- secureConnectionStart: 106.688,
132
- connectEnd: 129.69,
133
- requestStart: 129.81,
134
- responseStart: 187.006,
135
- responseEnd: 188.209
136
- */
137
- data.type = type;
138
- data.domainLookupStart = timing.domainLookupStart;
139
- data.domainLookupEnd = timing.domainLookupEnd;
140
- data.connectStart = timing.connectStart;
141
- data.secureConnectionStart = timing.secureConnectionStart;
142
- data.connectEnd = timing.connectEnd;
143
- data.requestStart = timing.requestStart;
144
- data.responseStart = timing.responseStart;
145
- data.responseEnd = timing.responseEnd;
146
- saveNetworkData();
147
- if (world && world.attach) {
148
- world.attach(JSON.stringify(data), { mediaType: "application/json+network" });
166
+ else {
167
+ // console.error("No data found for request ID", requestId);
149
168
  }
150
169
  }
151
- else {
152
- // console.error("No data found for request ID", requestId);
170
+ catch (error) {
171
+ // console.error("Error handling response:", error);
153
172
  }
154
173
  });
155
- // Event listener for when a request fails
156
- page.on("requestfailed", async (request) => {
157
- const requestId = request.requestId;
158
- const endTime = Date.now();
159
- const startTime = requestTimes.get(requestId);
160
- await handleRequestFinishedOrFailed(request, true);
174
+ // Event listener for when a request is finished
175
+ page.on("requestfinished", async (request) => {
161
176
  try {
162
- const res = await request.response();
163
- const statusCode = res ? res.status() : request.failure().errorText;
177
+ const requestId = request.requestId;
178
+ const endTime = Date.now();
179
+ const startTime = requestTimes.get(requestId);
180
+ await handleRequestFinishedOrFailed(request, false, context);
181
+ const response = await request.response();
182
+ const timing = request.timing();
164
183
  // Find the corresponding data object
165
184
  const data = networkData.find((item) => item.requestId === requestId);
166
185
  if (data) {
167
186
  data.responseEnd = endTime;
168
187
  data.responseTime = endTime - startTime;
169
- data.status = statusCode;
170
- data.size = 0;
188
+ // Get response size
189
+ try {
190
+ let size = 0;
191
+ if (responseHasBody(response)) {
192
+ const buf = await response.body();
193
+ size = buf?.length ?? 0;
194
+ }
195
+ data.size = size;
196
+ }
197
+ catch {
198
+ data.size = 0;
199
+ }
200
+ const type = request.resourceType();
201
+ /*
202
+ domainLookupStart: 80.655,
203
+ domainLookupEnd: 80.668,
204
+ connectStart: 80.668,
205
+ secureConnectionStart: 106.688,
206
+ connectEnd: 129.69,
207
+ requestStart: 129.81,
208
+ responseStart: 187.006,
209
+ responseEnd: 188.209
210
+ */
211
+ data.type = type;
212
+ data.domainLookupStart = timing.domainLookupStart;
213
+ data.domainLookupEnd = timing.domainLookupEnd;
214
+ data.connectStart = timing.connectStart;
215
+ data.secureConnectionStart = timing.secureConnectionStart;
216
+ data.connectEnd = timing.connectEnd;
217
+ data.requestStart = timing.requestStart;
218
+ data.responseStart = timing.responseStart;
219
+ data.responseEnd = timing.responseEnd;
171
220
  saveNetworkData();
172
221
  if (world && world.attach) {
173
222
  world.attach(JSON.stringify(data), { mediaType: "application/json+network" });
@@ -178,7 +227,41 @@ function registerNetworkEvents(world, web, context, page) {
178
227
  }
179
228
  }
180
229
  catch (error) {
181
- // ignore
230
+ // console.error("Error handling request finished:", error);
231
+ }
232
+ });
233
+ // Event listener for when a request fails
234
+ page.on("requestfailed", async (request) => {
235
+ try {
236
+ const requestId = request.requestId;
237
+ const endTime = Date.now();
238
+ const startTime = requestTimes.get(requestId);
239
+ await handleRequestFinishedOrFailed(request, true, context);
240
+ try {
241
+ const res = await request.response();
242
+ const statusCode = res ? res.status() : request.failure().errorText;
243
+ // Find the corresponding data object
244
+ const data = networkData.find((item) => item.requestId === requestId);
245
+ if (data) {
246
+ data.responseEnd = endTime;
247
+ data.responseTime = endTime - startTime;
248
+ data.status = statusCode;
249
+ data.size = 0;
250
+ saveNetworkData();
251
+ if (world && world.attach) {
252
+ world.attach(JSON.stringify(data), { mediaType: "application/json+network" });
253
+ }
254
+ }
255
+ else {
256
+ // console.error("No data found for request ID", requestId);
257
+ }
258
+ }
259
+ catch (error) {
260
+ // ignore
261
+ }
262
+ }
263
+ catch (error) {
264
+ // console.error("Error handling request failed:", error);
182
265
  }
183
266
  });
184
267
  }
@@ -188,7 +271,11 @@ function registerNetworkEvents(world, web, context, page) {
188
271
  }
189
272
  }
190
273
  async function appendEntryToStepFile(stepHash, entry) {
274
+ if (!stepHash)
275
+ return;
276
+ const debug = createDebug("network:appendEntryToStepFile");
191
277
  const file = path.join(detailedNetworkFolder, `${stepHash}.json`);
278
+ debug("appending to step file:", file);
192
279
  let data = [];
193
280
  try {
194
281
  /* read if it already exists */
@@ -199,16 +286,31 @@ async function appendEntryToStepFile(stepHash, entry) {
199
286
  /* ignore – file does not exist or cannot be parsed */
200
287
  }
201
288
  data.push(entry);
202
- await fs.promises.writeFile(file, JSON.stringify(data, null, 2), "utf8");
289
+ try {
290
+ debug("writing to step file:", file);
291
+ await fs.promises.writeFile(file, JSON.stringify(data, null, 2), "utf8");
292
+ }
293
+ catch (error) {
294
+ debug("Error writing to step file:", error);
295
+ }
203
296
  }
204
297
  const detailedNetworkFolder = path.join(tmpdir(), "blinq_network_events");
298
+ let outOfStep = true;
299
+ let timeoutId = null;
205
300
  const executionState = {
206
301
  currentStepHash: null,
302
+ previousStepHash: null,
207
303
  liveRequestsMap: new Map(),
304
+ liveRequestsMapPrevious: new Map(),
208
305
  };
209
- export function networkBeforeStep(stepName) {
210
- const storeDetailedNetworkData = process.env.STORE_DETAILED_NETWORK_DATA === "true";
211
- if (!storeDetailedNetworkData) {
306
+ const storeDetailedNetworkData = (context) => context && context.STORE_DETAILED_NETWORK_DATA === true;
307
+ export function networkBeforeStep(stepName, context) {
308
+ if (timeoutId) {
309
+ clearTimeout(timeoutId);
310
+ timeoutId = null;
311
+ }
312
+ outOfStep = false;
313
+ if (!storeDetailedNetworkData(context)) {
212
314
  return;
213
315
  }
214
316
  // check if the folder exists, if not create it
@@ -217,15 +319,10 @@ export function networkBeforeStep(stepName) {
217
319
  }
218
320
  // const stepHash = stepNameToHash(stepName);
219
321
  let stepHash = "";
220
- executionState.liveRequestsMap.clear();
221
- if (process.env.CURRENT_STEP_ID) {
222
- // console.log("Using CURRENT_STEP_ID from environment variables:", process.env.CURRENT_STEP_ID);
223
- // If CURRENT_STEP_ID is set, use it as the step hash
224
- stepHash = process.env.CURRENT_STEP_ID;
225
- }
226
- else {
227
- stepHash = stepNameToHash(stepName);
228
- }
322
+ executionState.liveRequestsMapPrevious = executionState.liveRequestsMap;
323
+ executionState.liveRequestsMap = new Map();
324
+ stepHash = stepNameToHash(stepName);
325
+ executionState.previousStepHash = executionState.currentStepHash; // NEW
229
326
  executionState.currentStepHash = stepHash;
230
327
  // check if the file exists, if exists delete it
231
328
  const networkFile = path.join(detailedNetworkFolder, `${stepHash}.json`);
@@ -236,30 +333,35 @@ export function networkBeforeStep(stepName) {
236
333
  // Ignore error if file does not exist
237
334
  }
238
335
  }
239
- export async function networkAfterStep(stepName) {
240
- const storeDetailedNetworkData = process.env.STORE_DETAILED_NETWORK_DATA === "true";
241
- if (!storeDetailedNetworkData) {
336
+ const saveQueue = new SaveQueue();
337
+ async function saveMap(current) {
338
+ await saveQueue.enqueueSave(current);
339
+ }
340
+ export async function networkAfterStep(stepName, context) {
341
+ if (!storeDetailedNetworkData(context)) {
242
342
  return;
243
343
  }
244
- await new Promise((r) => setTimeout(r, 1000));
245
- const entries = Array.from(executionState.liveRequestsMap.values());
246
- const file = path.join(detailedNetworkFolder, `${executionState.currentStepHash}.json`);
247
- await fs.promises.writeFile(file, JSON.stringify(entries, null, 2), "utf8");
344
+ //await new Promise((r) => setTimeout(r, 1000));
345
+ await saveMap(true);
248
346
  /* reset for next step */
249
- executionState.liveRequestsMap.clear();
250
- executionState.previousStepHash = executionState.currentStepHash; // ➋ NEW
251
- executionState.currentStepHash = null;
347
+ //executionState.previousStepHash = executionState.currentStepHash; // ➋ NEW
348
+ //executionState.liveRequestsMap.clear();
349
+ outOfStep = true;
350
+ // set a timer of 60 seconds to the outOfStep, after that it will be set to false so no network collection will happen
351
+ timeoutId = setTimeout(() => {
352
+ outOfStep = false;
353
+ context.STORE_DETAILED_NETWORK_DATA = false;
354
+ }, 60000);
252
355
  }
253
356
  function stepNameToHash(stepName) {
254
357
  const templateName = _stepNameToTemplate(stepName);
255
358
  // create hash from the template name
256
359
  return crypto.createHash("sha256").update(templateName).digest("hex");
257
360
  }
258
- function handleRequest(request) {
259
- const storeDetailedNetworkData = process.env.STORE_DETAILED_NETWORK_DATA === "true";
260
- if (!storeDetailedNetworkData || !executionState.currentStepHash) {
361
+ function handleRequest(request, context) {
362
+ const debug = createDebug("automation_model:network:handleRequest");
363
+ if (!storeDetailedNetworkData(context))
261
364
  return;
262
- }
263
365
  const entry = {
264
366
  requestId: request.requestId,
265
367
  url: request.url(),
@@ -270,48 +372,33 @@ function handleRequest(request) {
270
372
  stepHash: executionState.currentStepHash,
271
373
  };
272
374
  executionState.liveRequestsMap.set(request, entry);
273
- // console.log("Request started:", entry);
375
+ debug("Request to", request.url(), "with", request.requestId, "added to current step map at", Date.now());
274
376
  }
275
- function saveNetworkDataToFile(requestData) {
276
- const storeDetailedNetworkData = process.env.STORE_DETAILED_NETWORK_DATA === "true";
277
- if (!storeDetailedNetworkData) {
377
+ async function handleRequestFinishedOrFailed(request, failed, context) {
378
+ const debug = createDebug("automation_model:network:handleRequestFinishedOrFailed");
379
+ if (!storeDetailedNetworkData(context))
278
380
  return;
279
- }
280
- const networkFile = path.join(detailedNetworkFolder, `${requestData.stepHash}.json`);
281
- // read the existing data if it exists (should be an array)
282
- let existingData = [];
283
- if (fs.existsSync(networkFile)) {
284
- const data = fs.readFileSync(networkFile, "utf8");
285
- try {
286
- existingData = JSON.parse(data);
287
- }
288
- catch (e) {
289
- // console.error("Failed to parse existing network data:", e);
290
- }
291
- }
292
- // Add the live requests to the existing data
293
- existingData.push(requestData);
294
- // Save the updated data back to the file
295
- // console.log("Saving network data to file:", networkFile);
296
- fs.writeFileSync(networkFile, JSON.stringify(existingData, null, 2), "utf8");
297
- }
298
- async function handleRequestFinishedOrFailed(request, failed) {
299
- const storeDetailedNetworkData = process.env.STORE_DETAILED_NETWORK_DATA === "true";
300
- if (!storeDetailedNetworkData) {
301
- return;
302
- }
381
+ const requestId = request.requestId;
382
+ debug("Request id in handleRequestFinishedOrFailed:", requestId, "at", Date.now());
303
383
  // const response = await request.response(); // This may be null if the request failed
304
384
  let entry = executionState.liveRequestsMap.get(request);
385
+ debug("Request entry found in current map:", entry?.requestId || false);
305
386
  if (!entry) {
306
- entry = {
307
- requestId: request.requestId,
308
- url: request.url(),
309
- method: request.method?.() ?? "GET",
310
- headers: request.headers?.() ?? {},
311
- postData: request.postData?.() ?? undefined,
312
- stepHash: executionState.previousStepHash ?? "unknown",
313
- requestTimestamp: Date.now(),
314
- };
387
+ // check if the request is in the previous step's map
388
+ entry = executionState.liveRequestsMapPrevious.get(request);
389
+ debug("Request entry found in previous map:", entry?.requestId || false);
390
+ if (!entry) {
391
+ debug("No entry, creating fallback! for url:", request.url());
392
+ entry = {
393
+ requestId: request.requestId,
394
+ url: request.url(),
395
+ method: request.method?.() ?? "GET",
396
+ headers: request.headers?.() ?? {},
397
+ postData: request.postData?.() ?? undefined,
398
+ stepHash: executionState.previousStepHash ?? "unknown",
399
+ requestTimestamp: Date.now(),
400
+ };
401
+ }
315
402
  }
316
403
  // Remove the request from the live requests map
317
404
  let respData;
@@ -330,33 +417,35 @@ async function handleRequestFinishedOrFailed(request, failed) {
330
417
  }
331
418
  else {
332
419
  const response = await request.response();
333
- // console.log("Response received for request:", request.url());
334
- const headers = response?.headers() || {};
420
+ const headers = response?.headers?.() || {};
335
421
  let contentType = headers["content-type"] || null;
336
422
  let body = null;
337
423
  try {
338
- if (contentType && contentType.includes("application/json")) {
339
- const text = await response.text();
340
- body = JSON.parse(text);
341
- // console.log("Parsed JSON response body:", body);
342
- }
343
- else if (contentType && contentType.includes("text")) {
344
- body = await response.text();
345
- // console.log("Parsed text response body:", body);
346
- }
347
- else if (contentType && contentType.includes("application/csv")) {
348
- body = await response.text();
349
- contentType = "text/csv"; // Normalize content type for CSV
350
- // console.log("Parsed CSV response body:", body);
424
+ if (responseHasBody(response)) {
425
+ if (contentType && contentType.includes("application/json")) {
426
+ body = JSON.parse(await response.text());
427
+ }
428
+ else if ((contentType && contentType.includes("text")) ||
429
+ (contentType && contentType.includes("application/csv"))) {
430
+ body = await response.text();
431
+ if (contentType.includes("application/csv"))
432
+ contentType = "text/csv";
433
+ }
434
+ else {
435
+ // If you want binary, you could read it here—but only when responseHasBody(response) is true
436
+ // const buffer = await response.body();
437
+ // body = buffer.toString("base64");
438
+ }
351
439
  }
352
440
  else {
353
- // Optionally handle binary here
354
- // const buffer = await response.body();
355
- // body = buffer.toString("base64"); // if you want to store binary safely
441
+ // For redirects / no-body statuses, it's useful to keep redirect info
442
+ // e.g., include Location header if present
443
+ // body stays null
356
444
  }
357
445
  }
358
446
  catch (err) {
359
447
  console.error("Error reading response body:", err);
448
+ body = await response.text();
360
449
  }
361
450
  respData = {
362
451
  status: response.status(),
@@ -370,42 +459,36 @@ async function handleRequestFinishedOrFailed(request, failed) {
370
459
  if (executionState.liveRequestsMap.has(request)) {
371
460
  /* “normal” path – keep it in the buffer */
372
461
  entry.response = respData;
462
+ if (outOfStep && executionState.currentStepHash) {
463
+ await saveMap(true);
464
+ }
373
465
  }
374
466
  else {
375
- /* orphan response – append directly to the previous step file */
376
- entry.response = respData;
377
- await appendEntryToStepFile(entry.stepHash, entry); // ➍ NEW
467
+ if (executionState.liveRequestsMapPrevious.has(request)) {
468
+ entry.response = respData;
469
+ await saveMap(false);
470
+ }
471
+ else {
472
+ /* orphan response – append directly to the previous step file */
473
+ entry.response = respData;
474
+ await appendEntryToStepFile(entry.stepHash, entry); // ➍ NEW
475
+ }
378
476
  }
379
477
  entry.response = respData;
380
478
  return;
381
479
  }
382
- // Handle successful request with a response
383
- // const headers = response.headers();
384
- // const contentType = headers["content-type"] || "";
385
- // let body = null;
386
- // try {
387
- // if (contentType.includes("application/json")) {
388
- // const text = await response.text();
389
- // body = JSON.parse(text);
390
- // } else if (contentType.includes("text")) {
391
- // body = await response.text();
392
- // } else {
393
- // // Optionally handle binary here
394
- // // const buffer = await response.body();
395
- // // body = buffer.toString("base64"); // if you want to store binary safely
396
- // }
397
- // } catch (err) {
398
- // console.error("Error reading response body:", err);
399
- // }
400
- // requestData.response = {
401
- // status: response.status(),
402
- // headers,
403
- // url: response.url(),
404
- // timestamp: Date.now(),
405
- // body,
406
- // contentType,
407
- // };
408
- // saveNetworkDataToFile(requestData);
409
- // }
480
+ function responseHasBody(response) {
481
+ if (!response)
482
+ return false;
483
+ const s = response.status?.() ?? 0;
484
+ // RFC 7231: 1xx, 204, 205, 304 have no body. Playwright: 3xx (redirect) body is unavailable.
485
+ if ((s >= 100 && s < 200) || s === 204 || s === 205 || s === 304 || (s >= 300 && s < 400))
486
+ return false;
487
+ // HEAD responses have no body by definition
488
+ const method = response.request?.().method?.() ?? "GET";
489
+ if (method === "HEAD")
490
+ return false;
491
+ return true;
492
+ }
410
493
  export { registerNetworkEvents, registerDownloadEvent };
411
494
  //# sourceMappingURL=network.js.map