@getlimelight/sdk 0.6.1 → 0.7.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -13
- package/dist/index.d.mts +102 -27
- package/dist/index.d.ts +102 -27
- package/dist/index.js +772 -32
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +773 -32
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// Bridge Node CJS require to globalThis so runtime dynamic requires work
|
|
2
|
+
// in environments like ts-node where require is module-scoped only.
|
|
3
|
+
// In ESM or browser contexts typeof require is 'undefined', so this is a no-op.
|
|
4
|
+
if (typeof require === "function" && typeof globalThis !== "undefined" && typeof globalThis.require === "undefined" && typeof process !== "undefined" && process.versions && process.versions.node) { Object.defineProperty(globalThis, "require", { value: require, configurable: true, writable: true, enumerable: false }); }
|
|
1
5
|
"use strict";
|
|
2
6
|
var __defProp = Object.defineProperty;
|
|
3
7
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -66,6 +70,8 @@ var NetworkType = /* @__PURE__ */ ((NetworkType2) => {
|
|
|
66
70
|
NetworkType2["FETCH"] = "fetch";
|
|
67
71
|
NetworkType2["XHR"] = "xhr";
|
|
68
72
|
NetworkType2["GRAPHQL"] = "graphql";
|
|
73
|
+
NetworkType2["INCOMING"] = "incoming";
|
|
74
|
+
NetworkType2["HTTP"] = "http";
|
|
69
75
|
return NetworkType2;
|
|
70
76
|
})(NetworkType || {});
|
|
71
77
|
var NetworkPhase = /* @__PURE__ */ ((NetworkPhase2) => {
|
|
@@ -86,17 +92,17 @@ var BodyFormat = /* @__PURE__ */ ((BodyFormat2) => {
|
|
|
86
92
|
BodyFormat2["UNSERIALIZABLE"] = "UNSERIALIZABLE";
|
|
87
93
|
return BodyFormat2;
|
|
88
94
|
})(BodyFormat || {});
|
|
89
|
-
var HttpMethod = /* @__PURE__ */ ((
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
return
|
|
95
|
+
var HttpMethod = /* @__PURE__ */ ((HttpMethod7) => {
|
|
96
|
+
HttpMethod7["GET"] = "GET";
|
|
97
|
+
HttpMethod7["POST"] = "POST";
|
|
98
|
+
HttpMethod7["PUT"] = "PUT";
|
|
99
|
+
HttpMethod7["PATCH"] = "PATCH";
|
|
100
|
+
HttpMethod7["DELETE"] = "DELETE";
|
|
101
|
+
HttpMethod7["HEAD"] = "HEAD";
|
|
102
|
+
HttpMethod7["OPTIONS"] = "OPTIONS";
|
|
103
|
+
HttpMethod7["TRACE"] = "TRACE";
|
|
104
|
+
HttpMethod7["CONNECT"] = "CONNECT";
|
|
105
|
+
return HttpMethod7;
|
|
100
106
|
})(HttpMethod || {});
|
|
101
107
|
var HttpStatusClass = /* @__PURE__ */ ((HttpStatusClass2) => {
|
|
102
108
|
HttpStatusClass2[HttpStatusClass2["INFORMATIONAL"] = 100] = "INFORMATIONAL";
|
|
@@ -286,7 +292,7 @@ var LIMELIGHT_WEB_WSS_URL = "wss://api.getlimelight.io";
|
|
|
286
292
|
var LIMELIGHT_DESKTOP_WSS_URL = "ws://localhost:8484";
|
|
287
293
|
var LIMELIGHT_MCP_WS_URL = "ws://localhost:9229";
|
|
288
294
|
var WS_PATH = "/limelight";
|
|
289
|
-
var SDK_VERSION = true ? "0.
|
|
295
|
+
var SDK_VERSION = true ? "0.7.8" : "test-version";
|
|
290
296
|
var RENDER_THRESHOLDS = {
|
|
291
297
|
HOT_VELOCITY: 5,
|
|
292
298
|
HIGH_RENDER_COUNT: 50,
|
|
@@ -300,6 +306,16 @@ var RENDER_THRESHOLDS = {
|
|
|
300
306
|
TOP_PROPS_TO_REPORT: 5
|
|
301
307
|
// Only report top N changed props
|
|
302
308
|
};
|
|
309
|
+
var BINARY_CONTENT_TYPES = [
|
|
310
|
+
"image/",
|
|
311
|
+
"audio/",
|
|
312
|
+
"video/",
|
|
313
|
+
"application/octet-stream",
|
|
314
|
+
"application/pdf",
|
|
315
|
+
"application/zip",
|
|
316
|
+
"application/gzip"
|
|
317
|
+
];
|
|
318
|
+
var MAX_BODY_SIZE = 1024 * 1024;
|
|
303
319
|
|
|
304
320
|
// src/helpers/safety/redactSensitiveHeaders.ts
|
|
305
321
|
var redactSensitiveHeaders = (headers) => {
|
|
@@ -540,6 +556,9 @@ var formatRequestName = (url) => {
|
|
|
540
556
|
}
|
|
541
557
|
};
|
|
542
558
|
|
|
559
|
+
// src/helpers/utils/environment.ts
|
|
560
|
+
var hasDOM = () => typeof window !== "undefined" && typeof document !== "undefined";
|
|
561
|
+
|
|
543
562
|
// src/helpers/render/generateRenderId.ts
|
|
544
563
|
var counter = 0;
|
|
545
564
|
var generateRenderId = () => {
|
|
@@ -576,6 +595,21 @@ var getCurrentTransactionId = () => {
|
|
|
576
595
|
return globalGetTransactionId?.() ?? null;
|
|
577
596
|
};
|
|
578
597
|
|
|
598
|
+
// src/helpers/http/resolveUrl.ts
|
|
599
|
+
var resolveUrl = (protocol, options) => {
|
|
600
|
+
const host = options.hostname || options.host || "localhost";
|
|
601
|
+
const port = options.port ? `:${options.port}` : "";
|
|
602
|
+
const path = options.path || "/";
|
|
603
|
+
return `${protocol}//${host}${port}${path}`;
|
|
604
|
+
};
|
|
605
|
+
|
|
606
|
+
// src/helpers/http/isBinaryContentType.ts
|
|
607
|
+
var isBinaryContentType = (contentType) => {
|
|
608
|
+
return BINARY_CONTENT_TYPES.some(
|
|
609
|
+
(type) => contentType.toLowerCase().includes(type)
|
|
610
|
+
);
|
|
611
|
+
};
|
|
612
|
+
|
|
579
613
|
// src/limelight/interceptors/ConsoleInterceptor.ts
|
|
580
614
|
var ConsoleInterceptor = class {
|
|
581
615
|
constructor(sendMessage, getSessionId) {
|
|
@@ -705,13 +739,31 @@ var generateRequestId = () => {
|
|
|
705
739
|
return `req-${Date.now()}-${Math.random().toString(36).substring(7)}`;
|
|
706
740
|
};
|
|
707
741
|
|
|
742
|
+
// src/limelight/context/traceContext.ts
|
|
743
|
+
var _resolved = false;
|
|
744
|
+
var _traceContext;
|
|
745
|
+
var getTraceContext = () => {
|
|
746
|
+
if (!_resolved) {
|
|
747
|
+
_resolved = true;
|
|
748
|
+
try {
|
|
749
|
+
const _require = globalThis["require"];
|
|
750
|
+
const { AsyncLocalStorage } = _require("node:async_hooks");
|
|
751
|
+
_traceContext = new AsyncLocalStorage();
|
|
752
|
+
} catch {
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
return _traceContext;
|
|
756
|
+
};
|
|
757
|
+
|
|
708
758
|
// src/limelight/interceptors/NetworkInterceptor.ts
|
|
709
759
|
var NetworkInterceptor = class {
|
|
710
760
|
constructor(sendMessage, getSessionId) {
|
|
711
761
|
this.sendMessage = sendMessage;
|
|
712
762
|
this.getSessionId = getSessionId;
|
|
713
763
|
this.globalObject = detectGlobalObject();
|
|
714
|
-
|
|
764
|
+
if (typeof this.globalObject.fetch === "function") {
|
|
765
|
+
this.originalFetch = this.globalObject.fetch.bind(this.globalObject);
|
|
766
|
+
}
|
|
715
767
|
}
|
|
716
768
|
originalFetch;
|
|
717
769
|
config = null;
|
|
@@ -731,9 +783,18 @@ var NetworkInterceptor = class {
|
|
|
731
783
|
}
|
|
732
784
|
return;
|
|
733
785
|
}
|
|
786
|
+
if (!this.originalFetch) {
|
|
787
|
+
if (config?.enableInternalLogging) {
|
|
788
|
+
console.warn(
|
|
789
|
+
"[Limelight] fetch is not available in this environment, skipping network interception"
|
|
790
|
+
);
|
|
791
|
+
}
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
734
794
|
this.isSetup = true;
|
|
735
795
|
this.config = config;
|
|
736
796
|
const self2 = this;
|
|
797
|
+
const originalFetch = this.originalFetch;
|
|
737
798
|
this.globalObject.fetch = async function(input, init = {}) {
|
|
738
799
|
const requestId = generateRequestId();
|
|
739
800
|
const startTime = Date.now();
|
|
@@ -751,6 +812,12 @@ var NetworkInterceptor = class {
|
|
|
751
812
|
});
|
|
752
813
|
}
|
|
753
814
|
headers["x-limelight-intercepted"] = "fetch";
|
|
815
|
+
const traceHeaderName = self2.config?.traceHeaderName ?? "x-limelight-trace-id";
|
|
816
|
+
if (!headers[traceHeaderName]) {
|
|
817
|
+
const existingTraceId = getTraceContext()?.getStore()?.traceId;
|
|
818
|
+
headers[traceHeaderName] = existingTraceId || generateRequestId();
|
|
819
|
+
}
|
|
820
|
+
const traceId = headers[traceHeaderName];
|
|
754
821
|
modifiedInit.headers = new Headers(headers);
|
|
755
822
|
let requestBodyToSerialize = init.body;
|
|
756
823
|
if (input instanceof Request && !requestBodyToSerialize) {
|
|
@@ -784,6 +851,7 @@ var NetworkInterceptor = class {
|
|
|
784
851
|
}
|
|
785
852
|
let requestEvent = {
|
|
786
853
|
id: requestId,
|
|
854
|
+
traceId,
|
|
787
855
|
sessionId: self2.getSessionId(),
|
|
788
856
|
timestamp: startTime,
|
|
789
857
|
phase: "REQUEST" /* REQUEST */,
|
|
@@ -800,17 +868,17 @@ var NetworkInterceptor = class {
|
|
|
800
868
|
if (self2.config?.beforeSend) {
|
|
801
869
|
const modifiedEvent = self2.config.beforeSend(requestEvent);
|
|
802
870
|
if (!modifiedEvent) {
|
|
803
|
-
return
|
|
871
|
+
return originalFetch(input, modifiedInit);
|
|
804
872
|
}
|
|
805
873
|
if (modifiedEvent.phase !== "REQUEST" /* REQUEST */) {
|
|
806
874
|
console.error("[Limelight] beforeSend must return same event type");
|
|
807
|
-
return
|
|
875
|
+
return originalFetch(input, modifiedInit);
|
|
808
876
|
}
|
|
809
877
|
requestEvent = modifiedEvent;
|
|
810
878
|
}
|
|
811
879
|
self2.sendMessage(requestEvent);
|
|
812
880
|
try {
|
|
813
|
-
const response = await
|
|
881
|
+
const response = await originalFetch(input, modifiedInit);
|
|
814
882
|
const clone = response.clone();
|
|
815
883
|
const endTime = Date.now();
|
|
816
884
|
const duration = endTime - startTime;
|
|
@@ -830,6 +898,7 @@ var NetworkInterceptor = class {
|
|
|
830
898
|
);
|
|
831
899
|
let responseEvent = {
|
|
832
900
|
id: requestId,
|
|
901
|
+
traceId,
|
|
833
902
|
sessionId: self2.getSessionId(),
|
|
834
903
|
timestamp: endTime,
|
|
835
904
|
phase: "RESPONSE" /* RESPONSE */,
|
|
@@ -862,6 +931,7 @@ var NetworkInterceptor = class {
|
|
|
862
931
|
const errorStack = err instanceof Error ? err.stack : void 0;
|
|
863
932
|
let errorEvent = {
|
|
864
933
|
id: requestId,
|
|
934
|
+
traceId,
|
|
865
935
|
sessionId: self2.getSessionId(),
|
|
866
936
|
timestamp: Date.now(),
|
|
867
937
|
phase: isAbort ? "ABORT" /* ABORT */ : "ERROR" /* ERROR */,
|
|
@@ -892,7 +962,9 @@ var NetworkInterceptor = class {
|
|
|
892
962
|
return;
|
|
893
963
|
}
|
|
894
964
|
this.isSetup = false;
|
|
895
|
-
|
|
965
|
+
if (this.originalFetch) {
|
|
966
|
+
this.globalObject.fetch = this.originalFetch;
|
|
967
|
+
}
|
|
896
968
|
}
|
|
897
969
|
};
|
|
898
970
|
|
|
@@ -901,9 +973,11 @@ var XHRInterceptor = class {
|
|
|
901
973
|
constructor(sendMessage, getSessionId) {
|
|
902
974
|
this.sendMessage = sendMessage;
|
|
903
975
|
this.getSessionId = getSessionId;
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
976
|
+
if (typeof XMLHttpRequest !== "undefined") {
|
|
977
|
+
this.originalXHROpen = XMLHttpRequest.prototype.open;
|
|
978
|
+
this.originalXHRSend = XMLHttpRequest.prototype.send;
|
|
979
|
+
this.originalXHRSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
|
|
980
|
+
}
|
|
907
981
|
}
|
|
908
982
|
originalXHROpen;
|
|
909
983
|
originalXHRSend;
|
|
@@ -918,6 +992,9 @@ var XHRInterceptor = class {
|
|
|
918
992
|
* @returns {void}
|
|
919
993
|
*/
|
|
920
994
|
setup(config) {
|
|
995
|
+
if (typeof XMLHttpRequest === "undefined") {
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
921
998
|
if (this.isSetup) {
|
|
922
999
|
if (this.config?.enableInternalLogging) {
|
|
923
1000
|
console.warn("[Limelight] XHR interceptor already set up");
|
|
@@ -956,12 +1033,24 @@ var XHRInterceptor = class {
|
|
|
956
1033
|
return self2.originalXHRSend.apply(this, arguments);
|
|
957
1034
|
}
|
|
958
1035
|
if (data) {
|
|
1036
|
+
const traceHeaderName = self2.config?.traceHeaderName ?? "x-limelight-trace-id";
|
|
1037
|
+
if (!data.headers[traceHeaderName]) {
|
|
1038
|
+
data.traceId = generateRequestId();
|
|
1039
|
+
self2.originalXHRSetRequestHeader.call(
|
|
1040
|
+
this,
|
|
1041
|
+
traceHeaderName,
|
|
1042
|
+
data.traceId
|
|
1043
|
+
);
|
|
1044
|
+
} else {
|
|
1045
|
+
data.traceId = data.headers[traceHeaderName];
|
|
1046
|
+
}
|
|
959
1047
|
const requestBody = serializeBody(
|
|
960
1048
|
body,
|
|
961
1049
|
self2.config?.disableBodyCapture
|
|
962
1050
|
);
|
|
963
1051
|
let requestEvent = {
|
|
964
1052
|
id: data.id,
|
|
1053
|
+
traceId: data.traceId,
|
|
965
1054
|
sessionId: self2.getSessionId(),
|
|
966
1055
|
timestamp: data.startTime,
|
|
967
1056
|
phase: "REQUEST" /* REQUEST */,
|
|
@@ -1021,6 +1110,7 @@ var XHRInterceptor = class {
|
|
|
1021
1110
|
);
|
|
1022
1111
|
let responseEvent = {
|
|
1023
1112
|
id: data.id,
|
|
1113
|
+
traceId: data.traceId,
|
|
1024
1114
|
sessionId: self2.getSessionId(),
|
|
1025
1115
|
timestamp: endTime,
|
|
1026
1116
|
phase: "RESPONSE" /* RESPONSE */,
|
|
@@ -1055,6 +1145,7 @@ var XHRInterceptor = class {
|
|
|
1055
1145
|
responseSent = true;
|
|
1056
1146
|
let errorEvent = {
|
|
1057
1147
|
id: data.id,
|
|
1148
|
+
traceId: data.traceId,
|
|
1058
1149
|
sessionId: self2.getSessionId(),
|
|
1059
1150
|
timestamp: Date.now(),
|
|
1060
1151
|
phase,
|
|
@@ -1133,15 +1224,368 @@ var XHRInterceptor = class {
|
|
|
1133
1224
|
*/
|
|
1134
1225
|
cleanup() {
|
|
1135
1226
|
if (!this.isSetup) {
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
this.isSetup = false;
|
|
1230
|
+
if (typeof XMLHttpRequest !== "undefined") {
|
|
1231
|
+
XMLHttpRequest.prototype.open = this.originalXHROpen;
|
|
1232
|
+
XMLHttpRequest.prototype.send = this.originalXHRSend;
|
|
1233
|
+
XMLHttpRequest.prototype.setRequestHeader = this.originalXHRSetRequestHeader;
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
};
|
|
1237
|
+
|
|
1238
|
+
// src/limelight/interceptors/HttpInterceptor.ts
|
|
1239
|
+
var HttpInterceptor = class {
|
|
1240
|
+
constructor(sendMessage, getSessionId) {
|
|
1241
|
+
this.sendMessage = sendMessage;
|
|
1242
|
+
this.getSessionId = getSessionId;
|
|
1243
|
+
try {
|
|
1244
|
+
const _require = globalThis["require"];
|
|
1245
|
+
this.httpModule = _require("http");
|
|
1246
|
+
this.httpsModule = _require("https");
|
|
1247
|
+
this.originalHttpRequest = this.httpModule.request;
|
|
1248
|
+
this.originalHttpGet = this.httpModule.get;
|
|
1249
|
+
this.originalHttpsRequest = this.httpsModule.request;
|
|
1250
|
+
this.originalHttpsGet = this.httpsModule.get;
|
|
1251
|
+
} catch {
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
originalHttpRequest = null;
|
|
1255
|
+
originalHttpGet = null;
|
|
1256
|
+
originalHttpsRequest = null;
|
|
1257
|
+
originalHttpsGet = null;
|
|
1258
|
+
httpModule = null;
|
|
1259
|
+
httpsModule = null;
|
|
1260
|
+
config = null;
|
|
1261
|
+
isSetup = false;
|
|
1262
|
+
setup(config) {
|
|
1263
|
+
if (this.isSetup) {
|
|
1136
1264
|
if (this.config?.enableInternalLogging) {
|
|
1137
|
-
console.warn("[Limelight]
|
|
1265
|
+
console.warn("[Limelight] HTTP interceptor already set up");
|
|
1266
|
+
}
|
|
1267
|
+
return;
|
|
1268
|
+
}
|
|
1269
|
+
if (!this.httpModule || !this.httpsModule) {
|
|
1270
|
+
if (config?.enableInternalLogging) {
|
|
1271
|
+
console.warn(
|
|
1272
|
+
"[Limelight] Node http module not available, skipping HTTP interception"
|
|
1273
|
+
);
|
|
1274
|
+
}
|
|
1275
|
+
return;
|
|
1276
|
+
}
|
|
1277
|
+
this.isSetup = true;
|
|
1278
|
+
this.config = config;
|
|
1279
|
+
const self2 = this;
|
|
1280
|
+
const httpMod = this.httpModule;
|
|
1281
|
+
const httpsMod = this.httpsModule;
|
|
1282
|
+
httpMod.request = (...args) => {
|
|
1283
|
+
return self2.interceptRequest(
|
|
1284
|
+
"http:",
|
|
1285
|
+
self2.originalHttpRequest,
|
|
1286
|
+
httpMod,
|
|
1287
|
+
args
|
|
1288
|
+
);
|
|
1289
|
+
};
|
|
1290
|
+
httpMod.get = (...args) => {
|
|
1291
|
+
const req = self2.interceptRequest(
|
|
1292
|
+
"http:",
|
|
1293
|
+
self2.originalHttpRequest,
|
|
1294
|
+
httpMod,
|
|
1295
|
+
args
|
|
1296
|
+
);
|
|
1297
|
+
req.end();
|
|
1298
|
+
return req;
|
|
1299
|
+
};
|
|
1300
|
+
httpsMod.request = (...args) => {
|
|
1301
|
+
return self2.interceptRequest(
|
|
1302
|
+
"https:",
|
|
1303
|
+
self2.originalHttpsRequest,
|
|
1304
|
+
httpsMod,
|
|
1305
|
+
args
|
|
1306
|
+
);
|
|
1307
|
+
};
|
|
1308
|
+
httpsMod.get = (...args) => {
|
|
1309
|
+
const req = self2.interceptRequest(
|
|
1310
|
+
"https:",
|
|
1311
|
+
self2.originalHttpsRequest,
|
|
1312
|
+
httpsMod,
|
|
1313
|
+
args
|
|
1314
|
+
);
|
|
1315
|
+
req.end();
|
|
1316
|
+
return req;
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1319
|
+
interceptRequest(protocol, originalMethod, module2, args) {
|
|
1320
|
+
let url;
|
|
1321
|
+
let options;
|
|
1322
|
+
let callback;
|
|
1323
|
+
if (typeof args[0] === "string" || args[0] instanceof URL) {
|
|
1324
|
+
url = args[0];
|
|
1325
|
+
if (typeof args[1] === "function") {
|
|
1326
|
+
options = {};
|
|
1327
|
+
callback = args[1];
|
|
1328
|
+
} else {
|
|
1329
|
+
options = args[1] || {};
|
|
1330
|
+
callback = args[2];
|
|
1331
|
+
}
|
|
1332
|
+
} else {
|
|
1333
|
+
options = args[0] || {};
|
|
1334
|
+
callback = args[1];
|
|
1335
|
+
}
|
|
1336
|
+
let resolvedUrl;
|
|
1337
|
+
if (url) {
|
|
1338
|
+
resolvedUrl = url.toString();
|
|
1339
|
+
} else {
|
|
1340
|
+
resolvedUrl = resolveUrl(protocol, options);
|
|
1341
|
+
}
|
|
1342
|
+
const limelightServerUrl = this.config?.serverUrl || "";
|
|
1343
|
+
if (limelightServerUrl && resolvedUrl.includes(limelightServerUrl)) {
|
|
1344
|
+
return originalMethod.apply(module2, args);
|
|
1345
|
+
}
|
|
1346
|
+
const self2 = this;
|
|
1347
|
+
const requestId = generateRequestId();
|
|
1348
|
+
const startTime = Date.now();
|
|
1349
|
+
const initiator = getInitiator();
|
|
1350
|
+
const traceHeaderName = this.config?.traceHeaderName ?? "x-limelight-trace-id";
|
|
1351
|
+
const existingTraceId = getTraceContext()?.getStore()?.traceId;
|
|
1352
|
+
const traceId = existingTraceId || generateRequestId();
|
|
1353
|
+
if (!options.headers) {
|
|
1354
|
+
options.headers = {};
|
|
1355
|
+
}
|
|
1356
|
+
options.headers[traceHeaderName] = traceId;
|
|
1357
|
+
const method = (options.method || "GET").toUpperCase();
|
|
1358
|
+
let patchedArgs;
|
|
1359
|
+
if (url) {
|
|
1360
|
+
patchedArgs = callback ? [url, options, callback] : [url, options];
|
|
1361
|
+
} else {
|
|
1362
|
+
patchedArgs = callback ? [options, callback] : [options];
|
|
1363
|
+
}
|
|
1364
|
+
const req = originalMethod.apply(
|
|
1365
|
+
module2,
|
|
1366
|
+
patchedArgs
|
|
1367
|
+
);
|
|
1368
|
+
const bodyChunks = [];
|
|
1369
|
+
let totalBodySize = 0;
|
|
1370
|
+
let requestEventSent = false;
|
|
1371
|
+
const originalWrite = req.write.bind(req);
|
|
1372
|
+
const originalEnd = req.end.bind(req);
|
|
1373
|
+
req.write = (chunk, encodingOrCallback, callback2) => {
|
|
1374
|
+
if (chunk && totalBodySize < MAX_BODY_SIZE) {
|
|
1375
|
+
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
1376
|
+
bodyChunks.push(buf);
|
|
1377
|
+
totalBodySize += buf.length;
|
|
1378
|
+
}
|
|
1379
|
+
return originalWrite(chunk, encodingOrCallback, callback2);
|
|
1380
|
+
};
|
|
1381
|
+
req.end = (chunk, encodingOrCallback, callback2) => {
|
|
1382
|
+
if (chunk && typeof chunk !== "function" && totalBodySize < MAX_BODY_SIZE) {
|
|
1383
|
+
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
1384
|
+
bodyChunks.push(buf);
|
|
1385
|
+
totalBodySize += buf.length;
|
|
1386
|
+
}
|
|
1387
|
+
if (!requestEventSent) {
|
|
1388
|
+
requestEventSent = true;
|
|
1389
|
+
const fullBody = bodyChunks.length > 0 ? Buffer.concat(bodyChunks).toString("utf-8") : void 0;
|
|
1390
|
+
const headers = {};
|
|
1391
|
+
const rawHeaders = req.getHeaders();
|
|
1392
|
+
for (const [key, value] of Object.entries(rawHeaders)) {
|
|
1393
|
+
if (value !== void 0) {
|
|
1394
|
+
headers[key.toLowerCase()] = Array.isArray(value) ? value.join(", ") : String(value);
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
const requestBody = serializeBody(
|
|
1398
|
+
fullBody,
|
|
1399
|
+
self2.config?.disableBodyCapture
|
|
1400
|
+
);
|
|
1401
|
+
let requestEvent = {
|
|
1402
|
+
id: requestId,
|
|
1403
|
+
traceId,
|
|
1404
|
+
sessionId: self2.getSessionId(),
|
|
1405
|
+
timestamp: startTime,
|
|
1406
|
+
phase: "REQUEST" /* REQUEST */,
|
|
1407
|
+
networkType: "http" /* HTTP */,
|
|
1408
|
+
url: resolvedUrl,
|
|
1409
|
+
method,
|
|
1410
|
+
headers: redactSensitiveHeaders(headers),
|
|
1411
|
+
body: requestBody,
|
|
1412
|
+
name: formatRequestName(resolvedUrl),
|
|
1413
|
+
initiator,
|
|
1414
|
+
requestSize: requestBody?.size ?? 0
|
|
1415
|
+
};
|
|
1416
|
+
if (self2.config?.beforeSend) {
|
|
1417
|
+
const modifiedEvent = self2.config.beforeSend(requestEvent);
|
|
1418
|
+
if (!modifiedEvent) {
|
|
1419
|
+
return originalEnd(
|
|
1420
|
+
chunk,
|
|
1421
|
+
encodingOrCallback,
|
|
1422
|
+
callback2
|
|
1423
|
+
);
|
|
1424
|
+
}
|
|
1425
|
+
if (modifiedEvent.phase !== "REQUEST" /* REQUEST */) {
|
|
1426
|
+
console.error("[Limelight] beforeSend must return same event type");
|
|
1427
|
+
return originalEnd(
|
|
1428
|
+
chunk,
|
|
1429
|
+
encodingOrCallback,
|
|
1430
|
+
callback2
|
|
1431
|
+
);
|
|
1432
|
+
}
|
|
1433
|
+
requestEvent = modifiedEvent;
|
|
1434
|
+
}
|
|
1435
|
+
self2.sendMessage(requestEvent);
|
|
1436
|
+
}
|
|
1437
|
+
return originalEnd(chunk, encodingOrCallback, callback2);
|
|
1438
|
+
};
|
|
1439
|
+
let responseSent = false;
|
|
1440
|
+
req.on("response", (res) => {
|
|
1441
|
+
const responseChunks = [];
|
|
1442
|
+
let responseSize = 0;
|
|
1443
|
+
res.on("data", (chunk) => {
|
|
1444
|
+
if (responseSize < MAX_BODY_SIZE) {
|
|
1445
|
+
responseChunks.push(chunk);
|
|
1446
|
+
responseSize += chunk.length;
|
|
1447
|
+
}
|
|
1448
|
+
});
|
|
1449
|
+
res.on("end", () => {
|
|
1450
|
+
if (responseSent) return;
|
|
1451
|
+
responseSent = true;
|
|
1452
|
+
const endTime = Date.now();
|
|
1453
|
+
const duration = endTime - startTime;
|
|
1454
|
+
const responseHeaders = {};
|
|
1455
|
+
if (res.headers) {
|
|
1456
|
+
for (const [key, value] of Object.entries(res.headers)) {
|
|
1457
|
+
if (value) {
|
|
1458
|
+
responseHeaders[key.toLowerCase()] = Array.isArray(value) ? value.join(", ") : value;
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
const contentType = responseHeaders["content-type"] || "";
|
|
1463
|
+
let responseBodyStr;
|
|
1464
|
+
if (responseChunks.length > 0) {
|
|
1465
|
+
if (isBinaryContentType(contentType)) {
|
|
1466
|
+
responseBodyStr = `[Binary Data: ${contentType}]`;
|
|
1467
|
+
} else {
|
|
1468
|
+
const full = Buffer.concat(responseChunks);
|
|
1469
|
+
responseBodyStr = full.length > MAX_BODY_SIZE ? full.toString("utf-8", 0, MAX_BODY_SIZE) + "...[truncated]" : full.toString("utf-8");
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
const responseBody = serializeBody(
|
|
1473
|
+
responseBodyStr,
|
|
1474
|
+
self2.config?.disableBodyCapture
|
|
1475
|
+
);
|
|
1476
|
+
const statusCode = res.statusCode ?? 0;
|
|
1477
|
+
let responseEvent = {
|
|
1478
|
+
id: requestId,
|
|
1479
|
+
traceId,
|
|
1480
|
+
sessionId: self2.getSessionId(),
|
|
1481
|
+
timestamp: endTime,
|
|
1482
|
+
phase: "RESPONSE" /* RESPONSE */,
|
|
1483
|
+
networkType: "http" /* HTTP */,
|
|
1484
|
+
status: statusCode,
|
|
1485
|
+
statusText: res.statusMessage || "",
|
|
1486
|
+
headers: redactSensitiveHeaders(responseHeaders),
|
|
1487
|
+
body: responseBody,
|
|
1488
|
+
duration,
|
|
1489
|
+
responseSize: responseBody?.size ?? 0,
|
|
1490
|
+
redirected: false,
|
|
1491
|
+
ok: statusCode >= 200 && statusCode < 300
|
|
1492
|
+
};
|
|
1493
|
+
if (self2.config?.beforeSend) {
|
|
1494
|
+
const modifiedEvent = self2.config.beforeSend(responseEvent);
|
|
1495
|
+
if (!modifiedEvent) return;
|
|
1496
|
+
if (modifiedEvent.phase !== "RESPONSE" /* RESPONSE */) {
|
|
1497
|
+
console.error("[Limelight] beforeSend must return same event type");
|
|
1498
|
+
return;
|
|
1499
|
+
}
|
|
1500
|
+
responseEvent = modifiedEvent;
|
|
1501
|
+
}
|
|
1502
|
+
self2.sendMessage(responseEvent);
|
|
1503
|
+
});
|
|
1504
|
+
});
|
|
1505
|
+
req.on("error", (err) => {
|
|
1506
|
+
if (responseSent) return;
|
|
1507
|
+
responseSent = true;
|
|
1508
|
+
let errorEvent = {
|
|
1509
|
+
id: requestId,
|
|
1510
|
+
traceId,
|
|
1511
|
+
sessionId: self2.getSessionId(),
|
|
1512
|
+
timestamp: Date.now(),
|
|
1513
|
+
phase: "ERROR" /* ERROR */,
|
|
1514
|
+
networkType: "http" /* HTTP */,
|
|
1515
|
+
errorMessage: err.message || "Network request failed",
|
|
1516
|
+
stack: err.stack
|
|
1517
|
+
};
|
|
1518
|
+
if (self2.config?.beforeSend) {
|
|
1519
|
+
const modifiedEvent = self2.config.beforeSend(errorEvent);
|
|
1520
|
+
if (modifiedEvent && (modifiedEvent.phase === "ERROR" /* ERROR */ || modifiedEvent.phase === "ABORT" /* ABORT */)) {
|
|
1521
|
+
errorEvent = modifiedEvent;
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
self2.sendMessage(errorEvent);
|
|
1525
|
+
});
|
|
1526
|
+
req.on("timeout", () => {
|
|
1527
|
+
if (responseSent) return;
|
|
1528
|
+
responseSent = true;
|
|
1529
|
+
let errorEvent = {
|
|
1530
|
+
id: requestId,
|
|
1531
|
+
traceId,
|
|
1532
|
+
sessionId: self2.getSessionId(),
|
|
1533
|
+
timestamp: Date.now(),
|
|
1534
|
+
phase: "ERROR" /* ERROR */,
|
|
1535
|
+
networkType: "http" /* HTTP */,
|
|
1536
|
+
errorMessage: "Request timeout"
|
|
1537
|
+
};
|
|
1538
|
+
if (self2.config?.beforeSend) {
|
|
1539
|
+
const modifiedEvent = self2.config.beforeSend(errorEvent);
|
|
1540
|
+
if (modifiedEvent && (modifiedEvent.phase === "ERROR" /* ERROR */ || modifiedEvent.phase === "ABORT" /* ABORT */)) {
|
|
1541
|
+
errorEvent = modifiedEvent;
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
self2.sendMessage(errorEvent);
|
|
1545
|
+
});
|
|
1546
|
+
req.on("close", () => {
|
|
1547
|
+
if (responseSent) return;
|
|
1548
|
+
if (!req.destroyed) return;
|
|
1549
|
+
responseSent = true;
|
|
1550
|
+
let errorEvent = {
|
|
1551
|
+
id: requestId,
|
|
1552
|
+
traceId,
|
|
1553
|
+
sessionId: self2.getSessionId(),
|
|
1554
|
+
timestamp: Date.now(),
|
|
1555
|
+
phase: "ABORT" /* ABORT */,
|
|
1556
|
+
networkType: "http" /* HTTP */,
|
|
1557
|
+
errorMessage: "Request aborted"
|
|
1558
|
+
};
|
|
1559
|
+
if (self2.config?.beforeSend) {
|
|
1560
|
+
const modifiedEvent = self2.config.beforeSend(errorEvent);
|
|
1561
|
+
if (modifiedEvent && (modifiedEvent.phase === "ERROR" /* ERROR */ || modifiedEvent.phase === "ABORT" /* ABORT */)) {
|
|
1562
|
+
errorEvent = modifiedEvent;
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
self2.sendMessage(errorEvent);
|
|
1566
|
+
});
|
|
1567
|
+
return req;
|
|
1568
|
+
}
|
|
1569
|
+
cleanup() {
|
|
1570
|
+
if (!this.isSetup) {
|
|
1571
|
+
if (this.config?.enableInternalLogging) {
|
|
1572
|
+
console.warn("[Limelight] HTTP interceptor not set up");
|
|
1138
1573
|
}
|
|
1139
1574
|
return;
|
|
1140
1575
|
}
|
|
1141
1576
|
this.isSetup = false;
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1577
|
+
if (this.httpModule && this.originalHttpRequest) {
|
|
1578
|
+
this.httpModule.request = this.originalHttpRequest;
|
|
1579
|
+
}
|
|
1580
|
+
if (this.httpModule && this.originalHttpGet) {
|
|
1581
|
+
this.httpModule.get = this.originalHttpGet;
|
|
1582
|
+
}
|
|
1583
|
+
if (this.httpsModule && this.originalHttpsRequest) {
|
|
1584
|
+
this.httpsModule.request = this.originalHttpsRequest;
|
|
1585
|
+
}
|
|
1586
|
+
if (this.httpsModule && this.originalHttpsGet) {
|
|
1587
|
+
this.httpsModule.get = this.originalHttpsGet;
|
|
1588
|
+
}
|
|
1145
1589
|
}
|
|
1146
1590
|
};
|
|
1147
1591
|
|
|
@@ -1794,6 +2238,83 @@ var RenderInterceptor = class {
|
|
|
1794
2238
|
}
|
|
1795
2239
|
};
|
|
1796
2240
|
|
|
2241
|
+
// src/limelight/interceptors/ErrorInterceptor.ts
|
|
2242
|
+
var ErrorInterceptor = class {
|
|
2243
|
+
constructor(sendMessage, getSessionId) {
|
|
2244
|
+
this.sendMessage = sendMessage;
|
|
2245
|
+
this.getSessionId = getSessionId;
|
|
2246
|
+
}
|
|
2247
|
+
isSetup = false;
|
|
2248
|
+
config = null;
|
|
2249
|
+
counter = 0;
|
|
2250
|
+
uncaughtExceptionHandler = null;
|
|
2251
|
+
unhandledRejectionHandler = null;
|
|
2252
|
+
setup(config) {
|
|
2253
|
+
if (this.isSetup) {
|
|
2254
|
+
if (this.config?.enableInternalLogging) {
|
|
2255
|
+
console.warn("[Limelight] Error interceptor already set up");
|
|
2256
|
+
}
|
|
2257
|
+
return;
|
|
2258
|
+
}
|
|
2259
|
+
if (typeof process === "undefined" || !process.on) {
|
|
2260
|
+
return;
|
|
2261
|
+
}
|
|
2262
|
+
this.isSetup = true;
|
|
2263
|
+
this.config = config;
|
|
2264
|
+
this.uncaughtExceptionHandler = (error) => {
|
|
2265
|
+
this.sendErrorEvent(error, "uncaughtException");
|
|
2266
|
+
setTimeout(() => {
|
|
2267
|
+
process.exit(1);
|
|
2268
|
+
}, 200);
|
|
2269
|
+
};
|
|
2270
|
+
this.unhandledRejectionHandler = (reason) => {
|
|
2271
|
+
const error = reason instanceof Error ? reason : new Error(String(reason));
|
|
2272
|
+
this.sendErrorEvent(error, "unhandledRejection");
|
|
2273
|
+
};
|
|
2274
|
+
process.on("uncaughtException", this.uncaughtExceptionHandler);
|
|
2275
|
+
process.on("unhandledRejection", this.unhandledRejectionHandler);
|
|
2276
|
+
}
|
|
2277
|
+
cleanup() {
|
|
2278
|
+
if (!this.isSetup) return;
|
|
2279
|
+
this.isSetup = false;
|
|
2280
|
+
if (this.uncaughtExceptionHandler) {
|
|
2281
|
+
process.removeListener(
|
|
2282
|
+
"uncaughtException",
|
|
2283
|
+
this.uncaughtExceptionHandler
|
|
2284
|
+
);
|
|
2285
|
+
this.uncaughtExceptionHandler = null;
|
|
2286
|
+
}
|
|
2287
|
+
if (this.unhandledRejectionHandler) {
|
|
2288
|
+
process.removeListener(
|
|
2289
|
+
"unhandledRejection",
|
|
2290
|
+
this.unhandledRejectionHandler
|
|
2291
|
+
);
|
|
2292
|
+
this.unhandledRejectionHandler = null;
|
|
2293
|
+
}
|
|
2294
|
+
this.config = null;
|
|
2295
|
+
}
|
|
2296
|
+
sendErrorEvent(error, source) {
|
|
2297
|
+
const sessionId = this.getSessionId();
|
|
2298
|
+
const event = {
|
|
2299
|
+
id: `${sessionId}-${Date.now()}-${this.counter++}`,
|
|
2300
|
+
phase: "CONSOLE",
|
|
2301
|
+
type: "CONSOLE" /* CONSOLE */,
|
|
2302
|
+
level: "error" /* ERROR */,
|
|
2303
|
+
timestamp: Date.now(),
|
|
2304
|
+
sessionId,
|
|
2305
|
+
source: "app" /* APP */,
|
|
2306
|
+
consoleType: "exception" /* EXCEPTION */,
|
|
2307
|
+
args: [safeStringify(`[${source}] ${error.message}`)],
|
|
2308
|
+
stackTrace: error.stack
|
|
2309
|
+
};
|
|
2310
|
+
if (this.config?.beforeSend) {
|
|
2311
|
+
const modified = this.config.beforeSend(event);
|
|
2312
|
+
if (!modified) return;
|
|
2313
|
+
}
|
|
2314
|
+
this.sendMessage(event);
|
|
2315
|
+
}
|
|
2316
|
+
};
|
|
2317
|
+
|
|
1797
2318
|
// src/limelight/interceptors/StateInterceptor.ts
|
|
1798
2319
|
var StateInterceptor = class {
|
|
1799
2320
|
sendMessage;
|
|
@@ -2277,6 +2798,153 @@ var CommandHandler = class {
|
|
|
2277
2798
|
}
|
|
2278
2799
|
};
|
|
2279
2800
|
|
|
2801
|
+
// src/limelight/middleware/httpMiddleware.ts
|
|
2802
|
+
var DEFAULT_MAX_BODY_SIZE = 64 * 1024;
|
|
2803
|
+
var captureRequest = (req, res, sendMessage, getSessionId, config, options) => {
|
|
2804
|
+
const requestId = generateRequestId();
|
|
2805
|
+
const startTime = Date.now();
|
|
2806
|
+
const maxBodySize = options?.maxBodySize ?? DEFAULT_MAX_BODY_SIZE;
|
|
2807
|
+
const traceHeaderName = config?.traceHeaderName ?? "x-limelight-trace-id";
|
|
2808
|
+
const incomingTraceId = req.headers[traceHeaderName];
|
|
2809
|
+
const traceId = incomingTraceId || generateRequestId();
|
|
2810
|
+
req.limelightTraceId = traceId;
|
|
2811
|
+
const url = req.url || "/";
|
|
2812
|
+
const method = (req.method || "GET").toUpperCase();
|
|
2813
|
+
const headers = {};
|
|
2814
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
2815
|
+
if (value) {
|
|
2816
|
+
headers[key.toLowerCase()] = Array.isArray(value) ? value.join(", ") : value;
|
|
2817
|
+
}
|
|
2818
|
+
}
|
|
2819
|
+
let requestBody = serializeBody(req.body, config?.disableBodyCapture);
|
|
2820
|
+
if (requestBody?.raw && requestBody.raw.length > maxBodySize) {
|
|
2821
|
+
requestBody = {
|
|
2822
|
+
...requestBody,
|
|
2823
|
+
raw: requestBody.raw.slice(0, maxBodySize) + "...[truncated]",
|
|
2824
|
+
size: requestBody.size
|
|
2825
|
+
};
|
|
2826
|
+
}
|
|
2827
|
+
let requestEvent = {
|
|
2828
|
+
id: requestId,
|
|
2829
|
+
traceId,
|
|
2830
|
+
sessionId: getSessionId(),
|
|
2831
|
+
timestamp: startTime,
|
|
2832
|
+
phase: "REQUEST" /* REQUEST */,
|
|
2833
|
+
networkType: "incoming" /* INCOMING */,
|
|
2834
|
+
url,
|
|
2835
|
+
method,
|
|
2836
|
+
headers: redactSensitiveHeaders(headers),
|
|
2837
|
+
body: requestBody,
|
|
2838
|
+
name: url.split("?")[0]?.split("/").filter(Boolean).pop() ?? "/",
|
|
2839
|
+
initiator: "incoming",
|
|
2840
|
+
requestSize: requestBody?.size ?? 0
|
|
2841
|
+
};
|
|
2842
|
+
if (config?.beforeSend) {
|
|
2843
|
+
const modified = config.beforeSend(requestEvent);
|
|
2844
|
+
if (!modified) return;
|
|
2845
|
+
if (modified.phase !== "REQUEST" /* REQUEST */) {
|
|
2846
|
+
console.error("[Limelight] beforeSend must return same event type");
|
|
2847
|
+
return;
|
|
2848
|
+
}
|
|
2849
|
+
requestEvent = modified;
|
|
2850
|
+
}
|
|
2851
|
+
sendMessage(requestEvent);
|
|
2852
|
+
const chunks = [];
|
|
2853
|
+
let totalSize = 0;
|
|
2854
|
+
const originalWrite = res.write;
|
|
2855
|
+
const originalEnd = res.end;
|
|
2856
|
+
res.write = (chunk, ...args) => {
|
|
2857
|
+
if (chunk && typeof chunk !== "function" && totalSize < maxBodySize) {
|
|
2858
|
+
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
2859
|
+
chunks.push(buf);
|
|
2860
|
+
totalSize += buf.length;
|
|
2861
|
+
}
|
|
2862
|
+
return originalWrite.apply(res, [chunk, ...args]);
|
|
2863
|
+
};
|
|
2864
|
+
res.end = (chunk, ...args) => {
|
|
2865
|
+
if (chunk && typeof chunk !== "function" && totalSize < maxBodySize) {
|
|
2866
|
+
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
2867
|
+
chunks.push(buf);
|
|
2868
|
+
totalSize += buf.length;
|
|
2869
|
+
}
|
|
2870
|
+
return originalEnd.apply(res, [chunk, ...args]);
|
|
2871
|
+
};
|
|
2872
|
+
res.on("finish", () => {
|
|
2873
|
+
const endTime = Date.now();
|
|
2874
|
+
const duration = endTime - startTime;
|
|
2875
|
+
const responseHeaders = {};
|
|
2876
|
+
const rawHeaders = res.getHeaders();
|
|
2877
|
+
for (const [key, value] of Object.entries(rawHeaders)) {
|
|
2878
|
+
if (value) {
|
|
2879
|
+
responseHeaders[key.toLowerCase()] = Array.isArray(value) ? value.join(", ") : String(value);
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2882
|
+
let responseBodyStr;
|
|
2883
|
+
if (chunks.length > 0 && !config?.disableBodyCapture) {
|
|
2884
|
+
const full = Buffer.concat(chunks);
|
|
2885
|
+
const fullStr = full.toString("utf-8");
|
|
2886
|
+
responseBodyStr = fullStr.length > maxBodySize ? fullStr.slice(0, maxBodySize) + "...[truncated]" : fullStr;
|
|
2887
|
+
}
|
|
2888
|
+
const responseBody = serializeBody(
|
|
2889
|
+
responseBodyStr,
|
|
2890
|
+
config?.disableBodyCapture
|
|
2891
|
+
);
|
|
2892
|
+
let responseEvent = {
|
|
2893
|
+
id: requestId,
|
|
2894
|
+
traceId,
|
|
2895
|
+
sessionId: getSessionId(),
|
|
2896
|
+
timestamp: endTime,
|
|
2897
|
+
phase: "RESPONSE" /* RESPONSE */,
|
|
2898
|
+
networkType: "incoming" /* INCOMING */,
|
|
2899
|
+
status: res.statusCode,
|
|
2900
|
+
statusText: res.statusMessage || "",
|
|
2901
|
+
headers: redactSensitiveHeaders(responseHeaders),
|
|
2902
|
+
body: responseBody,
|
|
2903
|
+
duration,
|
|
2904
|
+
responseSize: responseBody?.size ?? 0,
|
|
2905
|
+
redirected: false,
|
|
2906
|
+
ok: res.statusCode >= 200 && res.statusCode < 300
|
|
2907
|
+
};
|
|
2908
|
+
if (config?.beforeSend) {
|
|
2909
|
+
const modified = config.beforeSend(responseEvent);
|
|
2910
|
+
if (!modified) return;
|
|
2911
|
+
if (modified.phase !== "RESPONSE" /* RESPONSE */) {
|
|
2912
|
+
console.error("[Limelight] beforeSend must return same event type");
|
|
2913
|
+
return;
|
|
2914
|
+
}
|
|
2915
|
+
responseEvent = modified;
|
|
2916
|
+
}
|
|
2917
|
+
sendMessage(responseEvent);
|
|
2918
|
+
});
|
|
2919
|
+
};
|
|
2920
|
+
var createHttpMiddleware = (sendMessage, getSessionId, getConfig, options) => {
|
|
2921
|
+
return (req, res, next) => {
|
|
2922
|
+
captureRequest(req, res, sendMessage, getSessionId, getConfig(), options);
|
|
2923
|
+
const traceId = req.limelightTraceId;
|
|
2924
|
+
const ctx = getTraceContext();
|
|
2925
|
+
if (ctx && traceId) {
|
|
2926
|
+
ctx.run({ traceId }, next);
|
|
2927
|
+
} else {
|
|
2928
|
+
next();
|
|
2929
|
+
}
|
|
2930
|
+
};
|
|
2931
|
+
};
|
|
2932
|
+
|
|
2933
|
+
// src/limelight/middleware/withLimelight.ts
|
|
2934
|
+
var createWithLimelight = (sendMessage, getSessionId, getConfig, options) => {
|
|
2935
|
+
return (handler) => {
|
|
2936
|
+
return (req, res) => {
|
|
2937
|
+
captureRequest(req, res, sendMessage, getSessionId, getConfig(), options);
|
|
2938
|
+
const traceId = req.limelightTraceId;
|
|
2939
|
+
const ctx = getTraceContext();
|
|
2940
|
+
if (ctx && traceId) {
|
|
2941
|
+
return ctx.run({ traceId }, () => handler(req, res));
|
|
2942
|
+
}
|
|
2943
|
+
return handler(req, res);
|
|
2944
|
+
};
|
|
2945
|
+
};
|
|
2946
|
+
};
|
|
2947
|
+
|
|
2280
2948
|
// src/limelight/LimelightClient.ts
|
|
2281
2949
|
var LimelightClient = class {
|
|
2282
2950
|
ws = null;
|
|
@@ -2290,9 +2958,11 @@ var LimelightClient = class {
|
|
|
2290
2958
|
maxQueueSize = 100;
|
|
2291
2959
|
networkInterceptor;
|
|
2292
2960
|
xhrInterceptor;
|
|
2961
|
+
httpInterceptor;
|
|
2293
2962
|
consoleInterceptor;
|
|
2294
2963
|
renderInterceptor;
|
|
2295
2964
|
stateInterceptor;
|
|
2965
|
+
errorInterceptor;
|
|
2296
2966
|
requestBridge;
|
|
2297
2967
|
commandHandler = null;
|
|
2298
2968
|
constructor() {
|
|
@@ -2304,6 +2974,10 @@ var LimelightClient = class {
|
|
|
2304
2974
|
this.sendMessage.bind(this),
|
|
2305
2975
|
() => this.sessionId
|
|
2306
2976
|
);
|
|
2977
|
+
this.httpInterceptor = new HttpInterceptor(
|
|
2978
|
+
this.sendMessage.bind(this),
|
|
2979
|
+
() => this.sessionId
|
|
2980
|
+
);
|
|
2307
2981
|
this.consoleInterceptor = new ConsoleInterceptor(
|
|
2308
2982
|
this.sendMessage.bind(this),
|
|
2309
2983
|
() => this.sessionId
|
|
@@ -2316,6 +2990,10 @@ var LimelightClient = class {
|
|
|
2316
2990
|
this.sendMessage.bind(this),
|
|
2317
2991
|
() => this.sessionId
|
|
2318
2992
|
);
|
|
2993
|
+
this.errorInterceptor = new ErrorInterceptor(
|
|
2994
|
+
this.sendMessage.bind(this),
|
|
2995
|
+
() => this.sessionId
|
|
2996
|
+
);
|
|
2319
2997
|
this.requestBridge = new RequestBridge(
|
|
2320
2998
|
this.sendMessage.bind(this),
|
|
2321
2999
|
() => this.sessionId
|
|
@@ -2357,12 +3035,20 @@ var LimelightClient = class {
|
|
|
2357
3035
|
try {
|
|
2358
3036
|
if (this.config.enableNetworkInspector) {
|
|
2359
3037
|
this.networkInterceptor.setup(this.config);
|
|
2360
|
-
|
|
3038
|
+
if (typeof XMLHttpRequest !== "undefined") {
|
|
3039
|
+
this.xhrInterceptor.setup(this.config);
|
|
3040
|
+
}
|
|
3041
|
+
if (!hasDOM()) {
|
|
3042
|
+
this.httpInterceptor.setup(this.config);
|
|
3043
|
+
}
|
|
2361
3044
|
}
|
|
2362
3045
|
if (this.config.enableConsole) {
|
|
2363
3046
|
this.consoleInterceptor.setup(this.config);
|
|
3047
|
+
if (!hasDOM()) {
|
|
3048
|
+
this.errorInterceptor.setup(this.config);
|
|
3049
|
+
}
|
|
2364
3050
|
}
|
|
2365
|
-
if (this.config.enableRenderInspector) {
|
|
3051
|
+
if (this.config.enableRenderInspector && hasDOM()) {
|
|
2366
3052
|
this.renderInterceptor.setup(this.config);
|
|
2367
3053
|
}
|
|
2368
3054
|
if (this.config.stores && this.config.enableStateInspector) {
|
|
@@ -2390,7 +3076,7 @@ var LimelightClient = class {
|
|
|
2390
3076
|
if (!this.config?.enabled) {
|
|
2391
3077
|
return;
|
|
2392
3078
|
}
|
|
2393
|
-
if (this.ws && this.ws.readyState ===
|
|
3079
|
+
if (this.ws && this.ws.readyState === 1) {
|
|
2394
3080
|
if (this.config?.enableInternalLogging) {
|
|
2395
3081
|
console.warn("[Limelight] Already connected. Call disconnect() first.");
|
|
2396
3082
|
}
|
|
@@ -2413,15 +3099,24 @@ var LimelightClient = class {
|
|
|
2413
3099
|
}
|
|
2414
3100
|
return;
|
|
2415
3101
|
}
|
|
3102
|
+
const WsConstructor = this.config.webSocketImpl ?? (typeof WebSocket !== "undefined" ? WebSocket : void 0);
|
|
3103
|
+
if (!WsConstructor) {
|
|
3104
|
+
if (this.config?.enableInternalLogging) {
|
|
3105
|
+
console.error(
|
|
3106
|
+
"[Limelight] WebSocket is not available. Pass webSocketImpl in config (e.g. ws package)."
|
|
3107
|
+
);
|
|
3108
|
+
}
|
|
3109
|
+
return;
|
|
3110
|
+
}
|
|
2416
3111
|
try {
|
|
2417
|
-
this.ws = new
|
|
3112
|
+
this.ws = new WsConstructor(serverUrl);
|
|
2418
3113
|
const message = {
|
|
2419
3114
|
phase: "CONNECT",
|
|
2420
3115
|
sessionId: this.sessionId,
|
|
2421
3116
|
timestamp: Date.now(),
|
|
2422
3117
|
data: {
|
|
2423
3118
|
appName,
|
|
2424
|
-
platform: platform || (typeof
|
|
3119
|
+
platform: platform || (hasDOM() ? "web" : typeof process !== "undefined" ? "node" : "react-native"),
|
|
2425
3120
|
projectKey: this.config.projectKey || "",
|
|
2426
3121
|
sdkVersion: SDK_VERSION
|
|
2427
3122
|
}
|
|
@@ -2488,10 +3183,13 @@ var LimelightClient = class {
|
|
|
2488
3183
|
* @returns {void}
|
|
2489
3184
|
*/
|
|
2490
3185
|
flushMessageQueue() {
|
|
2491
|
-
if (this.ws?.readyState !==
|
|
3186
|
+
if (this.ws?.readyState !== 1) return;
|
|
2492
3187
|
while (this.messageQueue.length > 0) {
|
|
2493
3188
|
const message = this.messageQueue.shift();
|
|
2494
3189
|
try {
|
|
3190
|
+
if (message && "sessionId" in message && !message.sessionId) {
|
|
3191
|
+
message.sessionId = this.sessionId;
|
|
3192
|
+
}
|
|
2495
3193
|
this.ws.send(safeStringify(message));
|
|
2496
3194
|
} catch (error) {
|
|
2497
3195
|
if (this.config?.enableInternalLogging) {
|
|
@@ -2509,9 +3207,9 @@ var LimelightClient = class {
|
|
|
2509
3207
|
* @returns {void}
|
|
2510
3208
|
*/
|
|
2511
3209
|
sendMessage(message) {
|
|
2512
|
-
if (this.ws?.readyState ===
|
|
3210
|
+
if (this.ws?.readyState === 1) {
|
|
2513
3211
|
this.flushMessageQueue();
|
|
2514
|
-
if (this.ws?.readyState ===
|
|
3212
|
+
if (this.ws?.readyState === 1) {
|
|
2515
3213
|
try {
|
|
2516
3214
|
this.ws.send(safeStringify(message));
|
|
2517
3215
|
} catch (error) {
|
|
@@ -2568,7 +3266,9 @@ var LimelightClient = class {
|
|
|
2568
3266
|
}
|
|
2569
3267
|
this.networkInterceptor.cleanup();
|
|
2570
3268
|
this.xhrInterceptor.cleanup();
|
|
3269
|
+
this.httpInterceptor.cleanup();
|
|
2571
3270
|
this.consoleInterceptor.cleanup();
|
|
3271
|
+
this.errorInterceptor.cleanup();
|
|
2572
3272
|
this.renderInterceptor.cleanup();
|
|
2573
3273
|
this.stateInterceptor.cleanup();
|
|
2574
3274
|
this.requestBridge.cleanup();
|
|
@@ -2615,6 +3315,46 @@ var LimelightClient = class {
|
|
|
2615
3315
|
failRequest(requestId, error) {
|
|
2616
3316
|
this.requestBridge.failRequest(requestId, error);
|
|
2617
3317
|
}
|
|
3318
|
+
/**
|
|
3319
|
+
* Returns an Express/Connect-compatible middleware that captures incoming
|
|
3320
|
+
* HTTP requests and responses.
|
|
3321
|
+
*
|
|
3322
|
+
* Place after body-parser middleware (express.json(), etc.) for request body capture.
|
|
3323
|
+
*
|
|
3324
|
+
* @example
|
|
3325
|
+
* ```ts
|
|
3326
|
+
* app.use(express.json());
|
|
3327
|
+
* app.use(Limelight.middleware());
|
|
3328
|
+
* ```
|
|
3329
|
+
*/
|
|
3330
|
+
middleware(options) {
|
|
3331
|
+
return createHttpMiddleware(
|
|
3332
|
+
this.sendMessage.bind(this),
|
|
3333
|
+
() => this.sessionId,
|
|
3334
|
+
() => this.config,
|
|
3335
|
+
options
|
|
3336
|
+
);
|
|
3337
|
+
}
|
|
3338
|
+
/**
|
|
3339
|
+
* Wraps a Next.js Pages API route handler with request/response capture.
|
|
3340
|
+
* Works with Pages Router (`pages/api/`), not App Router (`app/api/`).
|
|
3341
|
+
*
|
|
3342
|
+
* @example
|
|
3343
|
+
* ```ts
|
|
3344
|
+
* // pages/api/users.ts
|
|
3345
|
+
* export default Limelight.withLimelight((req, res) => {
|
|
3346
|
+
* res.json({ ok: true });
|
|
3347
|
+
* });
|
|
3348
|
+
* ```
|
|
3349
|
+
*/
|
|
3350
|
+
withLimelight(handler) {
|
|
3351
|
+
const wrapper = createWithLimelight(
|
|
3352
|
+
this.sendMessage.bind(this),
|
|
3353
|
+
() => this.sessionId,
|
|
3354
|
+
() => this.config
|
|
3355
|
+
);
|
|
3356
|
+
return wrapper(handler);
|
|
3357
|
+
}
|
|
2618
3358
|
};
|
|
2619
3359
|
var Limelight = new LimelightClient();
|
|
2620
3360
|
// Annotate the CommonJS export names for ESM import in node:
|