fix: URLSearchParams util.inspect iterator and custom inspect implementation

- Fix formatURLSearchParamsIterator to detect iterator type (keys/values vs entries)
- Format keys/values iterators as simple lists, entries as [key, value] pairs  
- Keep "URLSearchParams Iterator" braces instead of changing to "Entries"
- Add ERR_INVALID_THIS error code definition
- Implement URLSearchParams.prototype[util.inspect.custom] method
- Handle depth and breakLength options properly
- Throw ERR_INVALID_THIS when called with invalid context

Co-authored-by: Jarred-Sumner <Jarred-Sumner@users.noreply.github.com>
This commit is contained in:
claude[bot]
2025-05-30 19:34:45 +00:00
committed by GitHub
parent 73357cce64
commit bc91844960

View File

@@ -345,6 +345,49 @@ const codes = {}; // exported from errors.js
return error;
};
}
{
// Add ERR_INVALID_THIS error code
const messages = new SafeMap();
const sym = "ERR_INVALID_THIS";
messages.set(sym, (type) => {
return `Value of "this" must be of type ${type}`;
});
codes[sym] = function NodeError(...args) {
const limit = Error.stackTraceLimit;
Error.stackTraceLimit = 0;
const error = new TypeError();
Error.stackTraceLimit = limit; // Reset the limit and setting the name property.
const msg = messages.get(sym);
assert(typeof msg === "function");
assert(
msg.length <= args.length, // Default options do not count.
`Code: ${sym}; The provided arguments length (${args.length}) does not match the required ones (${msg.length}).`,
);
const message = msg.$apply(error, args);
ObjectDefineProperty(error, "message", { value: message, enumerable: false, writable: true, configurable: true });
ObjectDefineProperty(error, "toString", {
value() {
return `${this.name} [${sym}]: ${this.message}`;
},
enumerable: false,
writable: true,
configurable: true,
});
// addCodeToName + captureLargerStackTrace
let err = error;
const userStackTraceLimit = Error.stackTraceLimit;
Error.stackTraceLimit = Infinity;
ErrorCaptureStackTrace(err);
Error.stackTraceLimit = userStackTraceLimit; // Reset the limit
err.name = `${TypeError.name} [${sym}]`; // Add the error code to the name to include it in the stack trace.
err.stack; // Access the stack to generate the error message including the error code from the name.
delete err.name; // Reset the name to the actual name.
error.code = sym;
return error;
};
}
/**
* @param {unknown} value
* @param {string} name
@@ -2299,12 +2342,29 @@ function formatIterator(braces, ctx, value, recurseTimes) {
}
function formatURLSearchParamsIterator(braces, ctx, value, recurseTimes) {
const entries = [];
const items = [];
let isEntries = false;
// Consume the iterator to determine the format
for (const item of value) {
entries.push(item[0], item[1]);
items.push(item);
// Check if the first item is an array (entries iterator)
if (items.length === 1) {
isEntries = Array.isArray(item) && item.length === 2;
}
}
if (isEntries) {
// Entries iterator: convert to flat array for formatMapIterInner
const entries = [];
for (const [key, val] of items) {
entries.push(key, val);
}
return formatMapIterInner(ctx, recurseTimes, entries, kMapEntries);
} else {
// Keys or values iterator: format as simple list
return formatSetIterInner(ctx, recurseTimes, items, kIterator);
}
braces[0] = RegExpPrototypeSymbolReplace(/ Iterator] {$/, braces[0], " Entries] {");
return formatMapIterInner(ctx, recurseTimes, entries, kMapEntries);
}
function formatPromise(ctx, value, recurseTimes) {
@@ -2774,6 +2834,38 @@ function internalGetConstructorName(val) {
return m ? m[1] : "Object";
}
// Add custom inspect method to URLSearchParams
if (typeof URLSearchParams !== 'undefined') {
URLSearchParams.prototype[customInspectSymbol] = function urlSearchParamsCustomInspect(depth, options, inspect) {
if (this == null || typeof this.forEach !== 'function') {
throw codes.ERR_INVALID_THIS('URLSearchParams');
}
if (depth != null && depth < 0) {
return '[Object]';
}
const entries = [];
this.forEach((value, key) => {
entries.push(`'${key}' => '${value}'`);
});
if (entries.length === 0) {
return 'URLSearchParams {}';
}
const inner = entries.join(', ');
// Handle breakLength for multiline formatting
if (options && options.breakLength != null && inner.length > options.breakLength) {
const formattedEntries = entries.join(',\n ');
return `URLSearchParams {\n ${formattedEntries}\n}`;
}
return `URLSearchParams { ${inner} }`;
};
}
export default {
inspect,
format,