@thaparoyal/replayapi 0.1.1 → 0.1.2

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/dist/index.d.mts CHANGED
@@ -1,3 +1,5 @@
1
+ import { IncomingMessage, ServerResponse } from 'http';
2
+
1
3
  /**
2
4
  * ReplayAPI SDK Types
3
5
  */
@@ -56,6 +58,42 @@ interface CaptureStats {
56
58
  startedAt: Date;
57
59
  }
58
60
 
61
+ /**
62
+ * ReplayAPI SDK — Express/Connect middleware
63
+ *
64
+ * Captures incoming HTTP requests and responses,
65
+ * then sends them asynchronously to the ReplayAPI backend.
66
+ *
67
+ * @example
68
+ * ```ts
69
+ * import express from 'express';
70
+ * import { replayApi } from '@thaparoyal/replayapi';
71
+ *
72
+ * const app = express();
73
+ * replayApi.init('rp_your_api_key');
74
+ *
75
+ * // Add middleware BEFORE your routes
76
+ * app.use(replayApi.middleware());
77
+ * ```
78
+ */
79
+
80
+ /**
81
+ * Create the Express/Connect middleware function.
82
+ *
83
+ * Options:
84
+ * - `exclude` — URL patterns to skip (e.g. health checks)
85
+ * - `maxBodySize` — max body size to capture in bytes (default: 1MB)
86
+ */
87
+ interface MiddlewareOptions {
88
+ /** URL patterns to exclude from capture */
89
+ exclude?: Array<string | RegExp>;
90
+ /** Max body size to capture in bytes (default: 1MB) */
91
+ maxBodySize?: number;
92
+ }
93
+ declare function createMiddleware(options?: MiddlewareOptions): (req: IncomingMessage & {
94
+ body?: unknown;
95
+ }, res: ServerResponse, next: (err?: unknown) => void) => void;
96
+
59
97
  /**
60
98
  * @replayapi/node — ReplayAPI SDK for Node.js
61
99
  *
@@ -113,6 +151,7 @@ declare const replayApi: {
113
151
  stop: typeof stop;
114
152
  isActive: typeof isActive;
115
153
  getStats: typeof getStats;
154
+ middleware: typeof createMiddleware;
116
155
  };
117
156
 
118
- export { type CaptureStats, type ReplayApiConfig, type ReplayApiInstance, getStats, init, isActive, replayApi, stop };
157
+ export { type CaptureStats, type MiddlewareOptions, type ReplayApiConfig, type ReplayApiInstance, createMiddleware, getStats, init, isActive, replayApi, stop };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { IncomingMessage, ServerResponse } from 'http';
2
+
1
3
  /**
2
4
  * ReplayAPI SDK Types
3
5
  */
@@ -56,6 +58,42 @@ interface CaptureStats {
56
58
  startedAt: Date;
57
59
  }
58
60
 
