Spaces:
Sleeping
Sleeping
| /** | |
| * Normalizes node type from n8n export format to database format | |
| * | |
| * Examples: | |
| * - 'n8n-nodes-base.httpRequest' → 'nodes-base.httpRequest' | |
| * - '@n8n/n8n-nodes-langchain.agent' → 'nodes-langchain.agent' | |
| * - 'n8n-nodes-langchain.chatTrigger' → 'nodes-langchain.chatTrigger' | |
| * - 'nodes-base.slack' → 'nodes-base.slack' (unchanged) | |
| * | |
| * @param nodeType The node type to normalize | |
| * @returns The normalized node type | |
| */ | |
| export function normalizeNodeType(nodeType: string): string { | |
| // Handle n8n-nodes-base -> nodes-base | |
| if (nodeType.startsWith('n8n-nodes-base.')) { | |
| return nodeType.replace('n8n-nodes-base.', 'nodes-base.'); | |
| } | |
| // Handle @n8n/n8n-nodes-langchain -> nodes-langchain | |
| if (nodeType.startsWith('@n8n/n8n-nodes-langchain.')) { | |
| return nodeType.replace('@n8n/n8n-nodes-langchain.', 'nodes-langchain.'); | |
| } | |
| // Handle n8n-nodes-langchain -> nodes-langchain (without @n8n/ prefix) | |
| if (nodeType.startsWith('n8n-nodes-langchain.')) { | |
| return nodeType.replace('n8n-nodes-langchain.', 'nodes-langchain.'); | |
| } | |
| // Return unchanged if already normalized or unknown format | |
| return nodeType; | |
| } | |
| /** | |
| * Gets alternative node type formats to try for lookups | |
| * | |
| * @param nodeType The original node type | |
| * @returns Array of alternative formats to try | |
| */ | |
| export function getNodeTypeAlternatives(nodeType: string): string[] { | |
| // Defensive: validate input to prevent TypeError when nodeType is undefined/null/empty | |
| if (!nodeType || typeof nodeType !== 'string' || nodeType.trim() === '') { | |
| return []; | |
| } | |
| const alternatives: string[] = []; | |
| // Add lowercase version | |
| alternatives.push(nodeType.toLowerCase()); | |
| // If it has a prefix, try case variations on the node name part | |
| if (nodeType.includes('.')) { | |
| const [prefix, nodeName] = nodeType.split('.'); | |
| // Try different case variations for the node name | |
| if (nodeName && nodeName.toLowerCase() !== nodeName) { | |
| alternatives.push(`${prefix}.${nodeName.toLowerCase()}`); | |
| } | |
| // For camelCase names like "chatTrigger", also try with capital first letter variations | |
| // e.g., "chattrigger" -> "chatTrigger" | |
| if (nodeName && nodeName.toLowerCase() === nodeName && nodeName.length > 1) { | |
| // Try to detect common patterns and create camelCase version | |
| const camelCaseVariants = generateCamelCaseVariants(nodeName); | |
| camelCaseVariants.forEach(variant => { | |
| alternatives.push(`${prefix}.${variant}`); | |
| }); | |
| } | |
| } | |
| // If it's just a bare node name, try with common prefixes | |
| if (!nodeType.includes('.')) { | |
| alternatives.push(`nodes-base.${nodeType}`); | |
| alternatives.push(`nodes-langchain.${nodeType}`); | |
| // Also try camelCase variants for bare names | |
| const camelCaseVariants = generateCamelCaseVariants(nodeType); | |
| camelCaseVariants.forEach(variant => { | |
| alternatives.push(`nodes-base.${variant}`); | |
| alternatives.push(`nodes-langchain.${variant}`); | |
| }); | |
| } | |
| // Normalize all alternatives and combine with originals | |
| const normalizedAlternatives = alternatives.map(alt => normalizeNodeType(alt)); | |
| // Combine original alternatives with normalized ones and remove duplicates | |
| return [...new Set([...alternatives, ...normalizedAlternatives])]; | |
| } | |
| /** | |
| * Generate camelCase variants for a lowercase string | |
| * @param str The lowercase string | |
| * @returns Array of possible camelCase variants | |
| */ | |
| function generateCamelCaseVariants(str: string): string[] { | |
| const variants: string[] = []; | |
| // Common patterns for n8n nodes | |
| const patterns = [ | |
| // Pattern: wordTrigger (e.g., chatTrigger, webhookTrigger) | |
| /^(.+)(trigger|node|request|response)$/i, | |
| // Pattern: httpRequest, mysqlDatabase | |
| /^(http|mysql|postgres|mongo|redis|mqtt|smtp|imap|ftp|ssh|api)(.+)$/i, | |
| // Pattern: googleSheets, microsoftTeams | |
| /^(google|microsoft|amazon|slack|discord|telegram)(.+)$/i, | |
| ]; | |
| for (const pattern of patterns) { | |
| const match = str.toLowerCase().match(pattern); | |
| if (match) { | |
| const [, first, second] = match; | |
| // Capitalize the second part | |
| variants.push(first.toLowerCase() + second.charAt(0).toUpperCase() + second.slice(1).toLowerCase()); | |
| } | |
| } | |
| // Generic camelCase: capitalize after common word boundaries | |
| if (variants.length === 0) { | |
| // Try splitting on common boundaries and capitalizing | |
| const words = str.split(/[-_\s]+/); | |
| if (words.length > 1) { | |
| const camelCase = words[0].toLowerCase() + words.slice(1).map(w => | |
| w.charAt(0).toUpperCase() + w.slice(1).toLowerCase() | |
| ).join(''); | |
| variants.push(camelCase); | |
| } | |
| } | |
| return variants; | |
| } | |
| /** | |
| * Constructs the workflow node type from package name and normalized node type | |
| * This creates the format that n8n expects in workflow definitions | |
| * | |
| * Examples: | |
| * - ('n8n-nodes-base', 'nodes-base.webhook') → 'n8n-nodes-base.webhook' | |
| * - ('@n8n/n8n-nodes-langchain', 'nodes-langchain.agent') → '@n8n/n8n-nodes-langchain.agent' | |
| * | |
| * @param packageName The package name from the database | |
| * @param nodeType The normalized node type from the database | |
| * @returns The workflow node type for use in n8n workflows | |
| */ | |
| export function getWorkflowNodeType(packageName: string, nodeType: string): string { | |
| // Extract just the node name from the normalized type | |
| const nodeName = nodeType.split('.').pop() || nodeType; | |
| // Construct the full workflow type based on package | |
| if (packageName === 'n8n-nodes-base') { | |
| return `n8n-nodes-base.${nodeName}`; | |
| } else if (packageName === '@n8n/n8n-nodes-langchain') { | |
| return `@n8n/n8n-nodes-langchain.${nodeName}`; | |
| } | |
| // Fallback for unknown packages - return as is | |
| return nodeType; | |
| } |