@getlimelight/sdk 0.5.5 → 0.7.4

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.mjs CHANGED
@@ -29,6 +29,7 @@ var NetworkType = /* @__PURE__ */ ((NetworkType2) => {
29
29
  NetworkType2["FETCH"] = "fetch";
30
30
  NetworkType2["XHR"] = "xhr";
31
31
  NetworkType2["GRAPHQL"] = "graphql";
32
+ NetworkType2["INCOMING"] = "incoming";
32
33
  return NetworkType2;
33
34
  })(NetworkType || {});
34
35
  var NetworkPhase = /* @__PURE__ */ ((NetworkPhase2) => {
@@ -49,17 +50,17 @@ var BodyFormat = /* @__PURE__ */ ((BodyFormat2) => {
49
50
  BodyFormat2["UNSERIALIZABLE"] = "UNSERIALIZABLE";
50
51
  return BodyFormat2;
51
52
  })(BodyFormat || {});
52
- var HttpMethod = /* @__PURE__ */ ((HttpMethod5) => {
53
- HttpMethod5["GET"] = "GET";
54
- HttpMethod5["POST"] = "POST";
55
- HttpMethod5["PUT"] = "PUT";
56
- HttpMethod5["PATCH"] = "PATCH";
57
- HttpMethod5["DELETE"] = "DELETE";
58
- HttpMethod5["HEAD"] = "HEAD";
59
- HttpMethod5["OPTIONS"] = "OPTIONS";
60
- HttpMethod5["TRACE"] = "TRACE";
61
- HttpMethod5["CONNECT"] = "CONNECT";
62
- return HttpMethod5;
53
+ var HttpMethod = /* @__PURE__ */ ((HttpMethod6) => {
54
+ HttpMethod6["GET"] = "GET";
55
+ HttpMethod6["POST"] = "POST";
56
+ HttpMethod6["PUT"] = "PUT";
57
+ HttpMethod6["PATCH"] = "PATCH";
58
+ HttpMethod6["DELETE"] = "DELETE";
59
+ HttpMethod6["HEAD"] = "HEAD";
60
+ HttpMethod6["OPTIONS"] = "OPTIONS";
61
+ HttpMethod6["TRACE"] = "TRACE";
62
+ HttpMethod6["CONNECT"] = "CONNECT";
63
+ return HttpMethod6;
63
64
  })(HttpMethod || {});
64
65
  var HttpStatusClass = /* @__PURE__ */ ((HttpStatusClass2) => {
65
66
  HttpStatusClass2[HttpStatusClass2["INFORMATIONAL"] = 100] = "INFORMATIONAL";
@@ -247,8 +248,9 @@ var SENSITIVE_HEADERS = [
247
248
  ];
248
249
  var LIMELIGHT_WEB_WSS_URL = "wss://api.getlimelight.io";
249
250
  var LIMELIGHT_DESKTOP_WSS_URL = "ws://localhost:8484";
251
+ var LIMELIGHT_MCP_WS_URL = "ws://localhost:9229";
250
252
  var WS_PATH = "/limelight";
251
- var SDK_VERSION = true ? "0.5.5" : "test-version";
253
+ var SDK_VERSION = true ? "0.7.4" : "test-version";
252
254
  var RENDER_THRESHOLDS = {
253
255
  HOT_VELOCITY: 5,
254
256
  HIGH_RENDER_COUNT: 50,
@@ -502,6 +504,9 @@ var formatRequestName = (url) => {
502
504
  }
503
505
  };
504
506
 
507
+ // src/helpers/utils/environment.ts
508
+ var hasDOM = () => typeof window !== "undefined" && typeof document !== "undefined";
509
+
505
510
  // src/helpers/render/generateRenderId.ts
506
511
  var counter = 0;
507
512
  var generateRenderId = () => {
@@ -667,13 +672,31 @@ var generateRequestId = () => {
667
672
  return `req-${Date.now()}-${Math.random().toString(36).substring(7)}`;
668
673
  };
669
674
 
675
+ // src/limelight/context/traceContext.ts
676
+ var _resolved = false;
677
+ var _traceContext;
678
+ var getTraceContext = () => {
679
+ if (!_resolved) {
680
+ _resolved = true;
681
+ try {
682
+ const _require = globalThis["require"];
683
+ const { AsyncLocalStorage } = _require("node:async_hooks");
684
+ _traceContext = new AsyncLocalStorage();
685
+ } catch {
686
+ }
687
+ }
688
+ return _traceContext;
689
+ };
690
+
670
691
  // src/limelight/interceptors/NetworkInterceptor.ts
671
692
  var NetworkInterceptor = class {
672
693
  constructor(sendMessage, getSessionId) {
673
694
  this.sendMessage = sendMessage;
674
695
  this.getSessionId = getSessionId;
675
696
  this.globalObject = detectGlobalObject();
676
- this.originalFetch = this.globalObject.fetch.bind(this.globalObject);
697
+ if (typeof this.globalObject.fetch === "function") {
698
+ this.originalFetch = this.globalObject.fetch.bind(this.globalObject);
699
+ }
677
700
  }
678
701
  originalFetch;
679
702
  config = null;
@@ -693,9 +716,18 @@ var NetworkInterceptor = class {
693
716
  }
694
717
  return;
695
718
  }
719
+ if (!this.originalFetch) {
720
+ if (config?.enableInternalLogging) {
721
+ console.warn(
722
+ "[Limelight] fetch is not available in this environment, skipping network interception"
723
+ );
724
+ }
725
+ return;
726
+ }
696
727
  this.isSetup = true;
697
728
  this.config = config;
698
729
  const self2 = this;
730
+ const originalFetch = this.originalFetch;
699
731
  this.globalObject.fetch = async function(input, init = {}) {
700
732
  const requestId = generateRequestId();
701
733
  const startTime = Date.now();
@@ -713,6 +745,12 @@ var NetworkInterceptor = class {
713
745
  });
714
746
  }
715
747
  headers["x-limelight-intercepted"] = "fetch";
748
+ const traceHeaderName = self2.config?.traceHeaderName ?? "x-limelight-trace-id";
749
+ if (!headers[traceHeaderName]) {
750
+ const existingTraceId = getTraceContext()?.getStore()?.traceId;
751
+ headers[traceHeaderName] = existingTraceId || generateRequestId();
752
+ }
753
+ const traceId = headers[traceHeaderName];
716
754
  modifiedInit.headers = new Headers(headers);
717
755
  let requestBodyToSerialize = init.body;
718
756
  if (input instanceof Request && !requestBodyToSerialize) {
@@ -746,6 +784,7 @@ var NetworkInterceptor = class {
746
784
  }
747
785
  let requestEvent = {
748
786
  id: requestId,
787
+ traceId,
749
788
  sessionId: self2.getSessionId(),
750
789
  timestamp: startTime,
751
790
  phase: "REQUEST" /* REQUEST */,
@@ -762,17 +801,17 @@ var NetworkInterceptor = class {
762
801
  if (self2.config?.beforeSend) {
763
802
  const modifiedEvent = self2.config.beforeSend(requestEvent);
764
803
  if (!modifiedEvent) {
765
- return self2.originalFetch(input, modifiedInit);
804
+ return originalFetch(input, modifiedInit);
766
805
  }
767
806
  if (modifiedEvent.phase !== "REQUEST" /* REQUEST */) {
768
807
  console.error("[Limelight] beforeSend must return same event type");
769
- return self2.originalFetch(input, modifiedInit);
808
+ return originalFetch(input, modifiedInit);
770
809
  }
771
810
  requestEvent = modifiedEvent;
772
811
  }
773
812
  self2.sendMessage(requestEvent);
774
813
  try {
775
- const response = await self2.originalFetch(input, modifiedInit);
814
+ const response = await originalFetch(input, modifiedInit);
776
815
  const clone = response.clone();
777
816
  const endTime = Date.now();
778
817
  const duration = endTime - startTime;
@@ -792,6 +831,7 @@ var NetworkInterceptor = class {
792
831
  );
793
832
  let responseEvent = {
794
833
  id: requestId,
834
+ traceId,
795
835
  sessionId: self2.getSessionId(),
796
836
  timestamp: endTime,
797
837
  phase: "RESPONSE" /* RESPONSE */,
@@ -824,6 +864,7 @@ var NetworkInterceptor = class {
824
864
  const errorStack = err instanceof Error ? err.stack : void 0;
825
865
  let errorEvent = {
826
866
  id: requestId,
867
+ traceId,
827
868
  sessionId: self2.getSessionId(),
828
869
  timestamp: Date.now(),
829
870
  phase: isAbort ? "ABORT" /* ABORT */ : "ERROR" /* ERROR */,
@@ -854,7 +895,9 @@ var NetworkInterceptor = class {
854
895
  return;
855
896
  }
856
897
  this.isSetup = false;
857
- this.globalObject.fetch = this.originalFetch;
898
+ if (this.originalFetch) {
899
+ this.globalObject.fetch = this.originalFetch;
900
+ }
858
901
  }
859
902
  };
860
903
 
@@ -863,9 +906,11 @@ var XHRInterceptor = class {
863
906
  constructor(sendMessage, getSessionId) {
864
907
  this.sendMessage = sendMessage;
865
908
  this.getSessionId = getSessionId;
866
- this.originalXHROpen = XMLHttpRequest.prototype.open;
867
- this.originalXHRSend = XMLHttpRequest.prototype.send;
868
- this.originalXHRSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
909
+ if (typeof XMLHttpRequest !== "undefined") {
910
+ this.originalXHROpen = XMLHttpRequest.prototype.open;
911
+ this.originalXHRSend = XMLHttpRequest.prototype.send;
912
+ this.originalXHRSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
913
+ }
869
914
  }
870
915
  originalXHROpen;
871
916
  originalXHRSend;
@@ -880,6 +925,9 @@ var XHRInterceptor = class {
880
925
  * @returns {void}
881
926
  */
882
927
  setup(config) {
928
+ if (typeof XMLHttpRequest === "undefined") {
929
+ return;
930
+ }
883
931
  if (this.isSetup) {
884
932
  if (this.config?.enableInternalLogging) {
885
933
  console.warn("[Limelight] XHR interceptor already set up");
@@ -918,12 +966,24 @@ var XHRInterceptor = class {
918
966
  return self2.originalXHRSend.apply(this, arguments);
919
967
  }
920
968
  if (data) {
969
+ const traceHeaderName = self2.config?.traceHeaderName ?? "x-limelight-trace-id";
970
+ if (!data.headers[traceHeaderName]) {
971
+ data.traceId = generateRequestId();
972
+ self2.originalXHRSetRequestHeader.call(
973
+ this,
974
+ traceHeaderName,
975
+ data.traceId
976
+ );
977
+ } else {
978
+ data.traceId = data.headers[traceHeaderName];
979
+ }
921
980
  const requestBody = serializeBody(
922
981
  body,
923
982
  self2.config?.disableBodyCapture
924
983
  );
925
984
  let requestEvent = {
926
985
  id: data.id,
986
+ traceId: data.traceId,
927
987
  sessionId: self2.getSessionId(),
928
988
  timestamp: data.startTime,
929
989
  phase: "REQUEST" /* REQUEST */,
@@ -983,6 +1043,7 @@ var XHRInterceptor = class {
983
1043
  );
984
1044
  let responseEvent = {
985
1045
  id: data.id,
1046
+ traceId: data.traceId,
986
1047
  sessionId: self2.getSessionId(),
987
1048
  timestamp: endTime,
988
1049
  phase: "RESPONSE" /* RESPONSE */,
@@ -1017,6 +1078,7 @@ var XHRInterceptor = class {
1017
1078
  responseSent = true;
1018
1079
  let errorEvent = {
1019
1080
  id: data.id,
1081
+ traceId: data.traceId,
1020
1082
  sessionId: self2.getSessionId(),
1021
1083
  timestamp: Date.now(),
1022
1084
  phase,
@@ -1095,15 +1157,14 @@ var XHRInterceptor = class {
1095
1157
  */
1096
1158
  cleanup() {
1097
1159
  if (!this.isSetup) {
1098
- if (this.config?.enableInternalLogging) {
1099
- console.warn("[Limelight] XHR interceptor not set up");
1100
- }
1101
1160
  return;
1102
1161
  }
1103
1162
  this.isSetup = false;
1104
- XMLHttpRequest.prototype.open = this.originalXHROpen;
1105
- XMLHttpRequest.prototype.send = this.originalXHRSend;
1106
- XMLHttpRequest.prototype.setRequestHeader = this.originalXHRSetRequestHeader;
1163
+ if (typeof XMLHttpRequest !== "undefined") {
1164
+ XMLHttpRequest.prototype.open = this.originalXHROpen;
1165
+ XMLHttpRequest.prototype.send = this.originalXHRSend;
1166
+ XMLHttpRequest.prototype.setRequestHeader = this.originalXHRSetRequestHeader;
1167
+ }
1107
1168
  }
1108
1169
  };
1109
1170
 
@@ -1756,6 +1817,83 @@ var RenderInterceptor = class {
1756
1817
  }
1757
1818
  };
1758
1819
 
1820
+ // src/limelight/interceptors/ErrorInterceptor.ts
1821
+ var ErrorInterceptor = class {
1822
+ constructor(sendMessage, getSessionId) {
1823
+ this.sendMessage = sendMessage;
1824
+ this.getSessionId = getSessionId;
1825
+ }
1826
+ isSetup = false;
1827
+ config = null;
1828
+ counter = 0;
1829
+ uncaughtExceptionHandler = null;
1830
+ unhandledRejectionHandler = null;
1831
+ setup(config) {
1832
+ if (this.isSetup) {
1833
+ if (this.config?.enableInternalLogging) {
1834
+ console.warn("[Limelight] Error interceptor already set up");
1835
+ }
1836
+ return;
1837
+ }
1838
+ if (typeof process === "undefined" || !process.on) {
1839
+ return;
1840
+ }
1841
+ this.isSetup = true;
1842
+ this.config = config;
1843
+ this.uncaughtExceptionHandler = (error) => {
1844
+ this.sendErrorEvent(error, "uncaughtException");
1845
+ setTimeout(() => {
1846
+ process.exit(1);
1847
+ }, 200);
1848
+ };
1849
+ this.unhandledRejectionHandler = (reason) => {
1850
+ const error = reason instanceof Error ? reason : new Error(String(reason));
1851
+ this.sendErrorEvent(error, "unhandledRejection");
1852
+ };
1853
+ process.on("uncaughtException", this.uncaughtExceptionHandler);
1854
+ process.on("unhandledRejection", this.unhandledRejectionHandler);
1855
+ }
1856
+ cleanup() {
1857
+ if (!this.isSetup) return;
1858
+ this.isSetup = false;
1859
+ if (this.uncaughtExceptionHandler) {
1860
+ process.removeListener(
1861
+ "uncaughtException",
1862
+ this.uncaughtExceptionHandler
1863
+ );
1864
+ this.uncaughtExceptionHandler = null;
1865
+ }
1866
+ if (this.unhandledRejectionHandler) {
1867
+ process.removeListener(
1868
+ "unhandledRejection",
1869
+ this.unhandledRejectionHandler
1870
+ );
1871
+ this.unhandledRejectionHandler = null;
1872
+ }
1873
+ this.config = null;
1874
+ }
1875
+ sendErrorEvent(error, source) {
1876
+ const sessionId = this.getSessionId();
1877
+ const event = {
1878
+ id: `${sessionId}-${Date.now()}-${this.counter++}`,
1879
+ phase: "CONSOLE",
1880
+ type: "CONSOLE" /* CONSOLE */,
1881
+ level: "error" /* ERROR */,
1882
+ timestamp: Date.now(),
1883
+ sessionId,
1884
+ source: "app" /* APP */,
1885
+ consoleType: "exception" /* EXCEPTION */,
1886
+ args: [safeStringify(`[${source}] ${error.message}`)],
1887
+ stackTrace: error.stack
1888
+ };
1889
+ if (this.config?.beforeSend) {
1890
+ const modified = this.config.beforeSend(event);
1891
+ if (!modified) return;
1892
+ }
1893
+ this.sendMessage(event);
1894
+ }
1895
+ };
1896
+
1759
1897
  // src/limelight/interceptors/StateInterceptor.ts
1760
1898
  var StateInterceptor = class {
1761
1899
  sendMessage;
@@ -2239,6 +2377,153 @@ var CommandHandler = class {
2239
2377
  }
2240
2378
  };
2241
2379
 
2380
+ // src/limelight/middleware/httpMiddleware.ts
2381
+ var DEFAULT_MAX_BODY_SIZE = 64 * 1024;
2382
+ var captureRequest = (req, res, sendMessage, getSessionId, config, options) => {
2383
+ const requestId = generateRequestId();
2384
+ const startTime = Date.now();
2385
+ const maxBodySize = options?.maxBodySize ?? DEFAULT_MAX_BODY_SIZE;
2386
+ const traceHeaderName = config?.traceHeaderName ?? "x-limelight-trace-id";
2387
+ const incomingTraceId = req.headers[traceHeaderName];
2388
+ const traceId = incomingTraceId || generateRequestId();
2389
+ req.limelightTraceId = traceId;
2390
+ const url = req.url || "/";
2391
+ const method = (req.method || "GET").toUpperCase();
2392
+ const headers = {};
2393
+ for (const [key, value] of Object.entries(req.headers)) {
2394
+ if (value) {
2395
+ headers[key.toLowerCase()] = Array.isArray(value) ? value.join(", ") : value;
2396
+ }
2397
+ }
2398
+ let requestBody = serializeBody(req.body, config?.disableBodyCapture);
2399
+ if (requestBody?.raw && requestBody.raw.length > maxBodySize) {
2400
+ requestBody = {
2401
+ ...requestBody,
2402
+ raw: requestBody.raw.slice(0, maxBodySize) + "...[truncated]",
2403
+ size: requestBody.size
2404
+ };
2405
+ }
2406
+ let requestEvent = {
2407
+ id: requestId,
2408
+ traceId,
2409
+ sessionId: getSessionId(),
2410
+ timestamp: startTime,
2411
+ phase: "REQUEST" /* REQUEST */,
2412
+ networkType: "incoming" /* INCOMING */,
2413
+ url,
2414
+ method,
2415
+ headers: redactSensitiveHeaders(headers),
2416
+ body: requestBody,
2417
+ name: url.split("?")[0]?.split("/").filter(Boolean).pop() ?? "/",
2418
+ initiator: "incoming",
2419
+ requestSize: requestBody?.size ?? 0
2420
+ };
2421
+ if (config?.beforeSend) {
2422
+ const modified = config.beforeSend(requestEvent);
2423
+ if (!modified) return;
2424
+ if (modified.phase !== "REQUEST" /* REQUEST */) {
2425
+ console.error("[Limelight] beforeSend must return same event type");
2426
+ return;
2427
+ }
2428
+ requestEvent = modified;
2429
+ }
2430
+ sendMessage(requestEvent);
2431
+ const chunks = [];
2432
+ let totalSize = 0;
2433
+ const originalWrite = res.write;
2434
+ const originalEnd = res.end;
2435
+ res.write = (chunk, ...args) => {
2436
+ if (chunk && typeof chunk !== "function" && totalSize < maxBodySize) {
2437
+ const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
2438
+ chunks.push(buf);
2439
+ totalSize += buf.length;
2440
+ }
2441
+ return originalWrite.apply(res, [chunk, ...args]);
2442
+ };
2443
+ res.end = (chunk, ...args) => {
2444
+ if (chunk && typeof chunk !== "function" && totalSize < maxBodySize) {
2445
+ const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
2446
+ chunks.push(buf);
2447
+ totalSize += buf.length;
2448
+ }
2449
+ return originalEnd.apply(res, [chunk, ...args]);
2450
+ };
2451
+ res.on("finish", () => {
2452
+ const endTime = Date.now();
2453
+ const duration = endTime - startTime;
2454
+ const responseHeaders = {};
2455
+ const rawHeaders = res.getHeaders();
2456
+ for (const [key, value] of Object.entries(rawHeaders)) {
2457
+ if (value) {
2458
+ responseHeaders[key.toLowerCase()] = Array.isArray(value) ? value.join(", ") : String(value);
2459
+ }
2460
+ }
2461
+ let responseBodyStr;
2462
+ if (chunks.length > 0 && !config?.disableBodyCapture) {
2463
+ const full = Buffer.concat(chunks);
2464
+ const fullStr = full.toString("utf-8");
2465
+ responseBodyStr = fullStr.length > maxBodySize ? fullStr.slice(0, maxBodySize) + "...[truncated]" : fullStr;
2466
+ }
2467
+ const responseBody = serializeBody(
2468
+ responseBodyStr,
2469
+ config?.disableBodyCapture
2470
+ );
2471
+ let responseEvent = {
2472
+ id: requestId,
2473
+ traceId,
2474
+ sessionId: getSessionId(),
2475
+ timestamp: endTime,
2476
+ phase: "RESPONSE" /* RESPONSE */,
2477
+ networkType: "incoming" /* INCOMING */,
2478
+ status: res.statusCode,
2479
+ statusText: res.statusMessage || "",
2480
+ headers: redactSensitiveHeaders(responseHeaders),
2481
+ body: responseBody,
2482
+ duration,
2483
+ responseSize: responseBody?.size ?? 0,
2484
+ redirected: false,
2485
+ ok: res.statusCode >= 200 && res.statusCode < 300
2486
+ };
2487
+ if (config?.beforeSend) {
2488
+ const modified = config.beforeSend(responseEvent);
2489
+ if (!modified) return;
2490
+ if (modified.phase !== "RESPONSE" /* RESPONSE */) {
2491
+ console.error("[Limelight] beforeSend must return same event type");
2492
+ return;
2493
+ }
2494
+ responseEvent = modified;
2495
+ }
2496
+ sendMessage(responseEvent);
2497
+ });
2498
+ };
2499
+ var createHttpMiddleware = (sendMessage, getSessionId, getConfig, options) => {
2500
+ return (req, res, next) => {
2501
+ captureRequest(req, res, sendMessage, getSessionId, getConfig(), options);
2502
+ const traceId = req.limelightTraceId;
2503
+ const ctx = getTraceContext();
2504
+ if (ctx && traceId) {
2505
+ ctx.run({ traceId }, next);
2506
+ } else {
2507
+ next();
2508
+ }
2509
+ };
2510
+ };
2511
+
2512
+ // src/limelight/middleware/withLimelight.ts
2513
+ var createWithLimelight = (sendMessage, getSessionId, getConfig, options) => {
2514
+ return (handler) => {
2515
+ return (req, res) => {
2516
+ captureRequest(req, res, sendMessage, getSessionId, getConfig(), options);
2517
+ const traceId = req.limelightTraceId;
2518
+ const ctx = getTraceContext();
2519
+ if (ctx && traceId) {
2520
+ return ctx.run({ traceId }, () => handler(req, res));
2521
+ }
2522
+ return handler(req, res);
2523
+ };
2524
+ };
2525
+ };
2526
+
2242
2527
  // src/limelight/LimelightClient.ts
2243
2528
  var LimelightClient = class {
2244
2529
  ws = null;
@@ -2255,6 +2540,7 @@ var LimelightClient = class {
2255
2540
  consoleInterceptor;
2256
2541
  renderInterceptor;
2257
2542
  stateInterceptor;
2543
+ errorInterceptor;
2258
2544
  requestBridge;
2259
2545
  commandHandler = null;
2260
2546
  constructor() {
@@ -2278,6 +2564,10 @@ var LimelightClient = class {
2278
2564
  this.sendMessage.bind(this),
2279
2565
  () => this.sessionId
2280
2566
  );
2567
+ this.errorInterceptor = new ErrorInterceptor(
2568
+ this.sendMessage.bind(this),
2569
+ () => this.sessionId
2570
+ );
2281
2571
  this.requestBridge = new RequestBridge(
2282
2572
  this.sendMessage.bind(this),
2283
2573
  () => this.sessionId
@@ -2298,7 +2588,7 @@ var LimelightClient = class {
2298
2588
  */
2299
2589
  configure(config) {
2300
2590
  const isEnabled = config?.enabled ?? isDevelopment();
2301
- const configServerUrl = config?.serverUrl ? config.serverUrl : config?.projectKey ? `${LIMELIGHT_WEB_WSS_URL}${WS_PATH}` : `${LIMELIGHT_DESKTOP_WSS_URL}${WS_PATH}`;
2591
+ const configServerUrl = config?.serverUrl ? config.serverUrl : config?.target === "mcp" ? LIMELIGHT_MCP_WS_URL : config?.projectKey ? `${LIMELIGHT_WEB_WSS_URL}${WS_PATH}` : `${LIMELIGHT_DESKTOP_WSS_URL}${WS_PATH}`;
2302
2592
  this.config = {
2303
2593
  ...config,
2304
2594
  appName: config?.appName ?? "Limelight App",
@@ -2319,12 +2609,17 @@ var LimelightClient = class {
2319
2609
  try {
2320
2610
  if (this.config.enableNetworkInspector) {
2321
2611
  this.networkInterceptor.setup(this.config);
2322
- this.xhrInterceptor.setup(this.config);
2612
+ if (typeof XMLHttpRequest !== "undefined") {
2613
+ this.xhrInterceptor.setup(this.config);
2614
+ }
2323
2615
  }
2324
2616
  if (this.config.enableConsole) {
2325
2617
  this.consoleInterceptor.setup(this.config);
2618
+ if (!hasDOM()) {
2619
+ this.errorInterceptor.setup(this.config);
2620
+ }
2326
2621
  }
2327
- if (this.config.enableRenderInspector) {
2622
+ if (this.config.enableRenderInspector && hasDOM()) {
2328
2623
  this.renderInterceptor.setup(this.config);
2329
2624
  }
2330
2625
  if (this.config.stores && this.config.enableStateInspector) {
@@ -2352,7 +2647,7 @@ var LimelightClient = class {
2352
2647
  if (!this.config?.enabled) {
2353
2648
  return;
2354
2649
  }
2355
- if (this.ws && this.ws.readyState === WebSocket.OPEN) {
2650
+ if (this.ws && this.ws.readyState === 1) {
2356
2651
  if (this.config?.enableInternalLogging) {
2357
2652
  console.warn("[Limelight] Already connected. Call disconnect() first.");
2358
2653
  }
@@ -2375,15 +2670,24 @@ var LimelightClient = class {
2375
2670
  }
2376
2671
  return;
2377
2672
  }
2673
+ const WsConstructor = this.config.webSocketImpl ?? (typeof WebSocket !== "undefined" ? WebSocket : void 0);
2674
+ if (!WsConstructor) {
2675
+ if (this.config?.enableInternalLogging) {
2676
+ console.error(
2677
+ "[Limelight] WebSocket is not available. Pass webSocketImpl in config (e.g. ws package)."
2678
+ );
2679
+ }
2680
+ return;
2681
+ }
2378
2682
  try {
2379
- this.ws = new WebSocket(serverUrl);
2683
+ this.ws = new WsConstructor(serverUrl);
2380
2684
  const message = {
2381
2685
  phase: "CONNECT",
2382
2686
  sessionId: this.sessionId,
2383
2687
  timestamp: Date.now(),
2384
2688
  data: {
2385
2689
  appName,
2386
- platform: platform || (typeof window !== "undefined" ? "web" : "react-native"),
2690
+ platform: platform || (hasDOM() ? "web" : typeof process !== "undefined" ? "node" : "react-native"),
2387
2691
  projectKey: this.config.projectKey || "",
2388
2692
  sdkVersion: SDK_VERSION
2389
2693
  }
@@ -2450,10 +2754,13 @@ var LimelightClient = class {
2450
2754
  * @returns {void}
2451
2755
  */
2452
2756
  flushMessageQueue() {
2453
- if (this.ws?.readyState !== WebSocket.OPEN) return;
2757
+ if (this.ws?.readyState !== 1) return;
2454
2758
  while (this.messageQueue.length > 0) {
2455
2759
  const message = this.messageQueue.shift();
2456
2760
  try {
2761
+ if (message && "sessionId" in message && !message.sessionId) {
2762
+ message.sessionId = this.sessionId;
2763
+ }
2457
2764
  this.ws.send(safeStringify(message));
2458
2765
  } catch (error) {
2459
2766
  if (this.config?.enableInternalLogging) {
@@ -2471,9 +2778,9 @@ var LimelightClient = class {
2471
2778
  * @returns {void}
2472
2779
  */
2473
2780
  sendMessage(message) {
2474
- if (this.ws?.readyState === WebSocket.OPEN) {
2781
+ if (this.ws?.readyState === 1) {
2475
2782
  this.flushMessageQueue();
2476
- if (this.ws?.readyState === WebSocket.OPEN) {
2783
+ if (this.ws?.readyState === 1) {
2477
2784
  try {
2478
2785
  this.ws.send(safeStringify(message));
2479
2786
  } catch (error) {
@@ -2531,6 +2838,7 @@ var LimelightClient = class {
2531
2838
  this.networkInterceptor.cleanup();
2532
2839
  this.xhrInterceptor.cleanup();
2533
2840
  this.consoleInterceptor.cleanup();
2841
+ this.errorInterceptor.cleanup();
2534
2842
  this.renderInterceptor.cleanup();
2535
2843
  this.stateInterceptor.cleanup();
2536
2844
  this.requestBridge.cleanup();
@@ -2577,6 +2885,46 @@ var LimelightClient = class {
2577
2885
  failRequest(requestId, error) {
2578
2886
  this.requestBridge.failRequest(requestId, error);
2579
2887
  }
2888
+ /**
2889
+ * Returns an Express/Connect-compatible middleware that captures incoming
2890
+ * HTTP requests and responses.
2891
+ *
2892
+ * Place after body-parser middleware (express.json(), etc.) for request body capture.
2893
+ *
2894
+ * @example
2895
+ * ```ts
2896
+ * app.use(express.json());
2897
+ * app.use(Limelight.middleware());
2898
+ * ```
2899
+ */
2900
+ middleware(options) {
2901
+ return createHttpMiddleware(
2902
+ this.sendMessage.bind(this),
2903
+ () => this.sessionId,
2904
+ () => this.config,
2905
+ options
2906
+ );
2907
+ }
2908
+ /**
2909
+ * Wraps a Next.js Pages API route handler with request/response capture.
2910
+ * Works with Pages Router (`pages/api/`), not App Router (`app/api/`).
2911
+ *
2912
+ * @example
2913
+ * ```ts
2914
+ * // pages/api/users.ts
2915
+ * export default Limelight.withLimelight((req, res) => {
2916
+ * res.json({ ok: true });
2917
+ * });
2918
+ * ```
2919
+ */
2920
+ withLimelight(handler) {
2921
+ const wrapper = createWithLimelight(
2922
+ this.sendMessage.bind(this),
2923
+ () => this.sessionId,
2924
+ () => this.config
2925
+ );
2926
+ return wrapper(handler);
2927
+ }
2580
2928
  };
2581
2929
  var Limelight = new LimelightClient();
2582
2930
  export {