@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.mjs
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
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 }); }
|
|
5
|
+
|
|
1
6
|
// src/types/console.ts
|
|
2
7
|
var ConsoleLevel = /* @__PURE__ */ ((ConsoleLevel2) => {
|
|
3
8
|
ConsoleLevel2["LOG"] = "log";
|
|
@@ -29,6 +34,8 @@ var NetworkType = /* @__PURE__ */ ((NetworkType2) => {
|
|
|
29
34
|
NetworkType2["FETCH"] = "fetch";
|
|
30
35
|
NetworkType2["XHR"] = "xhr";
|
|
31
36
|
NetworkType2["GRAPHQL"] = "graphql";
|
|
37
|
+
NetworkType2["INCOMING"] = "incoming";
|
|
38
|
+
NetworkType2["HTTP"] = "http";
|
|
32
39
|
return NetworkType2;
|
|
33
40
|
})(NetworkType || {});
|
|
34
41
|
var NetworkPhase = /* @__PURE__ */ ((NetworkPhase2) => {
|
|
@@ -49,17 +56,17 @@ var BodyFormat = /* @__PURE__ */ ((BodyFormat2) => {
|
|
|
49
56
|
BodyFormat2["UNSERIALIZABLE"] = "UNSERIALIZABLE";
|
|
50
57
|
return BodyFormat2;
|
|
51
58
|
})(BodyFormat || {});
|
|
52
|
-
var HttpMethod = /* @__PURE__ */ ((
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
return
|
|
59
|
+
var HttpMethod = /* @__PURE__ */ ((HttpMethod7) => {
|
|
60
|
+
HttpMethod7["GET"] = "GET";
|
|
61
|
+
HttpMethod7["POST"] = "POST";
|
|
62
|
+
HttpMethod7["PUT"] = "PUT";
|
|
63
|
+
HttpMethod7["PATCH"] = "PATCH";
|
|
64
|
+
HttpMethod7["DELETE"] = "DELETE";
|
|
65
|
+
HttpMethod7["HEAD"] = "HEAD";
|
|
66
|
+
HttpMethod7["OPTIONS"] = "OPTIONS";
|
|
67
|
+
HttpMethod7["TRACE"] = "TRACE";
|
|
68
|
+
HttpMethod7["CONNECT"] = "CONNECT";
|
|
69
|
+
return HttpMethod7;
|
|
63
70
|
})(HttpMethod || {});
|
|
64
71
|
var HttpStatusClass = /* @__PURE__ */ ((HttpStatusClass2) => {
|
|
65
72
|
HttpStatusClass2[HttpStatusClass2["INFORMATIONAL"] = 100] = "INFORMATIONAL";
|
|
@@ -249,7 +256,7 @@ var LIMELIGHT_WEB_WSS_URL = "wss://api.getlimelight.io";
|
|
|
249
256
|
var LIMELIGHT_DESKTOP_WSS_URL = "ws://localhost:8484";
|
|
250
257
|
var LIMELIGHT_MCP_WS_URL = "ws://localhost:9229";
|
|
251
258
|
var WS_PATH = "/limelight";
|
|
252
|
-
var SDK_VERSION = true ? "0.
|
|
259
|
+
var SDK_VERSION = true ? "0.7.8" : "test-version";
|
|
253
260
|
var RENDER_THRESHOLDS = {
|
|
254
261
|
HOT_VELOCITY: 5,
|
|
255
262
|
HIGH_RENDER_COUNT: 50,
|
|
@@ -263,6 +270,16 @@ var RENDER_THRESHOLDS = {
|
|
|
263
270
|
TOP_PROPS_TO_REPORT: 5
|
|
264
271
|
// Only report top N changed props
|
|
265
272
|
};
|
|
273
|
+
var BINARY_CONTENT_TYPES = [
|
|
274
|
+
"image/",
|
|
275
|
+
"audio/",
|
|
276
|
+
"video/",
|
|
277
|
+
"application/octet-stream",
|
|
278
|
+
"application/pdf",
|
|
279
|
+
"application/zip",
|
|
280
|
+
"application/gzip"
|
|
281
|
+
];
|
|
282
|
+
var MAX_BODY_SIZE = 1024 * 1024;
|
|
266
283
|
|
|
267
284
|
// src/helpers/safety/redactSensitiveHeaders.ts
|
|
268
285
|
var redactSensitiveHeaders = (headers) => {
|
|
@@ -503,6 +520,9 @@ var formatRequestName = (url) => {
|
|
|
503
520
|
}
|
|
504
521
|
};
|
|
505
522
|
|
|
523
|
+
// src/helpers/utils/environment.ts
|
|
524
|
+
var hasDOM = () => typeof window !== "undefined" && typeof document !== "undefined";
|
|
525
|
+
|
|
506
526
|
// src/helpers/render/generateRenderId.ts
|
|
507
527
|
var counter = 0;
|
|
508
528
|
var generateRenderId = () => {
|
|
@@ -539,6 +559,21 @@ var getCurrentTransactionId = () => {
|
|
|
539
559
|
return globalGetTransactionId?.() ?? null;
|
|
540
560
|
};
|
|
541
561
|
|
|
562
|
+
// src/helpers/http/resolveUrl.ts
|
|
563
|
+
var resolveUrl = (protocol, options) => {
|
|
564
|
+
const host = options.hostname || options.host || "localhost";
|
|
565
|
+
const port = options.port ? `:${options.port}` : "";
|
|
566
|
+
const path = options.path || "/";
|
|
567
|
+
return `${protocol}//${host}${port}${path}`;
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
// src/helpers/http/isBinaryContentType.ts
|
|
571
|
+
var isBinaryContentType = (contentType) => {
|
|
572
|
+
return BINARY_CONTENT_TYPES.some(
|
|
573
|
+
(type) => contentType.toLowerCase().includes(type)
|
|
574
|
+
);
|
|
575
|
+
};
|
|
576
|
+
|
|
542
577
|
// src/limelight/interceptors/ConsoleInterceptor.ts
|
|
543
578
|
var ConsoleInterceptor = class {
|
|
544
579
|
constructor(sendMessage, getSessionId) {
|
|
@@ -668,13 +703,31 @@ var generateRequestId = () => {
|
|
|
668
703
|
return `req-${Date.now()}-${Math.random().toString(36).substring(7)}`;
|
|
669
704
|
};
|
|
670
705
|
|
|
706
|
+
// src/limelight/context/traceContext.ts
|
|
707
|
+
var _resolved = false;
|
|
708
|
+
var _traceContext;
|
|
709
|
+
var getTraceContext = () => {
|
|
710
|
+
if (!_resolved) {
|
|
711
|
+
_resolved = true;
|
|
712
|
+
try {
|
|
713
|
+
const _require = globalThis["require"];
|
|
714
|
+
const { AsyncLocalStorage } = _require("node:async_hooks");
|
|
715
|
+
_traceContext = new AsyncLocalStorage();
|
|
716
|
+
} catch {
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
return _traceContext;
|
|
720
|
+
};
|
|
721
|
+
|
|
671
722
|
// src/limelight/interceptors/NetworkInterceptor.ts
|
|
672
723
|
var NetworkInterceptor = class {
|
|
673
724
|
constructor(sendMessage, getSessionId) {
|
|
674
725
|
this.sendMessage = sendMessage;
|
|
675
726
|
this.getSessionId = getSessionId;
|
|
676
727
|
this.globalObject = detectGlobalObject();
|
|
677
|
-
|
|
728
|
+
if (typeof this.globalObject.fetch === "function") {
|
|
729
|
+
this.originalFetch = this.globalObject.fetch.bind(this.globalObject);
|
|
730
|
+
}
|
|
678
731
|
}
|
|
679
732
|
originalFetch;
|
|
680
733
|
config = null;
|
|
@@ -694,9 +747,18 @@ var NetworkInterceptor = class {
|
|
|
694
747
|
}
|
|
695
748
|
return;
|
|
696
749
|
}
|
|
750
|
+
if (!this.originalFetch) {
|
|
751
|
+
if (config?.enableInternalLogging) {
|
|
752
|
+
console.warn(
|
|
753
|
+
"[Limelight] fetch is not available in this environment, skipping network interception"
|
|
754
|
+
);
|
|
755
|
+
}
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
697
758
|
this.isSetup = true;
|
|
698
759
|
this.config = config;
|
|
699
760
|
const self2 = this;
|
|
761
|
+
const originalFetch = this.originalFetch;
|
|
700
762
|
this.globalObject.fetch = async function(input, init = {}) {
|
|
701
763
|
const requestId = generateRequestId();
|
|
702
764
|
const startTime = Date.now();
|
|
@@ -714,6 +776,12 @@ var NetworkInterceptor = class {
|
|
|
714
776
|
});
|
|
715
777
|
}
|
|
716
778
|
headers["x-limelight-intercepted"] = "fetch";
|
|
779
|
+
const traceHeaderName = self2.config?.traceHeaderName ?? "x-limelight-trace-id";
|
|
780
|
+
if (!headers[traceHeaderName]) {
|
|
781
|
+
const existingTraceId = getTraceContext()?.getStore()?.traceId;
|
|
782
|
+
headers[traceHeaderName] = existingTraceId || generateRequestId();
|
|
783
|
+
}
|
|
784
|
+
const traceId = headers[traceHeaderName];
|
|
717
785
|
modifiedInit.headers = new Headers(headers);
|
|
718
786
|
let requestBodyToSerialize = init.body;
|
|
719
787
|
if (input instanceof Request && !requestBodyToSerialize) {
|
|
@@ -747,6 +815,7 @@ var NetworkInterceptor = class {
|
|
|
747
815
|
}
|
|
748
816
|
let requestEvent = {
|
|
749
817
|
id: requestId,
|
|
818
|
+
traceId,
|
|
750
819
|
sessionId: self2.getSessionId(),
|
|
751
820
|
timestamp: startTime,
|
|
752
821
|
phase: "REQUEST" /* REQUEST */,
|
|
@@ -763,17 +832,17 @@ var NetworkInterceptor = class {
|
|
|
763
832
|
if (self2.config?.beforeSend) {
|
|
764
833
|
const modifiedEvent = self2.config.beforeSend(requestEvent);
|
|
765
834
|
if (!modifiedEvent) {
|
|
766
|
-
return
|
|
835
|
+
return originalFetch(input, modifiedInit);
|
|
767
836
|
}
|
|
768
837
|
if (modifiedEvent.phase !== "REQUEST" /* REQUEST */) {
|
|
769
838
|
console.error("[Limelight] beforeSend must return same event type");
|
|
770
|
-
return
|
|
839
|
+
return originalFetch(input, modifiedInit);
|
|
771
840
|
}
|
|
772
841
|
requestEvent = modifiedEvent;
|
|
773
842
|
}
|
|
774
843
|
self2.sendMessage(requestEvent);
|
|
775
844
|
try {
|
|
776
|
-
const response = await
|
|
845
|
+
const response = await originalFetch(input, modifiedInit);
|
|
777
846
|
const clone = response.clone();
|
|
778
847
|
const endTime = Date.now();
|
|
779
848
|
const duration = endTime - startTime;
|
|
@@ -793,6 +862,7 @@ var NetworkInterceptor = class {
|
|
|
793
862
|
);
|
|
794
863
|
let responseEvent = {
|
|
795
864
|
id: requestId,
|
|
865
|
+
traceId,
|
|
796
866
|
sessionId: self2.getSessionId(),
|
|
797
867
|
timestamp: endTime,
|
|
798
868
|
phase: "RESPONSE" /* RESPONSE */,
|
|
@@ -825,6 +895,7 @@ var NetworkInterceptor = class {
|
|
|
825
895
|
const errorStack = err instanceof Error ? err.stack : void 0;
|
|
826
896
|
let errorEvent = {
|
|
827
897
|
id: requestId,
|
|
898
|
+
traceId,
|
|
828
899
|
sessionId: self2.getSessionId(),
|
|
829
900
|
timestamp: Date.now(),
|
|
830
901
|
phase: isAbort ? "ABORT" /* ABORT */ : "ERROR" /* ERROR */,
|
|
@@ -855,7 +926,9 @@ var NetworkInterceptor = class {
|
|
|
855
926
|
return;
|
|
856
927
|
}
|
|
857
928
|
this.isSetup = false;
|
|
858
|
-
|
|
929
|
+
if (this.originalFetch) {
|
|
930
|
+
this.globalObject.fetch = this.originalFetch;
|
|
931
|
+
}
|
|
859
932
|
}
|
|
860
933
|
};
|
|
861
934
|
|
|
@@ -864,9 +937,11 @@ var XHRInterceptor = class {
|
|
|
864
937
|
constructor(sendMessage, getSessionId) {
|
|
865
938
|
this.sendMessage = sendMessage;
|
|
866
939
|
this.getSessionId = getSessionId;
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
940
|
+
if (typeof XMLHttpRequest !== "undefined") {
|
|
941
|
+
this.originalXHROpen = XMLHttpRequest.prototype.open;
|
|
942
|
+
this.originalXHRSend = XMLHttpRequest.prototype.send;
|
|
943
|
+
this.originalXHRSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
|
|
944
|
+
}
|
|
870
945
|
}
|
|
871
946
|
originalXHROpen;
|
|
872
947
|
originalXHRSend;
|
|
@@ -881,6 +956,9 @@ var XHRInterceptor = class {
|
|
|
881
956
|
* @returns {void}
|
|
882
957
|
*/
|
|
883
958
|
setup(config) {
|
|
959
|
+
if (typeof XMLHttpRequest === "undefined") {
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
884
962
|
if (this.isSetup) {
|
|
885
963
|
if (this.config?.enableInternalLogging) {
|
|
886
964
|
console.warn("[Limelight] XHR interceptor already set up");
|
|
@@ -919,12 +997,24 @@ var XHRInterceptor = class {
|
|
|
919
997
|
return self2.originalXHRSend.apply(this, arguments);
|
|
920
998
|
}
|
|
921
999
|
if (data) {
|
|
1000
|
+
const traceHeaderName = self2.config?.traceHeaderName ?? "x-limelight-trace-id";
|
|
1001
|
+
if (!data.headers[traceHeaderName]) {
|
|
1002
|
+
data.traceId = generateRequestId();
|
|
1003
|
+
self2.originalXHRSetRequestHeader.call(
|
|
1004
|
+
this,
|
|
1005
|
+
traceHeaderName,
|
|
1006
|
+
data.traceId
|
|
1007
|
+
);
|
|
1008
|
+
} else {
|
|
1009
|
+
data.traceId = data.headers[traceHeaderName];
|
|
1010
|
+
}
|
|
922
1011
|
const requestBody = serializeBody(
|
|
923
1012
|
body,
|
|
924
1013
|
self2.config?.disableBodyCapture
|
|
925
1014
|
);
|
|
926
1015
|
let requestEvent = {
|
|
927
1016
|
id: data.id,
|
|
1017
|
+
traceId: data.traceId,
|
|
928
1018
|
sessionId: self2.getSessionId(),
|
|
929
1019
|
timestamp: data.startTime,
|
|
930
1020
|
phase: "REQUEST" /* REQUEST */,
|
|
@@ -984,6 +1074,7 @@ var XHRInterceptor = class {
|
|
|
984
1074
|
);
|
|
985
1075
|
let responseEvent = {
|
|
986
1076
|
id: data.id,
|
|
1077
|
+
traceId: data.traceId,
|
|
987
1078
|
sessionId: self2.getSessionId(),
|
|
988
1079
|
timestamp: endTime,
|
|
989
1080
|
phase: "RESPONSE" /* RESPONSE */,
|
|
@@ -1018,6 +1109,7 @@ var XHRInterceptor = class {
|
|
|
1018
1109
|
responseSent = true;
|
|
1019
1110
|
let errorEvent = {
|
|
1020
1111
|
id: data.id,
|
|
1112
|
+
traceId: data.traceId,
|
|
1021
1113
|
sessionId: self2.getSessionId(),
|
|
1022
1114
|
timestamp: Date.now(),
|
|
1023
1115
|
phase,
|
|
@@ -1094,17 +1186,370 @@ var XHRInterceptor = class {
|
|
|
1094
1186
|
* Restores original XMLHttpRequest methods and removes all interception.
|
|
1095
1187
|
* @returns {void}
|
|
1096
1188
|
*/
|
|
1189
|
+
cleanup() {
|
|
1190
|
+
if (!this.isSetup) {
|
|
1191
|
+
return;
|
|
1192
|
+
}
|
|
1193
|
+
this.isSetup = false;
|
|
1194
|
+
if (typeof XMLHttpRequest !== "undefined") {
|
|
1195
|
+
XMLHttpRequest.prototype.open = this.originalXHROpen;
|
|
1196
|
+
XMLHttpRequest.prototype.send = this.originalXHRSend;
|
|
1197
|
+
XMLHttpRequest.prototype.setRequestHeader = this.originalXHRSetRequestHeader;
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
};
|
|
1201
|
+
|
|
1202
|
+
// src/limelight/interceptors/HttpInterceptor.ts
|
|
1203
|
+
var HttpInterceptor = class {
|
|
1204
|
+
constructor(sendMessage, getSessionId) {
|
|
1205
|
+
this.sendMessage = sendMessage;
|
|
1206
|
+
this.getSessionId = getSessionId;
|
|
1207
|
+
try {
|
|
1208
|
+
const _require = globalThis["require"];
|
|
1209
|
+
this.httpModule = _require("http");
|
|
1210
|
+
this.httpsModule = _require("https");
|
|
1211
|
+
this.originalHttpRequest = this.httpModule.request;
|
|
1212
|
+
this.originalHttpGet = this.httpModule.get;
|
|
1213
|
+
this.originalHttpsRequest = this.httpsModule.request;
|
|
1214
|
+
this.originalHttpsGet = this.httpsModule.get;
|
|
1215
|
+
} catch {
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
originalHttpRequest = null;
|
|
1219
|
+
originalHttpGet = null;
|
|
1220
|
+
originalHttpsRequest = null;
|
|
1221
|
+
originalHttpsGet = null;
|
|
1222
|
+
httpModule = null;
|
|
1223
|
+
httpsModule = null;
|
|
1224
|
+
config = null;
|
|
1225
|
+
isSetup = false;
|
|
1226
|
+
setup(config) {
|
|
1227
|
+
if (this.isSetup) {
|
|
1228
|
+
if (this.config?.enableInternalLogging) {
|
|
1229
|
+
console.warn("[Limelight] HTTP interceptor already set up");
|
|
1230
|
+
}
|
|
1231
|
+
return;
|
|
1232
|
+
}
|
|
1233
|
+
if (!this.httpModule || !this.httpsModule) {
|
|
1234
|
+
if (config?.enableInternalLogging) {
|
|
1235
|
+
console.warn(
|
|
1236
|
+
"[Limelight] Node http module not available, skipping HTTP interception"
|
|
1237
|
+
);
|
|
1238
|
+
}
|
|
1239
|
+
return;
|
|
1240
|
+
}
|
|
1241
|
+
this.isSetup = true;
|
|
1242
|
+
this.config = config;
|
|
1243
|
+
const self2 = this;
|
|
1244
|
+
const httpMod = this.httpModule;
|
|
1245
|
+
const httpsMod = this.httpsModule;
|
|
1246
|
+
httpMod.request = (...args) => {
|
|
1247
|
+
return self2.interceptRequest(
|
|
1248
|
+
"http:",
|
|
1249
|
+
self2.originalHttpRequest,
|
|
1250
|
+
httpMod,
|
|
1251
|
+
args
|
|
1252
|
+
);
|
|
1253
|
+
};
|
|
1254
|
+
httpMod.get = (...args) => {
|
|
1255
|
+
const req = self2.interceptRequest(
|
|
1256
|
+
"http:",
|
|
1257
|
+
self2.originalHttpRequest,
|
|
1258
|
+
httpMod,
|
|
1259
|
+
args
|
|
1260
|
+
);
|
|
1261
|
+
req.end();
|
|
1262
|
+
return req;
|
|
1263
|
+
};
|
|
1264
|
+
httpsMod.request = (...args) => {
|
|
1265
|
+
return self2.interceptRequest(
|
|
1266
|
+
"https:",
|
|
1267
|
+
self2.originalHttpsRequest,
|
|
1268
|
+
httpsMod,
|
|
1269
|
+
args
|
|
1270
|
+
);
|
|
1271
|
+
};
|
|
1272
|
+
httpsMod.get = (...args) => {
|
|
1273
|
+
const req = self2.interceptRequest(
|
|
1274
|
+
"https:",
|
|
1275
|
+
self2.originalHttpsRequest,
|
|
1276
|
+
httpsMod,
|
|
1277
|
+
args
|
|
1278
|
+
);
|
|
1279
|
+
req.end();
|
|
1280
|
+
return req;
|
|
1281
|
+
};
|
|
1282
|
+
}
|
|
1283
|
+
interceptRequest(protocol, originalMethod, module, args) {
|
|
1284
|
+
let url;
|
|
1285
|
+
let options;
|
|
1286
|
+
let callback;
|
|
1287
|
+
if (typeof args[0] === "string" || args[0] instanceof URL) {
|
|
1288
|
+
url = args[0];
|
|
1289
|
+
if (typeof args[1] === "function") {
|
|
1290
|
+
options = {};
|
|
1291
|
+
callback = args[1];
|
|
1292
|
+
} else {
|
|
1293
|
+
options = args[1] || {};
|
|
1294
|
+
callback = args[2];
|
|
1295
|
+
}
|
|
1296
|
+
} else {
|
|
1297
|
+
options = args[0] || {};
|
|
1298
|
+
callback = args[1];
|
|
1299
|
+
}
|
|
1300
|
+
let resolvedUrl;
|
|
1301
|
+
if (url) {
|
|
1302
|
+
resolvedUrl = url.toString();
|
|
1303
|
+
} else {
|
|
1304
|
+
resolvedUrl = resolveUrl(protocol, options);
|
|
1305
|
+
}
|
|
1306
|
+
const limelightServerUrl = this.config?.serverUrl || "";
|
|
1307
|
+
if (limelightServerUrl && resolvedUrl.includes(limelightServerUrl)) {
|
|
1308
|
+
return originalMethod.apply(module, args);
|
|
1309
|
+
}
|
|
1310
|
+
const self2 = this;
|
|
1311
|
+
const requestId = generateRequestId();
|
|
1312
|
+
const startTime = Date.now();
|
|
1313
|
+
const initiator = getInitiator();
|
|
1314
|
+
const traceHeaderName = this.config?.traceHeaderName ?? "x-limelight-trace-id";
|
|
1315
|
+
const existingTraceId = getTraceContext()?.getStore()?.traceId;
|
|
1316
|
+
const traceId = existingTraceId || generateRequestId();
|
|
1317
|
+
if (!options.headers) {
|
|
1318
|
+
options.headers = {};
|
|
1319
|
+
}
|
|
1320
|
+
options.headers[traceHeaderName] = traceId;
|
|
1321
|
+
const method = (options.method || "GET").toUpperCase();
|
|
1322
|
+
let patchedArgs;
|
|
1323
|
+
if (url) {
|
|
1324
|
+
patchedArgs = callback ? [url, options, callback] : [url, options];
|
|
1325
|
+
} else {
|
|
1326
|
+
patchedArgs = callback ? [options, callback] : [options];
|
|
1327
|
+
}
|
|
1328
|
+
const req = originalMethod.apply(
|
|
1329
|
+
module,
|
|
1330
|
+
patchedArgs
|
|
1331
|
+
);
|
|
1332
|
+
const bodyChunks = [];
|
|
1333
|
+
let totalBodySize = 0;
|
|
1334
|
+
let requestEventSent = false;
|
|
1335
|
+
const originalWrite = req.write.bind(req);
|
|
1336
|
+
const originalEnd = req.end.bind(req);
|
|
1337
|
+
req.write = (chunk, encodingOrCallback, callback2) => {
|
|
1338
|
+
if (chunk && totalBodySize < MAX_BODY_SIZE) {
|
|
1339
|
+
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
1340
|
+
bodyChunks.push(buf);
|
|
1341
|
+
totalBodySize += buf.length;
|
|
1342
|
+
}
|
|
1343
|
+
return originalWrite(chunk, encodingOrCallback, callback2);
|
|
1344
|
+
};
|
|
1345
|
+
req.end = (chunk, encodingOrCallback, callback2) => {
|
|
1346
|
+
if (chunk && typeof chunk !== "function" && totalBodySize < MAX_BODY_SIZE) {
|
|
1347
|
+
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
1348
|
+
bodyChunks.push(buf);
|
|
1349
|
+
totalBodySize += buf.length;
|
|
1350
|
+
}
|
|
1351
|
+
if (!requestEventSent) {
|
|
1352
|
+
requestEventSent = true;
|
|
1353
|
+
const fullBody = bodyChunks.length > 0 ? Buffer.concat(bodyChunks).toString("utf-8") : void 0;
|
|
1354
|
+
const headers = {};
|
|
1355
|
+
const rawHeaders = req.getHeaders();
|
|
1356
|
+
for (const [key, value] of Object.entries(rawHeaders)) {
|
|
1357
|
+
if (value !== void 0) {
|
|
1358
|
+
headers[key.toLowerCase()] = Array.isArray(value) ? value.join(", ") : String(value);
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
const requestBody = serializeBody(
|
|
1362
|
+
fullBody,
|
|
1363
|
+
self2.config?.disableBodyCapture
|
|
1364
|
+
);
|
|
1365
|
+
let requestEvent = {
|
|
1366
|
+
id: requestId,
|
|
1367
|
+
traceId,
|
|
1368
|
+
sessionId: self2.getSessionId(),
|
|
1369
|
+
timestamp: startTime,
|
|
1370
|
+
phase: "REQUEST" /* REQUEST */,
|
|
1371
|
+
networkType: "http" /* HTTP */,
|
|
1372
|
+
url: resolvedUrl,
|
|
1373
|
+
method,
|
|
1374
|
+
headers: redactSensitiveHeaders(headers),
|
|
1375
|
+
body: requestBody,
|
|
1376
|
+
name: formatRequestName(resolvedUrl),
|
|
1377
|
+
initiator,
|
|
1378
|
+
requestSize: requestBody?.size ?? 0
|
|
1379
|
+
};
|
|
1380
|
+
if (self2.config?.beforeSend) {
|
|
1381
|
+
const modifiedEvent = self2.config.beforeSend(requestEvent);
|
|
1382
|
+
if (!modifiedEvent) {
|
|
1383
|
+
return originalEnd(
|
|
1384
|
+
chunk,
|
|
1385
|
+
encodingOrCallback,
|
|
1386
|
+
callback2
|
|
1387
|
+
);
|
|
1388
|
+
}
|
|
1389
|
+
if (modifiedEvent.phase !== "REQUEST" /* REQUEST */) {
|
|
1390
|
+
console.error("[Limelight] beforeSend must return same event type");
|
|
1391
|
+
return originalEnd(
|
|
1392
|
+
chunk,
|
|
1393
|
+
encodingOrCallback,
|
|
1394
|
+
callback2
|
|
1395
|
+
);
|
|
1396
|
+
}
|
|
1397
|
+
requestEvent = modifiedEvent;
|
|
1398
|
+
}
|
|
1399
|
+
self2.sendMessage(requestEvent);
|
|
1400
|
+
}
|
|
1401
|
+
return originalEnd(chunk, encodingOrCallback, callback2);
|
|
1402
|
+
};
|
|
1403
|
+
let responseSent = false;
|
|
1404
|
+
req.on("response", (res) => {
|
|
1405
|
+
const responseChunks = [];
|
|
1406
|
+
let responseSize = 0;
|
|
1407
|
+
res.on("data", (chunk) => {
|
|
1408
|
+
if (responseSize < MAX_BODY_SIZE) {
|
|
1409
|
+
responseChunks.push(chunk);
|
|
1410
|
+
responseSize += chunk.length;
|
|
1411
|
+
}
|
|
1412
|
+
});
|
|
1413
|
+
res.on("end", () => {
|
|
1414
|
+
if (responseSent) return;
|
|
1415
|
+
responseSent = true;
|
|
1416
|
+
const endTime = Date.now();
|
|
1417
|
+
const duration = endTime - startTime;
|
|
1418
|
+
const responseHeaders = {};
|
|
1419
|
+
if (res.headers) {
|
|
1420
|
+
for (const [key, value] of Object.entries(res.headers)) {
|
|
1421
|
+
if (value) {
|
|
1422
|
+
responseHeaders[key.toLowerCase()] = Array.isArray(value) ? value.join(", ") : value;
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
const contentType = responseHeaders["content-type"] || "";
|
|
1427
|
+
let responseBodyStr;
|
|
1428
|
+
if (responseChunks.length > 0) {
|
|
1429
|
+
if (isBinaryContentType(contentType)) {
|
|
1430
|
+
responseBodyStr = `[Binary Data: ${contentType}]`;
|
|
1431
|
+
} else {
|
|
1432
|
+
const full = Buffer.concat(responseChunks);
|
|
1433
|
+
responseBodyStr = full.length > MAX_BODY_SIZE ? full.toString("utf-8", 0, MAX_BODY_SIZE) + "...[truncated]" : full.toString("utf-8");
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
const responseBody = serializeBody(
|
|
1437
|
+
responseBodyStr,
|
|
1438
|
+
self2.config?.disableBodyCapture
|
|
1439
|
+
);
|
|
1440
|
+
const statusCode = res.statusCode ?? 0;
|
|
1441
|
+
let responseEvent = {
|
|
1442
|
+
id: requestId,
|
|
1443
|
+
traceId,
|
|
1444
|
+
sessionId: self2.getSessionId(),
|
|
1445
|
+
timestamp: endTime,
|
|
1446
|
+
phase: "RESPONSE" /* RESPONSE */,
|
|
1447
|
+
networkType: "http" /* HTTP */,
|
|
1448
|
+
status: statusCode,
|
|
1449
|
+
statusText: res.statusMessage || "",
|
|
1450
|
+
headers: redactSensitiveHeaders(responseHeaders),
|
|
1451
|
+
body: responseBody,
|
|
1452
|
+
duration,
|
|
1453
|
+
responseSize: responseBody?.size ?? 0,
|
|
1454
|
+
redirected: false,
|
|
1455
|
+
ok: statusCode >= 200 && statusCode < 300
|
|
1456
|
+
};
|
|
1457
|
+
if (self2.config?.beforeSend) {
|
|
1458
|
+
const modifiedEvent = self2.config.beforeSend(responseEvent);
|
|
1459
|
+
if (!modifiedEvent) return;
|
|
1460
|
+
if (modifiedEvent.phase !== "RESPONSE" /* RESPONSE */) {
|
|
1461
|
+
console.error("[Limelight] beforeSend must return same event type");
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1464
|
+
responseEvent = modifiedEvent;
|
|
1465
|
+
}
|
|
1466
|
+
self2.sendMessage(responseEvent);
|
|
1467
|
+
});
|
|
1468
|
+
});
|
|
1469
|
+
req.on("error", (err) => {
|
|
1470
|
+
if (responseSent) return;
|
|
1471
|
+
responseSent = true;
|
|
1472
|
+
let errorEvent = {
|
|
1473
|
+
id: requestId,
|
|
1474
|
+
traceId,
|
|
1475
|
+
sessionId: self2.getSessionId(),
|
|
1476
|
+
timestamp: Date.now(),
|
|
1477
|
+
phase: "ERROR" /* ERROR */,
|
|
1478
|
+
networkType: "http" /* HTTP */,
|
|
1479
|
+
errorMessage: err.message || "Network request failed",
|
|
1480
|
+
stack: err.stack
|
|
1481
|
+
};
|
|
1482
|
+
if (self2.config?.beforeSend) {
|
|
1483
|
+
const modifiedEvent = self2.config.beforeSend(errorEvent);
|
|
1484
|
+
if (modifiedEvent && (modifiedEvent.phase === "ERROR" /* ERROR */ || modifiedEvent.phase === "ABORT" /* ABORT */)) {
|
|
1485
|
+
errorEvent = modifiedEvent;
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
self2.sendMessage(errorEvent);
|
|
1489
|
+
});
|
|
1490
|
+
req.on("timeout", () => {
|
|
1491
|
+
if (responseSent) return;
|
|
1492
|
+
responseSent = true;
|
|
1493
|
+
let errorEvent = {
|
|
1494
|
+
id: requestId,
|
|
1495
|
+
traceId,
|
|
1496
|
+
sessionId: self2.getSessionId(),
|
|
1497
|
+
timestamp: Date.now(),
|
|
1498
|
+
phase: "ERROR" /* ERROR */,
|
|
1499
|
+
networkType: "http" /* HTTP */,
|
|
1500
|
+
errorMessage: "Request timeout"
|
|
1501
|
+
};
|
|
1502
|
+
if (self2.config?.beforeSend) {
|
|
1503
|
+
const modifiedEvent = self2.config.beforeSend(errorEvent);
|
|
1504
|
+
if (modifiedEvent && (modifiedEvent.phase === "ERROR" /* ERROR */ || modifiedEvent.phase === "ABORT" /* ABORT */)) {
|
|
1505
|
+
errorEvent = modifiedEvent;
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
self2.sendMessage(errorEvent);
|
|
1509
|
+
});
|
|
1510
|
+
req.on("close", () => {
|
|
1511
|
+
if (responseSent) return;
|
|
1512
|
+
if (!req.destroyed) return;
|
|
1513
|
+
responseSent = true;
|
|
1514
|
+
let errorEvent = {
|
|
1515
|
+
id: requestId,
|
|
1516
|
+
traceId,
|
|
1517
|
+
sessionId: self2.getSessionId(),
|
|
1518
|
+
timestamp: Date.now(),
|
|
1519
|
+
phase: "ABORT" /* ABORT */,
|
|
1520
|
+
networkType: "http" /* HTTP */,
|
|
1521
|
+
errorMessage: "Request aborted"
|
|
1522
|
+
};
|
|
1523
|
+
if (self2.config?.beforeSend) {
|
|
1524
|
+
const modifiedEvent = self2.config.beforeSend(errorEvent);
|
|
1525
|
+
if (modifiedEvent && (modifiedEvent.phase === "ERROR" /* ERROR */ || modifiedEvent.phase === "ABORT" /* ABORT */)) {
|
|
1526
|
+
errorEvent = modifiedEvent;
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
self2.sendMessage(errorEvent);
|
|
1530
|
+
});
|
|
1531
|
+
return req;
|
|
1532
|
+
}
|
|
1097
1533
|
cleanup() {
|
|
1098
1534
|
if (!this.isSetup) {
|
|
1099
1535
|
if (this.config?.enableInternalLogging) {
|
|
1100
|
-
console.warn("[Limelight]
|
|
1536
|
+
console.warn("[Limelight] HTTP interceptor not set up");
|
|
1101
1537
|
}
|
|
1102
1538
|
return;
|
|
1103
1539
|
}
|
|
1104
1540
|
this.isSetup = false;
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1541
|
+
if (this.httpModule && this.originalHttpRequest) {
|
|
1542
|
+
this.httpModule.request = this.originalHttpRequest;
|
|
1543
|
+
}
|
|
1544
|
+
if (this.httpModule && this.originalHttpGet) {
|
|
1545
|
+
this.httpModule.get = this.originalHttpGet;
|
|
1546
|
+
}
|
|
1547
|
+
if (this.httpsModule && this.originalHttpsRequest) {
|
|
1548
|
+
this.httpsModule.request = this.originalHttpsRequest;
|
|
1549
|
+
}
|
|
1550
|
+
if (this.httpsModule && this.originalHttpsGet) {
|
|
1551
|
+
this.httpsModule.get = this.originalHttpsGet;
|
|
1552
|
+
}
|
|
1108
1553
|
}
|
|
1109
1554
|
};
|
|
1110
1555
|
|
|
@@ -1757,6 +2202,83 @@ var RenderInterceptor = class {
|
|
|
1757
2202
|
}
|
|
1758
2203
|
};
|
|
1759
2204
|
|
|
2205
|
+
// src/limelight/interceptors/ErrorInterceptor.ts
|
|
2206
|
+
var ErrorInterceptor = class {
|
|
2207
|
+
constructor(sendMessage, getSessionId) {
|
|
2208
|
+
this.sendMessage = sendMessage;
|
|
2209
|
+
this.getSessionId = getSessionId;
|
|
2210
|
+
}
|
|
2211
|
+
isSetup = false;
|
|
2212
|
+
config = null;
|
|
2213
|
+
counter = 0;
|
|
2214
|
+
uncaughtExceptionHandler = null;
|
|
2215
|
+
unhandledRejectionHandler = null;
|
|
2216
|
+
setup(config) {
|
|
2217
|
+
if (this.isSetup) {
|
|
2218
|
+
if (this.config?.enableInternalLogging) {
|
|
2219
|
+
console.warn("[Limelight] Error interceptor already set up");
|
|
2220
|
+
}
|
|
2221
|
+
return;
|
|
2222
|
+
}
|
|
2223
|
+
if (typeof process === "undefined" || !process.on) {
|
|
2224
|
+
return;
|
|
2225
|
+
}
|
|
2226
|
+
this.isSetup = true;
|
|
2227
|
+
this.config = config;
|
|
2228
|
+
this.uncaughtExceptionHandler = (error) => {
|
|
2229
|
+
this.sendErrorEvent(error, "uncaughtException");
|
|
2230
|
+
setTimeout(() => {
|
|
2231
|
+
process.exit(1);
|
|
2232
|
+
}, 200);
|
|
2233
|
+
};
|
|
2234
|
+
this.unhandledRejectionHandler = (reason) => {
|
|
2235
|
+
const error = reason instanceof Error ? reason : new Error(String(reason));
|
|
2236
|
+
this.sendErrorEvent(error, "unhandledRejection");
|
|
2237
|
+
};
|
|
2238
|
+
process.on("uncaughtException", this.uncaughtExceptionHandler);
|
|
2239
|
+
process.on("unhandledRejection", this.unhandledRejectionHandler);
|
|
2240
|
+
}
|
|
2241
|
+
cleanup() {
|
|
2242
|
+
if (!this.isSetup) return;
|
|
2243
|
+
this.isSetup = false;
|
|
2244
|
+
if (this.uncaughtExceptionHandler) {
|
|
2245
|
+
process.removeListener(
|
|
2246
|
+
"uncaughtException",
|
|
2247
|
+
this.uncaughtExceptionHandler
|
|
2248
|
+
);
|
|
2249
|
+
this.uncaughtExceptionHandler = null;
|
|
2250
|
+
}
|
|
2251
|
+
if (this.unhandledRejectionHandler) {
|
|
2252
|
+
process.removeListener(
|
|
2253
|
+
"unhandledRejection",
|
|
2254
|
+
this.unhandledRejectionHandler
|
|
2255
|
+
);
|
|
2256
|
+
this.unhandledRejectionHandler = null;
|
|
2257
|
+
}
|
|
2258
|
+
this.config = null;
|
|
2259
|
+
}
|
|
2260
|
+
sendErrorEvent(error, source) {
|
|
2261
|
+
const sessionId = this.getSessionId();
|
|
2262
|
+
const event = {
|
|
2263
|
+
id: `${sessionId}-${Date.now()}-${this.counter++}`,
|
|
2264
|
+
phase: "CONSOLE",
|
|
2265
|
+
type: "CONSOLE" /* CONSOLE */,
|
|
2266
|
+
level: "error" /* ERROR */,
|
|
2267
|
+
timestamp: Date.now(),
|
|
2268
|
+
sessionId,
|
|
2269
|
+
source: "app" /* APP */,
|
|
2270
|
+
consoleType: "exception" /* EXCEPTION */,
|
|
2271
|
+
args: [safeStringify(`[${source}] ${error.message}`)],
|
|
2272
|
+
stackTrace: error.stack
|
|
2273
|
+
};
|
|
2274
|
+
if (this.config?.beforeSend) {
|
|
2275
|
+
const modified = this.config.beforeSend(event);
|
|
2276
|
+
if (!modified) return;
|
|
2277
|
+
}
|
|
2278
|
+
this.sendMessage(event);
|
|
2279
|
+
}
|
|
2280
|
+
};
|
|
2281
|
+
|
|
1760
2282
|
// src/limelight/interceptors/StateInterceptor.ts
|
|
1761
2283
|
var StateInterceptor = class {
|
|
1762
2284
|
sendMessage;
|
|
@@ -2240,6 +2762,153 @@ var CommandHandler = class {
|
|
|
2240
2762
|
}
|
|
2241
2763
|
};
|
|
2242
2764
|
|
|
2765
|
+
// src/limelight/middleware/httpMiddleware.ts
|
|
2766
|
+
var DEFAULT_MAX_BODY_SIZE = 64 * 1024;
|
|
2767
|
+
var captureRequest = (req, res, sendMessage, getSessionId, config, options) => {
|
|
2768
|
+
const requestId = generateRequestId();
|
|
2769
|
+
const startTime = Date.now();
|
|
2770
|
+
const maxBodySize = options?.maxBodySize ?? DEFAULT_MAX_BODY_SIZE;
|
|
2771
|
+
const traceHeaderName = config?.traceHeaderName ?? "x-limelight-trace-id";
|
|
2772
|
+
const incomingTraceId = req.headers[traceHeaderName];
|
|
2773
|
+
const traceId = incomingTraceId || generateRequestId();
|
|
2774
|
+
req.limelightTraceId = traceId;
|
|
2775
|
+
const url = req.url || "/";
|
|
2776
|
+
const method = (req.method || "GET").toUpperCase();
|
|
2777
|
+
const headers = {};
|
|
2778
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
2779
|
+
if (value) {
|
|
2780
|
+
headers[key.toLowerCase()] = Array.isArray(value) ? value.join(", ") : value;
|
|
2781
|
+
}
|
|
2782
|
+
}
|
|
2783
|
+
let requestBody = serializeBody(req.body, config?.disableBodyCapture);
|
|
2784
|
+
if (requestBody?.raw && requestBody.raw.length > maxBodySize) {
|
|
2785
|
+
requestBody = {
|
|
2786
|
+
...requestBody,
|
|
2787
|
+
raw: requestBody.raw.slice(0, maxBodySize) + "...[truncated]",
|
|
2788
|
+
size: requestBody.size
|
|
2789
|
+
};
|
|
2790
|
+
}
|
|
2791
|
+
let requestEvent = {
|
|
2792
|
+
id: requestId,
|
|
2793
|
+
traceId,
|
|
2794
|
+
sessionId: getSessionId(),
|
|
2795
|
+
timestamp: startTime,
|
|
2796
|
+
phase: "REQUEST" /* REQUEST */,
|
|
2797
|
+
networkType: "incoming" /* INCOMING */,
|
|
2798
|
+
url,
|
|
2799
|
+
method,
|
|
2800
|
+
headers: redactSensitiveHeaders(headers),
|
|
2801
|
+
body: requestBody,
|
|
2802
|
+
name: url.split("?")[0]?.split("/").filter(Boolean).pop() ?? "/",
|
|
2803
|
+
initiator: "incoming",
|
|
2804
|
+
requestSize: requestBody?.size ?? 0
|
|
2805
|
+
};
|
|
2806
|
+
if (config?.beforeSend) {
|
|
2807
|
+
const modified = config.beforeSend(requestEvent);
|
|
2808
|
+
if (!modified) return;
|
|
2809
|
+
if (modified.phase !== "REQUEST" /* REQUEST */) {
|
|
2810
|
+
console.error("[Limelight] beforeSend must return same event type");
|
|
2811
|
+
return;
|
|
2812
|
+
}
|
|
2813
|
+
requestEvent = modified;
|
|
2814
|
+
}
|
|
2815
|
+
sendMessage(requestEvent);
|
|
2816
|
+
const chunks = [];
|
|
2817
|
+
let totalSize = 0;
|
|
2818
|
+
const originalWrite = res.write;
|
|
2819
|
+
const originalEnd = res.end;
|
|
2820
|
+
res.write = (chunk, ...args) => {
|
|
2821
|
+
if (chunk && typeof chunk !== "function" && totalSize < maxBodySize) {
|
|
2822
|
+
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
2823
|
+
chunks.push(buf);
|
|
2824
|
+
totalSize += buf.length;
|
|
2825
|
+
}
|
|
2826
|
+
return originalWrite.apply(res, [chunk, ...args]);
|
|
2827
|
+
};
|
|
2828
|
+
res.end = (chunk, ...args) => {
|
|
2829
|
+
if (chunk && typeof chunk !== "function" && totalSize < maxBodySize) {
|
|
2830
|
+
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
2831
|
+
chunks.push(buf);
|
|
2832
|
+
totalSize += buf.length;
|
|
2833
|
+
}
|
|
2834
|
+
return originalEnd.apply(res, [chunk, ...args]);
|
|
2835
|
+
};
|
|
2836
|
+
res.on("finish", () => {
|
|
2837
|
+
const endTime = Date.now();
|
|
2838
|
+
const duration = endTime - startTime;
|
|
2839
|
+
const responseHeaders = {};
|
|
2840
|
+
const rawHeaders = res.getHeaders();
|
|
2841
|
+
for (const [key, value] of Object.entries(rawHeaders)) {
|
|
2842
|
+
if (value) {
|
|
2843
|
+
responseHeaders[key.toLowerCase()] = Array.isArray(value) ? value.join(", ") : String(value);
|
|
2844
|
+
}
|
|
2845
|
+
}
|
|
2846
|
+
let responseBodyStr;
|
|
2847
|
+
if (chunks.length > 0 && !config?.disableBodyCapture) {
|
|
2848
|
+
const full = Buffer.concat(chunks);
|
|
2849
|
+
const fullStr = full.toString("utf-8");
|
|
2850
|
+
responseBodyStr = fullStr.length > maxBodySize ? fullStr.slice(0, maxBodySize) + "...[truncated]" : fullStr;
|
|
2851
|
+
}
|
|
2852
|
+
const responseBody = serializeBody(
|
|
2853
|
+
responseBodyStr,
|
|
2854
|
+
config?.disableBodyCapture
|
|
2855
|
+
);
|
|
2856
|
+
let responseEvent = {
|
|
2857
|
+
id: requestId,
|
|
2858
|
+
traceId,
|
|
2859
|
+
sessionId: getSessionId(),
|
|
2860
|
+
timestamp: endTime,
|
|
2861
|
+
phase: "RESPONSE" /* RESPONSE */,
|
|
2862
|
+
networkType: "incoming" /* INCOMING */,
|
|
2863
|
+
status: res.statusCode,
|
|
2864
|
+
statusText: res.statusMessage || "",
|
|
2865
|
+
headers: redactSensitiveHeaders(responseHeaders),
|
|
2866
|
+
body: responseBody,
|
|
2867
|
+
duration,
|
|
2868
|
+
responseSize: responseBody?.size ?? 0,
|
|
2869
|
+
redirected: false,
|
|
2870
|
+
ok: res.statusCode >= 200 && res.statusCode < 300
|
|
2871
|
+
};
|
|
2872
|
+
if (config?.beforeSend) {
|
|
2873
|
+
const modified = config.beforeSend(responseEvent);
|
|
2874
|
+
if (!modified) return;
|
|
2875
|
+
if (modified.phase !== "RESPONSE" /* RESPONSE */) {
|
|
2876
|
+
console.error("[Limelight] beforeSend must return same event type");
|
|
2877
|
+
return;
|
|
2878
|
+
}
|
|
2879
|
+
responseEvent = modified;
|
|
2880
|
+
}
|
|
2881
|
+
sendMessage(responseEvent);
|
|
2882
|
+
});
|
|
2883
|
+
};
|
|
2884
|
+
var createHttpMiddleware = (sendMessage, getSessionId, getConfig, options) => {
|
|
2885
|
+
return (req, res, next) => {
|
|
2886
|
+
captureRequest(req, res, sendMessage, getSessionId, getConfig(), options);
|
|
2887
|
+
const traceId = req.limelightTraceId;
|
|
2888
|
+
const ctx = getTraceContext();
|
|
2889
|
+
if (ctx && traceId) {
|
|
2890
|
+
ctx.run({ traceId }, next);
|
|
2891
|
+
} else {
|
|
2892
|
+
next();
|
|
2893
|
+
}
|
|
2894
|
+
};
|
|
2895
|
+
};
|
|
2896
|
+
|
|
2897
|
+
// src/limelight/middleware/withLimelight.ts
|
|
2898
|
+
var createWithLimelight = (sendMessage, getSessionId, getConfig, options) => {
|
|
2899
|
+
return (handler) => {
|
|
2900
|
+
return (req, res) => {
|
|
2901
|
+
captureRequest(req, res, sendMessage, getSessionId, getConfig(), options);
|
|
2902
|
+
const traceId = req.limelightTraceId;
|
|
2903
|
+
const ctx = getTraceContext();
|
|
2904
|
+
if (ctx && traceId) {
|
|
2905
|
+
return ctx.run({ traceId }, () => handler(req, res));
|
|
2906
|
+
}
|
|
2907
|
+
return handler(req, res);
|
|
2908
|
+
};
|
|
2909
|
+
};
|
|
2910
|
+
};
|
|
2911
|
+
|
|
2243
2912
|
// src/limelight/LimelightClient.ts
|
|
2244
2913
|
var LimelightClient = class {
|
|
2245
2914
|
ws = null;
|
|
@@ -2253,9 +2922,11 @@ var LimelightClient = class {
|
|
|
2253
2922
|
maxQueueSize = 100;
|
|
2254
2923
|
networkInterceptor;
|
|
2255
2924
|
xhrInterceptor;
|
|
2925
|
+
httpInterceptor;
|
|
2256
2926
|
consoleInterceptor;
|
|
2257
2927
|
renderInterceptor;
|
|
2258
2928
|
stateInterceptor;
|
|
2929
|
+
errorInterceptor;
|
|
2259
2930
|
requestBridge;
|
|
2260
2931
|
commandHandler = null;
|
|
2261
2932
|
constructor() {
|
|
@@ -2267,6 +2938,10 @@ var LimelightClient = class {
|
|
|
2267
2938
|
this.sendMessage.bind(this),
|
|
2268
2939
|
() => this.sessionId
|
|
2269
2940
|
);
|
|
2941
|
+
this.httpInterceptor = new HttpInterceptor(
|
|
2942
|
+
this.sendMessage.bind(this),
|
|
2943
|
+
() => this.sessionId
|
|
2944
|
+
);
|
|
2270
2945
|
this.consoleInterceptor = new ConsoleInterceptor(
|
|
2271
2946
|
this.sendMessage.bind(this),
|
|
2272
2947
|
() => this.sessionId
|
|
@@ -2279,6 +2954,10 @@ var LimelightClient = class {
|
|
|
2279
2954
|
this.sendMessage.bind(this),
|
|
2280
2955
|
() => this.sessionId
|
|
2281
2956
|
);
|
|
2957
|
+
this.errorInterceptor = new ErrorInterceptor(
|
|
2958
|
+
this.sendMessage.bind(this),
|
|
2959
|
+
() => this.sessionId
|
|
2960
|
+
);
|
|
2282
2961
|
this.requestBridge = new RequestBridge(
|
|
2283
2962
|
this.sendMessage.bind(this),
|
|
2284
2963
|
() => this.sessionId
|
|
@@ -2320,12 +2999,20 @@ var LimelightClient = class {
|
|
|
2320
2999
|
try {
|
|
2321
3000
|
if (this.config.enableNetworkInspector) {
|
|
2322
3001
|
this.networkInterceptor.setup(this.config);
|
|
2323
|
-
|
|
3002
|
+
if (typeof XMLHttpRequest !== "undefined") {
|
|
3003
|
+
this.xhrInterceptor.setup(this.config);
|
|
3004
|
+
}
|
|
3005
|
+
if (!hasDOM()) {
|
|
3006
|
+
this.httpInterceptor.setup(this.config);
|
|
3007
|
+
}
|
|
2324
3008
|
}
|
|
2325
3009
|
if (this.config.enableConsole) {
|
|
2326
3010
|
this.consoleInterceptor.setup(this.config);
|
|
3011
|
+
if (!hasDOM()) {
|
|
3012
|
+
this.errorInterceptor.setup(this.config);
|
|
3013
|
+
}
|
|
2327
3014
|
}
|
|
2328
|
-
if (this.config.enableRenderInspector) {
|
|
3015
|
+
if (this.config.enableRenderInspector && hasDOM()) {
|
|
2329
3016
|
this.renderInterceptor.setup(this.config);
|
|
2330
3017
|
}
|
|
2331
3018
|
if (this.config.stores && this.config.enableStateInspector) {
|
|
@@ -2353,7 +3040,7 @@ var LimelightClient = class {
|
|
|
2353
3040
|
if (!this.config?.enabled) {
|
|
2354
3041
|
return;
|
|
2355
3042
|
}
|
|
2356
|
-
if (this.ws && this.ws.readyState ===
|
|
3043
|
+
if (this.ws && this.ws.readyState === 1) {
|
|
2357
3044
|
if (this.config?.enableInternalLogging) {
|
|
2358
3045
|
console.warn("[Limelight] Already connected. Call disconnect() first.");
|
|
2359
3046
|
}
|
|
@@ -2376,15 +3063,24 @@ var LimelightClient = class {
|
|
|
2376
3063
|
}
|
|
2377
3064
|
return;
|
|
2378
3065
|
}
|
|
3066
|
+
const WsConstructor = this.config.webSocketImpl ?? (typeof WebSocket !== "undefined" ? WebSocket : void 0);
|
|
3067
|
+
if (!WsConstructor) {
|
|
3068
|
+
if (this.config?.enableInternalLogging) {
|
|
3069
|
+
console.error(
|
|
3070
|
+
"[Limelight] WebSocket is not available. Pass webSocketImpl in config (e.g. ws package)."
|
|
3071
|
+
);
|
|
3072
|
+
}
|
|
3073
|
+
return;
|
|
3074
|
+
}
|
|
2379
3075
|
try {
|
|
2380
|
-
this.ws = new
|
|
3076
|
+
this.ws = new WsConstructor(serverUrl);
|
|
2381
3077
|
const message = {
|
|
2382
3078
|
phase: "CONNECT",
|
|
2383
3079
|
sessionId: this.sessionId,
|
|
2384
3080
|
timestamp: Date.now(),
|
|
2385
3081
|
data: {
|
|
2386
3082
|
appName,
|
|
2387
|
-
platform: platform || (typeof
|
|
3083
|
+
platform: platform || (hasDOM() ? "web" : typeof process !== "undefined" ? "node" : "react-native"),
|
|
2388
3084
|
projectKey: this.config.projectKey || "",
|
|
2389
3085
|
sdkVersion: SDK_VERSION
|
|
2390
3086
|
}
|
|
@@ -2451,10 +3147,13 @@ var LimelightClient = class {
|
|
|
2451
3147
|
* @returns {void}
|
|
2452
3148
|
*/
|
|
2453
3149
|
flushMessageQueue() {
|
|
2454
|
-
if (this.ws?.readyState !==
|
|
3150
|
+
if (this.ws?.readyState !== 1) return;
|
|
2455
3151
|
while (this.messageQueue.length > 0) {
|
|
2456
3152
|
const message = this.messageQueue.shift();
|
|
2457
3153
|
try {
|
|
3154
|
+
if (message && "sessionId" in message && !message.sessionId) {
|
|
3155
|
+
message.sessionId = this.sessionId;
|
|
3156
|
+
}
|
|
2458
3157
|
this.ws.send(safeStringify(message));
|
|
2459
3158
|
} catch (error) {
|
|
2460
3159
|
if (this.config?.enableInternalLogging) {
|
|
@@ -2472,9 +3171,9 @@ var LimelightClient = class {
|
|
|
2472
3171
|
* @returns {void}
|
|
2473
3172
|
*/
|
|
2474
3173
|
sendMessage(message) {
|
|
2475
|
-
if (this.ws?.readyState ===
|
|
3174
|
+
if (this.ws?.readyState === 1) {
|
|
2476
3175
|
this.flushMessageQueue();
|
|
2477
|
-
if (this.ws?.readyState ===
|
|
3176
|
+
if (this.ws?.readyState === 1) {
|
|
2478
3177
|
try {
|
|
2479
3178
|
this.ws.send(safeStringify(message));
|
|
2480
3179
|
} catch (error) {
|
|
@@ -2531,7 +3230,9 @@ var LimelightClient = class {
|
|
|
2531
3230
|
}
|
|
2532
3231
|
this.networkInterceptor.cleanup();
|
|
2533
3232
|
this.xhrInterceptor.cleanup();
|
|
3233
|
+
this.httpInterceptor.cleanup();
|
|
2534
3234
|
this.consoleInterceptor.cleanup();
|
|
3235
|
+
this.errorInterceptor.cleanup();
|
|
2535
3236
|
this.renderInterceptor.cleanup();
|
|
2536
3237
|
this.stateInterceptor.cleanup();
|
|
2537
3238
|
this.requestBridge.cleanup();
|
|
@@ -2578,6 +3279,46 @@ var LimelightClient = class {
|
|
|
2578
3279
|
failRequest(requestId, error) {
|
|
2579
3280
|
this.requestBridge.failRequest(requestId, error);
|
|
2580
3281
|
}
|
|
3282
|
+
/**
|
|
3283
|
+
* Returns an Express/Connect-compatible middleware that captures incoming
|
|
3284
|
+
* HTTP requests and responses.
|
|
3285
|
+
*
|
|
3286
|
+
* Place after body-parser middleware (express.json(), etc.) for request body capture.
|
|
3287
|
+
*
|
|
3288
|
+
* @example
|
|
3289
|
+
* ```ts
|
|
3290
|
+
* app.use(express.json());
|
|
3291
|
+
* app.use(Limelight.middleware());
|
|
3292
|
+
* ```
|
|
3293
|
+
*/
|
|
3294
|
+
middleware(options) {
|
|
3295
|
+
return createHttpMiddleware(
|
|
3296
|
+
this.sendMessage.bind(this),
|
|
3297
|
+
() => this.sessionId,
|
|
3298
|
+
() => this.config,
|
|
3299
|
+
options
|
|
3300
|
+
);
|
|
3301
|
+
}
|
|
3302
|
+
/**
|
|
3303
|
+
* Wraps a Next.js Pages API route handler with request/response capture.
|
|
3304
|
+
* Works with Pages Router (`pages/api/`), not App Router (`app/api/`).
|
|
3305
|
+
*
|
|
3306
|
+
* @example
|
|
3307
|
+
* ```ts
|
|
3308
|
+
* // pages/api/users.ts
|
|
3309
|
+
* export default Limelight.withLimelight((req, res) => {
|
|
3310
|
+
* res.json({ ok: true });
|
|
3311
|
+
* });
|
|
3312
|
+
* ```
|
|
3313
|
+
*/
|
|
3314
|
+
withLimelight(handler) {
|
|
3315
|
+
const wrapper = createWithLimelight(
|
|
3316
|
+
this.sendMessage.bind(this),
|
|
3317
|
+
() => this.sessionId,
|
|
3318
|
+
() => this.config
|
|
3319
|
+
);
|
|
3320
|
+
return wrapper(handler);
|
|
3321
|
+
}
|
|
2581
3322
|
};
|
|
2582
3323
|
var Limelight = new LimelightClient();
|
|
2583
3324
|
export {
|