@getlimelight/sdk 0.3.0 → 0.4.0
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 +10 -19
- package/dist/index.d.mts +80 -1
- package/dist/index.d.ts +80 -1
- package/dist/index.js +409 -42
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +409 -42
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -258,7 +258,7 @@ var SENSITIVE_HEADERS = [
|
|
|
258
258
|
var LIMELIGHT_WEB_WSS_URL = "wss://api.getlimelight.io";
|
|
259
259
|
var LIMELIGHT_DESKTOP_WSS_URL = "ws://localhost:8484";
|
|
260
260
|
var WS_PATH = "/limelight";
|
|
261
|
-
var SDK_VERSION = true ? "0.
|
|
261
|
+
var SDK_VERSION = true ? "0.4.0" : "test-version";
|
|
262
262
|
var RENDER_THRESHOLDS = {
|
|
263
263
|
HOT_VELOCITY: 5,
|
|
264
264
|
HIGH_RENDER_COUNT: 50,
|
|
@@ -287,11 +287,11 @@ var redactSensitiveHeaders = (headers) => {
|
|
|
287
287
|
// src/helpers/safety/safeStringify.ts
|
|
288
288
|
var safeStringify = (value, maxDepth = 10, pretty = false) => {
|
|
289
289
|
const seen = /* @__PURE__ */ new WeakMap();
|
|
290
|
-
const process2 = (val, currentDepth) => {
|
|
290
|
+
const process2 = (val, currentDepth, path) => {
|
|
291
291
|
if (val === null) return null;
|
|
292
292
|
if (val === void 0) return "[undefined]";
|
|
293
293
|
if (typeof val === "bigint") return `${val}n`;
|
|
294
|
-
if (typeof val === "symbol") return val.
|
|
294
|
+
if (typeof val === "symbol") return `[Symbol: ${val.description || ""}]`;
|
|
295
295
|
if (typeof val === "function") {
|
|
296
296
|
return `[Function: ${val.name || "anonymous"}]`;
|
|
297
297
|
}
|
|
@@ -299,52 +299,113 @@ var safeStringify = (value, maxDepth = 10, pretty = false) => {
|
|
|
299
299
|
if (currentDepth >= maxDepth) {
|
|
300
300
|
return "[Max Depth]";
|
|
301
301
|
}
|
|
302
|
-
|
|
303
|
-
|
|
302
|
+
const seenPath = seen.get(val);
|
|
303
|
+
if (seenPath !== void 0) {
|
|
304
|
+
return `[Circular \u2192 ${seenPath}]`;
|
|
304
305
|
}
|
|
305
|
-
seen.set(val,
|
|
306
|
+
seen.set(val, path || "root");
|
|
306
307
|
if (val instanceof Error) {
|
|
307
308
|
return {
|
|
308
309
|
__type: "Error",
|
|
309
310
|
name: val.name,
|
|
310
311
|
message: val.message,
|
|
311
|
-
stack: val.stack
|
|
312
|
+
stack: val.stack,
|
|
313
|
+
...Object.fromEntries(
|
|
314
|
+
Object.entries(val).filter(
|
|
315
|
+
([k]) => !["name", "message", "stack"].includes(k)
|
|
316
|
+
)
|
|
317
|
+
)
|
|
312
318
|
};
|
|
313
319
|
}
|
|
314
320
|
if (val instanceof Date) {
|
|
315
|
-
return val.toISOString();
|
|
321
|
+
return { __type: "Date", value: val.toISOString() };
|
|
316
322
|
}
|
|
317
323
|
if (val instanceof RegExp) {
|
|
318
|
-
return val.toString();
|
|
324
|
+
return { __type: "RegExp", value: val.toString() };
|
|
319
325
|
}
|
|
320
326
|
if (val instanceof Map) {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
+
return {
|
|
328
|
+
__type: "Map",
|
|
329
|
+
size: val.size,
|
|
330
|
+
entries: Array.from(val.entries()).map(([k, v], i) => [
|
|
331
|
+
process2(k, currentDepth + 1, `${path}.Map[${i}].key`),
|
|
332
|
+
process2(v, currentDepth + 1, `${path}.Map[${i}].value`)
|
|
333
|
+
])
|
|
334
|
+
};
|
|
327
335
|
}
|
|
328
336
|
if (val instanceof Set) {
|
|
329
|
-
return
|
|
337
|
+
return {
|
|
338
|
+
__type: "Set",
|
|
339
|
+
size: val.size,
|
|
340
|
+
values: Array.from(val).map(
|
|
341
|
+
(v, i) => process2(v, currentDepth + 1, `${path}.Set[${i}]`)
|
|
342
|
+
)
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
if (val instanceof WeakMap) {
|
|
346
|
+
return { __type: "WeakMap", note: "[Contents not enumerable]" };
|
|
347
|
+
}
|
|
348
|
+
if (val instanceof WeakSet) {
|
|
349
|
+
return { __type: "WeakSet", note: "[Contents not enumerable]" };
|
|
350
|
+
}
|
|
351
|
+
if (val instanceof Promise) {
|
|
352
|
+
return { __type: "Promise", note: "[Pending state not accessible]" };
|
|
353
|
+
}
|
|
354
|
+
if (val instanceof ArrayBuffer) {
|
|
355
|
+
return { __type: "ArrayBuffer", byteLength: val.byteLength };
|
|
330
356
|
}
|
|
331
357
|
if (ArrayBuffer.isView(val)) {
|
|
332
|
-
|
|
358
|
+
const typedArray = val;
|
|
359
|
+
return {
|
|
360
|
+
__type: val.constructor.name,
|
|
361
|
+
length: typedArray.length ?? typedArray.byteLength,
|
|
362
|
+
preview: typedArray.length <= 10 ? Array.from(typedArray.slice?.(0, 10) ?? []) : `[${typedArray.length} items]`
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
if (typeof URL !== "undefined" && val instanceof URL) {
|
|
366
|
+
return { __type: "URL", href: val.href };
|
|
367
|
+
}
|
|
368
|
+
if (typeof URLSearchParams !== "undefined" && val instanceof URLSearchParams) {
|
|
369
|
+
return {
|
|
370
|
+
__type: "URLSearchParams",
|
|
371
|
+
entries: Object.fromEntries(val.entries())
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
if (val && typeof val.$$typeof === "symbol" && String(val.$$typeof).includes("react.element")) {
|
|
375
|
+
return {
|
|
376
|
+
__type: "ReactElement",
|
|
377
|
+
type: typeof val.type === "function" ? val.type.name || "Component" : val.type || "unknown",
|
|
378
|
+
key: val.key
|
|
379
|
+
};
|
|
333
380
|
}
|
|
334
381
|
if (Array.isArray(val)) {
|
|
335
|
-
return val.map(
|
|
382
|
+
return val.map(
|
|
383
|
+
(item, i) => process2(item, currentDepth + 1, `${path}[${i}]`)
|
|
384
|
+
);
|
|
336
385
|
}
|
|
337
386
|
const result = {};
|
|
387
|
+
const proto = Object.getPrototypeOf(val);
|
|
388
|
+
if (proto && proto.constructor && proto.constructor.name !== "Object") {
|
|
389
|
+
result.__type = proto.constructor.name;
|
|
390
|
+
}
|
|
338
391
|
for (const key in val) {
|
|
339
392
|
if (Object.prototype.hasOwnProperty.call(val, key)) {
|
|
340
|
-
|
|
393
|
+
try {
|
|
394
|
+
result[key] = process2(
|
|
395
|
+
val[key],
|
|
396
|
+
currentDepth + 1,
|
|
397
|
+
`${path}.${key}`
|
|
398
|
+
);
|
|
399
|
+
} catch (e) {
|
|
400
|
+
result[key] = `[Error accessing property: ${e instanceof Error ? e.message : String(e)}]`;
|
|
401
|
+
}
|
|
341
402
|
}
|
|
342
403
|
}
|
|
343
404
|
return result;
|
|
344
405
|
};
|
|
345
406
|
try {
|
|
346
|
-
const processed = process2(value, 0);
|
|
347
|
-
return JSON.stringify(processed, null, pretty ? 2 : 0);
|
|
407
|
+
const processed = process2(value, 0, "root");
|
|
408
|
+
return JSON.stringify(processed, null, pretty ? 2 : void 0);
|
|
348
409
|
} catch (error) {
|
|
349
410
|
return JSON.stringify({
|
|
350
411
|
__error: "Stringification failed",
|
|
@@ -507,7 +568,9 @@ var ConsoleInterceptor = class {
|
|
|
507
568
|
*/
|
|
508
569
|
setup(config) {
|
|
509
570
|
if (this.isSetup) {
|
|
510
|
-
|
|
571
|
+
if (this.config?.internalLoggingEnabled) {
|
|
572
|
+
console.warn("[Limelight] Console interceptor already set up");
|
|
573
|
+
}
|
|
511
574
|
return;
|
|
512
575
|
}
|
|
513
576
|
this.isSetup = true;
|
|
@@ -591,7 +654,9 @@ var ConsoleInterceptor = class {
|
|
|
591
654
|
*/
|
|
592
655
|
cleanup() {
|
|
593
656
|
if (!this.isSetup) {
|
|
594
|
-
|
|
657
|
+
if (this.config?.internalLoggingEnabled) {
|
|
658
|
+
console.warn("[Limelight] Console interceptor not set up");
|
|
659
|
+
}
|
|
595
660
|
return;
|
|
596
661
|
}
|
|
597
662
|
this.isSetup = false;
|
|
@@ -631,7 +696,9 @@ var NetworkInterceptor = class {
|
|
|
631
696
|
*/
|
|
632
697
|
setup(config) {
|
|
633
698
|
if (this.isSetup) {
|
|
634
|
-
|
|
699
|
+
if (this.config?.internalLoggingEnabled) {
|
|
700
|
+
console.warn("[Limelight] Network interceptor already set up");
|
|
701
|
+
}
|
|
635
702
|
return;
|
|
636
703
|
}
|
|
637
704
|
this.isSetup = true;
|
|
@@ -667,9 +734,11 @@ var NetworkInterceptor = class {
|
|
|
667
734
|
}
|
|
668
735
|
} catch {
|
|
669
736
|
requestBodyToSerialize = void 0;
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
737
|
+
if (self.config?.internalLoggingEnabled) {
|
|
738
|
+
console.warn(
|
|
739
|
+
"[Limelight] Failed to read request body from Request object"
|
|
740
|
+
);
|
|
741
|
+
}
|
|
673
742
|
}
|
|
674
743
|
}
|
|
675
744
|
const requestBody = serializeBody(
|
|
@@ -786,7 +855,9 @@ var NetworkInterceptor = class {
|
|
|
786
855
|
*/
|
|
787
856
|
cleanup() {
|
|
788
857
|
if (!this.isSetup) {
|
|
789
|
-
|
|
858
|
+
if (this.config?.internalLoggingEnabled) {
|
|
859
|
+
console.warn("[Limelight] Network interceptor not set up");
|
|
860
|
+
}
|
|
790
861
|
return;
|
|
791
862
|
}
|
|
792
863
|
this.isSetup = false;
|
|
@@ -817,7 +888,9 @@ var XHRInterceptor = class {
|
|
|
817
888
|
*/
|
|
818
889
|
setup(config) {
|
|
819
890
|
if (this.isSetup) {
|
|
820
|
-
|
|
891
|
+
if (this.config?.internalLoggingEnabled) {
|
|
892
|
+
console.warn("[Limelight] XHR interceptor already set up");
|
|
893
|
+
}
|
|
821
894
|
return;
|
|
822
895
|
}
|
|
823
896
|
this.isSetup = true;
|
|
@@ -1029,7 +1102,9 @@ var XHRInterceptor = class {
|
|
|
1029
1102
|
*/
|
|
1030
1103
|
cleanup() {
|
|
1031
1104
|
if (!this.isSetup) {
|
|
1032
|
-
|
|
1105
|
+
if (this.config?.internalLoggingEnabled) {
|
|
1106
|
+
console.warn("[Limelight] XHR interceptor not set up");
|
|
1107
|
+
}
|
|
1033
1108
|
return;
|
|
1034
1109
|
}
|
|
1035
1110
|
this.isSetup = false;
|
|
@@ -1061,12 +1136,16 @@ var RenderInterceptor = class {
|
|
|
1061
1136
|
}
|
|
1062
1137
|
setup(config) {
|
|
1063
1138
|
if (this.isSetup) {
|
|
1064
|
-
|
|
1139
|
+
if (this.config?.internalLoggingEnabled) {
|
|
1140
|
+
console.warn("[Limelight] Render interceptor already set up");
|
|
1141
|
+
}
|
|
1065
1142
|
return;
|
|
1066
1143
|
}
|
|
1067
1144
|
this.config = config;
|
|
1068
1145
|
if (!this.installHook()) {
|
|
1069
|
-
|
|
1146
|
+
if (this.config?.internalLoggingEnabled) {
|
|
1147
|
+
console.warn("[Limelight] Failed to install render hook");
|
|
1148
|
+
}
|
|
1070
1149
|
return;
|
|
1071
1150
|
}
|
|
1072
1151
|
this.snapshotTimer = setInterval(() => {
|
|
@@ -1074,6 +1153,12 @@ var RenderInterceptor = class {
|
|
|
1074
1153
|
}, RENDER_THRESHOLDS.SNAPSHOT_INTERVAL_MS);
|
|
1075
1154
|
this.isSetup = true;
|
|
1076
1155
|
}
|
|
1156
|
+
resetProfiles() {
|
|
1157
|
+
this.profiles.clear();
|
|
1158
|
+
this.pendingUnmounts = [];
|
|
1159
|
+
this.currentCommitComponents.clear();
|
|
1160
|
+
this.componentIdCounter = 0;
|
|
1161
|
+
}
|
|
1077
1162
|
installHook() {
|
|
1078
1163
|
const globalObj = typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : null;
|
|
1079
1164
|
if (!globalObj) return false;
|
|
@@ -1129,7 +1214,7 @@ var RenderInterceptor = class {
|
|
|
1129
1214
|
this.countRenderedComponents(root.current);
|
|
1130
1215
|
this.walkFiberTree(root.current, null, 0);
|
|
1131
1216
|
} catch (error) {
|
|
1132
|
-
if (
|
|
1217
|
+
if (this.config?.internalLoggingEnabled) {
|
|
1133
1218
|
console.warn("[Limelight] Error processing fiber tree:", error);
|
|
1134
1219
|
}
|
|
1135
1220
|
}
|
|
@@ -1594,6 +1679,259 @@ var RenderInterceptor = class {
|
|
|
1594
1679
|
}
|
|
1595
1680
|
};
|
|
1596
1681
|
|
|
1682
|
+
// src/limelight/interceptors/StateInterceptor.ts
|
|
1683
|
+
var StateInterceptor = class {
|
|
1684
|
+
sendMessage;
|
|
1685
|
+
getSessionId;
|
|
1686
|
+
stores = /* @__PURE__ */ new Map();
|
|
1687
|
+
config = null;
|
|
1688
|
+
constructor(sendMessage, getSessionId) {
|
|
1689
|
+
this.sendMessage = sendMessage;
|
|
1690
|
+
this.getSessionId = getSessionId;
|
|
1691
|
+
}
|
|
1692
|
+
setup(config) {
|
|
1693
|
+
this.config = config;
|
|
1694
|
+
if (!config.stores) return;
|
|
1695
|
+
if (config.enableStateInspector === false) return;
|
|
1696
|
+
for (const [name, store] of Object.entries(config.stores)) {
|
|
1697
|
+
this.registerStore(name, store);
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
/**
|
|
1701
|
+
* Register a store for inspection.
|
|
1702
|
+
* Can be called manually via Limelight.addStore() for dynamic registration.
|
|
1703
|
+
*/
|
|
1704
|
+
registerStore(name, store) {
|
|
1705
|
+
if (this.stores.has(name)) {
|
|
1706
|
+
console.warn(`[Limelight] Store "${name}" already registered`);
|
|
1707
|
+
return;
|
|
1708
|
+
}
|
|
1709
|
+
const library = this.detectLibrary(store);
|
|
1710
|
+
if (!library) {
|
|
1711
|
+
console.warn(
|
|
1712
|
+
`[Limelight] Could not detect store type for "${name}". Expected Zustand or Redux store.`
|
|
1713
|
+
);
|
|
1714
|
+
return;
|
|
1715
|
+
}
|
|
1716
|
+
const state = this.getState(store, library);
|
|
1717
|
+
const initEvent = {
|
|
1718
|
+
phase: "STATE:INIT" /* INIT */,
|
|
1719
|
+
sessionId: this.getSessionId(),
|
|
1720
|
+
timestamp: Date.now(),
|
|
1721
|
+
data: {
|
|
1722
|
+
storeId: name,
|
|
1723
|
+
library,
|
|
1724
|
+
state
|
|
1725
|
+
}
|
|
1726
|
+
};
|
|
1727
|
+
this.emitEvent(initEvent);
|
|
1728
|
+
const unsubscribe = this.subscribe(store, library, name);
|
|
1729
|
+
this.stores.set(name, { name, library, unsubscribe });
|
|
1730
|
+
}
|
|
1731
|
+
/**
|
|
1732
|
+
* Unregister a store and stop listening to changes.
|
|
1733
|
+
*/
|
|
1734
|
+
unregisterStore(name) {
|
|
1735
|
+
const store = this.stores.get(name);
|
|
1736
|
+
if (store) {
|
|
1737
|
+
store.unsubscribe();
|
|
1738
|
+
this.stores.delete(name);
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
/**
|
|
1742
|
+
* Emit an event, applying beforeSend hook if configured
|
|
1743
|
+
*/
|
|
1744
|
+
emitEvent(event) {
|
|
1745
|
+
if (this.config?.beforeSend) {
|
|
1746
|
+
const modifiedEvent = this.config.beforeSend(event);
|
|
1747
|
+
if (!modifiedEvent) {
|
|
1748
|
+
return;
|
|
1749
|
+
}
|
|
1750
|
+
if (modifiedEvent.phase !== "STATE:INIT" /* INIT */ && modifiedEvent.phase !== "STATE:UPDATE" /* UPDATE */) {
|
|
1751
|
+
console.error("[Limelight] beforeSend must return same event type");
|
|
1752
|
+
return;
|
|
1753
|
+
}
|
|
1754
|
+
this.sendMessage(modifiedEvent);
|
|
1755
|
+
} else {
|
|
1756
|
+
this.sendMessage(event);
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
/**
|
|
1760
|
+
* Detect whether a store is Zustand or Redux
|
|
1761
|
+
*/
|
|
1762
|
+
detectLibrary(store) {
|
|
1763
|
+
if (!store || typeof store !== "function" && typeof store !== "object") {
|
|
1764
|
+
return null;
|
|
1765
|
+
}
|
|
1766
|
+
if (typeof store === "object" && "dispatch" in store && "getState" in store && "subscribe" in store && typeof store.dispatch === "function") {
|
|
1767
|
+
return "redux" /* REDUX */;
|
|
1768
|
+
}
|
|
1769
|
+
if (typeof store === "function" && "getState" in store && "subscribe" in store && typeof store.getState === "function") {
|
|
1770
|
+
return "zustand" /* ZUSTAND */;
|
|
1771
|
+
}
|
|
1772
|
+
if (typeof store === "object" && "getState" in store && "setState" in store && "subscribe" in store && !("dispatch" in store)) {
|
|
1773
|
+
return "zustand" /* ZUSTAND */;
|
|
1774
|
+
}
|
|
1775
|
+
return null;
|
|
1776
|
+
}
|
|
1777
|
+
/**
|
|
1778
|
+
* Get current state from a store
|
|
1779
|
+
*/
|
|
1780
|
+
getState(store, library) {
|
|
1781
|
+
const storeAny = store;
|
|
1782
|
+
return storeAny.getState();
|
|
1783
|
+
}
|
|
1784
|
+
/**
|
|
1785
|
+
* Subscribe to store changes
|
|
1786
|
+
*/
|
|
1787
|
+
subscribe(store, library, storeName) {
|
|
1788
|
+
const storeAny = store;
|
|
1789
|
+
if (library === "zustand" /* ZUSTAND */) {
|
|
1790
|
+
return this.subscribeZustand(storeAny, storeName);
|
|
1791
|
+
} else {
|
|
1792
|
+
return this.subscribeRedux(storeAny, storeName);
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
/**
|
|
1796
|
+
* Subscribe to Zustand store changes
|
|
1797
|
+
*/
|
|
1798
|
+
subscribeZustand(store, storeName) {
|
|
1799
|
+
return store.subscribe((state, prevState) => {
|
|
1800
|
+
const action = this.inferZustandAction(state, prevState);
|
|
1801
|
+
const stackTrace = this.captureStackTrace();
|
|
1802
|
+
const updateEvent = {
|
|
1803
|
+
phase: "STATE:UPDATE" /* UPDATE */,
|
|
1804
|
+
sessionId: this.getSessionId(),
|
|
1805
|
+
timestamp: Date.now(),
|
|
1806
|
+
data: {
|
|
1807
|
+
storeId: storeName,
|
|
1808
|
+
library: "zustand" /* ZUSTAND */,
|
|
1809
|
+
state,
|
|
1810
|
+
action,
|
|
1811
|
+
stackTrace
|
|
1812
|
+
}
|
|
1813
|
+
};
|
|
1814
|
+
this.emitEvent(updateEvent);
|
|
1815
|
+
});
|
|
1816
|
+
}
|
|
1817
|
+
/**
|
|
1818
|
+
* Subscribe to Redux store changes
|
|
1819
|
+
*/
|
|
1820
|
+
subscribeRedux(store, storeName) {
|
|
1821
|
+
let lastAction = { type: "@@INIT" };
|
|
1822
|
+
const originalDispatch = store.dispatch;
|
|
1823
|
+
store.dispatch = (action) => {
|
|
1824
|
+
lastAction = {
|
|
1825
|
+
type: action?.type || "unknown",
|
|
1826
|
+
payload: action?.payload
|
|
1827
|
+
};
|
|
1828
|
+
return originalDispatch(action);
|
|
1829
|
+
};
|
|
1830
|
+
const unsubscribe = store.subscribe(() => {
|
|
1831
|
+
const newState = store.getState();
|
|
1832
|
+
const stackTrace = this.captureStackTrace();
|
|
1833
|
+
const updateEvent = {
|
|
1834
|
+
phase: "STATE:UPDATE" /* UPDATE */,
|
|
1835
|
+
sessionId: this.getSessionId(),
|
|
1836
|
+
timestamp: Date.now(),
|
|
1837
|
+
data: {
|
|
1838
|
+
storeId: storeName,
|
|
1839
|
+
library: "redux" /* REDUX */,
|
|
1840
|
+
state: newState,
|
|
1841
|
+
action: lastAction,
|
|
1842
|
+
stackTrace
|
|
1843
|
+
}
|
|
1844
|
+
};
|
|
1845
|
+
this.emitEvent(updateEvent);
|
|
1846
|
+
});
|
|
1847
|
+
return () => {
|
|
1848
|
+
unsubscribe();
|
|
1849
|
+
store.dispatch = originalDispatch;
|
|
1850
|
+
};
|
|
1851
|
+
}
|
|
1852
|
+
/**
|
|
1853
|
+
* Infer action name from stack trace for Zustand
|
|
1854
|
+
*/
|
|
1855
|
+
inferZustandAction(state, prevState) {
|
|
1856
|
+
const actionType = this.parseActionFromStack(this.captureStackTrace());
|
|
1857
|
+
const payload = this.computePartialState(state, prevState);
|
|
1858
|
+
return {
|
|
1859
|
+
type: actionType,
|
|
1860
|
+
payload
|
|
1861
|
+
};
|
|
1862
|
+
}
|
|
1863
|
+
/**
|
|
1864
|
+
* Parse function name from stack trace
|
|
1865
|
+
*/
|
|
1866
|
+
parseActionFromStack(stack) {
|
|
1867
|
+
if (!stack) return "set";
|
|
1868
|
+
const lines = stack.split("\n");
|
|
1869
|
+
for (const line of lines) {
|
|
1870
|
+
if (line.includes("node_modules/zustand")) continue;
|
|
1871
|
+
if (line.includes("node_modules/immer")) continue;
|
|
1872
|
+
if (line.includes("StateInterceptor")) continue;
|
|
1873
|
+
if (line.includes("limelight")) continue;
|
|
1874
|
+
const v8Match = line.match(/at\s+(?:Object\.)?(\w+)\s+\(/) || line.match(/at\s+(\w+)\s*\[/) || line.match(/at\s+(\w+)/);
|
|
1875
|
+
const hermesMatch = line.match(/^(\w+)@/);
|
|
1876
|
+
const match = v8Match || hermesMatch;
|
|
1877
|
+
if (match && match[1]) {
|
|
1878
|
+
const name = match[1];
|
|
1879
|
+
if (![
|
|
1880
|
+
"anonymous",
|
|
1881
|
+
"Object",
|
|
1882
|
+
"Array",
|
|
1883
|
+
"Function",
|
|
1884
|
+
"eval",
|
|
1885
|
+
"Error"
|
|
1886
|
+
].includes(name)) {
|
|
1887
|
+
return name;
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
return "set";
|
|
1892
|
+
}
|
|
1893
|
+
/**
|
|
1894
|
+
* Compute what keys changed between states (shallow)
|
|
1895
|
+
*/
|
|
1896
|
+
computePartialState(state, prevState) {
|
|
1897
|
+
if (typeof state !== "object" || state === null || typeof prevState !== "object" || prevState === null) {
|
|
1898
|
+
return state;
|
|
1899
|
+
}
|
|
1900
|
+
const partial = {};
|
|
1901
|
+
const stateObj = state;
|
|
1902
|
+
const prevObj = prevState;
|
|
1903
|
+
for (const key of Object.keys(stateObj)) {
|
|
1904
|
+
if (stateObj[key] !== prevObj[key]) {
|
|
1905
|
+
partial[key] = stateObj[key];
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
if (Object.keys(partial).length === 0) {
|
|
1909
|
+
return state;
|
|
1910
|
+
}
|
|
1911
|
+
return partial;
|
|
1912
|
+
}
|
|
1913
|
+
/**
|
|
1914
|
+
* Capture current stack trace
|
|
1915
|
+
*/
|
|
1916
|
+
captureStackTrace() {
|
|
1917
|
+
try {
|
|
1918
|
+
const err = new Error();
|
|
1919
|
+
return err.stack;
|
|
1920
|
+
} catch {
|
|
1921
|
+
return void 0;
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
/**
|
|
1925
|
+
* Cleanup all subscriptions
|
|
1926
|
+
*/
|
|
1927
|
+
cleanup() {
|
|
1928
|
+
for (const [, store] of this.stores) {
|
|
1929
|
+
store.unsubscribe();
|
|
1930
|
+
}
|
|
1931
|
+
this.stores.clear();
|
|
1932
|
+
}
|
|
1933
|
+
};
|
|
1934
|
+
|
|
1597
1935
|
// src/limelight/LimelightClient.ts
|
|
1598
1936
|
var LimelightClient = class {
|
|
1599
1937
|
ws = null;
|
|
@@ -1609,6 +1947,7 @@ var LimelightClient = class {
|
|
|
1609
1947
|
xhrInterceptor;
|
|
1610
1948
|
consoleInterceptor;
|
|
1611
1949
|
renderInterceptor;
|
|
1950
|
+
stateInterceptor;
|
|
1612
1951
|
constructor() {
|
|
1613
1952
|
this.networkInterceptor = new NetworkInterceptor(
|
|
1614
1953
|
this.sendMessage.bind(this),
|
|
@@ -1626,6 +1965,10 @@ var LimelightClient = class {
|
|
|
1626
1965
|
this.sendMessage.bind(this),
|
|
1627
1966
|
() => this.sessionId
|
|
1628
1967
|
);
|
|
1968
|
+
this.stateInterceptor = new StateInterceptor(
|
|
1969
|
+
this.sendMessage.bind(this),
|
|
1970
|
+
() => this.sessionId
|
|
1971
|
+
);
|
|
1629
1972
|
}
|
|
1630
1973
|
/**
|
|
1631
1974
|
* Configures the Limelight client with the provided settings.
|
|
@@ -1646,7 +1989,9 @@ var LimelightClient = class {
|
|
|
1646
1989
|
enableNetworkInspector: config?.enableNetworkInspector ?? true,
|
|
1647
1990
|
enableConsole: config?.enableConsole ?? true,
|
|
1648
1991
|
enableGraphQL: config?.enableGraphQL ?? true,
|
|
1649
|
-
enableRenderInspector: config?.enableRenderInspector ?? true
|
|
1992
|
+
enableRenderInspector: config?.enableRenderInspector ?? true,
|
|
1993
|
+
enableStateInspector: config?.enableStateInspector ?? true,
|
|
1994
|
+
internalLoggingEnabled: config?.internalLoggingEnabled ?? false
|
|
1650
1995
|
};
|
|
1651
1996
|
if (!this.config?.enabled) {
|
|
1652
1997
|
return;
|
|
@@ -1663,8 +2008,13 @@ var LimelightClient = class {
|
|
|
1663
2008
|
if (this.config.enableRenderInspector) {
|
|
1664
2009
|
this.renderInterceptor.setup(this.config);
|
|
1665
2010
|
}
|
|
2011
|
+
if (this.config.stores && this.config.enableStateInspector) {
|
|
2012
|
+
this.stateInterceptor.setup(this.config);
|
|
2013
|
+
}
|
|
1666
2014
|
} catch (error) {
|
|
1667
|
-
|
|
2015
|
+
if (this.config?.internalLoggingEnabled) {
|
|
2016
|
+
console.error("[Limelight] Failed to setup interceptors:", error);
|
|
2017
|
+
}
|
|
1668
2018
|
}
|
|
1669
2019
|
}
|
|
1670
2020
|
/**
|
|
@@ -1684,7 +2034,9 @@ var LimelightClient = class {
|
|
|
1684
2034
|
return;
|
|
1685
2035
|
}
|
|
1686
2036
|
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
1687
|
-
|
|
2037
|
+
if (this.config?.internalLoggingEnabled) {
|
|
2038
|
+
console.warn("[Limelight] Already connected. Call disconnect() first.");
|
|
2039
|
+
}
|
|
1688
2040
|
return;
|
|
1689
2041
|
}
|
|
1690
2042
|
if (this.ws) {
|
|
@@ -1699,7 +2051,9 @@ var LimelightClient = class {
|
|
|
1699
2051
|
}
|
|
1700
2052
|
const { serverUrl, appName, platform } = this.config;
|
|
1701
2053
|
if (!serverUrl) {
|
|
1702
|
-
|
|
2054
|
+
if (this.config?.internalLoggingEnabled) {
|
|
2055
|
+
console.error("[Limelight] serverUrl missing in configuration.");
|
|
2056
|
+
}
|
|
1703
2057
|
return;
|
|
1704
2058
|
}
|
|
1705
2059
|
try {
|
|
@@ -1721,13 +2075,17 @@ var LimelightClient = class {
|
|
|
1721
2075
|
this.sendMessage(message);
|
|
1722
2076
|
};
|
|
1723
2077
|
this.ws.onerror = (error) => {
|
|
1724
|
-
|
|
2078
|
+
if (this.config?.internalLoggingEnabled) {
|
|
2079
|
+
console.error("[Limelight] WebSocket error:", error);
|
|
2080
|
+
}
|
|
1725
2081
|
};
|
|
1726
2082
|
this.ws.onclose = () => {
|
|
1727
2083
|
this.attemptReconnect();
|
|
1728
2084
|
};
|
|
1729
2085
|
} catch (error) {
|
|
1730
|
-
|
|
2086
|
+
if (this.config?.internalLoggingEnabled) {
|
|
2087
|
+
console.error("[Limelight] Failed to connect:", error);
|
|
2088
|
+
}
|
|
1731
2089
|
this.attemptReconnect();
|
|
1732
2090
|
}
|
|
1733
2091
|
}
|
|
@@ -1769,7 +2127,9 @@ var LimelightClient = class {
|
|
|
1769
2127
|
try {
|
|
1770
2128
|
this.ws.send(safeStringify(message));
|
|
1771
2129
|
} catch (error) {
|
|
1772
|
-
|
|
2130
|
+
if (this.config?.internalLoggingEnabled) {
|
|
2131
|
+
console.error("[Limelight] Failed to send queued message:", error);
|
|
2132
|
+
}
|
|
1773
2133
|
}
|
|
1774
2134
|
}
|
|
1775
2135
|
}
|
|
@@ -1788,7 +2148,9 @@ var LimelightClient = class {
|
|
|
1788
2148
|
try {
|
|
1789
2149
|
this.ws.send(safeStringify(message));
|
|
1790
2150
|
} catch (error) {
|
|
1791
|
-
|
|
2151
|
+
if (this.config?.internalLoggingEnabled) {
|
|
2152
|
+
console.error("[Limelight] Failed to send message:", error);
|
|
2153
|
+
}
|
|
1792
2154
|
this.messageQueue.push(message);
|
|
1793
2155
|
}
|
|
1794
2156
|
} else {
|
|
@@ -1796,7 +2158,11 @@ var LimelightClient = class {
|
|
|
1796
2158
|
}
|
|
1797
2159
|
} else {
|
|
1798
2160
|
if (this.messageQueue.length >= this.maxQueueSize) {
|
|
1799
|
-
|
|
2161
|
+
if (this.config?.internalLoggingEnabled) {
|
|
2162
|
+
console.warn(
|
|
2163
|
+
"[Limelight] Message queue full, dropping oldest message"
|
|
2164
|
+
);
|
|
2165
|
+
}
|
|
1800
2166
|
this.messageQueue.shift();
|
|
1801
2167
|
}
|
|
1802
2168
|
this.messageQueue.push(message);
|
|
@@ -1837,6 +2203,7 @@ var LimelightClient = class {
|
|
|
1837
2203
|
this.xhrInterceptor.cleanup();
|
|
1838
2204
|
this.consoleInterceptor.cleanup();
|
|
1839
2205
|
this.renderInterceptor.cleanup();
|
|
2206
|
+
this.stateInterceptor.cleanup();
|
|
1840
2207
|
this.reconnectAttempts = 0;
|
|
1841
2208
|
this.messageQueue = [];
|
|
1842
2209
|
}
|