rust-http-playback-proxy 0.2.0-r1

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 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
@@ -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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"}
@@ -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"}
@@ -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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"}
@@ -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,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -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-r1",
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/ideamans/rust-http-playback-proxy.git",
43
+ "directory": "typescript"
44
+ },
45
+ "homepage": "https://github.com/ideamans/rust-http-playback-proxy#readme",
46
+ "bugs": {
47
+ "url": "https://github.com/ideamans/rust-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
+ }