Skip to content

Commit

Permalink
fix: improve proxy and type handling in inspect element
Browse files Browse the repository at this point in the history
- Remove unreliable proxy detection and instanceof checks
- Add safer URLSearchParams handling to prevent Next.js errors
- Use capability checks instead of type detection
- Improve error handling for property access

This addresses issues with proxy detection and type checking in the
element inspector, making it more reliable across different environments
while preventing Next.js-specific errors when handling search params.
  • Loading branch information
pivanov committed Dec 14, 2024
1 parent e8927ff commit 4a93a91
Showing 1 changed file with 64 additions and 69 deletions.
133 changes: 64 additions & 69 deletions packages/scan/src/core/web/inspect-element/view-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,50 +12,45 @@ const EXPANDED_PATHS = new Set<string>();
const fadeOutTimers = new WeakMap<HTMLElement, ReturnType<typeof setTimeout>>();
const disabledButtons = new Set<HTMLButtonElement>();

// Utility to check and unwrap proxies
const isProxy = (obj: any) => Object.getPrototypeOf(obj)?.constructor?.name === 'Proxy';

const unwrapProxy = (proxy: any) => {
try {
const descriptors = Object.getOwnPropertyDescriptors(proxy);
const unwrapped = Object.fromEntries(
Object.entries(descriptors).reduce<Array<[string, any]>>((acc, [key, descriptor]) => {
if (key !== 'Symbol(Symbol.iterator)') {
acc.push([key, descriptor.value]);
}
return acc;
}, []),
);

return unwrapped;
} catch (error) {
return proxy;
}
};



const getProxyValue = (proxy: any) => {
try {
if (!proxy || typeof proxy !== 'object') {
return proxy
};
return proxy;
}

// Handle URLSearchParams-like objects
if (proxy[Symbol.iterator]) {
// Instead of checking for Symbol.iterator, check if it's a URLSearchParams-like object
// by checking for common methods
if (typeof proxy.entries === 'function' && typeof proxy.get === 'function') {
try {
return Object.fromEntries(proxy);
// Convert to plain object more safely
const entries = Array.from(proxy.entries() as Iterable<[string, any]>);
return Object.fromEntries(entries);
} catch (err) {
// Silent fail
// If conversion fails, return original
return proxy;
}
}

// Handle standard proxies
if (isProxy(proxy)) {
return unwrapProxy(proxy);
// Instead of checking for proxy, try to safely get own properties
try {
const descriptors = Object.getOwnPropertyDescriptors(proxy);
const unwrapped = Object.fromEntries(
Object.entries(descriptors).reduce<Array<[string, any]>>((acc, [key, descriptor]) => {
// Skip Symbol properties and getters that might throw
if (typeof key === 'string' && 'value' in descriptor) {
acc.push([key, descriptor.value]);
}
return acc;
}, [])
);

return unwrapped;
} catch (err) {
// If unwrapping fails, return original
return proxy;
}

return proxy;
} catch (err) {
return proxy;
}
Expand Down Expand Up @@ -798,51 +793,51 @@ export const getValueClassName = (value: any) => {
};

export const getValuePreview = (value: any) => {
if (Array.isArray(value)) {
return `Array(${value.length})`;
}
if (value === null) return 'null';
if (value === undefined) return 'undefined';
switch (typeof value) {
case 'string':
return `"${value}"`;
case 'number':
return value.toString();
case 'boolean':
return value.toString();
case 'object': {
try {
if (value === null) return 'null';
if (value === undefined) return 'undefined';

// Handle special built-in types first
if (value instanceof Promise) return '[Promise]';
if (value instanceof Set) return `Set(${value.size})`;
if (value instanceof Map) return `Map(${value.size})`;

// Try to unwrap proxy values
const proto = Object.getPrototypeOf(value);
if (proto?.constructor?.name === 'Proxy') {
const unwrapped = getProxyValue(value);
if (unwrapped !== value) {
const keys = Object.keys(unwrapped);
return `Proxy{${keys.join(', ')}}`;
}
return '[Next.js Params]';

try {
if (Array.isArray(value)) {
return `Array(${value.length})`;
}

switch (typeof value) {
case 'string':
return `"${value}"`;
case 'number':
case 'boolean':
return String(value);
case 'object': {
// Safe checks for common interfaces rather than type detection
if (value.then && typeof value.then === 'function') {
return '[Promise]';
}
if (value.size !== undefined && value.add && typeof value.add === 'function') {
return `Set(${value.size ?? '?'})`;
}
if (value.size !== undefined && value.set && typeof value.set === 'function') {
return `Map(${value.size ?? '?'})`;
}
if (typeof value.entries === 'function' && typeof value.get === 'function') {
return '[URLSearchParams]';
}

// Handle regular objects
const keys = Object.keys(value);
if (keys.length <= 3) {
return `{${keys.join(', ')}}`;
try {
const keys = Object.keys(value);
if (keys.length <= 3) {
return `{${keys.join(', ')}}`;
}
return `{${keys.slice(0, 3).join(', ')}, ...}`;
} catch {
return '{...}';
}
return `{${keys.slice(0, 3).join(', ')}, ...}`;
} catch (error) {
return '{...}';
}
default:
return typeof value;
}
default:
return typeof value;
} catch {
return String(value);
}
};

Expand Down

0 comments on commit 4a93a91

Please sign in to comment.