UNPKG

n8n-editor-ui

Version:

Workflow Editor UI for n8n

932 lines (931 loc) 36.2 kB
import { o as __toESM } from "./chunk-6z4oVpB-.js"; import { C as computed, It as ref, _t as watch } from "./vue.runtime.esm-bundler-tP5dCd7J.js"; import { M as useLocalStorage, _t as useI18n } from "./_MapCache-ficiegRb.js"; import { r as require_get } from "./truncate-B1HVeveJ.js"; import { $r as BulkCommand, Ec as STORES, Gn as useNodeHelpers, Jr as CanvasNodeDirtiness, Kn as injectWorkflowState, N as useWorkflowsStore, O as useTelemetry, Qr as AddNodeCommand, Rn as useWorkflowHelpers, Ui as isEmpty, Xr as useHistoryStore, Yn as useDocumentTitle, Zr as AddConnectionCommand, b as useExternalHooks, gs as useSettingsStore, ii as RemoveNodeCommand, n as useWorkflowSaving, ri as RemoveConnectionCommand, ti as EnableNodeToggleCommand, v as useToast, wt as displayForm } from "./builder.store-sBTWwxRU.js"; import { Jr as createHeartbeatMessage, M as IN_PROGRESS_EXECUTION_ID, Ua as NodeConnectionTypes, Vi as createRunExecutionData, Vs as LOCAL_STORAGE_RUN_DATA_WORKER, qi as generateNodesGraph, zt as CHAT_TRIGGER_NODE_TYPE } from "./constants-BbpucWL4.js"; import { k as defineStore, r as useRootStore } from "./_baseOrderBy-B_6CV8x6.js"; import { t as retry } from "./retry-dP46utx2.js"; import { t as useExecutionsStore } from "./executions.store-CXjGPxs0.js"; import { t as useCanvasOperations } from "./useCanvasOperations-CAHezq7d.js"; const useHeartbeat = (options) => { const { interval, onHeartbeat } = options; const heartbeatTimer = ref(null); const startHeartbeat = () => { heartbeatTimer.value = setInterval(onHeartbeat, interval); }; const stopHeartbeat = () => { if (heartbeatTimer.value) { clearInterval(heartbeatTimer.value); heartbeatTimer.value = null; } }; return { startHeartbeat, stopHeartbeat }; }; const useReconnectTimer = ({ onAttempt, onAttemptScheduled }) => { const initialReconnectDelay = 1e3; const maxReconnectDelay = 15e3; const reconnectTimer = ref(null); const reconnectAttempts = ref(0); const scheduleReconnect = () => { const delay = Math.min(initialReconnectDelay * 2 ** reconnectAttempts.value, maxReconnectDelay); reconnectAttempts.value++; onAttemptScheduled(delay); reconnectTimer.value = setTimeout(() => { onAttempt(); }, delay); }; const stopReconnectTimer = () => { if (reconnectTimer.value) { clearTimeout(reconnectTimer.value); reconnectTimer.value = null; } }; const resetConnectionAttempts = () => { reconnectAttempts.value = 0; }; return { scheduleReconnect, stopReconnectTimer, resetConnectionAttempts }; }; const useWebSocketClient = (options) => { const isConnected = ref(false); const socket = ref(null); const { startHeartbeat, stopHeartbeat } = useHeartbeat({ interval: 3e4, onHeartbeat: () => { socket.value?.send(JSON.stringify(createHeartbeatMessage())); } }); const onConnected = () => { socket.value?.removeEventListener("open", onConnected); isConnected.value = true; startHeartbeat(); reconnectTimer.resetConnectionAttempts(); }; const onConnectionLost = (event) => { console.warn(`[WebSocketClient] Connection lost, code=${event.code ?? "unknown"}`); disconnect(); reconnectTimer.scheduleReconnect(); }; const onMessage = (event) => { options.onMessage(event.data); }; const onError = (error) => { console.warn("[WebSocketClient] Connection error:", error); }; const disconnect = () => { if (socket.value) { stopHeartbeat(); reconnectTimer.stopReconnectTimer(); socket.value.removeEventListener("message", onMessage); socket.value.removeEventListener("error", onError); socket.value.removeEventListener("close", onConnectionLost); socket.value.close(1e3); socket.value = null; } isConnected.value = false; }; const connect = () => { disconnect(); socket.value = new WebSocket(options.url); socket.value.addEventListener("open", onConnected); socket.value.addEventListener("message", onMessage); socket.value.addEventListener("error", onError); socket.value.addEventListener("close", onConnectionLost); socket.value.binaryType = "arraybuffer"; }; const reconnectTimer = useReconnectTimer({ onAttempt: connect, onAttemptScheduled: (delay) => { console.log(`[WebSocketClient] Attempting to reconnect in ${delay}ms`); } }); const sendMessage = (serializedMessage) => { if (!isConnected.value || !socket.value) throw new Error("Not connected to the server"); socket.value.send(serializedMessage); }; return { isConnected, connect, disconnect, sendMessage }; }; const useEventSourceClient = (options) => { const isConnected = ref(false); const eventSource = ref(null); const onConnected = () => { isConnected.value = true; reconnectTimer.resetConnectionAttempts(); }; const onConnectionLost = () => { console.warn("[EventSourceClient] Connection lost"); isConnected.value = false; reconnectTimer.scheduleReconnect(); }; const onMessage = (event) => { options.onMessage(event.data); }; const disconnect = () => { if (eventSource.value) { reconnectTimer.stopReconnectTimer(); eventSource.value.close(); eventSource.value = null; } isConnected.value = false; }; const connect = () => { disconnect(); eventSource.value = new EventSource(options.url, { withCredentials: true }); eventSource.value.addEventListener("open", onConnected); eventSource.value.addEventListener("message", onMessage); eventSource.value.addEventListener("close", onConnectionLost); }; const reconnectTimer = useReconnectTimer({ onAttempt: connect, onAttemptScheduled: (delay) => { console.log(`[EventSourceClient] Attempting to reconnect in ${delay}ms`); } }); const sendMessage = (_) => {}; return { isConnected, connect, disconnect, sendMessage }; }; /** * @license * Copyright 2019 Google LLC * SPDX-License-Identifier: Apache-2.0 */ var proxyMarker = Symbol("Comlink.proxy"); var createEndpoint = Symbol("Comlink.endpoint"); var releaseProxy = Symbol("Comlink.releaseProxy"); var finalizer = Symbol("Comlink.finalizer"); var throwMarker = Symbol("Comlink.thrown"); var isObject = (val) => typeof val === "object" && val !== null || typeof val === "function"; var transferHandlers = new Map([["proxy", { canHandle: (val) => isObject(val) && val[proxyMarker], serialize(obj) { const { port1, port2 } = new MessageChannel(); expose(obj, port1); return [port2, [port2]]; }, deserialize(port) { port.start(); return wrap(port); } }], ["throw", { canHandle: (value) => isObject(value) && throwMarker in value, serialize({ value }) { let serialized; if (value instanceof Error) serialized = { isError: true, value: { message: value.message, name: value.name, stack: value.stack } }; else serialized = { isError: false, value }; return [serialized, []]; }, deserialize(serialized) { if (serialized.isError) throw Object.assign(new Error(serialized.value.message), serialized.value); throw serialized.value; } }]]); function isAllowedOrigin(allowedOrigins, origin) { for (const allowedOrigin of allowedOrigins) { if (origin === allowedOrigin || allowedOrigin === "*") return true; if (allowedOrigin instanceof RegExp && allowedOrigin.test(origin)) return true; } return false; } function expose(obj, ep = globalThis, allowedOrigins = ["*"]) { ep.addEventListener("message", function callback(ev) { if (!ev || !ev.data) return; if (!isAllowedOrigin(allowedOrigins, ev.origin)) { console.warn(`Invalid origin '${ev.origin}' for comlink proxy`); return; } const { id, type, path } = Object.assign({ path: [] }, ev.data); const argumentList = (ev.data.argumentList || []).map(fromWireValue); let returnValue; try { const parent = path.slice(0, -1).reduce((obj$1, prop) => obj$1[prop], obj); const rawValue = path.reduce((obj$1, prop) => obj$1[prop], obj); switch (type) { case "GET": returnValue = rawValue; break; case "SET": parent[path.slice(-1)[0]] = fromWireValue(ev.data.value); returnValue = true; break; case "APPLY": returnValue = rawValue.apply(parent, argumentList); break; case "CONSTRUCT": returnValue = proxy(new rawValue(...argumentList)); break; case "ENDPOINT": { const { port1, port2 } = new MessageChannel(); expose(obj, port2); returnValue = transfer(port1, [port1]); } break; case "RELEASE": returnValue = void 0; break; default: return; } } catch (value) { returnValue = { value, [throwMarker]: 0 }; } Promise.resolve(returnValue).catch((value) => { return { value, [throwMarker]: 0 }; }).then((returnValue$1) => { const [wireValue, transferables] = toWireValue(returnValue$1); ep.postMessage(Object.assign(Object.assign({}, wireValue), { id }), transferables); if (type === "RELEASE") { ep.removeEventListener("message", callback); closeEndPoint(ep); if (finalizer in obj && typeof obj[finalizer] === "function") obj[finalizer](); } }).catch((error) => { const [wireValue, transferables] = toWireValue({ value: /* @__PURE__ */ new TypeError("Unserializable return value"), [throwMarker]: 0 }); ep.postMessage(Object.assign(Object.assign({}, wireValue), { id }), transferables); }); }); if (ep.start) ep.start(); } function isMessagePort(endpoint) { return endpoint.constructor.name === "MessagePort"; } function closeEndPoint(endpoint) { if (isMessagePort(endpoint)) endpoint.close(); } function wrap(ep, target) { return createProxy(ep, [], target); } function throwIfProxyReleased(isReleased) { if (isReleased) throw new Error("Proxy has been released and is not useable"); } function releaseEndpoint(ep) { return requestResponseMessage(ep, { type: "RELEASE" }).then(() => { closeEndPoint(ep); }); } var proxyCounter = /* @__PURE__ */ new WeakMap(); var proxyFinalizers = "FinalizationRegistry" in globalThis && new FinalizationRegistry((ep) => { const newCount = (proxyCounter.get(ep) || 0) - 1; proxyCounter.set(ep, newCount); if (newCount === 0) releaseEndpoint(ep); }); function registerProxy(proxy$1, ep) { const newCount = (proxyCounter.get(ep) || 0) + 1; proxyCounter.set(ep, newCount); if (proxyFinalizers) proxyFinalizers.register(proxy$1, ep, proxy$1); } function unregisterProxy(proxy$1) { if (proxyFinalizers) proxyFinalizers.unregister(proxy$1); } function createProxy(ep, path = [], target = function() {}) { let isProxyReleased = false; const proxy$1 = new Proxy(target, { get(_target, prop) { throwIfProxyReleased(isProxyReleased); if (prop === releaseProxy) return () => { unregisterProxy(proxy$1); releaseEndpoint(ep); isProxyReleased = true; }; if (prop === "then") { if (path.length === 0) return { then: () => proxy$1 }; const r = requestResponseMessage(ep, { type: "GET", path: path.map((p) => p.toString()) }).then(fromWireValue); return r.then.bind(r); } return createProxy(ep, [...path, prop]); }, set(_target, prop, rawValue) { throwIfProxyReleased(isProxyReleased); const [value, transferables] = toWireValue(rawValue); return requestResponseMessage(ep, { type: "SET", path: [...path, prop].map((p) => p.toString()), value }, transferables).then(fromWireValue); }, apply(_target, _thisArg, rawArgumentList) { throwIfProxyReleased(isProxyReleased); const last = path[path.length - 1]; if (last === createEndpoint) return requestResponseMessage(ep, { type: "ENDPOINT" }).then(fromWireValue); if (last === "bind") return createProxy(ep, path.slice(0, -1)); const [argumentList, transferables] = processArguments(rawArgumentList); return requestResponseMessage(ep, { type: "APPLY", path: path.map((p) => p.toString()), argumentList }, transferables).then(fromWireValue); }, construct(_target, rawArgumentList) { throwIfProxyReleased(isProxyReleased); const [argumentList, transferables] = processArguments(rawArgumentList); return requestResponseMessage(ep, { type: "CONSTRUCT", path: path.map((p) => p.toString()), argumentList }, transferables).then(fromWireValue); } }); registerProxy(proxy$1, ep); return proxy$1; } function myFlat(arr) { return Array.prototype.concat.apply([], arr); } function processArguments(argumentList) { const processed = argumentList.map(toWireValue); return [processed.map((v) => v[0]), myFlat(processed.map((v) => v[1]))]; } var transferCache = /* @__PURE__ */ new WeakMap(); function transfer(obj, transfers) { transferCache.set(obj, transfers); return obj; } function proxy(obj) { return Object.assign(obj, { [proxyMarker]: true }); } function toWireValue(value) { for (const [name, handler] of transferHandlers) if (handler.canHandle(value)) { const [serializedValue, transferables] = handler.serialize(value); return [{ type: "HANDLER", name, value: serializedValue }, transferables]; } return [{ type: "RAW", value }, transferCache.get(value) || []]; } function fromWireValue(value) { switch (value.type) { case "HANDLER": return transferHandlers.get(value.name).deserialize(value.value); case "RAW": return value.value; } } function requestResponseMessage(ep, msg, transfers) { return new Promise((resolve) => { const id = generateUUID(); ep.addEventListener("message", function l(ev) { if (!ev.data || !ev.data.id || ev.data.id !== id) return; ep.removeEventListener("message", l); resolve(ev.data); }); if (ep.start) ep.start(); ep.postMessage(Object.assign({ id }, msg), transfers); }); } function generateUUID() { return new Array(4).fill(0).map(() => Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(16)).join("-"); } const runDataWorker = wrap(new Worker(new URL( /* @vite-ignore */ "/%7B%7BBASE_PATH%7D%7D/assets/worker-CIvWfg3a.js", "" + import.meta.url ), { type: "module" })); const usePushConnectionStore = defineStore(STORES.PUSH, () => { const rootStore = useRootStore(); const settingsStore = useSettingsStore(); const isRunDataWorkerEnabled = useLocalStorage(LOCAL_STORAGE_RUN_DATA_WORKER, false); const outgoingQueue = ref([]); const isConnectionRequested = ref(false); const onMessageReceivedHandlers = ref([]); const addEventListener = (handler) => { onMessageReceivedHandlers.value.push(handler); return () => { const index = onMessageReceivedHandlers.value.indexOf(handler); if (index !== -1) onMessageReceivedHandlers.value.splice(index, 1); }; }; const useWebSockets = computed(() => settingsStore.pushBackend === "websocket"); const getConnectionUrl = () => { const restUrl = rootStore.restUrl; const url$1 = `/push?pushRef=${rootStore.pushRef}`; if (useWebSockets.value) { const { protocol, host } = window.location; return `${restUrl.startsWith("http") ? restUrl.replace(/^http/, "ws") : `${protocol === "https:" ? "wss" : "ws"}://${host + restUrl}`}${url$1}`; } else return `${restUrl}${url$1}`; }; async function onMessage(data) { if (data instanceof ArrayBuffer) if (isRunDataWorkerEnabled.value) { await runDataWorker.onNodeExecuteAfterData(data); return; } else data = new TextDecoder("utf-8").decode(new Uint8Array(data)); let parsedData; try { parsedData = JSON.parse(data); } catch (error) { return; } onMessageReceivedHandlers.value.forEach((handler) => handler(parsedData)); } const url = getConnectionUrl(); const client = computed(() => useWebSockets.value ? useWebSocketClient({ url, onMessage }) : useEventSourceClient({ url, onMessage })); function serializeAndSend(message) { if (client.value.isConnected.value) client.value.sendMessage(JSON.stringify(message)); else outgoingQueue.value.push(message); } const pushConnect = () => { isConnectionRequested.value = true; client.value.connect(); }; const pushDisconnect = () => { isConnectionRequested.value = false; client.value.disconnect(); }; watch(() => client.value.isConnected.value, (didConnect) => { if (!didConnect) return; if (outgoingQueue.value.length) { for (const message of outgoingQueue.value) serializeAndSend(message); outgoingQueue.value = []; } }); const clearQueue = () => { outgoingQueue.value = []; }; return { isConnected: computed(() => client.value.isConnected.value), isConnectionRequested, onMessageReceivedHandlers, addEventListener, pushConnect, pushDisconnect, send: serializeAndSend, clearQueue }; }); function shouldCommandMarkDirty(command, nodeName, siblingCommands, getIncomingConnections, getOutgoingConnectors) { if (command instanceof BulkCommand) return command.commands.some((cmd) => shouldCommandMarkDirty(cmd, nodeName, command.commands, getIncomingConnections, getOutgoingConnectors)); if (command instanceof AddConnectionCommand) return command.connectionData[1]?.node === nodeName; if (command instanceof RemoveConnectionCommand) { const [from, to] = command.connectionData; if (to.node !== nodeName) return false; return siblingCommands.some((sibling) => sibling instanceof RemoveNodeCommand && sibling.node.name === from.node); } const incomingNodes = Object.values(getIncomingConnections(nodeName)).flat().flat().filter((connection) => connection !== null).map((connection) => connection.node); if (command instanceof AddNodeCommand) return incomingNodes.includes(command.node.name); if (command instanceof EnableNodeToggleCommand) return incomingNodes.includes(command.nodeName) && (command.newState || Object.keys(getOutgoingConnectors(command.nodeName)).some((type) => type !== NodeConnectionTypes.Main)); return false; } function findLoop(nodeName, visited, getIncomingConnections) { const index = visited.indexOf(nodeName); if (index >= 0) return visited.slice(index); const newVisited = [...visited, nodeName]; for (const [type, typeConnections] of Object.entries(getIncomingConnections(nodeName))) { if (type !== NodeConnectionTypes.Main) continue; for (const connections of typeConnections) for (const { node } of connections ?? []) { const loop = findLoop(node, newVisited, getIncomingConnections); if (loop) return loop; } } } function useNodeDirtiness() { const historyStore = useHistoryStore(); const workflowsStore = useWorkflowsStore(); function getParentSubNodes(nodeName) { return Object.entries(workflowsStore.incomingConnectionsByNodeName(nodeName)).filter(([type]) => type !== NodeConnectionTypes.Main).flatMap(([, typeConnections]) => typeConnections.flat().filter((conn) => conn !== null)); } function getDirtinessByParametersUpdate(nodeName, after) { if ((workflowsStore.getParametersLastUpdate(nodeName) ?? 0) > after) return CanvasNodeDirtiness.PARAMETERS_UPDATED; for (const connection of getParentSubNodes(nodeName)) if (getDirtinessByParametersUpdate(connection.node, after) !== void 0) return CanvasNodeDirtiness.UPSTREAM_DIRTY; } function getDirtinessByConnectionsUpdate(nodeName, after) { for (let i = historyStore.undoStack.length - 1; i >= 0; i--) { const command = historyStore.undoStack[i]; if (command.getTimestamp() < after) break; if (shouldCommandMarkDirty(command, nodeName, [], workflowsStore.incomingConnectionsByNodeName, workflowsStore.outgoingConnectionsByNodeName)) return CanvasNodeDirtiness.INCOMING_CONNECTIONS_UPDATED; } for (const connection of getParentSubNodes(nodeName)) if (getDirtinessByConnectionsUpdate(connection.node, after) !== void 0) return CanvasNodeDirtiness.UPSTREAM_DIRTY; } const depthByName = computed(() => { const depth = {}; function setDepthRecursively(nodeName, current, visited) { if (visited.has(nodeName)) return; const myVisited = new Set(visited); myVisited.add(nodeName); for (const [type, typeConnections] of Object.entries(workflowsStore.outgoingConnectionsByNodeName(nodeName))) { if (type !== NodeConnectionTypes.Main) continue; for (const connections of typeConnections) for (const { node } of connections ?? []) { if (!depth[node] || depth[node] > current) depth[node] = current; setDepthRecursively(node, current + 1, myVisited); } } } for (const startNode of workflowsStore.allNodes) { if (Object.keys(workflowsStore.incomingConnectionsByNodeName(startNode.name)).length > 0) continue; depth[startNode.name] = 0; setDepthRecursively(startNode.name, 1, /* @__PURE__ */ new Set()); } return depth; }); return { dirtinessByName: computed(() => { const dirtiness = {}; const runDataByNode = workflowsStore.getWorkflowRunData ?? {}; function setDirtiness(nodeName, value) { dirtiness[nodeName] = dirtiness[nodeName] ?? value; const loop = findLoop(nodeName, [], workflowsStore.incomingConnectionsByNodeName); if (!loop) return; const loopEntryNodeName = [...loop].sort((a, b) => (depthByName.value[a] ?? Number.MAX_SAFE_INTEGER) - (depthByName.value[b] ?? Number.MAX_SAFE_INTEGER))?.[0]; if (loopEntryNodeName && depthByName.value[loopEntryNodeName]) dirtiness[loopEntryNodeName] = dirtiness[loopEntryNodeName] ?? CanvasNodeDirtiness.UPSTREAM_DIRTY; } for (const [nodeName, runData] of Object.entries(runDataByNode)) { const runAt = runData[0]?.startTime ?? 0; if (!runAt) continue; const parameterUpdate = getDirtinessByParametersUpdate(nodeName, runAt); if (parameterUpdate) { setDirtiness(nodeName, parameterUpdate); continue; } const connectionUpdate = getDirtinessByConnectionsUpdate(nodeName, runAt); if (connectionUpdate) { setDirtiness(nodeName, connectionUpdate); continue; } if (Object.values(workflowsStore.incomingConnectionsByNodeName(nodeName)).flat().flat().filter((connection) => connection !== null).some((connection) => { return (workflowsStore.getPinnedDataLastUpdate(connection.node) ?? 0) > runAt; })) { setDirtiness(nodeName, CanvasNodeDirtiness.PINNED_DATA_UPDATED); continue; } if ((workflowsStore.getPinnedDataLastRemovedAt(nodeName) ?? 0) > runAt) { setDirtiness(nodeName, CanvasNodeDirtiness.PINNED_DATA_UPDATED); continue; } } return dirtiness; }) }; } var LOCAL_STORAGE_AGENT_REQUESTS = "N8N_AGENT_REQUESTS"; const useAgentRequestStore = defineStore("agentRequest", () => { const agentRequests = useLocalStorage(LOCAL_STORAGE_AGENT_REQUESTS, {}); const ensureWorkflowAndNodeExist = (workflowId, nodeId) => { if (!agentRequests.value[workflowId]) agentRequests.value[workflowId] = {}; if (!agentRequests.value[workflowId][nodeId]) agentRequests.value[workflowId][nodeId] = { query: {} }; }; const getAgentRequests = (workflowId, nodeId) => { return agentRequests.value[workflowId]?.[nodeId]?.query || {}; }; const getQueryValue = (workflowId, nodeId, paramName) => { const query = agentRequests.value[workflowId]?.[nodeId]?.query; if (typeof query === "string") return; return query?.[paramName]; }; const setAgentRequestForNode = (workflowId, nodeId, request) => { ensureWorkflowAndNodeExist(workflowId, nodeId); agentRequests.value[workflowId][nodeId] = { ...request, query: typeof request.query === "string" ? request.query : { ...request.query } }; }; const clearAgentRequests = (workflowId, nodeId) => { if (agentRequests.value[workflowId]) agentRequests.value[workflowId][nodeId] = { query: {} }; }; const clearAllAgentRequests = (workflowId) => { if (workflowId) agentRequests.value[workflowId] = {}; else agentRequests.value = {}; }; const getAgentRequest = (workflowId, nodeId) => { if (agentRequests.value[workflowId]) return agentRequests.value[workflowId]?.[nodeId]; }; return { agentRequests, getAgentRequests, getQueryValue, setAgentRequestForNode, clearAgentRequests, clearAllAgentRequests, getAgentRequest }; }); var import_get = /* @__PURE__ */ __toESM(require_get()); function useRunWorkflow(useRunWorkflowOpts) { const workflowHelpers = useWorkflowHelpers(); const i18n = useI18n(); const toast = useToast(); const telemetry = useTelemetry(); const externalHooks = useExternalHooks(); const settingsStore = useSettingsStore(); const agentRequestStore = useAgentRequestStore(); const rootStore = useRootStore(); const pushConnectionStore = usePushConnectionStore(); const workflowsStore = useWorkflowsStore(); const workflowState = useRunWorkflowOpts.workflowState ?? injectWorkflowState(); const nodeHelpers = useNodeHelpers({ workflowState }); const workflowSaving = useWorkflowSaving({ router: useRunWorkflowOpts.router, workflowState }); const executionsStore = useExecutionsStore(); const { dirtinessByName } = useNodeDirtiness(); const { startChat } = useCanvasOperations(); const workflowObject = computed(() => workflowsStore.workflowObject); function sortNodesByYPosition(nodes) { return [...nodes].sort((a, b) => { const nodeA = workflowsStore.getNodeByName(a)?.position ?? [0, 0]; const nodeB = workflowsStore.getNodeByName(b)?.position ?? [0, 0]; const nodeAYPosition = nodeA[1]; const nodeBYPosition = nodeB[1]; if (nodeAYPosition === nodeBYPosition) return 0; return nodeAYPosition > nodeBYPosition ? 1 : -1; }); } async function runWorkflowApi(runData) { if (!pushConnectionStore.isConnected) throw new Error(i18n.baseText("workflowRun.noActiveConnectionToTheServer")); workflowState.setActiveExecutionId(null); let response; try { response = await workflowsStore.runWorkflow(runData); } catch (error) { workflowState.setActiveExecutionId(void 0); throw error; } const workflowExecutionIdIsNew = workflowsStore.previousExecutionId !== response.executionId; const workflowExecutionIdIsPending = workflowsStore.activeExecutionId === null; if (response.executionId && workflowExecutionIdIsNew && workflowExecutionIdIsPending) workflowState.setActiveExecutionId(response.executionId); if (response.waitingForWebhook === true) workflowsStore.executionWaitingForWebhook = true; return response; } async function runWorkflow(options) { if (workflowsStore.activeExecutionId) return; toast.clearAllStickyNotifications(); try { let directParentNodes = []; if (options.destinationNode !== void 0) directParentNodes = workflowObject.value.getParentNodes(options.destinationNode.nodeName, NodeConnectionTypes.Main, -1); const runData = workflowsStore.getWorkflowRunData; if (!workflowsStore.isWorkflowSaved[workflowsStore.workflowId]) await workflowSaving.saveCurrentWorkflow(); const workflowData = await workflowHelpers.getWorkflowDataToSave(); const { startNodeNames } = consolidateRunDataAndStartNodes(directParentNodes, runData, workflowData.pinData, workflowObject.value); const destinationNodeType = options.destinationNode ? workflowsStore.getNodeByName(options.destinationNode.nodeName)?.type : ""; let executedNode; let triggerToStartFrom; if (startNodeNames.length === 0 && directParentNodes.length === 0 && "destinationNode" in options && options.destinationNode !== void 0) { executedNode = options.destinationNode.nodeName; startNodeNames.push(options.destinationNode.nodeName); } else if (options.triggerNode && options.nodeData && !options.rerunTriggerNode) startNodeNames.push(...workflowObject.value.getChildNodes(options.triggerNode, NodeConnectionTypes.Main, 1)); else if (options.destinationNode) executedNode = options.destinationNode.nodeName; if (options.triggerNode) triggerToStartFrom = { name: options.triggerNode, data: options.nodeData }; if (options.destinationNode && (workflowsStore.checkIfNodeHasChatParent(options.destinationNode.nodeName) || destinationNodeType === "@n8n/n8n-nodes-langchain.chatTrigger") && options.source !== "RunData.ManualChatMessage") { const startNode = workflowObject.value.getStartNode(options.destinationNode.nodeName); if (startNode && startNode.type === "@n8n/n8n-nodes-langchain.chatTrigger") { const chatHasInputData = nodeHelpers.getNodeInputData(startNode, 0, 0, "input")?.length > 0; const chatHasPinData = !!workflowData.pinData?.[startNode.name]; if (!chatHasInputData && !chatHasPinData) { workflowsStore.chatPartialExecutionDestinationNode = options.destinationNode.nodeName; startChat(); return; } } } const triggers = workflowData.nodes.filter((node) => node.type.toLowerCase().includes("trigger") && !node.disabled); if (!options.destinationNode && options.source !== "RunData.ManualChatMessage" && workflowData.nodes.some((node) => node.type === "@n8n/n8n-nodes-langchain.chatTrigger")) { if (triggers.filter((node) => node.type !== "@n8n/n8n-nodes-langchain.chatTrigger").length) { const chatTriggerNode = workflowData.nodes.find((node) => node.type === CHAT_TRIGGER_NODE_TYPE); if (chatTriggerNode) chatTriggerNode.disabled = true; } } const isPartialExecution = options.destinationNode !== void 0; const startNodes = sortNodesByYPosition(startNodeNames).map((name) => { let sourceData = (0, import_get.default)(runData, [ name, 0, "source", 0 ], null); if (sourceData === null) { const parentNodes = workflowObject.value.getParentNodes(name, NodeConnectionTypes.Main, 1); sourceData = (0, import_get.default)(workflowHelpers.executeData(workflowObject.value.connectionsBySourceNode, parentNodes, name, NodeConnectionTypes.Main, 0), [ "source", NodeConnectionTypes.Main, 0 ], null); } return { name, sourceData }; }).filter((node) => { if (options.destinationNode && workflowsStore.checkIfNodeHasChatParent(options.destinationNode.nodeName)) return node.name !== options.destinationNode.nodeName; return true; }); const startRunData = { workflowData, runData: isPartialExecution ? runData ?? void 0 : void 0, startNodes, triggerToStartFrom }; if ("destinationNode" in options) { startRunData.destinationNode = options.destinationNode; const nodeId = workflowsStore.getNodeByName(options.destinationNode?.nodeName ?? "")?.id; if (workflowObject.value.id && nodeId) { const agentRequest = agentRequestStore.getAgentRequest(workflowObject.value.id, nodeId); if (agentRequest) startRunData.agentRequest = { query: agentRequest.query ?? {}, tool: { name: agentRequest.toolName ?? "" } }; } } if (startRunData.runData) { const nodeNames = Object.entries(dirtinessByName.value).flatMap(([nodeName, dirtiness]) => dirtiness ? [nodeName] : []); startRunData.dirtyNodeNames = nodeNames.length > 0 ? nodeNames : void 0; } const executionData = { id: IN_PROGRESS_EXECUTION_ID, finished: false, mode: "manual", status: "running", createdAt: /* @__PURE__ */ new Date(), startedAt: /* @__PURE__ */ new Date(), stoppedAt: void 0, workflowId: workflowObject.value.id, executedNode, triggerNode: triggerToStartFrom?.name, data: createRunExecutionData({ resultData: { runData: startRunData.runData ?? {}, pinData: workflowData.pinData } }), workflowData: { id: workflowsStore.workflowId, name: workflowData.name, active: workflowData.active, createdAt: 0, updatedAt: 0, ...workflowData } }; workflowState.setWorkflowExecutionData(executionData); nodeHelpers.updateNodesExecutionIssues(); useDocumentTitle().setDocumentTitle(workflowObject.value.name, "EXECUTING"); const runWorkflowApiResponse = await runWorkflowApi(startRunData); const pinData = workflowData.pinData ?? {}; const getTestUrl = (() => { return (node) => { const path = node.parameters.path || node.parameters.options?.path || node.webhookId; return `${rootStore.formTestUrl}/${path}`; }; })(); try { await displayForm({ nodes: workflowData.nodes, runData: workflowsStore.getWorkflowExecution?.data?.resultData?.runData, destinationNode: options.destinationNode?.nodeName, triggerNode: options.triggerNode, pinData, directParentNodes, source: options.source, getTestUrl }); } catch (error) {} await externalHooks.run("workflowRun.runWorkflow", { nodeName: options.destinationNode?.nodeName, source: options.source }); return runWorkflowApiResponse; } catch (error) { workflowState.setWorkflowExecutionData(null); useDocumentTitle().setDocumentTitle(workflowObject.value.name, "ERROR"); toast.showError(error, i18n.baseText("workflowRun.showError.title")); return; } } function consolidateRunDataAndStartNodes(directParentNodes, runData, pinData, workflow) { const startNodeNames = /* @__PURE__ */ new Set(); let newRunData; if (runData !== null && Object.keys(runData).length !== 0) { newRunData = {}; for (const directParentNode of directParentNodes) { const parentNodes = workflow.getParentNodes(directParentNode, NodeConnectionTypes.Main); if (workflow.nodes[directParentNode].disabled) continue; parentNodes.push(directParentNode); for (const parentNode of parentNodes) { if (!runData[parentNode]?.length && !pinData?.[parentNode]?.length || runData[parentNode]?.[0]?.error !== void 0) { startNodeNames.add(parentNode); break; } if (runData[parentNode] && !runData[parentNode]?.[0]?.error) newRunData[parentNode] = runData[parentNode]?.slice(0, 1); } } if (isEmpty(newRunData)) newRunData = void 0; } return { runData: newRunData, startNodeNames: [...startNodeNames] }; } async function stopCurrentExecution() { const executionId = workflowsStore.activeExecutionId; let stopData; if (!executionId) return; try { stopData = await executionsStore.stopCurrentExecution(executionId); } catch (error) { const execution = await workflowsStore.getExecution(executionId); if (execution === void 0) toast.showMessage({ title: i18n.baseText("nodeView.showMessage.stopExecutionCatch.unsaved.title"), message: i18n.baseText("nodeView.showMessage.stopExecutionCatch.unsaved.message"), type: "success" }); else if (execution?.finished) { const executedData = { data: execution.data, workflowData: workflowsStore.workflow, finished: execution.finished, mode: execution.mode, startedAt: execution.startedAt, stoppedAt: execution.stoppedAt }; workflowState.setWorkflowExecutionData(executedData); toast.showMessage({ title: i18n.baseText("nodeView.showMessage.stopExecutionCatch.title"), message: i18n.baseText("nodeView.showMessage.stopExecutionCatch.message"), type: "success" }); } else toast.showError(error, i18n.baseText("nodeView.showError.stopExecution.title")); } finally { if (!await retry(async () => { const execution = await workflowsStore.getExecution(executionId); if (!["running", "waiting"].includes(execution?.status)) { workflowState.markExecutionAsStopped(stopData); return true; } return false; }, 250, 20)) workflowState.markExecutionAsStopped(stopData); } } async function stopWaitingForWebhook() { try { await workflowsStore.removeTestWebhook(workflowsStore.workflowId); } catch (error) { toast.showError(error, i18n.baseText("nodeView.showError.stopWaitingForWebhook.title")); return; } } async function runEntireWorkflow(source, triggerNode) { workflowHelpers.getWorkflowDataToSave().then((workflowData) => { const telemetryPayload = { workflow_id: workflowObject.value.id, node_graph_string: JSON.stringify(generateNodesGraph(workflowData, workflowHelpers.getNodeTypes(), { isCloudDeployment: settingsStore.isCloudDeployment }).nodeGraph), button_type: source }; telemetry.track("User clicked execute workflow button", telemetryPayload); externalHooks.run("nodeView.onRunWorkflow", telemetryPayload); }); runWorkflow({ triggerNode: triggerNode ?? workflowsStore.selectedTriggerNodeName }); } return { consolidateRunDataAndStartNodes, runEntireWorkflow, runWorkflow, runWorkflowApi, stopCurrentExecution, stopWaitingForWebhook, sortNodesByYPosition }; } export { proxy as a, usePushConnectionStore as i, useAgentRequestStore as n, wrap as o, useNodeDirtiness as r, useRunWorkflow as t };