rust-http-playback-proxy 0.2.0
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 +259 -0
- package/bin/darwin-amd64/http-playback-proxy +0 -0
- package/bin/darwin-arm64/http-playback-proxy +0 -0
- package/bin/linux-amd64/http-playback-proxy +0 -0
- package/bin/linux-arm64/http-playback-proxy +0 -0
- package/bin/windows-amd64/http-playback-proxy.exe +0 -0
- package/dist/binary.d.ts +43 -0
- package/dist/binary.d.ts.map +1 -0
- package/dist/binary.js +241 -0
- package/dist/binary.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/proxy.d.ts +80 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/proxy.js +389 -0
- package/dist/proxy.js.map +1 -0
- package/dist/types.d.ts +62 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +65 -0
package/README.md
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
# HTTP Playback Proxy - TypeScript/Node.js Wrapper
|
|
2
|
+
|
|
3
|
+
TypeScript/Node.js wrapper for the HTTP Playback Proxy Rust binary.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install http-playback-proxy
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Recording Mode
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { startRecording } from 'http-playback-proxy';
|
|
17
|
+
|
|
18
|
+
async function record() {
|
|
19
|
+
// Start recording proxy (with entry URL)
|
|
20
|
+
const proxy = await startRecording({
|
|
21
|
+
entryUrl: 'https://example.com',
|
|
22
|
+
port: 8080,
|
|
23
|
+
deviceType: 'mobile',
|
|
24
|
+
inventoryDir: './inventory',
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
console.log(`Recording proxy started on port ${proxy.port}`);
|
|
28
|
+
|
|
29
|
+
// Do your HTTP requests through the proxy (e.g., with a browser)
|
|
30
|
+
// ...
|
|
31
|
+
|
|
32
|
+
// Stop recording after some time (this saves the inventory)
|
|
33
|
+
setTimeout(async () => {
|
|
34
|
+
await proxy.stop();
|
|
35
|
+
|
|
36
|
+
// Load the recorded inventory
|
|
37
|
+
const inventory = await proxy.getInventory();
|
|
38
|
+
console.log(`Recorded ${inventory.resources.length} resources`);
|
|
39
|
+
}, 10000);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Example: Start recording without entry URL (manual browsing)
|
|
43
|
+
async function recordWithoutEntryURL() {
|
|
44
|
+
// All options are optional - uses defaults
|
|
45
|
+
const proxy = await startRecording({});
|
|
46
|
+
|
|
47
|
+
console.log(`Recording proxy started on port ${proxy.port}`);
|
|
48
|
+
console.log('Configure your browser to use proxy 127.0.0.1:8080');
|
|
49
|
+
console.log('Then browse to any website...');
|
|
50
|
+
|
|
51
|
+
// Stop after some time
|
|
52
|
+
setTimeout(async () => {
|
|
53
|
+
await proxy.stop();
|
|
54
|
+
}, 30000);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
record().catch(console.error);
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Playback Mode
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import { startPlayback } from 'http-playback-proxy';
|
|
64
|
+
|
|
65
|
+
async function playback() {
|
|
66
|
+
// Start playback proxy
|
|
67
|
+
const proxy = await startPlayback({
|
|
68
|
+
port: 8080,
|
|
69
|
+
inventoryDir: './inventory',
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
console.log(`Playback proxy started on port ${proxy.port}`);
|
|
73
|
+
|
|
74
|
+
// Do your HTTP requests through the proxy
|
|
75
|
+
// The proxy will replay the recorded responses with accurate timing
|
|
76
|
+
// ...
|
|
77
|
+
|
|
78
|
+
// Stop playback after some time
|
|
79
|
+
setTimeout(async () => {
|
|
80
|
+
await proxy.stop();
|
|
81
|
+
}, 10000);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
playback().catch(console.error);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Working with Inventory
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { loadInventory, getResourceContentPath } from 'http-playback-proxy';
|
|
91
|
+
|
|
92
|
+
async function analyzeInventory() {
|
|
93
|
+
// Load inventory
|
|
94
|
+
const inventory = await loadInventory('./inventory/inventory.json');
|
|
95
|
+
|
|
96
|
+
// Iterate through resources
|
|
97
|
+
for (const [i, resource] of inventory.resources.entries()) {
|
|
98
|
+
console.log(`Resource ${i}: ${resource.method} ${resource.url}`);
|
|
99
|
+
console.log(` TTFB: ${resource.ttfbMs} ms`);
|
|
100
|
+
if (resource.statusCode) {
|
|
101
|
+
console.log(` Status: ${resource.statusCode}`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Get content file path
|
|
105
|
+
if (resource.contentFilePath) {
|
|
106
|
+
const contentPath = getResourceContentPath('./inventory', resource);
|
|
107
|
+
console.log(` Content: ${contentPath}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
analyzeInventory().catch(console.error);
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## API Reference
|
|
116
|
+
|
|
117
|
+
### Types
|
|
118
|
+
|
|
119
|
+
#### `RecordingOptions`
|
|
120
|
+
```typescript
|
|
121
|
+
interface RecordingOptions {
|
|
122
|
+
entryUrl?: string; // Optional: Entry URL to start recording from
|
|
123
|
+
port?: number; // Optional: Port to listen on (default: 8080, will auto-search)
|
|
124
|
+
deviceType?: DeviceType; // Optional: 'desktop' or 'mobile' (default: 'mobile')
|
|
125
|
+
inventoryDir?: string; // Optional: Directory to save inventory (default: './inventory')
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
#### `PlaybackOptions`
|
|
130
|
+
```typescript
|
|
131
|
+
interface PlaybackOptions {
|
|
132
|
+
port?: number; // Optional: Port to listen on (default: 8080, will auto-search)
|
|
133
|
+
inventoryDir?: string; // Optional: Directory containing inventory (default: './inventory')
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
#### `Inventory`
|
|
138
|
+
```typescript
|
|
139
|
+
interface Inventory {
|
|
140
|
+
entryUrl?: string;
|
|
141
|
+
deviceType?: DeviceType;
|
|
142
|
+
resources: Resource[];
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
#### `Resource`
|
|
147
|
+
```typescript
|
|
148
|
+
interface Resource {
|
|
149
|
+
method: string;
|
|
150
|
+
url: string;
|
|
151
|
+
ttfbMs: number;
|
|
152
|
+
mbps?: number;
|
|
153
|
+
statusCode?: number;
|
|
154
|
+
errorMessage?: string;
|
|
155
|
+
rawHeaders?: Record<string, string>;
|
|
156
|
+
contentEncoding?: ContentEncodingType;
|
|
157
|
+
contentTypeMime?: string;
|
|
158
|
+
contentTypeCharset?: string;
|
|
159
|
+
contentFilePath?: string;
|
|
160
|
+
contentUtf8?: string;
|
|
161
|
+
contentBase64?: string;
|
|
162
|
+
minify?: boolean;
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Functions
|
|
167
|
+
|
|
168
|
+
#### `startRecording(options: RecordingOptions): Promise<Proxy>`
|
|
169
|
+
Starts a recording proxy.
|
|
170
|
+
|
|
171
|
+
#### `startPlayback(options: PlaybackOptions): Promise<Proxy>`
|
|
172
|
+
Starts a playback proxy.
|
|
173
|
+
|
|
174
|
+
#### `Proxy.stop(): Promise<void>`
|
|
175
|
+
Stops the proxy gracefully.
|
|
176
|
+
|
|
177
|
+
#### `Proxy.isRunning(): boolean`
|
|
178
|
+
Checks if the proxy is still running.
|
|
179
|
+
|
|
180
|
+
#### `Proxy.wait(): Promise<void>`
|
|
181
|
+
Waits for the proxy to exit.
|
|
182
|
+
|
|
183
|
+
#### `Proxy.getInventory(): Promise<Inventory>`
|
|
184
|
+
Loads the inventory for this proxy.
|
|
185
|
+
|
|
186
|
+
#### `loadInventory(path: string): Promise<Inventory>`
|
|
187
|
+
Loads an inventory from a JSON file.
|
|
188
|
+
|
|
189
|
+
#### `saveInventory(path: string, inventory: Inventory): Promise<void>`
|
|
190
|
+
Saves an inventory to a JSON file.
|
|
191
|
+
|
|
192
|
+
#### `getResourceContentPath(inventoryDir: string, resource: Resource): string`
|
|
193
|
+
Returns the full path to a resource's content file.
|
|
194
|
+
|
|
195
|
+
#### `getInventoryPath(inventoryDir: string): string`
|
|
196
|
+
Returns the path to the inventory.json file.
|
|
197
|
+
|
|
198
|
+
#### `ensureBinary(): Promise<void>`
|
|
199
|
+
Ensures the binary is available, downloading if necessary.
|
|
200
|
+
|
|
201
|
+
## Binary Distribution
|
|
202
|
+
|
|
203
|
+
### How Binaries are Located
|
|
204
|
+
|
|
205
|
+
The TypeScript wrapper searches for binaries in the following order:
|
|
206
|
+
|
|
207
|
+
1. **Package directory** (`node_modules/http-playback-proxy/bin/<platform>/`) - Production
|
|
208
|
+
- Binaries are bundled with the npm package via GitHub Actions
|
|
209
|
+
- Included in the `files` field of package.json
|
|
210
|
+
2. **Cache directory** (`~/.cache/http-playback-proxy/bin/<platform>/`) - Fallback
|
|
211
|
+
- Used if package binaries are not available
|
|
212
|
+
- Automatically downloaded on first use
|
|
213
|
+
- Can be customized via `HTTP_PLAYBACK_PROXY_CACHE_DIR` environment variable
|
|
214
|
+
|
|
215
|
+
### Package Contents
|
|
216
|
+
|
|
217
|
+
The npm package includes:
|
|
218
|
+
- `dist/` - Compiled TypeScript code
|
|
219
|
+
- `bin/` - Prebuilt binaries for all supported platforms
|
|
220
|
+
- `README.md` - Documentation
|
|
221
|
+
|
|
222
|
+
### First-Time Usage
|
|
223
|
+
|
|
224
|
+
The wrapper automatically ensures the binary is available:
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
import { startRecording } from 'http-playback-proxy';
|
|
228
|
+
|
|
229
|
+
async function main() {
|
|
230
|
+
// Automatically uses packaged binary or downloads if needed
|
|
231
|
+
const proxy = await startRecording({});
|
|
232
|
+
// ...
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
You can also explicitly ensure the binary is available:
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
import { ensureBinary } from 'http-playback-proxy';
|
|
240
|
+
|
|
241
|
+
await ensureBinary();
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
#### `checkBinaryExists(): boolean`
|
|
245
|
+
Checks if the binary exists locally.
|
|
246
|
+
|
|
247
|
+
#### `downloadBinary(version?: string): Promise<void>`
|
|
248
|
+
Downloads a specific version of the binary from GitHub Releases.
|
|
249
|
+
|
|
250
|
+
## Environment Variables
|
|
251
|
+
|
|
252
|
+
- `HTTP_PLAYBACK_PROXY_CACHE_DIR`: Custom cache directory for downloaded binaries
|
|
253
|
+
- `XDG_CACHE_HOME`: XDG cache directory (fallback)
|
|
254
|
+
|
|
255
|
+
If neither is set, binaries are cached in `~/.cache/http-playback-proxy`.
|
|
256
|
+
|
|
257
|
+
## License
|
|
258
|
+
|
|
259
|
+
MIT
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/binary.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the version of this TypeScript wrapper
|
|
3
|
+
*/
|
|
4
|
+
export declare function getVersion(): string;
|
|
5
|
+
/**
|
|
6
|
+
* Get the current platform identifier
|
|
7
|
+
*/
|
|
8
|
+
export declare function getPlatform(): string;
|
|
9
|
+
/**
|
|
10
|
+
* Get the binary file name for the current platform
|
|
11
|
+
*/
|
|
12
|
+
export declare function getBinaryName(): string;
|
|
13
|
+
/**
|
|
14
|
+
* Get the expected path to the binary relative to package root
|
|
15
|
+
*/
|
|
16
|
+
export declare function getBinaryPath(): string;
|
|
17
|
+
/**
|
|
18
|
+
* Get the cache directory for downloaded binaries
|
|
19
|
+
*/
|
|
20
|
+
export declare function getCacheDir(): string;
|
|
21
|
+
/**
|
|
22
|
+
* Get the package root directory
|
|
23
|
+
* In development: typescript/dist/ -> typescript/
|
|
24
|
+
* In production: node_modules/http-playback-proxy/dist/ -> node_modules/http-playback-proxy/
|
|
25
|
+
*/
|
|
26
|
+
export declare function getPackageRoot(): string;
|
|
27
|
+
/**
|
|
28
|
+
* Check if the binary exists
|
|
29
|
+
*/
|
|
30
|
+
export declare function checkBinaryExists(): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Download the binary from GitHub Releases
|
|
33
|
+
*/
|
|
34
|
+
export declare function downloadBinary(version?: string): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Ensure the binary is available, downloading if necessary
|
|
37
|
+
*/
|
|
38
|
+
export declare function ensureBinary(): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Get the full path to the binary
|
|
41
|
+
*/
|
|
42
|
+
export declare function getFullBinaryPath(): string;
|
|
43
|
+
//# sourceMappingURL=binary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binary.d.ts","sourceRoot":"","sources":["../src/binary.ts"],"names":[],"mappings":"AAYA;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAQnC;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAapC;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAGtC;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAepC;AAED;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAoBvC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAc3C;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA8DpE;AAED;;GAEG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAYlD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAc1C"}
|
package/dist/binary.js
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.getVersion = getVersion;
|
|
37
|
+
exports.getPlatform = getPlatform;
|
|
38
|
+
exports.getBinaryName = getBinaryName;
|
|
39
|
+
exports.getBinaryPath = getBinaryPath;
|
|
40
|
+
exports.getCacheDir = getCacheDir;
|
|
41
|
+
exports.getPackageRoot = getPackageRoot;
|
|
42
|
+
exports.checkBinaryExists = checkBinaryExists;
|
|
43
|
+
exports.downloadBinary = downloadBinary;
|
|
44
|
+
exports.ensureBinary = ensureBinary;
|
|
45
|
+
exports.getFullBinaryPath = getFullBinaryPath;
|
|
46
|
+
const path = __importStar(require("path"));
|
|
47
|
+
const fs = __importStar(require("fs"));
|
|
48
|
+
const os = __importStar(require("os"));
|
|
49
|
+
const https = __importStar(require("https"));
|
|
50
|
+
const fs_1 = require("fs");
|
|
51
|
+
const tar = __importStar(require("tar"));
|
|
52
|
+
const GITHUB_USER = 'pagespeed-quest';
|
|
53
|
+
const GITHUB_REPO = 'http-playback-proxy';
|
|
54
|
+
const BASE_URL = `https://github.com/${GITHUB_USER}/${GITHUB_REPO}/releases/download`;
|
|
55
|
+
/**
|
|
56
|
+
* Get the version of this TypeScript wrapper
|
|
57
|
+
*/
|
|
58
|
+
function getVersion() {
|
|
59
|
+
try {
|
|
60
|
+
const packageJsonPath = path.join(__dirname, '..', 'package.json');
|
|
61
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
62
|
+
return packageJson.version;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return '0.0.0';
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Get the current platform identifier
|
|
70
|
+
*/
|
|
71
|
+
function getPlatform() {
|
|
72
|
+
const platform = process.platform;
|
|
73
|
+
const arch = process.arch;
|
|
74
|
+
if (platform === 'darwin') {
|
|
75
|
+
return arch === 'arm64' ? 'darwin-arm64' : 'darwin-amd64';
|
|
76
|
+
}
|
|
77
|
+
else if (platform === 'linux') {
|
|
78
|
+
return arch === 'arm64' ? 'linux-arm64' : 'linux-amd64';
|
|
79
|
+
}
|
|
80
|
+
else if (platform === 'win32') {
|
|
81
|
+
return 'windows-amd64';
|
|
82
|
+
}
|
|
83
|
+
throw new Error(`Unsupported platform: ${platform}-${arch}`);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Get the binary file name for the current platform
|
|
87
|
+
*/
|
|
88
|
+
function getBinaryName() {
|
|
89
|
+
return process.platform === 'win32' ? 'http-playback-proxy.exe' : 'http-playback-proxy';
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get the expected path to the binary relative to package root
|
|
93
|
+
*/
|
|
94
|
+
function getBinaryPath() {
|
|
95
|
+
const platform = getPlatform();
|
|
96
|
+
return path.join('bin', platform, getBinaryName());
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Get the cache directory for downloaded binaries
|
|
100
|
+
*/
|
|
101
|
+
function getCacheDir() {
|
|
102
|
+
// Check environment variable first
|
|
103
|
+
const envCache = process.env.HTTP_PLAYBACK_PROXY_CACHE_DIR;
|
|
104
|
+
if (envCache) {
|
|
105
|
+
return envCache;
|
|
106
|
+
}
|
|
107
|
+
// Try XDG_CACHE_HOME
|
|
108
|
+
const xdgCache = process.env.XDG_CACHE_HOME;
|
|
109
|
+
if (xdgCache) {
|
|
110
|
+
return path.join(xdgCache, 'http-playback-proxy');
|
|
111
|
+
}
|
|
112
|
+
// Fall back to user's home directory
|
|
113
|
+
return path.join(os.homedir(), '.cache', 'http-playback-proxy');
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Get the package root directory
|
|
117
|
+
* In development: typescript/dist/ -> typescript/
|
|
118
|
+
* In production: node_modules/http-playback-proxy/dist/ -> node_modules/http-playback-proxy/
|
|
119
|
+
*/
|
|
120
|
+
function getPackageRoot() {
|
|
121
|
+
// __dirname in compiled code (dist/) points to dist/
|
|
122
|
+
// So ../ goes to package root
|
|
123
|
+
const packageRoot = path.join(__dirname, '..');
|
|
124
|
+
// Verify this is actually the package root by checking for package.json
|
|
125
|
+
const packageJsonPath = path.join(packageRoot, 'package.json');
|
|
126
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
127
|
+
try {
|
|
128
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
129
|
+
if (packageJson.name === 'http-playback-proxy') {
|
|
130
|
+
return packageRoot;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// Fall through to return default path
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// Fallback to simple relative path
|
|
138
|
+
return packageRoot;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Check if the binary exists
|
|
142
|
+
*/
|
|
143
|
+
function checkBinaryExists() {
|
|
144
|
+
// Check package directory first
|
|
145
|
+
const packagePath = path.join(getPackageRoot(), getBinaryPath());
|
|
146
|
+
if (fs.existsSync(packagePath)) {
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
// Check cache directory
|
|
150
|
+
const cachePath = path.join(getCacheDir(), getBinaryPath());
|
|
151
|
+
if (fs.existsSync(cachePath)) {
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Download the binary from GitHub Releases
|
|
158
|
+
*/
|
|
159
|
+
async function downloadBinary(version) {
|
|
160
|
+
const ver = version || getVersion();
|
|
161
|
+
const platform = getPlatform();
|
|
162
|
+
const archiveName = `http-playback-proxy-v${ver}-${platform}.tar.gz`;
|
|
163
|
+
const url = `${BASE_URL}/v${ver}/${archiveName}`;
|
|
164
|
+
// Try to download to cache directory
|
|
165
|
+
const cacheDir = getCacheDir();
|
|
166
|
+
let targetDir = cacheDir;
|
|
167
|
+
try {
|
|
168
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
169
|
+
}
|
|
170
|
+
catch (err) {
|
|
171
|
+
console.warn(`Could not create cache directory, using package directory: ${err}`);
|
|
172
|
+
targetDir = getPackageRoot();
|
|
173
|
+
}
|
|
174
|
+
console.log(`Downloading http-playback-proxy binary for ${platform}...`);
|
|
175
|
+
console.log(`URL: ${url}`);
|
|
176
|
+
console.log(`Target: ${targetDir}`);
|
|
177
|
+
// Download the tar.gz archive
|
|
178
|
+
const tmpFile = path.join(os.tmpdir(), archiveName);
|
|
179
|
+
const tmpStream = (0, fs_1.createWriteStream)(tmpFile);
|
|
180
|
+
await new Promise((resolve, reject) => {
|
|
181
|
+
https.get(url, (response) => {
|
|
182
|
+
if (response.statusCode !== 200) {
|
|
183
|
+
reject(new Error(`Failed to download binary: HTTP ${response.statusCode}`));
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
response.pipe(tmpStream);
|
|
187
|
+
tmpStream.on('finish', () => {
|
|
188
|
+
tmpStream.close();
|
|
189
|
+
resolve();
|
|
190
|
+
});
|
|
191
|
+
tmpStream.on('error', reject);
|
|
192
|
+
}).on('error', reject);
|
|
193
|
+
});
|
|
194
|
+
// Extract the tar.gz archive
|
|
195
|
+
const binDir = path.join(targetDir, 'bin', platform);
|
|
196
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
197
|
+
await tar.extract({
|
|
198
|
+
file: tmpFile,
|
|
199
|
+
cwd: binDir,
|
|
200
|
+
});
|
|
201
|
+
// Make binary executable on Unix-like systems
|
|
202
|
+
if (process.platform !== 'win32') {
|
|
203
|
+
const binaryPath = path.join(binDir, getBinaryName());
|
|
204
|
+
fs.chmodSync(binaryPath, 0o755);
|
|
205
|
+
}
|
|
206
|
+
// Clean up temp file
|
|
207
|
+
fs.unlinkSync(tmpFile);
|
|
208
|
+
console.log(`Successfully downloaded and extracted binary to ${targetDir}`);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Ensure the binary is available, downloading if necessary
|
|
212
|
+
*/
|
|
213
|
+
async function ensureBinary() {
|
|
214
|
+
if (checkBinaryExists()) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
console.log('Pre-built binary not found. Attempting to download from GitHub Releases...');
|
|
218
|
+
try {
|
|
219
|
+
await downloadBinary();
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
throw new Error(`Failed to download binary v${getVersion()}: ${err}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Get the full path to the binary
|
|
227
|
+
*/
|
|
228
|
+
function getFullBinaryPath() {
|
|
229
|
+
// Check package directory first
|
|
230
|
+
const packagePath = path.join(getPackageRoot(), getBinaryPath());
|
|
231
|
+
if (fs.existsSync(packagePath)) {
|
|
232
|
+
return packagePath;
|
|
233
|
+
}
|
|
234
|
+
// Check cache directory
|
|
235
|
+
const cachePath = path.join(getCacheDir(), getBinaryPath());
|
|
236
|
+
if (fs.existsSync(cachePath)) {
|
|
237
|
+
return cachePath;
|
|
238
|
+
}
|
|
239
|
+
throw new Error('Binary not found, please call ensureBinary() first');
|
|
240
|
+
}
|
|
241
|
+
//# sourceMappingURL=binary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binary.js","sourceRoot":"","sources":["../src/binary.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP Playback Proxy - TypeScript/Node.js Wrapper
|
|
3
|
+
*
|
|
4
|
+
* A wrapper for the HTTP Playback Proxy Rust binary that provides:
|
|
5
|
+
* - Recording HTTP traffic with accurate timing
|
|
6
|
+
* - Playing back recorded traffic with precise timing control
|
|
7
|
+
* - Inventory management for recorded resources
|
|
8
|
+
*/
|
|
9
|
+
export { Proxy, startRecording, startPlayback, loadInventory, saveInventory, getResourceContentPath, getInventoryPath, } from './proxy';
|
|
10
|
+
export { ensureBinary, checkBinaryExists, downloadBinary, getVersion, getPlatform, getBinaryName, } from './binary';
|
|
11
|
+
export type { DeviceType, ContentEncodingType, HttpHeaders, Resource, Inventory, RecordingOptions, PlaybackOptions, ProxyMode, } from './types';
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,KAAK,EACL,cAAc,EACd,aAAa,EACb,aAAa,EACb,aAAa,EACb,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACd,UAAU,EACV,WAAW,EACX,aAAa,GACd,MAAM,UAAU,CAAC;AAElB,YAAY,EACV,UAAU,EACV,mBAAmB,EACnB,WAAW,EACX,QAAQ,EACR,SAAS,EACT,gBAAgB,EAChB,eAAe,EACf,SAAS,GACV,MAAM,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* HTTP Playback Proxy - TypeScript/Node.js Wrapper
|
|
4
|
+
*
|
|
5
|
+
* A wrapper for the HTTP Playback Proxy Rust binary that provides:
|
|
6
|
+
* - Recording HTTP traffic with accurate timing
|
|
7
|
+
* - Playing back recorded traffic with precise timing control
|
|
8
|
+
* - Inventory management for recorded resources
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.getBinaryName = exports.getPlatform = exports.getVersion = exports.downloadBinary = exports.checkBinaryExists = exports.ensureBinary = exports.getInventoryPath = exports.getResourceContentPath = exports.saveInventory = exports.loadInventory = exports.startPlayback = exports.startRecording = exports.Proxy = void 0;
|
|
12
|
+
var proxy_1 = require("./proxy");
|
|
13
|
+
Object.defineProperty(exports, "Proxy", { enumerable: true, get: function () { return proxy_1.Proxy; } });
|
|
14
|
+
Object.defineProperty(exports, "startRecording", { enumerable: true, get: function () { return proxy_1.startRecording; } });
|
|
15
|
+
Object.defineProperty(exports, "startPlayback", { enumerable: true, get: function () { return proxy_1.startPlayback; } });
|
|
16
|
+
Object.defineProperty(exports, "loadInventory", { enumerable: true, get: function () { return proxy_1.loadInventory; } });
|
|
17
|
+
Object.defineProperty(exports, "saveInventory", { enumerable: true, get: function () { return proxy_1.saveInventory; } });
|
|
18
|
+
Object.defineProperty(exports, "getResourceContentPath", { enumerable: true, get: function () { return proxy_1.getResourceContentPath; } });
|
|
19
|
+
Object.defineProperty(exports, "getInventoryPath", { enumerable: true, get: function () { return proxy_1.getInventoryPath; } });
|
|
20
|
+
var binary_1 = require("./binary");
|
|
21
|
+
Object.defineProperty(exports, "ensureBinary", { enumerable: true, get: function () { return binary_1.ensureBinary; } });
|
|
22
|
+
Object.defineProperty(exports, "checkBinaryExists", { enumerable: true, get: function () { return binary_1.checkBinaryExists; } });
|
|
23
|
+
Object.defineProperty(exports, "downloadBinary", { enumerable: true, get: function () { return binary_1.downloadBinary; } });
|
|
24
|
+
Object.defineProperty(exports, "getVersion", { enumerable: true, get: function () { return binary_1.getVersion; } });
|
|
25
|
+
Object.defineProperty(exports, "getPlatform", { enumerable: true, get: function () { return binary_1.getPlatform; } });
|
|
26
|
+
Object.defineProperty(exports, "getBinaryName", { enumerable: true, get: function () { return binary_1.getBinaryName; } });
|
|
27
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAEH,iCAQiB;AAPf,8FAAA,KAAK,OAAA;AACL,uGAAA,cAAc,OAAA;AACd,sGAAA,aAAa,OAAA;AACb,sGAAA,aAAa,OAAA;AACb,sGAAA,aAAa,OAAA;AACb,+GAAA,sBAAsB,OAAA;AACtB,yGAAA,gBAAgB,OAAA;AAGlB,mCAOkB;AANhB,sGAAA,YAAY,OAAA;AACZ,2GAAA,iBAAiB,OAAA;AACjB,wGAAA,cAAc,OAAA;AACd,oGAAA,UAAU,OAAA;AACV,qGAAA,WAAW,OAAA;AACX,uGAAA,aAAa,OAAA"}
|
package/dist/proxy.d.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { ChildProcess } from 'child_process';
|
|
2
|
+
import type { ProxyMode, RecordingOptions, PlaybackOptions, Inventory } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Represents a running proxy instance
|
|
5
|
+
*/
|
|
6
|
+
export declare class Proxy {
|
|
7
|
+
readonly mode: ProxyMode;
|
|
8
|
+
readonly inventoryDir: string;
|
|
9
|
+
readonly entryUrl?: string;
|
|
10
|
+
readonly deviceType?: string;
|
|
11
|
+
private _port;
|
|
12
|
+
private _controlPort?;
|
|
13
|
+
private process?;
|
|
14
|
+
constructor(mode: ProxyMode, port: number, inventoryDir: string, controlPort?: number, entryUrl?: string, deviceType?: string);
|
|
15
|
+
/**
|
|
16
|
+
* Get the actual port number (may differ from requested port if 0 was used)
|
|
17
|
+
*/
|
|
18
|
+
get port(): number;
|
|
19
|
+
/**
|
|
20
|
+
* Get the control API port number (if enabled)
|
|
21
|
+
*/
|
|
22
|
+
get controlPort(): number | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Update the port number (used internally when OS assigns a port)
|
|
25
|
+
*/
|
|
26
|
+
updatePort(port: number): void;
|
|
27
|
+
/**
|
|
28
|
+
* Update the control port number (used internally when OS assigns a port)
|
|
29
|
+
*/
|
|
30
|
+
updateControlPort(controlPort: number): void;
|
|
31
|
+
/**
|
|
32
|
+
* Set the child process
|
|
33
|
+
*/
|
|
34
|
+
setProcess(proc: ChildProcess): void;
|
|
35
|
+
/**
|
|
36
|
+
* Stop the proxy gracefully
|
|
37
|
+
* Sends shutdown request via control API if available, otherwise uses SIGTERM
|
|
38
|
+
*/
|
|
39
|
+
stop(): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Check if the proxy is still running
|
|
42
|
+
*/
|
|
43
|
+
isRunning(): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Wait for the proxy to exit
|
|
46
|
+
*/
|
|
47
|
+
wait(): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Load the inventory for this proxy
|
|
50
|
+
* This is useful after recording is complete
|
|
51
|
+
*/
|
|
52
|
+
getInventory(): Promise<Inventory>;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Start a recording proxy
|
|
56
|
+
*/
|
|
57
|
+
export declare function startRecording(options: RecordingOptions): Promise<Proxy>;
|
|
58
|
+
/**
|
|
59
|
+
* Start a playback proxy
|
|
60
|
+
*/
|
|
61
|
+
export declare function startPlayback(options: PlaybackOptions): Promise<Proxy>;
|
|
62
|
+
/**
|
|
63
|
+
* Load an inventory from a JSON file
|
|
64
|
+
*/
|
|
65
|
+
export declare function loadInventory(inventoryPath: string): Promise<Inventory>;
|
|
66
|
+
/**
|
|
67
|
+
* Save an inventory to a JSON file
|
|
68
|
+
*/
|
|
69
|
+
export declare function saveInventory(inventoryPath: string, inventory: Inventory): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Get the full path to a resource's content file
|
|
72
|
+
*/
|
|
73
|
+
export declare function getResourceContentPath(inventoryDir: string, resource: {
|
|
74
|
+
contentFilePath?: string;
|
|
75
|
+
}): string;
|
|
76
|
+
/**
|
|
77
|
+
* Get the path to the inventory.json file
|
|
78
|
+
*/
|
|
79
|
+
export declare function getInventoryPath(inventoryDir: string): string;
|
|
80
|
+
//# sourceMappingURL=proxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,YAAY,EAAE,MAAM,eAAe,CAAC;AAIpD,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEvF;;GAEG;AACH,qBAAa,KAAK;IAChB,SAAgB,IAAI,EAAE,SAAS,CAAC;IAChC,SAAgB,YAAY,EAAE,MAAM,CAAC;IACrC,SAAgB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClC,SAAgB,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,YAAY,CAAC,CAAS;IAC9B,OAAO,CAAC,OAAO,CAAC,CAAe;gBAG7B,IAAI,EAAE,SAAS,EACf,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,WAAW,CAAC,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,MAAM,EACjB,UAAU,CAAC,EAAE,MAAM;IAUrB;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;OAEG;IACH,IAAI,WAAW,IAAI,MAAM,GAAG,SAAS,CAEpC;IAED;;OAEG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI9B;;OAEG;IACH,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAI5C;;OAEG;IACH,UAAU,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI;IAIpC;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAqE3B;;OAEG;IACH,SAAS,IAAI,OAAO;IAcpB;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB3B;;;OAGG;IACG,YAAY,IAAI,OAAO,CAAC,SAAS,CAAC;CAIzC;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,KAAK,CAAC,CA6F9E;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,KAAK,CAAC,CA0F5E;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAG7E;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAG9F;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE;IAAE,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAK3G;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAE7D"}
|
package/dist/proxy.js
ADDED
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.Proxy = void 0;
|
|
37
|
+
exports.startRecording = startRecording;
|
|
38
|
+
exports.startPlayback = startPlayback;
|
|
39
|
+
exports.loadInventory = loadInventory;
|
|
40
|
+
exports.saveInventory = saveInventory;
|
|
41
|
+
exports.getResourceContentPath = getResourceContentPath;
|
|
42
|
+
exports.getInventoryPath = getInventoryPath;
|
|
43
|
+
const child_process_1 = require("child_process");
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const binary_1 = require("./binary");
|
|
47
|
+
/**
|
|
48
|
+
* Represents a running proxy instance
|
|
49
|
+
*/
|
|
50
|
+
class Proxy {
|
|
51
|
+
constructor(mode, port, inventoryDir, controlPort, entryUrl, deviceType) {
|
|
52
|
+
this.mode = mode;
|
|
53
|
+
this._port = port;
|
|
54
|
+
this._controlPort = controlPort;
|
|
55
|
+
this.inventoryDir = inventoryDir;
|
|
56
|
+
this.entryUrl = entryUrl;
|
|
57
|
+
this.deviceType = deviceType;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Get the actual port number (may differ from requested port if 0 was used)
|
|
61
|
+
*/
|
|
62
|
+
get port() {
|
|
63
|
+
return this._port;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Get the control API port number (if enabled)
|
|
67
|
+
*/
|
|
68
|
+
get controlPort() {
|
|
69
|
+
return this._controlPort;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Update the port number (used internally when OS assigns a port)
|
|
73
|
+
*/
|
|
74
|
+
updatePort(port) {
|
|
75
|
+
this._port = port;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Update the control port number (used internally when OS assigns a port)
|
|
79
|
+
*/
|
|
80
|
+
updateControlPort(controlPort) {
|
|
81
|
+
this._controlPort = controlPort;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Set the child process
|
|
85
|
+
*/
|
|
86
|
+
setProcess(proc) {
|
|
87
|
+
this.process = proc;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Stop the proxy gracefully
|
|
91
|
+
* Sends shutdown request via control API if available, otherwise uses SIGTERM
|
|
92
|
+
*/
|
|
93
|
+
async stop() {
|
|
94
|
+
if (!this.process) {
|
|
95
|
+
throw new Error('Proxy is not running');
|
|
96
|
+
}
|
|
97
|
+
return new Promise((resolve, reject) => {
|
|
98
|
+
if (!this.process) {
|
|
99
|
+
reject(new Error('Proxy is not running'));
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
// Set up timeout
|
|
103
|
+
const timeout = setTimeout(() => {
|
|
104
|
+
if (this.process) {
|
|
105
|
+
this.process.kill('SIGKILL');
|
|
106
|
+
reject(new Error('Proxy did not stop gracefully, killed forcefully'));
|
|
107
|
+
}
|
|
108
|
+
}, 10000);
|
|
109
|
+
// Listen for exit
|
|
110
|
+
this.process.once('exit', (code, signal) => {
|
|
111
|
+
clearTimeout(timeout);
|
|
112
|
+
// Exit code 0 is expected for clean shutdown
|
|
113
|
+
// Also accept null exit code and SIGINT signal
|
|
114
|
+
if (code === 0 || code === null || signal === 'SIGINT') {
|
|
115
|
+
resolve();
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
reject(new Error(`Proxy exited with code ${code} signal ${signal}`));
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
// Send shutdown request via control API if enabled
|
|
122
|
+
if (this._controlPort) {
|
|
123
|
+
const http = require('http');
|
|
124
|
+
const req = http.request({
|
|
125
|
+
hostname: '127.0.0.1',
|
|
126
|
+
port: this._controlPort,
|
|
127
|
+
path: '/_shutdown',
|
|
128
|
+
method: 'POST',
|
|
129
|
+
}, (res) => {
|
|
130
|
+
// Response received, proxy should be shutting down
|
|
131
|
+
res.resume(); // Consume response
|
|
132
|
+
});
|
|
133
|
+
req.on('error', (err) => {
|
|
134
|
+
// If HTTP request fails, fall back to SIGTERM
|
|
135
|
+
console.warn(`HTTP shutdown failed: ${err.message}, falling back to SIGTERM`);
|
|
136
|
+
try {
|
|
137
|
+
this.process?.kill('SIGTERM');
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// Ignore if already dead
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
req.end();
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
// No control port, use SIGTERM directly
|
|
147
|
+
try {
|
|
148
|
+
this.process.kill('SIGTERM');
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// Ignore if already dead
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Check if the proxy is still running
|
|
158
|
+
*/
|
|
159
|
+
isRunning() {
|
|
160
|
+
if (!this.process) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
// Check if process is still alive
|
|
164
|
+
try {
|
|
165
|
+
process.kill(this.process.pid, 0);
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Wait for the proxy to exit
|
|
174
|
+
*/
|
|
175
|
+
async wait() {
|
|
176
|
+
if (!this.process) {
|
|
177
|
+
throw new Error('Proxy is not running');
|
|
178
|
+
}
|
|
179
|
+
return new Promise((resolve, reject) => {
|
|
180
|
+
this.process.once('exit', (code) => {
|
|
181
|
+
if (code === 0) {
|
|
182
|
+
resolve();
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
reject(new Error(`Proxy exited with code ${code}`));
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Load the inventory for this proxy
|
|
192
|
+
* This is useful after recording is complete
|
|
193
|
+
*/
|
|
194
|
+
async getInventory() {
|
|
195
|
+
const inventoryPath = path.join(this.inventoryDir, 'inventory.json');
|
|
196
|
+
return loadInventory(inventoryPath);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
exports.Proxy = Proxy;
|
|
200
|
+
/**
|
|
201
|
+
* Start a recording proxy
|
|
202
|
+
*/
|
|
203
|
+
async function startRecording(options) {
|
|
204
|
+
await (0, binary_1.ensureBinary)();
|
|
205
|
+
const binaryPath = (0, binary_1.getFullBinaryPath)();
|
|
206
|
+
// Set defaults to match CLI behavior
|
|
207
|
+
const port = options.port !== undefined ? options.port : 18080;
|
|
208
|
+
const deviceType = options.deviceType || 'mobile';
|
|
209
|
+
const inventoryDir = options.inventoryDir || './inventory';
|
|
210
|
+
// Build command
|
|
211
|
+
const args = ['recording'];
|
|
212
|
+
// Add entry URL if provided
|
|
213
|
+
if (options.entryUrl) {
|
|
214
|
+
args.push(options.entryUrl);
|
|
215
|
+
}
|
|
216
|
+
// Add port option (only if explicitly specified and not 0 or default)
|
|
217
|
+
if (options.port !== undefined && options.port !== 0 && options.port !== 18080) {
|
|
218
|
+
args.push('--port', port.toString());
|
|
219
|
+
}
|
|
220
|
+
// Add device type
|
|
221
|
+
args.push('--device', deviceType);
|
|
222
|
+
// Add inventory directory
|
|
223
|
+
args.push('--inventory', inventoryDir);
|
|
224
|
+
// Add control port if specified
|
|
225
|
+
const controlPort = options.controlPort;
|
|
226
|
+
if (controlPort !== undefined) {
|
|
227
|
+
args.push('--control-port', controlPort.toString());
|
|
228
|
+
}
|
|
229
|
+
// Start the process with piped stdout to capture port info
|
|
230
|
+
const spawnOptions = {
|
|
231
|
+
stdio: ['ignore', 'pipe', 'inherit'],
|
|
232
|
+
detached: false,
|
|
233
|
+
};
|
|
234
|
+
if (process.platform === 'win32') {
|
|
235
|
+
spawnOptions.windowsVerbatimArguments = false;
|
|
236
|
+
}
|
|
237
|
+
const proc = (0, child_process_1.spawn)(binaryPath, args, spawnOptions);
|
|
238
|
+
const proxy = new Proxy('recording', port, inventoryDir, controlPort, options.entryUrl, deviceType);
|
|
239
|
+
proxy.setProcess(proc);
|
|
240
|
+
// Capture stdout to extract actual port number when using port 0
|
|
241
|
+
return new Promise((resolve, reject) => {
|
|
242
|
+
let resolved = false;
|
|
243
|
+
const timeout = setTimeout(() => {
|
|
244
|
+
if (!resolved) {
|
|
245
|
+
resolved = true;
|
|
246
|
+
resolve(proxy);
|
|
247
|
+
}
|
|
248
|
+
}, 2000);
|
|
249
|
+
proc.stdout?.on('data', (data) => {
|
|
250
|
+
const output = data.toString();
|
|
251
|
+
// Look for "Recording proxy listening on 127.0.0.1:XXXXX" or "Playback proxy listening on 127.0.0.1:XXXXX"
|
|
252
|
+
const match = output.match(/proxy listening on (?:127\.0\.0\.1|0\.0\.0\.0):(\d+)/i);
|
|
253
|
+
if (match && match[1]) {
|
|
254
|
+
const actualPort = parseInt(match[1], 10);
|
|
255
|
+
proxy.updatePort(actualPort);
|
|
256
|
+
if (!resolved) {
|
|
257
|
+
resolved = true;
|
|
258
|
+
clearTimeout(timeout);
|
|
259
|
+
resolve(proxy);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
// Forward output to console
|
|
263
|
+
process.stdout.write(data);
|
|
264
|
+
});
|
|
265
|
+
proc.on('error', (err) => {
|
|
266
|
+
if (!resolved) {
|
|
267
|
+
resolved = true;
|
|
268
|
+
clearTimeout(timeout);
|
|
269
|
+
reject(err);
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
proc.on('exit', (code) => {
|
|
273
|
+
if (!resolved && code !== 0) {
|
|
274
|
+
resolved = true;
|
|
275
|
+
clearTimeout(timeout);
|
|
276
|
+
reject(new Error(`Proxy exited with code ${code}`));
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Start a playback proxy
|
|
283
|
+
*/
|
|
284
|
+
async function startPlayback(options) {
|
|
285
|
+
await (0, binary_1.ensureBinary)();
|
|
286
|
+
const binaryPath = (0, binary_1.getFullBinaryPath)();
|
|
287
|
+
// Set defaults
|
|
288
|
+
const port = options.port !== undefined ? options.port : 18080;
|
|
289
|
+
const inventoryDir = options.inventoryDir || './inventory';
|
|
290
|
+
// Verify inventory exists
|
|
291
|
+
const inventoryPath = getInventoryPath(inventoryDir);
|
|
292
|
+
if (!fs.existsSync(inventoryPath)) {
|
|
293
|
+
throw new Error(`Inventory file not found at ${inventoryPath}`);
|
|
294
|
+
}
|
|
295
|
+
// Build command
|
|
296
|
+
const args = ['playback'];
|
|
297
|
+
// Add port option (only if not default)
|
|
298
|
+
if (options.port !== undefined && options.port !== 18080) {
|
|
299
|
+
args.push('--port', port.toString());
|
|
300
|
+
}
|
|
301
|
+
// Add inventory directory
|
|
302
|
+
args.push('--inventory', inventoryDir);
|
|
303
|
+
// Add control port if specified
|
|
304
|
+
const controlPort = options.controlPort;
|
|
305
|
+
if (controlPort !== undefined) {
|
|
306
|
+
args.push('--control-port', controlPort.toString());
|
|
307
|
+
}
|
|
308
|
+
// Start the process with piped stdout to capture port info
|
|
309
|
+
const spawnOptions = {
|
|
310
|
+
stdio: ['ignore', 'pipe', 'inherit'],
|
|
311
|
+
detached: false,
|
|
312
|
+
};
|
|
313
|
+
if (process.platform === 'win32') {
|
|
314
|
+
spawnOptions.windowsVerbatimArguments = false;
|
|
315
|
+
}
|
|
316
|
+
const proc = (0, child_process_1.spawn)(binaryPath, args, spawnOptions);
|
|
317
|
+
const proxy = new Proxy('playback', port, inventoryDir, controlPort);
|
|
318
|
+
proxy.setProcess(proc);
|
|
319
|
+
// Capture stdout to extract actual port number when using port 0
|
|
320
|
+
return new Promise((resolve, reject) => {
|
|
321
|
+
let resolved = false;
|
|
322
|
+
const timeout = setTimeout(() => {
|
|
323
|
+
if (!resolved) {
|
|
324
|
+
resolved = true;
|
|
325
|
+
resolve(proxy);
|
|
326
|
+
}
|
|
327
|
+
}, 2000);
|
|
328
|
+
proc.stdout?.on('data', (data) => {
|
|
329
|
+
const output = data.toString();
|
|
330
|
+
// Look for "Playback proxy listening on 127.0.0.1:XXXXX"
|
|
331
|
+
const match = output.match(/proxy listening on (?:127\.0\.0\.1|0\.0\.0\.0):(\d+)/i);
|
|
332
|
+
if (match && match[1]) {
|
|
333
|
+
const actualPort = parseInt(match[1], 10);
|
|
334
|
+
proxy.updatePort(actualPort);
|
|
335
|
+
if (!resolved) {
|
|
336
|
+
resolved = true;
|
|
337
|
+
clearTimeout(timeout);
|
|
338
|
+
resolve(proxy);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
// Forward output to console
|
|
342
|
+
process.stdout.write(data);
|
|
343
|
+
});
|
|
344
|
+
proc.on('error', (err) => {
|
|
345
|
+
if (!resolved) {
|
|
346
|
+
resolved = true;
|
|
347
|
+
clearTimeout(timeout);
|
|
348
|
+
reject(err);
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
proc.on('exit', (code) => {
|
|
352
|
+
if (!resolved && code !== 0) {
|
|
353
|
+
resolved = true;
|
|
354
|
+
clearTimeout(timeout);
|
|
355
|
+
reject(new Error(`Proxy exited with code ${code}`));
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Load an inventory from a JSON file
|
|
362
|
+
*/
|
|
363
|
+
async function loadInventory(inventoryPath) {
|
|
364
|
+
const data = await fs.promises.readFile(inventoryPath, 'utf8');
|
|
365
|
+
return JSON.parse(data);
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Save an inventory to a JSON file
|
|
369
|
+
*/
|
|
370
|
+
async function saveInventory(inventoryPath, inventory) {
|
|
371
|
+
const data = JSON.stringify(inventory, null, 2);
|
|
372
|
+
await fs.promises.writeFile(inventoryPath, data, 'utf8');
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Get the full path to a resource's content file
|
|
376
|
+
*/
|
|
377
|
+
function getResourceContentPath(inventoryDir, resource) {
|
|
378
|
+
if (!resource.contentFilePath) {
|
|
379
|
+
return '';
|
|
380
|
+
}
|
|
381
|
+
return path.join(inventoryDir, resource.contentFilePath);
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Get the path to the inventory.json file
|
|
385
|
+
*/
|
|
386
|
+
function getInventoryPath(inventoryDir) {
|
|
387
|
+
return path.join(inventoryDir, 'inventory.json');
|
|
388
|
+
}
|
|
389
|
+
//# sourceMappingURL=proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device type for recording
|
|
3
|
+
*/
|
|
4
|
+
export type DeviceType = 'desktop' | 'mobile';
|
|
5
|
+
/**
|
|
6
|
+
* Content encoding type
|
|
7
|
+
*/
|
|
8
|
+
export type ContentEncodingType = 'gzip' | 'compress' | 'deflate' | 'br' | 'identity';
|
|
9
|
+
/**
|
|
10
|
+
* HTTP headers map
|
|
11
|
+
*/
|
|
12
|
+
export type HttpHeaders = Record<string, string>;
|
|
13
|
+
/**
|
|
14
|
+
* Represents a single HTTP resource in the inventory
|
|
15
|
+
*/
|
|
16
|
+
export interface Resource {
|
|
17
|
+
method: string;
|
|
18
|
+
url: string;
|
|
19
|
+
ttfbMs: number;
|
|
20
|
+
mbps?: number;
|
|
21
|
+
statusCode?: number;
|
|
22
|
+
errorMessage?: string;
|
|
23
|
+
rawHeaders?: HttpHeaders;
|
|
24
|
+
contentEncoding?: ContentEncodingType;
|
|
25
|
+
contentTypeMime?: string;
|
|
26
|
+
contentTypeCharset?: string;
|
|
27
|
+
contentFilePath?: string;
|
|
28
|
+
contentUtf8?: string;
|
|
29
|
+
contentBase64?: string;
|
|
30
|
+
minify?: boolean;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Represents the complete inventory of recorded resources
|
|
34
|
+
*/
|
|
35
|
+
export interface Inventory {
|
|
36
|
+
entryUrl?: string;
|
|
37
|
+
deviceType?: DeviceType;
|
|
38
|
+
resources: Resource[];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Options for starting a recording proxy
|
|
42
|
+
*/
|
|
43
|
+
export interface RecordingOptions {
|
|
44
|
+
entryUrl?: string;
|
|
45
|
+
port?: number;
|
|
46
|
+
deviceType?: DeviceType;
|
|
47
|
+
inventoryDir?: string;
|
|
48
|
+
controlPort?: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Options for starting a playback proxy
|
|
52
|
+
*/
|
|
53
|
+
export interface PlaybackOptions {
|
|
54
|
+
port?: number;
|
|
55
|
+
inventoryDir?: string;
|
|
56
|
+
controlPort?: number;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Proxy mode
|
|
60
|
+
*/
|
|
61
|
+
export type ProxyMode = 'recording' | 'playback';
|
|
62
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE9C;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,GAAG,IAAI,GAAG,UAAU,CAAC;AAEtF;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,WAAW,CAAC;IACzB,eAAe,CAAC,EAAE,mBAAmB,CAAC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,SAAS,EAAE,QAAQ,EAAE,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG,UAAU,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "rust-http-playback-proxy",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "HTTP traffic recording and playback proxy with precise timing control for PageSpeed optimization and performance testing",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"test": "npm run build && node --test dist/test/*.test.js",
|
|
18
|
+
"test:watch": "npm run build && node --test --watch dist/test/*.test.js",
|
|
19
|
+
"prepublishOnly": "npm run build"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"http",
|
|
23
|
+
"https",
|
|
24
|
+
"proxy",
|
|
25
|
+
"playback",
|
|
26
|
+
"recording",
|
|
27
|
+
"performance",
|
|
28
|
+
"testing",
|
|
29
|
+
"mitm",
|
|
30
|
+
"pagespeed",
|
|
31
|
+
"web-performance",
|
|
32
|
+
"timing",
|
|
33
|
+
"ttfb",
|
|
34
|
+
"network-simulation",
|
|
35
|
+
"replay",
|
|
36
|
+
"rust"
|
|
37
|
+
],
|
|
38
|
+
"author": "PageSpeed Quest",
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "https://github.com/pagespeed-quest/http-playback-proxy.git",
|
|
43
|
+
"directory": "typescript"
|
|
44
|
+
},
|
|
45
|
+
"homepage": "https://github.com/pagespeed-quest/http-playback-proxy#readme",
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/pagespeed-quest/http-playback-proxy/issues"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"tar": "^7.0.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/node": "^20.0.0",
|
|
54
|
+
"@types/tar": "^6.0.0",
|
|
55
|
+
"typescript": "^5.0.0"
|
|
56
|
+
},
|
|
57
|
+
"files": [
|
|
58
|
+
"dist",
|
|
59
|
+
"bin",
|
|
60
|
+
"README.md"
|
|
61
|
+
],
|
|
62
|
+
"engines": {
|
|
63
|
+
"node": ">=18.0.0"
|
|
64
|
+
}
|
|
65
|
+
}
|