@getlimelight/sdk 0.4.6 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +25 -4
- package/dist/index.d.ts +25 -4
- package/dist/index.js +193 -21
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +192 -21
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -7,7 +7,8 @@ declare enum NetworkPhase {
|
|
|
7
7
|
CONNECT = "CONNECT",
|
|
8
8
|
REQUEST = "REQUEST",
|
|
9
9
|
RESPONSE = "RESPONSE",
|
|
10
|
-
ERROR = "ERROR"
|
|
10
|
+
ERROR = "ERROR",
|
|
11
|
+
ABORT = "ABORT"
|
|
11
12
|
}
|
|
12
13
|
declare enum BodyFormat {
|
|
13
14
|
TEXT = "TEXT",
|
|
@@ -92,7 +93,7 @@ interface NetworkResponse extends BaseNetworkEvent {
|
|
|
92
93
|
* NETWORK ERROR (3rd possible outcome)
|
|
93
94
|
*/
|
|
94
95
|
interface NetworkErrorEvent extends BaseNetworkEvent {
|
|
95
|
-
phase: NetworkPhase.ERROR;
|
|
96
|
+
phase: NetworkPhase.ERROR | NetworkPhase.ABORT;
|
|
96
97
|
errorMessage: string;
|
|
97
98
|
stack?: string;
|
|
98
99
|
}
|
|
@@ -210,6 +211,25 @@ declare enum GraphqlOprtation {
|
|
|
210
211
|
SUB = "SUBSCRIPTION"
|
|
211
212
|
}
|
|
212
213
|
|
|
214
|
+
declare enum CommandType {
|
|
215
|
+
CLEAR_RENDERS = "CLEAR_RENDERS",
|
|
216
|
+
ACK = "ACK"
|
|
217
|
+
}
|
|
218
|
+
interface BaseCommand {
|
|
219
|
+
type: CommandType;
|
|
220
|
+
id?: string;
|
|
221
|
+
}
|
|
222
|
+
interface ClearRendersCommand extends BaseCommand {
|
|
223
|
+
type: CommandType.CLEAR_RENDERS;
|
|
224
|
+
}
|
|
225
|
+
type Command = ClearRendersCommand;
|
|
226
|
+
interface CommandAckEvent {
|
|
227
|
+
phase: CommandType.ACK;
|
|
228
|
+
commandId: string;
|
|
229
|
+
type: CommandType;
|
|
230
|
+
success: boolean;
|
|
231
|
+
}
|
|
232
|
+
|
|
213
233
|
/**
|
|
214
234
|
* Render lifecycle phases
|
|
215
235
|
*/
|
|
@@ -450,7 +470,7 @@ interface ConnectionEvent {
|
|
|
450
470
|
/**
|
|
451
471
|
* Union type representing all possible Limelight messages.
|
|
452
472
|
*/
|
|
453
|
-
type LimelightMessage = NetworkRequest | NetworkResponse | NetworkErrorEvent | ConsoleEvent | ConnectionEvent | RenderSnapshot | TransactionEvent | StateInitEvent | StateUpdateEvent;
|
|
473
|
+
type LimelightMessage = NetworkRequest | NetworkResponse | NetworkErrorEvent | ConsoleEvent | ConnectionEvent | RenderSnapshot | TransactionEvent | StateInitEvent | StateUpdateEvent | CommandAckEvent;
|
|
454
474
|
|
|
455
475
|
/**
|
|
456
476
|
* Represents a single frame in a stack trace.
|
|
@@ -512,6 +532,7 @@ declare class LimelightClient {
|
|
|
512
532
|
private renderInterceptor;
|
|
513
533
|
private stateInterceptor;
|
|
514
534
|
private requestBridge;
|
|
535
|
+
private commandHandler;
|
|
515
536
|
constructor();
|
|
516
537
|
/**
|
|
517
538
|
* Configures the Limelight client with the provided settings.
|
|
@@ -612,4 +633,4 @@ declare global {
|
|
|
612
633
|
}
|
|
613
634
|
}
|
|
614
635
|
|
|
615
|
-
export { type BaseNetworkEvent, BodyFormat, type ConnectEvent, type ConnectionEvent, type ConsoleEvent, ConsoleLevel, ConsoleSource, ConsoleType, EventType, type GraphQLRequest, type GraphQLResponse, GraphqlOprtation, HttpMethod, HttpStatusClass, Limelight, type LimelightConfig, type LimelightEvent, type LimelightMessage, type NetworkErrorEvent, type NetworkEvent, NetworkPhase, type NetworkRequest, type NetworkRequestWithResponse, type NetworkResponse, NetworkType, type ParsedStackTrace, type RequestBridgeConfig, type ResponseBridgeConfig, type SerializedBody, type Session, type StackFrame };
|
|
636
|
+
export { type BaseCommand, type BaseNetworkEvent, BodyFormat, type ClearRendersCommand, type Command, type CommandAckEvent, CommandType, type ConnectEvent, type ConnectionEvent, type ConsoleEvent, ConsoleLevel, ConsoleSource, ConsoleType, EventType, type GraphQLRequest, type GraphQLResponse, GraphqlOprtation, HttpMethod, HttpStatusClass, Limelight, type LimelightConfig, type LimelightEvent, type LimelightMessage, type NetworkErrorEvent, type NetworkEvent, NetworkPhase, type NetworkRequest, type NetworkRequestWithResponse, type NetworkResponse, NetworkType, type ParsedStackTrace, type RequestBridgeConfig, type ResponseBridgeConfig, type SerializedBody, type Session, type StackFrame };
|
package/dist/index.d.ts
CHANGED
|
@@ -7,7 +7,8 @@ declare enum NetworkPhase {
|
|
|
7
7
|
CONNECT = "CONNECT",
|
|
8
8
|
REQUEST = "REQUEST",
|
|
9
9
|
RESPONSE = "RESPONSE",
|
|
10
|
-
ERROR = "ERROR"
|
|
10
|
+
ERROR = "ERROR",
|
|
11
|
+
ABORT = "ABORT"
|
|
11
12
|
}
|
|
12
13
|
declare enum BodyFormat {
|
|
13
14
|
TEXT = "TEXT",
|
|
@@ -92,7 +93,7 @@ interface NetworkResponse extends BaseNetworkEvent {
|
|
|
92
93
|
* NETWORK ERROR (3rd possible outcome)
|
|
93
94
|
*/
|
|
94
95
|
interface NetworkErrorEvent extends BaseNetworkEvent {
|
|
95
|
-
phase: NetworkPhase.ERROR;
|
|
96
|
+
phase: NetworkPhase.ERROR | NetworkPhase.ABORT;
|
|
96
97
|
errorMessage: string;
|
|
97
98
|
stack?: string;
|
|
98
99
|
}
|
|
@@ -210,6 +211,25 @@ declare enum GraphqlOprtation {
|
|
|
210
211
|
SUB = "SUBSCRIPTION"
|
|
211
212
|
}
|
|
212
213
|
|
|
214
|
+
declare enum CommandType {
|
|
215
|
+
CLEAR_RENDERS = "CLEAR_RENDERS",
|
|
216
|
+
ACK = "ACK"
|
|
217
|
+
}
|
|
218
|
+
interface BaseCommand {
|
|
219
|
+
type: CommandType;
|
|
220
|
+
id?: string;
|
|
221
|
+
}
|
|
222
|
+
interface ClearRendersCommand extends BaseCommand {
|
|
223
|
+
type: CommandType.CLEAR_RENDERS;
|
|
224
|
+
}
|
|
225
|
+
type Command = ClearRendersCommand;
|
|
226
|
+
interface CommandAckEvent {
|
|
227
|
+
phase: CommandType.ACK;
|
|
228
|
+
commandId: string;
|
|
229
|
+
type: CommandType;
|
|
230
|
+
success: boolean;
|
|
231
|
+
}
|
|
232
|
+
|
|
213
233
|
/**
|
|
214
234
|
* Render lifecycle phases
|
|
215
235
|
*/
|
|
@@ -450,7 +470,7 @@ interface ConnectionEvent {
|
|
|
450
470
|
/**
|
|
451
471
|
* Union type representing all possible Limelight messages.
|
|
452
472
|
*/
|
|
453
|
-
type LimelightMessage = NetworkRequest | NetworkResponse | NetworkErrorEvent | ConsoleEvent | ConnectionEvent | RenderSnapshot | TransactionEvent | StateInitEvent | StateUpdateEvent;
|
|
473
|
+
type LimelightMessage = NetworkRequest | NetworkResponse | NetworkErrorEvent | ConsoleEvent | ConnectionEvent | RenderSnapshot | TransactionEvent | StateInitEvent | StateUpdateEvent | CommandAckEvent;
|
|
454
474
|
|
|
455
475
|
/**
|
|
456
476
|
* Represents a single frame in a stack trace.
|
|
@@ -512,6 +532,7 @@ declare class LimelightClient {
|
|
|
512
532
|
private renderInterceptor;
|
|
513
533
|
private stateInterceptor;
|
|
514
534
|
private requestBridge;
|
|
535
|
+
private commandHandler;
|
|
515
536
|
constructor();
|
|
516
537
|
/**
|
|
517
538
|
* Configures the Limelight client with the provided settings.
|
|
@@ -612,4 +633,4 @@ declare global {
|
|
|
612
633
|
}
|
|
613
634
|
}
|
|
614
635
|
|
|
615
|
-
export { type BaseNetworkEvent, BodyFormat, type ConnectEvent, type ConnectionEvent, type ConsoleEvent, ConsoleLevel, ConsoleSource, ConsoleType, EventType, type GraphQLRequest, type GraphQLResponse, GraphqlOprtation, HttpMethod, HttpStatusClass, Limelight, type LimelightConfig, type LimelightEvent, type LimelightMessage, type NetworkErrorEvent, type NetworkEvent, NetworkPhase, type NetworkRequest, type NetworkRequestWithResponse, type NetworkResponse, NetworkType, type ParsedStackTrace, type RequestBridgeConfig, type ResponseBridgeConfig, type SerializedBody, type Session, type StackFrame };
|
|
636
|
+
export { type BaseCommand, type BaseNetworkEvent, BodyFormat, type ClearRendersCommand, type Command, type CommandAckEvent, CommandType, type ConnectEvent, type ConnectionEvent, type ConsoleEvent, ConsoleLevel, ConsoleSource, ConsoleType, EventType, type GraphQLRequest, type GraphQLResponse, GraphqlOprtation, HttpMethod, HttpStatusClass, Limelight, type LimelightConfig, type LimelightEvent, type LimelightMessage, type NetworkErrorEvent, type NetworkEvent, NetworkPhase, type NetworkRequest, type NetworkRequestWithResponse, type NetworkResponse, NetworkType, type ParsedStackTrace, type RequestBridgeConfig, type ResponseBridgeConfig, type SerializedBody, type Session, type StackFrame };
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
BodyFormat: () => BodyFormat,
|
|
24
|
+
CommandType: () => CommandType,
|
|
24
25
|
ConsoleLevel: () => ConsoleLevel,
|
|
25
26
|
ConsoleSource: () => ConsoleSource,
|
|
26
27
|
ConsoleType: () => ConsoleType,
|
|
@@ -72,6 +73,7 @@ var NetworkPhase = /* @__PURE__ */ ((NetworkPhase2) => {
|
|
|
72
73
|
NetworkPhase2["REQUEST"] = "REQUEST";
|
|
73
74
|
NetworkPhase2["RESPONSE"] = "RESPONSE";
|
|
74
75
|
NetworkPhase2["ERROR"] = "ERROR";
|
|
76
|
+
NetworkPhase2["ABORT"] = "ABORT";
|
|
75
77
|
return NetworkPhase2;
|
|
76
78
|
})(NetworkPhase || {});
|
|
77
79
|
var BodyFormat = /* @__PURE__ */ ((BodyFormat2) => {
|
|
@@ -118,6 +120,13 @@ var GraphqlOprtation = /* @__PURE__ */ ((GraphqlOprtation2) => {
|
|
|
118
120
|
return GraphqlOprtation2;
|
|
119
121
|
})(GraphqlOprtation || {});
|
|
120
122
|
|
|
123
|
+
// src/types/commands.ts
|
|
124
|
+
var CommandType = /* @__PURE__ */ ((CommandType2) => {
|
|
125
|
+
CommandType2["CLEAR_RENDERS"] = "CLEAR_RENDERS";
|
|
126
|
+
CommandType2["ACK"] = "ACK";
|
|
127
|
+
return CommandType2;
|
|
128
|
+
})(CommandType || {});
|
|
129
|
+
|
|
121
130
|
// src/helpers/detection/detectConsoleType.ts
|
|
122
131
|
var detectConsoleType = (level, args) => {
|
|
123
132
|
const messageStr = args.map((arg) => {
|
|
@@ -267,7 +276,7 @@ var SENSITIVE_HEADERS = [
|
|
|
267
276
|
var LIMELIGHT_WEB_WSS_URL = "wss://api.getlimelight.io";
|
|
268
277
|
var LIMELIGHT_DESKTOP_WSS_URL = "ws://localhost:8484";
|
|
269
278
|
var WS_PATH = "/limelight";
|
|
270
|
-
var SDK_VERSION = true ? "0.
|
|
279
|
+
var SDK_VERSION = true ? "0.5.2" : "test-version";
|
|
271
280
|
var RENDER_THRESHOLDS = {
|
|
272
281
|
HOT_VELOCITY: 5,
|
|
273
282
|
HIGH_RENDER_COUNT: 50,
|
|
@@ -838,13 +847,14 @@ var NetworkInterceptor = class {
|
|
|
838
847
|
} catch (err) {
|
|
839
848
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
840
849
|
const errorStack = err instanceof Error ? err.stack : void 0;
|
|
850
|
+
const isAbort = err instanceof DOMException && err.name === "AbortError";
|
|
841
851
|
let errorEvent = {
|
|
842
852
|
id: requestId,
|
|
843
853
|
sessionId: self.getSessionId(),
|
|
844
854
|
timestamp: Date.now(),
|
|
845
|
-
phase: "ERROR" /* ERROR */,
|
|
855
|
+
phase: isAbort ? "ABORT" /* ABORT */ : "ERROR" /* ERROR */,
|
|
846
856
|
networkType: "fetch" /* FETCH */,
|
|
847
|
-
errorMessage,
|
|
857
|
+
errorMessage: isAbort ? "Request aborted" : errorMessage,
|
|
848
858
|
stack: errorStack
|
|
849
859
|
};
|
|
850
860
|
if (self.config?.beforeSend) {
|
|
@@ -1028,14 +1038,14 @@ var XHRInterceptor = class {
|
|
|
1028
1038
|
self.sendMessage(responseEvent);
|
|
1029
1039
|
cleanup.call(this);
|
|
1030
1040
|
};
|
|
1031
|
-
const sendError = (errorMessage) => {
|
|
1041
|
+
const sendError = (errorMessage, phase = "ERROR" /* ERROR */) => {
|
|
1032
1042
|
if (responseSent) return;
|
|
1033
1043
|
responseSent = true;
|
|
1034
1044
|
let errorEvent = {
|
|
1035
1045
|
id: data.id,
|
|
1036
1046
|
sessionId: self.getSessionId(),
|
|
1037
1047
|
timestamp: Date.now(),
|
|
1038
|
-
phase
|
|
1048
|
+
phase,
|
|
1039
1049
|
networkType: "xhr" /* XHR */,
|
|
1040
1050
|
errorMessage
|
|
1041
1051
|
};
|
|
@@ -1061,7 +1071,7 @@ var XHRInterceptor = class {
|
|
|
1061
1071
|
cleanup.call(this);
|
|
1062
1072
|
};
|
|
1063
1073
|
const abortHandler = function() {
|
|
1064
|
-
sendError("Request aborted");
|
|
1074
|
+
sendError("Request aborted", "ABORT" /* ABORT */);
|
|
1065
1075
|
cleanup.call(this);
|
|
1066
1076
|
};
|
|
1067
1077
|
const timeoutHandler = function() {
|
|
@@ -1162,12 +1172,10 @@ var RenderInterceptor = class {
|
|
|
1162
1172
|
}, RENDER_THRESHOLDS.SNAPSHOT_INTERVAL_MS);
|
|
1163
1173
|
this.isSetup = true;
|
|
1164
1174
|
}
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
this.componentIdCounter = 0;
|
|
1170
|
-
}
|
|
1175
|
+
/**
|
|
1176
|
+
* Installs or wraps the React DevTools global hook.
|
|
1177
|
+
* Returns true if successful, false otherwise.
|
|
1178
|
+
*/
|
|
1171
1179
|
installHook() {
|
|
1172
1180
|
const globalObj = typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : null;
|
|
1173
1181
|
if (!globalObj) return false;
|
|
@@ -1180,6 +1188,11 @@ var RenderInterceptor = class {
|
|
|
1180
1188
|
}
|
|
1181
1189
|
return true;
|
|
1182
1190
|
}
|
|
1191
|
+
/**
|
|
1192
|
+
* Wraps an existing React DevTools hook to intercept render events.
|
|
1193
|
+
* Preserves original functionality.
|
|
1194
|
+
* @param hook - The existing React DevTools hook
|
|
1195
|
+
*/
|
|
1183
1196
|
wrapExistingHook(hook) {
|
|
1184
1197
|
this.originalHook = hook;
|
|
1185
1198
|
this.originalOnCommitFiberRoot = hook.onCommitFiberRoot?.bind(hook);
|
|
@@ -1193,6 +1206,11 @@ var RenderInterceptor = class {
|
|
|
1193
1206
|
this.handleCommitFiberUnmount(rendererID, fiber);
|
|
1194
1207
|
};
|
|
1195
1208
|
}
|
|
1209
|
+
/**
|
|
1210
|
+
* Creates a new React DevTools hook to intercept render events.
|
|
1211
|
+
* @param globalObj - The global object (window or global)
|
|
1212
|
+
* @param hookKey - The key for the React DevTools hook
|
|
1213
|
+
*/
|
|
1196
1214
|
createHook(globalObj, hookKey) {
|
|
1197
1215
|
const renderers = /* @__PURE__ */ new Map();
|
|
1198
1216
|
let rendererIdCounter = 0;
|
|
@@ -1215,6 +1233,8 @@ var RenderInterceptor = class {
|
|
|
1215
1233
|
/**
|
|
1216
1234
|
* Handles a fiber root commit - walks tree and ACCUMULATES into profiles.
|
|
1217
1235
|
* Two-pass: first count components, then accumulate with distributed cost.
|
|
1236
|
+
* @param rendererID - The renderer ID
|
|
1237
|
+
* @param root - The fiber root
|
|
1218
1238
|
*/
|
|
1219
1239
|
handleCommitFiberRoot(_rendererID, root) {
|
|
1220
1240
|
this.currentCommitComponents.clear();
|
|
@@ -1230,6 +1250,7 @@ var RenderInterceptor = class {
|
|
|
1230
1250
|
}
|
|
1231
1251
|
/**
|
|
1232
1252
|
* First pass: count rendered components for cost distribution.
|
|
1253
|
+
* @param fiber - The current fiber node
|
|
1233
1254
|
*/
|
|
1234
1255
|
countRenderedComponents(fiber) {
|
|
1235
1256
|
if (!fiber) return;
|
|
@@ -1239,6 +1260,11 @@ var RenderInterceptor = class {
|
|
|
1239
1260
|
this.countRenderedComponents(fiber.child);
|
|
1240
1261
|
this.countRenderedComponents(fiber.sibling);
|
|
1241
1262
|
}
|
|
1263
|
+
/**
|
|
1264
|
+
* Handles a fiber unmount - marks component as unmounted.
|
|
1265
|
+
* @param rendererID - The renderer ID
|
|
1266
|
+
* @param fiber - The fiber being unmounted
|
|
1267
|
+
*/
|
|
1242
1268
|
handleCommitFiberUnmount(_rendererID, fiber) {
|
|
1243
1269
|
if (!this.isUserComponent(fiber)) return;
|
|
1244
1270
|
const componentId = this.fiberToComponentId.get(fiber);
|
|
@@ -1252,20 +1278,28 @@ var RenderInterceptor = class {
|
|
|
1252
1278
|
}
|
|
1253
1279
|
/**
|
|
1254
1280
|
* Walks fiber tree and accumulates render stats into profiles.
|
|
1281
|
+
* @param fiber - The current fiber node
|
|
1282
|
+
* @param parentComponentId - The parent component ID
|
|
1283
|
+
* @param depth - The current depth in the tree
|
|
1255
1284
|
*/
|
|
1256
1285
|
walkFiberTree(fiber, parentComponentId, depth) {
|
|
1257
1286
|
if (!fiber) return;
|
|
1287
|
+
let currentParentId = parentComponentId;
|
|
1258
1288
|
if (this.isUserComponent(fiber) && this.didFiberRender(fiber)) {
|
|
1259
1289
|
const componentId = this.getOrCreateComponentId(fiber);
|
|
1260
1290
|
this.accumulateRender(fiber, componentId, parentComponentId, depth);
|
|
1261
1291
|
this.currentCommitComponents.add(componentId);
|
|
1262
|
-
|
|
1292
|
+
currentParentId = componentId;
|
|
1263
1293
|
}
|
|
1264
|
-
this.walkFiberTree(fiber.child,
|
|
1294
|
+
this.walkFiberTree(fiber.child, currentParentId, depth + 1);
|
|
1265
1295
|
this.walkFiberTree(fiber.sibling, parentComponentId, depth);
|
|
1266
1296
|
}
|
|
1267
1297
|
/**
|
|
1268
1298
|
* Core accumulation logic - this is where we build up the profile.
|
|
1299
|
+
* @param fiber - The current fiber node
|
|
1300
|
+
* @param componentId - The component ID
|
|
1301
|
+
* @param parentComponentId - The parent component ID
|
|
1302
|
+
* @param depth - The current depth in the tree
|
|
1269
1303
|
*/
|
|
1270
1304
|
accumulateRender(fiber, componentId, parentComponentId, depth) {
|
|
1271
1305
|
const now = Date.now();
|
|
@@ -1326,7 +1360,9 @@ var RenderInterceptor = class {
|
|
|
1326
1360
|
this.updateSuspiciousFlag(profile);
|
|
1327
1361
|
}
|
|
1328
1362
|
/**
|
|
1329
|
-
*
|
|
1363
|
+
* Accumulate prop change details into the profile.
|
|
1364
|
+
* @param profile - The component profile
|
|
1365
|
+
* @param changes - The list of prop change details
|
|
1330
1366
|
*/
|
|
1331
1367
|
accumulatePropChanges(profile, changes) {
|
|
1332
1368
|
const stats = profile.propChangeStats;
|
|
@@ -1355,7 +1391,9 @@ var RenderInterceptor = class {
|
|
|
1355
1391
|
}
|
|
1356
1392
|
}
|
|
1357
1393
|
/**
|
|
1358
|
-
*
|
|
1394
|
+
* Build prop change snapshot for emission.
|
|
1395
|
+
* @param profile - The component profile
|
|
1396
|
+
* @returns The prop change snapshot or undefined
|
|
1359
1397
|
*/
|
|
1360
1398
|
buildPropChangeSnapshot(profile) {
|
|
1361
1399
|
const stats = profile.propChangeStats;
|
|
@@ -1373,6 +1411,10 @@ var RenderInterceptor = class {
|
|
|
1373
1411
|
});
|
|
1374
1412
|
return { topChangedProps };
|
|
1375
1413
|
}
|
|
1414
|
+
/**
|
|
1415
|
+
* Updates the suspicious flag based on render velocity and count.
|
|
1416
|
+
* @param profile - The component profile
|
|
1417
|
+
*/
|
|
1376
1418
|
updateSuspiciousFlag(profile) {
|
|
1377
1419
|
const velocity = this.calculateVelocity(profile);
|
|
1378
1420
|
if (velocity > RENDER_THRESHOLDS.HOT_VELOCITY) {
|
|
@@ -1391,6 +1433,8 @@ var RenderInterceptor = class {
|
|
|
1391
1433
|
/**
|
|
1392
1434
|
* Calculates renders per second from velocity window.
|
|
1393
1435
|
* Cheap: just count / window duration, no array operations.
|
|
1436
|
+
* @param profile - The component profile
|
|
1437
|
+
* @returns The calculated velocity
|
|
1394
1438
|
*/
|
|
1395
1439
|
calculateVelocity(profile) {
|
|
1396
1440
|
const now = Date.now();
|
|
@@ -1403,6 +1447,8 @@ var RenderInterceptor = class {
|
|
|
1403
1447
|
}
|
|
1404
1448
|
/**
|
|
1405
1449
|
* Emits a snapshot of all profiles with deltas.
|
|
1450
|
+
* Only emits profiles that have significant changes since last emit.
|
|
1451
|
+
* Also emits unmounts.
|
|
1406
1452
|
*/
|
|
1407
1453
|
emitSnapshot() {
|
|
1408
1454
|
const now = Date.now();
|
|
@@ -1486,7 +1532,11 @@ var RenderInterceptor = class {
|
|
|
1486
1532
|
this.sendMessage(message);
|
|
1487
1533
|
}
|
|
1488
1534
|
/**
|
|
1489
|
-
*
|
|
1535
|
+
* Now returns prop change details when applicable.
|
|
1536
|
+
* Infers the cause of the render by comparing current and previous fiber states.
|
|
1537
|
+
* @param fiber - The current fiber node
|
|
1538
|
+
* @param parentComponentId - The parent component ID
|
|
1539
|
+
* @returns The inferred render cause
|
|
1490
1540
|
*/
|
|
1491
1541
|
inferRenderCause(fiber, parentComponentId) {
|
|
1492
1542
|
const alternate = fiber.alternate;
|
|
@@ -1535,6 +1585,9 @@ var RenderInterceptor = class {
|
|
|
1535
1585
|
/**
|
|
1536
1586
|
* Diff props to find which keys changed and whether it's reference-only.
|
|
1537
1587
|
* This is the key insight generator.
|
|
1588
|
+
* @param prevProps - The previous props
|
|
1589
|
+
* @param nextProps - The next props
|
|
1590
|
+
* @returns List of prop change details
|
|
1538
1591
|
*/
|
|
1539
1592
|
diffProps(prevProps, nextProps) {
|
|
1540
1593
|
if (!prevProps || !nextProps) {
|
|
@@ -1562,8 +1615,11 @@ var RenderInterceptor = class {
|
|
|
1562
1615
|
return changes;
|
|
1563
1616
|
}
|
|
1564
1617
|
/**
|
|
1565
|
-
*
|
|
1618
|
+
* Shallow equality check to determine if a prop is reference-only change.
|
|
1566
1619
|
* We only go one level deep to keep it fast.
|
|
1620
|
+
* @param a - The first value
|
|
1621
|
+
* @param b - The second value
|
|
1622
|
+
* @returns True if shallow equal, false otherwise
|
|
1567
1623
|
*/
|
|
1568
1624
|
isShallowEqual(a, b) {
|
|
1569
1625
|
if (a === b) return true;
|
|
@@ -1590,13 +1646,28 @@ var RenderInterceptor = class {
|
|
|
1590
1646
|
}
|
|
1591
1647
|
return a === b;
|
|
1592
1648
|
}
|
|
1649
|
+
/**
|
|
1650
|
+
* Determines if a fiber represents a user-defined component.
|
|
1651
|
+
* @param fiber - The fiber node
|
|
1652
|
+
* @returns True if user component, false otherwise
|
|
1653
|
+
*/
|
|
1593
1654
|
isUserComponent(fiber) {
|
|
1594
1655
|
const tag = fiber.tag;
|
|
1595
1656
|
return tag === 0 /* FunctionComponent */ || tag === 1 /* ClassComponent */ || tag === 11 /* ForwardRef */ || tag === 14 /* MemoComponent */ || tag === 15 /* SimpleMemoComponent */;
|
|
1596
1657
|
}
|
|
1658
|
+
/**
|
|
1659
|
+
* Determines if a fiber performed work during the commit.
|
|
1660
|
+
* @param fiber - The fiber node
|
|
1661
|
+
* @returns True if performed work, false otherwise
|
|
1662
|
+
*/
|
|
1597
1663
|
didFiberRender(fiber) {
|
|
1598
1664
|
return (fiber.flags & 1 /* PerformedWork */) !== 0;
|
|
1599
1665
|
}
|
|
1666
|
+
/**
|
|
1667
|
+
* Gets or creates a unique component ID for a fiber.
|
|
1668
|
+
* @param fiber - The fiber node
|
|
1669
|
+
* @returns The unique component ID
|
|
1670
|
+
*/
|
|
1600
1671
|
getOrCreateComponentId(fiber) {
|
|
1601
1672
|
let id = this.fiberToComponentId.get(fiber);
|
|
1602
1673
|
if (id) return id;
|
|
@@ -1611,6 +1682,11 @@ var RenderInterceptor = class {
|
|
|
1611
1682
|
this.fiberToComponentId.set(fiber, id);
|
|
1612
1683
|
return id;
|
|
1613
1684
|
}
|
|
1685
|
+
/**
|
|
1686
|
+
* Gets the display name of a component from a fiber.
|
|
1687
|
+
* @param fiber - The fiber node
|
|
1688
|
+
* @returns The component name
|
|
1689
|
+
*/
|
|
1614
1690
|
getComponentName(fiber) {
|
|
1615
1691
|
const type = fiber.type;
|
|
1616
1692
|
if (!type) return "Unknown";
|
|
@@ -1628,6 +1704,11 @@ var RenderInterceptor = class {
|
|
|
1628
1704
|
}
|
|
1629
1705
|
return "Unknown";
|
|
1630
1706
|
}
|
|
1707
|
+
/**
|
|
1708
|
+
* Gets the component type from a fiber.
|
|
1709
|
+
* @param fiber - The fiber node
|
|
1710
|
+
* @returns The component type
|
|
1711
|
+
*/
|
|
1631
1712
|
getComponentType(fiber) {
|
|
1632
1713
|
switch (fiber.tag) {
|
|
1633
1714
|
case 0 /* FunctionComponent */:
|
|
@@ -1661,6 +1742,9 @@ var RenderInterceptor = class {
|
|
|
1661
1742
|
getSuspiciousComponents() {
|
|
1662
1743
|
return Array.from(this.profiles.values()).filter((p) => p.isSuspicious);
|
|
1663
1744
|
}
|
|
1745
|
+
/**
|
|
1746
|
+
* Cleans up and restores original hook behavior.
|
|
1747
|
+
*/
|
|
1664
1748
|
cleanup() {
|
|
1665
1749
|
if (!this.isSetup) return;
|
|
1666
1750
|
this.emitSnapshot();
|
|
@@ -1686,6 +1770,16 @@ var RenderInterceptor = class {
|
|
|
1686
1770
|
this.config = null;
|
|
1687
1771
|
this.isSetup = false;
|
|
1688
1772
|
}
|
|
1773
|
+
/**
|
|
1774
|
+
* Resets all collected profiles
|
|
1775
|
+
*/
|
|
1776
|
+
resetProfiles() {
|
|
1777
|
+
this.profiles.clear();
|
|
1778
|
+
this.fiberToComponentId = /* @__PURE__ */ new WeakMap();
|
|
1779
|
+
this.pendingUnmounts = [];
|
|
1780
|
+
this.currentCommitComponents.clear();
|
|
1781
|
+
this.componentIdCounter = 0;
|
|
1782
|
+
}
|
|
1689
1783
|
};
|
|
1690
1784
|
|
|
1691
1785
|
// src/limelight/interceptors/StateInterceptor.ts
|
|
@@ -1724,7 +1818,7 @@ var StateInterceptor = class {
|
|
|
1724
1818
|
);
|
|
1725
1819
|
return;
|
|
1726
1820
|
}
|
|
1727
|
-
const state = this.getState(store
|
|
1821
|
+
const state = this.getState(store);
|
|
1728
1822
|
const initEvent = {
|
|
1729
1823
|
phase: "STATE:INIT" /* INIT */,
|
|
1730
1824
|
sessionId: this.getSessionId(),
|
|
@@ -1741,6 +1835,8 @@ var StateInterceptor = class {
|
|
|
1741
1835
|
}
|
|
1742
1836
|
/**
|
|
1743
1837
|
* Unregister a store and stop listening to changes.
|
|
1838
|
+
* Can be called manually via Limelight.removeStore().
|
|
1839
|
+
* @param name The name of the store to unregister
|
|
1744
1840
|
*/
|
|
1745
1841
|
unregisterStore(name) {
|
|
1746
1842
|
const store = this.stores.get(name);
|
|
@@ -1751,6 +1847,7 @@ var StateInterceptor = class {
|
|
|
1751
1847
|
}
|
|
1752
1848
|
/**
|
|
1753
1849
|
* Emit an event, applying beforeSend hook if configured
|
|
1850
|
+
* @param event The event to emit
|
|
1754
1851
|
*/
|
|
1755
1852
|
emitEvent(event) {
|
|
1756
1853
|
if (this.config?.beforeSend) {
|
|
@@ -1769,6 +1866,8 @@ var StateInterceptor = class {
|
|
|
1769
1866
|
}
|
|
1770
1867
|
/**
|
|
1771
1868
|
* Detect whether a store is Zustand or Redux
|
|
1869
|
+
* @param store The store to inspect
|
|
1870
|
+
* @return StateLibrary or null if unknown
|
|
1772
1871
|
*/
|
|
1773
1872
|
detectLibrary(store) {
|
|
1774
1873
|
if (!store || typeof store !== "function" && typeof store !== "object") {
|
|
@@ -1787,13 +1886,19 @@ var StateInterceptor = class {
|
|
|
1787
1886
|
}
|
|
1788
1887
|
/**
|
|
1789
1888
|
* Get current state from a store
|
|
1889
|
+
* @param store The store to get state from
|
|
1890
|
+
* @return The current state
|
|
1790
1891
|
*/
|
|
1791
|
-
getState(store
|
|
1892
|
+
getState(store) {
|
|
1792
1893
|
const storeAny = store;
|
|
1793
1894
|
return storeAny.getState();
|
|
1794
1895
|
}
|
|
1795
1896
|
/**
|
|
1796
1897
|
* Subscribe to store changes
|
|
1898
|
+
* @param store The store to subscribe to
|
|
1899
|
+
* @param library The detected state library
|
|
1900
|
+
* @param storeName The name of the store
|
|
1901
|
+
* @return Unsubscribe function
|
|
1797
1902
|
*/
|
|
1798
1903
|
subscribe(store, library, storeName) {
|
|
1799
1904
|
const storeAny = store;
|
|
@@ -1805,6 +1910,9 @@ var StateInterceptor = class {
|
|
|
1805
1910
|
}
|
|
1806
1911
|
/**
|
|
1807
1912
|
* Subscribe to Zustand store changes
|
|
1913
|
+
* @param store The Zustand store
|
|
1914
|
+
* @param storeName The name of the store
|
|
1915
|
+
* @return Unsubscribe function
|
|
1808
1916
|
*/
|
|
1809
1917
|
subscribeZustand(store, storeName) {
|
|
1810
1918
|
return store.subscribe((state, prevState) => {
|
|
@@ -1827,6 +1935,9 @@ var StateInterceptor = class {
|
|
|
1827
1935
|
}
|
|
1828
1936
|
/**
|
|
1829
1937
|
* Subscribe to Redux store changes
|
|
1938
|
+
* @param store The Redux store
|
|
1939
|
+
* @param storeName The name of the store
|
|
1940
|
+
* @return Unsubscribe function
|
|
1830
1941
|
*/
|
|
1831
1942
|
subscribeRedux(store, storeName) {
|
|
1832
1943
|
let lastAction = { type: "@@INIT" };
|
|
@@ -1862,6 +1973,9 @@ var StateInterceptor = class {
|
|
|
1862
1973
|
}
|
|
1863
1974
|
/**
|
|
1864
1975
|
* Infer action name from stack trace for Zustand
|
|
1976
|
+
* @param state The new state
|
|
1977
|
+
* @param prevState The previous state
|
|
1978
|
+
* @return Inferred StateAction
|
|
1865
1979
|
*/
|
|
1866
1980
|
inferZustandAction(state, prevState) {
|
|
1867
1981
|
const actionType = this.parseActionFromStack(this.captureStackTrace());
|
|
@@ -1873,6 +1987,8 @@ var StateInterceptor = class {
|
|
|
1873
1987
|
}
|
|
1874
1988
|
/**
|
|
1875
1989
|
* Parse function name from stack trace
|
|
1990
|
+
* @param stack The stack trace string
|
|
1991
|
+
* @return The inferred action name
|
|
1876
1992
|
*/
|
|
1877
1993
|
parseActionFromStack(stack) {
|
|
1878
1994
|
if (!stack) return "set";
|
|
@@ -1903,6 +2019,9 @@ var StateInterceptor = class {
|
|
|
1903
2019
|
}
|
|
1904
2020
|
/**
|
|
1905
2021
|
* Compute what keys changed between states (shallow)
|
|
2022
|
+
* @param state The new state
|
|
2023
|
+
* @param prevState The previous state
|
|
2024
|
+
* @return Partial state with only changed keys
|
|
1906
2025
|
*/
|
|
1907
2026
|
computePartialState(state, prevState) {
|
|
1908
2027
|
if (typeof state !== "object" || state === null || typeof prevState !== "object" || prevState === null) {
|
|
@@ -1943,7 +2062,7 @@ var StateInterceptor = class {
|
|
|
1943
2062
|
}
|
|
1944
2063
|
};
|
|
1945
2064
|
|
|
1946
|
-
// src/limelight/
|
|
2065
|
+
// src/limelight/bridges/RequestBridge.ts
|
|
1947
2066
|
var RequestBridge = class {
|
|
1948
2067
|
constructor(sendMessage, getSessionId) {
|
|
1949
2068
|
this.sendMessage = sendMessage;
|
|
@@ -2110,6 +2229,42 @@ var RequestBridge = class {
|
|
|
2110
2229
|
}
|
|
2111
2230
|
};
|
|
2112
2231
|
|
|
2232
|
+
// src/limelight/handlers/CommandHandler.ts
|
|
2233
|
+
var CommandHandler = class {
|
|
2234
|
+
constructor(interceptors, sendMessage, getConfig) {
|
|
2235
|
+
this.interceptors = interceptors;
|
|
2236
|
+
this.sendMessage = sendMessage;
|
|
2237
|
+
this.getConfig = getConfig;
|
|
2238
|
+
}
|
|
2239
|
+
/**
|
|
2240
|
+
* Handles an incoming command.
|
|
2241
|
+
* @param command - The command to handle
|
|
2242
|
+
*/
|
|
2243
|
+
handle(command) {
|
|
2244
|
+
const config = this.getConfig();
|
|
2245
|
+
if (config?.enableInternalLogging) {
|
|
2246
|
+
console.log("[Limelight] Received command:", command.type);
|
|
2247
|
+
}
|
|
2248
|
+
switch (command.type) {
|
|
2249
|
+
case "CLEAR_RENDERS" /* CLEAR_RENDERS */:
|
|
2250
|
+
this.interceptors.render.resetProfiles();
|
|
2251
|
+
break;
|
|
2252
|
+
default:
|
|
2253
|
+
if (config?.enableInternalLogging) {
|
|
2254
|
+
console.warn("[Limelight] Unknown command:", command.type);
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
if (command.id) {
|
|
2258
|
+
this.sendMessage({
|
|
2259
|
+
phase: "ACK" /* ACK */,
|
|
2260
|
+
commandId: command.id,
|
|
2261
|
+
type: command.type,
|
|
2262
|
+
success: true
|
|
2263
|
+
});
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
};
|
|
2267
|
+
|
|
2113
2268
|
// src/limelight/LimelightClient.ts
|
|
2114
2269
|
var LimelightClient = class {
|
|
2115
2270
|
ws = null;
|
|
@@ -2127,6 +2282,7 @@ var LimelightClient = class {
|
|
|
2127
2282
|
renderInterceptor;
|
|
2128
2283
|
stateInterceptor;
|
|
2129
2284
|
requestBridge;
|
|
2285
|
+
commandHandler = null;
|
|
2130
2286
|
constructor() {
|
|
2131
2287
|
this.networkInterceptor = new NetworkInterceptor(
|
|
2132
2288
|
this.sendMessage.bind(this),
|
|
@@ -2152,6 +2308,11 @@ var LimelightClient = class {
|
|
|
2152
2308
|
this.sendMessage.bind(this),
|
|
2153
2309
|
() => this.sessionId
|
|
2154
2310
|
);
|
|
2311
|
+
this.commandHandler = new CommandHandler(
|
|
2312
|
+
{ render: this.renderInterceptor },
|
|
2313
|
+
this.sendMessage.bind(this),
|
|
2314
|
+
() => this.config
|
|
2315
|
+
);
|
|
2155
2316
|
}
|
|
2156
2317
|
/**
|
|
2157
2318
|
* Configures the Limelight client with the provided settings.
|
|
@@ -2258,6 +2419,16 @@ var LimelightClient = class {
|
|
|
2258
2419
|
this.flushMessageQueue();
|
|
2259
2420
|
this.sendMessage(message);
|
|
2260
2421
|
};
|
|
2422
|
+
this.ws.onmessage = (event) => {
|
|
2423
|
+
try {
|
|
2424
|
+
const command = JSON.parse(event.data);
|
|
2425
|
+
this.commandHandler?.handle(command);
|
|
2426
|
+
} catch (error) {
|
|
2427
|
+
if (this.config?.enableInternalLogging) {
|
|
2428
|
+
console.error("[Limelight] Failed to parse command:", error);
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
};
|
|
2261
2432
|
this.ws.onerror = (error) => {
|
|
2262
2433
|
if (this.config?.enableInternalLogging) {
|
|
2263
2434
|
console.error("[Limelight] WebSocket error:", error);
|
|
@@ -2437,6 +2608,7 @@ var Limelight = new LimelightClient();
|
|
|
2437
2608
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2438
2609
|
0 && (module.exports = {
|
|
2439
2610
|
BodyFormat,
|
|
2611
|
+
CommandType,
|
|
2440
2612
|
ConsoleLevel,
|
|
2441
2613
|
ConsoleSource,
|
|
2442
2614
|
ConsoleType,
|