Spaces:
Sleeping
Sleeping
File size: 5,713 Bytes
da2e594 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | /**
* 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;
} |