test-proxy-recorder 0.3.0 → 0.3.1

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/index.d.ts CHANGED
@@ -14,13 +14,14 @@ declare class ProxyServer {
14
14
  private currentSession;
15
15
  private recordingsDir;
16
16
  private recordingIdCounter;
17
+ private sequenceCounterByKey;
17
18
  private replaySessions;
19
+ private recordingPromises;
18
20
  constructor(targets: string[], recordingsDir: string);
19
21
  init(): Promise<void>;
20
22
  listen(port: number): http.Server;
21
23
  private setupProxyEventHandlers;
22
24
  private handleProxyError;
23
- private handleProxyResponse;
24
25
  /**
25
26
  * Get CORS headers for a given request
26
27
  * @param req The incoming HTTP request
@@ -56,6 +57,7 @@ declare class ProxyServer {
56
57
  */
57
58
  private getOrCreateReplaySession;
58
59
  private parseGetParams;
60
+ private parseControlRequest;
59
61
  private handleControlRequest;
60
62
  private clearModeTimeout;
61
63
  private switchMode;
@@ -63,17 +65,18 @@ declare class ProxyServer {
63
65
  private switchToRecordMode;
64
66
  private switchToReplayMode;
65
67
  private setupModeTimeout;
68
+ private flushPendingRecordings;
66
69
  private saveCurrentSession;
67
- private saveRequestRecordSync;
68
- private updateRequestBodySync;
69
- private recordResponse;
70
- private recordResponseData;
70
+ private getRecordingIdOrError;
71
+ private ensureSessionLoaded;
72
+ private getServedTracker;
73
+ private selectReplayRecord;
71
74
  private handleReplayRequest;
72
75
  private handleReplayError;
73
76
  private handleRequest;
74
77
  private handleCorsPreflightRequest;
75
78
  private handleProxyRequest;
76
- private bufferAndProxyRequest;
79
+ private recordAndProxyRequest;
77
80
  private handleUpgrade;
78
81
  private handleRecordWebSocket;
79
82
  private handleReplayWebSocket;
package/dist/index.mjs CHANGED
@@ -48,13 +48,23 @@ async function loadRecordingSession(filePath) {
48
48
  return JSON.parse(fileContent);
49
49
  }
50
50
  function processRecordings(recordings) {
51
- const keySequenceMap = /* @__PURE__ */ new Map();
52
- return recordings.map((recording) => {
51
+ const recordingsByKey = /* @__PURE__ */ new Map();
52
+ for (const recording of recordings) {
53
53
  const key = recording.key;
54
- const currentSeq = keySequenceMap.get(key) || 0;
55
- keySequenceMap.set(key, currentSeq + 1);
56
- return { ...recording, sequence: currentSeq };
57
- });
54
+ if (!recordingsByKey.has(key)) {
55
+ recordingsByKey.set(key, []);
56
+ }
57
+ recordingsByKey.get(key).push(recording);
58
+ }
59
+ const processedRecordings = [];
60
+ for (const [_key, keyRecordings] of recordingsByKey) {
61
+ keyRecordings.sort((a, b) => a.recordingId - b.recordingId);
62
+ keyRecordings.forEach((recording, index) => {
63
+ processedRecordings.push({ ...recording, sequence: index });
64
+ });
65
+ }
66
+ processedRecordings.sort((a, b) => a.recordingId - b.recordingId);
67
+ return processedRecordings;
58
68
  }
59
69
  async function saveRecordingSession(recordingsDir, session) {
60
70
  const filePath = getRecordingPath(recordingsDir, session.id);
@@ -117,19 +127,25 @@ var ProxyServer = class {
117
127
  recordingsDir;
118
128
  recordingIdCounter;
119
129
  // Unique ID for each recording entry
130
+ sequenceCounterByKey;
131
+ // Sequence counter per key (endpoint)
120
132
  replaySessions;
121
133
  // Track multiple concurrent replay sessions by recording ID
134
+ recordingPromises;
135
+ // Stack of promises that resolve to completed recordings
122
136
  constructor(targets, recordingsDir) {
123
137
  this.targets = targets;
124
138
  this.currentTargetIndex = 0;
125
139
  this.mode = Modes.transparent;
126
140
  this.recordingId = null;
127
141
  this.recordingIdCounter = 0;
142
+ this.sequenceCounterByKey = /* @__PURE__ */ new Map();
128
143
  this.replayId = null;
129
144
  this.modeTimeout = null;
130
145
  this.currentSession = null;
131
146
  this.recordingsDir = recordingsDir;
132
147
  this.replaySessions = /* @__PURE__ */ new Map();
148
+ this.recordingPromises = [];
133
149
  this.proxy = httpProxy.createProxyServer({
134
150
  secure: false,
135
151
  changeOrigin: true,
@@ -155,7 +171,7 @@ var ProxyServer = class {
155
171
  }
156
172
  setupProxyEventHandlers() {
157
173
  this.proxy.on("error", this.handleProxyError.bind(this));
158
- this.proxy.on("proxyRes", this.handleProxyResponse.bind(this));
174
+ this.proxy.on("proxyRes", this.addCorsHeaders.bind(this));
159
175
  }
160
176
  handleProxyError(err, req, res) {
161
177
  console.error("Proxy error:", err);
@@ -171,12 +187,6 @@ var ProxyServer = class {
171
187
  }
172
188
  res.end(JSON.stringify({ error: "Proxy error", message: err.message }));
173
189
  }
174
- handleProxyResponse(proxyRes, req) {
175
- this.addCorsHeaders(proxyRes, req);
176
- if (this.mode === Modes.record && this.recordingId) {
177
- this.recordResponse(req, proxyRes);
178
- }
179
- }
180
190
  /**
181
191
  * Get CORS headers for a given request
182
192
  * @param req The incoming HTTP request
@@ -270,18 +280,20 @@ var ProxyServer = class {
270
280
  }
271
281
  return { mode, id, timeout };
272
282
  }
283
+ async parseControlRequest(req) {
284
+ if (req.method === "GET") {
285
+ return this.parseGetParams(req);
286
+ }
287
+ if (req.method === "POST") {
288
+ const body = await readRequestBody(req);
289
+ console.log(`MODE CHANGE (${req.method})`, body);
290
+ return JSON.parse(body);
291
+ }
292
+ throw new Error("Unsupported control method");
293
+ }
273
294
  async handleControlRequest(req, res) {
274
295
  try {
275
- let data;
276
- if (req.method === "GET") {
277
- data = this.parseGetParams(req);
278
- } else if (req.method === "POST") {
279
- const body = await readRequestBody(req);
280
- console.log(`MODE CHANGE (${req.method})`, body);
281
- data = JSON.parse(body);
282
- } else {
283
- return;
284
- }
296
+ const data = await this.parseControlRequest(req);
285
297
  const { mode, id, timeout: requestTimeout } = data;
286
298
  const timeout = requestTimeout ?? DEFAULT_TIMEOUT_MS;
287
299
  this.clearModeTimeout();
@@ -314,7 +326,7 @@ var ProxyServer = class {
314
326
  async switchMode(mode, id) {
315
327
  console.log(`Switching to ${mode.toUpperCase()} mode`);
316
328
  if (this.currentSession && this.mode === Modes.record) {
317
- await this.saveCurrentSession(true);
329
+ await this.saveCurrentSession();
318
330
  console.log("Session saved, continuing with mode switch");
319
331
  }
320
332
  switch (mode) {
@@ -354,6 +366,8 @@ var ProxyServer = class {
354
366
  this.recordingId = id;
355
367
  this.replayId = null;
356
368
  this.currentSession = { id, recordings: [], websocketRecordings: [] };
369
+ this.recordingIdCounter = 0;
370
+ this.sequenceCounterByKey.clear();
357
371
  console.log(`Switched to record mode with ID: ${id}`);
358
372
  }
359
373
  async switchToReplayMode(id) {
@@ -373,146 +387,39 @@ var ProxyServer = class {
373
387
  setupModeTimeout(timeout) {
374
388
  this.modeTimeout = setTimeout(async () => {
375
389
  console.log("Timeout reached, switching back to transparent mode");
376
- await this.saveCurrentSession(true);
390
+ await this.saveCurrentSession();
377
391
  this.switchToTransparentMode();
378
392
  this.modeTimeout = null;
379
393
  }, timeout);
380
394
  }
381
- async saveCurrentSession(filterIncomplete = false) {
382
- if (!this.currentSession) {
395
+ async flushPendingRecordings() {
396
+ if (this.recordingPromises.length === 0) {
383
397
  return;
384
398
  }
385
- if (filterIncomplete) {
386
- const incompleteCount = this.currentSession.recordings.filter(
387
- (r) => !r.response
388
- ).length;
389
- if (incompleteCount > 0) {
390
- this.currentSession.recordings = this.currentSession.recordings.filter(
391
- (r) => r.response
392
- );
399
+ const results = await Promise.allSettled(this.recordingPromises);
400
+ if (this.currentSession) {
401
+ for (const result of results) {
402
+ if (result.status === "fulfilled" && result.value) {
403
+ this.currentSession.recordings.push(result.value);
404
+ }
393
405
  }
394
- }
395
- console.log(
396
- `Saving session with ${this.currentSession.recordings.length} HTTP and ${this.currentSession.websocketRecordings.length} WebSocket recordings`
397
- );
398
- await saveRecordingSession(this.recordingsDir, this.currentSession);
399
- }
400
- saveRequestRecordSync(req, body) {
401
- if (!this.currentSession) {
402
- return;
403
- }
404
- const key = getReqID(req);
405
- const recordingId = this.recordingIdCounter++;
406
- req.__recordingId = recordingId;
407
- const record = {
408
- request: {
409
- method: req.method,
410
- url: req.url,
411
- headers: req.headers,
412
- body: body || null
413
- },
414
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
415
- key,
416
- recordingId
417
- };
418
- this.currentSession.recordings.push(record);
419
- console.log(
420
- // eslint-disable-next-line sonarjs/no-nested-template-literals
421
- `saveRequestRecordSync: Saved ${req.method} ${req.url} (key: ${key}, recordingId: ${recordingId}, body: ${body ? `${body.length} chars` : "null"}, total: ${this.currentSession.recordings.length}, sessionId: ${this.currentSession.id})`
422
- );
423
- }
424
- updateRequestBodySync(req, body) {
425
- if (!this.currentSession) {
426
- return;
427
- }
428
- const recordingId = req.__recordingId;
429
- if (recordingId === void 0) {
430
- console.error(
431
- `updateRequestBodySync: No recording ID found on request ${req.method} ${req.url}`
432
- );
433
- return;
434
- }
435
- const record = this.currentSession.recordings.find(
436
- (r) => r.recordingId === recordingId
437
- );
438
- if (!record) {
439
- console.error(
440
- `updateRequestBodySync: Could not find recording with ID ${recordingId} for ${req.method} ${req.url}`
406
+ console.log(
407
+ `Flushed ${results.length} recordings to session (total: ${this.currentSession.recordings.length})`
441
408
  );
442
- return;
443
409
  }
444
- record.request.body = body || null;
445
- console.log(
446
- `updateRequestBodySync: Updated body for ${req.method} ${req.url} (${body.length} chars, recordingId: ${recordingId})`
447
- );
410
+ this.recordingPromises = [];
448
411
  }
449
- async recordResponse(req, proxyRes) {
412
+ async saveCurrentSession() {
450
413
  if (!this.currentSession) {
451
414
  return;
452
415
  }
453
- const recordingId = req.__recordingId;
454
- if (recordingId === void 0) {
455
- console.error(
456
- `recordResponse: No recording ID found on request ${req.method} ${req.url}`
457
- );
458
- return;
459
- }
460
- const record = this.currentSession.recordings.find(
461
- (r) => r.recordingId === recordingId
462
- );
463
- if (!record) {
464
- console.error(
465
- `recordResponse: Could not find recording with ID ${recordingId} for ${req.method} ${req.url}`
466
- );
467
- return;
468
- }
469
- const chunks = [];
470
- proxyRes.on("data", (chunk) => {
471
- chunks.push(chunk);
472
- });
473
- proxyRes.on("end", async () => {
474
- const body = Buffer.concat(chunks).toString("utf8");
475
- record.response = {
476
- statusCode: proxyRes.statusCode,
477
- headers: proxyRes.headers,
478
- body: body || null
479
- };
480
- console.log(
481
- `Recorded: ${req.method} ${req.url} (recordingId: ${recordingId})`
482
- );
483
- });
484
- }
485
- async recordResponseData(req, proxyRes, body) {
486
- if (!this.currentSession) {
487
- return false;
488
- }
489
- const recordingId = req.__recordingId;
490
- if (recordingId === void 0) {
491
- console.error(
492
- `recordResponseData: No recording ID found on request ${req.method} ${req.url}`
493
- );
494
- return false;
495
- }
496
- const record = this.currentSession.recordings.find(
497
- (r) => r.recordingId === recordingId
498
- );
499
- if (!record) {
500
- console.error(
501
- `recordResponseData: Could not find recording with ID ${recordingId} for ${req.method} ${req.url}`
502
- );
503
- return false;
504
- }
505
- record.response = {
506
- statusCode: proxyRes.statusCode,
507
- headers: proxyRes.headers,
508
- body: body || null
509
- };
416
+ await this.flushPendingRecordings();
510
417
  console.log(
511
- `recordResponseData: Recorded response for ${req.method} ${req.url} (recordingId: ${recordingId})`
418
+ `Saving session with ${this.currentSession.recordings.length} HTTP and ${this.currentSession.websocketRecordings.length} WebSocket recordings`
512
419
  );
513
- return true;
420
+ await saveRecordingSession(this.recordingsDir, this.currentSession);
514
421
  }
515
- async handleReplayRequest(req, res) {
422
+ getRecordingIdOrError(req, res) {
516
423
  const recordingId = this.getRecordingIdFromRequest(req) || this.replayId;
517
424
  if (!recordingId) {
518
425
  const corsHeaders = this.getCorsHeaders(req);
@@ -521,21 +428,50 @@ var ProxyServer = class {
521
428
  ...corsHeaders
522
429
  });
523
430
  res.end(JSON.stringify({ error: "No replay session active" }));
524
- return;
431
+ return null;
525
432
  }
433
+ return recordingId;
434
+ }
435
+ async ensureSessionLoaded(recordingId, filePath) {
436
+ const sessionState = this.getOrCreateReplaySession(recordingId);
437
+ if (!sessionState.loadedSession) {
438
+ sessionState.loadedSession = await loadRecordingSession(filePath);
439
+ console.log(`[REPLAY] Loaded recording session: ${recordingId}`);
440
+ }
441
+ return sessionState;
442
+ }
443
+ getServedTracker(sessionState, key) {
444
+ if (!sessionState.servedRecordingIdsByKey.has(key)) {
445
+ sessionState.servedRecordingIdsByKey.set(key, /* @__PURE__ */ new Set());
446
+ }
447
+ return sessionState.servedRecordingIdsByKey.get(key);
448
+ }
449
+ selectReplayRecord(recordsWithKey, servedForThisKey, key, recordingId) {
450
+ for (const rec of recordsWithKey) {
451
+ if (!servedForThisKey.has(rec.recordingId)) {
452
+ return rec;
453
+ }
454
+ }
455
+ if (recordsWithKey.length > 0) {
456
+ console.log(
457
+ `[REPLAY WARNING] All ${recordsWithKey.length} recordings already served for ${key} (session: ${recordingId}), reusing last one`
458
+ );
459
+ return recordsWithKey[recordsWithKey.length - 1];
460
+ }
461
+ return null;
462
+ }
463
+ async handleReplayRequest(req, res) {
464
+ const recordingId = this.getRecordingIdOrError(req, res);
465
+ if (!recordingId) return;
526
466
  const key = getReqID(req);
527
467
  const filePath = getRecordingPath(this.recordingsDir, recordingId);
528
468
  try {
529
- const sessionState = this.getOrCreateReplaySession(recordingId);
530
- if (!sessionState.loadedSession) {
531
- sessionState.loadedSession = await loadRecordingSession(filePath);
532
- console.log(`[REPLAY] Loaded recording session: ${recordingId}`);
533
- }
469
+ const sessionState = await this.ensureSessionLoaded(
470
+ recordingId,
471
+ filePath
472
+ );
534
473
  const session = sessionState.loadedSession;
535
- if (!sessionState.servedRecordingIdsByKey.has(key)) {
536
- sessionState.servedRecordingIdsByKey.set(key, /* @__PURE__ */ new Set());
537
- }
538
- const servedForThisKey = sessionState.servedRecordingIdsByKey.get(key);
474
+ const servedForThisKey = this.getServedTracker(sessionState, key);
539
475
  const host = req.headers.host || "unknown";
540
476
  const recordsWithKey = session.recordings.filter((r) => r.key === key && r.response).toSorted((a, b) => {
541
477
  const aSeq = a.sequence !== void 0 ? a.sequence : a.recordingId;
@@ -564,30 +500,23 @@ var ProxyServer = class {
564
500
  }
565
501
  const requestCount = servedForThisKey.size + 1;
566
502
  console.log(
567
- `[replay request #${requestCount}] ${req.method} ${req.url} (session: ${recordingId}, total: ${recordsWithKey.length}, served: ${servedForThisKey.size})`
503
+ `[replay request #${requestCount}] ${req.method} ${req.url} (key: ${key}, session: ${recordingId}, total: ${recordsWithKey.length}, served: ${servedForThisKey.size})`
568
504
  );
569
- let record;
570
- for (const rec of recordsWithKey) {
571
- if (!servedForThisKey.has(rec.recordingId)) {
572
- record = rec;
573
- break;
574
- }
575
- }
576
- if (!record) {
577
- console.log(
578
- `[REPLAY WARNING] All ${recordsWithKey.length} recordings already served for ${key} (session: ${recordingId}), reusing last one`
505
+ const record = this.selectReplayRecord(
506
+ recordsWithKey,
507
+ servedForThisKey,
508
+ key,
509
+ recordingId
510
+ );
511
+ if (!record || !record.response) {
512
+ throw new Error(
513
+ `No response recorded for this request: ${req.method} ${host}${req.url}`
579
514
  );
580
- record = recordsWithKey[recordsWithKey.length - 1];
581
515
  }
582
516
  servedForThisKey.add(record.recordingId);
583
517
  console.log(
584
518
  `[replay serving] recordingId: ${record.recordingId}, session: ${recordingId}, body_len: ${record.response?.body?.length || 0}`
585
519
  );
586
- if (!record.response) {
587
- throw new Error(
588
- `No response recorded for this request: ${req.method} ${host}${req.url}`
589
- );
590
- }
591
520
  const { statusCode, headers, body } = record.response;
592
521
  const responseHeaders = {
593
522
  ...headers,
@@ -642,82 +571,124 @@ var ProxyServer = class {
642
571
  const target = this.getTarget();
643
572
  console.log(`[${this.mode}] ${req.method} ${req.url} -> ${target}`);
644
573
  if (this.mode === Modes.record) {
645
- this.saveRequestRecordSync(req, null);
646
- await this.bufferAndProxyRequest(req, res, target);
574
+ await this.recordAndProxyRequest(req, res, target);
647
575
  } else {
648
576
  this.proxy.web(req, res, { target });
649
577
  }
650
578
  }
651
- // TODO: check if can handle streaming requests
652
- async bufferAndProxyRequest(req, res, target) {
653
- const chunks = [];
654
- req.on("data", (chunk) => {
655
- chunks.push(chunk);
656
- });
657
- try {
658
- await new Promise((resolve, reject) => {
659
- req.on("end", () => resolve());
660
- req.on("error", (err) => reject(err));
661
- setTimeout(
662
- () => reject(new Error("Request buffering timeout")),
663
- 3e4
664
- );
665
- });
666
- } catch (error) {
667
- console.error("Error buffering request:", error);
579
+ // Note: streaming requests are buffered before proxying; streaming passthrough is not yet implemented
580
+ async recordAndProxyRequest(req, res, target) {
581
+ if (!this.currentSession) {
582
+ return;
668
583
  }
669
- const body = Buffer.concat(chunks).toString("utf8");
670
- this.updateRequestBodySync(req, body);
671
- const targetUrl = new URL(target);
672
- const isHttps = targetUrl.protocol === "https:";
673
- const requestModule = isHttps ? https : http;
674
- const defaultPort = isHttps ? 443 : 80;
675
- const proxyReq = requestModule.request(
676
- {
677
- hostname: targetUrl.hostname,
678
- port: targetUrl.port || defaultPort,
679
- path: req.url,
680
- method: req.method,
681
- headers: req.headers
682
- },
683
- (proxyRes) => {
684
- this.addCorsHeaders(proxyRes, req);
685
- const responseChunks = [];
686
- proxyRes.on("data", (chunk) => {
687
- responseChunks.push(chunk);
688
- });
689
- proxyRes.on("end", async () => {
690
- const responseBody = Buffer.concat(responseChunks);
691
- const recorded = await this.recordResponseData(
692
- req,
693
- proxyRes,
694
- responseBody.toString("utf8")
695
- );
696
- const responseHeaders = {
697
- ...proxyRes.headers,
698
- ...this.getCorsHeaders(req)
699
- };
700
- res.writeHead(proxyRes.statusCode || 200, responseHeaders);
701
- res.end(responseBody);
702
- if (recorded) {
703
- console.log(`Recorded: ${req.method} ${req.url}`);
584
+ const key = getReqID(req);
585
+ const recordingId = this.recordingIdCounter++;
586
+ const sequence = this.sequenceCounterByKey.get(key) || 0;
587
+ this.sequenceCounterByKey.set(key, sequence + 1);
588
+ const recordingPromise = new Promise((resolve) => {
589
+ (async () => {
590
+ try {
591
+ const chunks = [];
592
+ req.on("data", (chunk) => {
593
+ chunks.push(chunk);
594
+ });
595
+ try {
596
+ await new Promise((resolveBuffer, rejectBuffer) => {
597
+ req.on("end", () => resolveBuffer());
598
+ req.on("error", (err) => rejectBuffer(err));
599
+ setTimeout(
600
+ () => rejectBuffer(new Error("Request buffering timeout")),
601
+ 3e4
602
+ );
603
+ });
604
+ } catch (error) {
605
+ console.error("Error buffering request:", error);
704
606
  }
705
- });
706
- proxyRes.on("error", (err) => {
707
- console.error("Proxy response error:", err);
708
- if (!res.headersSent) {
607
+ const requestBody = Buffer.concat(chunks).toString("utf8");
608
+ const targetUrl = new URL(target);
609
+ const isHttps = targetUrl.protocol === "https:";
610
+ const requestModule = isHttps ? https : http;
611
+ const defaultPort = isHttps ? 443 : 80;
612
+ const proxyReq = requestModule.request(
613
+ {
614
+ hostname: targetUrl.hostname,
615
+ port: targetUrl.port || defaultPort,
616
+ path: req.url,
617
+ method: req.method,
618
+ headers: req.headers
619
+ },
620
+ (proxyRes) => {
621
+ this.addCorsHeaders(proxyRes, req);
622
+ const responseChunks = [];
623
+ proxyRes.on("data", (chunk) => {
624
+ responseChunks.push(chunk);
625
+ });
626
+ proxyRes.on("end", async () => {
627
+ try {
628
+ const responseBody = Buffer.concat(responseChunks);
629
+ const responseBodyStr = responseBody.toString("utf8");
630
+ const recording = {
631
+ request: {
632
+ method: req.method,
633
+ url: req.url,
634
+ headers: req.headers,
635
+ body: requestBody || null
636
+ },
637
+ response: {
638
+ statusCode: proxyRes.statusCode,
639
+ headers: proxyRes.headers,
640
+ body: responseBodyStr || null
641
+ },
642
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
643
+ key,
644
+ recordingId,
645
+ sequence
646
+ };
647
+ const responseHeaders = {
648
+ ...proxyRes.headers,
649
+ ...this.getCorsHeaders(req)
650
+ };
651
+ res.writeHead(proxyRes.statusCode || 200, responseHeaders);
652
+ res.end(responseBody);
653
+ console.log(
654
+ `Recorded: ${req.method} ${req.url} (recordingId: ${recordingId}, sequence: ${sequence})`
655
+ );
656
+ resolve(recording);
657
+ } catch (error) {
658
+ console.error("Error completing recording:", error);
659
+ resolve(null);
660
+ }
661
+ });
662
+ proxyRes.on("error", (err) => {
663
+ console.error("Proxy response error:", err);
664
+ if (!res.headersSent) {
665
+ this.handleProxyError(err, req, res);
666
+ }
667
+ resolve(null);
668
+ });
669
+ }
670
+ );
671
+ proxyReq.on("error", (err) => {
709
672
  this.handleProxyError(err, req, res);
673
+ resolve(null);
674
+ });
675
+ if (chunks.length > 0) {
676
+ proxyReq.write(Buffer.concat(chunks));
710
677
  }
711
- });
712
- }
713
- );
714
- proxyReq.on("error", (err) => {
715
- this.handleProxyError(err, req, res);
678
+ proxyReq.end();
679
+ } catch (error) {
680
+ console.error("Error in recordAndProxyRequest:", error);
681
+ try {
682
+ this.handleProxyError(error, req, res);
683
+ } catch (error_) {
684
+ console.error("Failed to handle proxy error:", error_);
685
+ }
686
+ resolve(null);
687
+ }
688
+ })();
716
689
  });
717
- if (chunks.length > 0) {
718
- proxyReq.write(Buffer.concat(chunks));
719
- }
720
- proxyReq.end();
690
+ this.recordingPromises.push(recordingPromise);
691
+ await recordingPromise;
721
692
  }
722
693
  handleUpgrade(req, socket, head) {
723
694
  if (this.mode === Modes.replay) {
@@ -979,7 +950,7 @@ var playwrightProxy = {
979
950
  try {
980
951
  await setProxyMode(Modes.replay, sessionId);
981
952
  console.log(
982
- `[Cleanup] Switched to transparent mode for session: ${sessionId}`
953
+ `[Cleanup] Switched to replay mode for session: ${sessionId}`
983
954
  );
984
955
  } catch (error) {
985
956
  console.error("[Cleanup] Error during page close cleanup:", error);
@@ -106,7 +106,7 @@ var playwrightProxy = {
106
106
  try {
107
107
  await setProxyMode(Modes.replay, sessionId);
108
108
  console.log(
109
- `[Cleanup] Switched to transparent mode for session: ${sessionId}`
109
+ `[Cleanup] Switched to replay mode for session: ${sessionId}`
110
110
  );
111
111
  } catch (error) {
112
112
  console.error("[Cleanup] Error during page close cleanup:", error);
@@ -104,7 +104,7 @@ var playwrightProxy = {
104
104
  try {
105
105
  await setProxyMode(Modes.replay, sessionId);
106
106
  console.log(
107
- `[Cleanup] Switched to transparent mode for session: ${sessionId}`
107
+ `[Cleanup] Switched to replay mode for session: ${sessionId}`
108
108
  );
109
109
  } catch (error) {
110
110
  console.error("[Cleanup] Error during page close cleanup:", error);