test-proxy-recorder 0.3.3 → 0.3.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.
- package/README.md +180 -524
- package/dist/index.cjs +98 -84
- package/dist/index.d.cts +9 -6
- package/dist/index.d.ts +9 -6
- package/dist/index.mjs +98 -84
- package/dist/playwright/index.cjs +7 -8
- package/dist/playwright/index.mjs +7 -8
- package/dist/proxy.js +131 -105
- package/package.json +8 -2
package/dist/index.mjs
CHANGED
|
@@ -59,9 +59,9 @@ function processRecordings(recordings) {
|
|
|
59
59
|
const processedRecordings = [];
|
|
60
60
|
for (const [_key, keyRecordings] of recordingsByKey) {
|
|
61
61
|
keyRecordings.sort((a, b) => a.recordingId - b.recordingId);
|
|
62
|
-
keyRecordings.
|
|
62
|
+
for (const [index, recording] of keyRecordings.entries()) {
|
|
63
63
|
processedRecordings.push({ ...recording, sequence: index });
|
|
64
|
-
}
|
|
64
|
+
}
|
|
65
65
|
}
|
|
66
66
|
processedRecordings.sort((a, b) => a.recordingId - b.recordingId);
|
|
67
67
|
return processedRecordings;
|
|
@@ -119,8 +119,7 @@ function sendJsonResponse(res, statusCode, data) {
|
|
|
119
119
|
|
|
120
120
|
// src/ProxyServer.ts
|
|
121
121
|
var ProxyServer = class {
|
|
122
|
-
|
|
123
|
-
currentTargetIndex;
|
|
122
|
+
target;
|
|
124
123
|
mode;
|
|
125
124
|
recordingId;
|
|
126
125
|
replayId;
|
|
@@ -128,19 +127,22 @@ var ProxyServer = class {
|
|
|
128
127
|
proxy;
|
|
129
128
|
currentSession;
|
|
130
129
|
recordingsDir;
|
|
130
|
+
timeoutMs;
|
|
131
131
|
recordingIdCounter;
|
|
132
132
|
// Unique ID for each recording entry
|
|
133
133
|
sequenceCounterByKey;
|
|
134
134
|
// Sequence counter per key (endpoint)
|
|
135
135
|
replaySessions;
|
|
136
136
|
// Track multiple concurrent replay sessions by recording ID
|
|
137
|
+
sessionEvictionTimer;
|
|
138
|
+
// Periodic timer to evict idle replay sessions
|
|
137
139
|
recordingPromises;
|
|
138
140
|
// Stack of promises that resolve to completed recordings
|
|
139
141
|
flushPromise;
|
|
140
142
|
// Promise for in-progress flush operation
|
|
141
|
-
constructor(
|
|
142
|
-
this.
|
|
143
|
-
this.
|
|
143
|
+
constructor(target, recordingsDir, timeoutMs) {
|
|
144
|
+
this.target = target;
|
|
145
|
+
this.timeoutMs = timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
144
146
|
this.mode = Modes.transparent;
|
|
145
147
|
this.recordingId = null;
|
|
146
148
|
this.recordingIdCounter = 0;
|
|
@@ -150,6 +152,7 @@ var ProxyServer = class {
|
|
|
150
152
|
this.currentSession = null;
|
|
151
153
|
this.recordingsDir = recordingsDir;
|
|
152
154
|
this.replaySessions = /* @__PURE__ */ new Map();
|
|
155
|
+
this.sessionEvictionTimer = null;
|
|
153
156
|
this.recordingPromises = [];
|
|
154
157
|
this.flushPromise = null;
|
|
155
158
|
this.proxy = httpProxy.createProxyServer({
|
|
@@ -212,11 +215,6 @@ var ProxyServer = class {
|
|
|
212
215
|
const corsHeaders = this.getCorsHeaders(req);
|
|
213
216
|
Object.assign(proxyRes.headers, corsHeaders);
|
|
214
217
|
}
|
|
215
|
-
getTarget() {
|
|
216
|
-
const target = this.targets[this.currentTargetIndex];
|
|
217
|
-
this.currentTargetIndex = (this.currentTargetIndex + 1) % this.targets.length;
|
|
218
|
-
return target;
|
|
219
|
-
}
|
|
220
218
|
/**
|
|
221
219
|
* Extract recording ID from custom HTTP header
|
|
222
220
|
* Used for concurrent replay session routing, especially with Next.js
|
|
@@ -252,13 +250,7 @@ var ProxyServer = class {
|
|
|
252
250
|
getRecordingIdFromRequest(req) {
|
|
253
251
|
const fromHeader = this.getRecordingIdFromHeader(req);
|
|
254
252
|
const fromCookie = this.getRecordingIdFromCookie(req);
|
|
255
|
-
|
|
256
|
-
return fromHeader;
|
|
257
|
-
}
|
|
258
|
-
if (fromCookie) {
|
|
259
|
-
return fromCookie;
|
|
260
|
-
}
|
|
261
|
-
return null;
|
|
253
|
+
return fromHeader ?? fromCookie ?? null;
|
|
262
254
|
}
|
|
263
255
|
/**
|
|
264
256
|
* Get or create a replay session state for a given recording ID
|
|
@@ -278,6 +270,7 @@ var ProxyServer = class {
|
|
|
278
270
|
sortedRecordingsByKey: /* @__PURE__ */ new Map()
|
|
279
271
|
};
|
|
280
272
|
this.replaySessions.set(recordingId, session);
|
|
273
|
+
this.startSessionEvictionTimer();
|
|
281
274
|
console.log(
|
|
282
275
|
`[CONCURRENT REPLAY] Created new session for recording: ${recordingId}`
|
|
283
276
|
);
|
|
@@ -289,8 +282,9 @@ var ProxyServer = class {
|
|
|
289
282
|
* @param sessionId The session ID to clean up
|
|
290
283
|
*/
|
|
291
284
|
async cleanupSession(sessionId) {
|
|
292
|
-
|
|
293
|
-
|
|
285
|
+
this.replaySessions.delete(sessionId);
|
|
286
|
+
if (this.replaySessions.size === 0) {
|
|
287
|
+
this.stopSessionEvictionTimer();
|
|
294
288
|
}
|
|
295
289
|
if (this.recordingId === sessionId) {
|
|
296
290
|
await this.saveCurrentSession();
|
|
@@ -302,29 +296,44 @@ var ProxyServer = class {
|
|
|
302
296
|
}
|
|
303
297
|
console.log(`[CLEANUP] Session ${sessionId} cleaned up successfully`);
|
|
304
298
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
const id = url.searchParams.get("id") || void 0;
|
|
309
|
-
const timeoutParam = url.searchParams.get("timeout");
|
|
310
|
-
const timeout = timeoutParam ? Number.parseInt(timeoutParam, 10) : void 0;
|
|
311
|
-
if (!mode) {
|
|
312
|
-
throw new Error("Mode parameter is required");
|
|
299
|
+
startSessionEvictionTimer() {
|
|
300
|
+
if (this.sessionEvictionTimer) {
|
|
301
|
+
return;
|
|
313
302
|
}
|
|
314
|
-
|
|
303
|
+
const CHECK_INTERVAL_MS = 3e4;
|
|
304
|
+
this.sessionEvictionTimer = setInterval(() => {
|
|
305
|
+
const now = Date.now();
|
|
306
|
+
for (const [id, session] of this.replaySessions) {
|
|
307
|
+
if (now - session.lastAccessTime >= this.timeoutMs) {
|
|
308
|
+
console.log(
|
|
309
|
+
`[EVICTION] Evicting idle replay session: ${id} (idle for ${Math.round((now - session.lastAccessTime) / 1e3)}s)`
|
|
310
|
+
);
|
|
311
|
+
this.replaySessions.delete(id);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (this.replaySessions.size === 0) {
|
|
315
|
+
this.stopSessionEvictionTimer();
|
|
316
|
+
}
|
|
317
|
+
}, CHECK_INTERVAL_MS);
|
|
318
|
+
this.sessionEvictionTimer.unref();
|
|
315
319
|
}
|
|
316
|
-
|
|
317
|
-
if (
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
if (req.method === "POST") {
|
|
321
|
-
const body = await readRequestBody(req);
|
|
322
|
-
console.log(`MODE CHANGE (${req.method})`, body);
|
|
323
|
-
return JSON.parse(body);
|
|
320
|
+
stopSessionEvictionTimer() {
|
|
321
|
+
if (this.sessionEvictionTimer) {
|
|
322
|
+
clearInterval(this.sessionEvictionTimer);
|
|
323
|
+
this.sessionEvictionTimer = null;
|
|
324
324
|
}
|
|
325
|
-
|
|
325
|
+
}
|
|
326
|
+
async parseControlBody(req) {
|
|
327
|
+
const body = await readRequestBody(req);
|
|
328
|
+
console.log(`MODE CHANGE (${req.method})`, body);
|
|
329
|
+
return JSON.parse(body);
|
|
326
330
|
}
|
|
327
331
|
async handleControlRequest(req, res) {
|
|
332
|
+
if (req.method === "HEAD") {
|
|
333
|
+
res.writeHead(HTTP_STATUS_OK);
|
|
334
|
+
res.end();
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
328
337
|
if (req.method === "GET") {
|
|
329
338
|
sendJsonResponse(res, HTTP_STATUS_OK, {
|
|
330
339
|
recordingsDir: this.recordingsDir,
|
|
@@ -333,8 +342,11 @@ var ProxyServer = class {
|
|
|
333
342
|
});
|
|
334
343
|
return;
|
|
335
344
|
}
|
|
345
|
+
await this.handleControlPost(req, res);
|
|
346
|
+
}
|
|
347
|
+
async handleControlPost(req, res) {
|
|
336
348
|
try {
|
|
337
|
-
const data = await this.
|
|
349
|
+
const data = await this.parseControlBody(req);
|
|
338
350
|
const { mode, id, timeout: requestTimeout, cleanup } = data;
|
|
339
351
|
if (cleanup && id) {
|
|
340
352
|
await this.cleanupSession(id);
|
|
@@ -345,29 +357,7 @@ var ProxyServer = class {
|
|
|
345
357
|
});
|
|
346
358
|
return;
|
|
347
359
|
}
|
|
348
|
-
|
|
349
|
-
throw new Error(
|
|
350
|
-
"Mode parameter is required when cleanup is not specified"
|
|
351
|
-
);
|
|
352
|
-
}
|
|
353
|
-
const timeout = requestTimeout ?? DEFAULT_TIMEOUT_MS;
|
|
354
|
-
this.clearModeTimeout();
|
|
355
|
-
await this.switchMode(mode, id);
|
|
356
|
-
this.setupModeTimeout(timeout);
|
|
357
|
-
if (mode === Modes.replay && id) {
|
|
358
|
-
res.setHeader(
|
|
359
|
-
"Set-Cookie",
|
|
360
|
-
`proxy-recording-id=${encodeURIComponent(id)}; HttpOnly; Path=/; SameSite=Lax`
|
|
361
|
-
);
|
|
362
|
-
console.log(`[CONCURRENT REPLAY] Set cookie for recording: ${id}`);
|
|
363
|
-
}
|
|
364
|
-
sendJsonResponse(res, HTTP_STATUS_OK, {
|
|
365
|
-
success: true,
|
|
366
|
-
mode: this.mode,
|
|
367
|
-
id: this.recordingId || this.replayId,
|
|
368
|
-
timeout,
|
|
369
|
-
recordingsDir: this.recordingsDir
|
|
370
|
-
});
|
|
360
|
+
await this.applyModeChange(res, mode, id, requestTimeout);
|
|
371
361
|
} catch (error) {
|
|
372
362
|
console.error("Control request error:", error);
|
|
373
363
|
sendJsonResponse(res, HTTP_STATUS_BAD_REQUEST, {
|
|
@@ -375,6 +365,31 @@ var ProxyServer = class {
|
|
|
375
365
|
});
|
|
376
366
|
}
|
|
377
367
|
}
|
|
368
|
+
async applyModeChange(res, mode, id, requestTimeout) {
|
|
369
|
+
if (!mode) {
|
|
370
|
+
throw new Error(
|
|
371
|
+
"Mode parameter is required when cleanup is not specified"
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
const timeout = requestTimeout ?? this.timeoutMs;
|
|
375
|
+
this.clearModeTimeout();
|
|
376
|
+
await this.switchMode(mode, id);
|
|
377
|
+
this.setupModeTimeout(timeout);
|
|
378
|
+
if (mode === Modes.replay && id) {
|
|
379
|
+
res.setHeader(
|
|
380
|
+
"Set-Cookie",
|
|
381
|
+
`proxy-recording-id=${encodeURIComponent(id)}; HttpOnly; Path=/; SameSite=Lax`
|
|
382
|
+
);
|
|
383
|
+
console.log(`[CONCURRENT REPLAY] Set cookie for recording: ${id}`);
|
|
384
|
+
}
|
|
385
|
+
sendJsonResponse(res, HTTP_STATUS_OK, {
|
|
386
|
+
success: true,
|
|
387
|
+
mode: this.mode,
|
|
388
|
+
id: this.recordingId || this.replayId,
|
|
389
|
+
timeout,
|
|
390
|
+
recordingsDir: this.recordingsDir
|
|
391
|
+
});
|
|
392
|
+
}
|
|
378
393
|
clearModeTimeout() {
|
|
379
394
|
clearTimeout(this.modeTimeout || 0);
|
|
380
395
|
this.modeTimeout = null;
|
|
@@ -414,7 +429,7 @@ var ProxyServer = class {
|
|
|
414
429
|
this.recordingId = null;
|
|
415
430
|
this.replayId = null;
|
|
416
431
|
this.currentSession = null;
|
|
417
|
-
|
|
432
|
+
this.clearModeTimeout();
|
|
418
433
|
console.log("Switched to transparent mode");
|
|
419
434
|
}
|
|
420
435
|
switchToRecordMode(id) {
|
|
@@ -445,7 +460,7 @@ var ProxyServer = class {
|
|
|
445
460
|
console.log(`Switched to replay mode with ID: ${id}`);
|
|
446
461
|
}
|
|
447
462
|
setupModeTimeout(timeout) {
|
|
448
|
-
|
|
463
|
+
this.clearModeTimeout();
|
|
449
464
|
this.modeTimeout = setTimeout(async () => {
|
|
450
465
|
console.log("Timeout reached, switching back to transparent mode");
|
|
451
466
|
await this.saveCurrentSession();
|
|
@@ -570,11 +585,10 @@ var ProxyServer = class {
|
|
|
570
585
|
try {
|
|
571
586
|
const sessionState = this.getOrCreateReplaySession(recordingId);
|
|
572
587
|
if (!sessionState.loadedSession) {
|
|
573
|
-
|
|
574
|
-
`Recording session file not found: ${filePath}`
|
|
588
|
+
throw Object.assign(
|
|
589
|
+
new Error(`Recording session file not found: ${filePath}`),
|
|
590
|
+
{ code: "ENOENT" }
|
|
575
591
|
);
|
|
576
|
-
error.code = "ENOENT";
|
|
577
|
-
throw error;
|
|
578
592
|
}
|
|
579
593
|
const servedForThisKey = this.getServedTracker(sessionState, key);
|
|
580
594
|
const host = req.headers.host || "unknown";
|
|
@@ -669,7 +683,7 @@ var ProxyServer = class {
|
|
|
669
683
|
res.end();
|
|
670
684
|
}
|
|
671
685
|
async handleProxyRequest(req, res) {
|
|
672
|
-
const target = this.
|
|
686
|
+
const target = this.target;
|
|
673
687
|
console.log(`[${this.mode}] ${req.method} ${req.url} -> ${target}`);
|
|
674
688
|
if (this.mode === Modes.record) {
|
|
675
689
|
await this.recordAndProxyRequest(req, res, target);
|
|
@@ -795,7 +809,7 @@ var ProxyServer = class {
|
|
|
795
809
|
this.handleReplayWebSocket(req, socket);
|
|
796
810
|
return;
|
|
797
811
|
}
|
|
798
|
-
const target = this.
|
|
812
|
+
const target = this.target;
|
|
799
813
|
console.log(`[${this.mode}] WebSocket upgrade ${req.url} -> ${target}`);
|
|
800
814
|
if (this.mode === Modes.record) {
|
|
801
815
|
this.handleRecordWebSocket(req, socket, head, target);
|
|
@@ -868,11 +882,12 @@ var ProxyServer = class {
|
|
|
868
882
|
console.error("WebSocket server error:", err);
|
|
869
883
|
});
|
|
870
884
|
}
|
|
871
|
-
handleReplayWebSocket(req, socket) {
|
|
885
|
+
async handleReplayWebSocket(req, socket) {
|
|
872
886
|
const url = req.url || "/";
|
|
873
887
|
const key = `WS_${url.replaceAll("/", "_")}`;
|
|
874
888
|
const filePath = getRecordingPath(this.recordingsDir, this.replayId);
|
|
875
|
-
|
|
889
|
+
try {
|
|
890
|
+
const session = await loadRecordingSession(filePath);
|
|
876
891
|
const wsRecording = session.websocketRecordings.find(
|
|
877
892
|
(r) => r.key === key
|
|
878
893
|
);
|
|
@@ -938,21 +953,22 @@ var ProxyServer = class {
|
|
|
938
953
|
console.log("Replay WebSocket closed");
|
|
939
954
|
});
|
|
940
955
|
});
|
|
941
|
-
}
|
|
956
|
+
} catch (error) {
|
|
942
957
|
console.error("Replay error:", error);
|
|
943
958
|
socket.write("HTTP/1.1 404 Not Found\r\n\r\n");
|
|
944
959
|
socket.destroy();
|
|
945
|
-
}
|
|
960
|
+
}
|
|
946
961
|
}
|
|
947
962
|
logServerStartup(port) {
|
|
948
963
|
console.log(`Proxy server running on http://localhost:${port}`);
|
|
949
964
|
console.log(`Mode: ${this.mode}`);
|
|
950
|
-
console.log(`
|
|
965
|
+
console.log(`Target: ${this.target}`);
|
|
951
966
|
console.log(
|
|
952
967
|
`Control endpoint: http://localhost:${port}${CONTROL_ENDPOINT}`
|
|
953
968
|
);
|
|
954
969
|
}
|
|
955
970
|
};
|
|
971
|
+
var registeredContexts = /* @__PURE__ */ new WeakSet();
|
|
956
972
|
function getProxyPort() {
|
|
957
973
|
const envPort = process.env.TEST_PROXY_RECORDER_PORT;
|
|
958
974
|
if (envPort) {
|
|
@@ -971,7 +987,7 @@ async function setProxyMode(mode, sessionId, timeout) {
|
|
|
971
987
|
id: sessionId,
|
|
972
988
|
...timeout && { timeout }
|
|
973
989
|
};
|
|
974
|
-
const response = await fetch(`http://
|
|
990
|
+
const response = await fetch(`http://localhost:${proxyPort}/__control`, {
|
|
975
991
|
method: "POST",
|
|
976
992
|
headers: { "Content-Type": "application/json" },
|
|
977
993
|
body: JSON.stringify(body)
|
|
@@ -994,7 +1010,7 @@ async function cleanupSession(sessionId) {
|
|
|
994
1010
|
cleanup: true,
|
|
995
1011
|
id: sessionId
|
|
996
1012
|
};
|
|
997
|
-
const response = await fetch(`http://
|
|
1013
|
+
const response = await fetch(`http://localhost:${proxyPort}/__control`, {
|
|
998
1014
|
method: "POST",
|
|
999
1015
|
headers: { "Content-Type": "application/json" },
|
|
1000
1016
|
body: JSON.stringify(body)
|
|
@@ -1058,7 +1074,7 @@ async function getRecordingsDir() {
|
|
|
1058
1074
|
}
|
|
1059
1075
|
const proxyPort = getProxyPort();
|
|
1060
1076
|
try {
|
|
1061
|
-
const response = await fetch(`http://
|
|
1077
|
+
const response = await fetch(`http://localhost:${proxyPort}/__control`);
|
|
1062
1078
|
if (response.ok) {
|
|
1063
1079
|
const data = await response.json();
|
|
1064
1080
|
if (data.recordingsDir) {
|
|
@@ -1145,10 +1161,8 @@ var playwrightProxy = {
|
|
|
1145
1161
|
// Ensure the handler applies to all matching requests
|
|
1146
1162
|
);
|
|
1147
1163
|
const context = page.context();
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
if (!globalThis[handlerKey]) {
|
|
1151
|
-
globalThis[handlerKey] = true;
|
|
1164
|
+
if (!registeredContexts.has(context)) {
|
|
1165
|
+
registeredContexts.add(context);
|
|
1152
1166
|
context.on("close", async () => {
|
|
1153
1167
|
try {
|
|
1154
1168
|
await cleanupSession(sessionId);
|
|
@@ -1158,7 +1172,7 @@ var playwrightProxy = {
|
|
|
1158
1172
|
error
|
|
1159
1173
|
);
|
|
1160
1174
|
} finally {
|
|
1161
|
-
delete
|
|
1175
|
+
registeredContexts.delete(context);
|
|
1162
1176
|
}
|
|
1163
1177
|
});
|
|
1164
1178
|
}
|
|
@@ -17,6 +17,7 @@ var Modes = {
|
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
// src/playwright/index.ts
|
|
20
|
+
var registeredContexts = /* @__PURE__ */ new WeakSet();
|
|
20
21
|
function getProxyPort() {
|
|
21
22
|
const envPort = process.env.TEST_PROXY_RECORDER_PORT;
|
|
22
23
|
if (envPort) {
|
|
@@ -35,7 +36,7 @@ async function setProxyMode(mode, sessionId, timeout) {
|
|
|
35
36
|
id: sessionId,
|
|
36
37
|
...timeout && { timeout }
|
|
37
38
|
};
|
|
38
|
-
const response = await fetch(`http://
|
|
39
|
+
const response = await fetch(`http://localhost:${proxyPort}/__control`, {
|
|
39
40
|
method: "POST",
|
|
40
41
|
headers: { "Content-Type": "application/json" },
|
|
41
42
|
body: JSON.stringify(body)
|
|
@@ -58,7 +59,7 @@ async function cleanupSession(sessionId) {
|
|
|
58
59
|
cleanup: true,
|
|
59
60
|
id: sessionId
|
|
60
61
|
};
|
|
61
|
-
const response = await fetch(`http://
|
|
62
|
+
const response = await fetch(`http://localhost:${proxyPort}/__control`, {
|
|
62
63
|
method: "POST",
|
|
63
64
|
headers: { "Content-Type": "application/json" },
|
|
64
65
|
body: JSON.stringify(body)
|
|
@@ -122,7 +123,7 @@ async function getRecordingsDir() {
|
|
|
122
123
|
}
|
|
123
124
|
const proxyPort = getProxyPort();
|
|
124
125
|
try {
|
|
125
|
-
const response = await fetch(`http://
|
|
126
|
+
const response = await fetch(`http://localhost:${proxyPort}/__control`);
|
|
126
127
|
if (response.ok) {
|
|
127
128
|
const data = await response.json();
|
|
128
129
|
if (data.recordingsDir) {
|
|
@@ -209,10 +210,8 @@ var playwrightProxy = {
|
|
|
209
210
|
// Ensure the handler applies to all matching requests
|
|
210
211
|
);
|
|
211
212
|
const context = page.context();
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
if (!globalThis[handlerKey]) {
|
|
215
|
-
globalThis[handlerKey] = true;
|
|
213
|
+
if (!registeredContexts.has(context)) {
|
|
214
|
+
registeredContexts.add(context);
|
|
216
215
|
context.on("close", async () => {
|
|
217
216
|
try {
|
|
218
217
|
await cleanupSession(sessionId);
|
|
@@ -222,7 +221,7 @@ var playwrightProxy = {
|
|
|
222
221
|
error
|
|
223
222
|
);
|
|
224
223
|
} finally {
|
|
225
|
-
delete
|
|
224
|
+
registeredContexts.delete(context);
|
|
226
225
|
}
|
|
227
226
|
});
|
|
228
227
|
}
|
|
@@ -11,6 +11,7 @@ var Modes = {
|
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
// src/playwright/index.ts
|
|
14
|
+
var registeredContexts = /* @__PURE__ */ new WeakSet();
|
|
14
15
|
function getProxyPort() {
|
|
15
16
|
const envPort = process.env.TEST_PROXY_RECORDER_PORT;
|
|
16
17
|
if (envPort) {
|
|
@@ -29,7 +30,7 @@ async function setProxyMode(mode, sessionId, timeout) {
|
|
|
29
30
|
id: sessionId,
|
|
30
31
|
...timeout && { timeout }
|
|
31
32
|
};
|
|
32
|
-
const response = await fetch(`http://
|
|
33
|
+
const response = await fetch(`http://localhost:${proxyPort}/__control`, {
|
|
33
34
|
method: "POST",
|
|
34
35
|
headers: { "Content-Type": "application/json" },
|
|
35
36
|
body: JSON.stringify(body)
|
|
@@ -52,7 +53,7 @@ async function cleanupSession(sessionId) {
|
|
|
52
53
|
cleanup: true,
|
|
53
54
|
id: sessionId
|
|
54
55
|
};
|
|
55
|
-
const response = await fetch(`http://
|
|
56
|
+
const response = await fetch(`http://localhost:${proxyPort}/__control`, {
|
|
56
57
|
method: "POST",
|
|
57
58
|
headers: { "Content-Type": "application/json" },
|
|
58
59
|
body: JSON.stringify(body)
|
|
@@ -116,7 +117,7 @@ async function getRecordingsDir() {
|
|
|
116
117
|
}
|
|
117
118
|
const proxyPort = getProxyPort();
|
|
118
119
|
try {
|
|
119
|
-
const response = await fetch(`http://
|
|
120
|
+
const response = await fetch(`http://localhost:${proxyPort}/__control`);
|
|
120
121
|
if (response.ok) {
|
|
121
122
|
const data = await response.json();
|
|
122
123
|
if (data.recordingsDir) {
|
|
@@ -203,10 +204,8 @@ var playwrightProxy = {
|
|
|
203
204
|
// Ensure the handler applies to all matching requests
|
|
204
205
|
);
|
|
205
206
|
const context = page.context();
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
if (!globalThis[handlerKey]) {
|
|
209
|
-
globalThis[handlerKey] = true;
|
|
207
|
+
if (!registeredContexts.has(context)) {
|
|
208
|
+
registeredContexts.add(context);
|
|
210
209
|
context.on("close", async () => {
|
|
211
210
|
try {
|
|
212
211
|
await cleanupSession(sessionId);
|
|
@@ -216,7 +215,7 @@ var playwrightProxy = {
|
|
|
216
215
|
error
|
|
217
216
|
);
|
|
218
217
|
} finally {
|
|
219
|
-
delete
|
|
218
|
+
registeredContexts.delete(context);
|
|
220
219
|
}
|
|
221
220
|
});
|
|
222
221
|
}
|