UNPKG

n8n

Version:

n8n Workflow Automation Tool

290 lines 12.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.listNodeDiscriminators = listNodeDiscriminators; exports.resolveNodeTypeDefinition = resolveNodeTypeDefinition; exports.resolveBuiltinNodeDefinitionDirs = resolveBuiltinNodeDefinitionDirs; const backend_common_1 = require("@n8n/backend-common"); const node_fs_1 = require("node:fs"); const node_path_1 = require("node:path"); function isValidPathComponent(component) { if (!component || component.trim() === '') return false; if (component.includes('\0')) return false; if (component.includes('/') || component.includes('\\')) return false; if (component === '..' || component.startsWith('..')) return false; return true; } function parseNodeId(nodeId) { if (nodeId.startsWith('@n8n/')) { const withoutPrefix = nodeId.slice(5); const dotIndex = withoutPrefix.indexOf('.'); if (dotIndex === -1) return null; return { packageName: withoutPrefix.slice(0, dotIndex), nodeName: withoutPrefix.slice(dotIndex + 1), }; } const dotIndex = nodeId.indexOf('.'); if (dotIndex === -1) return null; return { packageName: nodeId.slice(0, dotIndex), nodeName: nodeId.slice(dotIndex + 1), }; } function toSnakeCase(str) { return str .replace(/([A-Z])/g, '_$1') .toLowerCase() .replace(/^_/, '') .replace(/[-\s]+/g, '_'); } function getNodesPaths(nodeDefinitionDirs) { return nodeDefinitionDirs.map((dir) => (0, backend_common_1.safeJoinPath)(dir, 'nodes')); } function findNodeDir(parsed, nodesPaths) { for (const nodesPath of nodesPaths) { try { const nodeDir = (0, backend_common_1.safeJoinPath)(nodesPath, parsed.packageName, parsed.nodeName); if ((0, node_fs_1.existsSync)(nodeDir)) return { nodesPath, nodeDir }; } catch { continue; } } if (parsed.nodeName.endsWith('Tool')) { const baseName = parsed.nodeName.slice(0, -4); for (const nodesPath of nodesPaths) { try { const nodeDir = (0, backend_common_1.safeJoinPath)(nodesPath, parsed.packageName, baseName); if ((0, node_fs_1.existsSync)(nodeDir)) return { nodesPath, nodeDir }; } catch { continue; } } } return null; } function getNodeVersions(nodeId, nodeDefinitionDirs) { const parsed = parseNodeId(nodeId); if (!parsed) return []; const nodesPaths = getNodesPaths(nodeDefinitionDirs); const found = findNodeDir(parsed, nodesPaths); if (!found) return []; try { const entries = (0, node_fs_1.readdirSync)(found.nodeDir, { withFileTypes: true }); const versions = []; for (const entry of entries) { if (entry.isFile() && entry.name.startsWith('v') && entry.name.endsWith('.ts') && entry.name !== 'index.ts' && !entry.name.endsWith('.schema.js')) { versions.push(entry.name.replace('.ts', '')); } else if (entry.isDirectory() && /^v\d+$/.test(entry.name)) { versions.push(entry.name); } } versions.sort((a, b) => { const aNum = parseInt(a.slice(1), 10); const bNum = parseInt(b.slice(1), 10); return bNum - aNum; }); return versions; } catch { return []; } } function tryResolveNodeFilePath(nodeId, version, nodeDefinitionDirs, discriminators) { const parsed = parseNodeId(nodeId); if (!parsed) return { error: `Invalid node ID format: '${nodeId}'` }; if (!isValidPathComponent(parsed.packageName) || !isValidPathComponent(parsed.nodeName)) { return { error: `Invalid node ID: '${nodeId}'` }; } const nodesPaths = getNodesPaths(nodeDefinitionDirs); const found = findNodeDir(parsed, nodesPaths); if (!found) { return { error: `Node type '${nodeId}' not found. Use search-nodes to find the correct node ID.`, }; } try { return resolveFilePath(nodeId, version, found.nodeDir, nodeDefinitionDirs, discriminators); } catch { return { error: 'Invalid path - path traversal detected' }; } } function resolveResourceOperationFile(nodeId, nodeDir, targetVersion, resources, discriminators) { if (!discriminators?.resource || !discriminators?.operation) { return { error: `Node '${nodeId}' requires resource and operation discriminators. Available resources: ${resources.join(', ')}.`, }; } if (!isValidPathComponent(discriminators.resource) || !isValidPathComponent(discriminators.operation)) { return { error: 'Invalid discriminator value' }; } const resourceDir = (0, backend_common_1.safeJoinPath)(nodeDir, targetVersion, `resource_${toSnakeCase(discriminators.resource)}`); if (!(0, node_fs_1.existsSync)(resourceDir)) { return { error: `Invalid resource '${discriminators.resource}' for node '${nodeId}'. Available: ${resources.join(', ')}`, }; } const filePath = (0, backend_common_1.safeJoinPath)(nodeDir, targetVersion, `resource_${toSnakeCase(discriminators.resource)}`, `operation_${toSnakeCase(discriminators.operation)}.ts`); if (!(0, node_fs_1.existsSync)(filePath)) { const ops = (0, node_fs_1.readdirSync)(resourceDir) .filter((f) => f.startsWith('operation_') && f.endsWith('.ts')) .map((f) => f.replace('operation_', '').replace('.ts', '')); return { error: `Invalid operation '${discriminators.operation}' for resource '${discriminators.resource}'. Available: ${ops.join(', ')}`, }; } return { filePath }; } function resolveModeFile(nodeId, nodeDir, targetVersion, modes, discriminators) { if (!discriminators?.mode) { return { error: `Node '${nodeId}' requires mode discriminator. Available modes: ${modes.join(', ')}.`, }; } if (!isValidPathComponent(discriminators.mode)) { return { error: 'Invalid mode value' }; } const filePath = (0, backend_common_1.safeJoinPath)(nodeDir, targetVersion, `mode_${toSnakeCase(discriminators.mode)}.ts`); if (!(0, node_fs_1.existsSync)(filePath)) { return { error: `Invalid mode '${discriminators.mode}' for node '${nodeId}'. Available: ${modes.join(', ')}`, }; } return { filePath }; } function resolveFilePath(nodeId, version, nodeDir, nodeDefinitionDirs, discriminators) { let targetVersion = version; if (!targetVersion) { const versions = getNodeVersions(nodeId, nodeDefinitionDirs); if (versions.length === 0) return { error: `No versions found for node '${nodeId}'` }; targetVersion = versions[0]; } if (!targetVersion.startsWith('v')) { targetVersion = `v${targetVersion.replace('.', '')}`; } else { targetVersion = `v${targetVersion.slice(1).replace('.', '')}`; } const versionDir = (0, backend_common_1.safeJoinPath)(nodeDir, targetVersion); const isSplit = (0, node_fs_1.existsSync)(versionDir) && (0, node_fs_1.statSync)(versionDir).isDirectory(); if (isSplit) { const entries = (0, node_fs_1.readdirSync)(versionDir, { withFileTypes: true }); const resources = entries .filter((e) => e.isDirectory() && e.name.startsWith('resource_')) .map((e) => e.name.replace('resource_', '')); const modes = entries .filter((e) => e.isFile() && e.name.startsWith('mode_') && e.name.endsWith('.ts')) .map((e) => e.name.replace('mode_', '').replace('.ts', '')); if (resources.length > 0) { return resolveResourceOperationFile(nodeId, nodeDir, targetVersion, resources, discriminators); } if (modes.length > 0) { return resolveModeFile(nodeId, nodeDir, targetVersion, modes, discriminators); } return { error: `Node '${nodeId}' has split structure but no recognized discriminators` }; } const filePath = (0, backend_common_1.safeJoinPath)(nodeDir, `${targetVersion}.ts`); if (!(0, node_fs_1.existsSync)(filePath)) { return { error: `Version '${version}' not found for node '${nodeId}'` }; } return { filePath }; } function listNodeDiscriminators(nodeId, nodeDefinitionDirs) { const parsed = parseNodeId(nodeId); if (!parsed) return null; if (!isValidPathComponent(parsed.packageName) || !isValidPathComponent(parsed.nodeName)) return null; const nodesPaths = getNodesPaths(nodeDefinitionDirs); const found = findNodeDir(parsed, nodesPaths); if (!found) return null; const { nodeDir } = found; const versions = getNodeVersions(nodeId, nodeDefinitionDirs); if (versions.length === 0) return null; const versionDir = (0, backend_common_1.safeJoinPath)(nodeDir, versions[0]); if (!(0, node_fs_1.existsSync)(versionDir) || !(0, node_fs_1.statSync)(versionDir).isDirectory()) return null; const entries = (0, node_fs_1.readdirSync)(versionDir, { withFileTypes: true }); const resourceDirs = entries.filter((e) => e.isDirectory() && e.name.startsWith('resource_')); if (resourceDirs.length === 0) return null; const resources = resourceDirs.map((dir) => { const resourceName = dir.name.replace('resource_', ''); const resourcePath = (0, backend_common_1.safeJoinPath)(versionDir, dir.name); const ops = (0, node_fs_1.readdirSync)(resourcePath) .filter((f) => f.startsWith('operation_') && f.endsWith('.ts')) .map((f) => f.replace('operation_', '').replace('.ts', '')); return { name: resourceName, operations: ops }; }); return { resources }; } function resolveNodeTypeDefinition(nodeId, nodeDefinitionDirs, options) { const nodesPaths = getNodesPaths(nodeDefinitionDirs); if (!nodesPaths.some((p) => (0, node_fs_1.existsSync)(p))) { return { content: '', error: 'Node types directory not found. Types may not have been generated yet.', }; } const discriminators = options ? { resource: options.resource, operation: options.operation, mode: options.mode } : undefined; let result = tryResolveNodeFilePath(nodeId, options?.version, nodeDefinitionDirs, discriminators); if (result.error && nodeId.endsWith('Tool')) { const baseNodeId = nodeId.slice(0, -4); result = tryResolveNodeFilePath(baseNodeId, options?.version, nodeDefinitionDirs, discriminators); } if (result.error || !result.filePath) { return { content: '', error: result.error ?? `Node type '${nodeId}' not found.` }; } try { const content = (0, node_fs_1.readFileSync)(result.filePath, 'utf-8'); const actualVersion = result.filePath.match(/\/(v\d+)(?:\/|\.ts)/)?.[1]; return { content, version: actualVersion }; } catch (error) { return { content: '', error: `Error reading node definition for '${nodeId}': ${error instanceof Error ? error.message : 'Unknown error'}`, }; } } function resolveBuiltinNodeDefinitionDirs() { const dirs = []; for (const packageId of ['n8n-nodes-base', '@n8n/n8n-nodes-langchain']) { try { const packageJsonPath = require.resolve(`${packageId}/package.json`); const distDir = (0, node_path_1.dirname)(packageJsonPath); const nodeDefsDir = (0, backend_common_1.safeJoinPath)(distDir, 'dist', 'node-definitions'); if ((0, node_fs_1.existsSync)(nodeDefsDir)) { dirs.push(nodeDefsDir); } } catch { } } return dirs; } //# sourceMappingURL=node-definition-resolver.js.map