61
+ /**
62
+ * ReplayAPI SDK — Express/Connect middleware
63
+ *
64
+ * Captures incoming HTTP requests and responses,
65
+ * then sends them asynchronously to the ReplayAPI backend.
66
+ *
67
+ * @example
68
+ * ```ts
69
+ * import express from 'express';
70
+ * import { replayApi } from '@thaparoyal/replayapi';
71
+ *
72
+ * const app = express();
73
+ * replayApi.init('rp_your_api_key');
74
+ *
75
+ * // Add middleware BEFORE your routes
76
+ * app.use(replayApi.middleware());
77
+ * ```
78
+ */
79
+
80
+ /**
81
+ * Create the Express/Connect middleware function.
82
+ *
83
+ * Options:
84
+ * - `exclude` — URL patterns to skip (e.g. health checks)
85
+ * - `maxBodySize` — max body size to capture in bytes (default: 1MB)
86
+ */
87
+ interface MiddlewareOptions {
88
+ /** URL patterns to exclude from capture */
89
+ exclude?: Array<string | RegExp>;
90
+ /** Max body size to capture in bytes (default: 1MB) */
91
+ maxBodySize?: number;
92
+ }
93
+ declare function createMiddleware(options?: MiddlewareOptions): (req: IncomingMessage & {
94
+ body?: unknown;
95
+ }, res: ServerResponse, next: (err?: unknown) => void) => void;
96
+
59
97
  /**
60
98
  * @replayapi/node — ReplayAPI SDK for Node.js
61
99
  *
@@ -113,6 +151,7 @@ declare const replayApi: {
113
151
  stop: typeof stop;
114
152
  isActive: typeof isActive;
115
153
  getStats: typeof getStats;
154
+ middleware: typeof createMiddleware;
116
155
  };
117
156
 
118
- export { type CaptureStats, type ReplayApiConfig, type ReplayApiInstance, getStats, init, isActive, replayApi, stop };
157
+ export { type CaptureStats, type MiddlewareOptions, type ReplayApiConfig, type ReplayApiInstance, createMiddleware, getStats, init, isActive, replayApi, stop };
package/dist/index.js CHANGED
@@ -363,6 +363,176 @@ function getFetchStats() {
363
363
  return { ...stats2 };
364
364
  }
365
365
 
366
+ // src/middleware.ts
367
+ var API_URL = process.env.REPLAY_API_URL || "http://localhost:8000";
368
+ var apiKey = null;
369
+ var environment = "development";
370
+ var captureCount = 0;
371
+ var errorCount = 0;
372
+ function configureMiddleware(key, env) {
373
+ apiKey = key;
374
+ environment = env;
375
+ }
376
+ function mapEnvironment(env) {
377
+ const map = {
378
+ development: "dev",
379
+ dev: "dev",
380
+ local: "local",
381
+ staging: "staging",
382
+ production: "production",
383
+ prod: "production",
384
+ test: "test",
385
+ testing: "test"
386
+ };
387
+ return map[env.toLowerCase()] || "dev";
388
+ }
389
+ async function sendToBackend(event) {
390
+ try {
391
+ const url = `${API_URL}/api/ingest`;
392
+ const { default: http2 } = await import('http');
393
+ const { default: https2 } = await import('https');
394
+ const parsedUrl = new URL(url);
395
+ const transport = parsedUrl.protocol === "https:" ? https2 : http2;
396
+ const body = JSON.stringify(event);
397
+ const req = transport.request(
398
+ {
399
+ hostname: parsedUrl.hostname,
400
+ port: parsedUrl.port,
401
+ path: parsedUrl.pathname,
402
+ method: "POST",
403
+ headers: {
404
+ "Content-Type": "application/json",
405
+ "X-API-Key": apiKey || "",
406
+ "Content-Length": Buffer.byteLength(body)
407
+ }
408
+ },
409
+ (res) => {
410
+ res.resume();
411
+ if (res.statusCode && res.statusCode >= 400) {
412
+ debug(`ingest response: ${res.statusCode}`);
413
+ }
414
+ }
415
+ );
416
+ req.on("error", (err) => {
417
+ errorCount++;
418
+ debug(`ingest error: ${err.message}`);
419
+ });
420
+ req.write(body);
421
+ req.end();
422
+ captureCount++;
423
+ } catch (err) {
424
+ errorCount++;
425
+ debug(`ingest send error: ${err.message}`);
426
+ }
427
+ }
428
+ function collectResponseBody(res, callback) {
429
+ const chunks = [];
430
+ const originalWrite = res.write.bind(res);
431
+ const originalEnd = res.end.bind(res);
432
+ res.write = function(chunk, ...args) {
433
+ if (chunk) {
434
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
435
+ }
436
+ return originalWrite(chunk, ...args);
437
+ };
438
+ res.end = function(chunk, ...args) {
439
+ if (chunk && typeof chunk !== "function") {
440
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
441
+ }
442
+ const body = Buffer.concat(chunks).toString("utf-8");
443
+ callback(body);
444
+ return originalEnd(chunk, ...args);
445
+ };
446
+ }
447
+ function tryParseJson(str) {
448
+ try {
449
+ return JSON.parse(str);
450
+ } catch {
451
+ return void 0;
452
+ }
453
+ }
454
+ function createMiddleware(options = {}) {
455
+ const { exclude = [], maxBodySize = 1024 * 1024 } = options;
456
+ return function replayMiddleware(req, res, next) {
457
+ if (!apiKey) {
458
+ return next();
459
+ }
460
+ const url = req.url || "/";
461
+ for (const pattern of exclude) {
462
+ if (typeof pattern === "string" && url.includes(pattern)) return next();
463
+ if (pattern instanceof RegExp && pattern.test(url)) return next();
464
+ }
465
+ const startTime = Date.now();
466
+ const startHrTime = process.hrtime.bigint();
467
+ const method = (req.method || "GET").toUpperCase();
468
+ const host = (req.headers.host || "unknown").split(":")[0];
469
+ const protocol = req.protocol === "https" ? "http" : "http";
470
+ let path = url;
471
+ let queryParams = {};
472
+ const qIndex = url.indexOf("?");
473
+ if (qIndex !== -1) {
474
+ path = url.substring(0, qIndex);
475
+ try {
476
+ const searchParams = new URLSearchParams(url.substring(qIndex + 1));
477
+ queryParams = Object.fromEntries(searchParams.entries());
478
+ } catch {
479
+ }
480
+ }
481
+ const requestHeaders = {};
482
+ for (const [key, val] of Object.entries(req.headers)) {
483
+ if (val) {
484
+ requestHeaders[key] = Array.isArray(val) ? val.join(", ") : val;
485
+ }
486
+ }
487
+ let requestBody = void 0;
488
+ if (req.body !== void 0) {
489
+ const bodyStr = typeof req.body === "string" ? req.body : JSON.stringify(req.body);
490
+ if (bodyStr.length <= maxBodySize) {
491
+ requestBody = typeof req.body === "string" ? tryParseJson(req.body) || req.body : req.body;
492
+ }
493
+ }
494
+ collectResponseBody(res, (responseBodyStr) => {
495
+ const durationMs = Number(process.hrtime.bigint() - startHrTime) / 1e6;
496
+ const statusCode = res.statusCode;
497
+ const responseHeaders = {};
498
+ const rawHeaders = res.getHeaders();
499
+ for (const [key, val] of Object.entries(rawHeaders)) {
500
+ if (val) {
501
+ responseHeaders[key] = Array.isArray(val) ? val.join(", ") : String(val);
502
+ }
503
+ }
504
+ let responseBody = void 0;
505
+ if (responseBodyStr.length <= maxBodySize) {
506
+ responseBody = tryParseJson(responseBodyStr) || responseBodyStr;
507
+ }
508
+ const event = {
509
+ method,
510
+ protocol,
511
+ host,
512
+ path,
513
+ queryParams,
514
+ requestHeaders,
515
+ requestBodyInline: requestBody,
516
+ requestSizeBytes: requestBody ? Buffer.byteLength(JSON.stringify(requestBody)) : 0,
517
+ statusCode,
518
+ responseHeaders,
519
+ responseBodyInline: responseBody,
520
+ responseSizeBytes: responseBodyStr.length,
521
+ startedAt: new Date(startTime).toISOString(),
522
+ durationMs: Math.round(durationMs * 100) / 100,
523
+ source: "sdk",
524
+ environment: mapEnvironment(environment),
525
+ clientIp: req.headers["x-forwarded-for"] ? String(req.headers["x-forwarded-for"]).split(",")[0].trim() : req.socket?.remoteAddress || null,
526
+ tags: [],
527
+ metadata: {}
528
+ };
529
+ debug(`captured: ${method} ${path} ${statusCode} (${event.durationMs}ms)`);
530
+ sendToBackend(event);
531
+ });
532
+ next();
533
+ };
534
+ }
535
+
366
536
  // src/index.ts
367
537
  var initialized = false;
368
538
  function init(configOrApiKey) {
@@ -385,6 +555,7 @@ function init(configOrApiKey) {
385
555
  }
386
556
  config3.environment = config3.environment || process.env.REPLAY_ENVIRONMENT || "development";
387
557
  info(`initialized \u2014 env: ${config3.environment}`);
558
+ configureMiddleware(config3.apiKey, config3.environment);
388
559
  installHttpInterceptor(config3);
389
560
  installFetchInterceptor(config3);
390
561
  initialized = true;
@@ -414,9 +585,11 @@ var replayApi = {
414
585
  init,
415
586
  stop,
416
587
  isActive,
417
- getStats
588
+ getStats,
589
+ middleware: createMiddleware
418
590
  };
419
591
 
592
+ exports.createMiddleware = createMiddleware;
420
593
  exports.getStats = getStats;
421
594
  exports.init = init;
422
595
  exports.isActive = isActive;
package/dist/index.js.map CHANGED
@@ -1 +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 = process.env.REPLAY_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 = process.env.REPLAY_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"]}
1
+ {"version":3,"sources":["../src/matcher.ts","../src/logger.ts","../src/interceptor-http.ts","../src/interceptor-fetch.ts","../src/middleware.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 = process.env.REPLAY_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 = process.env.REPLAY_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 SDK — Express/Connect middleware\r\n *\r\n * Captures incoming HTTP requests and responses,\r\n * then sends them asynchronously to the ReplayAPI backend.\r\n *\r\n * @example\r\n * ```ts\r\n * import express from 'express';\r\n * import { replayApi } from '@thaparoyal/replayapi';\r\n *\r\n * const app = express();\r\n * replayApi.init('rp_your_api_key');\r\n *\r\n * // Add middleware BEFORE your routes\r\n * app.use(replayApi.middleware());\r\n * ```\r\n */\r\n\r\nimport type { IncomingMessage, ServerResponse } from \"http\";\r\nimport * as log from \"./logger.js\";\r\n\r\nconst API_URL = process.env.REPLAY_API_URL || \"http://localhost:8000\";\r\n\r\nlet apiKey: string | null = null;\r\nlet environment = \"development\";\r\nlet captureCount = 0;\r\nlet errorCount = 0;\r\n\r\n/**\r\n * Configure the middleware (called from init)\r\n */\r\nexport function configureMiddleware(key: string, env: string): void {\r\n apiKey = key;\r\n environment = env;\r\n}\r\n\r\n/**\r\n * Map environment string to valid enum value\r\n */\r\nfunction mapEnvironment(env: string): string {\r\n const map: Record<string, string> = {\r\n development: \"dev\",\r\n dev: \"dev\",\r\n local: \"local\",\r\n staging: \"staging\",\r\n production: \"production\",\r\n prod: \"production\",\r\n test: \"test\",\r\n testing: \"test\",\r\n };\r\n return map[env.toLowerCase()] || \"dev\";\r\n}\r\n\r\n/**\r\n * Send captured traffic event to the ReplayAPI backend (fire-and-forget)\r\n */\r\nasync function sendToBackend(event: Record<string, unknown>): Promise<void> {\r\n try {\r\n const url = `${API_URL}/api/ingest`;\r\n // Use native http to avoid intercepting our own calls\r\n const { default: http } = await import(\"http\");\r\n const { default: https } = await import(\"https\");\r\n\r\n const parsedUrl = new URL(url);\r\n const transport = parsedUrl.protocol === \"https:\" ? https : http;\r\n const body = JSON.stringify(event);\r\n\r\n const req = transport.request(\r\n {\r\n hostname: parsedUrl.hostname,\r\n port: parsedUrl.port,\r\n path: parsedUrl.pathname,\r\n method: \"POST\",\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n \"X-API-Key\": apiKey || \"\",\r\n \"Content-Length\": Buffer.byteLength(body),\r\n },\r\n },\r\n (res) => {\r\n // Drain the response\r\n res.resume();\r\n if (res.statusCode && res.statusCode >= 400) {\r\n log.debug(`ingest response: ${res.statusCode}`);\r\n }\r\n }\r\n );\r\n\r\n req.on(\"error\", (err) => {\r\n errorCount++;\r\n log.debug(`ingest error: ${err.message}`);\r\n });\r\n\r\n req.write(body);\r\n req.end();\r\n\r\n captureCount++;\r\n } catch (err) {\r\n errorCount++;\r\n log.debug(`ingest send error: ${(err as Error).message}`);\r\n }\r\n}\r\n\r\n/**\r\n * Collect the response body by intercepting write/end\r\n */\r\nfunction collectResponseBody(\r\n res: ServerResponse,\r\n callback: (body: string) => void\r\n): void {\r\n const chunks: Buffer[] = [];\r\n const originalWrite = res.write.bind(res);\r\n const originalEnd = res.end.bind(res);\r\n\r\n res.write = function (\r\n chunk: unknown,\r\n ...args: unknown[]\r\n ): boolean {\r\n if (chunk) {\r\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk as string));\r\n }\r\n return (originalWrite as Function)(chunk, ...args);\r\n };\r\n\r\n res.end = function (\r\n chunk?: unknown,\r\n ...args: unknown[]\r\n ): ServerResponse {\r\n if (chunk && typeof chunk !== \"function\") {\r\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk as string));\r\n }\r\n\r\n const body = Buffer.concat(chunks).toString(\"utf-8\");\r\n callback(body);\r\n\r\n return (originalEnd as Function)(chunk, ...args);\r\n };\r\n}\r\n\r\n/**\r\n * Parse JSON body safely\r\n */\r\nfunction tryParseJson(str: string): unknown {\r\n try {\r\n return JSON.parse(str);\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n\r\n/**\r\n * Create the Express/Connect middleware function.\r\n *\r\n * Options:\r\n * - `exclude` — URL patterns to skip (e.g. health checks)\r\n * - `maxBodySize` — max body size to capture in bytes (default: 1MB)\r\n */\r\nexport interface MiddlewareOptions {\r\n /** URL patterns to exclude from capture */\r\n exclude?: Array<string | RegExp>;\r\n /** Max body size to capture in bytes (default: 1MB) */\r\n maxBodySize?: number;\r\n}\r\n\r\nexport function createMiddleware(options: MiddlewareOptions = {}) {\r\n const { exclude = [], maxBodySize = 1024 * 1024 } = options;\r\n\r\n return function replayMiddleware(\r\n req: IncomingMessage & { body?: unknown },\r\n res: ServerResponse,\r\n next: (err?: unknown) => void\r\n ): void {\r\n if (!apiKey) {\r\n return next();\r\n }\r\n\r\n const url = req.url || \"/\";\r\n\r\n // Check exclude patterns\r\n for (const pattern of exclude) {\r\n if (typeof pattern === \"string\" && url.includes(pattern)) return next();\r\n if (pattern instanceof RegExp && pattern.test(url)) return next();\r\n }\r\n\r\n const startTime = Date.now();\r\n const startHrTime = process.hrtime.bigint();\r\n\r\n // Collect request info\r\n const method = (req.method || \"GET\").toUpperCase();\r\n const host = (req.headers.host || \"unknown\").split(\":\")[0];\r\n const protocol = (req as unknown as { protocol?: string }).protocol === \"https\" ? \"http\" : \"http\";\r\n\r\n // Parse path and query\r\n let path = url;\r\n let queryParams = {};\r\n const qIndex = url.indexOf(\"?\");\r\n if (qIndex !== -1) {\r\n path = url.substring(0, qIndex);\r\n try {\r\n const searchParams = new URLSearchParams(url.substring(qIndex + 1));\r\n queryParams = Object.fromEntries(searchParams.entries());\r\n } catch {\r\n // ignore\r\n }\r\n }\r\n\r\n // Get request headers (sanitize sensitive ones)\r\n const requestHeaders: Record<string, string> = {};\r\n for (const [key, val] of Object.entries(req.headers)) {\r\n if (val) {\r\n requestHeaders[key] = Array.isArray(val) ? val.join(\", \") : val;\r\n }\r\n }\r\n\r\n // Request body (Express populates req.body after body parsers)\r\n let requestBody: unknown = undefined;\r\n if (req.body !== undefined) {\r\n const bodyStr = typeof req.body === \"string\" ? req.body : JSON.stringify(req.body);\r\n if (bodyStr.length <= maxBodySize) {\r\n requestBody = typeof req.body === \"string\" ? tryParseJson(req.body) || req.body : req.body;\r\n }\r\n }\r\n\r\n // Intercept response body\r\n collectResponseBody(res, (responseBodyStr) => {\r\n const durationMs = Number(process.hrtime.bigint() - startHrTime) / 1e6;\r\n const statusCode = res.statusCode;\r\n\r\n // Get response headers\r\n const responseHeaders: Record<string, string> = {};\r\n const rawHeaders = res.getHeaders();\r\n for (const [key, val] of Object.entries(rawHeaders)) {\r\n if (val) {\r\n responseHeaders[key] = Array.isArray(val) ? val.join(\", \") : String(val);\r\n }\r\n }\r\n\r\n // Parse response body\r\n let responseBody: unknown = undefined;\r\n if (responseBodyStr.length <= maxBodySize) {\r\n responseBody = tryParseJson(responseBodyStr) || responseBodyStr;\r\n }\r\n\r\n // Build traffic event\r\n const event = {\r\n method,\r\n protocol,\r\n host,\r\n path,\r\n queryParams,\r\n requestHeaders,\r\n requestBodyInline: requestBody,\r\n requestSizeBytes: requestBody ? Buffer.byteLength(JSON.stringify(requestBody)) : 0,\r\n statusCode,\r\n responseHeaders,\r\n responseBodyInline: responseBody,\r\n responseSizeBytes: responseBodyStr.length,\r\n startedAt: new Date(startTime).toISOString(),\r\n durationMs: Math.round(durationMs * 100) / 100,\r\n source: \"sdk\",\r\n environment: mapEnvironment(environment),\r\n clientIp: req.headers[\"x-forwarded-for\"]\r\n ? String(req.headers[\"x-forwarded-for\"]).split(\",\")[0].trim()\r\n : req.socket?.remoteAddress || null,\r\n tags: [],\r\n metadata: {},\r\n };\r\n\r\n log.debug(`captured: ${method} ${path} ${statusCode} (${event.durationMs}ms)`);\r\n\r\n // Send asynchronously — don't block the response\r\n sendToBackend(event);\r\n });\r\n\r\n next();\r\n };\r\n}\r\n\r\nexport function getMiddlewareStats() {\r\n return { captured: captureCount, errors: errorCount };\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 { configureMiddleware, createMiddleware, getMiddlewareStats } from \"./middleware.js\";\r\nimport type { MiddlewareOptions } from \"./middleware.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 // Configure middleware with credentials\r\n configureMiddleware(config.apiKey, config.environment);\r\n\r\n // Install interceptors for outgoing traffic\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 middleware: createMiddleware,\r\n};\r\n\r\n// Named exports for flexibility\r\nexport { init, stop, isActive, getStats, createMiddleware };\r\n\r\n// Re-export types\r\nexport type { ReplayApiConfig, ReplayApiInstance, CaptureStats } from \"./types.js\";\r\nexport type { MiddlewareOptions } from \"./middleware.js\";\r\n"]}
package/dist/index.mjs CHANGED
@@ -356,6 +356,176 @@ function getFetchStats() {
356
356
  return { ...stats2 };
357
357
  }
