const JS_RESERVED = new Set([ "abstract", "arguments", "await", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "debugger", "default", "delete", "do", "double", "else", "enum", "eval", "export", "extends", "false", "final", "finally", "float", "for", "function", "goto", "if", "implements", "import", "in", "instanceof", "int", "interface", "let", "long", "native", "new", "null", "package", "private", "protected", "public", "return", "short", "static", "super", "switch", "synchronized", "this", "throw", "throws", "transient", "true", "try", "typeof", "undefined", "var", "void", "volatile", "while", "with", "yield" ]); /** * Sanitize a tool name into a valid JavaScript identifier. * Replaces hyphens, dots, and spaces with `_`, strips other invalid chars, * prefixes digit-leading names with `_`, and appends `_` to JS reserved words. */ export function sanitizeToolName(name: string): string { if (!name) return "_"; // Replace common separators with underscores let sanitized = name.replace(/[-.\s]/g, "_"); // Strip any remaining non-identifier characters sanitized = sanitized.replace(/[^a-zA-Z0-9_$]/g, ""); if (!sanitized) return "_"; // Prefix with _ if starts with a digit if (/^[0-9]/.test(sanitized)) { sanitized = "_" + sanitized; } // Append _ to reserved words if (JS_RESERVED.has(sanitized)) { sanitized = sanitized + "_"; } return sanitized; } export function toPascalCase(str: string) { return str .replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()) .replace(/^[a-z]/, (letter) => letter.toUpperCase()); } /** * Escape a character as a unicode escape sequence if it is a control character. */ function escapeControlChar(ch: string): string { const code = ch.charCodeAt(0); if (code <= 0x1f || code === 0x7f) { return "\\u" + code.toString(16).padStart(4, "0"); } return ch; } /** * Quote a property name if needed. * Escapes backslashes, quotes, and control characters. */ export function quoteProp(name: string): string { if (!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name)) { let escaped = ""; for (const ch of name) { if (ch === "\\") escaped += "\\\\"; else if (ch === '"') escaped += '\\"'; else if (ch === "\n") escaped += "\\n"; else if (ch === "\r") escaped += "\\r"; else if (ch === "\t") escaped += "\\t"; else if (ch === "\u2028") escaped += "\\u2028"; else if (ch === "\u2029") escaped += "\\u2029"; else escaped += escapeControlChar(ch); } return `"${escaped}"`; } return name; } /** * Escape a string for use inside a double-quoted TypeScript string literal. */ export function escapeStringLiteral(s: string): string { let out = ""; for (const ch of s) { if (ch === "\\") out += "\\\\"; else if (ch === '"') out += '\\"'; else if (ch === "\n") out += "\\n"; else if (ch === "\r") out += "\\r"; else if (ch === "\t") out += "\\t"; else if (ch === "\u2028") out += "\\u2028"; else if (ch === "\u2029") out += "\\u2029"; else out += escapeControlChar(ch); } return out; } /** * Escape a string for use inside a JSDoc comment. * Prevents premature comment closure from star-slash sequences. */ export function escapeJsDoc(text: string): string { return text.replace(/\*\//g, "*\\/"); }