@ai-capabilities-suite/mcp-debugger-core
Version:
Core debugging engine for Node.js and TypeScript applications. Provides Inspector Protocol integration, breakpoint management, variable inspection, execution control, profiling, hang detection, and source map support.
318 lines • 10.4 kB
JavaScript
"use strict";
/**
* Variable formatting customization for debug output
* Supports custom formatters, pretty-printing, truncation, and user-defined display rules
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.VariableFormatter = void 0;
/**
* Formats variables for display in debugging output
*/
class VariableFormatter {
constructor(options = {}) {
this.options = {
maxDepth: options.maxDepth ?? 3,
maxLength: options.maxLength ?? 1000,
maxArrayLength: options.maxArrayLength ?? 100,
maxStringLength: options.maxStringLength ?? 200,
indent: options.indent ?? 2,
showHidden: options.showHidden ?? false,
showTypes: options.showTypes ?? true,
customFormatters: options.customFormatters ?? new Map(),
};
this.customFormatters = this.options.customFormatters;
}
/**
* Format a value for display
*/
format(value, depth = 0) {
// Check custom formatters first
for (const [, formatter] of this.customFormatters) {
if (formatter.test(value)) {
return {
value: formatter.format(value, this.options),
type: typeof value,
truncated: false,
};
}
}
// Handle null and undefined
if (value === null) {
return { value: "null", type: "null", truncated: false };
}
if (value === undefined) {
return { value: "undefined", type: "undefined", truncated: false };
}
// Handle primitives
const type = typeof value;
if (type === "string") {
return this.formatString(value);
}
if (type === "number" || type === "boolean") {
return { value: String(value), type, truncated: false };
}
if (type === "symbol") {
return { value: value.toString(), type, truncated: false };
}
if (type === "bigint") {
return { value: value.toString() + "n", type, truncated: false };
}
// Handle functions
if (type === "function") {
return this.formatFunction(value);
}
// Handle objects
if (depth >= this.options.maxDepth) {
return { value: "[Max depth reached]", type: "object", truncated: true };
}
// Handle arrays
if (Array.isArray(value)) {
return this.formatArray(value, depth);
}
// Handle dates
if (value instanceof Date) {
return { value: value.toISOString(), type: "Date", truncated: false };
}
// Handle errors
if (value instanceof Error) {
return this.formatError(value);
}
// Handle regular expressions
if (value instanceof RegExp) {
return { value: value.toString(), type: "RegExp", truncated: false };
}
// Handle maps
if (value instanceof Map) {
return this.formatMap(value, depth);
}
// Handle sets
if (value instanceof Set) {
return this.formatSet(value, depth);
}
// Handle plain objects
return this.formatObject(value, depth);
}
/**
* Format a string value
*/
formatString(value) {
if (value.length <= this.options.maxStringLength) {
return {
value: JSON.stringify(value),
type: "string",
truncated: false,
};
}
const truncated = value.substring(0, this.options.maxStringLength);
return {
value: JSON.stringify(truncated) + "... (truncated)",
type: "string",
truncated: true,
};
}
/**
* Format a function
*/
formatFunction(value) {
const name = value.name || "anonymous";
const source = value.toString();
// Extract function signature
const signatureMatch = source.match(/^[^{]*/);
const signature = signatureMatch ? signatureMatch[0].trim() : name;
return {
value: `[Function: ${signature}]`,
type: "function",
truncated: false,
};
}
/**
* Format an array
*/
formatArray(value, depth) {
if (value.length === 0) {
return { value: "[]", type: "Array", truncated: false };
}
const maxLength = this.options.maxArrayLength;
const items = value.slice(0, maxLength);
const truncated = value.length > maxLength;
const formatted = items.map((item) => this.format(item, depth + 1).value);
let result;
if (this.shouldPrettyPrint(formatted)) {
const indent = " ".repeat(this.options.indent * (depth + 1));
const closeIndent = " ".repeat(this.options.indent * depth);
result =
"[\n" +
formatted.map((f) => indent + f).join(",\n") +
(truncated
? `,\n${indent}... (${value.length - maxLength} more)`
: "") +
"\n" +
closeIndent +
"]";
}
else {
result =
"[" +
formatted.join(", ") +
(truncated ? `, ... (${value.length - maxLength} more)` : "") +
"]";
}
return {
value: this.truncateIfNeeded(result),
type: "Array",
truncated: truncated || result.length > this.options.maxLength,
};
}
/**
* Format an object
*/
formatObject(value, depth) {
const keys = this.options.showHidden
? Object.getOwnPropertyNames(value)
: Object.keys(value);
if (keys.length === 0) {
return { value: "{}", type: "Object", truncated: false };
}
const entries = [];
for (const key of keys) {
try {
const propValue = value[key];
const formatted = this.format(propValue, depth + 1);
const typeInfo = this.options.showTypes ? ` (${formatted.type})` : "";
entries.push(`${key}${typeInfo}: ${formatted.value}`);
}
catch (error) {
entries.push(`${key}: [Error accessing property]`);
}
}
let result;
if (this.shouldPrettyPrint(entries)) {
const indent = " ".repeat(this.options.indent * (depth + 1));
const closeIndent = " ".repeat(this.options.indent * depth);
result =
"{\n" +
entries.map((e) => indent + e).join(",\n") +
"\n" +
closeIndent +
"}";
}
else {
result = "{ " + entries.join(", ") + " }";
}
return {
value: this.truncateIfNeeded(result),
type: value.constructor?.name || "Object",
truncated: result.length > this.options.maxLength,
};
}
/**
* Format an error
*/
formatError(value) {
const parts = [`${value.name}: ${value.message}`];
if (value.stack) {
const stackLines = value.stack.split("\n").slice(1, 4);
parts.push(...stackLines);
}
return {
value: parts.join("\n"),
type: "Error",
truncated: false,
};
}
/**
* Format a Map
*/
formatMap(value, depth) {
if (value.size === 0) {
return { value: "Map(0) {}", type: "Map", truncated: false };
}
const entries = [];
let count = 0;
for (const [key, val] of value) {
if (count >= this.options.maxArrayLength) {
entries.push(`... (${value.size - count} more)`);
break;
}
const formattedKey = this.format(key, depth + 1).value;
const formattedVal = this.format(val, depth + 1).value;
entries.push(`${formattedKey} => ${formattedVal}`);
count++;
}
const result = `Map(${value.size}) { ${entries.join(", ")} }`;
return {
value: this.truncateIfNeeded(result),
type: "Map",
truncated: result.length > this.options.maxLength,
};
}
/**
* Format a Set
*/
formatSet(value, depth) {
if (value.size === 0) {
return { value: "Set(0) {}", type: "Set", truncated: false };
}
const items = [];
let count = 0;
for (const item of value) {
if (count >= this.options.maxArrayLength) {
items.push(`... (${value.size - count} more)`);
break;
}
items.push(this.format(item, depth + 1).value);
count++;
}
const result = `Set(${value.size}) { ${items.join(", ")} }`;
return {
value: this.truncateIfNeeded(result),
type: "Set",
truncated: result.length > this.options.maxLength,
};
}
/**
* Check if output should be pretty-printed
*/
shouldPrettyPrint(items) {
const totalLength = items.join("").length;
return totalLength > 80 || items.length > 3;
}
/**
* Truncate string if it exceeds max length
*/
truncateIfNeeded(value) {
if (value.length <= this.options.maxLength) {
return value;
}
return value.substring(0, this.options.maxLength) + "... (truncated)";
}
/**
* Register a custom formatter
*/
registerFormatter(name, formatter) {
this.customFormatters.set(name, formatter);
}
/**
* Unregister a custom formatter
*/
unregisterFormatter(name) {
return this.customFormatters.delete(name);
}
/**
* Update formatting options
*/
updateOptions(options) {
this.options = {
...this.options,
...options,
};
}
/**
* Get current options
*/
getOptions() {
return { ...this.options };
}
}
exports.VariableFormatter = VariableFormatter;
//# sourceMappingURL=variable-formatter.js.map