@react-devtools-plus/kit 0.9.4 → 0.9.5

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.cjs CHANGED
@@ -105,9 +105,28 @@ function getFiberFromElement(element) {
105
105
  //#endregion
106
106
  //#region src/core/context/index.ts
107
107
  /**
108
+ * Properties that can cause prototype pollution and should be filtered out
109
+ * SuperJSON will throw an error if these properties are present in the object
110
+ */
111
+ const DANGEROUS_PROPERTIES$1 = new Set([
112
+ "constructor",
113
+ "__proto__",
114
+ "prototype"
115
+ ]);
116
+ /**
117
+ * Check if a property key is safe to serialize (not a prototype pollution risk)
118
+ */
119
+ function isSafePropertyKey$1(key) {
120
+ return !DANGEROUS_PROPERTIES$1.has(key);
121
+ }
122
+ /**
108
123
  * Serialize a value into a displayable PropValue
124
+ * @param value - The value to serialize
125
+ * @param depth - Current recursion depth
126
+ * @param maxDepth - Maximum recursion depth
127
+ * @param context - Optional context to collect warnings about filtered properties
109
128
  */
110
- function serializeValue$1(value, depth = 0, maxDepth = 6) {
129
+ function serializeValue$1(value, depth = 0, maxDepth = 6, context) {
111
130
  if (value === null) return {
112
131
  type: "null",
113
132
  value: "null"
@@ -150,7 +169,7 @@ function serializeValue$1(value, depth = 0, maxDepth = 6) {
150
169
  if (Array.isArray(value)) {
151
170
  const children = {};
152
171
  value.forEach((item, index) => {
153
- children[String(index)] = serializeValue$1(item, depth + 1, maxDepth);
172
+ children[String(index)] = serializeValue$1(item, depth + 1, maxDepth, context);
154
173
  });
155
174
  return {
156
175
  type: "array",
@@ -166,10 +185,13 @@ function serializeValue$1(value, depth = 0, maxDepth = 6) {
166
185
  value: `<${((_value$type = value.type) === null || _value$type === void 0 ? void 0 : _value$type.displayName) || ((_value$type2 = value.type) === null || _value$type2 === void 0 ? void 0 : _value$type2.name) || value.type || "Unknown"} />`
167
186
  };
168
187
  }
169
- const keys = Object.keys(value);
188
+ const allKeys = Object.keys(value);
189
+ const safeKeys = allKeys.filter(isSafePropertyKey$1);
190
+ const filteredKeys = allKeys.filter((key) => !isSafePropertyKey$1(key));
191
+ if (context && filteredKeys.length > 0) filteredKeys.forEach((key) => context.filteredProperties.add(key));
170
192
  const children = {};
171
- for (const key of keys) try {
172
- children[key] = serializeValue$1(value[key], depth + 1, maxDepth);
193
+ for (const key of safeKeys) try {
194
+ children[key] = serializeValue$1(value[key], depth + 1, maxDepth, context);
173
195
  } catch (_unused) {
174
196
  children[key] = {
175
197
  type: "unknown",
@@ -179,7 +201,7 @@ function serializeValue$1(value, depth = 0, maxDepth = 6) {
179
201
  return {
180
202
  type: "object",
181
203
  value: `Object`,
182
- preview: keys.length > 0 ? `{${keys.slice(0, 3).join(", ")}${keys.length > 3 ? ", ..." : ""}}` : "{}",
204
+ preview: safeKeys.length > 0 ? `{${safeKeys.slice(0, 3).join(", ")}${safeKeys.length > 3 ? ", ..." : ""}}` : "{}",
183
205
  children: Object.keys(children).length > 0 ? children : void 0
184
206
  };
185
207
  }
@@ -189,6 +211,23 @@ function serializeValue$1(value, depth = 0, maxDepth = 6) {
189
211
  };
190
212
  }
191
213
  /**
214
+ * Serialize a context value and collect any warnings
215
+ */
216
+ function serializeContextValue(value) {
217
+ const context = { filteredProperties: /* @__PURE__ */ new Set() };
218
+ const propValue = serializeValue$1(value, 0, 6, context);
219
+ const warnings = [];
220
+ for (const property of context.filteredProperties) warnings.push({
221
+ type: "filtered_property",
222
+ property,
223
+ message: `Property "${property}" was hidden to prevent prototype pollution errors. This does not affect your application.`
224
+ });
225
+ return {
226
+ propValue,
227
+ warnings
228
+ };
229
+ }
230
+ /**
192
231
  * Get the display name of a Context
193
232
  */
194
233
  function getContextDisplayName(fiber) {
@@ -264,10 +303,11 @@ function buildProviderInfo(fiber, visited) {
264
303
  const consumers = [];
265
304
  if (fiber.child) findConsumers(fiber.child, contextType, consumers, /* @__PURE__ */ new WeakSet());
266
305
  const uniqueConsumers = consumers.filter((consumer, index, self) => index === self.findIndex((c) => c.id === consumer.id));
306
+ const { propValue, warnings } = serializeContextValue(getContextValue(fiber));
267
307
  const info = {
268
308
  id: getFiberId(fiber),
269
309
  name: getContextDisplayName(fiber),
270
- value: serializeValue$1(getContextValue(fiber)),
310
+ value: propValue,
271
311
  fiberId: getFiberId(fiber),
272
312
  consumerCount: uniqueConsumers.length,
273
313
  consumers: uniqueConsumers,
@@ -276,7 +316,8 @@ function buildProviderInfo(fiber, visited) {
276
316
  fileName: fiber._debugSource.fileName,
277
317
  lineNumber: fiber._debugSource.lineNumber,
278
318
  columnNumber: fiber._debugSource.columnNumber
279
- } : void 0
319
+ } : void 0,
320
+ warnings: warnings.length > 0 ? warnings : void 0
280
321
  };
281
322
  const findNestedProviders = (child) => {
282
323
  var _child$type;
@@ -361,6 +402,21 @@ function getAllContexts(root) {
361
402
  //#endregion
362
403
  //#region src/core/fiber/details.ts
363
404
  /**
405
+ * Properties that can cause prototype pollution and should be filtered out
406
+ * SuperJSON throws: "Detected property constructor. This is a property pollution risk"
407
+ */
408
+ const DANGEROUS_PROPERTIES = new Set([
409
+ "constructor",
410
+ "__proto__",
411
+ "prototype"
412
+ ]);
413
+ /**
414
+ * Check if a property key is safe to serialize (not a prototype pollution risk)
415
+ */
416
+ function isSafePropertyKey(key) {
417
+ return !DANGEROUS_PROPERTIES.has(key);
418
+ }
419
+ /**
364
420
  * Serialize a value into a displayable PropValue
365
421
  */
366
422
  function serializeValue(value, depth = 0, maxDepth = 8) {
@@ -422,7 +478,7 @@ function serializeValue(value, depth = 0, maxDepth = 8) {
422
478
  value: `<${((_value$type = value.type) === null || _value$type === void 0 ? void 0 : _value$type.displayName) || ((_value$type2 = value.type) === null || _value$type2 === void 0 ? void 0 : _value$type2.name) || value.type || "Unknown"} />`
423
479
  };
424
480
  }
425
- const keys = Object.keys(value);
481
+ const keys = Object.keys(value).filter(isSafePropertyKey);
426
482
  const children = {};
427
483
  for (const key of keys) try {
428
484
  children[key] = serializeValue(value[key], depth + 1, maxDepth);
@@ -1586,7 +1642,7 @@ function extractRouteFromElement(element, parentPath = "") {
1586
1642
  return routeInfo;
1587
1643
  }
1588
1644
  /**
1589
- * Extract routes from Routes component's children prop
1645
+ * Extract routes from Routes component's children prop (React Router v6)
1590
1646
  */
1591
1647
  function extractRoutesFromProps(props) {
1592
1648
  const routes = [];
@@ -1602,6 +1658,77 @@ function extractRoutesFromProps(props) {
1602
1658
  return routes;
1603
1659
  }
1604
1660
  /**
1661
+ * Get component name from React Router v5 Route props
1662
+ */
1663
+ function getV5ComponentName(props) {
1664
+ if (props.component) return props.component.name || props.component.displayName;
1665
+ if (props.render) return "render()";
1666
+ if (typeof props.children === "function") return "children()";
1667
+ }
1668
+ /**
1669
+ * Extract route info from a React Router v5 Route element (JSX props)
1670
+ */
1671
+ function extractRouteFromElementV5(element, parentPath = "") {
1672
+ if (!element || !element.props) return null;
1673
+ const props = element.props;
1674
+ const path = props.path || "*";
1675
+ let fullPath = path;
1676
+ if (path !== "*" && !path.startsWith("/") && parentPath) fullPath = `${parentPath}/${path}`.replace(/\/+/g, "/");
1677
+ const params = extractParamsFromPath(path);
1678
+ const childRoutes = [];
1679
+ if (props.children && typeof props.children !== "function") {
1680
+ const children = Array.isArray(props.children) ? props.children : [props.children];
1681
+ for (const child of children) {
1682
+ var _child$type3, _child$type4;
1683
+ if (child && child.type && (child.type.name === "Route" || ((_child$type3 = child.type) === null || _child$type3 === void 0 ? void 0 : _child$type3.displayName) === "Route")) {
1684
+ const childRoute = extractRouteFromElementV5(child, fullPath);
1685
+ if (childRoute) childRoutes.push(childRoute);
1686
+ }
1687
+ if (child && child.type && (child.type.name === "Switch" || ((_child$type4 = child.type) === null || _child$type4 === void 0 ? void 0 : _child$type4.displayName) === "Switch")) {
1688
+ const switchChildren = extractRoutesFromSwitchProps(child.props, fullPath);
1689
+ childRoutes.push(...switchChildren);
1690
+ }
1691
+ }
1692
+ }
1693
+ const hasChildren = childRoutes.length > 0;
1694
+ const routeInfo = {
1695
+ path: fullPath,
1696
+ element: getV5ComponentName(props),
1697
+ exact: props.exact === true,
1698
+ strict: props.strict === true,
1699
+ isLayout: hasChildren,
1700
+ isLazy: isLazyElement(props.component),
1701
+ params: params.length > 0 ? params : void 0
1702
+ };
1703
+ if (hasChildren) routeInfo.children = childRoutes;
1704
+ return routeInfo;
1705
+ }
1706
+ /**
1707
+ * Extract routes from Switch component's children prop (React Router v5)
1708
+ */
1709
+ function extractRoutesFromSwitchProps(props, parentPath = "") {
1710
+ const routes = [];
1711
+ if (!(props === null || props === void 0 ? void 0 : props.children)) return routes;
1712
+ const children = Array.isArray(props.children) ? props.children : [props.children];
1713
+ for (const child of children) if (child && child.type) {
1714
+ const typeName = child.type.name || child.type.displayName;
1715
+ if (typeName === "Route") {
1716
+ const route = extractRouteFromElementV5(child, parentPath);
1717
+ if (route) routes.push(route);
1718
+ }
1719
+ if (typeName === "Redirect") {
1720
+ var _child$props, _child$props2, _child$props3;
1721
+ const redirectInfo = {
1722
+ path: ((_child$props = child.props) === null || _child$props === void 0 ? void 0 : _child$props.from) || "*",
1723
+ element: `Redirect → ${((_child$props2 = child.props) === null || _child$props2 === void 0 ? void 0 : _child$props2.to) || "unknown"}`,
1724
+ exact: ((_child$props3 = child.props) === null || _child$props3 === void 0 ? void 0 : _child$props3.exact) === true
1725
+ };
1726
+ routes.push(redirectInfo);
1727
+ }
1728
+ }
1729
+ return routes;
1730
+ }
1731
+ /**
1605
1732
  * Extract route configuration from fiber tree
1606
1733
  */
1607
1734
  function extractRoutesFromFiber(fiber) {
@@ -1636,7 +1763,7 @@ function extractRoutesFromFiber(fiber) {
1636
1763
  return routes;
1637
1764
  }
1638
1765
  /**
1639
- * Find Routes component and extract route configuration
1766
+ * Find Routes/Switch component and extract route configuration
1640
1767
  */
1641
1768
  function findAndExtractRoutes(fiber) {
1642
1769
  if (!fiber) return [];
@@ -1654,6 +1781,13 @@ function findAndExtractRoutes(fiber) {
1654
1781
  return;
1655
1782
  }
1656
1783
  }
1784
+ if (name === "Switch") {
1785
+ const switchRoutes = extractRoutesFromSwitchProps(props);
1786
+ if (switchRoutes.length > 0) {
1787
+ routes = switchRoutes;
1788
+ return;
1789
+ }
1790
+ }
1657
1791
  if (name === "DataRoutes" || name === "RoutesRenderer") {
1658
1792
  const fiberRoutes = extractRoutesFromFiber(node);
1659
1793
  if (fiberRoutes.length > 0) routes.push(...fiberRoutes);
@@ -1769,7 +1903,7 @@ function detectRouterType(fiber) {
1769
1903
  if (!node || visited.has(node)) return null;
1770
1904
  visited.add(node);
1771
1905
  const name = getDisplayName(node);
1772
- if (name === "Router" || name === "BrowserRouter" || name === "HashRouter" || name === "MemoryRouter" || name === "Routes" || name === "Route") return "react-router";
1906
+ if (name === "Router" || name === "BrowserRouter" || name === "HashRouter" || name === "MemoryRouter" || name === "Routes" || name === "Switch" || name === "Route") return "react-router";
1773
1907
  if (node.child) {
1774
1908
  const result = traverse$1(node.child);
1775
1909
  if (result) return result;
package/dist/index.d.cts CHANGED
@@ -148,6 +148,16 @@ declare global {
148
148
  }
149
149
  //#endregion
150
150
  //#region src/core/context/index.d.ts
151
+ /**
152
+ * Warning about filtered properties in context values
153
+ */
154
+ interface ContextWarning {
155
+ type: 'filtered_property';
156
+ /** The property name that was filtered */
157
+ property: string;
158
+ /** Human-readable message */
159
+ message: string;
160
+ }
151
161
  /**
152
162
  * Information about a Context Provider
153
163
  */
@@ -171,6 +181,8 @@ interface ContextProviderInfo {
171
181
  lineNumber: number;
172
182
  columnNumber: number;
173
183
  };
184
+ /** Warnings about the context value (e.g., filtered properties) */
185
+ warnings?: ContextWarning[];
174
186
  }
175
187
  /**
176
188
  * Information about a Context Consumer
@@ -386,6 +398,10 @@ interface RouteInfo {
386
398
  hasErrorBoundary?: boolean;
387
399
  /** Dynamic segments in the path (e.g., :id, :userId) */
388
400
  params?: string[];
401
+ /** Whether this route requires exact match (React Router v5) */
402
+ exact?: boolean;
403
+ /** Whether this route uses strict matching (React Router v5) */
404
+ strict?: boolean;
389
405
  }
390
406
  interface MatchedRoute {
391
407
  path: string;
@@ -463,4 +479,4 @@ declare function createRpcServer<RemoteFunctions = Record<string, never>, LocalF
463
479
  //#region src/messaging/presets/iframe/context.d.ts
464
480
  declare function setIframeServerContext(iframe: HTMLIFrameElement | null): void;
465
481
  //#endregion
466
- export { ComponentDetails, ComponentTreeNode, ContextConsumerInfo, ContextProviderInfo, ContextTree, CreateRpcClientOptions, CreateRpcServerOptions, FiberNode, FiberRoot, HookInfo, HookStateInfo, MatchedRoute, NavigationEntry, PropValue, REACT_TAGS, ReactDevToolsHook, RenderedByInfo, RouteInfo, RouterState, SourceInfo, TIMELINE_LAYERS, type TimelineEvent, type TimelineLayer, type TimelineLayersState, ToggleInspectorOptions, TreeListener, addComponentEvent, addPerformanceEvent, buildTree, cleanupHighlight, clearFiberRegistry, clearNavigationHistory, clearOverrides, clearTimeline, createRpcClient, createRpcServer, getAllContexts, getAppFiberRoot, getComponentDetails, getComponentHookStates, getContextProviderInfo, getContextTree, getDisplayName, getFiberById, getFiberFromElement, getFiberId, getFiberRoot, getOverriddenContext, getOverriddenProp, getReactVersion, getRouterInfo, getRpcClient, getRpcServer, getTimelineState, hideHighlight, highlightNode, installReactHook, installTimelineEventListeners, isEditableProp, mergeClientRects, navigateTo, onInspectorSelect, onOpenInEditor, onTimelineEvent, onTreeUpdated, openInEditor, parseValue, rebuildTree, scrollToNode, setComponentProp, setContextValue, setContextValueAtPath, setContextValueFromJson, setFiberRoot, setHookState, setHookStateFromJson, setIframeServerContext, setRpcServerToGlobal, shouldIncludeFiber, showHighlight, toggleInspector, trackFiberPerformanceEnd, trackFiberPerformanceStart, updateTimelineState };
482
+ export { ComponentDetails, ComponentTreeNode, ContextConsumerInfo, ContextProviderInfo, ContextTree, ContextWarning, CreateRpcClientOptions, CreateRpcServerOptions, FiberNode, FiberRoot, HookInfo, HookStateInfo, MatchedRoute, NavigationEntry, PropValue, REACT_TAGS, ReactDevToolsHook, RenderedByInfo, RouteInfo, RouterState, SourceInfo, TIMELINE_LAYERS, type TimelineEvent, type TimelineLayer, type TimelineLayersState, ToggleInspectorOptions, TreeListener, addComponentEvent, addPerformanceEvent, buildTree, cleanupHighlight, clearFiberRegistry, clearNavigationHistory, clearOverrides, clearTimeline, createRpcClient, createRpcServer, getAllContexts, getAppFiberRoot, getComponentDetails, getComponentHookStates, getContextProviderInfo, getContextTree, getDisplayName, getFiberById, getFiberFromElement, getFiberId, getFiberRoot, getOverriddenContext, getOverriddenProp, getReactVersion, getRouterInfo, getRpcClient, getRpcServer, getTimelineState, hideHighlight, highlightNode, installReactHook, installTimelineEventListeners, isEditableProp, mergeClientRects, navigateTo, onInspectorSelect, onOpenInEditor, onTimelineEvent, onTreeUpdated, openInEditor, parseValue, rebuildTree, scrollToNode, setComponentProp, setContextValue, setContextValueAtPath, setContextValueFromJson, setFiberRoot, setHookState, setHookStateFromJson, setIframeServerContext, setRpcServerToGlobal, shouldIncludeFiber, showHighlight, toggleInspector, trackFiberPerformanceEnd, trackFiberPerformanceStart, updateTimelineState };
package/dist/index.d.ts CHANGED
@@ -148,6 +148,16 @@ declare global {
148
148
  }
149
149
  //#endregion
150
150
  //#region src/core/context/index.d.ts
151
+ /**
152
+ * Warning about filtered properties in context values
153
+ */
154
+ interface ContextWarning {
155
+ type: 'filtered_property';
156
+ /** The property name that was filtered */
157
+ property: string;
158
+ /** Human-readable message */
159
+ message: string;
160
+ }
151
161
  /**
152
162
  * Information about a Context Provider
153
163
  */
@@ -171,6 +181,8 @@ interface ContextProviderInfo {
171
181
  lineNumber: number;
172
182
  columnNumber: number;
173
183
  };
184
+ /** Warnings about the context value (e.g., filtered properties) */
185
+ warnings?: ContextWarning[];
174
186
  }
175
187
  /**
176
188
  * Information about a Context Consumer
@@ -386,6 +398,10 @@ interface RouteInfo {
386
398
  hasErrorBoundary?: boolean;
387
399
  /** Dynamic segments in the path (e.g., :id, :userId) */
388
400
  params?: string[];
401
+ /** Whether this route requires exact match (React Router v5) */
402
+ exact?: boolean;
403
+ /** Whether this route uses strict matching (React Router v5) */
404
+ strict?: boolean;
389
405
  }
390
406
  interface MatchedRoute {
391
407
  path: string;
@@ -463,4 +479,4 @@ declare function createRpcServer<RemoteFunctions = Record<string, never>, LocalF
463
479
  //#region src/messaging/presets/iframe/context.d.ts
464
480
  declare function setIframeServerContext(iframe: HTMLIFrameElement | null): void;
465
481
  //#endregion
466
- export { ComponentDetails, ComponentTreeNode, ContextConsumerInfo, ContextProviderInfo, ContextTree, CreateRpcClientOptions, CreateRpcServerOptions, FiberNode, FiberRoot, HookInfo, HookStateInfo, MatchedRoute, NavigationEntry, PropValue, REACT_TAGS, ReactDevToolsHook, RenderedByInfo, RouteInfo, RouterState, SourceInfo, TIMELINE_LAYERS, type TimelineEvent, type TimelineLayer, type TimelineLayersState, ToggleInspectorOptions, TreeListener, addComponentEvent, addPerformanceEvent, buildTree, cleanupHighlight, clearFiberRegistry, clearNavigationHistory, clearOverrides, clearTimeline, createRpcClient, createRpcServer, getAllContexts, getAppFiberRoot, getComponentDetails, getComponentHookStates, getContextProviderInfo, getContextTree, getDisplayName, getFiberById, getFiberFromElement, getFiberId, getFiberRoot, getOverriddenContext, getOverriddenProp, getReactVersion, getRouterInfo, getRpcClient, getRpcServer, getTimelineState, hideHighlight, highlightNode, installReactHook, installTimelineEventListeners, isEditableProp, mergeClientRects, navigateTo, onInspectorSelect, onOpenInEditor, onTimelineEvent, onTreeUpdated, openInEditor, parseValue, rebuildTree, scrollToNode, setComponentProp, setContextValue, setContextValueAtPath, setContextValueFromJson, setFiberRoot, setHookState, setHookStateFromJson, setIframeServerContext, setRpcServerToGlobal, shouldIncludeFiber, showHighlight, toggleInspector, trackFiberPerformanceEnd, trackFiberPerformanceStart, updateTimelineState };
482
+ export { ComponentDetails, ComponentTreeNode, ContextConsumerInfo, ContextProviderInfo, ContextTree, ContextWarning, CreateRpcClientOptions, CreateRpcServerOptions, FiberNode, FiberRoot, HookInfo, HookStateInfo, MatchedRoute, NavigationEntry, PropValue, REACT_TAGS, ReactDevToolsHook, RenderedByInfo, RouteInfo, RouterState, SourceInfo, TIMELINE_LAYERS, type TimelineEvent, type TimelineLayer, type TimelineLayersState, ToggleInspectorOptions, TreeListener, addComponentEvent, addPerformanceEvent, buildTree, cleanupHighlight, clearFiberRegistry, clearNavigationHistory, clearOverrides, clearTimeline, createRpcClient, createRpcServer, getAllContexts, getAppFiberRoot, getComponentDetails, getComponentHookStates, getContextProviderInfo, getContextTree, getDisplayName, getFiberById, getFiberFromElement, getFiberId, getFiberRoot, getOverriddenContext, getOverriddenProp, getReactVersion, getRouterInfo, getRpcClient, getRpcServer, getTimelineState, hideHighlight, highlightNode, installReactHook, installTimelineEventListeners, isEditableProp, mergeClientRects, navigateTo, onInspectorSelect, onOpenInEditor, onTimelineEvent, onTreeUpdated, openInEditor, parseValue, rebuildTree, scrollToNode, setComponentProp, setContextValue, setContextValueAtPath, setContextValueFromJson, setFiberRoot, setHookState, setHookStateFromJson, setIframeServerContext, setRpcServerToGlobal, shouldIncludeFiber, showHighlight, toggleInspector, trackFiberPerformanceEnd, trackFiberPerformanceStart, updateTimelineState };
package/dist/index.js CHANGED
@@ -81,9 +81,28 @@ function getFiberFromElement(element) {
81
81
  //#endregion
82
82
  //#region src/core/context/index.ts
83
83
  /**
84
+ * Properties that can cause prototype pollution and should be filtered out
85
+ * SuperJSON will throw an error if these properties are present in the object
86
+ */
87
+ const DANGEROUS_PROPERTIES$1 = new Set([
88
+ "constructor",
89
+ "__proto__",
90
+ "prototype"
91
+ ]);
92
+ /**
93
+ * Check if a property key is safe to serialize (not a prototype pollution risk)
94
+ */
95
+ function isSafePropertyKey$1(key) {
96
+ return !DANGEROUS_PROPERTIES$1.has(key);
97
+ }
98
+ /**
84
99
  * Serialize a value into a displayable PropValue
100
+ * @param value - The value to serialize
101
+ * @param depth - Current recursion depth
102
+ * @param maxDepth - Maximum recursion depth
103
+ * @param context - Optional context to collect warnings about filtered properties
85
104
  */
86
- function serializeValue$1(value, depth = 0, maxDepth = 6) {
105
+ function serializeValue$1(value, depth = 0, maxDepth = 6, context) {
87
106
  if (value === null) return {
88
107
  type: "null",
89
108
  value: "null"
@@ -126,7 +145,7 @@ function serializeValue$1(value, depth = 0, maxDepth = 6) {
126
145
  if (Array.isArray(value)) {
127
146
  const children = {};
128
147
  value.forEach((item, index) => {
129
- children[String(index)] = serializeValue$1(item, depth + 1, maxDepth);
148
+ children[String(index)] = serializeValue$1(item, depth + 1, maxDepth, context);
130
149
  });
131
150
  return {
132
151
  type: "array",
@@ -142,10 +161,13 @@ function serializeValue$1(value, depth = 0, maxDepth = 6) {
142
161
  value: `<${((_value$type = value.type) === null || _value$type === void 0 ? void 0 : _value$type.displayName) || ((_value$type2 = value.type) === null || _value$type2 === void 0 ? void 0 : _value$type2.name) || value.type || "Unknown"} />`
143
162
  };
144
163
  }
145
- const keys = Object.keys(value);
164
+ const allKeys = Object.keys(value);
165
+ const safeKeys = allKeys.filter(isSafePropertyKey$1);
166
+ const filteredKeys = allKeys.filter((key) => !isSafePropertyKey$1(key));
167
+ if (context && filteredKeys.length > 0) filteredKeys.forEach((key) => context.filteredProperties.add(key));
146
168
  const children = {};
147
- for (const key of keys) try {
148
- children[key] = serializeValue$1(value[key], depth + 1, maxDepth);
169
+ for (const key of safeKeys) try {
170
+ children[key] = serializeValue$1(value[key], depth + 1, maxDepth, context);
149
171
  } catch (_unused) {
150
172
  children[key] = {
151
173
  type: "unknown",
@@ -155,7 +177,7 @@ function serializeValue$1(value, depth = 0, maxDepth = 6) {
155
177
  return {
156
178
  type: "object",
157
179
  value: `Object`,
158
- preview: keys.length > 0 ? `{${keys.slice(0, 3).join(", ")}${keys.length > 3 ? ", ..." : ""}}` : "{}",
180
+ preview: safeKeys.length > 0 ? `{${safeKeys.slice(0, 3).join(", ")}${safeKeys.length > 3 ? ", ..." : ""}}` : "{}",
159
181
  children: Object.keys(children).length > 0 ? children : void 0
160
182
  };
161
183
  }
@@ -165,6 +187,23 @@ function serializeValue$1(value, depth = 0, maxDepth = 6) {
165
187
  };
166
188
  }
167
189
  /**
190
+ * Serialize a context value and collect any warnings
191
+ */
192
+ function serializeContextValue(value) {
193
+ const context = { filteredProperties: /* @__PURE__ */ new Set() };
194
+ const propValue = serializeValue$1(value, 0, 6, context);
195
+ const warnings = [];
196
+ for (const property of context.filteredProperties) warnings.push({
197
+ type: "filtered_property",
198
+ property,
199
+ message: `Property "${property}" was hidden to prevent prototype pollution errors. This does not affect your application.`
200
+ });
201
+ return {
202
+ propValue,
203
+ warnings
204
+ };
205
+ }
206
+ /**
168
207
  * Get the display name of a Context
169
208
  */
170
209
  function getContextDisplayName(fiber) {
@@ -240,10 +279,11 @@ function buildProviderInfo(fiber, visited) {
240
279
  const consumers = [];
241
280
  if (fiber.child) findConsumers(fiber.child, contextType, consumers, /* @__PURE__ */ new WeakSet());
242
281
  const uniqueConsumers = consumers.filter((consumer, index, self) => index === self.findIndex((c) => c.id === consumer.id));
282
+ const { propValue, warnings } = serializeContextValue(getContextValue(fiber));
243
283
  const info = {
244
284
  id: getFiberId(fiber),
245
285
  name: getContextDisplayName(fiber),
246
- value: serializeValue$1(getContextValue(fiber)),
286
+ value: propValue,
247
287
  fiberId: getFiberId(fiber),
248
288
  consumerCount: uniqueConsumers.length,
249
289
  consumers: uniqueConsumers,
@@ -252,7 +292,8 @@ function buildProviderInfo(fiber, visited) {
252
292
  fileName: fiber._debugSource.fileName,
253
293
  lineNumber: fiber._debugSource.lineNumber,
254
294
  columnNumber: fiber._debugSource.columnNumber
255
- } : void 0
295
+ } : void 0,
296
+ warnings: warnings.length > 0 ? warnings : void 0
256
297
  };
257
298
  const findNestedProviders = (child) => {
258
299
  var _child$type;
@@ -337,6 +378,21 @@ function getAllContexts(root) {
337
378
  //#endregion
338
379
  //#region src/core/fiber/details.ts
339
380
  /**
381
+ * Properties that can cause prototype pollution and should be filtered out
382
+ * SuperJSON throws: "Detected property constructor. This is a property pollution risk"
383
+ */
384
+ const DANGEROUS_PROPERTIES = new Set([
385
+ "constructor",
386
+ "__proto__",
387
+ "prototype"
388
+ ]);
389
+ /**
390
+ * Check if a property key is safe to serialize (not a prototype pollution risk)
391
+ */
392
+ function isSafePropertyKey(key) {
393
+ return !DANGEROUS_PROPERTIES.has(key);
394
+ }
395
+ /**
340
396
  * Serialize a value into a displayable PropValue
341
397
  */
342
398
  function serializeValue(value, depth = 0, maxDepth = 8) {
@@ -398,7 +454,7 @@ function serializeValue(value, depth = 0, maxDepth = 8) {
398
454
  value: `<${((_value$type = value.type) === null || _value$type === void 0 ? void 0 : _value$type.displayName) || ((_value$type2 = value.type) === null || _value$type2 === void 0 ? void 0 : _value$type2.name) || value.type || "Unknown"} />`
399
455
  };
400
456
  }
401
- const keys = Object.keys(value);
457
+ const keys = Object.keys(value).filter(isSafePropertyKey);
402
458
  const children = {};
403
459
  for (const key of keys) try {
404
460
  children[key] = serializeValue(value[key], depth + 1, maxDepth);
@@ -1562,7 +1618,7 @@ function extractRouteFromElement(element, parentPath = "") {
1562
1618
  return routeInfo;
1563
1619
  }
1564
1620
  /**
1565
- * Extract routes from Routes component's children prop
1621
+ * Extract routes from Routes component's children prop (React Router v6)
1566
1622
  */
1567
1623
  function extractRoutesFromProps(props) {
1568
1624
  const routes = [];
@@ -1578,6 +1634,77 @@ function extractRoutesFromProps(props) {
1578
1634
  return routes;
1579
1635
  }
1580
1636
  /**
1637
+ * Get component name from React Router v5 Route props
1638
+ */
1639
+ function getV5ComponentName(props) {
1640
+ if (props.component) return props.component.name || props.component.displayName;
1641
+ if (props.render) return "render()";
1642
+ if (typeof props.children === "function") return "children()";
1643
+ }
1644
+ /**
1645
+ * Extract route info from a React Router v5 Route element (JSX props)
1646
+ */
1647
+ function extractRouteFromElementV5(element, parentPath = "") {
1648
+ if (!element || !element.props) return null;
1649
+ const props = element.props;
1650
+ const path = props.path || "*";
1651
+ let fullPath = path;
1652
+ if (path !== "*" && !path.startsWith("/") && parentPath) fullPath = `${parentPath}/${path}`.replace(/\/+/g, "/");
1653
+ const params = extractParamsFromPath(path);
1654
+ const childRoutes = [];
1655
+ if (props.children && typeof props.children !== "function") {
1656
+ const children = Array.isArray(props.children) ? props.children : [props.children];
1657
+ for (const child of children) {
1658
+ var _child$type3, _child$type4;
1659
+ if (child && child.type && (child.type.name === "Route" || ((_child$type3 = child.type) === null || _child$type3 === void 0 ? void 0 : _child$type3.displayName) === "Route")) {
1660
+ const childRoute = extractRouteFromElementV5(child, fullPath);
1661
+ if (childRoute) childRoutes.push(childRoute);
1662
+ }
1663
+ if (child && child.type && (child.type.name === "Switch" || ((_child$type4 = child.type) === null || _child$type4 === void 0 ? void 0 : _child$type4.displayName) === "Switch")) {
1664
+ const switchChildren = extractRoutesFromSwitchProps(child.props, fullPath);
1665
+ childRoutes.push(...switchChildren);
1666
+ }
1667
+ }
1668
+ }
1669
+ const hasChildren = childRoutes.length > 0;
1670
+ const routeInfo = {
1671
+ path: fullPath,
1672
+ element: getV5ComponentName(props),
1673
+ exact: props.exact === true,
1674
+ strict: props.strict === true,
1675
+ isLayout: hasChildren,
1676
+ isLazy: isLazyElement(props.component),
1677
+ params: params.length > 0 ? params : void 0
1678
+ };
1679
+ if (hasChildren) routeInfo.children = childRoutes;
1680
+ return routeInfo;
1681
+ }
1682
+ /**
1683
+ * Extract routes from Switch component's children prop (React Router v5)
1684
+ */
1685
+ function extractRoutesFromSwitchProps(props, parentPath = "") {
1686
+ const routes = [];
1687
+ if (!(props === null || props === void 0 ? void 0 : props.children)) return routes;
1688
+ const children = Array.isArray(props.children) ? props.children : [props.children];
1689
+ for (const child of children) if (child && child.type) {
1690
+ const typeName = child.type.name || child.type.displayName;
1691
+ if (typeName === "Route") {
1692
+ const route = extractRouteFromElementV5(child, parentPath);
1693
+ if (route) routes.push(route);
1694
+ }
1695
+ if (typeName === "Redirect") {
1696
+ var _child$props, _child$props2, _child$props3;
1697
+ const redirectInfo = {
1698
+ path: ((_child$props = child.props) === null || _child$props === void 0 ? void 0 : _child$props.from) || "*",
1699
+ element: `Redirect → ${((_child$props2 = child.props) === null || _child$props2 === void 0 ? void 0 : _child$props2.to) || "unknown"}`,
1700
+ exact: ((_child$props3 = child.props) === null || _child$props3 === void 0 ? void 0 : _child$props3.exact) === true
1701
+ };
1702
+ routes.push(redirectInfo);
1703
+ }
1704
+ }
1705
+ return routes;
1706
+ }
1707
+ /**
1581
1708
  * Extract route configuration from fiber tree
1582
1709
  */
1583
1710
  function extractRoutesFromFiber(fiber) {
@@ -1612,7 +1739,7 @@ function extractRoutesFromFiber(fiber) {
1612
1739
  return routes;
1613
1740
  }
1614
1741
  /**
1615
- * Find Routes component and extract route configuration
1742
+ * Find Routes/Switch component and extract route configuration
1616
1743
  */
1617
1744
  function findAndExtractRoutes(fiber) {
1618
1745
  if (!fiber) return [];
@@ -1630,6 +1757,13 @@ function findAndExtractRoutes(fiber) {
1630
1757
  return;
1631
1758
  }
1632
1759
  }
1760
+ if (name === "Switch") {
1761
+ const switchRoutes = extractRoutesFromSwitchProps(props);
1762
+ if (switchRoutes.length > 0) {
1763
+ routes = switchRoutes;
1764
+ return;
1765
+ }
1766
+ }
1633
1767
  if (name === "DataRoutes" || name === "RoutesRenderer") {
1634
1768
  const fiberRoutes = extractRoutesFromFiber(node);
1635
1769
  if (fiberRoutes.length > 0) routes.push(...fiberRoutes);
@@ -1745,7 +1879,7 @@ function detectRouterType(fiber) {
1745
1879
  if (!node || visited.has(node)) return null;
1746
1880
  visited.add(node);
1747
1881
  const name = getDisplayName(node);
1748
- if (name === "Router" || name === "BrowserRouter" || name === "HashRouter" || name === "MemoryRouter" || name === "Routes" || name === "Route") return "react-router";
1882
+ if (name === "Router" || name === "BrowserRouter" || name === "HashRouter" || name === "MemoryRouter" || name === "Routes" || name === "Switch" || name === "Route") return "react-router";
1749
1883
  if (node.child) {
1750
1884
  const result = traverse$1(node.child);
1751
1885
  if (result) return result;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@react-devtools-plus/kit",
3
3
  "type": "module",
4
- "version": "0.9.4",
4
+ "version": "0.9.5",
5
5
  "author": "wzc520pyfm",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -27,7 +27,7 @@
27
27
  "mitt": "^3.0.1",
28
28
  "perfect-debounce": "^2.0.0",
29
29
  "superjson": "^2.2.2",
30
- "@react-devtools-plus/shared": "^0.9.4"
30
+ "@react-devtools-plus/shared": "^0.9.5"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/node": "^24.7.2",