358
358
 
359
+ // src/middleware.ts
360
+ var API_URL = process.env.REPLAY_API_URL || "http://localhost:8000";
361
+ var apiKey = null;
362
+ var environment = "development";
363
+ var captureCount = 0;
364
+ var errorCount = 0;
365
+ function configureMiddleware(key, env) {
366
+ apiKey = key;
367
+ environment = env;
368
+ }
369
+ function mapEnvironment(env) {
370
+ const map = {
371
+ development: "dev",
372
+ dev: "dev",
373
+ local: "local",
374
+ staging: "staging",
375
+ production: "production",
376
+ prod: "production",
377
+ test: "test",
378
+ testing: "test"
379
+ };
380
+ return map[env.toLowerCase()] || "dev";
381
+ }
382
+ async function sendToBackend(event) {
383
+ try {
384
+ const url = `${API_URL}/api/ingest`;
385
+ const { default: http2 } = await import('http');
386
+ const { default: https2 } = await import('https');
387
+ const parsedUrl = new URL(url);
388
+ const transport = parsedUrl.protocol === "https:" ? https2 : http2;
389
+ const body = JSON.stringify(event);
390
+ const req = transport.request(
391
+ {
392
+ hostname: parsedUrl.hostname,
393
+ port: parsedUrl.port,
394
+ path: parsedUrl.pathname,
395
+ method: "POST",
396
+ headers: {
397
+ "Content-Type": "application/json",
398
+ "X-API-Key": apiKey || "",
399
+ "Content-Length": Buffer.byteLength(body)
400
+ }
401
+ },
402
+ (res) => {
403
+ res.resume();
404
+ if (res.statusCode && res.statusCode >= 400) {
405
+ debug(`ingest response: ${res.statusCode}`);
406
+ }
407
+ }
408
+ );
409
+ req.on("error", (err) => {
410
+ errorCount++;
411
+ debug(`ingest error: ${err.message}`);
412
+ });
413
+ req.write(body);
414
+ req.end();
415
+ captureCount++;
416
+ } catch (err) {
417
+ errorCount++;
418
+ debug(`ingest send error: ${err.message}`);
419
+ }
420
+ }
421
+ function collectResponseBody(res, callback) {
422
+ const chunks = [];
423
+ const originalWrite = res.write.bind(res);
424
+ const originalEnd = res.end.bind(res);
425
+ res.write = function(chunk, ...args) {
426
+ if (chunk) {
427
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
428
+ }
429
+ return originalWrite(chunk, ...args);
430
+ };
431
+ res.end = function(chunk, ...args) {
432
+ if (chunk && typeof chunk !== "function") {
433
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
434
+ }
435
+ const body = Buffer.concat(chunks).toString("utf-8");
436
+ callback(body);
437
+ return originalEnd(chunk, ...args);
438
+ };
439
+ }
440
+ function tryParseJson(str) {
441
+ try {
442
+ return JSON.parse(str);
443
+ } catch {
444
+ return void 0;
445
+ }
446
+ }
447
+ function createMiddleware(options = {}) {
448
+ const { exclude = [], maxBodySize = 1024 * 1024 } = options;
449
+ return function replayMiddleware(req, res, next) {
450
+ if (!apiKey) {
451
+ return next();
452
+ }
453
+ const url = req.url || "/";
454
+ for (const pattern of exclude) {
455
+ if (typeof pattern === "string" && url.includes(pattern)) return next();
456
+ if (pattern instanceof RegExp && pattern.test(url)) return next();
457
+ }
458
+ const startTime = Date.now();
459
+ const startHrTime = process.hrtime.bigint();
460
+ const method = (req.method || "GET").toUpperCase();
461
+ const host = (req.headers.host || "unknown").split(":")[0];
462
+ const protocol = req.protocol === "https" ? "http" : "http";
463
+ let path = url;
464
+ let queryParams = {};
465
+ const qIndex = url.indexOf("?");
466
+ if (qIndex !== -1) {
467
+ path = url.substring(0, qIndex);
468
+ try {
469
+ const searchParams = new URLSearchParams(url.substring(qIndex + 1));
470
+ queryParams = Object.fromEntries(searchParams.entries());
471
+ } catch {
472
+ }
473
+ }
474
+ const requestHeaders = {};
475
+ for (const [key, val] of Object.entries(req.headers)) {
476
+ if (val) {
477
+ requestHeaders[key] = Array.isArray(val) ? val.join(", ") : val;
478
+ }
479
+ }
480
+ let requestBody = void 0;
481
+ if (req.body !== void 0) {
482
+ const bodyStr = typeof req.body === "string" ? req.body : JSON.stringify(req.body);
483
+ if (bodyStr.length <= maxBodySize) {
484
+ requestBody = typeof req.body === "string" ? tryParseJson(req.body) || req.body : req.body;
485
+ }
486
+ }
487
+ collectResponseBody(res, (responseBodyStr) => {
488
+ const durationMs = Number(process.hrtime.bigint() - startHrTime) / 1e6;
489
+ const statusCode = res.statusCode;
490
+ const responseHeaders = {};
491
+ const rawHeaders = res.getHeaders();
492
+ for (const [key, val] of Object.entries(rawHeaders)) {
493
+ if (val) {
494
+ responseHeaders[key] = Array.isArray(val) ? val.join(", ") : String(val);
495
+ }
496
+ }
497
+ let responseBody = void 0;
498
+ if (responseBodyStr.length <= maxBodySize) {
499
+ responseBody = tryParseJson(responseBodyStr) || responseBodyStr;
500
+ }
501
+ const event = {
502
+ method,
503
+ protocol,
504
+ host,
505
+ path,
506
+ queryParams,
507
+ requestHeaders,
508
+ requestBodyInline: requestBody,
509
+ requestSizeBytes: requestBody ? Buffer.byteLength(JSON.stringify(requestBody)) : 0,
510
+ statusCode,
511
+ responseHeaders,
512
+ responseBodyInline: responseBody,
513
+ responseSizeBytes: responseBodyStr.length,
514
+ startedAt: new Date(startTime).toISOString(),
515
+ durationMs: Math.round(durationMs * 100) / 100,
516
+ source: "sdk",
517
+ environment: mapEnvironment(environment),
518
+ clientIp: req.headers["x-forwarded-for"] ? String(req.headers["x-forwarded-for"]).split(",")[0].trim() : req.socket?.remoteAddress || null,
519
+ tags: [],
520
+ metadata: {}
521
+ };
522
+ debug(`captured: ${method} ${path} ${statusCode} (${event.durationMs}ms)`);
523
+ sendToBackend(event);
524
+ });
525
+ next();
526
+ };
527
+ }
528
+
359
529
  // src/index.ts
