UNPKG

n8n

Version:

n8n Workflow Automation Tool

221 lines • 8.78 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.extractResolvedNodeParameters = extractResolvedNodeParameters; const instance_ai_1 = require("@n8n/instance-ai"); const n8n_workflow_1 = require("n8n-workflow"); const MAX_RESOLVED_LEAF_CHARS = 8_000; const UNRECONSTRUCTABLE_CONTEXT_VARS = [ '$ai', '$response', '$request', '$pageCount', '$secrets', '$vars', ]; function mentionsUnreconstructableVar(raw) { return UNRECONSTRUCTABLE_CONTEXT_VARS.some((variable) => raw.includes(variable)); } function capResolvedLeaf(value) { if (value === null || value === undefined) return value; const isComplex = typeof value === 'object'; const serialized = isComplex ? JSON.stringify(value) : String(value); if (serialized.length <= MAX_RESOLVED_LEAF_CHARS) return value; return { _truncated: true, preview: serialized.slice(0, MAX_RESOLVED_LEAF_CHARS), originalLength: serialized.length, }; } function classifyExpressionFailure(raw, errorMessage) { if (mentionsUnreconstructableVar(raw)) return 'unreconstructable-context'; for (const variable of UNRECONSTRUCTABLE_CONTEXT_VARS) { if (errorMessage.includes(variable)) return 'unreconstructable-context'; } return 'expression-error'; } function findConnectionOutputIndex(workflow, parentNodeName, currentNodeName) { const destinations = workflow.connectionsByDestinationNode[currentNodeName]?.main; if (!destinations) return 0; for (const mainConnections of destinations) { for (const connection of mainConnections ?? []) { if (connection?.node === parentNodeName) { return connection.index ?? 0; } } } return 0; } function annotatePairedItem(items, destinationInputIndex) { return items.map((item, itemIndex) => ({ ...item, pairedItem: { item: itemIndex, input: destinationInputIndex }, })); } function reconstructExecuteData(workflow, currentNodeName, runIndex, runData) { const inputName = 'main'; const currentNode = workflow.getNode(currentNodeName); const currentNodeShim = currentNode ?? { name: currentNodeName }; const currentNodeRun = runData[currentNodeName]?.[runIndex]; let destinationInputIndex = 0; let firstSource; for (let i = 0; i < (currentNodeRun?.source?.length ?? 0); i++) { const entry = currentNodeRun?.source?.[i]; if (entry !== null && entry !== undefined) { firstSource = entry; destinationInputIndex = i; break; } } if (firstSource) { const previousNode = firstSource.previousNode; const previousNodeOutput = firstSource.previousNodeOutput ?? 0; const previousNodeRun = firstSource.previousNodeRun ?? 0; const parentRun = runData[previousNode]?.[previousNodeRun]; if (parentRun?.data?.[inputName]) { return { executeData: { node: currentNodeShim, data: parentRun.data, source: { [inputName]: currentNodeRun?.source ?? [] }, }, connectionInputData: annotatePairedItem(parentRun.data[inputName][previousNodeOutput] ?? [], destinationInputIndex), }; } } const parentNodes = workflow.getParentNodes(currentNodeName, inputName, 1); for (const parentNodeName of parentNodes) { const parentRuns = runData[parentNodeName]; if (!parentRuns || parentRuns.length <= runIndex) continue; const parentRun = parentRuns[runIndex]; if (!parentRun?.data?.[inputName]) continue; const outputIndex = findConnectionOutputIndex(workflow, parentNodeName, currentNodeName); return { executeData: { node: currentNodeShim, data: parentRun.data, source: { [inputName]: [ { previousNode: parentNodeName, previousNodeOutput: outputIndex, previousNodeRun: runIndex, }, ], }, }, connectionInputData: annotatePairedItem(parentRun.data[inputName]?.[outputIndex] ?? [], 0), }; } return { executeData: { node: currentNodeShim, data: {}, source: null }, connectionInputData: [], }; } async function extractResolvedNodeParameters(executionRepository, nodeTypes, executionId, nodeName, options) { const execution = await executionRepository.findSingleExecution(executionId, { includeData: true, unflattenData: true, }); if (!execution) { throw new Error(`Execution ${executionId} not found`); } const workflowData = execution.workflowData; const nodeJson = workflowData.nodes.find((n) => n.name === nodeName); if (!nodeJson) { throw new Error(`Node "${nodeName}" not found in execution ${executionId}`); } const runData = execution.data?.resultData?.runData ?? {}; const nodeRuns = runData[nodeName] ?? []; const itemIndex = options?.itemIndex ?? 0; const runIndex = options?.runIndex ?? Math.max(nodeRuns.length - 1, 0); const workflow = new n8n_workflow_1.Workflow({ id: workflowData.id, name: workflowData.name, nodes: workflowData.nodes, connections: workflowData.connections, active: false, nodeTypes, staticData: workflowData.staticData, settings: workflowData.settings ?? {}, pinData: workflowData.pinData, }); const { executeData, connectionInputData } = reconstructExecuteData(workflow, nodeName, runIndex, runData); const additionalKeys = { $execution: { id: executionId, mode: execution.mode === 'manual' ? 'test' : 'production', resumeUrl: '', resumeFormUrl: '', }, $vars: {}, }; if (nodeJson.type === n8n_workflow_1.HTTP_REQUEST_NODE_TYPE) { additionalKeys.$pageCount = 0; additionalKeys.$response = { statusCode: 200, headers: {}, body: {} }; additionalKeys.$request = { headers: {}, body: {}, qs: {} }; } const runExecutionData = execution.data ?? (0, n8n_workflow_1.createEmptyRunExecutionData)(); const failedExpressions = []; const emptyResolutions = []; const walk = (value, path) => { if (typeof value === 'string' && value.startsWith('=')) { try { const resolvedValue = workflow.expression.getParameterValue(value, runExecutionData, runIndex, itemIndex, nodeName, connectionInputData, 'manual', additionalKeys, executeData, false, {}, nodeName); if (resolvedValue === null || resolvedValue === undefined || resolvedValue === '') { const entry = { path, raw: value, resolved: resolvedValue, }; if (mentionsUnreconstructableVar(value)) { entry.reason = 'unreconstructable-context'; } emptyResolutions.push(entry); } return capResolvedLeaf(resolvedValue); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); failedExpressions.push({ path, raw: value, error: errorMessage, reason: classifyExpressionFailure(value, errorMessage), }); return null; } } if (Array.isArray(value)) { return value.map((item, i) => walk(item, `${path}[${i}]`)); } if (value !== null && typeof value === 'object') { const out = {}; for (const [k, v] of Object.entries(value)) { const childPath = path === '' ? k : `${path}.${k}`; out[k] = walk(v, childPath); } return out; } return value; }; const parameters = (nodeJson.parameters ?? {}); const resolvedTree = walk(parameters, ''); const resolved = (0, instance_ai_1.wrapUntrustedData)(JSON.stringify(resolvedTree, null, 2), 'execution-output', `resolved-parameters:${nodeName}`); return { nodeName, runIndex, itemIndex, parameters, resolved, failedExpressions, emptyResolutions, }; } //# sourceMappingURL=extract-resolved-node-parameters.js.map