test-proxy-recorder 0.1.1 → 0.1.3

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 CHANGED
@@ -1,18 +1,18 @@
1
1
  # test-proxy-recorder
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/test-proxy-recorder.svg)](https://www.npmjs.com/package/test-proxy-recorder)
4
+ [![license](https://img.shields.io/github/license/asmyshlyaev177/test-proxy-recorder.svg?style=flat-square)](https://github.com/asmyshlyaev177/test-proxy-recorder/blob/master/LICENSE)
5
+
3
6
  HTTP proxy server for recording and replaying network requests in testing. Works seamlessly with Playwright and other testing frameworks.
4
7
 
5
- ### BETA VERSION, NOT STABLE FOR PRODUCTION USE
8
+ ## BETA VERSION
6
9
 
7
10
  ## Features
8
11
 
9
- - **Record Mode**: Capture HTTP/HTTPS requests and responses, including WebSocket connections
10
- - **Replay Mode**: Replay captured requests from disk without hitting real endpoints
11
- - **Transparent Mode**: Act as a simple proxy without recording or replaying
12
- - **Playwright Integration**: Built-in fixture for easy integration with Playwright tests
13
- - **WebSocket Support**: Full support for recording and replaying WebSocket connections
14
- - **Multiple Targets**: Load balance between multiple backend targets
15
- - **Timeout Control**: Automatic mode switching after configurable timeouts
12
+ - **Fast CI/CD Tests**: Record API responses once with real backend, replay them on CI/CD
13
+ - **Fast workflow**: Record real interactions with API, instead of mocking every request manually
14
+ - **Server Side Rendering**: Can record SSR requests from JS frameworks like NextJS.
15
+ - **Deterministic Tests**: Same responses every time, no flaky network issues, no need to wire up the whole Backend API for testing
16
16
 
17
17
  ## Installation
18
18
 
@@ -26,40 +26,23 @@ yarn add test-proxy-recorder
26
26
 
27
27
  ## Quick Start
28
28
 
29
- ### Standalone Usage
30
-
31
- ```typescript
32
- import { ProxyServer } from 'test-proxy-recorder';
33
-
34
- const proxy = new ProxyServer(
35
- ['http://localhost:3000'], // backend targets
36
- './recordings' // directory to store recordings
37
- );
38
-
39
- await proxy.init();
40
- const server = proxy.listen(8080);
41
- console.log('Proxy running on http://localhost:8080');
42
- ```
43
-
44
- ### With Playwright
45
-
46
- The proxy runs continuously in the background. Tests control the recording/replay mode using `playwrightProxy.before()` and `playwrightProxy.after()`:
29
+ 1. Run proxy with your backend API as a target `test-proxy-recorder --port 8100 --target http://localhost:8000 --recordings ./recordings`, here your backend on port 8000 as target, proxy on port 8100.
30
+ 2. Point your Frontend app to proxy port, 8100 as example
31
+ 3. The proxy runs continuously in the background. Tests control the recording/replay mode using `playwrightProxy.before()` and `playwrightProxy.after()`:
47
32
 
48
33
  ```typescript
49
34
  import { test } from '@playwright/test';
50
35
  import { playwrightProxy } from 'test-proxy-recorder';
51
36
 
52
- test('record API responses', async ({ page }, testInfo) => {
53
- // Set proxy to recording mode for this test
54
- await playwrightProxy.before(testInfo, 'recording');
37
+ test('Test UI with API responses', async ({ page }, testInfo) => {
38
+ // Set proxy to recording mode to record mocks, sanitized test title will be used as file name
39
+ await playwrightProxy.before(testInfo, 'record');
55
40
 
56
41
  // Make requests - they will be recorded
57
- await page.goto('http://localhost:8080/api/data');
58
-
59
- // Your test assertions here
60
- await expect(page.getByText('Data loaded')).toBeVisible();
42
+ await page.goto('/myPage');
43
+ /// ... test content ...
61
44
 
62
- // Clean up - return to transparent mode
45
+ // Save mock and return to transparent mode
63
46
  await playwrightProxy.after(testInfo);
64
47
  });
65
48
 
@@ -67,34 +50,13 @@ test('replay recorded responses', async ({ page }, testInfo) => {
67
50
  // Set proxy to replay mode - uses recording from test above
68
51
  await playwrightProxy.before(testInfo, 'replay');
69
52
 
70
- // This will use recorded responses instead of hitting the real API
71
- await page.goto('http://localhost:8080/api/data');
72
-
73
- await expect(page.getByText('Data loaded')).toBeVisible();
53
+ await page.goto('/myPage');
54
+ /// ... test content ...
74
55
 
75
56
  await playwrightProxy.after(testInfo);
76
57
  });
77
58
  ```
78
59
 
79
- You can also use standalone functions for more control:
80
-
81
- ```typescript
82
- import { test } from '@playwright/test';
83
- import { setProxyMode, generateSessionId } from 'test-proxy-recorder';
84
-
85
- test('custom proxy control', async ({ page }, testInfo) => {
86
- const sessionId = generateSessionId(testInfo);
87
-
88
- // Start recording with a 30s timeout
89
- await setProxyMode('recording', sessionId, 30000);
90
-
91
- await page.goto('http://localhost:8080/api/data');
92
-
93
- // Switch to transparent mode
94
- await setProxyMode('transparent', sessionId);
95
- });
96
- ```
97
-
98
60
  ## How It Works
99
61
 
100
62
  The proxy server runs continuously and can switch between three modes:
@@ -113,7 +75,7 @@ Replays previously recorded responses from disk instead of hitting the real API.
113
75
 
114
76
  ## Modes Control
115
77
 
116
- ### Via Playwright (Recommended)
78
+ ### Via Playwright
117
79
 
118
80
  ```typescript
119
81
  // Recording mode
@@ -129,40 +91,76 @@ await playwrightProxy.after(testInfo);
129
91
 
130
92
  ### Via HTTP Control Endpoint
131
93
 
94
+ Using curl:
95
+
132
96
  ```bash
133
97
  # Switch to record mode
134
- curl -X POST http://localhost:8080/__proxy_control__ \
98
+ curl -X POST http://localhost:8100/__control \
135
99
  -H "Content-Type: application/json" \
136
- -d '{"mode": "record", "id": "test-session-1", "timeout": 30000}'
100
+ -d '{"mode": "record", "id": "my-testfile-1", "timeout": 30000}'
137
101
 
138
102
  # Switch to replay mode
139
- curl -X POST http://localhost:8080/__proxy_control__ \
103
+ curl -X POST http://localhost:8100/__control \
140
104
  -H "Content-Type: application/json" \
141
- -d '{"mode": "replay", "id": "test-session-1"}'
105
+ -d '{"mode": "replay", "id": "my-testfile-1"}'
142
106
 
143
107
  # Switch to transparent mode
144
- curl -X POST http://localhost:8080/__proxy_control__ \
108
+ curl -X POST http://localhost:8100/__control \
145
109
  -H "Content-Type: application/json" \
146
- -d '{"mode": "transparent", "id": "test-session-1"}'
110
+ -d '{"mode": "transparent", "id": "my-testfile-1"}'
147
111
  ```
148
112
 
149
- ## CLI Usage
113
+ Using JavaScript fetch:
114
+
115
+ ```javascript
116
+ // Switch to record mode
117
+ await fetch('http://localhost:8100/__control', {
118
+ method: 'POST',
119
+ headers: {
120
+ 'Content-Type': 'application/json',
121
+ },
122
+ body: JSON.stringify({
123
+ mode: 'record',
124
+ id: 'my-testfile-1',
125
+ timeout: 30000
126
+ })
127
+ });
128
+
129
+ // Switch to replay mode
130
+ await fetch('http://localhost:8100/__control', {
131
+ method: 'POST',
132
+ headers: {
133
+ 'Content-Type': 'application/json',
134
+ },
135
+ body: JSON.stringify({
136
+ mode: 'replay',
137
+ id: 'my-testfile-1'
138
+ })
139
+ });
140
+
141
+ // Switch to transparent mode
142
+ await fetch('http://localhost:8100/__control', {
143
+ method: 'POST',
144
+ headers: {
145
+ 'Content-Type': 'application/json',
146
+ },
147
+ body: JSON.stringify({
148
+ mode: 'transparent',
149
+ })
150
+ });
151
+ ```
152
+
153
+ ## Usage
150
154
 
151
155
  ```bash
152
156
  # Start proxy server
153
- test-proxy-recorder --port 8080 --target http://localhost:3000 --recordings ./recordings
154
-
155
- # With multiple targets for load balancing
156
- test-proxy-recorder --port 8080 \
157
- --target http://localhost:3000 \
158
- --target http://localhost:3001 \
159
- --recordings ./recordings
157
+ test-proxy-recorder --port 8100 --target http://localhost:8000 --recordings ./recordings
160
158
  ```
161
159
 
162
160
  ### CLI Options
163
161
 
164
162
  - `--port, -p`: Port to listen on (default: 8080)
165
- - `--target, -t`: Backend target URL (can be specified multiple times)
163
+ - `--target, -t`: Backend target URL (can add multiple targets)
166
164
  - `--recordings, -r`: Directory to store recordings (default: ./recordings)
167
165
 
168
166
  ## API
@@ -180,13 +178,13 @@ class ProxyServer {
180
178
 
181
179
  ### Control Endpoint
182
180
 
183
- Send POST requests to `/__proxy_control__` with JSON body:
181
+ Send POST requests to `/__control` with JSON body:
184
182
 
185
183
  ```typescript
186
184
  interface ControlRequest {
187
185
  mode: 'transparent' | 'record' | 'replay';
188
- id?: string; // Required for record/replay modes
189
- timeout?: number; // Auto-switch to transparent mode after timeout (ms)
186
+ id?: string; // Will be used as file name for recordings, required for record/replay modes
187
+ timeout?: number; // Auto-switch to transparent mode after timeout (ms), default 120 seconds
190
188
  }
191
189
  ```
192
190
 
@@ -198,36 +196,11 @@ import { playwrightProxy } from 'test-proxy-recorder';
198
196
  // Main helper object for use with Playwright tests
199
197
  const playwrightProxy = {
200
198
  // Set proxy mode before test
201
- async before(testInfo: PlaywrightTestInfo, mode: 'recording' | 'replay' | 'transparent'): Promise<void>;
199
+ async before(testInfo: PlaywrightTestInfo, mode: 'record' | 'replay' | 'transparent'): Promise<void>;
202
200
 
203
201
  // Reset to transparent mode after test
204
202
  async after(testInfo: PlaywrightTestInfo): Promise<void>;
205
203
  };
206
-
207
- // Standalone functions for custom control:
208
- import {
209
- setProxyMode, // Set mode with custom session ID
210
- generateSessionId, // Generate session ID from test info
211
- startRecording, // Helper to start recording
212
- startReplay, // Helper to start replay
213
- stopProxy // Helper to stop recording/replay
214
- } from 'test-proxy-recorder';
215
- ```
216
-
217
- ### Usage Pattern
218
-
219
- ```typescript
220
- test('your test', async ({ page }, testInfo) => {
221
- // Setup: Set proxy mode at start of test
222
- await playwrightProxy.before(testInfo, 'replay');
223
-
224
- // Your test code here
225
- await page.goto('/your-page');
226
- await expect(page.getByText('Something')).toBeVisible();
227
-
228
- // Cleanup: Return to transparent mode
229
- await playwrightProxy.after(testInfo);
230
- });
231
204
  ```
232
205
 
233
206
  ## Recording Format
@@ -252,20 +225,20 @@ Each recording contains:
252
225
  1. **Start the proxy server** (runs continuously):
253
226
 
254
227
  ```bash
255
- test-proxy-recorder http://localhost:3000 --port 8080
228
+ test-proxy-recorder http://localhost:8000 --port 8100
256
229
  ```
257
230
 
258
231
  2. **Configure your app** to use the proxy:
259
232
 
260
233
  ```bash
261
- export EXTERNAL_API_URL=http://localhost:8080
234
+ export EXTERNAL_API_URL=http://localhost:8100 yarn dev
262
235
  ```
263
236
 
264
237
  3. **Record responses** (first run):
265
238
 
266
239
  ```typescript
267
240
  test('my test', async ({ page }, testInfo) => {
268
- await playwrightProxy.before(testInfo, 'recording');
241
+ await playwrightProxy.before(testInfo, 'record');
269
242
  // Test interacts with real API through proxy
270
243
  await page.goto('/my-page');
271
244
  await playwrightProxy.after(testInfo);
@@ -283,68 +256,10 @@ Each recording contains:
283
256
  });
284
257
  ```
285
258
 
286
- ## Use Cases
287
-
288
- - **Fast CI/CD Tests**: Record API responses once, replay them for instant test execution
289
- - **Deterministic Tests**: Same responses every time, no flaky network issues
290
- - **API Contract Testing**: Verify frontend handles all API scenarios
291
- - **WebSocket Testing**: Record and replay complex WebSocket message sequences
292
-
293
- ## Configuration Examples
294
-
295
- ### With Environment Variables
296
-
297
- ```typescript
298
- const proxy = new ProxyServer(
299
- [process.env.BACKEND_URL || 'http://localhost:3000'],
300
- process.env.RECORDINGS_DIR || './recordings'
301
- );
302
- ```
303
-
304
- ### Multiple Backend Targets
305
-
306
- ```typescript
307
- // Round-robin load balancing between targets
308
- const proxy = new ProxyServer(
309
- [
310
- 'http://backend-1:3000',
311
- 'http://backend-2:3000',
312
- 'http://backend-3:3000'
313
- ],
314
- './recordings'
315
- );
316
- ```
317
-
318
- ## WebSocket Support
319
-
320
- WebSocket connections are automatically detected and recorded/replayed:
321
-
322
- ```typescript
323
- test('websocket test', async ({ page }, testInfo) => {
324
- // Recording WebSocket messages
325
- await playwrightProxy.before(testInfo, 'recording');
326
-
327
- await page.goto('/websocket-page');
328
- // WebSocket connections and messages are recorded
329
-
330
- await playwrightProxy.after(testInfo);
331
- });
332
-
333
- test('websocket replay', async ({ page }, testInfo) => {
334
- // Replay WebSocket messages
335
- await playwrightProxy.before(testInfo, 'replay');
336
-
337
- await page.goto('/websocket-page');
338
- // Previously recorded WebSocket messages are replayed
339
-
340
- await playwrightProxy.after(testInfo);
341
- });
342
- ```
343
-
344
259
  ## Requirements
345
260
 
346
261
  - Node.js >= 22.0.0
347
- - For Playwright integration: @playwright/test >= 1.0.0
262
+ - @playwright/test >= 1.0.0
348
263
 
349
264
  ## License
350
265
 
@@ -353,11 +268,3 @@ MIT
353
268
  ## Contributing
354
269
 
355
270
  Contributions are welcome! Please feel free to submit a Pull Request.
356
-
357
- ## Author
358
-
359
- asmyshlyaev177
360
-
361
- ## Repository
362
-
363
- [https://github.com/asmyshlyaev177/test-proxy-recorder](https://github.com/asmyshlyaev177/test-proxy-recorder)
@@ -0,0 +1,95 @@
1
+ import { TestInfo } from '@playwright/test';
2
+ import http from 'node:http';
3
+
4
+ declare const Modes: {
5
+ readonly transparent: "transparent";
6
+ readonly record: "record";
7
+ readonly replay: "replay";
8
+ };
9
+ type Mode = (typeof Modes)[keyof typeof Modes];
10
+ interface ControlRequest {
11
+ mode: Mode;
12
+ id?: string;
13
+ timeout?: number;
14
+ }
15
+ interface RecordedRequest {
16
+ method: string;
17
+ url: string;
18
+ headers: http.IncomingHttpHeaders;
19
+ body: string | null;
20
+ }
21
+ interface RecordedResponse {
22
+ statusCode: number;
23
+ headers: http.IncomingHttpHeaders;
24
+ body: string | null;
25
+ }
26
+ interface Recording {
27
+ request: RecordedRequest;
28
+ response?: RecordedResponse;
29
+ timestamp: string;
30
+ key: string;
31
+ }
32
+ interface WebSocketMessage {
33
+ direction: 'client-to-server' | 'server-to-client';
34
+ data: string;
35
+ timestamp: string;
36
+ }
37
+ interface WebSocketRecording {
38
+ url: string;
39
+ messages: WebSocketMessage[];
40
+ timestamp: string;
41
+ key: string;
42
+ }
43
+ interface RecordingSession {
44
+ id: string;
45
+ recordings: Recording[];
46
+ websocketRecordings: WebSocketRecording[];
47
+ }
48
+
49
+ type PlaywrightTestInfo = Pick<TestInfo, 'title'>;
50
+ /**
51
+ * Set the proxy mode for a given session
52
+ * @param mode - The proxy mode to set (recording, replay, transparent)
53
+ * @param sessionId - Unique identifier for the session
54
+ * @param timeout - Optional timeout in milliseconds
55
+ */
56
+ declare function setProxyMode(mode: Mode, sessionId: string, timeout?: number): Promise<void>;
57
+ /**
58
+ * Generate a session ID from test info
59
+ * @param testInfo - Playwright test info object
60
+ */
61
+ declare function generateSessionId(testInfo: PlaywrightTestInfo): string;
62
+ /**
63
+ * Start recording for a test
64
+ * @param testInfo - Playwright test info object
65
+ */
66
+ declare function startRecording(testInfo: PlaywrightTestInfo): Promise<void>;
67
+ /**
68
+ * Start replay for a test
69
+ * @param testInfo - Playwright test info object
70
+ */
71
+ declare function startReplay(testInfo: PlaywrightTestInfo): Promise<void>;
72
+ /**
73
+ * Stop recording/replay and return to transparent mode
74
+ * @param testInfo - Playwright test info object
75
+ */
76
+ declare function stopProxy(testInfo: PlaywrightTestInfo): Promise<void>;
77
+ /**
78
+ * Playwright test fixture helper for managing proxy mode
79
+ * Use this in beforeEach/afterEach hooks
80
+ */
81
+ declare const playwrightProxy: {
82
+ /**
83
+ * Setup before test - sets the proxy mode
84
+ * @param testInfo - Playwright test info object
85
+ * @param mode - The proxy mode to use for this test
86
+ */
87
+ before(testInfo: PlaywrightTestInfo, mode: Mode): Promise<void>;
88
+ /**
89
+ * Cleanup after test - returns to transparent mode
90
+ * @param testInfo - Playwright test info object
91
+ */
92
+ after(testInfo: PlaywrightTestInfo): Promise<void>;
93
+ };
94
+
95
+ export { type ControlRequest as C, type Mode as M, type PlaywrightTestInfo as P, type Recording as R, type WebSocketRecording as W, type RecordingSession as a, startRecording as b, startReplay as c, stopProxy as d, generateSessionId as g, playwrightProxy as p, setProxyMode as s };
@@ -0,0 +1,95 @@
1
+ import { TestInfo } from '@playwright/test';
2
+ import http from 'node:http';
3
+
4
+ declare const Modes: {
5
+ readonly transparent: "transparent";
6
+ readonly record: "record";
7
+ readonly replay: "replay";
8
+ };
9
+ type Mode = (typeof Modes)[keyof typeof Modes];
10
+ interface ControlRequest {
11
+ mode: Mode;
12
+ id?: string;
13
+ timeout?: number;
14
+ }
15
+ interface RecordedRequest {
16
+ method: string;
17
+ url: string;
18
+ headers: http.IncomingHttpHeaders;
19
+ body: string | null;
20
+ }
21
+ interface RecordedResponse {
22
+ statusCode: number;
23
+ headers: http.IncomingHttpHeaders;
24
+ body: string | null;
25
+ }
26
+ interface Recording {
27
+ request: RecordedRequest;
28
+ response?: RecordedResponse;
29
+ timestamp: string;
30
+ key: string;
31
+ }
32
+ interface WebSocketMessage {
33
+ direction: 'client-to-server' | 'server-to-client';
34
+ data: string;
35
+ timestamp: string;
36
+ }
37
+ interface WebSocketRecording {
38
+ url: string;
39
+ messages: WebSocketMessage[];
40
+ timestamp: string;
41
+ key: string;
42
+ }
43
+ interface RecordingSession {
44
+ id: string;
45
+ recordings: Recording[];
46
+ websocketRecordings: WebSocketRecording[];
47
+ }
48
+
49
+ type PlaywrightTestInfo = Pick<TestInfo, 'title'>;
50
+ /**
51
+ * Set the proxy mode for a given session
52
+ * @param mode - The proxy mode to set (recording, replay, transparent)
53
+ * @param sessionId - Unique identifier for the session
54
+ * @param timeout - Optional timeout in milliseconds
55
+ */
56
+ declare function setProxyMode(mode: Mode, sessionId: string, timeout?: number): Promise<void>;
57
+ /**
58
+ * Generate a session ID from test info
59
+ * @param testInfo - Playwright test info object
60
+ */
61
+ declare function generateSessionId(testInfo: PlaywrightTestInfo): string;
62
+ /**
63
+ * Start recording for a test
64
+ * @param testInfo - Playwright test info object
65
+ */
66
+ declare function startRecording(testInfo: PlaywrightTestInfo): Promise<void>;
67
+ /**
68
+ * Start replay for a test
69
+ * @param testInfo - Playwright test info object
70
+ */
71
+ declare function startReplay(testInfo: PlaywrightTestInfo): Promise<void>;
72
+ /**
73
+ * Stop recording/replay and return to transparent mode
74
+ * @param testInfo - Playwright test info object
75
+ */
76
+ declare function stopProxy(testInfo: PlaywrightTestInfo): Promise<void>;
77
+ /**
78
+ * Playwright test fixture helper for managing proxy mode
79
+ * Use this in beforeEach/afterEach hooks
80
+ */
81
+ declare const playwrightProxy: {
82
+ /**
83
+ * Setup before test - sets the proxy mode
84
+ * @param testInfo - Playwright test info object
85
+ * @param mode - The proxy mode to use for this test
86
+ */
87
+ before(testInfo: PlaywrightTestInfo, mode: Mode): Promise<void>;
88
+ /**
89
+ * Cleanup after test - returns to transparent mode
90
+ * @param testInfo - Playwright test info object
91
+ */
92
+ after(testInfo: PlaywrightTestInfo): Promise<void>;
93
+ };
94
+
95
+ export { type ControlRequest as C, type Mode as M, type PlaywrightTestInfo as P, type Recording as R, type WebSocketRecording as W, type RecordingSession as a, startRecording as b, startReplay as c, stopProxy as d, generateSessionId as g, playwrightProxy as p, setProxyMode as s };
package/dist/index.cjs CHANGED
@@ -5,6 +5,7 @@ var http = require('http');
5
5
  var httpProxy = require('http-proxy');
6
6
  var ws = require('ws');
7
7
  var path = require('path');
8
+ var filenamify = require('filenamify');
8
9
 
9
10
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
10
11
 
@@ -12,6 +13,7 @@ var fs__default = /*#__PURE__*/_interopDefault(fs);
12
13
  var http__default = /*#__PURE__*/_interopDefault(http);
13
14
  var httpProxy__default = /*#__PURE__*/_interopDefault(httpProxy);
14
15
  var path__default = /*#__PURE__*/_interopDefault(path);
16
+ var filenamify__default = /*#__PURE__*/_interopDefault(filenamify);
15
17
 
16
18
  // src/ProxyServer.ts
17
19
 
@@ -47,6 +49,24 @@ async function saveRecordingSession(recordingsDir, session) {
47
49
  `Saved ${session.recordings.length} HTTP recordings and ${session.websocketRecordings?.length || 0} WebSocket recordings to ${filePath}`
48
50
  );
49
51
  }
52
+ var QUERY_HASH_LENGTH = 8;
53
+ function getReqID(req) {
54
+ const urlParts = req.url.split("?");
55
+ const pathname = urlParts[0];
56
+ const query = urlParts[1] || "";
57
+ const pathPart = pathname === "/" ? "root" : pathname.slice(1);
58
+ const normalizedPath = filenamify__default.default(pathPart, { replacement: "_" });
59
+ const queryHash = generateQueryHash(query);
60
+ const filename = `${req.method}_${normalizedPath}${queryHash}.json`;
61
+ return filenamify__default.default(filename, { replacement: "_" });
62
+ }
63
+ function generateQueryHash(query) {
64
+ if (!query) {
65
+ return "";
66
+ }
67
+ const hash = Buffer.from(query).toString("base64").replaceAll(/[^a-zA-Z0-9]/g, "").slice(0, Math.max(0, QUERY_HASH_LENGTH));
68
+ return `_${hash}`;
69
+ }
50
70
 
51
71
  // src/utils/httpHelpers.ts
52
72
  var CONTENT_TYPE_JSON = "application/json";
@@ -62,28 +82,6 @@ function sendJsonResponse(res, statusCode, data) {
62
82
  res.end(JSON.stringify(data));
63
83
  }
64
84
 
65
- // src/utils/requestKeyGenerator.ts
66
- var QUERY_HASH_LENGTH = 8;
67
- function generateRequestKey(req) {
68
- const urlParts = req.url.split("?");
69
- const pathname = urlParts[0];
70
- const query = urlParts[1] || "";
71
- const normalizedPath = normalizePathname(pathname);
72
- const queryHash = generateQueryHash(query);
73
- return `${req.method}_${normalizedPath}${queryHash}.json`;
74
- }
75
- function normalizePathname(pathname) {
76
- const normalized = pathname.replaceAll("/", "_").replace(/^_/, "");
77
- return normalized || "root";
78
- }
79
- function generateQueryHash(query) {
80
- if (!query) {
81
- return "";
82
- }
83
- const hash = Buffer.from(query).toString("base64").replaceAll(/[^a-zA-Z0-9]/g, "").slice(0, Math.max(0, QUERY_HASH_LENGTH));
84
- return `_${hash}`;
85
- }
86
-
87
85
  // src/ProxyServer.ts
88
86
  var ProxyServer = class {
89
87
  targets;
@@ -209,6 +207,7 @@ var ProxyServer = class {
209
207
  this.recordingId = null;
210
208
  this.replayId = null;
211
209
  this.currentSession = null;
210
+ clearTimeout(this.modeTimeout || 0);
212
211
  console.log("Switched to transparent mode");
213
212
  }
214
213
  switchToRecordMode(id) {
@@ -259,7 +258,7 @@ var ProxyServer = class {
259
258
  if (!this.currentSession) {
260
259
  return;
261
260
  }
262
- const key = generateRequestKey(req);
261
+ const key = getReqID(req);
263
262
  const record = {
264
263
  request: {
265
264
  method: req.method,
@@ -276,7 +275,7 @@ var ProxyServer = class {
276
275
  if (!this.currentSession) {
277
276
  return;
278
277
  }
279
- const key = generateRequestKey(req);
278
+ const key = getReqID(req);
280
279
  const record = this.currentSession.recordings.find((r) => r.key === key);
281
280
  if (!record) {
282
281
  console.error("Request record not found for response:", key);
@@ -298,7 +297,7 @@ var ProxyServer = class {
298
297
  });
299
298
  }
300
299
  async handleReplayRequest(req, res) {
301
- const key = generateRequestKey(req);
300
+ const key = getReqID(req);
302
301
  const filePath = getRecordingPath(this.recordingsDir, this.replayId);
303
302
  try {
304
303
  const session = await loadRecordingSession(filePath);
@@ -555,15 +554,15 @@ function generateSessionId(testInfo) {
555
554
  }
556
555
  async function startRecording(testInfo) {
557
556
  const sessionId = generateSessionId(testInfo);
558
- await setProxyMode("recording", sessionId);
557
+ await setProxyMode(Modes.record, sessionId);
559
558
  }
560
559
  async function startReplay(testInfo) {
561
560
  const sessionId = generateSessionId(testInfo);
562
- await setProxyMode("replay", sessionId);
561
+ await setProxyMode(Modes.replay, sessionId);
563
562
  }
564
563
  async function stopProxy(testInfo) {
565
564
  const sessionId = generateSessionId(testInfo);
566
- await setProxyMode("transparent", sessionId);
565
+ await setProxyMode(Modes.transparent, sessionId);
567
566
  }
568
567
  var playwrightProxy = {
569
568
  /**
@@ -582,7 +581,7 @@ var playwrightProxy = {
582
581
  */
583
582
  async after(testInfo) {
584
583
  const sessionId = generateSessionId(testInfo);
585
- await setProxyMode("transparent", sessionId);
584
+ await setProxyMode(Modes.transparent, sessionId);
586
585
  }
587
586
  };
588
587
 
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import http from 'node:http';
2
- export { PlaywrightTestInfo, ProxyMode, generateSessionId, playwrightProxy, setProxyMode, startRecording, startReplay, stopProxy } from './playwright/index.cjs';
2
+ export { C as ControlRequest, M as Mode, P as PlaywrightTestInfo, R as Recording, a as RecordingSession, W as WebSocketRecording, g as generateSessionId, p as playwrightProxy, s as setProxyMode, b as startRecording, c as startReplay, d as stopProxy } from './index-DfFpm8mB.cjs';
3
3
  import '@playwright/test';
4
4
 
5
5
  declare class ProxyServer {
@@ -40,49 +40,4 @@ declare class ProxyServer {
40
40
  private logServerStartup;
41
41
  }
42
42
 
43
- declare const Modes: {
44
- readonly transparent: "transparent";
45
- readonly record: "record";
46
- readonly replay: "replay";
47
- };
48
- type Mode = (typeof Modes)[keyof typeof Modes];
49
- interface ControlRequest {
50
- mode: Mode;
51
- id?: string;
52
- timeout?: number;
53
- }
54
- interface RecordedRequest {
55
- method: string;
56
- url: string;
57
- headers: http.IncomingHttpHeaders;
58
- body: string | null;
59
- }
60
- interface RecordedResponse {
61
- statusCode: number;
62
- headers: http.IncomingHttpHeaders;
63
- body: string | null;
64
- }
65
- interface Recording {
66
- request: RecordedRequest;
67
- response?: RecordedResponse;
68
- timestamp: string;
69
- key: string;
70
- }
71
- interface WebSocketMessage {
72
- direction: 'client-to-server' | 'server-to-client';
73
- data: string;
74
- timestamp: string;
75
- }
76
- interface WebSocketRecording {
77
- url: string;
78
- messages: WebSocketMessage[];
79
- timestamp: string;
80
- key: string;
81
- }
82
- interface RecordingSession {
83
- id: string;
84
- recordings: Recording[];
85
- websocketRecordings: WebSocketRecording[];
86
- }
87
-
88
- export { type ControlRequest, type Mode, ProxyServer, type Recording, type RecordingSession, type WebSocketRecording };
43
+ export { ProxyServer };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import http from 'node:http';
2
- export { PlaywrightTestInfo, ProxyMode, generateSessionId, playwrightProxy, setProxyMode, startRecording, startReplay, stopProxy } from './playwright/index.js';
2
+ export { C as ControlRequest, M as Mode, P as PlaywrightTestInfo, R as Recording, a as RecordingSession, W as WebSocketRecording, g as generateSessionId, p as playwrightProxy, s as setProxyMode, b as startRecording, c as startReplay, d as stopProxy } from './index-DfFpm8mB.js';
3
3
  import '@playwright/test';
4
4
 
5
5
  declare class ProxyServer {
@@ -40,49 +40,4 @@ declare class ProxyServer {
40
40
  private logServerStartup;
41
41
  }
42
42
 
43
- declare const Modes: {
44
- readonly transparent: "transparent";
45
- readonly record: "record";
46
- readonly replay: "replay";
47
- };
48
- type Mode = (typeof Modes)[keyof typeof Modes];
49
- interface ControlRequest {
50
- mode: Mode;
51
- id?: string;
52
- timeout?: number;
53
- }
54
- interface RecordedRequest {
55
- method: string;
56
- url: string;
57
- headers: http.IncomingHttpHeaders;
58
- body: string | null;
59
- }
60
- interface RecordedResponse {
61
- statusCode: number;
62
- headers: http.IncomingHttpHeaders;
63
- body: string | null;
64
- }
65
- interface Recording {
66
- request: RecordedRequest;
67
- response?: RecordedResponse;
68
- timestamp: string;
69
- key: string;
70
- }
71
- interface WebSocketMessage {
72
- direction: 'client-to-server' | 'server-to-client';
73
- data: string;
74
- timestamp: string;
75
- }
76
- interface WebSocketRecording {
77
- url: string;
78
- messages: WebSocketMessage[];
79
- timestamp: string;
80
- key: string;
81
- }
82
- interface RecordingSession {
83
- id: string;
84
- recordings: Recording[];
85
- websocketRecordings: WebSocketRecording[];
86
- }
87
-
88
- export { type ControlRequest, type Mode, ProxyServer, type Recording, type RecordingSession, type WebSocketRecording };
43
+ export { ProxyServer };
package/dist/index.mjs CHANGED
@@ -3,6 +3,7 @@ import http from 'http';
3
3
  import httpProxy from 'http-proxy';
4
4
  import { WebSocket, WebSocketServer } from 'ws';
5
5
  import path from 'path';
6
+ import filenamify from 'filenamify';
6
7
 
7
8
  // src/ProxyServer.ts
8
9
 
@@ -38,6 +39,24 @@ async function saveRecordingSession(recordingsDir, session) {
38
39
  `Saved ${session.recordings.length} HTTP recordings and ${session.websocketRecordings?.length || 0} WebSocket recordings to ${filePath}`
39
40
  );
40
41
  }
42
+ var QUERY_HASH_LENGTH = 8;
43
+ function getReqID(req) {
44
+ const urlParts = req.url.split("?");
45
+ const pathname = urlParts[0];
46
+ const query = urlParts[1] || "";
47
+ const pathPart = pathname === "/" ? "root" : pathname.slice(1);
48
+ const normalizedPath = filenamify(pathPart, { replacement: "_" });
49
+ const queryHash = generateQueryHash(query);
50
+ const filename = `${req.method}_${normalizedPath}${queryHash}.json`;
51
+ return filenamify(filename, { replacement: "_" });
52
+ }
53
+ function generateQueryHash(query) {
54
+ if (!query) {
55
+ return "";
56
+ }
57
+ const hash = Buffer.from(query).toString("base64").replaceAll(/[^a-zA-Z0-9]/g, "").slice(0, Math.max(0, QUERY_HASH_LENGTH));
58
+ return `_${hash}`;
59
+ }
41
60
 
42
61
  // src/utils/httpHelpers.ts
43
62
  var CONTENT_TYPE_JSON = "application/json";
@@ -53,28 +72,6 @@ function sendJsonResponse(res, statusCode, data) {
53
72
  res.end(JSON.stringify(data));
54
73
  }
55
74
 
56
- // src/utils/requestKeyGenerator.ts
57
- var QUERY_HASH_LENGTH = 8;
58
- function generateRequestKey(req) {
59
- const urlParts = req.url.split("?");
60
- const pathname = urlParts[0];
61
- const query = urlParts[1] || "";
62
- const normalizedPath = normalizePathname(pathname);
63
- const queryHash = generateQueryHash(query);
64
- return `${req.method}_${normalizedPath}${queryHash}.json`;
65
- }
66
- function normalizePathname(pathname) {
67
- const normalized = pathname.replaceAll("/", "_").replace(/^_/, "");
68
- return normalized || "root";
69
- }
70
- function generateQueryHash(query) {
71
- if (!query) {
72
- return "";
73
- }
74
- const hash = Buffer.from(query).toString("base64").replaceAll(/[^a-zA-Z0-9]/g, "").slice(0, Math.max(0, QUERY_HASH_LENGTH));
75
- return `_${hash}`;
76
- }
77
-
78
75
  // src/ProxyServer.ts
79
76
  var ProxyServer = class {
80
77
  targets;
@@ -200,6 +197,7 @@ var ProxyServer = class {
200
197
  this.recordingId = null;
201
198
  this.replayId = null;
202
199
  this.currentSession = null;
200
+ clearTimeout(this.modeTimeout || 0);
203
201
  console.log("Switched to transparent mode");
204
202
  }
205
203
  switchToRecordMode(id) {
@@ -250,7 +248,7 @@ var ProxyServer = class {
250
248
  if (!this.currentSession) {
251
249
  return;
252
250
  }
253
- const key = generateRequestKey(req);
251
+ const key = getReqID(req);
254
252
  const record = {
255
253
  request: {
256
254
  method: req.method,
@@ -267,7 +265,7 @@ var ProxyServer = class {
267
265
  if (!this.currentSession) {
268
266
  return;
269
267
  }
270
- const key = generateRequestKey(req);
268
+ const key = getReqID(req);
271
269
  const record = this.currentSession.recordings.find((r) => r.key === key);
272
270
  if (!record) {
273
271
  console.error("Request record not found for response:", key);
@@ -289,7 +287,7 @@ var ProxyServer = class {
289
287
  });
290
288
  }
291
289
  async handleReplayRequest(req, res) {
292
- const key = generateRequestKey(req);
290
+ const key = getReqID(req);
293
291
  const filePath = getRecordingPath(this.recordingsDir, this.replayId);
294
292
  try {
295
293
  const session = await loadRecordingSession(filePath);
@@ -546,15 +544,15 @@ function generateSessionId(testInfo) {
546
544
  }
547
545
  async function startRecording(testInfo) {
548
546
  const sessionId = generateSessionId(testInfo);
549
- await setProxyMode("recording", sessionId);
547
+ await setProxyMode(Modes.record, sessionId);
550
548
  }
551
549
  async function startReplay(testInfo) {
552
550
  const sessionId = generateSessionId(testInfo);
553
- await setProxyMode("replay", sessionId);
551
+ await setProxyMode(Modes.replay, sessionId);
554
552
  }
555
553
  async function stopProxy(testInfo) {
556
554
  const sessionId = generateSessionId(testInfo);
557
- await setProxyMode("transparent", sessionId);
555
+ await setProxyMode(Modes.transparent, sessionId);
558
556
  }
559
557
  var playwrightProxy = {
560
558
  /**
@@ -573,7 +571,7 @@ var playwrightProxy = {
573
571
  */
574
572
  async after(testInfo) {
575
573
  const sessionId = generateSessionId(testInfo);
576
- await setProxyMode("transparent", sessionId);
574
+ await setProxyMode(Modes.transparent, sessionId);
577
575
  }
578
576
  };
579
577
 
@@ -1,5 +1,12 @@
1
1
  'use strict';
2
2
 
3
+ // src/types.ts
4
+ var Modes = {
5
+ transparent: "transparent",
6
+ record: "record",
7
+ replay: "replay"
8
+ };
9
+
3
10
  // src/playwright/index.ts
4
11
  var INTERNAL_API_URL = process.env.INTERNAL_API_URL || "http://localhost:8100";
5
12
  async function setProxyMode(mode, sessionId, timeout) {
@@ -31,15 +38,15 @@ function generateSessionId(testInfo) {
31
38
  }
32
39
  async function startRecording(testInfo) {
33
40
  const sessionId = generateSessionId(testInfo);
34
- await setProxyMode("recording", sessionId);
41
+ await setProxyMode(Modes.record, sessionId);
35
42
  }
36
43
  async function startReplay(testInfo) {
37
44
  const sessionId = generateSessionId(testInfo);
38
- await setProxyMode("replay", sessionId);
45
+ await setProxyMode(Modes.replay, sessionId);
39
46
  }
40
47
  async function stopProxy(testInfo) {
41
48
  const sessionId = generateSessionId(testInfo);
42
- await setProxyMode("transparent", sessionId);
49
+ await setProxyMode(Modes.transparent, sessionId);
43
50
  }
44
51
  var playwrightProxy = {
45
52
  /**
@@ -58,7 +65,7 @@ var playwrightProxy = {
58
65
  */
59
66
  async after(testInfo) {
60
67
  const sessionId = generateSessionId(testInfo);
61
- await setProxyMode("transparent", sessionId);
68
+ await setProxyMode(Modes.transparent, sessionId);
62
69
  }
63
70
  };
64
71
 
@@ -1,50 +1,3 @@
1
- import { TestInfo } from '@playwright/test';
2
-
3
- type ProxyMode = 'recording' | 'replay' | 'transparent';
4
- type PlaywrightTestInfo = Pick<TestInfo, 'title'>;
5
- /**
6
- * Set the proxy mode for a given session
7
- * @param mode - The proxy mode to set (recording, replay, transparent)
8
- * @param sessionId - Unique identifier for the session
9
- * @param timeout - Optional timeout in milliseconds
10
- */
11
- declare function setProxyMode(mode: ProxyMode, sessionId: string, timeout?: number): Promise<void>;
12
- /**
13
- * Generate a session ID from test info
14
- * @param testInfo - Playwright test info object
15
- */
16
- declare function generateSessionId(testInfo: PlaywrightTestInfo): string;
17
- /**
18
- * Start recording for a test
19
- * @param testInfo - Playwright test info object
20
- */
21
- declare function startRecording(testInfo: PlaywrightTestInfo): Promise<void>;
22
- /**
23
- * Start replay for a test
24
- * @param testInfo - Playwright test info object
25
- */
26
- declare function startReplay(testInfo: PlaywrightTestInfo): Promise<void>;
27
- /**
28
- * Stop recording/replay and return to transparent mode
29
- * @param testInfo - Playwright test info object
30
- */
31
- declare function stopProxy(testInfo: PlaywrightTestInfo): Promise<void>;
32
- /**
33
- * Playwright test fixture helper for managing proxy mode
34
- * Use this in beforeEach/afterEach hooks
35
- */
36
- declare const playwrightProxy: {
37
- /**
38
- * Setup before test - sets the proxy mode
39
- * @param testInfo - Playwright test info object
40
- * @param mode - The proxy mode to use for this test
41
- */
42
- before(testInfo: PlaywrightTestInfo, mode: ProxyMode): Promise<void>;
43
- /**
44
- * Cleanup after test - returns to transparent mode
45
- * @param testInfo - Playwright test info object
46
- */
47
- after(testInfo: PlaywrightTestInfo): Promise<void>;
48
- };
49
-
50
- export { type PlaywrightTestInfo, type ProxyMode, generateSessionId, playwrightProxy, setProxyMode, startRecording, startReplay, stopProxy };
1
+ import '@playwright/test';
2
+ export { P as PlaywrightTestInfo, g as generateSessionId, p as playwrightProxy, s as setProxyMode, b as startRecording, c as startReplay, d as stopProxy } from '../index-DfFpm8mB.cjs';
3
+ import 'node:http';
@@ -1,50 +1,3 @@
1
- import { TestInfo } from '@playwright/test';
2
-
3
- type ProxyMode = 'recording' | 'replay' | 'transparent';
4
- type PlaywrightTestInfo = Pick<TestInfo, 'title'>;
5
- /**
6
- * Set the proxy mode for a given session
7
- * @param mode - The proxy mode to set (recording, replay, transparent)
8
- * @param sessionId - Unique identifier for the session
9
- * @param timeout - Optional timeout in milliseconds
10
- */
11
- declare function setProxyMode(mode: ProxyMode, sessionId: string, timeout?: number): Promise<void>;
12
- /**
13
- * Generate a session ID from test info
14
- * @param testInfo - Playwright test info object
15
- */
16
- declare function generateSessionId(testInfo: PlaywrightTestInfo): string;
17
- /**
18
- * Start recording for a test
19
- * @param testInfo - Playwright test info object
20
- */
21
- declare function startRecording(testInfo: PlaywrightTestInfo): Promise<void>;
22
- /**
23
- * Start replay for a test
24
- * @param testInfo - Playwright test info object
25
- */
26
- declare function startReplay(testInfo: PlaywrightTestInfo): Promise<void>;
27
- /**
28
- * Stop recording/replay and return to transparent mode
29
- * @param testInfo - Playwright test info object
30
- */
31
- declare function stopProxy(testInfo: PlaywrightTestInfo): Promise<void>;
32
- /**
33
- * Playwright test fixture helper for managing proxy mode
34
- * Use this in beforeEach/afterEach hooks
35
- */
36
- declare const playwrightProxy: {
37
- /**
38
- * Setup before test - sets the proxy mode
39
- * @param testInfo - Playwright test info object
40
- * @param mode - The proxy mode to use for this test
41
- */
42
- before(testInfo: PlaywrightTestInfo, mode: ProxyMode): Promise<void>;
43
- /**
44
- * Cleanup after test - returns to transparent mode
45
- * @param testInfo - Playwright test info object
46
- */
47
- after(testInfo: PlaywrightTestInfo): Promise<void>;
48
- };
49
-
50
- export { type PlaywrightTestInfo, type ProxyMode, generateSessionId, playwrightProxy, setProxyMode, startRecording, startReplay, stopProxy };
1
+ import '@playwright/test';
2
+ export { P as PlaywrightTestInfo, g as generateSessionId, p as playwrightProxy, s as setProxyMode, b as startRecording, c as startReplay, d as stopProxy } from '../index-DfFpm8mB.js';
3
+ import 'node:http';
@@ -1,3 +1,10 @@
1
+ // src/types.ts
2
+ var Modes = {
3
+ transparent: "transparent",
4
+ record: "record",
5
+ replay: "replay"
6
+ };
7
+
1
8
  // src/playwright/index.ts
2
9
  var INTERNAL_API_URL = process.env.INTERNAL_API_URL || "http://localhost:8100";
3
10
  async function setProxyMode(mode, sessionId, timeout) {
@@ -29,15 +36,15 @@ function generateSessionId(testInfo) {
29
36
  }
30
37
  async function startRecording(testInfo) {
31
38
  const sessionId = generateSessionId(testInfo);
32
- await setProxyMode("recording", sessionId);
39
+ await setProxyMode(Modes.record, sessionId);
33
40
  }
34
41
  async function startReplay(testInfo) {
35
42
  const sessionId = generateSessionId(testInfo);
36
- await setProxyMode("replay", sessionId);
43
+ await setProxyMode(Modes.replay, sessionId);
37
44
  }
38
45
  async function stopProxy(testInfo) {
39
46
  const sessionId = generateSessionId(testInfo);
40
- await setProxyMode("transparent", sessionId);
47
+ await setProxyMode(Modes.transparent, sessionId);
41
48
  }
42
49
  var playwrightProxy = {
43
50
  /**
@@ -56,7 +63,7 @@ var playwrightProxy = {
56
63
  */
57
64
  async after(testInfo) {
58
65
  const sessionId = generateSessionId(testInfo);
59
- await setProxyMode("transparent", sessionId);
66
+ await setProxyMode(Modes.transparent, sessionId);
60
67
  }
61
68
  };
62
69
 
package/dist/proxy.js CHANGED
@@ -4,6 +4,7 @@ import fs from 'fs/promises';
4
4
  import http from 'http';
5
5
  import httpProxy from 'http-proxy';
6
6
  import { WebSocket, WebSocketServer } from 'ws';
7
+ import filenamify from 'filenamify';
7
8
 
8
9
  // src/cli.ts
9
10
  var DEFAULT_PORT = 8e3;
@@ -72,6 +73,24 @@ async function saveRecordingSession(recordingsDir2, session) {
72
73
  `Saved ${session.recordings.length} HTTP recordings and ${session.websocketRecordings?.length || 0} WebSocket recordings to ${filePath}`
73
74
  );
74
75
  }
76
+ var QUERY_HASH_LENGTH = 8;
77
+ function getReqID(req) {
78
+ const urlParts = req.url.split("?");
79
+ const pathname = urlParts[0];
80
+ const query = urlParts[1] || "";
81
+ const pathPart = pathname === "/" ? "root" : pathname.slice(1);
82
+ const normalizedPath = filenamify(pathPart, { replacement: "_" });
83
+ const queryHash = generateQueryHash(query);
84
+ const filename = `${req.method}_${normalizedPath}${queryHash}.json`;
85
+ return filenamify(filename, { replacement: "_" });
86
+ }
87
+ function generateQueryHash(query) {
88
+ if (!query) {
89
+ return "";
90
+ }
91
+ const hash = Buffer.from(query).toString("base64").replaceAll(/[^a-zA-Z0-9]/g, "").slice(0, Math.max(0, QUERY_HASH_LENGTH));
92
+ return `_${hash}`;
93
+ }
75
94
 
76
95
  // src/utils/httpHelpers.ts
77
96
  var CONTENT_TYPE_JSON = "application/json";
@@ -87,28 +106,6 @@ function sendJsonResponse(res, statusCode, data) {
87
106
  res.end(JSON.stringify(data));
88
107
  }
89
108
 
90
- // src/utils/requestKeyGenerator.ts
91
- var QUERY_HASH_LENGTH = 8;
92
- function generateRequestKey(req) {
93
- const urlParts = req.url.split("?");
94
- const pathname = urlParts[0];
95
- const query = urlParts[1] || "";
96
- const normalizedPath = normalizePathname(pathname);
97
- const queryHash = generateQueryHash(query);
98
- return `${req.method}_${normalizedPath}${queryHash}.json`;
99
- }
100
- function normalizePathname(pathname) {
101
- const normalized = pathname.replaceAll("/", "_").replace(/^_/, "");
102
- return normalized || "root";
103
- }
104
- function generateQueryHash(query) {
105
- if (!query) {
106
- return "";
107
- }
108
- const hash = Buffer.from(query).toString("base64").replaceAll(/[^a-zA-Z0-9]/g, "").slice(0, Math.max(0, QUERY_HASH_LENGTH));
109
- return `_${hash}`;
110
- }
111
-
112
109
  // src/ProxyServer.ts
113
110
  var ProxyServer = class {
114
111
  targets;
@@ -234,6 +231,7 @@ var ProxyServer = class {
234
231
  this.recordingId = null;
235
232
  this.replayId = null;
236
233
  this.currentSession = null;
234
+ clearTimeout(this.modeTimeout || 0);
237
235
  console.log("Switched to transparent mode");
238
236
  }
239
237
  switchToRecordMode(id) {
@@ -284,7 +282,7 @@ var ProxyServer = class {
284
282
  if (!this.currentSession) {
285
283
  return;
286
284
  }
287
- const key = generateRequestKey(req);
285
+ const key = getReqID(req);
288
286
  const record = {
289
287
  request: {
290
288
  method: req.method,
@@ -301,7 +299,7 @@ var ProxyServer = class {
301
299
  if (!this.currentSession) {
302
300
  return;
303
301
  }
304
- const key = generateRequestKey(req);
302
+ const key = getReqID(req);
305
303
  const record = this.currentSession.recordings.find((r) => r.key === key);
306
304
  if (!record) {
307
305
  console.error("Request record not found for response:", key);
@@ -323,7 +321,7 @@ var ProxyServer = class {
323
321
  });
324
322
  }
325
323
  async handleReplayRequest(req, res) {
326
- const key = generateRequestKey(req);
324
+ const key = getReqID(req);
327
325
  const filePath = getRecordingPath(this.recordingsDir, this.replayId);
328
326
  try {
329
327
  const session = await loadRecordingSession(filePath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "test-proxy-recorder",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "HTTP proxy server for recording and replaying network requests in testing. Works seamlessly with Playwright testing framework.",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
@@ -79,6 +79,7 @@
79
79
  },
80
80
  "dependencies": {
81
81
  "commander": "^12.0.0",
82
+ "filenamify": "^7.0.1",
82
83
  "http-proxy": "^1.18.1"
83
84
  },
84
85
  "peerDependencies": {