@thaparoyal/replayapi 0.1.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 ADDED
@@ -0,0 +1,85 @@
1
+ # @replayapi/node
2
+
3
+ ReplayAPI SDK for Node.js — Automatically capture outgoing HTTP traffic for replay testing.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @replayapi/node
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```ts
14
+ import { replayApi } from '@replayapi/node'
15
+
16
+ // Initialize once at app startup
17
+ replayApi.init({
18
+ apiKey: process.env.REPLAY_API_KEY!,
19
+ environment: 'staging',
20
+ })
21
+
22
+ // That's it — all http, https, and fetch calls are now captured
23
+ ```
24
+
25
+ ## Configuration
26
+
27
+ ```ts
28
+ replayApi.init({
29
+ // Required
30
+ apiKey: 'rp_your_api_key',
31
+
32
+ // Optional
33
+ proxyUrl: 'http://localhost:8080', // Default proxy URL
34
+ environment: 'staging', // Environment label
35
+ sessionId: 'user-checkout-flow', // Group related requests
36
+
37
+ // Filter what gets captured
38
+ include: ['api.example.com', /\/api\/v\d+\//],
39
+ exclude: ['healthcheck', /\.internal\./],
40
+
41
+ // Debug
42
+ debug: true, // Enable verbose logging
43
+ disabled: false, // Disable without removing code
44
+ timeout: 30000, // Proxy request timeout (ms)
45
+ })
46
+ ```
47
+
48
+ ## API
49
+
50
+ ### `replayApi.init(config)`
51
+
52
+ Initialize the SDK. Patches `http.request`, `https.request`, and `fetch` globally.
53
+
54
+ ### `replayApi.stop()`
55
+
56
+ Stop capturing and restore original HTTP behavior.
57
+
58
+ ### `replayApi.isActive()`
59
+
60
+ Returns `true` if the SDK is currently intercepting traffic.
61
+
62
+ ### `replayApi.getStats()`
63
+
64
+ ```ts
65
+ const stats = replayApi.getStats()
66
+ // { totalCaptured: 42, totalSkipped: 3, totalErrors: 0, startedAt: Date }
67
+ ```
68
+
69
+ ## How It Works
70
+
71
+ 1. Your app makes an HTTP request: `GET https://api.example.com/users`
72
+ 2. The SDK rewrites it to: `GET http://proxy:8080/users` with headers:
73
+ - `X-Api-Key: rp_...` (your API key)
74
+ - `X-Replay-Target: https://api.example.com` (original target)
75
+ - `X-Replay-Env: staging` (environment)
76
+ 3. The proxy captures the traffic, forwards to the target, returns the response
77
+ 4. Your app receives the response as if the proxy wasn't there
78
+
79
+ ## Supported
80
+
81
+ - Node.js 18+ (native `fetch` support)
82
+ - `http.request` / `http.get`
83
+ - `https.request` / `https.get`
84
+ - `globalThis.fetch`
85
+ - Libraries that use these under the hood (axios, node-fetch, got, etc.)
@@ -0,0 +1,118 @@
1
+ /**
2
+ * ReplayAPI SDK Types
3
+ */
4
+ interface ReplayApiConfig {
5
+ /** Your ReplayAPI key (starts with rp_) */
6
+ apiKey: string;
7
+ /**
8
+ * Environment label: development, staging, production, etc.
9
+ * @default "development"
10
+ * Override via REPLAY_ENVIRONMENT env var or this option.
11
+ */
12
+ environment?: string;
13
+ /** Session ID to group related requests */
14
+ sessionId?: string;
15
+ /**
16
+ * Filter which outgoing requests are captured.
17
+ * - If provided, only requests matching at least one pattern are captured.
18
+ * - Patterns are matched against the full URL.
19
+ * - Supports strings (substring match) or RegExp.
20
+ * @example include: ['api.example.com', /\/api\/v[12]\//]
21
+ */
22
+ include?: Array<string | RegExp>;
23
+ /**
24
+ * Exclude specific URLs from capture.
25
+ * - Evaluated after `include` — if a URL matches both, it is excluded.
26
+ * - By default, the proxy URL itself is always excluded.
27
+ * @example exclude: ['healthcheck', /\/internal\//]
28
+ */
29
+ exclude?: Array<string | RegExp>;
30
+ /** Enable debug logging (default: false) */
31
+ debug?: boolean;
32
+ /** Disable the SDK without removing the init call (default: false) */
33
+ disabled?: boolean;
34
+ /**
35
+ * Request timeout in ms when routing through proxy (default: 30000).
36
+ * This only affects the proxy hop — the actual upstream timeout is unchanged.
37
+ */
38
+ timeout?: number;
39
+ }
40
+ interface ReplayApiInstance {
41
+ /** Manually stop intercepting. Call init() again to restart. */
42
+ stop(): void;
43
+ /** Check if the SDK is currently intercepting */
44
+ isActive(): boolean;
45
+ /** Get capture statistics */
46
+ getStats(): CaptureStats;
47
+ }
48
+ interface CaptureStats {
49
+ /** Total requests intercepted */
50
+ totalCaptured: number;
51
+ /** Requests skipped by include/exclude filters */
52
+ totalSkipped: number;
53
+ /** Requests that failed to route through proxy */
54
+ totalErrors: number;
55
+ /** Timestamp when SDK was initialized */
56
+ startedAt: Date;
57
+ }
58
+
59
+ /**
60
+ * @replayapi/node — ReplayAPI SDK for Node.js
61
+ *
62
+ * Automatically captures outgoing HTTP traffic and routes it through
63
+ * the ReplayAPI proxy for recording, analysis, and replay testing.
64
+ *
65
+ * @example
66
+ * ```ts
67
+ * import { replayApi } from '@replayapi/node'
68
+ *
69
+ * // Simplest — just pass your API key
70
+ * replayApi.init('rp_your_api_key')
71
+ *
72
+ * // Or with options
73
+ * replayApi.init({ apiKey: 'rp_your_api_key', environment: 'staging' })
74
+ *
75
+ * // All outgoing HTTP/fetch calls are now captured automatically
76
+ * ```
77
+ */
78
+
79
+ /**
80
+ * Initialize the ReplayAPI SDK.
81
+ *
82
+ * Call this once at application startup, before any outgoing HTTP requests.
83
+ * The SDK will automatically intercept `http.request`, `https.request`,
84
+ * and `fetch` to route traffic through the ReplayAPI proxy.
85
+ *
86
+ * @example
87
+ * ```ts
88
+ * // Just an API key
89
+ * replayApi.init('rp_your_api_key')
90
+ *
91
+ * // Or with options
92
+ * replayApi.init({ apiKey: process.env.REPLAY_API_KEY!, environment: 'staging' })
93
+ * ```
94
+ */
95
+ declare function init(configOrApiKey: ReplayApiConfig | string): ReplayApiInstance;
96
+ /**
97
+ * Stop the SDK and restore original HTTP/fetch behavior.
98
+ */
99
+ declare function stop(): void;
100
+ /**
101
+ * Check if the SDK is currently intercepting traffic.
102
+ */
103
+ declare function isActive(): boolean;
104
+ /**
105
+ * Get combined capture statistics from all interceptors.
106
+ */
107
+ declare function getStats(): CaptureStats;
108
+ /**
109
+ * The main ReplayAPI SDK instance.
110
+ */
111
+ declare const replayApi: {
112
+ init: typeof init;
113
+ stop: typeof stop;
114
+ isActive: typeof isActive;
115
+ getStats: typeof getStats;
116
+ };
117
+
118
+ export { type CaptureStats, type ReplayApiConfig, type ReplayApiInstance, getStats, init, isActive, replayApi, stop };
@@ -0,0 +1,118 @@
1
+ /**
2
+ * ReplayAPI SDK Types
3
+ */
4
+ interface ReplayApiConfig {
5
+ /** Your ReplayAPI key (starts with rp_) */
6
+ apiKey: string;
7
+ /**
8
+ * Environment label: development, staging, production, etc.
9
+ * @default "development"
10
+ * Override via REPLAY_ENVIRONMENT env var or this option.
11
+ */
12
+ environment?: string;
13
+ /** Session ID to group related requests */
14
+ sessionId?: string;
15
+ /**
16
+ * Filter which outgoing requests are captured.
17
+ * - If provided, only requests matching at least one pattern are captured.
18
+ * - Patterns are matched against the full URL.
19
+ * - Supports strings (substring match) or RegExp.
20
+ * @example include: ['api.example.com', /\/api\/v[12]\//]
21
+ */
22
+ include?: Array<string | RegExp>;
23
+ /**
24
+ * Exclude specific URLs from capture.
25
+ * - Evaluated after `include` — if a URL matches both, it is excluded.
26
+ * - By default, the proxy URL itself is always excluded.
27
+ * @example exclude: ['healthcheck', /\/internal\//]
28
+ */
29
+ exclude?: Array<string | RegExp>;
30
+ /** Enable debug logging (default: false) */
31
+ debug?: boolean;
32
+ /** Disable the SDK without removing the init call (default: false) */
33
+ disabled?: boolean;
34
+ /**
35
+ * Request timeout in ms when routing through proxy (default: 30000).
36
+ * This only affects the proxy hop — the actual upstream timeout is unchanged.
37
+ */
38
+ timeout?: number;
39
+ }
40
+ interface ReplayApiInstance {
41
+ /** Manually stop intercepting. Call init() again to restart. */
42
+ stop(): void;
43
+ /** Check if the SDK is currently intercepting */
44
+ isActive(): boolean;
45
+ /** Get capture statistics */
46
+ getStats(): CaptureStats;
47
+ }
48
+ interface CaptureStats {
49
+ /** Total requests intercepted */
50
+ totalCaptured: number;
51
+ /** Requests skipped by include/exclude filters */
52
+ totalSkipped: number;
53
+ /** Requests that failed to route through proxy */
54
+ totalErrors: number;
55
+ /** Timestamp when SDK was initialized */
56
+ startedAt: Date;
57
+ }
58
+
59
+ /**
60
+ * @replayapi/node — ReplayAPI SDK for Node.js
61
+ *
62
+ * Automatically captures outgoing HTTP traffic and routes it through
63
+ * the ReplayAPI proxy for recording, analysis, and replay testing.
64
+ *
65
+ * @example
66
+ * ```ts
67
+ * import { replayApi } from '@replayapi/node'
68
+ *
69
+ * // Simplest — just pass your API key
70
+ * replayApi.init('rp_your_api_key')
71
+ *
72
+ * // Or with options
73
+ * replayApi.init({ apiKey: 'rp_your_api_key', environment: 'staging' })
74
+ *
75
+ * // All outgoing HTTP/fetch calls are now captured automatically
76
+ * ```
77
+ */
78
+
79
+ /**
80
+ * Initialize the ReplayAPI SDK.
81
+ *
82
+ * Call this once at application startup, before any outgoing HTTP requests.
83
+ * The SDK will automatically intercept `http.request`, `https.request`,
84
+ * and `fetch` to route traffic through the ReplayAPI proxy.
85
+ *
86
+ * @example
87
+ * ```ts
88
+ * // Just an API key
89
+ * replayApi.init('rp_your_api_key')
90
+ *
91
+ * // Or with options
92
+ * replayApi.init({ apiKey: process.env.REPLAY_API_KEY!, environment: 'staging' })
93
+ * ```
94
+ */
95
+ declare function init(configOrApiKey: ReplayApiConfig | string): ReplayApiInstance;
96
+ /**
97
+ * Stop the SDK and restore original HTTP/fetch behavior.
98
+ */
99
+ declare function stop(): void;
100
+ /**
101
+ * Check if the SDK is currently intercepting traffic.
102
+ */
103
+ declare function isActive(): boolean;
104
+ /**
105
+ * Get combined capture statistics from all interceptors.
106
+ */
107
+ declare function getStats(): CaptureStats;
108
+ /**
109
+ * The main ReplayAPI SDK instance.
110
+ */
111
+ declare const replayApi: {
112
+ init: typeof init;
113
+ stop: typeof stop;
114
+ isActive: typeof isActive;
115
+ getStats: typeof getStats;
116
+ };
117
+
118
+ export { type CaptureStats, type ReplayApiConfig, type ReplayApiInstance, getStats, init, isActive, replayApi, stop };
package/dist/index.js ADDED
@@ -0,0 +1,426 @@
1
+ 'use strict';
2
+
3
+ var http = require('http');
4
+ var https = require('https');
5
+ var url = require('url');
6
+
7
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
+
9
+ var http__default = /*#__PURE__*/_interopDefault(http);
10
+ var https__default = /*#__PURE__*/_interopDefault(https);
11
+
12
+ // src/interceptor-http.ts
13
+
14
+ // src/matcher.ts
15
+ function matchesPatterns(url, patterns) {
16
+ return patterns.some((pattern) => {
17
+ if (typeof pattern === "string") {
18
+ return url.includes(pattern);
19
+ }
20
+ return pattern.test(url);
21
+ });
22
+ }
23
+ function shouldCapture(url, proxyUrl, include, exclude) {
24
+ if (url.startsWith(proxyUrl)) {
25
+ return false;
26
+ }
27
+ if (url.includes("/__replay/")) {
28
+ return false;
29
+ }
30
+ if (include && include.length > 0) {
31
+ if (!matchesPatterns(url, include)) {
32
+ return false;
33
+ }
34
+ }
35
+ if (exclude && exclude.length > 0) {
36
+ if (matchesPatterns(url, exclude)) {
37
+ return false;
38
+ }
39
+ }
40
+ return true;
41
+ }
42
+
43
+ // src/logger.ts
44
+ var debugEnabled = false;
45
+ function setDebug(enabled) {
46
+ debugEnabled = enabled;
47
+ }
48
+ function debug(...args) {
49
+ if (debugEnabled) {
50
+ console.log("[replayapi]", ...args);
51
+ }
52
+ }
53
+ function info(...args) {
54
+ console.log("[replayapi]", ...args);
55
+ }
56
+ function warn(...args) {
57
+ console.warn("[replayapi]", ...args);
58
+ }
59
+ function error(...args) {
60
+ console.error("[replayapi]", ...args);
61
+ }
62
+
63
+ // src/interceptor-http.ts
64
+ var originalHttpRequest = null;
65
+ var originalHttpGet = null;
66
+ var originalHttpsRequest = null;
67
+ var originalHttpsGet = null;
68
+ var PROXY_URL = "http://localhost:8080";
69
+ var active = false;
70
+ var config;
71
+ var stats = {
72
+ totalCaptured: 0,
73
+ totalSkipped: 0,
74
+ totalErrors: 0,
75
+ startedAt: /* @__PURE__ */ new Date()
76
+ };
77
+ function parseRequestArgs(args) {
78
+ let targetUrl;
79
+ let options = {};
80
+ let callback;
81
+ if (typeof args[0] === "string") {
82
+ targetUrl = args[0];
83
+ if (typeof args[1] === "function") {
84
+ callback = args[1];
85
+ } else if (typeof args[1] === "object" && args[1] !== null) {
86
+ options = args[1];
87
+ if (typeof args[2] === "function") {
88
+ callback = args[2];
89
+ }
90
+ }
91
+ } else if (args[0] instanceof url.URL) {
92
+ targetUrl = args[0].toString();
93
+ if (typeof args[1] === "function") {
94
+ callback = args[1];
95
+ } else if (typeof args[1] === "object" && args[1] !== null) {
96
+ options = args[1];
97
+ if (typeof args[2] === "function") {
98
+ callback = args[2];
99
+ }
100
+ }
101
+ } else if (typeof args[0] === "object" && args[0] !== null) {
102
+ options = args[0];
103
+ const protocol = options.protocol || "http:";
104
+ const host = options.hostname || options.host || "localhost";
105
+ const port = options.port ? `:${options.port}` : "";
106
+ const path = options.path || "/";
107
+ targetUrl = `${protocol}//${host}${port}${path}`;
108
+ if (typeof args[1] === "function") {
109
+ callback = args[1];
110
+ }
111
+ } else {
112
+ targetUrl = "";
113
+ }
114
+ return { targetUrl, options, callback };
115
+ }
116
+ function createProxiedRequest(originalFn, defaultProtocol) {
117
+ return function proxiedRequest(...args) {
118
+ try {
119
+ const { targetUrl, options, callback } = parseRequestArgs(args);
120
+ if (!targetUrl) {
121
+ debug("could not parse request URL, passing through");
122
+ stats.totalSkipped++;
123
+ return originalFn.apply(
124
+ defaultProtocol === "https:" ? https__default.default : http__default.default,
125
+ args
126
+ );
127
+ }
128
+ if (!shouldCapture(
129
+ targetUrl,
130
+ config.proxyUrl,
131
+ config.include,
132
+ config.exclude
133
+ )) {
134
+ debug("skipping (filtered):", targetUrl);
135
+ stats.totalSkipped++;
136
+ return originalFn.apply(
137
+ defaultProtocol === "https:" ? https__default.default : http__default.default,
138
+ args
139
+ );
140
+ }
141
+ const proxyParsed = new url.URL(config.proxyUrl);
142
+ let parsedTarget;
143
+ try {
144
+ parsedTarget = new url.URL(targetUrl);
145
+ } catch {
146
+ debug("invalid URL, passing through:", targetUrl);
147
+ stats.totalSkipped++;
148
+ return originalFn.apply(
149
+ defaultProtocol === "https:" ? https__default.default : http__default.default,
150
+ args
151
+ );
152
+ }
153
+ const targetOrigin = `${parsedTarget.protocol}//${parsedTarget.host}`;
154
+ const proxiedOptions = {
155
+ ...options,
156
+ protocol: proxyParsed.protocol,
157
+ hostname: proxyParsed.hostname,
158
+ port: proxyParsed.port || (proxyParsed.protocol === "https:" ? 443 : 80),
159
+ path: parsedTarget.pathname + parsedTarget.search,
160
+ headers: {
161
+ ...options.headers,
162
+ "X-Api-Key": config.apiKey,
163
+ "X-Replay-Target": targetOrigin,
164
+ "X-Replay-Env": config.environment,
165
+ // Preserve the original Host header
166
+ Host: parsedTarget.host
167
+ }
168
+ };
169
+ if (config.sessionId) {
170
+ proxiedOptions.headers["X-Replay-Session"] = config.sessionId;
171
+ }
172
+ if (config.timeout) {
173
+ proxiedOptions.timeout = config.timeout;
174
+ }
175
+ debug("capturing:", options.method || "GET", targetUrl, "\u2192", config.proxyUrl);
176
+ stats.totalCaptured++;
177
+ const proxyUrl = new url.URL(`${proxiedOptions.protocol}//${proxiedOptions.hostname}:${proxiedOptions.port}${proxiedOptions.path}`);
178
+ return originalHttpRequest.call(http__default.default, proxyUrl, proxiedOptions, callback);
179
+ } catch (err) {
180
+ error("interceptor error, passing through:", err);
181
+ stats.totalErrors++;
182
+ return originalFn.apply(
183
+ defaultProtocol === "https:" ? https__default.default : http__default.default,
184
+ args
185
+ );
186
+ }
187
+ };
188
+ }
189
+ function createProxiedGet(proxiedRequest) {
190
+ return function proxiedGet(...args) {
191
+ const req = proxiedRequest(...args);
192
+ req.end();
193
+ return req;
194
+ };
195
+ }
196
+ function installHttpInterceptor(cfg) {
197
+ if (active) {
198
+ warn("interceptor already installed, call stop() first");
199
+ return;
200
+ }
201
+ config = {
202
+ ...cfg,
203
+ proxyUrl: PROXY_URL,
204
+ environment: cfg.environment || "development"
205
+ };
206
+ originalHttpRequest = http__default.default.request;
207
+ originalHttpGet = http__default.default.get;
208
+ originalHttpsRequest = https__default.default.request;
209
+ originalHttpsGet = https__default.default.get;
210
+ const proxiedHttpRequest = createProxiedRequest(originalHttpRequest, "http:");
211
+ http__default.default.request = proxiedHttpRequest;
212
+ http__default.default.get = createProxiedGet(proxiedHttpRequest);
213
+ const proxiedHttpsRequest = createProxiedRequest(
214
+ originalHttpsRequest,
215
+ "https:"
216
+ );
217
+ https__default.default.request = proxiedHttpsRequest;
218
+ https__default.default.get = createProxiedGet(proxiedHttpsRequest);
219
+ stats.totalCaptured = 0;
220
+ stats.totalSkipped = 0;
221
+ stats.totalErrors = 0;
222
+ stats.startedAt = /* @__PURE__ */ new Date();
223
+ active = true;
224
+ debug("http/https interceptor installed");
225
+ }
226
+ function uninstallHttpInterceptor() {
227
+ if (!active) return;
228
+ if (originalHttpRequest) http__default.default.request = originalHttpRequest;
229
+ if (originalHttpGet) http__default.default.get = originalHttpGet;
230
+ if (originalHttpsRequest) https__default.default.request = originalHttpsRequest;
231
+ if (originalHttpsGet) https__default.default.get = originalHttpsGet;
232
+ originalHttpRequest = null;
233
+ originalHttpGet = null;
234
+ originalHttpsRequest = null;
235
+ originalHttpsGet = null;
236
+ active = false;
237
+ debug("http/https interceptor removed");
238
+ }
239
+ function isHttpInterceptorActive() {
240
+ return active;
241
+ }
242
+ function getHttpStats() {
243
+ return { ...stats };
244
+ }
245
+
246
+ // src/interceptor-fetch.ts
247
+ var PROXY_URL2 = "http://localhost:8080";
248
+ var originalFetch = null;
249
+ var active2 = false;
250
+ var config2;
251
+ var stats2 = {
252
+ totalCaptured: 0,
253
+ totalSkipped: 0,
254
+ totalErrors: 0,
255
+ startedAt: /* @__PURE__ */ new Date()
256
+ };
257
+ function installFetchInterceptor(cfg) {
258
+ if (active2) {
259
+ warn("fetch interceptor already installed");
260
+ return;
261
+ }
262
+ if (typeof globalThis.fetch !== "function") {
263
+ debug("globalThis.fetch not available, skipping fetch interceptor");
264
+ return;
265
+ }
266
+ config2 = {
267
+ ...cfg,
268
+ proxyUrl: PROXY_URL2,
269
+ environment: cfg.environment || "development"
270
+ };
271
+ originalFetch = globalThis.fetch;
272
+ globalThis.fetch = async function proxiedFetch(input, init2) {
273
+ try {
274
+ let targetUrl;
275
+ if (typeof input === "string") {
276
+ targetUrl = input;
277
+ } else if (input instanceof URL) {
278
+ targetUrl = input.toString();
279
+ } else if (input instanceof Request) {
280
+ targetUrl = input.url;
281
+ } else {
282
+ targetUrl = String(input);
283
+ }
284
+ if (!shouldCapture(
285
+ targetUrl,
286
+ config2.proxyUrl,
287
+ config2.include,
288
+ config2.exclude
289
+ )) {
290
+ debug("fetch skipping (filtered):", targetUrl);
291
+ stats2.totalSkipped++;
292
+ return originalFetch(input, init2);
293
+ }
294
+ let parsedTarget;
295
+ try {
296
+ parsedTarget = new URL(targetUrl);
297
+ } catch {
298
+ debug("fetch: invalid URL, passing through:", targetUrl);
299
+ stats2.totalSkipped++;
300
+ return originalFetch(input, init2);
301
+ }
302
+ const targetOrigin = `${parsedTarget.protocol}//${parsedTarget.host}`;
303
+ const proxyPath = parsedTarget.pathname + parsedTarget.search;
304
+ const proxiedUrl = `${config2.proxyUrl}${proxyPath}`;
305
+ const headers = new Headers(init2?.headers || {});
306
+ if (input instanceof Request) {
307
+ input.headers.forEach((value, key) => {
308
+ if (!headers.has(key)) {
309
+ headers.set(key, value);
310
+ }
311
+ });
312
+ }
313
+ headers.set("X-Api-Key", config2.apiKey);
314
+ headers.set("X-Replay-Target", targetOrigin);
315
+ headers.set("X-Replay-Env", config2.environment);
316
+ headers.set("Host", parsedTarget.host);
317
+ if (config2.sessionId) {
318
+ headers.set("X-Replay-Session", config2.sessionId);
319
+ }
320
+ const proxiedInit = {
321
+ ...init2,
322
+ headers
323
+ };
324
+ if (input instanceof Request) {
325
+ proxiedInit.method = proxiedInit.method || input.method;
326
+ if (!proxiedInit.body && input.body) {
327
+ proxiedInit.body = input.body;
328
+ }
329
+ }
330
+ debug(
331
+ "fetch capturing:",
332
+ proxiedInit.method || "GET",
333
+ targetUrl,
334
+ "\u2192",
335
+ config2.proxyUrl
336
+ );
337
+ stats2.totalCaptured++;
338
+ return originalFetch(proxiedUrl, proxiedInit);
339
+ } catch (err) {
340
+ error("fetch interceptor error, passing through:", err);
341
+ stats2.totalErrors++;
342
+ return originalFetch(input, init2);
343
+ }
344
+ };
345
+ stats2.totalCaptured = 0;
346
+ stats2.totalSkipped = 0;
347
+ stats2.totalErrors = 0;
348
+ stats2.startedAt = /* @__PURE__ */ new Date();
349
+ active2 = true;
350
+ debug("fetch interceptor installed");
351
+ }
352
+ function uninstallFetchInterceptor() {
353
+ if (!active2 || !originalFetch) return;
354
+ globalThis.fetch = originalFetch;
355
+ originalFetch = null;
356
+ active2 = false;
357
+ debug("fetch interceptor removed");
358
+ }
359
+ function isFetchInterceptorActive() {
360
+ return active2;
361
+ }
362
+ function getFetchStats() {
363
+ return { ...stats2 };
364
+ }
365
+
366
+ // src/index.ts
367
+ var initialized = false;
368
+ function init(configOrApiKey) {
369
+ const config3 = typeof configOrApiKey === "string" ? { apiKey: configOrApiKey } : configOrApiKey;
370
+ if (initialized) {
371
+ warn("replayApi.init() called multiple times \u2014 stopping previous instance");
372
+ stop();
373
+ }
374
+ if (!config3.apiKey) {
375
+ throw new Error(
376
+ "[@replayapi/node] apiKey is required. Get one from your ReplayAPI dashboard."
377
+ );
378
+ }
379
+ if (config3.disabled) {
380
+ info("SDK disabled via config, skipping initialization");
381
+ return { stop, isActive, getStats };
382
+ }
383
+ if (config3.debug) {
384
+ setDebug(true);
385
+ }
386
+ config3.environment = config3.environment || process.env.REPLAY_ENVIRONMENT || "development";
387
+ info(`initialized \u2014 env: ${config3.environment}`);
388
+ installHttpInterceptor(config3);
389
+ installFetchInterceptor(config3);
390
+ initialized = true;
391
+ return { stop, isActive, getStats };
392
+ }
393
+ function stop() {
394
+ if (!initialized) return;
395
+ uninstallHttpInterceptor();
396
+ uninstallFetchInterceptor();
397
+ initialized = false;
398
+ info("stopped \u2014 all interceptors removed");
399
+ }
400
+ function isActive() {
401
+ return isHttpInterceptorActive() || isFetchInterceptorActive();
402
+ }
403
+ function getStats() {
404
+ const httpStats = getHttpStats();
405
+ const fetchStats = getFetchStats();
406
+ return {
407
+ totalCaptured: httpStats.totalCaptured + fetchStats.totalCaptured,
408
+ totalSkipped: httpStats.totalSkipped + fetchStats.totalSkipped,
409
+ totalErrors: httpStats.totalErrors + fetchStats.totalErrors,
410
+ startedAt: httpStats.startedAt < fetchStats.startedAt ? httpStats.startedAt : fetchStats.startedAt
411
+ };
412
+ }
413
+ var replayApi = {
414
+ init,
415
+ stop,
416
+ isActive,
417
+ getStats
418
+ };
419
+
420
+ exports.getStats = getStats;
421
+ exports.init = init;
422
+ exports.isActive = isActive;
423
+ exports.replayApi = replayApi;
424
+ exports.stop = stop;
425
+ //# sourceMappingURL=index.js.map
426
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/matcher.ts","../src/logger.ts","../src/interceptor-http.ts","../src/interceptor-fetch.ts","../src/index.ts"],"names":["URL","https","http","PROXY_URL","active","config","stats","init"],"mappings":"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","file":"index.js","sourcesContent":["/**\r\n * ReplayAPI SDK — URL matching utilities\r\n */\r\n\r\n/**\r\n * Check if a URL matches any of the given patterns.\r\n * Patterns can be strings (substring match) or RegExp.\r\n */\r\nexport function matchesPatterns(\r\n url: string,\r\n patterns: Array<string | RegExp>\r\n): boolean {\r\n return patterns.some((pattern) => {\r\n if (typeof pattern === \"string\") {\r\n return url.includes(pattern);\r\n }\r\n return pattern.test(url);\r\n });\r\n}\r\n\r\n/**\r\n * Determine if a request URL should be captured based on include/exclude filters.\r\n */\r\nexport function shouldCapture(\r\n url: string,\r\n proxyUrl: string,\r\n include?: Array<string | RegExp>,\r\n exclude?: Array<string | RegExp>\r\n): boolean {\r\n // Never capture requests to the proxy itself\r\n if (url.startsWith(proxyUrl)) {\r\n return false;\r\n }\r\n\r\n // Never capture requests to ReplayAPI internal paths\r\n if (url.includes(\"/__replay/\")) {\r\n return false;\r\n }\r\n\r\n // If include patterns are set, URL must match at least one\r\n if (include && include.length > 0) {\r\n if (!matchesPatterns(url, include)) {\r\n return false;\r\n }\r\n }\r\n\r\n // If exclude patterns are set, URL must not match any\r\n if (exclude && exclude.length > 0) {\r\n if (matchesPatterns(url, exclude)) {\r\n return false;\r\n }\r\n }\r\n\r\n return true;\r\n}\r\n","/**\r\n * ReplayAPI SDK — Simple logger\r\n */\r\n\r\nlet debugEnabled = false;\r\n\r\nexport function setDebug(enabled: boolean): void {\r\n debugEnabled = enabled;\r\n}\r\n\r\nexport function debug(...args: unknown[]): void {\r\n if (debugEnabled) {\r\n console.log(\"[replayapi]\", ...args);\r\n }\r\n}\r\n\r\nexport function info(...args: unknown[]): void {\r\n console.log(\"[replayapi]\", ...args);\r\n}\r\n\r\nexport function warn(...args: unknown[]): void {\r\n console.warn(\"[replayapi]\", ...args);\r\n}\r\n\r\nexport function error(...args: unknown[]): void {\r\n console.error(\"[replayapi]\", ...args);\r\n}\r\n","/**\r\n * ReplayAPI SDK — HTTP/HTTPS interceptor\r\n *\r\n * Monkey-patches Node.js http.request and https.request to redirect\r\n * outgoing requests through the ReplayAPI proxy. The proxy captures\r\n * the traffic and forwards it to the original target.\r\n *\r\n * How it works:\r\n * 1. Original request: GET https://api.example.com/users\r\n * 2. SDK rewrites to: GET http://proxy:8080/users\r\n * with headers:\r\n * X-Api-Key: rp_...\r\n * X-Replay-Target: https://api.example.com\r\n * X-Replay-Env: staging\r\n * 3. Proxy captures, forwards to target, returns response transparently\r\n */\r\n\r\nimport http from \"node:http\";\r\nimport https from \"node:https\";\r\nimport { URL } from \"node:url\";\r\nimport type { ReplayApiConfig, CaptureStats } from \"./types.js\";\r\nimport { shouldCapture } from \"./matcher.js\";\r\nimport * as log from \"./logger.js\";\r\n\r\n// Store original implementations\r\nlet originalHttpRequest: typeof http.request | null = null;\r\nlet originalHttpGet: typeof http.get | null = null;\r\nlet originalHttpsRequest: typeof https.request | null = null;\r\nlet originalHttpsGet: typeof https.get | null = null;\r\n\r\nconst PROXY_URL = \"http://localhost:8080\";\r\n\r\n// Track state\r\nlet active = false;\r\nlet config: Required<\r\n Pick<ReplayApiConfig, \"apiKey\" | \"environment\">\r\n> &\r\n ReplayApiConfig & { proxyUrl: string };\r\n\r\nconst stats: CaptureStats = {\r\n totalCaptured: 0,\r\n totalSkipped: 0,\r\n totalErrors: 0,\r\n startedAt: new Date(),\r\n};\r\n\r\n/**\r\n * Parse the various argument formats that http.request accepts:\r\n * - (url: string, options?, callback?)\r\n * - (url: URL, options?, callback?)\r\n * - (options, callback?)\r\n */\r\nfunction parseRequestArgs(\r\n args: unknown[]\r\n): {\r\n targetUrl: string;\r\n options: http.RequestOptions;\r\n callback?: (res: http.IncomingMessage) => void;\r\n} {\r\n let targetUrl: string;\r\n let options: http.RequestOptions = {};\r\n let callback: ((res: http.IncomingMessage) => void) | undefined;\r\n\r\n if (typeof args[0] === \"string\") {\r\n targetUrl = args[0];\r\n if (typeof args[1] === \"function\") {\r\n callback = args[1] as (res: http.IncomingMessage) => void;\r\n } else if (typeof args[1] === \"object\" && args[1] !== null) {\r\n options = args[1] as http.RequestOptions;\r\n if (typeof args[2] === \"function\") {\r\n callback = args[2] as (res: http.IncomingMessage) => void;\r\n }\r\n }\r\n } else if (args[0] instanceof URL) {\r\n targetUrl = args[0].toString();\r\n if (typeof args[1] === \"function\") {\r\n callback = args[1] as (res: http.IncomingMessage) => void;\r\n } else if (typeof args[1] === \"object\" && args[1] !== null) {\r\n options = args[1] as http.RequestOptions;\r\n if (typeof args[2] === \"function\") {\r\n callback = args[2] as (res: http.IncomingMessage) => void;\r\n }\r\n }\r\n } else if (typeof args[0] === \"object\" && args[0] !== null) {\r\n options = args[0] as http.RequestOptions;\r\n const protocol = (options as { protocol?: string }).protocol || \"http:\";\r\n const host = options.hostname || options.host || \"localhost\";\r\n const port = options.port ? `:${options.port}` : \"\";\r\n const path = options.path || \"/\";\r\n targetUrl = `${protocol}//${host}${port}${path}`;\r\n if (typeof args[1] === \"function\") {\r\n callback = args[1] as (res: http.IncomingMessage) => void;\r\n }\r\n } else {\r\n targetUrl = \"\";\r\n }\r\n\r\n return { targetUrl, options, callback };\r\n}\r\n\r\n/**\r\n * Create a proxied version of http.request / https.request\r\n */\r\nfunction createProxiedRequest(\r\n originalFn: typeof http.request,\r\n defaultProtocol: \"http:\" | \"https:\"\r\n): typeof http.request {\r\n return function proxiedRequest(\r\n ...args: unknown[]\r\n ): http.ClientRequest {\r\n try {\r\n const { targetUrl, options, callback } = parseRequestArgs(args);\r\n\r\n if (!targetUrl) {\r\n log.debug(\"could not parse request URL, passing through\");\r\n stats.totalSkipped++;\r\n return (originalFn as Function).apply(\r\n defaultProtocol === \"https:\" ? https : http,\r\n args\r\n ) as http.ClientRequest;\r\n }\r\n\r\n // Check if this URL should be captured\r\n if (\r\n !shouldCapture(\r\n targetUrl,\r\n config.proxyUrl,\r\n config.include,\r\n config.exclude\r\n )\r\n ) {\r\n log.debug(\"skipping (filtered):\", targetUrl);\r\n stats.totalSkipped++;\r\n return (originalFn as Function).apply(\r\n defaultProtocol === \"https:\" ? https : http,\r\n args\r\n ) as http.ClientRequest;\r\n }\r\n\r\n // Parse the proxy URL\r\n const proxyParsed = new URL(config.proxyUrl);\r\n\r\n // Parse the target URL to extract origin and path\r\n let parsedTarget: URL;\r\n try {\r\n parsedTarget = new URL(targetUrl);\r\n } catch {\r\n log.debug(\"invalid URL, passing through:\", targetUrl);\r\n stats.totalSkipped++;\r\n return (originalFn as Function).apply(\r\n defaultProtocol === \"https:\" ? https : http,\r\n args\r\n ) as http.ClientRequest;\r\n }\r\n\r\n const targetOrigin = `${parsedTarget.protocol}//${parsedTarget.host}`;\r\n\r\n // Build proxied options: send to proxy, with X-Replay-Target header\r\n const proxiedOptions: http.RequestOptions = {\r\n ...options,\r\n protocol: proxyParsed.protocol,\r\n hostname: proxyParsed.hostname,\r\n port: proxyParsed.port || (proxyParsed.protocol === \"https:\" ? 443 : 80),\r\n path: parsedTarget.pathname + parsedTarget.search,\r\n headers: {\r\n ...options.headers,\r\n \"X-Api-Key\": config.apiKey,\r\n \"X-Replay-Target\": targetOrigin,\r\n \"X-Replay-Env\": config.environment,\r\n // Preserve the original Host header\r\n Host: parsedTarget.host,\r\n },\r\n };\r\n\r\n // Add session ID if configured\r\n if (config.sessionId) {\r\n (proxiedOptions.headers as Record<string, string>)[\"X-Replay-Session\"] =\r\n config.sessionId;\r\n }\r\n\r\n // Set timeout if configured\r\n if (config.timeout) {\r\n proxiedOptions.timeout = config.timeout;\r\n }\r\n\r\n log.debug(\"capturing:\", options.method || \"GET\", targetUrl, \"→\", config.proxyUrl);\r\n stats.totalCaptured++;\r\n\r\n // Use http.request (not https) since we're talking to the proxy over HTTP\r\n const proxyUrl = new URL(`${proxiedOptions.protocol}//${proxiedOptions.hostname}:${proxiedOptions.port}${proxiedOptions.path}`);\r\n return originalHttpRequest!.call(http, proxyUrl, proxiedOptions, callback);\r\n } catch (err) {\r\n log.error(\"interceptor error, passing through:\", err);\r\n stats.totalErrors++;\r\n return (originalFn as Function).apply(\r\n defaultProtocol === \"https:\" ? https : http,\r\n args\r\n ) as http.ClientRequest;\r\n }\r\n } as typeof http.request;\r\n}\r\n\r\n/**\r\n * Create a proxied version of http.get / https.get\r\n */\r\nfunction createProxiedGet(\r\n proxiedRequest: typeof http.request\r\n): typeof http.get {\r\n return function proxiedGet(...args: unknown[]): http.ClientRequest {\r\n const req = (proxiedRequest as Function)(...args) as http.ClientRequest;\r\n req.end();\r\n return req;\r\n } as typeof http.get;\r\n}\r\n\r\n/**\r\n * Install the HTTP/HTTPS interceptors\r\n */\r\nexport function installHttpInterceptor(cfg: ReplayApiConfig): void {\r\n if (active) {\r\n log.warn(\"interceptor already installed, call stop() first\");\r\n return;\r\n }\r\n\r\n config = {\r\n ...cfg,\r\n proxyUrl: PROXY_URL,\r\n environment: cfg.environment || \"development\",\r\n };\r\n\r\n // Save originals\r\n originalHttpRequest = http.request;\r\n originalHttpGet = http.get;\r\n originalHttpsRequest = https.request;\r\n originalHttpsGet = https.get;\r\n\r\n // Patch http\r\n const proxiedHttpRequest = createProxiedRequest(originalHttpRequest, \"http:\");\r\n http.request = proxiedHttpRequest;\r\n http.get = createProxiedGet(proxiedHttpRequest);\r\n\r\n // Patch https\r\n const proxiedHttpsRequest = createProxiedRequest(\r\n originalHttpsRequest,\r\n \"https:\"\r\n );\r\n https.request = proxiedHttpsRequest;\r\n https.get = createProxiedGet(proxiedHttpsRequest);\r\n\r\n // Reset stats\r\n stats.totalCaptured = 0;\r\n stats.totalSkipped = 0;\r\n stats.totalErrors = 0;\r\n stats.startedAt = new Date();\r\n\r\n active = true;\r\n log.debug(\"http/https interceptor installed\");\r\n}\r\n\r\n/**\r\n * Uninstall the HTTP/HTTPS interceptors\r\n */\r\nexport function uninstallHttpInterceptor(): void {\r\n if (!active) return;\r\n\r\n if (originalHttpRequest) http.request = originalHttpRequest;\r\n if (originalHttpGet) http.get = originalHttpGet;\r\n if (originalHttpsRequest) https.request = originalHttpsRequest;\r\n if (originalHttpsGet) https.get = originalHttpsGet;\r\n\r\n originalHttpRequest = null;\r\n originalHttpGet = null;\r\n originalHttpsRequest = null;\r\n originalHttpsGet = null;\r\n\r\n active = false;\r\n log.debug(\"http/https interceptor removed\");\r\n}\r\n\r\nexport function isHttpInterceptorActive(): boolean {\r\n return active;\r\n}\r\n\r\nexport function getHttpStats(): CaptureStats {\r\n return { ...stats };\r\n}\r\n","/**\r\n * ReplayAPI SDK — Global fetch() interceptor\r\n *\r\n * Patches globalThis.fetch to route requests through the ReplayAPI proxy.\r\n * Works with Node.js 18+ native fetch and any polyfills that set globalThis.fetch.\r\n */\r\n\r\nimport type { ReplayApiConfig, CaptureStats } from \"./types.js\";\r\nimport { shouldCapture } from \"./matcher.js\";\r\nimport * as log from \"./logger.js\";\r\n\r\nconst PROXY_URL = \"http://localhost:8080\";\r\n\r\nlet originalFetch: typeof globalThis.fetch | null = null;\r\nlet active = false;\r\nlet config: Required<\r\n Pick<ReplayApiConfig, \"apiKey\" | \"environment\">\r\n> &\r\n ReplayApiConfig & { proxyUrl: string };\r\n\r\nconst stats: CaptureStats = {\r\n totalCaptured: 0,\r\n totalSkipped: 0,\r\n totalErrors: 0,\r\n startedAt: new Date(),\r\n};\r\n\r\n/**\r\n * Install the fetch interceptor\r\n */\r\nexport function installFetchInterceptor(cfg: ReplayApiConfig): void {\r\n if (active) {\r\n log.warn(\"fetch interceptor already installed\");\r\n return;\r\n }\r\n\r\n // Only patch if fetch exists (Node.js 18+)\r\n if (typeof globalThis.fetch !== \"function\") {\r\n log.debug(\"globalThis.fetch not available, skipping fetch interceptor\");\r\n return;\r\n }\r\n\r\n config = {\r\n ...cfg,\r\n proxyUrl: PROXY_URL,\r\n environment: cfg.environment || \"development\",\r\n };\r\n\r\n originalFetch = globalThis.fetch;\r\n\r\n globalThis.fetch = async function proxiedFetch(\r\n input: string | URL | Request,\r\n init?: RequestInit\r\n ): Promise<Response> {\r\n try {\r\n // Resolve the target URL\r\n let targetUrl: string;\r\n if (typeof input === \"string\") {\r\n targetUrl = input;\r\n } else if (input instanceof URL) {\r\n targetUrl = input.toString();\r\n } else if (input instanceof Request) {\r\n targetUrl = input.url;\r\n } else {\r\n targetUrl = String(input);\r\n }\r\n\r\n // Check if this URL should be captured\r\n if (\r\n !shouldCapture(\r\n targetUrl,\r\n config.proxyUrl,\r\n config.include,\r\n config.exclude\r\n )\r\n ) {\r\n log.debug(\"fetch skipping (filtered):\", targetUrl);\r\n stats.totalSkipped++;\r\n return originalFetch!(input, init);\r\n }\r\n\r\n // Parse target URL\r\n let parsedTarget: URL;\r\n try {\r\n parsedTarget = new URL(targetUrl);\r\n } catch {\r\n log.debug(\"fetch: invalid URL, passing through:\", targetUrl);\r\n stats.totalSkipped++;\r\n return originalFetch!(input, init);\r\n }\r\n\r\n const targetOrigin = `${parsedTarget.protocol}//${parsedTarget.host}`;\r\n const proxyPath = parsedTarget.pathname + parsedTarget.search;\r\n const proxiedUrl = `${config.proxyUrl}${proxyPath}`;\r\n\r\n // Build headers\r\n const headers = new Headers(init?.headers || {});\r\n\r\n // If input is a Request, merge its headers too\r\n if (input instanceof Request) {\r\n input.headers.forEach((value, key) => {\r\n if (!headers.has(key)) {\r\n headers.set(key, value);\r\n }\r\n });\r\n }\r\n\r\n // Add ReplayAPI headers\r\n headers.set(\"X-Api-Key\", config.apiKey);\r\n headers.set(\"X-Replay-Target\", targetOrigin);\r\n headers.set(\"X-Replay-Env\", config.environment);\r\n headers.set(\"Host\", parsedTarget.host);\r\n\r\n if (config.sessionId) {\r\n headers.set(\"X-Replay-Session\", config.sessionId);\r\n }\r\n\r\n // Build proxied init\r\n const proxiedInit: RequestInit = {\r\n ...init,\r\n headers,\r\n };\r\n\r\n // If input is a Request, preserve method and body\r\n if (input instanceof Request) {\r\n proxiedInit.method = proxiedInit.method || input.method;\r\n if (!proxiedInit.body && input.body) {\r\n proxiedInit.body = input.body;\r\n }\r\n }\r\n\r\n log.debug(\r\n \"fetch capturing:\",\r\n proxiedInit.method || \"GET\",\r\n targetUrl,\r\n \"→\",\r\n config.proxyUrl\r\n );\r\n stats.totalCaptured++;\r\n\r\n return originalFetch!(proxiedUrl, proxiedInit);\r\n } catch (err) {\r\n log.error(\"fetch interceptor error, passing through:\", err);\r\n stats.totalErrors++;\r\n return originalFetch!(input, init);\r\n }\r\n };\r\n\r\n // Reset stats\r\n stats.totalCaptured = 0;\r\n stats.totalSkipped = 0;\r\n stats.totalErrors = 0;\r\n stats.startedAt = new Date();\r\n\r\n active = true;\r\n log.debug(\"fetch interceptor installed\");\r\n}\r\n\r\n/**\r\n * Uninstall the fetch interceptor\r\n */\r\nexport function uninstallFetchInterceptor(): void {\r\n if (!active || !originalFetch) return;\r\n\r\n globalThis.fetch = originalFetch;\r\n originalFetch = null;\r\n active = false;\r\n log.debug(\"fetch interceptor removed\");\r\n}\r\n\r\nexport function isFetchInterceptorActive(): boolean {\r\n return active;\r\n}\r\n\r\nexport function getFetchStats(): CaptureStats {\r\n return { ...stats };\r\n}\r\n","/**\r\n * @replayapi/node — ReplayAPI SDK for Node.js\r\n *\r\n * Automatically captures outgoing HTTP traffic and routes it through\r\n * the ReplayAPI proxy for recording, analysis, and replay testing.\r\n *\r\n * @example\r\n * ```ts\r\n * import { replayApi } from '@replayapi/node'\r\n *\r\n * // Simplest — just pass your API key\r\n * replayApi.init('rp_your_api_key')\r\n *\r\n * // Or with options\r\n * replayApi.init({ apiKey: 'rp_your_api_key', environment: 'staging' })\r\n *\r\n * // All outgoing HTTP/fetch calls are now captured automatically\r\n * ```\r\n */\r\n\r\nimport type { ReplayApiConfig, ReplayApiInstance, CaptureStats } from \"./types.js\";\r\nimport {\r\n installHttpInterceptor,\r\n uninstallHttpInterceptor,\r\n isHttpInterceptorActive,\r\n getHttpStats,\r\n} from \"./interceptor-http.js\";\r\nimport {\r\n installFetchInterceptor,\r\n uninstallFetchInterceptor,\r\n isFetchInterceptorActive,\r\n getFetchStats,\r\n} from \"./interceptor-fetch.js\";\r\nimport { setDebug } from \"./logger.js\";\r\nimport * as log from \"./logger.js\";\r\n\r\nlet initialized = false;\r\n\r\n/**\r\n * Initialize the ReplayAPI SDK.\r\n *\r\n * Call this once at application startup, before any outgoing HTTP requests.\r\n * The SDK will automatically intercept `http.request`, `https.request`,\r\n * and `fetch` to route traffic through the ReplayAPI proxy.\r\n *\r\n * @example\r\n * ```ts\r\n * // Just an API key\r\n * replayApi.init('rp_your_api_key')\r\n *\r\n * // Or with options\r\n * replayApi.init({ apiKey: process.env.REPLAY_API_KEY!, environment: 'staging' })\r\n * ```\r\n */\r\nfunction init(configOrApiKey: ReplayApiConfig | string): ReplayApiInstance {\r\n // Accept a plain string as shorthand for { apiKey: string }\r\n const config: ReplayApiConfig =\r\n typeof configOrApiKey === \"string\" ? { apiKey: configOrApiKey } : configOrApiKey;\r\n\r\n if (initialized) {\r\n log.warn(\"replayApi.init() called multiple times — stopping previous instance\");\r\n stop();\r\n }\r\n\r\n // Validate config\r\n if (!config.apiKey) {\r\n throw new Error(\r\n \"[@replayapi/node] apiKey is required. Get one from your ReplayAPI dashboard.\"\r\n );\r\n }\r\n\r\n if (config.disabled) {\r\n log.info(\"SDK disabled via config, skipping initialization\");\r\n return { stop, isActive, getStats };\r\n }\r\n\r\n // Enable debug logging if requested\r\n if (config.debug) {\r\n setDebug(true);\r\n }\r\n\r\n // Resolve environment with env var fallback\r\n config.environment =\r\n config.environment || process.env.REPLAY_ENVIRONMENT || \"development\";\r\n\r\n log.info(`initialized — env: ${config.environment}`);\r\n\r\n // Install interceptors\r\n installHttpInterceptor(config);\r\n installFetchInterceptor(config);\r\n\r\n initialized = true;\r\n\r\n return { stop, isActive, getStats };\r\n}\r\n\r\n/**\r\n * Stop the SDK and restore original HTTP/fetch behavior.\r\n */\r\nfunction stop(): void {\r\n if (!initialized) return;\r\n\r\n uninstallHttpInterceptor();\r\n uninstallFetchInterceptor();\r\n\r\n initialized = false;\r\n log.info(\"stopped — all interceptors removed\");\r\n}\r\n\r\n/**\r\n * Check if the SDK is currently intercepting traffic.\r\n */\r\nfunction isActive(): boolean {\r\n return isHttpInterceptorActive() || isFetchInterceptorActive();\r\n}\r\n\r\n/**\r\n * Get combined capture statistics from all interceptors.\r\n */\r\nfunction getStats(): CaptureStats {\r\n const httpStats = getHttpStats();\r\n const fetchStats = getFetchStats();\r\n\r\n return {\r\n totalCaptured: httpStats.totalCaptured + fetchStats.totalCaptured,\r\n totalSkipped: httpStats.totalSkipped + fetchStats.totalSkipped,\r\n totalErrors: httpStats.totalErrors + fetchStats.totalErrors,\r\n startedAt: httpStats.startedAt < fetchStats.startedAt\r\n ? httpStats.startedAt\r\n : fetchStats.startedAt,\r\n };\r\n}\r\n\r\n/**\r\n * The main ReplayAPI SDK instance.\r\n */\r\nexport const replayApi = {\r\n init,\r\n stop,\r\n isActive,\r\n getStats,\r\n};\r\n\r\n// Named exports for flexibility\r\nexport { init, stop, isActive, getStats };\r\n\r\n// Re-export types\r\nexport type { ReplayApiConfig, ReplayApiInstance, CaptureStats } from \"./types.js\";\r\n"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,415 @@
1
+ import http from 'http';
2
+ import https from 'https';
3
+ import { URL as URL$1 } from 'url';
4
+
5
+ // src/interceptor-http.ts
6
+
7
+ // src/matcher.ts
8
+ function matchesPatterns(url, patterns) {
9
+ return patterns.some((pattern) => {
10
+ if (typeof pattern === "string") {
11
+ return url.includes(pattern);
12
+ }
13
+ return pattern.test(url);
14
+ });
15
+ }
16
+ function shouldCapture(url, proxyUrl, include, exclude) {
17
+ if (url.startsWith(proxyUrl)) {
18
+ return false;
19
+ }
20
+ if (url.includes("/__replay/")) {
21
+ return false;
22
+ }
23
+ if (include && include.length > 0) {
24
+ if (!matchesPatterns(url, include)) {
25
+ return false;
26
+ }
27
+ }
28
+ if (exclude && exclude.length > 0) {
29
+ if (matchesPatterns(url, exclude)) {
30
+ return false;
31
+ }
32
+ }
33
+ return true;
34
+ }
35
+
36
+ // src/logger.ts
37
+ var debugEnabled = false;
38
+ function setDebug(enabled) {
39
+ debugEnabled = enabled;
40
+ }
41
+ function debug(...args) {
42
+ if (debugEnabled) {
43
+ console.log("[replayapi]", ...args);
44
+ }
45
+ }
46
+ function info(...args) {
47
+ console.log("[replayapi]", ...args);
48
+ }
49
+ function warn(...args) {
50
+ console.warn("[replayapi]", ...args);
51
+ }
52
+ function error(...args) {
53
+ console.error("[replayapi]", ...args);
54
+ }
55
+
56
+ // src/interceptor-http.ts
57
+ var originalHttpRequest = null;
58
+ var originalHttpGet = null;
59
+ var originalHttpsRequest = null;
60
+ var originalHttpsGet = null;
61
+ var PROXY_URL = "http://localhost:8080";
62
+ var active = false;
63
+ var config;
64
+ var stats = {
65
+ totalCaptured: 0,
66
+ totalSkipped: 0,
67
+ totalErrors: 0,
68
+ startedAt: /* @__PURE__ */ new Date()
69
+ };
70
+ function parseRequestArgs(args) {
71
+ let targetUrl;
72
+ let options = {};
73
+ let callback;
74
+ if (typeof args[0] === "string") {
75
+ targetUrl = args[0];
76
+ if (typeof args[1] === "function") {
77
+ callback = args[1];
78
+ } else if (typeof args[1] === "object" && args[1] !== null) {
79
+ options = args[1];
80
+ if (typeof args[2] === "function") {
81
+ callback = args[2];
82
+ }
83
+ }
84
+ } else if (args[0] instanceof URL$1) {
85
+ targetUrl = args[0].toString();
86
+ if (typeof args[1] === "function") {
87
+ callback = args[1];
88
+ } else if (typeof args[1] === "object" && args[1] !== null) {
89
+ options = args[1];
90
+ if (typeof args[2] === "function") {
91
+ callback = args[2];
92
+ }
93
+ }
94
+ } else if (typeof args[0] === "object" && args[0] !== null) {
95
+ options = args[0];
96
+ const protocol = options.protocol || "http:";
97
+ const host = options.hostname || options.host || "localhost";
98
+ const port = options.port ? `:${options.port}` : "";
99
+ const path = options.path || "/";
100
+ targetUrl = `${protocol}//${host}${port}${path}`;
101
+ if (typeof args[1] === "function") {
102
+ callback = args[1];
103
+ }
104
+ } else {
105
+ targetUrl = "";
106
+ }
107
+ return { targetUrl, options, callback };
108
+ }
109
+ function createProxiedRequest(originalFn, defaultProtocol) {
110
+ return function proxiedRequest(...args) {
111
+ try {
112
+ const { targetUrl, options, callback } = parseRequestArgs(args);
113
+ if (!targetUrl) {
114
+ debug("could not parse request URL, passing through");
115
+ stats.totalSkipped++;
116
+ return originalFn.apply(
117
+ defaultProtocol === "https:" ? https : http,
118
+ args
119
+ );
120
+ }
121
+ if (!shouldCapture(
122
+ targetUrl,
123
+ config.proxyUrl,
124
+ config.include,
125
+ config.exclude
126
+ )) {
127
+ debug("skipping (filtered):", targetUrl);
128
+ stats.totalSkipped++;
129
+ return originalFn.apply(
130
+ defaultProtocol === "https:" ? https : http,
131
+ args
132
+ );
133
+ }
134
+ const proxyParsed = new URL$1(config.proxyUrl);
135
+ let parsedTarget;
136
+ try {
137
+ parsedTarget = new URL$1(targetUrl);
138
+ } catch {
139
+ debug("invalid URL, passing through:", targetUrl);
140
+ stats.totalSkipped++;
141
+ return originalFn.apply(
142
+ defaultProtocol === "https:" ? https : http,
143
+ args
144
+ );
145
+ }
146
+ const targetOrigin = `${parsedTarget.protocol}//${parsedTarget.host}`;
147
+ const proxiedOptions = {
148
+ ...options,
149
+ protocol: proxyParsed.protocol,
150
+ hostname: proxyParsed.hostname,
151
+ port: proxyParsed.port || (proxyParsed.protocol === "https:" ? 443 : 80),
152
+ path: parsedTarget.pathname + parsedTarget.search,
153
+ headers: {
154
+ ...options.headers,
155
+ "X-Api-Key": config.apiKey,
156
+ "X-Replay-Target": targetOrigin,
157
+ "X-Replay-Env": config.environment,
158
+ // Preserve the original Host header
159
+ Host: parsedTarget.host
160
+ }
161
+ };
162
+ if (config.sessionId) {
163
+ proxiedOptions.headers["X-Replay-Session"] = config.sessionId;
164
+ }
165
+ if (config.timeout) {
166
+ proxiedOptions.timeout = config.timeout;
167
+ }
168
+ debug("capturing:", options.method || "GET", targetUrl, "\u2192", config.proxyUrl);
169
+ stats.totalCaptured++;
170
+ const proxyUrl = new URL$1(`${proxiedOptions.protocol}//${proxiedOptions.hostname}:${proxiedOptions.port}${proxiedOptions.path}`);
171
+ return originalHttpRequest.call(http, proxyUrl, proxiedOptions, callback);
172
+ } catch (err) {
173
+ error("interceptor error, passing through:", err);
174
+ stats.totalErrors++;
175
+ return originalFn.apply(
176
+ defaultProtocol === "https:" ? https : http,
177
+ args
178
+ );
179
+ }
180
+ };
181
+ }
182
+ function createProxiedGet(proxiedRequest) {
183
+ return function proxiedGet(...args) {
184
+ const req = proxiedRequest(...args);
185
+ req.end();
186
+ return req;
187
+ };
188
+ }
189
+ function installHttpInterceptor(cfg) {
190
+ if (active) {
191
+ warn("interceptor already installed, call stop() first");
192
+ return;
193
+ }
194
+ config = {
195
+ ...cfg,
196
+ proxyUrl: PROXY_URL,
197
+ environment: cfg.environment || "development"
198
+ };
199
+ originalHttpRequest = http.request;
200
+ originalHttpGet = http.get;
201
+ originalHttpsRequest = https.request;
202
+ originalHttpsGet = https.get;
203
+ const proxiedHttpRequest = createProxiedRequest(originalHttpRequest, "http:");
204
+ http.request = proxiedHttpRequest;
205
+ http.get = createProxiedGet(proxiedHttpRequest);
206
+ const proxiedHttpsRequest = createProxiedRequest(
207
+ originalHttpsRequest,
208
+ "https:"
209
+ );
210
+ https.request = proxiedHttpsRequest;
211
+ https.get = createProxiedGet(proxiedHttpsRequest);
212
+ stats.totalCaptured = 0;
213
+ stats.totalSkipped = 0;
214
+ stats.totalErrors = 0;
215
+ stats.startedAt = /* @__PURE__ */ new Date();
216
+ active = true;
217
+ debug("http/https interceptor installed");
218
+ }
219
+ function uninstallHttpInterceptor() {
220
+ if (!active) return;
221
+ if (originalHttpRequest) http.request = originalHttpRequest;
222
+ if (originalHttpGet) http.get = originalHttpGet;
223
+ if (originalHttpsRequest) https.request = originalHttpsRequest;
224
+ if (originalHttpsGet) https.get = originalHttpsGet;
225
+ originalHttpRequest = null;
226
+ originalHttpGet = null;
227
+ originalHttpsRequest = null;
228
+ originalHttpsGet = null;
229
+ active = false;
230
+ debug("http/https interceptor removed");
231
+ }
232
+ function isHttpInterceptorActive() {
233
+ return active;
234
+ }
235
+ function getHttpStats() {
236
+ return { ...stats };
237
+ }
238
+
239
+ // src/interceptor-fetch.ts
240
+ var PROXY_URL2 = "http://localhost:8080";
241
+ var originalFetch = null;
242
+ var active2 = false;
243
+ var config2;
244
+ var stats2 = {
245
+ totalCaptured: 0,
246
+ totalSkipped: 0,
247
+ totalErrors: 0,
248
+ startedAt: /* @__PURE__ */ new Date()
249
+ };
250
+ function installFetchInterceptor(cfg) {
251
+ if (active2) {
252
+ warn("fetch interceptor already installed");
253
+ return;
254
+ }
255
+ if (typeof globalThis.fetch !== "function") {
256
+ debug("globalThis.fetch not available, skipping fetch interceptor");
257
+ return;
258
+ }
259
+ config2 = {
260
+ ...cfg,
261
+ proxyUrl: PROXY_URL2,
262
+ environment: cfg.environment || "development"
263
+ };
264
+ originalFetch = globalThis.fetch;
265
+ globalThis.fetch = async function proxiedFetch(input, init2) {
266
+ try {
267
+ let targetUrl;
268
+ if (typeof input === "string") {
269
+ targetUrl = input;
270
+ } else if (input instanceof URL) {
271
+ targetUrl = input.toString();
272
+ } else if (input instanceof Request) {
273
+ targetUrl = input.url;
274
+ } else {
275
+ targetUrl = String(input);
276
+ }
277
+ if (!shouldCapture(
278
+ targetUrl,
279
+ config2.proxyUrl,
280
+ config2.include,
281
+ config2.exclude
282
+ )) {
283
+ debug("fetch skipping (filtered):", targetUrl);
284
+ stats2.totalSkipped++;
285
+ return originalFetch(input, init2);
286
+ }
287
+ let parsedTarget;
288
+ try {
289
+ parsedTarget = new URL(targetUrl);
290
+ } catch {
291
+ debug("fetch: invalid URL, passing through:", targetUrl);
292
+ stats2.totalSkipped++;
293
+ return originalFetch(input, init2);
294
+ }
295
+ const targetOrigin = `${parsedTarget.protocol}//${parsedTarget.host}`;
296
+ const proxyPath = parsedTarget.pathname + parsedTarget.search;
297
+ const proxiedUrl = `${config2.proxyUrl}${proxyPath}`;
298
+ const headers = new Headers(init2?.headers || {});
299
+ if (input instanceof Request) {
300
+ input.headers.forEach((value, key) => {
301
+ if (!headers.has(key)) {
302
+ headers.set(key, value);
303
+ }
304
+ });
305
+ }
306
+ headers.set("X-Api-Key", config2.apiKey);
307
+ headers.set("X-Replay-Target", targetOrigin);
308
+ headers.set("X-Replay-Env", config2.environment);
309
+ headers.set("Host", parsedTarget.host);
310
+ if (config2.sessionId) {
311
+ headers.set("X-Replay-Session", config2.sessionId);
312
+ }
313
+ const proxiedInit = {
314
+ ...init2,
315
+ headers
316
+ };
317
+ if (input instanceof Request) {
318
+ proxiedInit.method = proxiedInit.method || input.method;
319
+ if (!proxiedInit.body && input.body) {
320
+ proxiedInit.body = input.body;
321
+ }
322
+ }
323
+ debug(
324
+ "fetch capturing:",
325
+ proxiedInit.method || "GET",
326
+ targetUrl,
327
+ "\u2192",
328
+ config2.proxyUrl
329
+ );
330
+ stats2.totalCaptured++;
331
+ return originalFetch(proxiedUrl, proxiedInit);
332
+ } catch (err) {
333
+ error("fetch interceptor error, passing through:", err);
334
+ stats2.totalErrors++;
335
+ return originalFetch(input, init2);
336
+ }
337
+ };
338
+ stats2.totalCaptured = 0;
339
+ stats2.totalSkipped = 0;
340
+ stats2.totalErrors = 0;
341
+ stats2.startedAt = /* @__PURE__ */ new Date();
342
+ active2 = true;
343
+ debug("fetch interceptor installed");
344
+ }
345
+ function uninstallFetchInterceptor() {
346
+ if (!active2 || !originalFetch) return;
347
+ globalThis.fetch = originalFetch;
348
+ originalFetch = null;
349
+ active2 = false;
350
+ debug("fetch interceptor removed");
351
+ }
352
+ function isFetchInterceptorActive() {
353
+ return active2;
354
+ }
355
+ function getFetchStats() {
356
+ return { ...stats2 };
357
+ }
358
+
359
+ // src/index.ts
360
+ var initialized = false;
361
+ function init(configOrApiKey) {
362
+ const config3 = typeof configOrApiKey === "string" ? { apiKey: configOrApiKey } : configOrApiKey;
363
+ if (initialized) {
364
+ warn("replayApi.init() called multiple times \u2014 stopping previous instance");
365
+ stop();
366
+ }
367
+ if (!config3.apiKey) {
368
+ throw new Error(
369
+ "[@replayapi/node] apiKey is required. Get one from your ReplayAPI dashboard."
370
+ );
371
+ }
372
+ if (config3.disabled) {
373
+ info("SDK disabled via config, skipping initialization");
374
+ return { stop, isActive, getStats };
375
+ }
376
+ if (config3.debug) {
377
+ setDebug(true);
378
+ }
379
+ config3.environment = config3.environment || process.env.REPLAY_ENVIRONMENT || "development";
380
+ info(`initialized \u2014 env: ${config3.environment}`);
381
+ installHttpInterceptor(config3);
382
+ installFetchInterceptor(config3);
383
+ initialized = true;
384
+ return { stop, isActive, getStats };
385
+ }
386
+ function stop() {
387
+ if (!initialized) return;
388
+ uninstallHttpInterceptor();
389
+ uninstallFetchInterceptor();
390
+ initialized = false;
391
+ info("stopped \u2014 all interceptors removed");
392
+ }
393
+ function isActive() {
394
+ return isHttpInterceptorActive() || isFetchInterceptorActive();
395
+ }
396
+ function getStats() {
397
+ const httpStats = getHttpStats();
398
+ const fetchStats = getFetchStats();
399
+ return {
400
+ totalCaptured: httpStats.totalCaptured + fetchStats.totalCaptured,
401
+ totalSkipped: httpStats.totalSkipped + fetchStats.totalSkipped,
402
+ totalErrors: httpStats.totalErrors + fetchStats.totalErrors,
403
+ startedAt: httpStats.startedAt < fetchStats.startedAt ? httpStats.startedAt : fetchStats.startedAt
404
+ };
405
+ }
406
+ var replayApi = {
407
+ init,
408
+ stop,
409
+ isActive,
410
+ getStats
411
+ };
412
+
413
+ export { getStats, init, isActive, replayApi, stop };
414
+ //# sourceMappingURL=index.mjs.map
415
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/matcher.ts","../src/logger.ts","../src/interceptor-http.ts","../src/interceptor-fetch.ts","../src/index.ts"],"names":["URL","PROXY_URL","active","config","stats","init"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\r\n * ReplayAPI SDK — URL matching utilities\r\n */\r\n\r\n/**\r\n * Check if a URL matches any of the given patterns.\r\n * Patterns can be strings (substring match) or RegExp.\r\n */\r\nexport function matchesPatterns(\r\n url: string,\r\n patterns: Array<string | RegExp>\r\n): boolean {\r\n return patterns.some((pattern) => {\r\n if (typeof pattern === \"string\") {\r\n return url.includes(pattern);\r\n }\r\n return pattern.test(url);\r\n });\r\n}\r\n\r\n/**\r\n * Determine if a request URL should be captured based on include/exclude filters.\r\n */\r\nexport function shouldCapture(\r\n url: string,\r\n proxyUrl: string,\r\n include?: Array<string | RegExp>,\r\n exclude?: Array<string | RegExp>\r\n): boolean {\r\n // Never capture requests to the proxy itself\r\n if (url.startsWith(proxyUrl)) {\r\n return false;\r\n }\r\n\r\n // Never capture requests to ReplayAPI internal paths\r\n if (url.includes(\"/__replay/\")) {\r\n return false;\r\n }\r\n\r\n // If include patterns are set, URL must match at least one\r\n if (include && include.length > 0) {\r\n if (!matchesPatterns(url, include)) {\r\n return false;\r\n }\r\n }\r\n\r\n // If exclude patterns are set, URL must not match any\r\n if (exclude && exclude.length > 0) {\r\n if (matchesPatterns(url, exclude)) {\r\n return false;\r\n }\r\n }\r\n\r\n return true;\r\n}\r\n","/**\r\n * ReplayAPI SDK — Simple logger\r\n */\r\n\r\nlet debugEnabled = false;\r\n\r\nexport function setDebug(enabled: boolean): void {\r\n debugEnabled = enabled;\r\n}\r\n\r\nexport function debug(...args: unknown[]): void {\r\n if (debugEnabled) {\r\n console.log(\"[replayapi]\", ...args);\r\n }\r\n}\r\n\r\nexport function info(...args: unknown[]): void {\r\n console.log(\"[replayapi]\", ...args);\r\n}\r\n\r\nexport function warn(...args: unknown[]): void {\r\n console.warn(\"[replayapi]\", ...args);\r\n}\r\n\r\nexport function error(...args: unknown[]): void {\r\n console.error(\"[replayapi]\", ...args);\r\n}\r\n","/**\r\n * ReplayAPI SDK — HTTP/HTTPS interceptor\r\n *\r\n * Monkey-patches Node.js http.request and https.request to redirect\r\n * outgoing requests through the ReplayAPI proxy. The proxy captures\r\n * the traffic and forwards it to the original target.\r\n *\r\n * How it works:\r\n * 1. Original request: GET https://api.example.com/users\r\n * 2. SDK rewrites to: GET http://proxy:8080/users\r\n * with headers:\r\n * X-Api-Key: rp_...\r\n * X-Replay-Target: https://api.example.com\r\n * X-Replay-Env: staging\r\n * 3. Proxy captures, forwards to target, returns response transparently\r\n */\r\n\r\nimport http from \"node:http\";\r\nimport https from \"node:https\";\r\nimport { URL } from \"node:url\";\r\nimport type { ReplayApiConfig, CaptureStats } from \"./types.js\";\r\nimport { shouldCapture } from \"./matcher.js\";\r\nimport * as log from \"./logger.js\";\r\n\r\n// Store original implementations\r\nlet originalHttpRequest: typeof http.request | null = null;\r\nlet originalHttpGet: typeof http.get | null = null;\r\nlet originalHttpsRequest: typeof https.request | null = null;\r\nlet originalHttpsGet: typeof https.get | null = null;\r\n\r\nconst PROXY_URL = \"http://localhost:8080\";\r\n\r\n// Track state\r\nlet active = false;\r\nlet config: Required<\r\n Pick<ReplayApiConfig, \"apiKey\" | \"environment\">\r\n> &\r\n ReplayApiConfig & { proxyUrl: string };\r\n\r\nconst stats: CaptureStats = {\r\n totalCaptured: 0,\r\n totalSkipped: 0,\r\n totalErrors: 0,\r\n startedAt: new Date(),\r\n};\r\n\r\n/**\r\n * Parse the various argument formats that http.request accepts:\r\n * - (url: string, options?, callback?)\r\n * - (url: URL, options?, callback?)\r\n * - (options, callback?)\r\n */\r\nfunction parseRequestArgs(\r\n args: unknown[]\r\n): {\r\n targetUrl: string;\r\n options: http.RequestOptions;\r\n callback?: (res: http.IncomingMessage) => void;\r\n} {\r\n let targetUrl: string;\r\n let options: http.RequestOptions = {};\r\n let callback: ((res: http.IncomingMessage) => void) | undefined;\r\n\r\n if (typeof args[0] === \"string\") {\r\n targetUrl = args[0];\r\n if (typeof args[1] === \"function\") {\r\n callback = args[1] as (res: http.IncomingMessage) => void;\r\n } else if (typeof args[1] === \"object\" && args[1] !== null) {\r\n options = args[1] as http.RequestOptions;\r\n if (typeof args[2] === \"function\") {\r\n callback = args[2] as (res: http.IncomingMessage) => void;\r\n }\r\n }\r\n } else if (args[0] instanceof URL) {\r\n targetUrl = args[0].toString();\r\n if (typeof args[1] === \"function\") {\r\n callback = args[1] as (res: http.IncomingMessage) => void;\r\n } else if (typeof args[1] === \"object\" && args[1] !== null) {\r\n options = args[1] as http.RequestOptions;\r\n if (typeof args[2] === \"function\") {\r\n callback = args[2] as (res: http.IncomingMessage) => void;\r\n }\r\n }\r\n } else if (typeof args[0] === \"object\" && args[0] !== null) {\r\n options = args[0] as http.RequestOptions;\r\n const protocol = (options as { protocol?: string }).protocol || \"http:\";\r\n const host = options.hostname || options.host || \"localhost\";\r\n const port = options.port ? `:${options.port}` : \"\";\r\n const path = options.path || \"/\";\r\n targetUrl = `${protocol}//${host}${port}${path}`;\r\n if (typeof args[1] === \"function\") {\r\n callback = args[1] as (res: http.IncomingMessage) => void;\r\n }\r\n } else {\r\n targetUrl = \"\";\r\n }\r\n\r\n return { targetUrl, options, callback };\r\n}\r\n\r\n/**\r\n * Create a proxied version of http.request / https.request\r\n */\r\nfunction createProxiedRequest(\r\n originalFn: typeof http.request,\r\n defaultProtocol: \"http:\" | \"https:\"\r\n): typeof http.request {\r\n return function proxiedRequest(\r\n ...args: unknown[]\r\n ): http.ClientRequest {\r\n try {\r\n const { targetUrl, options, callback } = parseRequestArgs(args);\r\n\r\n if (!targetUrl) {\r\n log.debug(\"could not parse request URL, passing through\");\r\n stats.totalSkipped++;\r\n return (originalFn as Function).apply(\r\n defaultProtocol === \"https:\" ? https : http,\r\n args\r\n ) as http.ClientRequest;\r\n }\r\n\r\n // Check if this URL should be captured\r\n if (\r\n !shouldCapture(\r\n targetUrl,\r\n config.proxyUrl,\r\n config.include,\r\n config.exclude\r\n )\r\n ) {\r\n log.debug(\"skipping (filtered):\", targetUrl);\r\n stats.totalSkipped++;\r\n return (originalFn as Function).apply(\r\n defaultProtocol === \"https:\" ? https : http,\r\n args\r\n ) as http.ClientRequest;\r\n }\r\n\r\n // Parse the proxy URL\r\n const proxyParsed = new URL(config.proxyUrl);\r\n\r\n // Parse the target URL to extract origin and path\r\n let parsedTarget: URL;\r\n try {\r\n parsedTarget = new URL(targetUrl);\r\n } catch {\r\n log.debug(\"invalid URL, passing through:\", targetUrl);\r\n stats.totalSkipped++;\r\n return (originalFn as Function).apply(\r\n defaultProtocol === \"https:\" ? https : http,\r\n args\r\n ) as http.ClientRequest;\r\n }\r\n\r\n const targetOrigin = `${parsedTarget.protocol}//${parsedTarget.host}`;\r\n\r\n // Build proxied options: send to proxy, with X-Replay-Target header\r\n const proxiedOptions: http.RequestOptions = {\r\n ...options,\r\n protocol: proxyParsed.protocol,\r\n hostname: proxyParsed.hostname,\r\n port: proxyParsed.port || (proxyParsed.protocol === \"https:\" ? 443 : 80),\r\n path: parsedTarget.pathname + parsedTarget.search,\r\n headers: {\r\n ...options.headers,\r\n \"X-Api-Key\": config.apiKey,\r\n \"X-Replay-Target\": targetOrigin,\r\n \"X-Replay-Env\": config.environment,\r\n // Preserve the original Host header\r\n Host: parsedTarget.host,\r\n },\r\n };\r\n\r\n // Add session ID if configured\r\n if (config.sessionId) {\r\n (proxiedOptions.headers as Record<string, string>)[\"X-Replay-Session\"] =\r\n config.sessionId;\r\n }\r\n\r\n // Set timeout if configured\r\n if (config.timeout) {\r\n proxiedOptions.timeout = config.timeout;\r\n }\r\n\r\n log.debug(\"capturing:\", options.method || \"GET\", targetUrl, \"→\", config.proxyUrl);\r\n stats.totalCaptured++;\r\n\r\n // Use http.request (not https) since we're talking to the proxy over HTTP\r\n const proxyUrl = new URL(`${proxiedOptions.protocol}//${proxiedOptions.hostname}:${proxiedOptions.port}${proxiedOptions.path}`);\r\n return originalHttpRequest!.call(http, proxyUrl, proxiedOptions, callback);\r\n } catch (err) {\r\n log.error(\"interceptor error, passing through:\", err);\r\n stats.totalErrors++;\r\n return (originalFn as Function).apply(\r\n defaultProtocol === \"https:\" ? https : http,\r\n args\r\n ) as http.ClientRequest;\r\n }\r\n } as typeof http.request;\r\n}\r\n\r\n/**\r\n * Create a proxied version of http.get / https.get\r\n */\r\nfunction createProxiedGet(\r\n proxiedRequest: typeof http.request\r\n): typeof http.get {\r\n return function proxiedGet(...args: unknown[]): http.ClientRequest {\r\n const req = (proxiedRequest as Function)(...args) as http.ClientRequest;\r\n req.end();\r\n return req;\r\n } as typeof http.get;\r\n}\r\n\r\n/**\r\n * Install the HTTP/HTTPS interceptors\r\n */\r\nexport function installHttpInterceptor(cfg: ReplayApiConfig): void {\r\n if (active) {\r\n log.warn(\"interceptor already installed, call stop() first\");\r\n return;\r\n }\r\n\r\n config = {\r\n ...cfg,\r\n proxyUrl: PROXY_URL,\r\n environment: cfg.environment || \"development\",\r\n };\r\n\r\n // Save originals\r\n originalHttpRequest = http.request;\r\n originalHttpGet = http.get;\r\n originalHttpsRequest = https.request;\r\n originalHttpsGet = https.get;\r\n\r\n // Patch http\r\n const proxiedHttpRequest = createProxiedRequest(originalHttpRequest, \"http:\");\r\n http.request = proxiedHttpRequest;\r\n http.get = createProxiedGet(proxiedHttpRequest);\r\n\r\n // Patch https\r\n const proxiedHttpsRequest = createProxiedRequest(\r\n originalHttpsRequest,\r\n \"https:\"\r\n );\r\n https.request = proxiedHttpsRequest;\r\n https.get = createProxiedGet(proxiedHttpsRequest);\r\n\r\n // Reset stats\r\n stats.totalCaptured = 0;\r\n stats.totalSkipped = 0;\r\n stats.totalErrors = 0;\r\n stats.startedAt = new Date();\r\n\r\n active = true;\r\n log.debug(\"http/https interceptor installed\");\r\n}\r\n\r\n/**\r\n * Uninstall the HTTP/HTTPS interceptors\r\n */\r\nexport function uninstallHttpInterceptor(): void {\r\n if (!active) return;\r\n\r\n if (originalHttpRequest) http.request = originalHttpRequest;\r\n if (originalHttpGet) http.get = originalHttpGet;\r\n if (originalHttpsRequest) https.request = originalHttpsRequest;\r\n if (originalHttpsGet) https.get = originalHttpsGet;\r\n\r\n originalHttpRequest = null;\r\n originalHttpGet = null;\r\n originalHttpsRequest = null;\r\n originalHttpsGet = null;\r\n\r\n active = false;\r\n log.debug(\"http/https interceptor removed\");\r\n}\r\n\r\nexport function isHttpInterceptorActive(): boolean {\r\n return active;\r\n}\r\n\r\nexport function getHttpStats(): CaptureStats {\r\n return { ...stats };\r\n}\r\n","/**\r\n * ReplayAPI SDK — Global fetch() interceptor\r\n *\r\n * Patches globalThis.fetch to route requests through the ReplayAPI proxy.\r\n * Works with Node.js 18+ native fetch and any polyfills that set globalThis.fetch.\r\n */\r\n\r\nimport type { ReplayApiConfig, CaptureStats } from \"./types.js\";\r\nimport { shouldCapture } from \"./matcher.js\";\r\nimport * as log from \"./logger.js\";\r\n\r\nconst PROXY_URL = \"http://localhost:8080\";\r\n\r\nlet originalFetch: typeof globalThis.fetch | null = null;\r\nlet active = false;\r\nlet config: Required<\r\n Pick<ReplayApiConfig, \"apiKey\" | \"environment\">\r\n> &\r\n ReplayApiConfig & { proxyUrl: string };\r\n\r\nconst stats: CaptureStats = {\r\n totalCaptured: 0,\r\n totalSkipped: 0,\r\n totalErrors: 0,\r\n startedAt: new Date(),\r\n};\r\n\r\n/**\r\n * Install the fetch interceptor\r\n */\r\nexport function installFetchInterceptor(cfg: ReplayApiConfig): void {\r\n if (active) {\r\n log.warn(\"fetch interceptor already installed\");\r\n return;\r\n }\r\n\r\n // Only patch if fetch exists (Node.js 18+)\r\n if (typeof globalThis.fetch !== \"function\") {\r\n log.debug(\"globalThis.fetch not available, skipping fetch interceptor\");\r\n return;\r\n }\r\n\r\n config = {\r\n ...cfg,\r\n proxyUrl: PROXY_URL,\r\n environment: cfg.environment || \"development\",\r\n };\r\n\r\n originalFetch = globalThis.fetch;\r\n\r\n globalThis.fetch = async function proxiedFetch(\r\n input: string | URL | Request,\r\n init?: RequestInit\r\n ): Promise<Response> {\r\n try {\r\n // Resolve the target URL\r\n let targetUrl: string;\r\n if (typeof input === \"string\") {\r\n targetUrl = input;\r\n } else if (input instanceof URL) {\r\n targetUrl = input.toString();\r\n } else if (input instanceof Request) {\r\n targetUrl = input.url;\r\n } else {\r\n targetUrl = String(input);\r\n }\r\n\r\n // Check if this URL should be captured\r\n if (\r\n !shouldCapture(\r\n targetUrl,\r\n config.proxyUrl,\r\n config.include,\r\n config.exclude\r\n )\r\n ) {\r\n log.debug(\"fetch skipping (filtered):\", targetUrl);\r\n stats.totalSkipped++;\r\n return originalFetch!(input, init);\r\n }\r\n\r\n // Parse target URL\r\n let parsedTarget: URL;\r\n try {\r\n parsedTarget = new URL(targetUrl);\r\n } catch {\r\n log.debug(\"fetch: invalid URL, passing through:\", targetUrl);\r\n stats.totalSkipped++;\r\n return originalFetch!(input, init);\r\n }\r\n\r\n const targetOrigin = `${parsedTarget.protocol}//${parsedTarget.host}`;\r\n const proxyPath = parsedTarget.pathname + parsedTarget.search;\r\n const proxiedUrl = `${config.proxyUrl}${proxyPath}`;\r\n\r\n // Build headers\r\n const headers = new Headers(init?.headers || {});\r\n\r\n // If input is a Request, merge its headers too\r\n if (input instanceof Request) {\r\n input.headers.forEach((value, key) => {\r\n if (!headers.has(key)) {\r\n headers.set(key, value);\r\n }\r\n });\r\n }\r\n\r\n // Add ReplayAPI headers\r\n headers.set(\"X-Api-Key\", config.apiKey);\r\n headers.set(\"X-Replay-Target\", targetOrigin);\r\n headers.set(\"X-Replay-Env\", config.environment);\r\n headers.set(\"Host\", parsedTarget.host);\r\n\r\n if (config.sessionId) {\r\n headers.set(\"X-Replay-Session\", config.sessionId);\r\n }\r\n\r\n // Build proxied init\r\n const proxiedInit: RequestInit = {\r\n ...init,\r\n headers,\r\n };\r\n\r\n // If input is a Request, preserve method and body\r\n if (input instanceof Request) {\r\n proxiedInit.method = proxiedInit.method || input.method;\r\n if (!proxiedInit.body && input.body) {\r\n proxiedInit.body = input.body;\r\n }\r\n }\r\n\r\n log.debug(\r\n \"fetch capturing:\",\r\n proxiedInit.method || \"GET\",\r\n targetUrl,\r\n \"→\",\r\n config.proxyUrl\r\n );\r\n stats.totalCaptured++;\r\n\r\n return originalFetch!(proxiedUrl, proxiedInit);\r\n } catch (err) {\r\n log.error(\"fetch interceptor error, passing through:\", err);\r\n stats.totalErrors++;\r\n return originalFetch!(input, init);\r\n }\r\n };\r\n\r\n // Reset stats\r\n stats.totalCaptured = 0;\r\n stats.totalSkipped = 0;\r\n stats.totalErrors = 0;\r\n stats.startedAt = new Date();\r\n\r\n active = true;\r\n log.debug(\"fetch interceptor installed\");\r\n}\r\n\r\n/**\r\n * Uninstall the fetch interceptor\r\n */\r\nexport function uninstallFetchInterceptor(): void {\r\n if (!active || !originalFetch) return;\r\n\r\n globalThis.fetch = originalFetch;\r\n originalFetch = null;\r\n active = false;\r\n log.debug(\"fetch interceptor removed\");\r\n}\r\n\r\nexport function isFetchInterceptorActive(): boolean {\r\n return active;\r\n}\r\n\r\nexport function getFetchStats(): CaptureStats {\r\n return { ...stats };\r\n}\r\n","/**\r\n * @replayapi/node — ReplayAPI SDK for Node.js\r\n *\r\n * Automatically captures outgoing HTTP traffic and routes it through\r\n * the ReplayAPI proxy for recording, analysis, and replay testing.\r\n *\r\n * @example\r\n * ```ts\r\n * import { replayApi } from '@replayapi/node'\r\n *\r\n * // Simplest — just pass your API key\r\n * replayApi.init('rp_your_api_key')\r\n *\r\n * // Or with options\r\n * replayApi.init({ apiKey: 'rp_your_api_key', environment: 'staging' })\r\n *\r\n * // All outgoing HTTP/fetch calls are now captured automatically\r\n * ```\r\n */\r\n\r\nimport type { ReplayApiConfig, ReplayApiInstance, CaptureStats } from \"./types.js\";\r\nimport {\r\n installHttpInterceptor,\r\n uninstallHttpInterceptor,\r\n isHttpInterceptorActive,\r\n getHttpStats,\r\n} from \"./interceptor-http.js\";\r\nimport {\r\n installFetchInterceptor,\r\n uninstallFetchInterceptor,\r\n isFetchInterceptorActive,\r\n getFetchStats,\r\n} from \"./interceptor-fetch.js\";\r\nimport { setDebug } from \"./logger.js\";\r\nimport * as log from \"./logger.js\";\r\n\r\nlet initialized = false;\r\n\r\n/**\r\n * Initialize the ReplayAPI SDK.\r\n *\r\n * Call this once at application startup, before any outgoing HTTP requests.\r\n * The SDK will automatically intercept `http.request`, `https.request`,\r\n * and `fetch` to route traffic through the ReplayAPI proxy.\r\n *\r\n * @example\r\n * ```ts\r\n * // Just an API key\r\n * replayApi.init('rp_your_api_key')\r\n *\r\n * // Or with options\r\n * replayApi.init({ apiKey: process.env.REPLAY_API_KEY!, environment: 'staging' })\r\n * ```\r\n */\r\nfunction init(configOrApiKey: ReplayApiConfig | string): ReplayApiInstance {\r\n // Accept a plain string as shorthand for { apiKey: string }\r\n const config: ReplayApiConfig =\r\n typeof configOrApiKey === \"string\" ? { apiKey: configOrApiKey } : configOrApiKey;\r\n\r\n if (initialized) {\r\n log.warn(\"replayApi.init() called multiple times — stopping previous instance\");\r\n stop();\r\n }\r\n\r\n // Validate config\r\n if (!config.apiKey) {\r\n throw new Error(\r\n \"[@replayapi/node] apiKey is required. Get one from your ReplayAPI dashboard.\"\r\n );\r\n }\r\n\r\n if (config.disabled) {\r\n log.info(\"SDK disabled via config, skipping initialization\");\r\n return { stop, isActive, getStats };\r\n }\r\n\r\n // Enable debug logging if requested\r\n if (config.debug) {\r\n setDebug(true);\r\n }\r\n\r\n // Resolve environment with env var fallback\r\n config.environment =\r\n config.environment || process.env.REPLAY_ENVIRONMENT || \"development\";\r\n\r\n log.info(`initialized — env: ${config.environment}`);\r\n\r\n // Install interceptors\r\n installHttpInterceptor(config);\r\n installFetchInterceptor(config);\r\n\r\n initialized = true;\r\n\r\n return { stop, isActive, getStats };\r\n}\r\n\r\n/**\r\n * Stop the SDK and restore original HTTP/fetch behavior.\r\n */\r\nfunction stop(): void {\r\n if (!initialized) return;\r\n\r\n uninstallHttpInterceptor();\r\n uninstallFetchInterceptor();\r\n\r\n initialized = false;\r\n log.info(\"stopped — all interceptors removed\");\r\n}\r\n\r\n/**\r\n * Check if the SDK is currently intercepting traffic.\r\n */\r\nfunction isActive(): boolean {\r\n return isHttpInterceptorActive() || isFetchInterceptorActive();\r\n}\r\n\r\n/**\r\n * Get combined capture statistics from all interceptors.\r\n */\r\nfunction getStats(): CaptureStats {\r\n const httpStats = getHttpStats();\r\n const fetchStats = getFetchStats();\r\n\r\n return {\r\n totalCaptured: httpStats.totalCaptured + fetchStats.totalCaptured,\r\n totalSkipped: httpStats.totalSkipped + fetchStats.totalSkipped,\r\n totalErrors: httpStats.totalErrors + fetchStats.totalErrors,\r\n startedAt: httpStats.startedAt < fetchStats.startedAt\r\n ? httpStats.startedAt\r\n : fetchStats.startedAt,\r\n };\r\n}\r\n\r\n/**\r\n * The main ReplayAPI SDK instance.\r\n */\r\nexport const replayApi = {\r\n init,\r\n stop,\r\n isActive,\r\n getStats,\r\n};\r\n\r\n// Named exports for flexibility\r\nexport { init, stop, isActive, getStats };\r\n\r\n// Re-export types\r\nexport type { ReplayApiConfig, ReplayApiInstance, CaptureStats } from \"./types.js\";\r\n"]}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@thaparoyal/replayapi",
3
+ "version": "0.1.0",
4
+ "description": "ReplayAPI SDK for Node.js — Capture HTTP traffic automatically",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsup",
21
+ "dev": "tsup --watch",
22
+ "typecheck": "tsc --noEmit",
23
+ "clean": "rm -rf dist"
24
+ },
25
+ "keywords": [
26
+ "api",
27
+ "replay",
28
+ "traffic",
29
+ "capture",
30
+ "testing",
31
+ "proxy",
32
+ "http",
33
+ "observability"
34
+ ],
35
+ "license": "MIT",
36
+ "devDependencies": {
37
+ "tsup": "^8.0.0",
38
+ "typescript": "^5.7.0",
39
+ "@types/node": "^22.0.0"
40
+ },
41
+ "engines": {
42
+ "node": ">=18.0.0"
43
+ }
44
+ }