360
530
  var initialized = false;
361
531
  function init(configOrApiKey) {
@@ -378,6 +548,7 @@ function init(configOrApiKey) {
378
548
  }
379
549
  config3.environment = config3.environment || process.env.REPLAY_ENVIRONMENT || "development";
380
550
  info(`initialized \u2014 env: ${config3.environment}`);
551
+ configureMiddleware(config3.apiKey, config3.environment);
381
552
  installHttpInterceptor(config3);
382
553
  installFetchInterceptor(config3);
383
554
  initialized = true;
@@ -407,9 +578,10 @@ var replayApi = {
407
578
  init,
408
579
  stop,
409
580
  isActive,
410
- getStats
581
+ getStats,
582
+ middleware: createMiddleware
411
583
  };
412
584
 
413
- export { getStats, init, isActive, replayApi, stop };
585
+ export { createMiddleware, getStats, init, isActive, replayApi, stop };
414
586
  //# sourceMappingURL=index.mjs.map
415
587
  //# sourceMappingURL=index.mjs.map
@@ -1 +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 = process.env.REPLAY_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 = process.env.REPLAY_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"]}
1
+ {"version":3,"sources":["../src/matcher.ts","../src/logger.ts","../src/interceptor-http.ts","../src/interceptor-fetch.ts","../src/middleware.ts","../src/index.ts"],"names":["URL","PROXY_URL","active","config","stats","init","http","https"],"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 = process.env.REPLAY_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 = process.env.REPLAY_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 SDK — Express/Connect middleware\r\n *\r\n * Captures incoming HTTP requests and responses,\r\n * then sends them asynchronously to the ReplayAPI backend.\r\n *\r\n * @example\r\n * ```ts\r\n * import express from 'express';\r\n * import { replayApi } from '@thaparoyal/replayapi';\r\n *\r\n * const app = express();\r\n * replayApi.init('rp_your_api_key');\r\n *\r\n * // Add middleware BEFORE your routes\r\n * app.use(replayApi.middleware());\r\n * ```\r\n */\r\n\r\nimport type { IncomingMessage, ServerResponse } from \"http\";\r\nimport * as log from \"./logger.js\";\r\n\r\nconst API_URL = process.env.REPLAY_API_URL || \"http://localhost:8000\";\r\n\r\nlet apiKey: string | null = null;\r\nlet environment = \"development\";\r\nlet captureCount = 0;\r\nlet errorCount = 0;\r\n\r\n/**\r\n * Configure the middleware (called from init)\r\n */\r\nexport function configureMiddleware(key: string, env: string): void {\r\n apiKey = key;\r\n environment = env;\r\n}\r\n\r\n/**\r\n * Map environment string to valid enum value\r\n */\r\nfunction mapEnvironment(env: string): string {\r\n const map: Record<string, string> = {\r\n development: \"dev\",\r\n dev: \"dev\",\r\n local: \"local\",\r\n staging: \"staging\",\r\n production: \"production\",\r\n prod: \"production\",\r\n test: \"test\",\r\n testing: \"test\",\r\n };\r\n return map[env.toLowerCase()] || \"dev\";\r\n}\r\n\r\n/**\r\n * Send captured traffic event to the ReplayAPI backend (fire-and-forget)\r\n */\r\nasync function sendToBackend(event: Record<string, unknown>): Promise<void> {\r\n try {\r\n const url = `${API_URL}/api/ingest`;\r\n // Use native http to avoid intercepting our own calls\r\n const { default: http } = await import(\"http\");\r\n const { default: https } = await import(\"https\");\r\n\r\n const parsedUrl = new URL(url);\r\n const transport = parsedUrl.protocol === \"https:\" ? https : http;\r\n const body = JSON.stringify(event);\r\n\r\n const req = transport.request(\r\n {\r\n hostname: parsedUrl.hostname,\r\n port: parsedUrl.port,\r\n path: parsedUrl.pathname,\r\n method: \"POST\",\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n \"X-API-Key\": apiKey || \"\",\r\n \"Content-Length\": Buffer.byteLength(body),\r\n },\r\n },\r\n (res) => {\r\n // Drain the response\r\n res.resume();\r\n if (res.statusCode && res.statusCode >= 400) {\r\n log.debug(`ingest response: ${res.statusCode}`);\r\n }\r\n }\r\n );\r\n\r\n req.on(\"error\", (err) => {\r\n errorCount++;\r\n log.debug(`ingest error: ${err.message}`);\r\n });\r\n\r\n req.write(body);\r\n req.end();\r\n\r\n captureCount++;\r\n } catch (err) {\r\n errorCount++;\r\n log.debug(`ingest send error: ${(err as Error).message}`);\r\n }\r\n}\r\n\r\n/**\r\n * Collect the response body by intercepting write/end\r\n */\r\nfunction collectResponseBody(\r\n res: ServerResponse,\r\n callback: (body: string) => void\r\n): void {\r\n const chunks: Buffer[] = [];\r\n const originalWrite = res.write.bind(res);\r\n const originalEnd = res.end.bind(res);\r\n\r\n res.write = function (\r\n chunk: unknown,\r\n ...args: unknown[]\r\n ): boolean {\r\n if (chunk) {\r\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk as string));\r\n }\r\n return (originalWrite as Function)(chunk, ...args);\r\n };\r\n\r\n res.end = function (\r\n chunk?: unknown,\r\n ...args: unknown[]\r\n ): ServerResponse {\r\n if (chunk && typeof chunk !== \"function\") {\r\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk as string));\r\n }\r\n\r\n const body = Buffer.concat(chunks).toString(\"utf-8\");\r\n callback(body);\r\n\r\n return (originalEnd as Function)(chunk, ...args);\r\n };\r\n}\r\n\r\n/**\r\n * Parse JSON body safely\r\n */\r\nfunction tryParseJson(str: string): unknown {\r\n try {\r\n return JSON.parse(str);\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n\r\n/**\r\n * Create the Express/Connect middleware function.\r\n *\r\n * Options:\r\n * - `exclude` — URL patterns to skip (e.g. health checks)\r\n * - `maxBodySize` — max body size to capture in bytes (default: 1MB)\r\n */\r\nexport interface MiddlewareOptions {\r\n /** URL patterns to exclude from capture */\r\n exclude?: Array<string | RegExp>;\r\n /** Max body size to capture in bytes (default: 1MB) */\r\n maxBodySize?: number;\r\n}\r\n\r\nexport function createMiddleware(options: MiddlewareOptions = {}) {\r\n const { exclude = [], maxBodySize = 1024 * 1024 } = options;\r\n\r\n return function replayMiddleware(\r\n req: IncomingMessage & { body?: unknown },\r\n res: ServerResponse,\r\n next: (err?: unknown) => void\r\n ): void {\r\n if (!apiKey) {\r\n return next();\r\n }\r\n\r\n const url = req.url || \"/\";\r\n\r\n // Check exclude patterns\r\n for (const pattern of exclude) {\r\n if (typeof pattern === \"string\" && url.includes(pattern)) return next();\r\n if (pattern instanceof RegExp && pattern.test(url)) return next();\r\n }\r\n\r\n const startTime = Date.now();\r\n const startHrTime = process.hrtime.bigint();\r\n\r\n // Collect request info\r\n const method = (req.method || \"GET\").toUpperCase();\r\n const host = (req.headers.host || \"unknown\").split(\":\")[0];\r\n const protocol = (req as unknown as { protocol?: string }).protocol === \"https\" ? \"http\" : \"http\";\r\n\r\n // Parse path and query\r\n let path = url;\r\n let queryParams = {};\r\n const qIndex = url.indexOf(\"?\");\r\n if (qIndex !== -1) {\r\n path = url.substring(0, qIndex);\r\n try {\r\n const searchParams = new URLSearchParams(url.substring(qIndex + 1));\r\n queryParams = Object.fromEntries(searchParams.entries());\r\n } catch {\r\n // ignore\r\n }\r\n }\r\n\r\n // Get request headers (sanitize sensitive ones)\r\n const requestHeaders: Record<string, string> = {};\r\n for (const [key, val] of Object.entries(req.headers)) {\r\n if (val) {\r\n requestHeaders[key] = Array.isArray(val) ? val.join(\", \") : val;\r\n }\r\n }\r\n\r\n // Request body (Express populates req.body after body parsers)\r\n let requestBody: unknown = undefined;\r\n if (req.body !== undefined) {\r\n const bodyStr = typeof req.body === \"string\" ? req.body : JSON.stringify(req.body);\r\n if (bodyStr.length <= maxBodySize) {\r\n requestBody = typeof req.body === \"string\" ? tryParseJson(req.body) || req.body : req.body;\r\n }\r\n }\r\n\r\n // Intercept response body\r\n collectResponseBody(res, (responseBodyStr) => {\r\n const durationMs = Number(process.hrtime.bigint() - startHrTime) / 1e6;\r\n const statusCode = res.statusCode;\r\n\r\n // Get response headers\r\n const responseHeaders: Record<string, string> = {};\r\n const rawHeaders = res.getHeaders();\r\n for (const [key, val] of Object.entries(rawHeaders)) {\r\n if (val) {\r\n responseHeaders[key] = Array.isArray(val) ? val.join(\", \") : String(val);\r\n }\r\n }\r\n\r\n // Parse response body\r\n let responseBody: unknown = undefined;\r\n if (responseBodyStr.length <= maxBodySize) {\r\n responseBody = tryParseJson(responseBodyStr) || responseBodyStr;\r\n }\r\n\r\n // Build traffic event\r\n const event = {\r\n method,\r\n protocol,\r\n host,\r\n path,\r\n queryParams,\r\n requestHeaders,\r\n requestBodyInline: requestBody,\r\n requestSizeBytes: requestBody ? Buffer.byteLength(JSON.stringify(requestBody)) : 0,\r\n statusCode,\r\n responseHeaders,\r\n responseBodyInline: responseBody,\r\n responseSizeBytes: responseBodyStr.length,\r\n startedAt: new Date(startTime).toISOString(),\r\n durationMs: Math.round(durationMs * 100) / 100,\r\n source: \"sdk\",\r\n environment: mapEnvironment(environment),\r\n clientIp: req.headers[\"x-forwarded-for\"]\r\n ? String(req.headers[\"x-forwarded-for\"]).split(\",\")[0].trim()\r\n : req.socket?.remoteAddress || null,\r\n tags: [],\r\n metadata: {},\r\n };\r\n\r\n log.debug(`captured: ${method} ${path} ${statusCode} (${event.durationMs}ms)`);\r\n\r\n // Send asynchronously — don't block the response\r\n sendToBackend(event);\r\n });\r\n\r\n next();\r\n };\r\n}\r\n\r\nexport function getMiddlewareStats() {\r\n return { captured: captureCount, errors: errorCount };\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 { configureMiddleware, createMiddleware, getMiddlewareStats } from \"./middleware.js\";\r\nimport type { MiddlewareOptions } from \"./middleware.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 // Configure middleware with credentials\r\n configureMiddleware(config.apiKey, config.environment);\r\n\r\n // Install interceptors for outgoing traffic\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 middleware: createMiddleware,\r\n};\r\n\r\n// Named exports for flexibility\r\nexport { init, stop, isActive, getStats, createMiddleware };\r\n\r\n// Re-export types\r\nexport type { ReplayApiConfig, ReplayApiInstance, CaptureStats } from \"./types.js\";\r\nexport type { MiddlewareOptions } from \"./middleware.js\";\r\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thaparoyal/replayapi",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "ReplayAPI SDK for Node.js — Capture HTTP traffic automatically",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",