n8n-editor-ui
Version:
Workflow Editor UI for n8n
24,193 lines • 911 kB
JavaScript
import { o as __toESM, t as __commonJSMin } from "./chunk-6z4oVpB-.js";
import { B as isMemoSame, C as computed, Cn as toDisplayString, D as createElementBlock, Dt as getCurrentScope, E as createCommentVNode, Et as effectScope, F as getCurrentInstance, Ft as readonly, G as nextTick, Gt as unref, Ht as toRefs, It as ref, J as onBeforeUnmount, L as h, M as createVNode, Mt as markRaw, Nt as onScopeDispose, O as createPropsRestProxy, P as defineComponent, Pt as reactive, Q as onUnmounted, T as createBlock, Tt as customRef, Ut as toValue, Vt as toRef, W as mergeProps, Z as onMounted, _ as Fragment, _t as watch, at as renderSlot, b as Teleport, bn as normalizeStyle, bt as withCtx, cn as isObject$2, ct as resolveDynamicComponent, en as hyphenate, et as openBlock, i as createApp, it as renderList, j as createTextVNode, jt as isRef, mn as isString$1, n as Transition$1, nt as provide, ot as resolveComponent, p as vShow, pt as useSlots, q as onBeforeMount, st as resolveDirective, tn as init_shared_esm_bundler, ut as useAttrs, vn as normalizeClass, w as createBaseVNode, xt as withDirectives, z as inject, zt as shallowRef } from "./vue.runtime.esm-bundler-tP5dCd7J.js";
import { M as useMediaQuery, R as useStorage$1, dt as addHeaders, ft as addNodeTranslation, gt as useI18n, j as useLocalStorage, pt as i18n, x as useAsyncState } from "./_MapCache-B159LOu5.js";
import { B as removeClass, I as addClass, Pt as isClient$1, R as getStyle, S as useGlobalComponentSettings, l as require_memoize, r as require_get, s as require_toString, t as truncate, v as useRoute, x as createEventBus } from "./truncate-Dc5upPom.js";
import { $ as AI_CATEGORY_LANGUAGE_MODELS, $i as displayParameterPath, $n as SEGMENT_NODE_TYPE, Aa as jsonParse, Ac as AI_ASSISTANT_MAX_CONTENT_LENGTH, An as MICROSOFT_EXCEL_NODE_TYPE, Bt as CHAT_TRIGGER_NODE_TYPE, Co as ApplicationError, Cr as XERO_NODE_TYPE, Da as deepCopy, Do as getResourcePermissions, E as TEMPLATES_NODES_FILTER, Er as ZENDESK_TRIGGER_NODE_TYPE, Et as RECOMMENDED_NODES, F as APPEND_ATTRIBUTION_DEFAULT_PATH, Fo as MAIN_HEADER_TABS, Ft as AI_MCP_TOOL_NODE_TYPE, Go as EXPERIMENT_TEMPLATE_RECO_V2_KEY, Gt as CREDENTIAL_ONLY_HTTP_NODE_VERSION, Ho as CONTACT_PROMPT_MODAL_KEY, Ht as CODE_NODE_TYPE, I as CUSTOM_API_CALL_KEY, In as NON_ACTIVATABLE_TRIGGER_NODE_TYPES, Io as VIEWS, Is as LOCAL_STORAGE_THEME, It as AI_TRANSFORM_NODE_TYPE, Ji as mapConnectionsByDestination, Jn as QUICKBOOKS_NODE_TYPE, Jo as FROM_AI_PARAMETERS_MODAL_KEY, Ka as NodeConnectionTypes, Ko as EXPERIMENT_TEMPLATE_RECO_V3_KEY, Kt as CREDENTIAL_ONLY_NODE_PREFIX, L as DATA_TYPE_ICON_MAP, Ln as NOTION_TRIGGER_NODE_TYPE, Lo as ABOUT_MODAL_KEY, Lt as BAMBOO_HR_NODE_TYPE, Mn as MODULE_ENABLED_NODES, Ni as require__baseFindIndex, Oo as hasScope$1, P as PLACEHOLDER_FILLED_AT_EXECUTION_TIME, Pa as sleep$1, Pi as isExpression, Pn as NODE_POSITION_CONFLICT_ALLOWLIST, Qn as SCHEDULE_TRIGGER_NODE_TYPE, Qo as MFA_SETUP_MODAL_KEY, Qt as DISCORD_NODE_TYPE, Ro as CHANGE_PASSWORD_MODAL_KEY, Sa as isResourceLocatorValue, Sr as WORKFLOW_TRIGGER_NODE_TYPE, St as HUMAN_IN_THE_LOOP_CATEGORY, Tn as MANUAL_CHAT_TRIGGER_NODE_TYPE, Tr as ZENDESK_NODE_TYPE, Tt as PRE_BUILT_AGENTS_COLLECTION, U as INSECURE_CONNECTION_WARNING, Uo as DUPLICATE_MODAL_KEY, Vo as CONFIRM_PASSWORD_MODAL_KEY, Vt as CLEARBIT_NODE_TYPE, Wa as ExpressionError, Wi as createEmptyRunExecutionData, Wn as PAGERDUTY_NODE_TYPE, Wo as EXPERIMENT_TEMPLATES_DATA_QUALITY_KEY, Xi as getChildNodes, Xo as IMPORT_WORKFLOW_URL_MODAL_KEY, Xs as WorkflowStateKey, Y as AI_CATEGORY_AGENTS, Yi as getNodeByName, Yo as IMPORT_CURL_MODAL_KEY, Yr as ROLE, Zi as generateNodesGraph, Zn as SALESFORCE_NODE_TYPE, Zo as LOG_STREAM_MODAL_KEY, _n as IF_NODE_TYPE, _o as require_isEqual, _r as WAIT_NODE_TYPE, _t as CORE_NODES_CATEGORY, a as MAX_WORKFLOW_NAME_LENGTH, aa as getNodeWebhookUrl, an as EXECUTE_COMMAND_NODE_TYPE, ao as FORM_TRIGGER_NODE_TYPE$1, ar as SLACK_NODE_TYPE, as as PRE_BUILT_AGENTS_MODAL_KEY, at as AI_CATEGORY_ROOT_NODES, bn as JIRA_NODE_TYPE, br as WOOCOMMERCE_TRIGGER_NODE_TYPE, ci as require__baseFlatten, cr as SPREADSHEET_FILE_NODE_TYPE, cs as VERSIONS_MODAL_KEY, dn as FORM_TRIGGER_NODE_TYPE, ds as WORKFLOW_ACTIVE_MODAL_KEY, ea as getConnectionTypes, en as ELASTIC_SECURITY_NODE_TYPE, eo as CHAT_TRIGGER_NODE_TYPE$1, er as SERVICENOW_NODE_TYPE, fn as GITHUB_TRIGGER_NODE_TYPE, fr as SWITCH_NODE_TYPE, fs as WORKFLOW_DIFF_MODAL_KEY, ga as mergeIssues, gi as Workflow, gn as HUBSPOT_TRIGGER_NODE_TYPE, go as WEBHOOK_NODE_TYPE$1, gs as WORKFLOW_SHARE_MODAL_KEY, gt as BETA_NODES, h as DOCS_DOMAIN, ha as makeNodeName, hn as HTTP_REQUEST_NODE_TYPE, hs as WORKFLOW_SETTINGS_MODAL_KEY, i as DUPLICATE_POSTFFIX, ia as getNodeParametersIssues, is as NPS_SURVEY_MODAL_KEY, jc as AI_NODES_PACKAGE_NAME, jn as MICROSOFT_TEAMS_NODE_TYPE, ka as isObjectEmpty, ki as splitExpression, lr as START_NODE_TYPE, ls as WHATS_NEW_MODAL_KEY, mo as TRIMMED_TASK_DATA_CONNECTIONS_KEY, mr as THE_HIVE_TRIGGER_NODE_TYPE, ms as WORKFLOW_HISTORY_VERSION_RESTORE, n as DEFAULT_NODETYPE_VERSION, na as getNodeOutputs, nn as EMAIL_SEND_NODE_TYPE, no as EVALUATION_NODE_TYPE, ns as NEW_ASSISTANT_SESSION_MODAL, nt as AI_CATEGORY_OTHER_TOOLS, or as SPLIT_IN_BATCHES_NODE_TYPE, os as PROMPT_MFA_CODE_MODAL_KEY, pc as EnterpriseEditionFeature, pn as GOOGLE_SHEETS_NODE_TYPE, po as SEND_AND_WAIT_OPERATION, pr as TELEGRAM_NODE_TYPE, ps as WORKFLOW_EXTRACTION_NAME_MODAL_KEY, pt as AI_SUBCATEGORY, q as NODE_MIN_INPUT_ITEMS_COUNT, qa as nodeConnectionTypes, qo as EXTERNAL_SECRETS_PROVIDER_MODAL_KEY, r as DEFAULT_WORKFLOW_PAGE_SIZE, ra as getNodeParameters, rn as ERROR_TRIGGER_NODE_TYPE, ro as EVALUATION_TRIGGER_NODE_TYPE, rs as NODE_PINNING_MODAL_KEY, s as PLACEHOLDER_EMPTY_WORKFLOW_ID, si as require_pick, ss as SETUP_CREDENTIALS_MODAL_KEY, st as AI_CATEGORY_TOOLS, t as DEFAULT_NEW_WORKFLOW_NAME, ta as getNodeInputs, tc as EXPERIMENTS_TO_TRACK, tr as SET_NODE_TYPE, tt as AI_CATEGORY_MEMORY, un as FORM_NODE_TYPE, uo as MANUAL_TRIGGER_NODE_TYPE, us as WORKFLOW_ACTIVATION_CONFLICTING_WEBHOOK_MODAL_KEY, vr as WEBHOOK_NODE_TYPE, vt as DEFAULT_SUBCATEGORY, wc as MAIN_AUTH_FIELD_NAME, wn as LIST_LIKE_NODE_OPERATIONS, ws as LOCAL_STORAGE_EXPERIMENT_OVERRIDES, xn as JIRA_TRIGGER_NODE_TYPE, xr as WORKABLE_TRIGGER_NODE_TYPE, yi as require__baseIteratee, yn as ITEM_LISTS_NODE_TYPE, yo as require_keys, z as MAPPING_PARAMS, zo as CHAT_EMBED_MODAL_KEY, zt as CALENDLY_TRIGGER_NODE_TYPE } from "./constants-CX1ozgFz.js";
import { n as require__isIterateeCall, o as require_identity, r as require__baseRest, t as require_merge, y as require_isArrayLike } from "./merge-BArJwbQo.js";
import { C as INSTANCE_VERSION_HEADER, E as N8N_IO_BASE_URL, S as INSTANCE_ID_HEADER, T as BROWSER_ID_STORAGE_KEY, c as get$3, d as post, f as request, h as axios_default, k as defineStore, l as getFullApiResponse, p as streamRequest, r as useRootStore, s as ResponseError, t as require__baseOrderBy, u as makeRestApiRequest, w as NPM_COMMUNITY_NODE_SEARCH_API_URL } from "./_baseOrderBy-DL6Sy5xK.js";
import { t as require_dateformat } from "./dateformat-CMLc6OKJ.js";
import { r as require_toNumber, t as useDebounce } from "./useDebounce-RgPaX171.js";
function createLoadingComponent(options) {
let afterLeaveTimer;
const afterLeaveFlag = ref(false);
const data = reactive({
...options,
originalPosition: "",
originalOverflow: "",
visible: false
});
function setText(text) {
data.text = text;
}
function destroySelf() {
const target = data.parent;
const ns = vm.ns;
if (!target.vLoadingAddClassList) {
let loadingNumber = target.getAttribute("loading-number");
loadingNumber = Number.parseInt(loadingNumber) - 1;
if (!loadingNumber) {
removeClass(target, ns.bm("parent", "relative"));
target.removeAttribute("loading-number");
} else target.setAttribute("loading-number", loadingNumber.toString());
removeClass(target, ns.bm("parent", "hidden"));
}
removeElLoadingChild();
loadingInstance.unmount();
}
function removeElLoadingChild() {
var _a, _b;
(_b = (_a = vm.$el) == null ? void 0 : _a.parentNode) == null || _b.removeChild(vm.$el);
}
function close() {
var _a;
if (options.beforeClose && !options.beforeClose()) return;
afterLeaveFlag.value = true;
clearTimeout(afterLeaveTimer);
afterLeaveTimer = window.setTimeout(handleAfterLeave, 400);
data.visible = false;
(_a = options.closed) == null || _a.call(options);
}
function handleAfterLeave() {
if (!afterLeaveFlag.value) return;
const target = data.parent;
afterLeaveFlag.value = false;
target.vLoadingAddClassList = void 0;
destroySelf();
}
const loadingInstance = createApp(defineComponent({
name: "ElLoading",
setup(_, { expose }) {
const { ns, zIndex } = useGlobalComponentSettings("loading");
expose({
ns,
zIndex
});
return () => {
const svg = data.spinner || data.svg;
const spinner = h("svg", {
class: "circular",
viewBox: data.svgViewBox ? data.svgViewBox : "0 0 50 50",
...svg ? { innerHTML: svg } : {}
}, [h("circle", {
class: "path",
cx: "25",
cy: "25",
r: "20",
fill: "none"
})]);
const spinnerText = data.text ? h("p", { class: ns.b("text") }, [data.text]) : void 0;
return h(Transition$1, {
name: ns.b("fade"),
onAfterLeave: handleAfterLeave
}, { default: withCtx(() => [withDirectives(createVNode("div", {
style: { backgroundColor: data.background || "" },
class: [
ns.b("mask"),
data.customClass,
data.fullscreen ? "is-fullscreen" : ""
]
}, [h("div", { class: ns.b("spinner") }, [spinner, spinnerText])]), [[vShow, data.visible]])]) });
};
}
}));
const vm = loadingInstance.mount(document.createElement("div"));
return {
...toRefs(data),
setText,
removeElLoadingChild,
close,
handleAfterLeave,
vm,
get $el() {
return vm.$el;
}
};
}
init_shared_esm_bundler();
var fullscreenInstance = void 0;
var Loading = function(options = {}) {
if (!isClient$1) return void 0;
const resolved = resolveOptions(options);
if (resolved.fullscreen && fullscreenInstance) return fullscreenInstance;
const instance = createLoadingComponent({
...resolved,
closed: () => {
var _a;
(_a = resolved.closed) == null || _a.call(resolved);
if (resolved.fullscreen) fullscreenInstance = void 0;
}
});
addStyle(resolved, resolved.parent, instance);
addClassList(resolved, resolved.parent, instance);
resolved.parent.vLoadingAddClassList = () => addClassList(resolved, resolved.parent, instance);
let loadingNumber = resolved.parent.getAttribute("loading-number");
if (!loadingNumber) loadingNumber = "1";
else loadingNumber = `${Number.parseInt(loadingNumber) + 1}`;
resolved.parent.setAttribute("loading-number", loadingNumber);
resolved.parent.appendChild(instance.$el);
nextTick(() => instance.visible.value = resolved.visible);
if (resolved.fullscreen) fullscreenInstance = instance;
return instance;
};
var resolveOptions = (options) => {
var _a, _b, _c, _d;
let target;
if (isString$1(options.target)) target = (_a = document.querySelector(options.target)) != null ? _a : document.body;
else target = options.target || document.body;
return {
parent: target === document.body || options.body ? document.body : target,
background: options.background || "",
svg: options.svg || "",
svgViewBox: options.svgViewBox || "",
spinner: options.spinner || false,
text: options.text || "",
fullscreen: target === document.body && ((_b = options.fullscreen) != null ? _b : true),
lock: (_c = options.lock) != null ? _c : false,
customClass: options.customClass || "",
visible: (_d = options.visible) != null ? _d : true,
target
};
};
var addStyle = async (options, parent, instance) => {
const { nextZIndex } = instance.vm.zIndex || instance.vm._.exposed.zIndex;
const maskStyle = {};
if (options.fullscreen) {
instance.originalPosition.value = getStyle(document.body, "position");
instance.originalOverflow.value = getStyle(document.body, "overflow");
maskStyle.zIndex = nextZIndex();
} else if (options.parent === document.body) {
instance.originalPosition.value = getStyle(document.body, "position");
await nextTick();
for (const property of ["top", "left"]) {
const scroll = property === "top" ? "scrollTop" : "scrollLeft";
maskStyle[property] = `${options.target.getBoundingClientRect()[property] + document.body[scroll] + document.documentElement[scroll] - Number.parseInt(getStyle(document.body, `margin-${property}`), 10)}px`;
}
for (const property of ["height", "width"]) maskStyle[property] = `${options.target.getBoundingClientRect()[property]}px`;
} else instance.originalPosition.value = getStyle(parent, "position");
for (const [key, value] of Object.entries(maskStyle)) instance.$el.style[key] = value;
};
var addClassList = (options, parent, instance) => {
const ns = instance.vm.ns || instance.vm._.exposed.ns;
if (![
"absolute",
"fixed",
"sticky"
].includes(instance.originalPosition.value)) addClass(parent, ns.bm("parent", "relative"));
else removeClass(parent, ns.bm("parent", "relative"));
if (options.fullscreen && options.lock) addClass(parent, ns.bm("parent", "hidden"));
else removeClass(parent, ns.bm("parent", "hidden"));
};
init_shared_esm_bundler();
var INSTANCE_KEY = Symbol("ElLoading");
var createInstance = (el, binding) => {
var _a, _b, _c, _d;
const vm = binding.instance;
const getBindingProp = (key) => isObject$2(binding.value) ? binding.value[key] : void 0;
const resolveExpression = (key) => {
const data = isString$1(key) && (vm == null ? void 0 : vm[key]) || key;
if (data) return ref(data);
else return data;
};
const getProp = (name$13) => resolveExpression(getBindingProp(name$13) || el.getAttribute(`element-loading-${hyphenate(name$13)}`));
const fullscreen = (_a = getBindingProp("fullscreen")) != null ? _a : binding.modifiers.fullscreen;
const options = {
text: getProp("text"),
svg: getProp("svg"),
svgViewBox: getProp("svgViewBox"),
spinner: getProp("spinner"),
background: getProp("background"),
customClass: getProp("customClass"),
fullscreen,
target: (_b = getBindingProp("target")) != null ? _b : fullscreen ? void 0 : el,
body: (_c = getBindingProp("body")) != null ? _c : binding.modifiers.body,
lock: (_d = getBindingProp("lock")) != null ? _d : binding.modifiers.lock
};
el[INSTANCE_KEY] = {
options,
instance: Loading(options)
};
};
var updateOptions = (newOptions, originalOptions) => {
for (const key of Object.keys(originalOptions)) if (isRef(originalOptions[key])) originalOptions[key].value = newOptions[key];
};
var vLoading = {
mounted(el, binding) {
if (binding.value) createInstance(el, binding);
},
updated(el, binding) {
const instance = el[INSTANCE_KEY];
if (binding.oldValue !== binding.value) if (binding.value && !binding.oldValue) createInstance(el, binding);
else if (binding.value && binding.oldValue) {
if (isObject$2(binding.value)) updateOptions(binding.value, instance.options);
} else instance?.instance.close();
},
unmounted(el) {
var _a;
(_a = el[INSTANCE_KEY]) == null || _a.instance.close();
}
};
var ElLoading = {
install(app) {
app.directive("loading", vLoading);
app.config.globalProperties.$loading = Loading;
},
directive: vLoading,
service: Loading
};
var STORES = {
COMMUNITY_NODES: "communityNodes",
ROOT: "root",
SETTINGS: "settings",
UI: "ui",
USERS: "users",
WORKFLOWS: "workflows",
WORKFLOWS_V2: "workflowsV2",
WORKFLOWS_EE: "workflowsEE",
EXECUTIONS: "executions",
NDV: "ndv",
TEMPLATES: "templates",
NODE_TYPES: "nodeTypes",
CREDENTIALS: "credentials",
TAGS: "tags",
ANNOTATION_TAGS: "annotationTags",
VERSIONS: "versions",
NODE_CREATOR: "nodeCreator",
WEBHOOKS: "webhooks",
HISTORY: "history",
CLOUD_PLAN: "cloudPlan",
RBAC: "rbac",
PUSH: "push",
COLLABORATION: "collaboration",
ASSISTANT: "assistant",
BUILDER: "builder",
CHAT_PANEL: "chatPanel",
CHAT_PANEL_STATE: "chatPanelState",
BECOME_TEMPLATE_CREATOR: "becomeTemplateCreator",
PROJECTS: "projects",
API_KEYS: "apiKeys",
EVALUATION: "evaluation",
FOLDERS: "folders",
MODULES: "modules",
FOCUS_PANEL: "focusPanel",
WORKFLOW_STATE: "workflowState",
AI_TEMPLATES_STARTER_COLLECTION: "aiTemplatesStarterCollection",
PERSONALIZED_TEMPLATES: "personalizedTemplates",
EXPERIMENT_READY_TO_RUN_WORKFLOWS: "readyToRunWorkflows",
EXPERIMENT_READY_TO_RUN_WORKFLOWS_V2: "readyToRunWorkflowsV2",
EXPERIMENT_TEMPLATE_RECO_V2: "templateRecoV2",
PERSONALIZED_TEMPLATES_V3: "personalizedTemplatesV3",
READY_TO_RUN: "readyToRun",
TEMPLATES_DATA_QUALITY: "templatesDataQuality",
BANNERS: "banners",
CONSENT: "consent"
};
var require_es5 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
(function(e, t) {
"object" == typeof exports && "object" == typeof module ? module.exports = t() : "function" == typeof define && define.amd ? define([], t) : "object" == typeof exports ? exports.bowser = t() : e.bowser = t();
})(exports, (function() {
return function(e) {
var t = {};
function r(n) {
if (t[n]) return t[n].exports;
var i$1 = t[n] = {
i: n,
l: !1,
exports: {}
};
return e[n].call(i$1.exports, i$1, i$1.exports, r), i$1.l = !0, i$1.exports;
}
return r.m = e, r.c = t, r.d = function(e$1, t$1, n) {
r.o(e$1, t$1) || Object.defineProperty(e$1, t$1, {
enumerable: !0,
get: n
});
}, r.r = function(e$1) {
"undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e$1, Symbol.toStringTag, { value: "Module" }), Object.defineProperty(e$1, "__esModule", { value: !0 });
}, r.t = function(e$1, t$1) {
if (1 & t$1 && (e$1 = r(e$1)), 8 & t$1) return e$1;
if (4 & t$1 && "object" == typeof e$1 && e$1 && e$1.__esModule) return e$1;
var n = Object.create(null);
if (r.r(n), Object.defineProperty(n, "default", {
enumerable: !0,
value: e$1
}), 2 & t$1 && "string" != typeof e$1) for (var i$1 in e$1) r.d(n, i$1, function(t$2) {
return e$1[t$2];
}.bind(null, i$1));
return n;
}, r.n = function(e$1) {
var t$1 = e$1 && e$1.__esModule ? function() {
return e$1.default;
} : function() {
return e$1;
};
return r.d(t$1, "a", t$1), t$1;
}, r.o = function(e$1, t$1) {
return Object.prototype.hasOwnProperty.call(e$1, t$1);
}, r.p = "", r(r.s = 90);
}({
17: function(e, t, r) {
t.__esModule = !0, t.default = void 0;
var n = r(18);
t.default = function() {
function e$1() {}
return e$1.getFirstMatch = function(e$2, t$1) {
var r$1 = t$1.match(e$2);
return r$1 && r$1.length > 0 && r$1[1] || "";
}, e$1.getSecondMatch = function(e$2, t$1) {
var r$1 = t$1.match(e$2);
return r$1 && r$1.length > 1 && r$1[2] || "";
}, e$1.matchAndReturnConst = function(e$2, t$1, r$1) {
if (e$2.test(t$1)) return r$1;
}, e$1.getWindowsVersionName = function(e$2) {
switch (e$2) {
case "NT": return "NT";
case "XP": return "XP";
case "NT 5.0": return "2000";
case "NT 5.1": return "XP";
case "NT 5.2": return "2003";
case "NT 6.0": return "Vista";
case "NT 6.1": return "7";
case "NT 6.2": return "8";
case "NT 6.3": return "8.1";
case "NT 10.0": return "10";
default: return;
}
}, e$1.getMacOSVersionName = function(e$2) {
var t$1 = e$2.split(".").splice(0, 2).map((function(e$3) {
return parseInt(e$3, 10) || 0;
}));
if (t$1.push(0), 10 === t$1[0]) switch (t$1[1]) {
case 5: return "Leopard";
case 6: return "Snow Leopard";
case 7: return "Lion";
case 8: return "Mountain Lion";
case 9: return "Mavericks";
case 10: return "Yosemite";
case 11: return "El Capitan";
case 12: return "Sierra";
case 13: return "High Sierra";
case 14: return "Mojave";
case 15: return "Catalina";
default: return;
}
}, e$1.getAndroidVersionName = function(e$2) {
var t$1 = e$2.split(".").splice(0, 2).map((function(e$3) {
return parseInt(e$3, 10) || 0;
}));
if (t$1.push(0), !(1 === t$1[0] && t$1[1] < 5)) return 1 === t$1[0] && t$1[1] < 6 ? "Cupcake" : 1 === t$1[0] && t$1[1] >= 6 ? "Donut" : 2 === t$1[0] && t$1[1] < 2 ? "Eclair" : 2 === t$1[0] && 2 === t$1[1] ? "Froyo" : 2 === t$1[0] && t$1[1] > 2 ? "Gingerbread" : 3 === t$1[0] ? "Honeycomb" : 4 === t$1[0] && t$1[1] < 1 ? "Ice Cream Sandwich" : 4 === t$1[0] && t$1[1] < 4 ? "Jelly Bean" : 4 === t$1[0] && t$1[1] >= 4 ? "KitKat" : 5 === t$1[0] ? "Lollipop" : 6 === t$1[0] ? "Marshmallow" : 7 === t$1[0] ? "Nougat" : 8 === t$1[0] ? "Oreo" : 9 === t$1[0] ? "Pie" : void 0;
}, e$1.getVersionPrecision = function(e$2) {
return e$2.split(".").length;
}, e$1.compareVersions = function(t$1, r$1, n$1) {
void 0 === n$1 && (n$1 = !1);
var i$1 = e$1.getVersionPrecision(t$1), s = e$1.getVersionPrecision(r$1), a = Math.max(i$1, s), o = 0, u = e$1.map([t$1, r$1], (function(t$2) {
var r$2 = a - e$1.getVersionPrecision(t$2), n$2 = t$2 + new Array(r$2 + 1).join(".0");
return e$1.map(n$2.split("."), (function(e$2) {
return new Array(20 - e$2.length).join("0") + e$2;
})).reverse();
}));
for (n$1 && (o = a - Math.min(i$1, s)), a -= 1; a >= o;) {
if (u[0][a] > u[1][a]) return 1;
if (u[0][a] === u[1][a]) {
if (a === o) return 0;
a -= 1;
} else if (u[0][a] < u[1][a]) return -1;
}
}, e$1.map = function(e$2, t$1) {
var r$1, n$1 = [];
if (Array.prototype.map) return Array.prototype.map.call(e$2, t$1);
for (r$1 = 0; r$1 < e$2.length; r$1 += 1) n$1.push(t$1(e$2[r$1]));
return n$1;
}, e$1.find = function(e$2, t$1) {
var r$1, n$1;
if (Array.prototype.find) return Array.prototype.find.call(e$2, t$1);
for (r$1 = 0, n$1 = e$2.length; r$1 < n$1; r$1 += 1) {
var i$1 = e$2[r$1];
if (t$1(i$1, r$1)) return i$1;
}
}, e$1.assign = function(e$2) {
for (var t$1, r$1, n$1 = e$2, i$1 = arguments.length, s = new Array(i$1 > 1 ? i$1 - 1 : 0), a = 1; a < i$1; a++) s[a - 1] = arguments[a];
if (Object.assign) return Object.assign.apply(Object, [e$2].concat(s));
var o = function() {
var e$3 = s[t$1];
"object" == typeof e$3 && null !== e$3 && Object.keys(e$3).forEach((function(t$2) {
n$1[t$2] = e$3[t$2];
}));
};
for (t$1 = 0, r$1 = s.length; t$1 < r$1; t$1 += 1) o();
return e$2;
}, e$1.getBrowserAlias = function(e$2) {
return n.BROWSER_ALIASES_MAP[e$2];
}, e$1.getBrowserTypeByAlias = function(e$2) {
return n.BROWSER_MAP[e$2] || "";
}, e$1;
}(), e.exports = t.default;
},
18: function(e, t, r) {
t.__esModule = !0, t.ENGINE_MAP = t.OS_MAP = t.PLATFORMS_MAP = t.BROWSER_MAP = t.BROWSER_ALIASES_MAP = void 0;
t.BROWSER_ALIASES_MAP = {
"Amazon Silk": "amazon_silk",
"Android Browser": "android",
Bada: "bada",
BlackBerry: "blackberry",
Chrome: "chrome",
Chromium: "chromium",
Electron: "electron",
Epiphany: "epiphany",
Firefox: "firefox",
Focus: "focus",
Generic: "generic",
"Google Search": "google_search",
Googlebot: "googlebot",
"Internet Explorer": "ie",
"K-Meleon": "k_meleon",
Maxthon: "maxthon",
"Microsoft Edge": "edge",
"MZ Browser": "mz",
"NAVER Whale Browser": "naver",
Opera: "opera",
"Opera Coast": "opera_coast",
PhantomJS: "phantomjs",
Puffin: "puffin",
QupZilla: "qupzilla",
QQ: "qq",
QQLite: "qqlite",
Safari: "safari",
Sailfish: "sailfish",
"Samsung Internet for Android": "samsung_internet",
SeaMonkey: "seamonkey",
Sleipnir: "sleipnir",
Swing: "swing",
Tizen: "tizen",
"UC Browser": "uc",
Vivaldi: "vivaldi",
"WebOS Browser": "webos",
WeChat: "wechat",
"Yandex Browser": "yandex",
Roku: "roku"
};
t.BROWSER_MAP = {
amazon_silk: "Amazon Silk",
android: "Android Browser",
bada: "Bada",
blackberry: "BlackBerry",
chrome: "Chrome",
chromium: "Chromium",
electron: "Electron",
epiphany: "Epiphany",
firefox: "Firefox",
focus: "Focus",
generic: "Generic",
googlebot: "Googlebot",
google_search: "Google Search",
ie: "Internet Explorer",
k_meleon: "K-Meleon",
maxthon: "Maxthon",
edge: "Microsoft Edge",
mz: "MZ Browser",
naver: "NAVER Whale Browser",
opera: "Opera",
opera_coast: "Opera Coast",
phantomjs: "PhantomJS",
puffin: "Puffin",
qupzilla: "QupZilla",
qq: "QQ Browser",
qqlite: "QQ Browser Lite",
safari: "Safari",
sailfish: "Sailfish",
samsung_internet: "Samsung Internet for Android",
seamonkey: "SeaMonkey",
sleipnir: "Sleipnir",
swing: "Swing",
tizen: "Tizen",
uc: "UC Browser",
vivaldi: "Vivaldi",
webos: "WebOS Browser",
wechat: "WeChat",
yandex: "Yandex Browser"
};
t.PLATFORMS_MAP = {
tablet: "tablet",
mobile: "mobile",
desktop: "desktop",
tv: "tv"
};
t.OS_MAP = {
WindowsPhone: "Windows Phone",
Windows: "Windows",
MacOS: "macOS",
iOS: "iOS",
Android: "Android",
WebOS: "WebOS",
BlackBerry: "BlackBerry",
Bada: "Bada",
Tizen: "Tizen",
Linux: "Linux",
ChromeOS: "Chrome OS",
PlayStation4: "PlayStation 4",
Roku: "Roku"
};
t.ENGINE_MAP = {
EdgeHTML: "EdgeHTML",
Blink: "Blink",
Trident: "Trident",
Presto: "Presto",
Gecko: "Gecko",
WebKit: "WebKit"
};
},
90: function(e, t, r) {
t.__esModule = !0, t.default = void 0;
var n, i$1 = (n = r(91)) && n.__esModule ? n : { default: n }, s = r(18);
function a(e$1, t$1) {
for (var r$1 = 0; r$1 < t$1.length; r$1++) {
var n$1 = t$1[r$1];
n$1.enumerable = n$1.enumerable || !1, n$1.configurable = !0, "value" in n$1 && (n$1.writable = !0), Object.defineProperty(e$1, n$1.key, n$1);
}
}
t.default = function() {
function e$1() {}
var t$1, r$1, n$1;
return e$1.getParser = function(e$2, t$2) {
if (void 0 === t$2 && (t$2 = !1), "string" != typeof e$2) throw new Error("UserAgent should be a string");
return new i$1.default(e$2, t$2);
}, e$1.parse = function(e$2) {
return new i$1.default(e$2).getResult();
}, t$1 = e$1, n$1 = [
{
key: "BROWSER_MAP",
get: function() {
return s.BROWSER_MAP;
}
},
{
key: "ENGINE_MAP",
get: function() {
return s.ENGINE_MAP;
}
},
{
key: "OS_MAP",
get: function() {
return s.OS_MAP;
}
},
{
key: "PLATFORMS_MAP",
get: function() {
return s.PLATFORMS_MAP;
}
}
], (r$1 = null) && a(t$1.prototype, r$1), n$1 && a(t$1, n$1), e$1;
}(), e.exports = t.default;
},
91: function(e, t, r) {
t.__esModule = !0, t.default = void 0;
var n = u(r(92)), i$1 = u(r(93)), s = u(r(94)), a = u(r(95)), o = u(r(17));
function u(e$1) {
return e$1 && e$1.__esModule ? e$1 : { default: e$1 };
}
t.default = function() {
function e$1(e$2, t$2) {
if (void 0 === t$2 && (t$2 = !1), null == e$2 || "" === e$2) throw new Error("UserAgent parameter can't be empty");
this._ua = e$2, this.parsedResult = {}, !0 !== t$2 && this.parse();
}
var t$1 = e$1.prototype;
return t$1.getUA = function() {
return this._ua;
}, t$1.test = function(e$2) {
return e$2.test(this._ua);
}, t$1.parseBrowser = function() {
var e$2 = this;
this.parsedResult.browser = {};
var t$2 = o.default.find(n.default, (function(t$3) {
if ("function" == typeof t$3.test) return t$3.test(e$2);
if (t$3.test instanceof Array) return t$3.test.some((function(t$4) {
return e$2.test(t$4);
}));
throw new Error("Browser's test function is not valid");
}));
return t$2 && (this.parsedResult.browser = t$2.describe(this.getUA())), this.parsedResult.browser;
}, t$1.getBrowser = function() {
return this.parsedResult.browser ? this.parsedResult.browser : this.parseBrowser();
}, t$1.getBrowserName = function(e$2) {
return e$2 ? String(this.getBrowser().name).toLowerCase() || "" : this.getBrowser().name || "";
}, t$1.getBrowserVersion = function() {
return this.getBrowser().version;
}, t$1.getOS = function() {
return this.parsedResult.os ? this.parsedResult.os : this.parseOS();
}, t$1.parseOS = function() {
var e$2 = this;
this.parsedResult.os = {};
var t$2 = o.default.find(i$1.default, (function(t$3) {
if ("function" == typeof t$3.test) return t$3.test(e$2);
if (t$3.test instanceof Array) return t$3.test.some((function(t$4) {
return e$2.test(t$4);
}));
throw new Error("Browser's test function is not valid");
}));
return t$2 && (this.parsedResult.os = t$2.describe(this.getUA())), this.parsedResult.os;
}, t$1.getOSName = function(e$2) {
var t$2 = this.getOS().name;
return e$2 ? String(t$2).toLowerCase() || "" : t$2 || "";
}, t$1.getOSVersion = function() {
return this.getOS().version;
}, t$1.getPlatform = function() {
return this.parsedResult.platform ? this.parsedResult.platform : this.parsePlatform();
}, t$1.getPlatformType = function(e$2) {
void 0 === e$2 && (e$2 = !1);
var t$2 = this.getPlatform().type;
return e$2 ? String(t$2).toLowerCase() || "" : t$2 || "";
}, t$1.parsePlatform = function() {
var e$2 = this;
this.parsedResult.platform = {};
var t$2 = o.default.find(s.default, (function(t$3) {
if ("function" == typeof t$3.test) return t$3.test(e$2);
if (t$3.test instanceof Array) return t$3.test.some((function(t$4) {
return e$2.test(t$4);
}));
throw new Error("Browser's test function is not valid");
}));
return t$2 && (this.parsedResult.platform = t$2.describe(this.getUA())), this.parsedResult.platform;
}, t$1.getEngine = function() {
return this.parsedResult.engine ? this.parsedResult.engine : this.parseEngine();
}, t$1.getEngineName = function(e$2) {
return e$2 ? String(this.getEngine().name).toLowerCase() || "" : this.getEngine().name || "";
}, t$1.parseEngine = function() {
var e$2 = this;
this.parsedResult.engine = {};
var t$2 = o.default.find(a.default, (function(t$3) {
if ("function" == typeof t$3.test) return t$3.test(e$2);
if (t$3.test instanceof Array) return t$3.test.some((function(t$4) {
return e$2.test(t$4);
}));
throw new Error("Browser's test function is not valid");
}));
return t$2 && (this.parsedResult.engine = t$2.describe(this.getUA())), this.parsedResult.engine;
}, t$1.parse = function() {
return this.parseBrowser(), this.parseOS(), this.parsePlatform(), this.parseEngine(), this;
}, t$1.getResult = function() {
return o.default.assign({}, this.parsedResult);
}, t$1.satisfies = function(e$2) {
var t$2 = this, r$1 = {}, n$1 = 0, i$2 = {}, s$1 = 0;
if (Object.keys(e$2).forEach((function(t$3) {
var a$2 = e$2[t$3];
"string" == typeof a$2 ? (i$2[t$3] = a$2, s$1 += 1) : "object" == typeof a$2 && (r$1[t$3] = a$2, n$1 += 1);
})), n$1 > 0) {
var a$1 = Object.keys(r$1), u$1 = o.default.find(a$1, (function(e$3) {
return t$2.isOS(e$3);
}));
if (u$1) {
var d = this.satisfies(r$1[u$1]);
if (void 0 !== d) return d;
}
var c = o.default.find(a$1, (function(e$3) {
return t$2.isPlatform(e$3);
}));
if (c) {
var f = this.satisfies(r$1[c]);
if (void 0 !== f) return f;
}
}
if (s$1 > 0) {
var l = Object.keys(i$2), h$1 = o.default.find(l, (function(e$3) {
return t$2.isBrowser(e$3, !0);
}));
if (void 0 !== h$1) return this.compareVersion(i$2[h$1]);
}
}, t$1.isBrowser = function(e$2, t$2) {
void 0 === t$2 && (t$2 = !1);
var r$1 = this.getBrowserName().toLowerCase(), n$1 = e$2.toLowerCase(), i$2 = o.default.getBrowserTypeByAlias(n$1);
return t$2 && i$2 && (n$1 = i$2.toLowerCase()), n$1 === r$1;
}, t$1.compareVersion = function(e$2) {
var t$2 = [0], r$1 = e$2, n$1 = !1, i$2 = this.getBrowserVersion();
if ("string" == typeof i$2) return ">" === e$2[0] || "<" === e$2[0] ? (r$1 = e$2.substr(1), "=" === e$2[1] ? (n$1 = !0, r$1 = e$2.substr(2)) : t$2 = [], ">" === e$2[0] ? t$2.push(1) : t$2.push(-1)) : "=" === e$2[0] ? r$1 = e$2.substr(1) : "~" === e$2[0] && (n$1 = !0, r$1 = e$2.substr(1)), t$2.indexOf(o.default.compareVersions(i$2, r$1, n$1)) > -1;
}, t$1.isOS = function(e$2) {
return this.getOSName(!0) === String(e$2).toLowerCase();
}, t$1.isPlatform = function(e$2) {
return this.getPlatformType(!0) === String(e$2).toLowerCase();
}, t$1.isEngine = function(e$2) {
return this.getEngineName(!0) === String(e$2).toLowerCase();
}, t$1.is = function(e$2, t$2) {
return void 0 === t$2 && (t$2 = !1), this.isBrowser(e$2, t$2) || this.isOS(e$2) || this.isPlatform(e$2);
}, t$1.some = function(e$2) {
var t$2 = this;
return void 0 === e$2 && (e$2 = []), e$2.some((function(e$3) {
return t$2.is(e$3);
}));
}, e$1;
}(), e.exports = t.default;
},
92: function(e, t, r) {
t.__esModule = !0, t.default = void 0;
var n, i$1 = (n = r(17)) && n.__esModule ? n : { default: n };
var s = /version\/(\d+(\.?_?\d+)+)/i;
t.default = [
{
test: [/googlebot/i],
describe: function(e$1) {
var t$1 = { name: "Googlebot" }, r$1 = i$1.default.getFirstMatch(/googlebot\/(\d+(\.\d+))/i, e$1) || i$1.default.getFirstMatch(s, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/opera/i],
describe: function(e$1) {
var t$1 = { name: "Opera" }, r$1 = i$1.default.getFirstMatch(s, e$1) || i$1.default.getFirstMatch(/(?:opera)[\s/](\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/opr\/|opios/i],
describe: function(e$1) {
var t$1 = { name: "Opera" }, r$1 = i$1.default.getFirstMatch(/(?:opr|opios)[\s/](\S+)/i, e$1) || i$1.default.getFirstMatch(s, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/SamsungBrowser/i],
describe: function(e$1) {
var t$1 = { name: "Samsung Internet for Android" }, r$1 = i$1.default.getFirstMatch(s, e$1) || i$1.default.getFirstMatch(/(?:SamsungBrowser)[\s/](\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/Whale/i],
describe: function(e$1) {
var t$1 = { name: "NAVER Whale Browser" }, r$1 = i$1.default.getFirstMatch(s, e$1) || i$1.default.getFirstMatch(/(?:whale)[\s/](\d+(?:\.\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/MZBrowser/i],
describe: function(e$1) {
var t$1 = { name: "MZ Browser" }, r$1 = i$1.default.getFirstMatch(/(?:MZBrowser)[\s/](\d+(?:\.\d+)+)/i, e$1) || i$1.default.getFirstMatch(s, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/focus/i],
describe: function(e$1) {
var t$1 = { name: "Focus" }, r$1 = i$1.default.getFirstMatch(/(?:focus)[\s/](\d+(?:\.\d+)+)/i, e$1) || i$1.default.getFirstMatch(s, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/swing/i],
describe: function(e$1) {
var t$1 = { name: "Swing" }, r$1 = i$1.default.getFirstMatch(/(?:swing)[\s/](\d+(?:\.\d+)+)/i, e$1) || i$1.default.getFirstMatch(s, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/coast/i],
describe: function(e$1) {
var t$1 = { name: "Opera Coast" }, r$1 = i$1.default.getFirstMatch(s, e$1) || i$1.default.getFirstMatch(/(?:coast)[\s/](\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/opt\/\d+(?:.?_?\d+)+/i],
describe: function(e$1) {
var t$1 = { name: "Opera Touch" }, r$1 = i$1.default.getFirstMatch(/(?:opt)[\s/](\d+(\.?_?\d+)+)/i, e$1) || i$1.default.getFirstMatch(s, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/yabrowser/i],
describe: function(e$1) {
var t$1 = { name: "Yandex Browser" }, r$1 = i$1.default.getFirstMatch(/(?:yabrowser)[\s/](\d+(\.?_?\d+)+)/i, e$1) || i$1.default.getFirstMatch(s, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/ucbrowser/i],
describe: function(e$1) {
var t$1 = { name: "UC Browser" }, r$1 = i$1.default.getFirstMatch(s, e$1) || i$1.default.getFirstMatch(/(?:ucbrowser)[\s/](\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/Maxthon|mxios/i],
describe: function(e$1) {
var t$1 = { name: "Maxthon" }, r$1 = i$1.default.getFirstMatch(s, e$1) || i$1.default.getFirstMatch(/(?:Maxthon|mxios)[\s/](\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/epiphany/i],
describe: function(e$1) {
var t$1 = { name: "Epiphany" }, r$1 = i$1.default.getFirstMatch(s, e$1) || i$1.default.getFirstMatch(/(?:epiphany)[\s/](\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/puffin/i],
describe: function(e$1) {
var t$1 = { name: "Puffin" }, r$1 = i$1.default.getFirstMatch(s, e$1) || i$1.default.getFirstMatch(/(?:puffin)[\s/](\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/sleipnir/i],
describe: function(e$1) {
var t$1 = { name: "Sleipnir" }, r$1 = i$1.default.getFirstMatch(s, e$1) || i$1.default.getFirstMatch(/(?:sleipnir)[\s/](\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/k-meleon/i],
describe: function(e$1) {
var t$1 = { name: "K-Meleon" }, r$1 = i$1.default.getFirstMatch(s, e$1) || i$1.default.getFirstMatch(/(?:k-meleon)[\s/](\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/micromessenger/i],
describe: function(e$1) {
var t$1 = { name: "WeChat" }, r$1 = i$1.default.getFirstMatch(/(?:micromessenger)[\s/](\d+(\.?_?\d+)+)/i, e$1) || i$1.default.getFirstMatch(s, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/qqbrowser/i],
describe: function(e$1) {
var t$1 = { name: /qqbrowserlite/i.test(e$1) ? "QQ Browser Lite" : "QQ Browser" }, r$1 = i$1.default.getFirstMatch(/(?:qqbrowserlite|qqbrowser)[/](\d+(\.?_?\d+)+)/i, e$1) || i$1.default.getFirstMatch(s, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/msie|trident/i],
describe: function(e$1) {
var t$1 = { name: "Internet Explorer" }, r$1 = i$1.default.getFirstMatch(/(?:msie |rv:)(\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/\sedg\//i],
describe: function(e$1) {
var t$1 = { name: "Microsoft Edge" }, r$1 = i$1.default.getFirstMatch(/\sedg\/(\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/edg([ea]|ios)/i],
describe: function(e$1) {
var t$1 = { name: "Microsoft Edge" }, r$1 = i$1.default.getSecondMatch(/edg([ea]|ios)\/(\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/vivaldi/i],
describe: function(e$1) {
var t$1 = { name: "Vivaldi" }, r$1 = i$1.default.getFirstMatch(/vivaldi\/(\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/seamonkey/i],
describe: function(e$1) {
var t$1 = { name: "SeaMonkey" }, r$1 = i$1.default.getFirstMatch(/seamonkey\/(\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/sailfish/i],
describe: function(e$1) {
var t$1 = { name: "Sailfish" }, r$1 = i$1.default.getFirstMatch(/sailfish\s?browser\/(\d+(\.\d+)?)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/silk/i],
describe: function(e$1) {
var t$1 = { name: "Amazon Silk" }, r$1 = i$1.default.getFirstMatch(/silk\/(\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/phantom/i],
describe: function(e$1) {
var t$1 = { name: "PhantomJS" }, r$1 = i$1.default.getFirstMatch(/phantomjs\/(\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/slimerjs/i],
describe: function(e$1) {
var t$1 = { name: "SlimerJS" }, r$1 = i$1.default.getFirstMatch(/slimerjs\/(\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/blackberry|\bbb\d+/i, /rim\stablet/i],
describe: function(e$1) {
var t$1 = { name: "BlackBerry" }, r$1 = i$1.default.getFirstMatch(s, e$1) || i$1.default.getFirstMatch(/blackberry[\d]+\/(\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/(web|hpw)[o0]s/i],
describe: function(e$1) {
var t$1 = { name: "WebOS Browser" }, r$1 = i$1.default.getFirstMatch(s, e$1) || i$1.default.getFirstMatch(/w(?:eb)?[o0]sbrowser\/(\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/bada/i],
describe: function(e$1) {
var t$1 = { name: "Bada" }, r$1 = i$1.default.getFirstMatch(/dolfin\/(\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/tizen/i],
describe: function(e$1) {
var t$1 = { name: "Tizen" }, r$1 = i$1.default.getFirstMatch(/(?:tizen\s?)?browser\/(\d+(\.?_?\d+)+)/i, e$1) || i$1.default.getFirstMatch(s, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/qupzilla/i],
describe: function(e$1) {
var t$1 = { name: "QupZilla" }, r$1 = i$1.default.getFirstMatch(/(?:qupzilla)[\s/](\d+(\.?_?\d+)+)/i, e$1) || i$1.default.getFirstMatch(s, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/firefox|iceweasel|fxios/i],
describe: function(e$1) {
var t$1 = { name: "Firefox" }, r$1 = i$1.default.getFirstMatch(/(?:firefox|iceweasel|fxios)[\s/](\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/electron/i],
describe: function(e$1) {
var t$1 = { name: "Electron" }, r$1 = i$1.default.getFirstMatch(/(?:electron)\/(\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/MiuiBrowser/i],
describe: function(e$1) {
var t$1 = { name: "Miui" }, r$1 = i$1.default.getFirstMatch(/(?:MiuiBrowser)[\s/](\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/chromium/i],
describe: function(e$1) {
var t$1 = { name: "Chromium" }, r$1 = i$1.default.getFirstMatch(/(?:chromium)[\s/](\d+(\.?_?\d+)+)/i, e$1) || i$1.default.getFirstMatch(s, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/chrome|crios|crmo/i],
describe: function(e$1) {
var t$1 = { name: "Chrome" }, r$1 = i$1.default.getFirstMatch(/(?:chrome|crios|crmo)\/(\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/GSA/i],
describe: function(e$1) {
var t$1 = { name: "Google Search" }, r$1 = i$1.default.getFirstMatch(/(?:GSA)\/(\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: function(e$1) {
var t$1 = !e$1.test(/like android/i), r$1 = e$1.test(/android/i);
return t$1 && r$1;
},
describe: function(e$1) {
var t$1 = { name: "Android Browser" }, r$1 = i$1.default.getFirstMatch(s, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/playstation 4/i],
describe: function(e$1) {
var t$1 = { name: "PlayStation 4" }, r$1 = i$1.default.getFirstMatch(s, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/safari|applewebkit/i],
describe: function(e$1) {
var t$1 = { name: "Safari" }, r$1 = i$1.default.getFirstMatch(s, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/.*/i],
describe: function(e$1) {
var t$1 = -1 !== e$1.search("\\(") ? /^(.*)\/(.*)[ \t]\((.*)/ : /^(.*)\/(.*) /;
return {
name: i$1.default.getFirstMatch(t$1, e$1),
version: i$1.default.getSecondMatch(t$1, e$1)
};
}
}
], e.exports = t.default;
},
93: function(e, t, r) {
t.__esModule = !0, t.default = void 0;
var n, i$1 = (n = r(17)) && n.__esModule ? n : { default: n }, s = r(18);
t.default = [
{
test: [/Roku\/DVP/],
describe: function(e$1) {
var t$1 = i$1.default.getFirstMatch(/Roku\/DVP-(\d+\.\d+)/i, e$1);
return {
name: s.OS_MAP.Roku,
version: t$1
};
}
},
{
test: [/windows phone/i],
describe: function(e$1) {
var t$1 = i$1.default.getFirstMatch(/windows phone (?:os)?\s?(\d+(\.\d+)*)/i, e$1);
return {
name: s.OS_MAP.WindowsPhone,
version: t$1
};
}
},
{
test: [/windows /i],
describe: function(e$1) {
var t$1 = i$1.default.getFirstMatch(/Windows ((NT|XP)( \d\d?.\d)?)/i, e$1), r$1 = i$1.default.getWindowsVersionName(t$1);
return {
name: s.OS_MAP.Windows,
version: t$1,
versionName: r$1
};
}
},
{
test: [/Macintosh(.*?) FxiOS(.*?)\//],
describe: function(e$1) {
var t$1 = { name: s.OS_MAP.iOS }, r$1 = i$1.default.getSecondMatch(/(Version\/)(\d[\d.]+)/, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/macintosh/i],
describe: function(e$1) {
var t$1 = i$1.default.getFirstMatch(/mac os x (\d+(\.?_?\d+)+)/i, e$1).replace(/[_\s]/g, "."), r$1 = i$1.default.getMacOSVersionName(t$1), n$1 = {
name: s.OS_MAP.MacOS,
version: t$1
};
return r$1 && (n$1.versionName = r$1), n$1;
}
},
{
test: [/(ipod|iphone|ipad)/i],
describe: function(e$1) {
var t$1 = i$1.default.getFirstMatch(/os (\d+([_\s]\d+)*) like mac os x/i, e$1).replace(/[_\s]/g, ".");
return {
name: s.OS_MAP.iOS,
version: t$1
};
}
},
{
test: function(e$1) {
var t$1 = !e$1.test(/like android/i), r$1 = e$1.test(/android/i);
return t$1 && r$1;
},
describe: function(e$1) {
var t$1 = i$1.default.getFirstMatch(/android[\s/-](\d+(\.\d+)*)/i, e$1), r$1 = i$1.default.getAndroidVersionName(t$1), n$1 = {
name: s.OS_MAP.Android,
version: t$1
};
return r$1 && (n$1.versionName = r$1), n$1;
}
},
{
test: [/(web|hpw)[o0]s/i],
describe: function(e$1) {
var t$1 = i$1.default.getFirstMatch(/(?:web|hpw)[o0]s\/(\d+(\.\d+)*)/i, e$1), r$1 = { name: s.OS_MAP.WebOS };
return t$1 && t$1.length && (r$1.version = t$1), r$1;
}
},
{
test: [/blackberry|\bbb\d+/i, /rim\stablet/i],
describe: function(e$1) {
var t$1 = i$1.default.getFirstMatch(/rim\stablet\sos\s(\d+(\.\d+)*)/i, e$1) || i$1.default.getFirstMatch(/blackberry\d+\/(\d+([_\s]\d+)*)/i, e$1) || i$1.default.getFirstMatch(/\bbb(\d+)/i, e$1);
return {
name: s.OS_MAP.BlackBerry,
version: t$1
};
}
},
{
test: [/bada/i],
describe: function(e$1) {
var t$1 = i$1.default.getFirstMatch(/bada\/(\d+(\.\d+)*)/i, e$1);
return {
name: s.OS_MAP.Bada,
version: t$1
};
}
},
{
test: [/tizen/i],
describe: function(e$1) {
var t$1 = i$1.default.getFirstMatch(/tizen[/\s](\d+(\.\d+)*)/i, e$1);
return {
name: s.OS_MAP.Tizen,
version: t$1
};
}
},
{
test: [/linux/i],
describe: function() {
return { name: s.OS_MAP.Linux };
}
},
{
test: [/CrOS/],
describe: function() {
return { name: s.OS_MAP.ChromeOS };
}
},
{
test: [/PlayStation 4/],
describe: function(e$1) {
var t$1 = i$1.default.getFirstMatch(/PlayStation 4[/\s](\d+(\.\d+)*)/i, e$1);
return {
name: s.OS_MAP.PlayStation4,
version: t$1
};
}
}
], e.exports = t.default;
},
94: function(e, t, r) {
t.__esModule = !0, t.default = void 0;
var n, i$1 = (n = r(17)) && n.__esModule ? n : { default: n }, s = r(18);
t.default = [
{
test: [/googlebot/i],
describe: function() {
return {
type: "bot",
vendor: "Google"
};
}
},
{
test: [/huawei/i],
describe: function(e$1) {
var t$1 = i$1.default.getFirstMatch(/(can-l01)/i, e$1) && "Nova", r$1 = {
type: s.PLATFORMS_MAP.mobile,
vendor: "Huawei"
};
return t$1 && (r$1.model = t$1), r$1;
}
},
{
test: [/nexus\s*(?:7|8|9|10).*/i],
describe: function() {
return {
type: s.PLATFORMS_MAP.tablet,
vendor: "Nexus"
};
}
},
{
test: [/ipad/i],
describe: function() {
return {
type: s.PLATFORMS_MAP.tablet,
vendor: "Apple",
model: "iPad"
};
}
},
{
test: [/Macintosh(.*?) FxiOS(.*?)\//],
describe: function() {
return {
type: s.PLATFORMS_MAP.tablet,
vendor: "Apple",
model: "iPad"
};
}
},
{
test: [/kftt build/i],
describe: function() {
return {
type: s.PLATFORMS_MAP.tablet,
vendor: "Amazon",
model: "Kindle Fire HD 7"
};
}
},
{
test: [/silk/i],
describe: function() {
return {
type: s.PLATFORMS_MAP.tablet,
vendor: "Amazon"
};
}
},
{
test: [/tablet(?! pc)/i],
describe: function() {
return { type: s.PLATFORMS_MAP.tablet };
}
},
{
test: function(e$1) {
var t$1 = e$1.test(/ipod|iphone/i), r$1 = e$1.test(/like (ipod|iphone)/i);
return t$1 && !r$1;
},
describe: function(e$1) {
var t$1 = i$1.default.getFirstMatch(/(ipod|iphone)/i, e$1);
return {
type: s.PLATFORMS_MAP.mobile,
vendor: "Apple",
model: t$1
};
}
},
{
test: [/nexus\s*[0-6].*/i, /galaxy nexus/i],
describe: function() {
return {
type: s.PLATFORMS_MAP.mobile,
vendor: "Nexus"
};
}
},
{
test: [/[^-]mobi/i],
describe: function() {
return { type: s.PLATFORMS_MAP.mobile };
}
},
{
test: function(e$1) {
return "blackberry" === e$1.getBrowserName(!0);
},
describe: function() {
return {
type: s.PLATFORMS_MAP.mobile,
vendor: "BlackBerry"
};
}
},
{
test: function(e$1) {
return "bada" === e$1.getBrowserName(!0);
},
describe: function() {
return { type: s.PLATFORMS_MAP.mobile };
}
},
{
test: function(e$1) {
return "windows phone" === e$1.getBrowserName();
},
describe: function() {
return {
type: s.PLATFORMS_MAP.mobile,
vendor: "Microsoft"
};
}
},
{
test: function(e$1) {
var t$1 = Number(String(e$1.getOSVersion()).split(".")[0]);
return "android" === e$1.getOSName(!0) && t$1 >= 3;
},
describe: function() {
return { type: s.PLATFORMS_MAP.tablet };
}
},
{
test: function(e$1) {
return "android" === e$1.getOSName(!0);
},
describe: function() {
return { type: s.PLATFORMS_MAP.mobile };
}
},
{
test: function(e$1) {
return "macos" === e$1.getOSName(!0);
},
describe: function() {
return {
type: s.PLATFORMS_MAP.desktop,
vendor: "Apple"
};
}
},
{
test: function(e$1) {
return "windows" === e$1.getOSName(!0);
},
describe: function() {
return { type: s.PLATFORMS_MAP.desktop };
}
},
{
test: function(e$1) {
return "linux" === e$1.getOSName(!0);
},
describe: function() {
return { type: s.PLATFORMS_MAP.desktop };
}
},
{
test: function(e$1) {
return "playstation 4" === e$1.getOSName(!0);
},
describe: function() {
return { type: s.PLATFORMS_MAP.tv };
}
},
{
test: function(e$1) {
return "roku" === e$1.getOSName(!0);
},
describe: function() {
return { type: s.PLATFORMS_MAP.tv };
}
}
], e.exports = t.default;
},
95: function(e, t, r) {
t.__esModule = !0, t.default = void 0;
var n, i$1 = (n = r(17)) && n.__esModule ? n : { default: n }, s = r(18);
t.default = [
{
test: function(e$1) {
return "microsoft edge" === e$1.getBrowserName(!0);
},
describe: function(e$1) {
if (/\sedg\//i.test(e$1)) return { name: s.ENGINE_MAP.Blink };
var t$1 = i$1.default.getFirstMatch(/edge\/(\d+(\.?_?\d+)+)/i, e$1);
return {
name: s.ENGINE_MAP.EdgeHTML,
version: t$1
};
}
},
{
test: [/trident/i],
describe: function(e$1) {
var t$1 = { name: s.ENGINE_MAP.Trident }, r$1 = i$1.default.getFirstMatch(/trident\/(\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: function(e$1) {
return e$1.test(/presto/i);
},
describe: function(e$1) {
var t$1 = { name: s.ENGINE_MAP.Presto }, r$1 = i$1.default.getFirstMatch(/presto\/(\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: function(e$1) {
var t$1 = e$1.test(/gecko/i), r$1 = e$1.test(/like gecko/i);
return t$1 && !r$1;
},
describe: function(e$1) {
var t$1 = { name: s.ENGINE_MAP.Gecko }, r$1 = i$1.default.getFirstMatch(/gecko\/(\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
},
{
test: [/(apple)?webkit\/537\.36/i],
describe: function() {
return { name: s.ENGINE_MAP.Blink };
}
},
{
test: [/(apple)?webkit/i],
describe: function(e$1) {
var t$1 = { name: s.ENGINE_MAP.WebKit }, r$1 = i$1.default.getFirstMatch(/webkit\/(\d+(\.?_?\d+)+)/i, e$1);
return r$1 && (t$1.version = r$1), t$1;
}
}
], e.exports = t.default;
}
});
}));
}));
async function sessionStarted(context) {
return await makeRestApiRequest(context, "GET", "/events/session-started");
}
async function getSettings(context) {
return await makeRestApiRequest(context, "GET", "/settings");
}
async function getModuleSettings(context) {
return await makeRestApiRequest(context, "GET", "/module-settings");
}
function stringifyArray(arr) {
return arr.join(",");
}
async function testHealthEndpoint(apiEndpoint) {
return await get$3(apiEndpoint, "/health");
}
async function getCategories(apiEndpoint, headers) {
return await get$3(apiEndpoint, "/templates/categories", void 0, headers);
}
async function getCollections(apiEndpoint, query, headers) {
return await get$3(apiEndpoint, "/templates/collections", {
category: query.categories,
search: query.search
}, headers);
}
async function getWorkflows$1(apiEndpoint, query, headers) {
const { apps, sort, combineWith, categories, nodes: nodes$13,...restQuery } = query;
return await get$3(apiEndpoint, "/templates/search", {
...restQuery,
category: stringifyArray(categories),
...apps && { apps: stringifyArray(apps) },
...nodes$13 && { nodes: stringifyArray(nodes$13) },
...sort && { sort },
...combineWith && { combineWith }
}, headers);
}
async function getCollectionById(apiEndpoint, collectionId, headers) {
return await get$3(apiEndpoint, `/templates/collections/${collectionId}`, void 0, headers);
}
async function getTemplateById(apiEndpoint, templateId, headers) {
return await get$3(apiEndpoint, `/templates/workflows/${templateId}`, void 0, headers);
}
async function getWorkflowTemplate(apiEndpoint, templateId, headers) {
return await get$3(apiEndpoint, `/workflows/templates/${templateId}`, void 0, headers);
}
let UserManagementAuthenticationMethod = /* @__PURE__ */ function(UserManagementAuthenticationMethod$1) {
UserManagementAuthenticationMethod$1["Email"] = "email";
UserManagementAuthenticationMethod$1["Ldap"] = "ldap";
UserManagementAuthenticationMethod$1["Saml"] = "saml";
UserManagementAuthenticationMethod$1["Oidc"] = "oidc";
return UserManagementAuthenticationMethod$1;
}({});
async function getApiKeys(context) {
return await makeRestApiRequest(context, "GET", "/api-keys");
}
async function getApiKeyScopes(context) {
return await makeRestApiRequest(context, "GET", "/api-keys/scopes");
}
async function createApiKey(context, payload) {
return await makeRestApiRequest(context, "POST", "/api-keys", payload);
}
async function deleteApiKey(context, id$1) {
return await makeRestApiRequest(context, "DELETE", `/api-keys/${id$1}`);
}
async function updateApiKey(context, id$1, payload) {
return await makeRestApiRequest(context, "PATCH", `/api-keys/${id$1}`, payload);
}
async function getCurrentPlan(context) {
return await get$3(context.baseUrl, "/admin/cloud-plan");
}
async function getCurrentUsage(context) {
return await get$3(context.baseUrl, "/cloud/limits");
}
async function getCloudUserInfo(context) {
return await get$3(context.baseUrl, "/cloud/proxy/user/me");
}
async function sendConfirmationEmail(context) {
return await post(context.baseUrl, "/cloud/proxy/user/resend-confirmation-email");
}
async function getAdminPanelLoginCode(context) {
return await get$3(context.baseUrl, "/cloud/proxy/login/code");
}
async function sendUserEvent(context, eventData) {
return await post(context.baseUrl, "/cloud/proxy/user/event", eventData);
}
async function getInstalledCommunityNodes(context) {
return (await get$3(context.baseUrl, "/community-packages")).data || [];
}
async function installNewPackage(context, name$13, verify, version) {
return await post(context.baseUrl, "/community-packages", {
name: name$13,
verify,
version
});
}
async function uninstallPackage(context, name$13) {
return await makeRestApiRequest(context, "DELETE", "/community-packages", { name: name$13 });
}
async function updatePackage(context, name$13, version, checksum) {
return await makeRestApiRequest(context, "PATCH", "/community-packages", {
name: name$13,
version,
checksum
});
}
async function getAvailableCommunityPackageCount() {
return (await get$3("https://api.npms.io/v2/", "search?q=keywords:n8n-community-node-package")).total || 0;
}
async function getBecomeCreatorCta(context) {
return await get$3(context.baseUrl, "/cta/become-creator");
}
function hasDestinationId(destination) {
return destination.id !== void 0;
}
async function saveDestinationToDb(context, destination, subscribedEvents = []) {
return await makeRestApiRequest(context, "POST", "/eventbus/destination", {
...destination,
subscribedEvents
});
}
async function deleteDestinationFromDb(context, destinationId) {
return await makeRestApiRequest(context, "DELETE", `/eventbus/destination?id=${destinationId}`);
}
async function sendTestMessageToDestination(context, destination) {
return await makeRestApiRequest(context, "GET", "/eventbus/testmessage", { ...destination });
}
async function getEventNamesFromBackend(context) {
return await makeRestApiRequest(context, "GET", "/eventbus/eventnames");
}
async function getDestinationsFromBackend(context) {
return await makeRestApiRequest(context, "GET", "/eventbus/destination");
}
var getExternalSecrets = async (context) => {
return await makeRestApiRequest(context, "GET", "/external-secrets/secrets");
};
var getExternalSecretsProviders = async (context) => {
return await makeRestApiRequest(context, "GET", "/external-secrets/providers");
};
var getExternalSecretsProvider = async (context, id$1) => {
return await makeRestApiRequest(context, "GET", `/external-secrets/providers/${id$1}`);
};
var testExternalSecretsProviderConnection = async (context, id$1, data) => {
return await makeRestApiRequest(context, "POST", `/external-secrets/providers/${id$1}/test`, data);
};
var updateProvider = async (context, id$1, data) => {
return await makeRestApiRequest(context, "POST", `/external-secrets/providers/${id$1}`, data);
};
var reloadProvider = async (context, id$1) => {
return await makeRestApiRequest(context, "POST", `/external-secrets/providers/${id$1}/update`);
};
var connectProvider = async (context, id$1, connected) => {
return await makeRestApiRequest(context, "POST", `/external-secrets/providers/${id$1}/connect`, { connected });
};
async function getLdapConfig(context) {
return await makeRestApiRequest(context, "GET", "/ldap/config");
}
async function testLdapConnection(context) {
return await makeRestApiRequest(context, "POST", "/ldap/test-connection");
}
async function updateLdapConfig(context, adConfig) {
return await makeRestApiRequest(context, "PUT", "/ldap/config", adConfig);
}
async function runLdapSync(context, data) {
return await makeRestApiRequest(context, "POST", "/ldap/sync", data);
}
async function getLdapSynchronizations(context, pagination) {
return await makeRestApiRequest(context, "GET", "/ldap/sync", pagination);
}
async function canEnableMFA(context) {
return await makeRestApiRequest(context, "POST", "/mfa/can-enable");
}
async function getMfaQR(context) {
return await makeRestApiRequest(context, "GET", "/mfa/qr");
}
async function enableMfa(context, data) {
return await makeRestApiRequest(context, "POST", "/mfa/enable", data);
}
async function verifyMfaCode(context, data) {
return await makeRestApiRequest(context, "POST", "/mfa/verify", data);
}
async function disableMfa(context, data) {
return await makeRestApiRequest(context, "POST", "/mfa/disable", data);
}
async function updateEnforceMfa(context, enforce) {
return await makeRestApiRequest(context, "POST", "/mfa/enforce-mfa", { enforce });
}
async function fetchNodeTypesJsonWithRetry(url, retries = 5, delay = 500) {
for (let attempt = 0; attempt < retries; attempt++) {
const response = await axios_default.get(url, { withCredentials: true });
if (typeof response.data === "object" && response.data !== null) return response.data;
await sleep$1(delay * attempt);
}
throw new Error("Could not fetch node types");
}
async function getNodeTypes$1(baseUrl) {
return await fetchNodeTypesJsonWithRetry(baseUrl + "types/nodes.json");
}
async function fetchCommunityNodeTypes(context) {
return await makeRestApiRequest(context, "GET", "/community-node-types");
}
async function fetchCommunityNodeAttributes(context, type) {
return await makeRestApiRequest(context, "GET", `/community-node-types/${encodeURIComponent(type)}`);
}
async function getNodeTranslationHeaders(context) {
return await makeRestApiRequest(context, "GET", "/node-translation-headers");
}
async function getNodesInformation(context, nodeInfos) {
return await makeRestApiRequest(context, "POST", "/node-types", { nodeInfos });
}
async function getNodeParameterOptions(context, sendData) {
return await makeRestApiRequest(context, "POST", "/dynamic-node-parameters/options", sendData);
}
async function getResourceLocatorResults(context, sendData) {
return await makeRestApiRequest(context, "POST", "/dynamic-node-parameters/resource-locator-results", sendData);
}
async function getResourceMapperFields(context, sendData) {
return await makeRestApiRequest(context, "POST", "/dynamic-node-parameters/resource-mapper-fields", sendData);
}
async function getLocalResourceMapperFields(context, sendData) {
return await makeRestApiRequest(context, "POST", "/dynamic-node-parameters/local-resource-mapper-fields", sendData);
}
async function getNodeParameterActionResult(context, sendData) {
return await makeRestApiRequest(context, "POST", "/dynamic-node-parameters/action-result", sendData);
}
async function updateNpsSurveyState(context, state) {
await makeRestApiRequest(context, "PATCH", "/user-settings/nps-survey", state);
}
var GET_STATUS_ENDPOINT = "/orchestration/worker/status";
var sendGetWorkerStatus = async (context) => {
await makeRestApiRequest(context, "POST", GET_STATUS_ENDPOINT);
};
var getProvisioningConfig = async (context) => {
return await makeRestApiRequest(context, "GET", "/sso/provisioning/config");
};
var saveProvisioningConfig = async (context, config) => {
return await makeRestApiRequest(context, "PATCH", "/sso/provisioning/config", config);
};
async function getPromptsData(instanceId, userId) {
return await get$3(N8N_IO_BASE_URL, "/prompts", {}, {
"n8n-instance-id": instanceId,
"n8n-user-id": userId
});
}
async function submitContactInfo(instanceId, userId, email) {
return await post(N8N_IO_BASE_URL, "/prompt", { email }, {
"n8n-instance-id": instanceId,
"n8n-user-id": userId
});
}
var getRoles = async (context) => {
return await makeRestApiRequest(context, "GET", "/roles?withUsageCount=true");
};
var createProjectRole = async (context, body) => {
return await makeRestApiRequest(context, "POST", "/roles", body);
};
var getRoleBySlug = async (context, body) => {
return await makeRestApiRequest(context, "GET", `/roles/${body.slug}?withUsageCount=true`);
};
var updateProjectRole = async (context, slug, body) => {
return await makeRestApiRequest(context, "PATCH", `/roles/${slug}`, body);
};
var deleteProjectRole = async (context, slug) => {
return await makeRestApiRequest(context, "DELETE", `/roles/${slug}`);
};
var initSSO = async (context, redirectUrl = "") => {
return await makeRestApiRequest(context, "GET", `/sso/saml/initsso?redirect=${redirectUrl}`);
};
var getSamlMetadata = async (context) => {
return await makeRestApiRequest(context, "GET", "/sso/saml/metadata");
};
var getSamlConfig = async (context) => {
return await makeRestApiRequest(context, "GET", "/sso/saml/config");
};
var saveSamlConfig = async (context, data) => {
return await makeRestApiRequest(context, "POST", "/sso/saml/config", data);
};
var testSamlConfig = async (context) => {
return await makeRestApiRequest(context, "GET", "/sso/saml/config/test");
};
var getOidcConfig = async (context) => {
return await makeRestApiRequest(context, "GET", "/sso/oidc/config");
};
var saveOidcConfig = async (context, data) => {
return await makeRestApiRequest(context, "POST", "/sso/oidc/config", data);
};
async function getThirdPartyLicenses(context) {
return await request({
method: "GET",
baseURL: context.baseUrl,
endpoint: "/third-party-licenses"
});
}
async function dismissBannerPermanently(context, data) {
return await makeRestApiRequest(context, "POST", "/owner/dismiss-banner", { banner: data.bannerName });
}
var getLicense = async (context) => {
return await makeRestApiRequest(context, "GET", "/license");
};
var activateLicenseKey = async (context, data) => {
return await makeRestApiRequest(context, "POST", "/license/activate", data);
};
var renewLicense = async (context) => {
return await makeRestApiRequest(context, "POST", "/license/renew");
};
var requestLicenseTrial = async (context) => {
return await makeRestApiRequest(context, "POST", "/license/enterprise/request_trial");
};
var registerCommunityEdition = async (context, params) => {
return await makeRestApiRequest(context, "POST", "/license/enterprise/community-registered", params);
};
async function loginCurrentUser(context) {
return await makeRestApiRequest(context, "GET", "/login");
}
async function login(context, params) {
return await makeRestApiRequest(context, "POST", "/login", params);
}
async function logout(context) {
await makeRestApiRequest(context, "POST", "/logout");
}
async function setupOwner(context, params) {
return await makeRestApiRequest(context, "POST", "/owner/setup", params);
}
async function validateSignupToken(context, params) {
return await makeRestApiRequest(context, "GET", "/resolve-signup-token", params);
}
async function sendForgotPasswordEmail(context, params) {
await makeRestApiRequest(context, "POST", "/forgot-password", params);
}
async function validatePasswordToken(context, params) {
await makeRestApiRequest(context, "GET", "/resolve-password-token", params);
}
async function changePassword(context, params) {
await makeRestApiRequest(context, "POST", "/change-password", params);
}
async function updateCurrentUser(context, params) {
return await makeRestApiRequest(context, "PATCH", "/me", params);
}
async function updateCurrentUserSettings(context, settings$7) {
return await makeRestApiRequest(context, "PATCH", "/me/settings", settings$7);
}
async function updateOtherUserSettings(context, userId, settings$7) {
return await makeRestApiRequest(context, "PATCH", `/users/${userId}/settings`, settings$7);
}
async function updateCurrentUserPassword(context, params) {
return await makeRestApiRequest(context, "PATCH", "/me/password", params);
}
async function deleteUser(context, { id: id$1, transferId }) {
await makeRestApiRequest(context, "DELETE", `/users/${id$1}`, transferId ? { transferId } : {});
}
async function getUsers(context, filter$1) {
return await makeRestApiRequest(context, "GET", "/users", filter$1);
}
async function getPasswordResetLink(context, { id: id$1 }) {
return await makeRestApiRequest(context, "GET", `/users/${id$1}/password-reset-link`);
}
async function submitPersonalizationSurvey(context, params) {
await makeRestApiRequest(context, "POST", "/me/survey", params);
}
async function updateGlobalRole(context, { id: id$1, newRoleName }) {
return await makeRestApiRequest(context, "PATCH", `/users/${id$1}/role`, { newRoleName });
}
async function getNextVersions(endpoint, currentVersion, instanceId) {
return await get$3(endpoint, currentVersion, {}, { [INSTANCE_ID_HEADER]: instanceId });
}
async function getWhatsNewSection(endpoint, currentVersion, instanceId) {
return await get$3(endpoint, "", {}, {
[INSTANCE_ID_HEADER]: instanceId,
[INSTANCE_VERSION_HEADER]: currentVersion
});
}
var findWebhook = async (context, data) => {
return await makeRestApiRequest(context, "POST", "/webhooks/find", data);
};
var getWorkflowHistory = async (context, workflowId, queryParams) => {
const { data } = await get$3(context.baseUrl, `/workflow-history/workflow/${workflowId}`, queryParams);
return data;
};
var getWorkflowVersion = async (context, workflowId, versionId) => {
const { data } = await get$3(context.baseUrl, `/workflow-history/workflow/${workflowId}/version/${versionId}`);
return data;
};
var import_es5 = /* @__PURE__ */ __toESM(require_es5());
const useSettingsStore = defineStore(STORES.SETTINGS, () => {
const initialized = ref(false);
const settings$7 = ref({});
const moduleSettings = ref({});
const userManagement = ref({
quota: -1,
showSetupOnFirstLoad: false,
smtpSetup: false,
authenticationMethod: UserManagementAuthenticationMethod.Email
});
const templatesEndpointHealthy = ref(false);
const api = ref({
enabled: false,
latestVersion: 0,
path: "/",
swaggerUi: { enabled: false }
});
const mfa = ref({ enabled: false });
const folders = ref({ enabled: false });
const saveDataErrorExecution = ref("all");
const saveDataSuccessExecution = ref("all");
const saveManualExecutions = ref(false);
const saveDataProgressExecution = ref(false);
const isMFAEnforced = ref(false);
const isDocker = computed(() => settings$7.value?.isDocker ?? false);
const databaseType = computed(() => settings$7.value?.databaseType);
const planName = computed(() => settings$7.value?.license.planName ?? "Community");
const consumerId = computed(() => settings$7.value?.license.consumerId);
const binaryDataMode = computed(() => settings$7.value?.binaryDataMode);
const pruning = computed(() => settings$7.value?.pruning);
const security = computed(() => ({
blockFileAccessToN8nFiles: settings$7.value.security.blockFileAccessToN8nFiles,
secureCookie: settings$7.value.authCookie.secure
}));
const isEnterpriseFeatureEnabled$1 = computed(() => settings$7.value.enterprise ?? {});
const nodeJsVersion = computed(() => settings$7.value.nodeJsVersion);
const nodeEnv = computed(() => settings$7.value.nodeEnv);
const concurrency = computed(() => settings$7.value.concurrency);
const isNativePythonRunnerEnabled = computed(() => settings$7.value.isNativePythonRunnerEnabled);
const isConcurrencyEnabled = computed(() => concurrency.value !== -1);
const isPublicApiEnabled = computed(() => api.value.enabled);
const isSwaggerUIEnabled = computed(() => api.value.swaggerUi.enabled);
const isPreviewMode = computed(() => settings$7.value.previewMode);
const publicApiLatestVersion = computed(() => api.value.latestVersion);
const publicApiPath = computed(() => api.value.path);
const isAiAssistantEnabled = computed(() => settings$7.value.aiAssistant?.enabled && settings$7.value.aiAssistant?.setup);
const isAskAiEnabled = computed(() => settings$7.value.askAi?.enabled);
const isAiBuilderEnabled = computed(() => settings$7.value.aiBuilder?.enabled && settings$7.value.aiBuilder?.setup);
const isAiAssistantOrBuilderEnabled = computed(() => isAiAssistantEnabled.value || isAiBuilderEnabled.value);
const showSetupPage = computed(() => userManagement.value.showSetupOnFirstLoad);
const deploymentType = computed(() => settings$7.value.deployment?.type || "default");
const isCloudDeployment = computed(() => settings$7.value.deployment?.type === "cloud");
const activeModules = computed(() => settings$7.value.activeModules);
const isModuleActive = (moduleName) => {
return activeModules.value?.includes(moduleName);
};
const isAiCreditsEnabled = computed(() => settings$7.value.aiCredits?.enabled);
const aiCreditsQuota = computed(() => settings$7.value.aiCredits?.credits);
const isSmtpSetup = computed(() => userManagement.value.smtpSetup);
const isPersonalizationSurveyEnabled = computed(() => settings$7.value.telemetry?.enabled && settings$7.value.personalizationSurveyEnabled);
const telemetry$1 = computed(() => settings$7.value.telemetry);
const logLevel = computed(() => settings$7.value.logLevel);
const isTelemetryEnabled = computed(() => settings$7.value.telemetry && settings$7.value.telemetry.enabled);
const isMFAEnforcementLicensed = computed(() => {
return settings$7.value.enterprise?.mfaEnforcement ?? false;
});
const isMfaFeatureEnabled = computed(() => mfa.value.enabled);
const isFoldersFeatureEnabled = computed(() => folders.value.enabled);
const isDataTableFeatureEnabled = computed(() => isModuleActive("data-table"));
const isChatFeatureEnabled = computed(() => isModuleActive("chat-hub"));
const isCustomRolesFeatureEnabled = computed(() => settings$7.value.enterprise?.customRoles ?? false);
const areTagsEnabled = computed(() => settings$7.value.workflowTagsDisabled !== void 0 ? !settings$7.value.workflowTagsDisabled : true);
const isHiringBannerEnabled = computed(() => settings$7.value.hiringBannerEnabled);
const isTemplatesEnabled = computed(() => Boolean(settings$7.value.templates?.enabled));
const isTemplatesEndpointReachable = computed(() => templatesEndpointHealthy.value);
const templatesHost = computed(() => settings$7.value.templates?.host ?? "");
const pushBackend = computed(() => settings$7.value.pushBackend);
const isCommunityNodesFeatureEnabled = computed(() => settings$7.value.communityNodesEnabled);
const isUnverifiedPackagesEnabled = computed(() => settings$7.value.unverifiedCommunityNodesEnabled);
const allowedModules = computed(() => settings$7.value.allowedModules);
const isQueueModeEnabled = computed(() => settings$7.value.executionMode === "queue");
const isMultiMain = computed(() => settings$7.value.isMultiMain);
const isWorkerViewAvailable = computed(() => !!settings$7.value.enterprise?.workerView);
const workflowCallerPolicyDefaultOption = computed(() => settings$7.value.workflowCallerPolicyDefaultOption);
const permanentlyDismissedBanners = computed(() => settings$7.value.banners?.dismissed ?? []);
const isCommunityPlan = computed(() => planName.value.toLowerCase() === "community");
const isDevRelease = computed(() => settings$7.value.releaseChannel === "dev");
const setSettings = (newSettings) => {
settings$7.value = newSettings;
userManagement.value = newSettings.userManagement;
if (userManagement.value) userManagement.value.showSetupOnFirstLoad = !!settings$7.value.userManagement.showSetupOnFirstLoad;
if (settings$7.value.publicApi) api.value = settings$7.value.publicApi;
mfa.value.enabled = settings$7.value.mfa?.enabled;
folders.value.enabled = settings$7.value.folders?.enabled;
if (settings$7.value.versionCli) useRootStore().setVersionCli(settings$7.value.versionCli);
if (settings$7.value.authCookie.secure) {
const { browser } = import_es5.default.parse(navigator.userAgent);
if (location.protocol === "http:" && (!["localhost", "127.0.0.1"].includes(location.hostname) || browser.name === "Safari")) {
document.write(INSECURE_CONNECTION_WARNING);
return;
}
}
};
const setAllowedModules = (allowedModules$1) => {
settings$7.value.allowedModules = allowedModules$1;
};
const setSaveDataErrorExecution = (newValue) => {
saveDataErrorExecution.value = newValue;
};
const setSaveDataSuccessExecution = (newValue) => {
saveDataSuccessExecution.value = newValue;
};
const setSaveManualExecutions = (newValue) => {
saveManualExecutions.value = newValue;
};
const setSaveDataProgressExecution = (newValue) => {
saveDataProgressExecution.value = newValue;
};
const getSettings$1 = async () => {
const rootStore = useRootStore();
const fetchedSettings = await getSettings(rootStore.restApiContext);
setSettings(fetchedSettings);
if (fetchedSettings.settingsMode === "public") return;
settings$7.value.communityNodesEnabled = fetchedSettings.communityNodesEnabled;
settings$7.value.unverifiedCommunityNodesEnabled = fetchedSettings.unverifiedCommunityNodesEnabled;
setAllowedModules(fetchedSettings.allowedModules);
setSaveDataErrorExecution(fetchedSettings.saveDataErrorExecution);
setSaveDataSuccessExecution(fetchedSettings.saveDataSuccessExecution);
setSaveDataProgressExecution(fetchedSettings.saveExecutionProgress);
setSaveManualExecutions(fetchedSettings.saveManualExecutions);
isMFAEnforced.value = settings$7.value.mfa?.enforced ?? false;
rootStore.setUrlBaseWebhook(fetchedSettings.urlBaseWebhook);
rootStore.setUrlBaseEditor(fetchedSettings.urlBaseEditor);
rootStore.setEndpointForm(fetchedSettings.endpointForm);
rootStore.setEndpointFormTest(fetchedSettings.endpointFormTest);
rootStore.setEndpointFormWaiting(fetchedSettings.endpointFormWaiting);
rootStore.setEndpointWebhook(fetchedSettings.endpointWebhook);
rootStore.setEndpointWebhookTest(fetchedSettings.endpointWebhookTest);
rootStore.setEndpointWebhookWaiting(fetchedSettings.endpointWebhookWaiting);
rootStore.setEndpointMcp(fetchedSettings.endpointMcp);
rootStore.setEndpointMcpTest(fetchedSettings.endpointMcpTest);
rootStore.setTimezone(fetchedSettings.timezone);
rootStore.setExecutionTimeout(fetchedSettings.executionTimeout);
rootStore.setMaxExecutionTimeout(fetchedSettings.maxExecutionTimeout);
rootStore.setInstanceId(fetchedSettings.instanceId);
rootStore.setOauthCallbackUrls(fetchedSettings.oauthCallbackUrls);
rootStore.setN8nMetadata(fetchedSettings.n8nMetadata || {});
rootStore.setDefaultLocale(fetchedSettings.defaultLocale);
rootStore.setBinaryDataMode(fetchedSettings.binaryDataMode);
if (fetchedSettings.telemetry.enabled) sessionStarted(rootStore.restApiContext);
};
const initialize = async () => {
if (initialized.value) return;
await getSettings$1();
initialized.value = true;
};
const stopShowingSetupPage = () => {
userManagement.value.showSetupOnFirstLoad = false;
};
const disableTemplates = () => {
settings$7.value = {
...settings$7.value,
templates: {
...settings$7.value.templates,
enabled: false
}
};
};
const testTemplatesEndpoint = async () => {
const timeout$2 = new Promise((_, reject) => setTimeout(() => reject(), 2e3));
await Promise.race([testHealthEndpoint(templatesHost.value), timeout$2]);
templatesEndpointHealthy.value = true;
};
const getTimezones = async () => {
return await makeRestApiRequest(useRootStore().restApiContext, "GET", "/options/timezones");
};
const reset = () => {
settings$7.value = {};
};
const getModuleSettings$1 = async () => {
moduleSettings.value = await getModuleSettings(useRootStore().restApiContext);
};
return {
settings: settings$7,
userManagement,
templatesEndpointHealthy,
api,
mfa,
isDocker,
isDevRelease,
isEnterpriseFeatureEnabled: isEnterpriseFeatureEnabled$1,
databaseType,
planName,
consumerId,
binaryDataMode,
pruning,
security,
nodeJsVersion,
nodeEnv,
concurrency,
isNativePythonRunnerEnabled,
isConcurrencyEnabled,
isPublicApiEnabled,
isSwaggerUIEnabled,
isPreviewMode,
publicApiLatestVersion,
publicApiPath,
showSetupPage,
deploymentType,
isCloudDeployment,
isSmtpSetup,
isPersonalizationSurveyEnabled,
telemetry: telemetry$1,
logLevel,
isTelemetryEnabled,
isMfaFeatureEnabled,
isFoldersFeatureEnabled,
isAiAssistantEnabled,
isCustomRolesFeatureEnabled,
areTagsEnabled,
isHiringBannerEnabled,
isTemplatesEnabled,
isTemplatesEndpointReachable,
templatesHost,
pushBackend,
isCommunityNodesFeatureEnabled,
isUnverifiedPackagesEnabled,
allowedModules,
isQueueModeEnabled,
isMultiMain,
isWorkerViewAvailable,
workflowCallerPolicyDefaultOption,
permanentlyDismissedBanners,
saveDataErrorExecution,
saveDataSuccessExecution,
saveManualExecutions,
saveDataProgressExecution,
isCommunityPlan,
isAskAiEnabled,
isAiBuilderEnabled,
isAiAssistantOrBuilderEnabled,
isAiCreditsEnabled,
aiCreditsQuota,
reset,
getTimezones,
testTemplatesEndpoint,
disableTemplates,
stopShowingSetupPage,
getSettings: getSettings$1,
setSettings,
initialize,
getModuleSettings: getModuleSettings$1,
moduleSettings,
isMFAEnforcementLicensed,
isMFAEnforced,
activeModules,
isModuleActive,
isDataTableFeatureEnabled,
isChatFeatureEnabled
};
});
function useStorage(key) {
const data = useStorage$1(key, null, void 0, { writeDefaults: false });
if (data.value === "undefined") data.value = null;
return data;
}
const LOCAL_STORAGE_MAPPING_IS_ONBOARDED = "N8N_MAPPING_ONBOARDED";
const LOCAL_STORAGE_AUTOCOMPLETE_IS_ONBOARDED = "N8N_AUTOCOMPLETE_ONBOARDED";
const LOCAL_STORAGE_TABLE_HOVER_IS_ONBOARDED = "N8N_TABLE_HOVER_ONBOARDED";
const LOCAL_STORAGE_NDV_INPUT_PANEL_DISPLAY_MODE = "N8N_NDV_INPUT_PANEL_DISPLAY_MODE";
const LOCAL_STORAGE_NDV_OUTPUT_PANEL_DISPLAY_MODE = "N8N_NDV_OUTPUT_PANEL_DISPLAY_MODE";
const LOCAL_STORAGE_NDV_PANEL_WIDTH = "N8N_NDV_PANEL_WIDTH";
var regex_default = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/i;
function validate(uuid) {
return typeof uuid === "string" && regex_default.test(uuid);
}
var validate_default = validate;
var byteToHex = [];
for (var i = 0; i < 256; ++i) byteToHex.push((i + 256).toString(16).slice(1));
function unsafeStringify(arr, offset = 0) {
return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
}
var getRandomValues;
var rnds8 = new Uint8Array(16);
function rng() {
if (!getRandomValues) {
getRandomValues = typeof crypto !== "undefined" && crypto.getRandomValues && crypto.getRandomValues.bind(crypto);
if (!getRandomValues) throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");
}
return getRandomValues(rnds8);
}
var native_default = { randomUUID: typeof crypto !== "undefined" && crypto.randomUUID && crypto.randomUUID.bind(crypto) };
function v4(options, buf, offset) {
if (native_default.randomUUID && !buf && !options) return native_default.randomUUID();
options = options || {};
var rnds = options.random || (options.rng || rng)();
rnds[6] = rnds[6] & 15 | 64;
rnds[8] = rnds[8] & 63 | 128;
if (buf) {
offset = offset || 0;
for (var i$1 = 0; i$1 < 16; ++i$1) buf[offset + i$1] = rnds[i$1];
return buf;
}
return unsafeStringify(rnds);
}
var v4_default = v4;
async function getNewWorkflow(context, data) {
const response = await makeRestApiRequest(context, "GET", "/workflows/new", data);
return {
name: response.name,
settings: response.defaultSettings
};
}
async function getWorkflow(context, id$1) {
return await makeRestApiRequest(context, "GET", `/workflows/${id$1}`);
}
async function getWorkflows(context, filter$1, options, select$1) {
return await getFullApiResponse(context, "GET", "/workflows", {
includeScopes: true,
...filter$1 ? { filter: filter$1 } : {},
...options ? options : {},
...select$1 ? { select: JSON.stringify(select$1) } : {}
});
}
async function getWorkflowsWithNodesIncluded(context, nodeTypes) {
return await getFullApiResponse(context, "POST", "/workflows/with-node-types", { nodeTypes });
}
async function getWorkflowsAndFolders(context, filter$1, options, includeFolders, onlySharedWithMe) {
return await getFullApiResponse(context, "GET", "/workflows", {
includeScopes: true,
includeFolders,
onlySharedWithMe,
...filter$1 ? { filter: filter$1 } : {},
...options ? options : {}
});
}
async function getActiveWorkflows(context) {
return await makeRestApiRequest(context, "GET", "/active-workflows");
}
async function getExecutionData(context, executionId) {
return await makeRestApiRequest(context, "GET", `/executions/${executionId}`);
}
async function getLastSuccessfulExecution(context, workflowId) {
return await makeRestApiRequest(context, "GET", `/workflows/${workflowId}/executions/last-successful`);
}
const MAX_TAG_NAME_LENGTH = 24;
const TAGS_MANAGER_MODAL_KEY = "tagsManager";
const ANNOTATION_TAGS_MANAGER_MODAL_KEY = "annotationTagsManager";
const DEBUG_PAYWALL_MODAL_KEY = "debugPaywall";
const COMMUNITY_PLUS_ENROLLMENT_MODAL = "communityPlusEnrollment";
const COMMUNITY_PLUS_DOCS_URL = "https://docs.n8n.io/hosting/community-edition-features/#registered-community-edition";
const VARIABLE_MODAL_KEY = "variableModal";
const CREDENTIAL_EDIT_MODAL_KEY = "editCredential";
const CREDENTIAL_SELECT_MODAL_KEY = "selectCredential";
const DELETE_USER_MODAL_KEY = "deleteUser";
const INVITE_USER_MODAL_KEY = "inviteUser";
const PERSONALIZATION_MODAL_KEY = "personalization";
const WORK_AREA_KEY = "workArea";
const COMPANY_TYPE_KEY = "companyType";
const SAAS_COMPANY_TYPE = "saas";
const ECOMMERCE_COMPANY_TYPE = "ecommerce";
const EDUCATION_TYPE = "education";
const DIGITAL_AGENCY_COMPANY_TYPE = "digital-agency";
const SYSTEMS_INTEGRATOR_COMPANY_TYPE = "systems-integrator";
const OTHER_COMPANY_TYPE = "other";
const PERSONAL_COMPANY_TYPE = "personal";
const COMPANY_INDUSTRY_EXTENDED_KEY = "companyIndustryExtended";
const OTHER_COMPANY_INDUSTRY_EXTENDED_KEY = "otherCompanyIndustryExtended";
const PHYSICAL_RETAIL_OR_SERVICES = "physical-retail-or-services";
const REAL_ESTATE_OR_CONSTRUCTION = "real-estate-or-construction";
const GOVERNMENT_INDUSTRY = "government";
const LEGAL_INDUSTRY = "legal-industry";
const MARKETING_INDUSTRY = "marketing-industry";
const MEDIA_INDUSTRY = "media-industry";
const MANUFACTURING_INDUSTRY = "manufacturing-industry";
const MSP_INDUSTRY = "msp";
const HEALTHCARE_INDUSTRY = "healthcare";
const FINANCE_INSURANCE_INDUSTRY = "finance-insurance-industry";
const IT_INDUSTRY = "it-industry";
const SECURITY_INDUSTRY = "security-industry";
const TELECOMS_INDUSTRY = "telecoms";
const OTHER_INDUSTRY_OPTION = "other";
const COMPANY_SIZE_KEY = "companySize";
const COMPANY_SIZE_20_OR_LESS = "<20";
const COMPANY_SIZE_20_99 = "20-99";
const COMPANY_SIZE_100_499 = "100-499";
const COMPANY_SIZE_500_999 = "500-999";
const COMPANY_SIZE_1000_OR_MORE = "1000+";
const COMPANY_SIZE_PERSONAL_USE = "personalUser";
const MARKETING_AUTOMATION_GOAL_KEY = "automationGoalSm";
const MARKETING_AUTOMATION_LEAD_GENERATION_GOAL = "lead-generation";
const MARKETING_AUTOMATION_CUSTOMER_COMMUNICATION = "customer-communication";
const MARKETING_AUTOMATION_ACTIONS = "actions";
const MARKETING_AUTOMATION_AD_CAMPAIGN = "ad-campaign";
const MARKETING_AUTOMATION_REPORTING = "reporting";
const MARKETING_AUTOMATION_DATA_SYNCHING = "data-syncing";
const MARKETING_AUTOMATION_OTHER = "other";
const OTHER_MARKETING_AUTOMATION_GOAL_KEY = "automationGoalSmOther";
const CODING_SKILL_KEY = "codingSkill";
const AUTOMATION_BENEFICIARY_KEY = "automationBeneficiary";
const AUTOMATION_BENEFICIARY_SELF = "myself";
const AUTOMATION_BENEFICIARY_MY_TEAM = "my-team";
const AUTOMATION_BENEFICIARY_OTHER_TEAMS = "other-teams";
const REPORTED_SOURCE_KEY = "reportedSource";
const REPORTED_SOURCE_OTHER_KEY = "reportedSourceOther";
const REPORTED_SOURCE_GOOGLE = "google";
const REPORTED_SOURCE_TWITTER = "twitter";
const REPORTED_SOURCE_LINKEDIN = "linkedin";
const REPORTED_SOURCE_YOUTUBE = "youtube";
const REPORTED_SOURCE_FRIEND = "friend";
const REPORTED_SOURCE_PODCAST = "podcast";
const REPORTED_SOURCE_EVENT = "event";
const REPORTED_SOURCE_OTHER = "other";
const AUTOMATION_GOAL_KEY = "automationGoal";
const DEVOPS_AUTOMATION_GOAL_KEY = "automationGoalDevops";
const DEVOPS_AUTOMATION_GOAL_OTHER_KEY = "automationGoalDevopsOther";
const DEVOPS_AUTOMATION_OTHER = "other";
const DEVOPS_AUTOMATION_CI_CD_GOAL = "ci-cd";
const DEVOPS_AUTOMATION_CLOUD_INFRASTRUCTURE_ORCHESTRATION_GOAL = "cloud-infrastructure-orchestration";
const DEVOPS_AUTOMATION_DATA_SYNCING_GOAL = "data-syncing";
const DEVOPS_INCIDENT_RESPONSE_GOAL = "incident-response";
const DEVOPS_MONITORING_AND_ALERTING_GOAL = "monitoring-alerting";
const DEVOPS_REPORTING_GOAL = "reporting";
const DEVOPS_TICKETING_SYSTEMS_INTEGRATIONS_GOAL = "ticketing-systems-integrations";
const CUSTOMER_INTEGRATIONS_GOAL = "customer-integrations";
const CUSTOMER_SUPPORT_GOAL = "customer-support";
const FINANCE_ACCOUNTING_GOAL = "finance-accounting";
const PRODUCT_GOAL = "product";
const SALES_MARKETING_GOAL = "sales-marketing";
const SECURITY_GOAL = "security";
const OTHER_AUTOMATION_GOAL = "other";
const ROLE_KEY = "role";
const ROLE_OTHER_KEY = "roleOther";
const ROLE_BUSINESS_OWNER = "business-owner";
const ROLE_CUSTOMER_SUPPORT = "customer-support";
const ROLE_DATA_SCIENCE = "data-science";
const ROLE_DEVOPS = "devops";
const ROLE_IT = "it";
const ROLE_ENGINEERING = "engineering";
const ROLE_SALES_AND_MARKETING = "sales-and-marketing";
const ROLE_SECURITY = "security";
const ROLE_OTHER = "other";
const ILLEGAL_FOLDER_CHARACTERS = [
"[",
"]",
"^",
"\\",
"/",
":",
"*",
"?",
"\"",
"<",
">",
"|"
];
const FOLDER_NAME_ILLEGAL_CHARACTERS_REGEX = /* @__PURE__ */ new RegExp(`[${ILLEGAL_FOLDER_CHARACTERS.map((char) => {
return char.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}).join("")}]`);
const FOLDER_NAME_ONLY_DOTS_REGEX = /^\.+$/;
const FOLDER_NAME_MAX_LENGTH = 128;
const DELETE_FOLDER_MODAL_KEY = "deleteFolder";
const MOVE_FOLDER_MODAL_KEY = "moveFolder";
const FOLDER_LIST_ITEM_ACTIONS = {
OPEN: "open",
CREATE: "create",
CREATE_WORKFLOW: "create_workflow",
RENAME: "rename",
MOVE: "move",
CHOWN: "change_owner",
TAGS: "manage_tags",
DELETE: "delete"
};
const SOURCE_CONTROL_PUSH_MODAL_KEY = "sourceControlPush";
const SOURCE_CONTROL_PULL_MODAL_KEY = "sourceControlPull";
const PROJECT_MOVE_RESOURCE_MODAL = "projectMoveResourceModal";
const COMMUNITY_PACKAGE_INSTALL_MODAL_KEY = "communityPackageInstall";
const COMMUNITY_PACKAGE_CONFIRM_MODAL_KEY = "communityPackageManageConfirm";
const COMMUNITY_NODES_INSTALLATION_DOCS_URL = `https://${DOCS_DOMAIN}/integrations/community-nodes/installation/gui-install/`;
const COMMUNITY_NODES_RISKS_DOCS_URL = `https://${DOCS_DOMAIN}/integrations/community-nodes/risks/`;
`${DOCS_DOMAIN}`;
const NPM_KEYWORD_SEARCH_URL = "https://www.npmjs.com/search?q=keywords%3An8n-community-node-package";
const COMMUNITY_PACKAGE_MANAGE_ACTIONS = {
UNINSTALL: "uninstall",
UPDATE: "update",
VIEW_DOCS: "view-documentation"
};
const API_KEY_CREATE_OR_EDIT_MODAL_KEY = "createOrEditApiKey";
function applyThemeToBody(theme, window_) {
if (theme === "system") (window_ ?? window).document.body.removeAttribute("data-theme");
else (window_ ?? window).document.body.setAttribute?.("data-theme", theme);
}
function isValidTheme(theme) {
return !!theme && ["light", "dark"].includes(theme);
}
function getThemeOverride() {
const override = getQueryParam("theme") ?? localStorage.getItem("N8N_THEME");
return isValidTheme(override) ? override : null;
}
function getQueryParam(paramName) {
return new URLSearchParams(window.location.search).get(paramName);
}
var modals = /* @__PURE__ */ new Map();
var listeners = /* @__PURE__ */ new Set();
function getAll() {
return new Map(modals);
}
function notifyListeners() {
listeners.forEach((listener) => listener(getAll()));
}
function register(modal) {
if (modals.has(modal.key)) {
console.warn(`Modal with key "${modal.key}" is already registered. Skipping.`);
return;
}
modals.set(modal.key, modal);
notifyListeners();
}
function subscribe(listener) {
listeners.add(listener);
return () => {
listeners.delete(listener);
};
}
var import_identity = /* @__PURE__ */ __toESM(require_identity());
var savedTheme = "system";
try {
const value = getThemeOverride();
if (value !== null) {
savedTheme = value;
applyThemeToBody(value);
}
} catch (e) {}
const useUIStore = defineStore(STORES.UI, () => {
const telemetry$1 = useTelemetry();
const activeActions = ref([]);
const activeCredentialType = ref(null);
const theme = useLocalStorage(LOCAL_STORAGE_THEME, savedTheme, {
writeDefaults: false,
serializer: {
read: (value) => isValidTheme(value) ? value : savedTheme,
write: import_identity.default
}
});
const modalsById = ref({
...Object.fromEntries([
ABOUT_MODAL_KEY,
CHAT_EMBED_MODAL_KEY,
CHANGE_PASSWORD_MODAL_KEY,
CONFIRM_PASSWORD_MODAL_KEY,
CONTACT_PROMPT_MODAL_KEY,
CREDENTIAL_SELECT_MODAL_KEY,
DUPLICATE_MODAL_KEY,
PERSONALIZATION_MODAL_KEY,
NODE_PINNING_MODAL_KEY,
INVITE_USER_MODAL_KEY,
TAGS_MANAGER_MODAL_KEY,
ANNOTATION_TAGS_MANAGER_MODAL_KEY,
NPS_SURVEY_MODAL_KEY,
VERSIONS_MODAL_KEY,
WORKFLOW_SETTINGS_MODAL_KEY,
WORKFLOW_SHARE_MODAL_KEY,
WORKFLOW_ACTIVE_MODAL_KEY,
COMMUNITY_PACKAGE_INSTALL_MODAL_KEY,
MFA_SETUP_MODAL_KEY,
PROMPT_MFA_CODE_MODAL_KEY,
SOURCE_CONTROL_PUSH_MODAL_KEY,
SOURCE_CONTROL_PULL_MODAL_KEY,
EXTERNAL_SECRETS_PROVIDER_MODAL_KEY,
DEBUG_PAYWALL_MODAL_KEY,
WORKFLOW_HISTORY_VERSION_RESTORE,
SETUP_CREDENTIALS_MODAL_KEY,
PROJECT_MOVE_RESOURCE_MODAL,
NEW_ASSISTANT_SESSION_MODAL,
IMPORT_WORKFLOW_URL_MODAL_KEY,
PRE_BUILT_AGENTS_MODAL_KEY,
WORKFLOW_DIFF_MODAL_KEY,
EXPERIMENT_TEMPLATE_RECO_V3_KEY,
VARIABLE_MODAL_KEY
].map((modalKey) => [modalKey, { open: false }])),
[DELETE_USER_MODAL_KEY]: {
open: false,
activeId: null
},
[COMMUNITY_PACKAGE_CONFIRM_MODAL_KEY]: {
open: false,
mode: "",
activeId: null
},
[IMPORT_CURL_MODAL_KEY]: {
open: false,
data: { curlCommands: {} }
},
[LOG_STREAM_MODAL_KEY]: {
open: false,
data: void 0
},
[API_KEY_CREATE_OR_EDIT_MODAL_KEY]: {
open: false,
data: {
activeId: null,
mode: ""
}
},
[CREDENTIAL_EDIT_MODAL_KEY]: {
open: false,
mode: "",
activeId: null,
showAuthSelector: false
},
[DELETE_FOLDER_MODAL_KEY]: {
open: false,
activeId: null,
data: {
workflowListEventBus: void 0,
content: {
workflowCount: 0,
subFolderCount: 0
}
}
},
[MOVE_FOLDER_MODAL_KEY]: {
open: false,
activeId: null,
data: { workflowListEventBus: void 0 }
},
[COMMUNITY_PLUS_ENROLLMENT_MODAL]: {
open: false,
data: { customHeading: void 0 }
},
[WORKFLOW_ACTIVATION_CONFLICTING_WEBHOOK_MODAL_KEY]: {
open: false,
data: {
triggerType: "",
workflowName: "",
workflowId: "",
webhookPath: "",
node: ""
}
},
[FROM_AI_PARAMETERS_MODAL_KEY]: {
open: false,
data: { nodeName: void 0 }
},
[IMPORT_WORKFLOW_URL_MODAL_KEY]: {
open: false,
data: { url: "" }
},
[WORKFLOW_EXTRACTION_NAME_MODAL_KEY]: {
open: false,
data: { workflowName: "" }
},
[WHATS_NEW_MODAL_KEY]: {
open: false,
data: { articleId: void 0 }
},
[EXPERIMENT_TEMPLATE_RECO_V2_KEY]: {
open: false,
data: { nodeName: "" }
},
[EXPERIMENT_TEMPLATES_DATA_QUALITY_KEY]: {
open: false,
data: {}
}
});
const modalStack = ref([]);
const sidebarMenuCollapsedPreference = useLocalStorage("sidebar.collapsed", true);
const sidebarMenuCollapsed = ref(sidebarMenuCollapsedPreference.value);
const currentView = ref("");
const stateIsDirty = ref(false);
const lastSelectedNode = ref(null);
const nodeViewOffsetPosition = ref([0, 0]);
const nodeViewInitialized = ref(false);
const addFirstStepOnLoad = ref(false);
const pendingNotificationsForViews = ref({});
const processingExecutionResults = ref(false);
const isBlankRedirect = ref(false);
const moduleTabs = ref({
overview: {},
project: {},
shared: {}
});
const registeredSettingsPages = ref({});
const appGridDimensions = ref({
width: 0,
height: 0
});
const lastInteractedWithNodeConnection = ref();
const lastInteractedWithNodeHandle = ref(null);
const lastInteractedWithNodeId = ref();
const lastCancelledConnectionPosition = ref();
const settingsStore = useSettingsStore();
const workflowsStore = useWorkflowsStore();
const isDarkThemePreferred = useMediaQuery("(prefers-color-scheme: dark)");
const preferredSystemTheme = computed(() => isDarkThemePreferred.value ? "dark" : "light");
const appliedTheme = computed(() => {
return theme.value === "system" ? preferredSystemTheme.value : theme.value;
});
const contextBasedTranslationKeys = computed(() => {
const deploymentType = settingsStore.deploymentType;
let contextKey = "";
if (deploymentType === "cloud") contextKey = ".cloud";
return {
feature: { unavailable: { title: `contextual.feature.unavailable.title${contextKey}` } },
credentials: { sharing: { unavailable: {
title: `contextual.credentials.sharing.unavailable.title${contextKey}`,
description: `contextual.credentials.sharing.unavailable.description${contextKey}`,
action: `contextual.credentials.sharing.unavailable.action${contextKey}`,
button: `contextual.credentials.sharing.unavailable.button${contextKey}`
} } },
workflows: { sharing: {
title: "contextual.workflows.sharing.title",
unavailable: {
title: `contextual.workflows.sharing.unavailable.title${contextKey}`,
description: {
modal: `contextual.workflows.sharing.unavailable.description.modal${contextKey}`,
tooltip: `contextual.workflows.sharing.unavailable.description.tooltip${contextKey}`
},
action: `contextual.workflows.sharing.unavailable.action${contextKey}`,
button: `contextual.workflows.sharing.unavailable.button${contextKey}`
}
} },
variables: { unavailable: {
title: `contextual.variables.unavailable.title${contextKey}`,
description: "contextual.variables.unavailable.description",
action: `contextual.variables.unavailable.action${contextKey}`,
button: `contextual.variables.unavailable.button${contextKey}`
} },
users: { settings: { unavailable: {
title: `contextual.users.settings.unavailable.title${contextKey}`,
description: `contextual.users.settings.unavailable.description${contextKey}`,
button: `contextual.users.settings.unavailable.button${contextKey}`
} } }
};
});
const lastInteractedWithNode = computed(() => {
if (lastInteractedWithNodeId.value) return workflowsStore.getNodeById(lastInteractedWithNodeId.value);
return null;
});
const isModalActiveById = computed(() => Object.keys(modalsById.value).reduce((acc, name$13) => {
acc[name$13] = name$13 === modalStack.value[0];
return acc;
}, {}));
const activeModals = computed(() => modalStack.value.map((modalName) => modalName));
const settingsSidebarItems = computed(() => {
const items = [];
Object.entries(registeredSettingsPages.value).forEach(([moduleName, moduleItems]) => {
if (settingsStore.isModuleActive(moduleName)) items.push(...moduleItems.map((item) => ({
...item,
available: true
})));
});
return items;
});
const isReadOnlyView = computed(() => {
return ![
VIEWS.WORKFLOW.toString(),
VIEWS.NEW_WORKFLOW.toString(),
VIEWS.EXECUTION_DEBUG.toString()
].includes(currentView.value);
});
const isActionActive = computed(() => activeActions.value.reduce((acc, action) => {
acc[action] = true;
return acc;
}, {}));
const headerHeight = computed(() => {
const style = getComputedStyle(document.body);
return Number(style.getPropertyValue("--header--height"));
});
const isAnyModalOpen = computed(() => {
return modalStack.value.length > 0;
});
const isProcessingExecutionResults = computed(() => processingExecutionResults.value);
const setTheme = (newTheme) => {
theme.value = newTheme;
applyThemeToBody(newTheme);
};
const setMode = (name$13, mode) => {
modalsById.value[name$13] = {
...modalsById.value[name$13],
mode
};
};
const setActiveId = (name$13, activeId) => {
modalsById.value[name$13] = {
...modalsById.value[name$13],
activeId
};
};
const setShowAuthSelector = (name$13, showAuthSelector) => {
modalsById.value[name$13] = {
...modalsById.value[name$13],
showAuthSelector
};
};
const setModalData = (payload) => {
modalsById.value[payload.name] = {
...modalsById.value[payload.name],
data: payload.data
};
};
const openModal = (name$13) => {
modalsById.value[name$13] = {
...modalsById.value[name$13],
open: true
};
modalStack.value = [name$13].concat(modalStack.value);
};
const openModalWithData = (payload) => {
setModalData(payload);
openModal(payload.name);
};
const closeModal = (name$13) => {
modalsById.value[name$13] = {
...modalsById.value[name$13],
open: false
};
modalStack.value = modalStack.value.filter((openModalName) => name$13 !== openModalName);
};
const openDeleteUserModal = (id$1) => {
setActiveId(DELETE_USER_MODAL_KEY, id$1);
openModal(DELETE_USER_MODAL_KEY);
};
const openExistingCredential = (id$1) => {
setActiveId(CREDENTIAL_EDIT_MODAL_KEY, id$1);
setMode(CREDENTIAL_EDIT_MODAL_KEY, "edit");
openModal(CREDENTIAL_EDIT_MODAL_KEY);
};
const openNewCredential = (type, showAuthOptions = false) => {
setActiveId(CREDENTIAL_EDIT_MODAL_KEY, type);
setShowAuthSelector(CREDENTIAL_EDIT_MODAL_KEY, showAuthOptions);
setMode(CREDENTIAL_EDIT_MODAL_KEY, "new");
openModal(CREDENTIAL_EDIT_MODAL_KEY);
};
const openCommunityPackageUninstallConfirmModal = (packageName) => {
setMode(COMMUNITY_PACKAGE_CONFIRM_MODAL_KEY, COMMUNITY_PACKAGE_MANAGE_ACTIONS.UNINSTALL);
setActiveId(COMMUNITY_PACKAGE_CONFIRM_MODAL_KEY, packageName);
openModal(COMMUNITY_PACKAGE_CONFIRM_MODAL_KEY);
};
const openCommunityPackageUpdateConfirmModal = (packageName, source) => {
telemetry$1.track("User clicked to open community node update modal", {
source,
package_name: packageName
});
setMode(COMMUNITY_PACKAGE_CONFIRM_MODAL_KEY, COMMUNITY_PACKAGE_MANAGE_ACTIONS.UPDATE);
setActiveId(COMMUNITY_PACKAGE_CONFIRM_MODAL_KEY, packageName);
openModal(COMMUNITY_PACKAGE_CONFIRM_MODAL_KEY);
};
const openDeleteFolderModal = (id$1, workflowListEventBus, content) => {
setActiveId(DELETE_FOLDER_MODAL_KEY, id$1);
openModalWithData({
name: DELETE_FOLDER_MODAL_KEY,
data: {
workflowListEventBus,
content
}
});
};
const openMoveToFolderModal = (resourceType, resource, workflowListEventBus) => {
openModalWithData({
name: MOVE_FOLDER_MODAL_KEY,
data: {
resourceType,
resource,
workflowListEventBus
}
});
};
const addActiveAction = (action) => {
if (!activeActions.value.includes(action)) activeActions.value.push(action);
};
const removeActiveAction = (action) => {
const actionIndex = activeActions.value.indexOf(action);
if (actionIndex !== -1) activeActions.value.splice(actionIndex, 1);
};
const toggleSidebarMenuCollapse = () => {
const newCollapsedState = !sidebarMenuCollapsed.value;
sidebarMenuCollapsedPreference.value = newCollapsedState;
sidebarMenuCollapsed.value = newCollapsedState;
};
const setNotificationsForView = (view, notifications) => {
pendingNotificationsForViews.value[view] = notifications;
};
function resetLastInteractedWith() {
lastInteractedWithNodeConnection.value = void 0;
lastInteractedWithNodeHandle.value = null;
lastInteractedWithNodeId.value = void 0;
lastCancelledConnectionPosition.value = void 0;
}
const registerCustomTabs = (page, moduleName, tabs) => {
if (!moduleTabs.value[page]) throw new Error(`Invalid page type: ${page}`);
moduleTabs.value[page][moduleName] = tabs;
};
const registerSettingsPages = (moduleName, items) => {
registeredSettingsPages.value[moduleName] = items;
};
const setProcessingExecutionResults = (value) => {
processingExecutionResults.value = value;
};
const registerModal = (modalKey, initialState) => {
if (!modalsById.value[modalKey]) modalsById.value[modalKey] = initialState || { open: false };
};
const unregisterModal = (modalKey) => {
if (modalsById.value[modalKey]) {
if (modalsById.value[modalKey].open) closeModal(modalKey);
delete modalsById.value[modalKey];
}
};
const initializeModalsFromRegistry = () => {
getAll().forEach((modalDef, key) => {
registerModal(key, modalDef.initialState);
});
};
const unsubscribeFromModalRegistry = subscribe((modals$1) => {
modals$1.forEach((modalDef, key) => {
if (!modalsById.value[key]) registerModal(key, modalDef.initialState);
});
});
const cleanup = () => {
unsubscribeFromModalRegistry();
};
return {
appGridDimensions,
settingsSidebarItems,
appliedTheme,
contextBasedTranslationKeys,
isModalActiveById,
isReadOnlyView,
isActionActive,
activeActions,
headerHeight,
stateIsDirty,
isBlankRedirect,
activeCredentialType,
lastSelectedNode,
lastInteractedWithNodeConnection,
lastInteractedWithNodeHandle,
lastInteractedWithNodeId,
lastInteractedWithNode,
lastCancelledConnectionPosition,
nodeViewOffsetPosition,
nodeViewInitialized,
addFirstStepOnLoad,
sidebarMenuCollapsed,
sidebarMenuCollapsedPreference,
theme: computed(() => theme.value),
modalsById,
currentView,
isAnyModalOpen,
pendingNotificationsForViews,
activeModals,
isProcessingExecutionResults,
setTheme,
setModalData,
openModalWithData,
openModal,
closeModal,
openDeleteUserModal,
openExistingCredential,
openNewCredential,
openCommunityPackageUninstallConfirmModal,
openCommunityPackageUpdateConfirmModal,
addActiveAction,
removeActiveAction,
toggleSidebarMenuCollapse,
setNotificationsForView,
resetLastInteractedWith,
setProcessingExecutionResults,
openDeleteFolderModal,
openMoveToFolderModal,
moduleTabs,
registerCustomTabs,
registerSettingsPages,
registerModal,
unregisterModal,
initializeModalsFromRegistry,
cleanup
};
});
const listenForModalChanges = (opts) => {
const { store, onModalClosed, onModalOpened } = opts;
const listeningForActions = [
"openModal",
"openModalWithData",
"closeModal"
];
return store.$onAction((result) => {
const { name: name$13, after, args } = result;
after(() => {
if (!listeningForActions.includes(name$13)) return;
switch (name$13) {
case "openModal": {
const modalName = args[0];
onModalOpened?.(modalName);
break;
}
case "openModalWithData": {
const { name: modalName } = args[0] ?? {};
onModalOpened?.(modalName);
break;
}
case "closeModal": {
const modalName = args[0];
onModalClosed?.(modalName);
break;
}
}
});
});
};
const codeNodeEditorEventBus = createEventBus();
const dataPinningEventBus = createEventBus();
const globalLinkActionsEventBus = createEventBus();
const htmlEditorEventBus = createEventBus();
const importCurlEventBus = createEventBus();
const nodeViewEventBus = createEventBus();
function isDateObject$1(maybeDate) {
return maybeDate instanceof Date;
}
function isObjectOrArray(maybeObject) {
return typeof maybeObject === "object" && maybeObject !== null && !isDateObject$1(maybeObject);
}
function isObject$1(maybeObject) {
return isObjectOrArray(maybeObject) && !Array.isArray(maybeObject);
}
const searchInObject = (obj, searchString) => (Array.isArray(obj) ? obj : Object.entries(obj)).some((entry) => isObjectOrArray(entry) ? searchInObject(entry, searchString) : entry?.toString().toLowerCase().includes(searchString.toLowerCase()));
const getObjectSizeInKB = (obj) => {
if (obj === null || obj === void 0) return 0;
if (typeof obj === "object" && Object.keys(obj).length === 0 || Array.isArray(obj) && obj.length === 0) return Number((2 / 1024).toFixed(2));
try {
const str = JSON.stringify(obj);
const kb = new TextEncoder().encode(str).length / 1024;
return Number(kb.toFixed(2));
} catch (error) {
throw new Error(`Failed to calculate object size: ${error instanceof Error ? error.message : "Unknown error"}`);
}
};
function omitKey(obj, key) {
return Object.fromEntries(Object.entries(obj).filter(([k]) => k !== key));
}
var import_dateformat = /* @__PURE__ */ __toESM(require_dateformat());
var SI_SYMBOL = [
"",
"k",
"M",
"G",
"T",
"P",
"E"
];
const omit = (keyToOmit, { [keyToOmit]: _,...remainder }) => remainder;
function isJsonKeyObject(item) {
if (!isObject$1(item)) return false;
return Object.keys(item).includes("json");
}
const isEmpty = (value) => {
if (!value && value !== 0) return true;
if (Array.isArray(value)) return !value.length || value.every(isEmpty);
if (typeof value === "object") return !Object.keys(value).length || Object.values(value).every(isEmpty);
return false;
};
function abbreviateNumber(num) {
const tier = Math.log10(Math.abs(num)) / 3 | 0;
if (tier === 0) return num;
const suffix = SI_SYMBOL[tier];
const scaled = num / Math.pow(10, tier * 3);
return Number(scaled.toFixed(1)) + suffix;
}
function convertToDisplayDate(epochTime) {
return (0, import_dateformat.default)(epochTime, "yyyy-mm-dd HH:MM:ss");
}
function convertToHumanReadableDate(epochTime) {
return (0, import_dateformat.default)(epochTime, "d mmmm, yyyy @ HH:MM Z");
}
function stringSizeInBytes(input) {
if (input === void 0) return 0;
return new Blob([typeof input === "string" ? input : JSON.stringify(input)]).size;
}
function toMegaBytes(bytes, decimalPlaces = 2) {
const megabytes = bytes / 1024 / 1024;
return parseFloat(megabytes.toFixed(decimalPlaces));
}
function shorten(s, limit, keep) {
if (s.length <= limit) return s;
return `${s.slice(0, limit - keep)}...${s.slice(s.length - keep, s.length)}`;
}
const convertPath = (path) => {
const placeholder = "*___~#^#~___*";
let inBrackets = path.match(/\[(.*?)]/g) ?? [];
inBrackets = inBrackets.map((item) => item.slice(1, -1)).map((item) => {
if (item.startsWith("\"") && item.endsWith("\"")) return item.slice(1, -1);
return item;
});
const pathParts = path.replace(/\[(.*?)]/g, placeholder).split(".");
const allParts = [];
pathParts.forEach((part) => {
let index = part.indexOf(placeholder);
while (index !== -1) {
if (index === 0) {
allParts.push(inBrackets.shift() ?? "");
part = part.substr(13);
} else {
allParts.push(part.substr(0, index));
part = part.substr(index);
}
index = part.indexOf(placeholder);
}
if (part !== "") allParts.push(part);
});
return "[\"" + allParts.join("\"][\"") + "\"]";
};
const clearJsonKey = (userInput) => {
const parsedUserInput = typeof userInput === "string" ? jsonParse(userInput) : userInput;
if (!Array.isArray(parsedUserInput)) return parsedUserInput;
return parsedUserInput.map((item) => isJsonKeyObject(item) ? item.json : item);
};
const tryToParseNumber = (value) => {
return isNaN(+value) ? value : +value;
};
function isPresent(arg) {
return arg !== null && arg !== void 0;
}
function isFocusableEl(el) {
return typeof el === "object" && el !== null && "focus" in el && typeof el.focus === "function";
}
function isBlurrableEl(el) {
return typeof el === "object" && el !== null && "blur" in el && typeof el.blur === "function";
}
function isSelectableEl(el) {
return typeof el === "object" && el !== null && "select" in el && typeof el.select === "function";
}
function hasFocusOnInput(el) {
return typeof el === "object" && el !== null && "focusOnInput" in el && typeof el.focusOnInput === "function";
}
var _hoisted_1$10 = { "data-test-id": "sanitized-error-message" };
var _hoisted_2$3 = ["data-action-parameter-node"];
var NodeExecutionErrorMessage_default = /* @__PURE__ */ defineComponent({
__name: "NodeExecutionErrorMessage",
props: {
nodeName: {},
errorMessage: {}
},
setup(__props) {
const i18n$1 = useI18n();
return (_ctx, _cache) => {
const _directive_n8n_html = resolveDirective("n8n-html");
return openBlock(), createElementBlock("div", null, [
withDirectives(createBaseVNode("span", _hoisted_1$10, null, 512), [[_directive_n8n_html, _ctx.errorMessage]]),
_cache[0] || (_cache[0] = createBaseVNode("br", null, null, -1)),
createBaseVNode("a", {
"data-action": "openNodeDetail",
"data-action-parameter-node": _ctx.nodeName
}, toDisplayString(unref(i18n$1).baseText("node.executionError.openNode")), 9, _hoisted_2$3)
]);
};
}
});
/*! (c) 2020 Andrea Giammarchi */
var { parse: $parse, stringify: $stringify } = JSON;
var { keys: keys$1 } = Object;
var Primitive = String;
var primitive = "string";
var ignore = {};
var object$1 = "object";
var noop$4 = (_, value) => value;
var primitives = (value) => value instanceof Primitive ? Primitive(value) : value;
var Primitives = (_, value) => typeof value === primitive ? new Primitive(value) : value;
var revive = (input, parsed, output, $) => {
const lazy = [];
for (let ke = keys$1(output), { length } = ke, y = 0; y < length; y++) {
const k = ke[y];
const value = output[k];
if (value instanceof Primitive) {
const tmp = input[value];
if (typeof tmp === object$1 && !parsed.has(tmp)) {
parsed.add(tmp);
output[k] = ignore;
lazy.push({
k,
a: [
input,
parsed,
tmp,
$
]
});
} else output[k] = $.call(output, k, tmp);
} else if (output[k] !== ignore) output[k] = $.call(output, k, value);
}
for (let { length } = lazy, i$1 = 0; i$1 < length; i$1++) {
const { k, a } = lazy[i$1];
output[k] = $.call(output, k, revive.apply(null, a));
}
return output;
};
const parse = (text, reviver) => {
const input = $parse(text, Primitives).map(primitives);
const value = input[0];
const $ = reviver || noop$4;
const tmp = typeof value === object$1 && value ? revive(input, /* @__PURE__ */ new Set(), value, $) : value;
return $.call({ "": tmp }, "", tmp);
};
async function getCredentialTypes(baseUrl) {
const { data } = await axios_default.get(baseUrl + "types/credentials.json", { withCredentials: true });
return data;
}
async function getCredentialsNewName(context, name$13) {
return await makeRestApiRequest(context, "GET", "/credentials/new", name$13 ? { name: name$13 } : {});
}
async function getAllCredentials(context, filter$1, includeScopes, onlySharedWithMe, includeGlobal) {
return await makeRestApiRequest(context, "GET", "/credentials", {
...includeScopes ? { includeScopes } : {},
includeData: true,
...filter$1 ? { filter: filter$1 } : {},
...onlySharedWithMe ? { onlySharedWithMe } : {},
...typeof includeGlobal === "boolean" ? { includeGlobal } : {}
});
}
async function getAllCredentialsForWorkflow(context, options) {
return await makeRestApiRequest(context, "GET", "/credentials/for-workflow", { ...options });
}
async function createNewCredential(context, payload) {
return await makeRestApiRequest(context, "POST", "/credentials", payload);
}
async function deleteCredential(context, id$1) {
return await makeRestApiRequest(context, "DELETE", `/credentials/${id$1}`);
}
async function updateCredential(context, id$1, data) {
return await makeRestApiRequest(context, "PATCH", `/credentials/${id$1}`, data);
}
async function getCredentialData(context, id$1) {
return await makeRestApiRequest(context, "GET", `/credentials/${id$1}`, { includeData: true });
}
async function oAuth1CredentialAuthorize(context, data) {
return await makeRestApiRequest(context, "GET", "/oauth1-credential/auth", data);
}
async function oAuth2CredentialAuthorize(context, data) {
return await makeRestApiRequest(context, "GET", "/oauth2-credential/auth", data);
}
async function testCredential(context, data) {
return await makeRestApiRequest(context, "POST", "/credentials/test", data);
}
async function setCredentialSharedWith(context, id$1, data) {
return await makeRestApiRequest(context, "PUT", `/credentials/${id$1}/share`, data);
}
async function moveCredentialToProject(context, id$1, destinationProjectId) {
return await makeRestApiRequest(context, "PUT", `/credentials/${id$1}/transfer`, { destinationProjectId });
}
var CRED_KEYWORDS_TO_FILTER = [
"API",
"OAuth1",
"OAuth2"
];
var NODE_KEYWORDS_TO_FILTER = ["Trigger"];
var RESOURCE_MAPPER_FIELD_NAME_REGEX = /value\["(.+?)"\]/s;
function getAppNameFromCredType(name$13) {
return name$13.split(" ").filter((word) => !CRED_KEYWORDS_TO_FILTER.includes(word)).join(" ");
}
function getAppNameFromNodeName(name$13) {
return name$13.split(" ").filter((word) => !NODE_KEYWORDS_TO_FILTER.includes(word)).join(" ");
}
function getTriggerNodeServiceName(nodeType) {
return nodeType.displayName.replace(/ trigger/i, "");
}
function getActivatableTriggerNodes(nodes$13) {
return nodes$13.filter((node) => !node.disabled && !NON_ACTIVATABLE_TRIGGER_NODE_TYPES.includes(node.type));
}
function filterTemplateNodes(nodes$13) {
const notCoreNodes = nodes$13.filter((node) => {
return !(node.categories || []).some((category) => category.name === CORE_NODES_CATEGORY);
});
return (notCoreNodes.length > 0 ? notCoreNodes : nodes$13).filter((elem) => !TEMPLATES_NODES_FILTER.includes(elem.name));
}
function hasExpressionMapping(value) {
return typeof value === "string" && !!MAPPING_PARAMS.find((param) => value.includes(param));
}
function isValueExpression(parameter, paramValue) {
if (parameter.noDataExpression === true) return false;
if (typeof paramValue === "string" && paramValue.charAt(0) === "=") return true;
if (isResourceLocatorValue(paramValue) && paramValue.value && paramValue.value.toString().charAt(0) === "=") return true;
return false;
}
const executionDataToJson = (inputData) => inputData.reduce((acc, item) => {
if (isJsonKeyObject(item)) acc.push(item.json);
return acc;
}, []);
const hasOnlyListMode = (parameter) => {
return parameter.modes !== void 0 && parameter.modes.length === 1 && parameter.modes[0].name === "list";
};
const isRequiredCredential = (nodeType, credential) => {
if (!credential.displayOptions?.show) return true;
const mainAuthField = getMainAuthField(nodeType);
if (mainAuthField) return mainAuthField.name in credential.displayOptions.show;
return false;
};
const getMainAuthField = (nodeType) => {
if (!nodeType) return null;
const credentialDependencies = getNodeAuthFields(nodeType);
const mainAuthFiled = credentialDependencies.find((prop) => prop.name === "authentication" && !prop.options?.find((option) => "value" in option && option.value === "none")) ?? null ?? findAlternativeAuthField(nodeType, credentialDependencies);
const isFieldRequired = mainAuthFiled ? isNodeParameterRequired(nodeType, mainAuthFiled) : false;
return mainAuthFiled && isFieldRequired ? mainAuthFiled : null;
};
var findAlternativeAuthField = (nodeType, fields) => {
const dependentAuthFieldValues = {};
nodeType.credentials?.forEach((cred) => {
if (cred.displayOptions?.show) for (const fieldName in cred.displayOptions.show) dependentAuthFieldValues[fieldName] = (dependentAuthFieldValues[fieldName] || []).concat((cred.displayOptions.show[fieldName] ?? []).map((val) => val ? val.toString() : ""));
});
return fields.find((field) => {
let required = true;
field.options?.forEach((option) => {
if ("value" in option && typeof option.value === "string" && !dependentAuthFieldValues[field.name].includes(option.value)) required = false;
});
return required;
}) || null;
};
const getNodeAuthOptions = (nodeType, nodeVersion) => {
if (!nodeType) return [];
const recommendedSuffix = i18n.baseText("credentialEdit.credentialConfig.recommendedAuthTypeSuffix");
let options = [];
const authProp = getMainAuthField(nodeType);
getNodeAuthFields(nodeType, nodeVersion).filter((prop) => prop.name === authProp?.name).forEach((field) => {
if (field.options) options = options.concat(field.options.map((option) => {
const optionValue = "value" in option ? `${option.value}` : "";
let hasOverrides = false;
const cred = getNodeCredentialForSelectedAuthType(nodeType, optionValue);
if (cred) hasOverrides = useCredentialsStore().getCredentialTypeByName(cred.name)?.__overwrittenProperties !== void 0;
return {
name: hasOverrides && !option.name.endsWith(recommendedSuffix) ? `${option.name} ${recommendedSuffix}` : option.name,
value: optionValue,
displayOptions: field.displayOptions
};
}) || []);
});
options.forEach((item, i$1) => {
if (item.name.includes(recommendedSuffix)) {
options.splice(i$1, 1);
options.unshift(item);
}
});
return options;
};
const getAllNodeCredentialForAuthType = (nodeType, authType) => {
if (nodeType) return nodeType.credentials?.filter((cred) => cred.displayOptions?.show && authType in (cred.displayOptions.show || {})) ?? [];
return [];
};
const getNodeCredentialForSelectedAuthType = (nodeType, authType) => {
const authField = getMainAuthField(nodeType);
const authFieldName = authField ? authField.name : "";
return nodeType.credentials?.find((cred) => cred.displayOptions?.show?.[authFieldName]?.includes(authType)) || null;
};
const getAuthTypeForNodeCredential = (nodeType, credentialType) => {
if (nodeType && credentialType) {
const authField = getMainAuthField(nodeType);
const authFieldName = authField ? authField.name : "";
return getNodeAuthOptions(nodeType).find((option) => credentialType.displayOptions?.show?.[authFieldName]?.includes(option.value)) || null;
}
return null;
};
const isAuthRelatedParameter = (authFields, parameter) => {
let isRelated = false;
authFields.forEach((prop) => {
if (prop.displayOptions?.show && parameter.name in prop.displayOptions.show) {
isRelated = true;
return;
}
});
return isRelated;
};
const getNodeAuthFields = (nodeType, nodeVersion) => {
const authFields = [];
if (nodeType?.credentials && nodeType.credentials.length > 0) nodeType.credentials.forEach((cred) => {
if (cred.displayOptions?.show) Object.keys(cred.displayOptions.show).forEach((option) => {
const nodeFieldsForName = nodeType.properties.filter((prop) => prop.name === option);
if (nodeFieldsForName) nodeFieldsForName.forEach((nodeField) => {
if (!authFields.includes(nodeField) && isNodeFieldMatchingNodeVersion(nodeField, nodeVersion)) authFields.push(nodeField);
});
});
});
return authFields;
};
const isNodeFieldMatchingNodeVersion = (nodeField, nodeVersion) => {
if (nodeVersion && nodeField.displayOptions?.show?.["@version"]) return nodeField.displayOptions.show["@version"]?.includes(nodeVersion);
return true;
};
const updateNodeAuthType = (workflowState, node, type) => {
if (!node) return;
const nodeType = useNodeTypesStore().getNodeType(node.type, node.typeVersion);
if (nodeType) {
const nodeAuthField = getMainAuthField(nodeType);
if (nodeAuthField) {
const updateInformation = {
name: node.name,
properties: { parameters: {
...node.parameters,
[nodeAuthField.name]: type
} }
};
workflowState.updateNodeProperties(updateInformation);
}
}
};
const isNodeParameterRequired = (nodeType, parameter) => {
if (!parameter.displayOptions?.show) return true;
Object.keys(parameter.displayOptions.show).forEach((name$13) => {
const relatedField = nodeType.properties.find((prop) => {
prop.name;
});
if (relatedField && !isNodeParameterRequired(nodeType, relatedField)) return false;
else return true;
});
return true;
};
const parseResourceMapperFieldName = (fullName) => {
const match = fullName.match(RESOURCE_MAPPER_FIELD_NAME_REGEX);
return match ? match.pop() : fullName;
};
const fieldCannotBeDeleted = (field, showMatchingColumnsSelector, resourceMapperMode = "", matchingFields = []) => {
const fieldIdentifier = "id" in field ? field.id : field.name;
return resourceMapperMode === "add" && field.required === true || isMatchingField(fieldIdentifier, matchingFields, showMatchingColumnsSelector);
};
const isResourceMapperFieldListStale = (oldFields, newFields) => {
if (oldFields.length !== newFields.length) return true;
const newFieldsMap = new Map(newFields.map((field) => [field.id, field]));
for (const oldField of oldFields) {
const newField = newFieldsMap.get(oldField.id);
if (!newField) return true;
if (oldField.displayName !== newField.displayName || oldField.required !== newField.required || oldField.defaultMatch !== newField.defaultMatch || oldField.display !== newField.display || oldField.canBeUsedToMatch !== newField.canBeUsedToMatch || oldField.type !== newField.type) return true;
}
return false;
};
const isMatchingField = (field, matchingFields, showMatchingColumnsSelector) => {
const fieldName = parseResourceMapperFieldName(field);
if (fieldName) return showMatchingColumnsSelector && (matchingFields || []).includes(fieldName);
return false;
};
const getThemedValue = (value, theme = "light") => {
if (!value) return null;
if (typeof value === "string") return value;
return value[theme];
};
const splitName = (projectName = "") => {
const [, name$13, email] = projectName.match(/^(.*?)(?:\s*<([^>]+)>)?$/) ?? [];
return {
name: name$13.trim() || void 0,
email
};
};
const MAX_NAME_LENGTH = 25;
const getTruncatedProjectName = (projectName) => {
const { name: name$13, email } = splitName(projectName ?? "");
return truncate(name$13 ?? email ?? "", 25);
};
let ResourceType = /* @__PURE__ */ function(ResourceType$1) {
ResourceType$1["Credential"] = "credential";
ResourceType$1["Workflow"] = "workflow";
return ResourceType$1;
}({});
let COMMANDS = /* @__PURE__ */ function(COMMANDS$1) {
COMMANDS$1["MOVE_NODE"] = "moveNode";
COMMANDS$1["ADD_NODE"] = "addNode";
COMMANDS$1["REMOVE_NODE"] = "removeNode";
COMMANDS$1["ADD_CONNECTION"] = "addConnection";
COMMANDS$1["REMOVE_CONNECTION"] = "removeConnection";
COMMANDS$1["ENABLE_NODE_TOGGLE"] = "enableNodeToggle";
COMMANDS$1["RENAME_NODE"] = "renameNode";
COMMANDS$1["REPLACE_NODE_PARAMETERS"] = "replaceNodeParameters";
return COMMANDS$1;
}({});
var CANVAS_ACTION_TIMEOUT = 10;
const historyBus = createEventBus();
var Undoable = class {};
var Command = class extends Undoable {
name;
timestamp;
constructor(name$13, timestamp) {
super();
this.name = name$13;
this.timestamp = timestamp;
}
getTimestamp() {
return this.timestamp;
}
};
var BulkCommand = class extends Undoable {
commands;
constructor(commands) {
super();
this.commands = commands;
}
getTimestamp() {
return Math.max(0, ...this.commands.map((command) => command.timestamp));
}
};
var MoveNodeCommand = class MoveNodeCommand extends Command {
nodeName;
oldPosition;
newPosition;
constructor(nodeName, oldPosition, newPosition, timestamp) {
super(COMMANDS.MOVE_NODE, timestamp);
this.nodeName = nodeName;
this.newPosition = newPosition;
this.oldPosition = oldPosition;
}
getReverseCommand(timestamp) {
return new MoveNodeCommand(this.nodeName, this.newPosition, this.oldPosition, timestamp);
}
isEqualTo(anotherCommand) {
return anotherCommand instanceof MoveNodeCommand && anotherCommand.nodeName === this.nodeName && anotherCommand.oldPosition[0] === this.oldPosition[0] && anotherCommand.oldPosition[1] === this.oldPosition[1] && anotherCommand.newPosition[0] === this.newPosition[0] && anotherCommand.newPosition[1] === this.newPosition[1];
}
async revert() {
return await new Promise((resolve) => {
historyBus.emit("nodeMove", {
nodeName: this.nodeName,
position: this.oldPosition
});
resolve();
});
}
};
var AddNodeCommand = class AddNodeCommand extends Command {
node;
constructor(node, timestamp) {
super(COMMANDS.ADD_NODE, timestamp);
this.node = node;
}
getReverseCommand(timestamp) {
return new RemoveNodeCommand(this.node, timestamp);
}
isEqualTo(anotherCommand) {
return anotherCommand instanceof AddNodeCommand && anotherCommand.node.name === this.node.name;
}
async revert() {
return await new Promise((resolve) => {
historyBus.emit("revertAddNode", { node: this.node });
resolve();
});
}
};
var RemoveNodeCommand = class extends Command {
node;
constructor(node, timestamp) {
super(COMMANDS.REMOVE_NODE, timestamp);
this.node = node;
}
getReverseCommand(timestamp) {
return new AddNodeCommand(this.node, timestamp);
}
isEqualTo(anotherCommand) {
return anotherCommand instanceof AddNodeCommand && anotherCommand.node.name === this.node.name;
}
async revert() {
return await new Promise((resolve) => {
historyBus.emit("revertRemoveNode", { node: this.node });
resolve();
});
}
};
var AddConnectionCommand = class AddConnectionCommand extends Command {
connectionData;
constructor(connectionData, timestamp) {
super(COMMANDS.ADD_CONNECTION, timestamp);
this.connectionData = connectionData;
}
getReverseCommand(timestamp) {
return new RemoveConnectionCommand(this.connectionData, timestamp);
}
isEqualTo(anotherCommand) {
return anotherCommand instanceof AddConnectionCommand && anotherCommand.connectionData[0].node === this.connectionData[0].node && anotherCommand.connectionData[1].node === this.connectionData[1].node && anotherCommand.connectionData[0].index === this.connectionData[0].index && anotherCommand.connectionData[1].index === this.connectionData[1].index;
}
async revert() {
return await new Promise((resolve) => {
historyBus.emit("revertAddConnection", { connection: this.connectionData });
resolve();
});
}
};
var RemoveConnectionCommand = class RemoveConnectionCommand extends Command {
connectionData;
constructor(connectionData, timestamp) {
super(COMMANDS.REMOVE_CONNECTION, timestamp);
this.connectionData = connectionData;
}
getReverseCommand(timestamp) {
return new AddConnectionCommand(this.connectionData, timestamp);
}
isEqualTo(anotherCommand) {
return anotherCommand instanceof RemoveConnectionCommand && anotherCommand.connectionData[0].node === this.connectionData[0].node && anotherCommand.connectionData[1].node === this.connectionData[1].node && anotherCommand.connectionData[0].index === this.connectionData[0].index && anotherCommand.connectionData[1].index === this.connectionData[1].index;
}
async revert() {
return await new Promise((resolve) => {
setTimeout(() => {
historyBus.emit("revertRemoveConnection", { connection: this.connectionData });
resolve();
}, CANVAS_ACTION_TIMEOUT);
});
}
};
var EnableNodeToggleCommand = class EnableNodeToggleCommand extends Command {
nodeName;
oldState;
newState;
constructor(nodeName, oldState, newState, timestamp) {
super(COMMANDS.ENABLE_NODE_TOGGLE, timestamp);
this.nodeName = nodeName;
this.newState = newState;
this.oldState = oldState;
}
getReverseCommand(timestamp) {
return new EnableNodeToggleCommand(this.nodeName, this.newState, this.oldState, timestamp);
}
isEqualTo(anotherCommand) {
return anotherCommand instanceof EnableNodeToggleCommand && anotherCommand.nodeName === this.nodeName;
}
async revert() {
return await new Promise((resolve) => {
historyBus.emit("enableNodeToggle", {
nodeName: this.nodeName,
isDisabled: this.oldState
});
resolve();
});
}
};
var RenameNodeCommand = class RenameNodeCommand extends Command {
currentName;
newName;
constructor(currentName, newName, timestamp) {
super(COMMANDS.RENAME_NODE, timestamp);
this.currentName = currentName;
this.newName = newName;
}
getReverseCommand(timestamp) {
return new RenameNodeCommand(this.newName, this.currentName, timestamp);
}
isEqualTo(anotherCommand) {
return anotherCommand instanceof RenameNodeCommand && anotherCommand.currentName === this.currentName && anotherCommand.newName === this.newName;
}
async revert() {
return await new Promise((resolve) => {
historyBus.emit("revertRenameNode", {
currentName: this.currentName,
newName: this.newName
});
resolve();
});
}
};
var ReplaceNodeParametersCommand = class ReplaceNodeParametersCommand extends Command {
constructor(nodeId, currentParameters, newParameters, timestamp) {
super(COMMANDS.REPLACE_NODE_PARAMETERS, timestamp);
this.nodeId = nodeId;
this.currentParameters = currentParameters;
this.newParameters = newParameters;
}
getReverseCommand(timestamp) {
return new ReplaceNodeParametersCommand(this.nodeId, this.newParameters, this.currentParameters, timestamp);
}
isEqualTo(anotherCommand) {
return anotherCommand instanceof ReplaceNodeParametersCommand && anotherCommand.nodeId === this.nodeId && anotherCommand.currentParameters === this.currentParameters && anotherCommand.newParameters === this.newParameters;
}
async revert() {
return await new Promise((resolve) => {
historyBus.emit("revertReplaceNodeParameters", {
nodeId: this.nodeId,
currentProperties: this.currentParameters,
newProperties: this.newParameters
});
resolve();
});
}
};
var STACK_LIMIT = 100;
const useHistoryStore = defineStore(STORES.HISTORY, {
state: () => ({
undoStack: [],
redoStack: [],
currentBulkAction: null,
bulkInProgress: false
}),
actions: {
popUndoableToUndo() {
if (this.undoStack.length > 0) return this.undoStack.pop();
},
pushCommandToUndo(undoable, clearRedo = true) {
if (!this.bulkInProgress) {
if (this.currentBulkAction) {
if (!(this.currentBulkAction.commands.find((c) => c.isEqualTo(undoable)) !== void 0)) this.currentBulkAction.commands.push(undoable);
} else this.undoStack.push(undoable);
this.checkUndoStackLimit();
if (clearRedo) this.clearRedoStack();
}
},
pushBulkCommandToUndo(undoable, clearRedo = true) {
this.undoStack.push(undoable);
this.checkUndoStackLimit();
if (clearRedo) this.clearRedoStack();
},
checkUndoStackLimit() {
if (this.undoStack.length > STACK_LIMIT) this.undoStack.shift();
},
checkRedoStackLimit() {
if (this.redoStack.length > STACK_LIMIT) this.redoStack.shift();
},
clearUndoStack() {
this.undoStack = [];
},
clearRedoStack() {
this.redoStack = [];
},
reset() {
this.clearRedoStack();
this.clearUndoStack();
},
popUndoableToRedo() {
if (this.redoStack.length > 0) return this.redoStack.pop();
},
pushUndoableToRedo(undoable) {
this.redoStack.push(undoable);
this.checkRedoStackLimit();
},
startRecordingUndo() {
this.currentBulkAction = new BulkCommand([]);
},
stopRecordingUndo() {
if (this.currentBulkAction && this.currentBulkAction.commands.length > 0) {
this.undoStack.push(this.currentBulkAction);
this.checkUndoStackLimit();
}
this.currentBulkAction = null;
}
}
});
let CanvasConnectionMode = /* @__PURE__ */ function(CanvasConnectionMode$1) {
CanvasConnectionMode$1["Input"] = "inputs";
CanvasConnectionMode$1["Output"] = "outputs";
return CanvasConnectionMode$1;
}({});
const canvasConnectionModes = [CanvasConnectionMode.Input, CanvasConnectionMode.Output];
let CanvasNodeRenderType = /* @__PURE__ */ function(CanvasNodeRenderType$1) {
CanvasNodeRenderType$1["Default"] = "default";
CanvasNodeRenderType$1["StickyNote"] = "n8n-nodes-base.stickyNote";
CanvasNodeRenderType$1["AddNodes"] = "n8n-nodes-internal.addNodes";
CanvasNodeRenderType$1["ChoicePrompt"] = "n8n-nodes-internal.choicePrompt";
return CanvasNodeRenderType$1;
}({});
const CanvasNodeDirtiness = {
PARAMETERS_UPDATED: "parameters-updated",
INCOMING_CONNECTIONS_UPDATED: "incoming-connections-updated",
PINNED_DATA_UPDATED: "pinned-data-updated",
UPSTREAM_DIRTY: "upstream-dirty"
};
function isNotNull(value) {
return value !== null;
}
function isValidCredentialResponse(value) {
return typeof value === "object" && value !== null && "id" in value;
}
const isObj = (obj) => !!obj && Object.getPrototypeOf(obj) === Object.prototype;
function isString(value) {
return typeof value === "string";
}
const isCredentialModalState = (value) => {
return typeof value === "object" && value !== null && "showAuthSelector" in value;
};
const isResourceMapperValue = (value) => {
return [
"string",
"number",
"boolean"
].includes(typeof value);
};
function isDateObject(date$1) {
return !!date$1 && Object.prototype.toString.call(date$1) === "[object Date]" && !isNaN(date$1);
}
function isValidNodeConnectionType(connectionType) {
return nodeConnectionTypes.includes(connectionType);
}
function isValidCanvasConnectionMode(mode) {
return canvasConnectionModes.includes(mode);
}
function isVueFlowConnection(connection) {
return "source" in connection && "target" in connection && "sourceHandle" in connection && "targetHandle" in connection;
}
function isTriggerPanelObject(triggerPanel) {
return triggerPanel !== void 0 && typeof triggerPanel === "object" && triggerPanel !== null;
}
function isFullExecutionResponse(execution) {
return !!execution && "status" in execution;
}
function isRouteLocationRaw(value) {
return typeof value === "string" || typeof value === "object" && value !== null && ("name" in value || "path" in value);
}
function isComponentPublicInstance(value) {
return value !== null && typeof value === "object" && "$props" in value;
}
function isWorkflowResource(value) {
return value.resourceType === "workflow";
}
function isFolderResource(value) {
return value.resourceType === "folder";
}
function isVariableResource(value) {
return value.resourceType === "variable";
}
function isCredentialsResource(value) {
return value.resourceType === "credential";
}
function isSharedResource(value) {
return isWorkflowResource(value) || isFolderResource(value) || isCredentialsResource(value);
}
function isResourceSortableByDate(value) {
return isWorkflowResource(value) || isFolderResource(value) || isCredentialsResource(value);
}
function isBaseTextKey(key) {
const i18n$1 = useI18n();
try {
i18n$1.baseText(key);
return true;
} catch {
return false;
}
}
function isProjectRole(role) {
return role.startsWith("project:");
}
const isWorkflowListItem = (resource) => "resource" in resource ? resource.resource !== "folder" : true;
function isPersonalizationSurveyV2OrLater(data) {
return "version" in data;
}
function getPersonalizedNodeTypes(answers) {
if (!answers) return [];
if (isPersonalizationSurveyV2OrLater(answers)) return getPersonalizationSurveyV2OrLater(answers);
return getPersonalizationSurveyV1(answers);
}
function getPersonalizationSurveyV2OrLater(answers) {
let nodeTypes = [];
const { version,...data } = answers;
if (Object.keys(data).length === 0) return [];
const companySize = answers[COMPANY_SIZE_KEY];
const companyType = answers[COMPANY_TYPE_KEY];
const automationGoal = "automationGoal" in answers ? answers[AUTOMATION_GOAL_KEY] : void 0;
let codingSkill = null;
if ("codingSkill" in answers && answers["codingSkill"]) {
codingSkill = parseInt(answers[CODING_SKILL_KEY], 10);
codingSkill = isNaN(codingSkill) ? 0 : codingSkill;
}
if (companyType === "ecommerce") nodeTypes = nodeTypes.concat(WOOCOMMERCE_TRIGGER_NODE_TYPE);
else if (companyType === "msp") nodeTypes = nodeTypes.concat(JIRA_TRIGGER_NODE_TYPE);
else if ((companyType === "personal" || automationGoal === "other" || automationGoal === "not-sure-yet") && codingSkill !== null && codingSkill >= 4) nodeTypes = nodeTypes.concat(WEBHOOK_NODE_TYPE);
else if ((companyType === "personal" || automationGoal === "other" || automationGoal === "not-sure-yet") && codingSkill !== null && codingSkill < 3) nodeTypes = nodeTypes.concat(SCHEDULE_TRIGGER_NODE_TYPE);
else if (automationGoal === "customer-integrations") nodeTypes = nodeTypes.concat(WEBHOOK_NODE_TYPE);
else if (automationGoal === "customer-support" || automationGoal === "finance-accounting") nodeTypes = nodeTypes.concat(ZENDESK_TRIGGER_NODE_TYPE);
else if (automationGoal === "sales-marketing") nodeTypes = nodeTypes.concat(HUBSPOT_TRIGGER_NODE_TYPE);
else if (automationGoal === "hr") nodeTypes = nodeTypes.concat(WORKABLE_TRIGGER_NODE_TYPE);
else if (automationGoal === "operations") nodeTypes = nodeTypes.concat(SCHEDULE_TRIGGER_NODE_TYPE);
else if (automationGoal === "product") nodeTypes = nodeTypes.concat(NOTION_TRIGGER_NODE_TYPE);
else if (automationGoal === "security") nodeTypes = nodeTypes.concat(THE_HIVE_TRIGGER_NODE_TYPE);
else nodeTypes = nodeTypes.concat(WEBHOOK_NODE_TYPE);
if (codingSkill !== null && codingSkill >= 4) nodeTypes = nodeTypes.concat(CODE_NODE_TYPE);
else nodeTypes = nodeTypes.concat(ITEM_LISTS_NODE_TYPE);
if (codingSkill !== null && codingSkill < 3) nodeTypes = nodeTypes.concat(IF_NODE_TYPE);
else nodeTypes = nodeTypes.concat(SWITCH_NODE_TYPE);
if (companySize === "500-999" || companySize === "1000+") switch (automationGoal) {
case CUSTOMER_INTEGRATIONS_GOAL:
nodeTypes = nodeTypes.concat(HTTP_REQUEST_NODE_TYPE);
break;
case CUSTOMER_SUPPORT_GOAL:
nodeTypes = nodeTypes.concat(ZENDESK_NODE_TYPE);
break;
case SALES_MARKETING_GOAL:
nodeTypes = nodeTypes.concat(SALESFORCE_NODE_TYPE);
break;
case "hr":
nodeTypes = nodeTypes.concat(SERVICENOW_NODE_TYPE);
break;
case PRODUCT_GOAL:
nodeTypes = nodeTypes.concat(JIRA_NODE_TYPE);
break;
case FINANCE_ACCOUNTING_GOAL:
nodeTypes = nodeTypes.concat(SPREADSHEET_FILE_NODE_TYPE);
break;
case SECURITY_GOAL:
nodeTypes = nodeTypes.concat(ELASTIC_SECURITY_NODE_TYPE);
break;
default: nodeTypes = nodeTypes.concat(SLACK_NODE_TYPE);
}
else switch (automationGoal) {
case CUSTOMER_INTEGRATIONS_GOAL:
nodeTypes = nodeTypes.concat(HTTP_REQUEST_NODE_TYPE);
break;
case CUSTOMER_SUPPORT_GOAL:
nodeTypes = nodeTypes.concat(ZENDESK_NODE_TYPE);
break;
case FINANCE_ACCOUNTING_GOAL:
nodeTypes = nodeTypes.concat(QUICKBOOKS_NODE_TYPE);
break;
case "hr":
nodeTypes = nodeTypes.concat(BAMBOO_HR_NODE_TYPE);
break;
case PRODUCT_GOAL:
nodeTypes = nodeTypes.concat(JIRA_NODE_TYPE);
break;
case SALES_MARKETING_GOAL:
nodeTypes = nodeTypes.concat(GOOGLE_SHEETS_NODE_TYPE);
break;
case SECURITY_GOAL:
nodeTypes = nodeTypes.concat(ELASTIC_SECURITY_NODE_TYPE);
break;
default: nodeTypes = nodeTypes.concat(SLACK_NODE_TYPE);
}
nodeTypes = nodeTypes.concat(SET_NODE_TYPE);
return nodeTypes;
}
function getPersonalizationSurveyV1(answers) {
const companySize = answers[COMPANY_SIZE_KEY];
const workArea = answers[WORK_AREA_KEY];
function isWorkAreaAnswer(name$13) {
if (Array.isArray(workArea)) return workArea.includes(name$13);
else return workArea === name$13;
}
const workAreaIsEmpty = !workArea || workArea.length === 0;
if (companySize === null && workAreaIsEmpty && answers["codingSkill"] === null) return [];
let codingSkill = null;
if (answers["codingSkill"]) {
codingSkill = parseInt(answers[CODING_SKILL_KEY], 10);
codingSkill = isNaN(codingSkill) ? 0 : codingSkill;
}
let nodeTypes = [];
if (isWorkAreaAnswer("IT-Engineering")) nodeTypes = nodeTypes.concat(WEBHOOK_NODE_TYPE);
else nodeTypes = nodeTypes.concat(SCHEDULE_TRIGGER_NODE_TYPE);
if (codingSkill !== null && codingSkill >= 4) nodeTypes = nodeTypes.concat(CODE_NODE_TYPE);
else nodeTypes = nodeTypes.concat(ITEM_LISTS_NODE_TYPE);
if (codingSkill !== null && codingSkill < 3) nodeTypes = nodeTypes.concat(IF_NODE_TYPE);
else nodeTypes = nodeTypes.concat(SWITCH_NODE_TYPE);
if (companySize === "500-999" || companySize === "1000+") if (isWorkAreaAnswer("sales-businessDevelopment")) nodeTypes = nodeTypes.concat(SALESFORCE_NODE_TYPE);
else if (isWorkAreaAnswer("security")) nodeTypes = nodeTypes.concat([ELASTIC_SECURITY_NODE_TYPE, HTTP_REQUEST_NODE_TYPE]);
else if (isWorkAreaAnswer("product")) nodeTypes = nodeTypes.concat([JIRA_TRIGGER_NODE_TYPE, SEGMENT_NODE_TYPE]);
else if (isWorkAreaAnswer("IT-Engineering")) nodeTypes = nodeTypes.concat([GITHUB_TRIGGER_NODE_TYPE, HTTP_REQUEST_NODE_TYPE]);
else nodeTypes = nodeTypes.concat([MICROSOFT_EXCEL_NODE_TYPE, MICROSOFT_TEAMS_NODE_TYPE]);
else if (isWorkAreaAnswer("sales-businessDevelopment")) nodeTypes = nodeTypes.concat(CLEARBIT_NODE_TYPE);
else if (isWorkAreaAnswer("security")) nodeTypes = nodeTypes.concat([PAGERDUTY_NODE_TYPE, HTTP_REQUEST_NODE_TYPE]);
else if (isWorkAreaAnswer("product")) nodeTypes = nodeTypes.concat([JIRA_TRIGGER_NODE_TYPE, CALENDLY_TRIGGER_NODE_TYPE]);
else if (isWorkAreaAnswer("IT-Engineering")) nodeTypes = nodeTypes.concat([EXECUTE_COMMAND_NODE_TYPE, HTTP_REQUEST_NODE_TYPE]);
else if (isWorkAreaAnswer("finance")) nodeTypes = nodeTypes.concat([
XERO_NODE_TYPE,
QUICKBOOKS_NODE_TYPE,
SPREADSHEET_FILE_NODE_TYPE
]);
else nodeTypes = nodeTypes.concat([EMAIL_SEND_NODE_TYPE, SLACK_NODE_TYPE]);
nodeTypes = nodeTypes.concat(SET_NODE_TYPE);
return nodeTypes;
}
async function inviteUsers(context, params) {
return await makeRestApiRequest(context, "POST", "/invitations", params);
}
async function acceptInvitation(context, params) {
const { inviteeId,...props } = params;
return await makeRestApiRequest(context, "POST", `/invitations/${params.inviteeId}/accept`, props);
}
var N8N_API_BASE_URL = "https://api.n8n.io/api";
var CONTACT_EMAIL_SUBMISSION_ENDPOINT = "/accounts/onboarding";
async function submitEmailOnSignup(instanceId, currentUser, email, agree) {
return await post(N8N_API_BASE_URL, CONTACT_EMAIL_SUBMISSION_ENDPOINT, {
instance_id: instanceId,
user_id: `${instanceId}#${currentUser.id}`,
email,
agree,
agree_updates: true
});
}
var _isPendingUser = (user) => !!user?.isPending;
var _isInstanceOwner = (user) => user?.role === ROLE.Owner;
var _isDefaultUser = (user) => _isInstanceOwner(user) && _isPendingUser(user);
var _isAdmin = (user) => user?.role === ROLE.Admin;
const useUsersStore = defineStore(STORES.USERS, () => {
const initialized = ref(false);
const currentUserId = ref(null);
const usersById = ref({});
const userQuota = ref(-1);
const loginHooks = ref([]);
const logoutHooks = ref([]);
const uiStore = useUIStore();
const rootStore = useRootStore();
const settingsStore = useSettingsStore();
const allUsers = computed(() => Object.values(usersById.value));
const currentUser = computed(() => currentUserId.value ? usersById.value[currentUserId.value] : null);
const userActivated = computed(() => Boolean(currentUser.value?.settings?.userActivated));
const isDefaultUser$1 = computed(() => _isDefaultUser(currentUser.value));
const isInstanceOwner$1 = computed(() => _isInstanceOwner(currentUser.value));
const isAdmin = computed(() => _isAdmin(currentUser.value));
const mfaEnabled = computed(() => currentUser.value?.mfaEnabled ?? false);
const globalRoleName = computed(() => currentUser.value?.role ?? "default");
const userClaimedAiCredits = computed(() => currentUser.value?.settings?.userClaimedAiCredits);
const isEasyAIWorkflowOnboardingDone = computed(() => Boolean(currentUser.value?.settings?.easyAIWorkflowOnboarded));
const canUserUpdateVersion = computed(() => {
return isInstanceOwner$1.value;
});
const setEasyAIWorkflowOnboardingDone = () => {
if (currentUser.value?.settings) currentUser.value.settings.easyAIWorkflowOnboarded = true;
};
const isCalloutDismissed = (callout) => Boolean(currentUser.value?.settings?.dismissedCallouts?.[callout]);
const setCalloutDismissed = (callout) => {
if (currentUser.value?.settings) {
if (!currentUser.value?.settings?.dismissedCallouts) currentUser.value.settings.dismissedCallouts = {};
currentUser.value.settings.dismissedCallouts[callout] = true;
}
};
const personalizedNodeTypes = computed(() => {
const user = currentUser.value;
if (!user) return [];
const answers = user.personalizationAnswers;
if (!answers) return [];
return getPersonalizedNodeTypes(answers);
});
const usersLimitNotReached = computed(() => userQuota.value === -1 || userQuota.value > allUsers.value.length);
const addUsers = (newUsers) => {
newUsers.forEach((userResponse) => {
const updatedUser = {
...usersById.value[userResponse.id] || {},
...userResponse
};
const user = {
...updatedUser,
fullName: userResponse.firstName ? `${updatedUser.firstName} ${updatedUser.lastName || ""}` : void 0,
isDefaultUser: _isDefaultUser(updatedUser),
isPendingUser: _isPendingUser(updatedUser)
};
usersById.value = {
...usersById.value,
[user.id]: user
};
});
};
const setCurrentUser = async (user) => {
addUsers([user]);
currentUserId.value = user.id;
for (const hook of loginHooks.value) try {
await hook(user);
} catch (error) {
console.error("Error executing login hook:", error);
}
};
const loginWithCookie = async () => {
const user = await loginCurrentUser(rootStore.restApiContext);
if (!user) return;
await setCurrentUser(user);
};
const initialize = async () => {
if (initialized.value) return;
try {
await loginWithCookie();
initialized.value = true;
} catch (e) {}
};
const unsetCurrentUser = () => {
currentUserId.value = null;
};
const deleteUserById = (userId) => {
const { [userId]: _,...rest } = usersById.value;
usersById.value = rest;
};
const setPersonalizationAnswers = (answers) => {
if (!currentUser.value) return;
usersById.value = {
...usersById.value,
[currentUser.value.id]: {
...currentUser.value,
personalizationAnswers: answers
}
};
};
const loginWithCreds = async (params) => {
const user = await login(rootStore.restApiContext, params);
if (!user) return;
await setCurrentUser(user);
};
const registerLoginHook = (hook) => {
loginHooks.value.push(hook);
};
const registerLogoutHook = (hook) => {
logoutHooks.value.push(hook);
};
const logout$1 = async () => {
await logout(rootStore.restApiContext);
unsetCurrentUser();
for (const hook of logoutHooks.value) try {
await hook();
} catch (error) {
console.error("Error executing logout hook:", error);
}
localStorage.removeItem(BROWSER_ID_STORAGE_KEY);
};
const createOwner = async (params) => {
const user = await setupOwner(rootStore.restApiContext, params);
if (user) {
await setCurrentUser(user);
settingsStore.stopShowingSetupPage();
}
};
const validateSignupToken$1 = async (params) => {
return await validateSignupToken(rootStore.restApiContext, params);
};
const acceptInvitation$1 = async (params) => {
const user = await acceptInvitation(rootStore.restApiContext, params);
if (user) await setCurrentUser(user);
};
const sendForgotPasswordEmail$1 = async (params) => {
await sendForgotPasswordEmail(rootStore.restApiContext, params);
};
const validatePasswordToken$1 = async (params) => {
await validatePasswordToken(rootStore.restApiContext, params);
};
const changePassword$1 = async (params) => {
await changePassword(rootStore.restApiContext, params);
};
const updateUser = async (params) => {
const user = await updateCurrentUser(rootStore.restApiContext, params);
addUsers([user]);
return user;
};
const updateUserName = async (params) => {
if (!currentUser.value) return;
return await updateUser({
email: currentUser.value.email,
...params
});
};
const updateUserSettings = async (settings$7) => {
const updatedSettings = await updateCurrentUserSettings(rootStore.restApiContext, settings$7);
if (currentUser.value) {
currentUser.value.settings = updatedSettings;
addUsers([currentUser.value]);
}
};
const updateOtherUserSettings$1 = async (userId, settings$7) => {
await updateOtherUserSettings(rootStore.restApiContext, userId, settings$7);
};
const updateCurrentUserPassword$1 = async (params) => {
await updateCurrentUserPassword(rootStore.restApiContext, params);
};
const deleteUser$1 = async (params) => {
await deleteUser(rootStore.restApiContext, params);
deleteUserById(params.id);
};
const fetchUsers = async () => {
const { items } = await getUsers(rootStore.restApiContext, {
take: -1,
skip: 0
});
addUsers(items);
};
const inviteUsers$1 = async (params) => {
const invitedUsers = await inviteUsers(rootStore.restApiContext, params);
addUsers(invitedUsers.map(({ user }) => ({
isPending: true,
...user
})));
return invitedUsers;
};
const reinviteUser = async ({ email, role }) => {
const invitationResponse = await inviteUsers(rootStore.restApiContext, [{
email,
role
}]);
if (!invitationResponse[0].user.emailSent) throw Error(invitationResponse[0].error);
};
const getUserPasswordResetLink = async (params) => {
return await getPasswordResetLink(rootStore.restApiContext, params);
};
const submitPersonalizationSurvey$1 = async (results) => {
await submitPersonalizationSurvey(rootStore.restApiContext, results);
setPersonalizationAnswers(results);
};
const showPersonalizationSurvey = async () => {
if (settingsStore.isPersonalizationSurveyEnabled && currentUser.value && !currentUser.value.personalizationAnswers) uiStore.openModal(PERSONALIZATION_MODAL_KEY);
};
const fetchMfaQR = async () => {
return await getMfaQR(rootStore.restApiContext);
};
const verifyMfaCode$1 = async (data) => {
return await verifyMfaCode(rootStore.restApiContext, data);
};
const canEnableMFA$1 = async () => {
return await canEnableMFA(rootStore.restApiContext);
};
const enableMfa$1 = async (data) => {
await enableMfa(rootStore.restApiContext, data);
if (currentUser.value) currentUser.value.mfaEnabled = true;
};
const disableMfa$1 = async (data) => {
await disableMfa(rootStore.restApiContext, data);
if (currentUser.value) currentUser.value.mfaEnabled = false;
};
const updateEnforceMfa$1 = async (enforce) => {
await updateEnforceMfa(rootStore.restApiContext, enforce);
settingsStore.isMFAEnforced = enforce;
};
const sendConfirmationEmail$1 = async () => {
await sendConfirmationEmail(rootStore.restApiContext);
};
const updateGlobalRole$1 = async ({ id: id$1, newRoleName }) => {
await updateGlobalRole(rootStore.restApiContext, {
id: id$1,
newRoleName
});
await fetchUsers();
};
const submitContactEmail = async (email, agree) => {
if (currentUser.value) return await submitEmailOnSignup(rootStore.instanceId, currentUser.value, email ?? currentUser.value.email, agree);
return null;
};
const submitContactInfo$1 = async (email) => {
try {
return await submitContactInfo(rootStore.instanceId, currentUserId.value ?? "", email);
} catch (error) {
return;
}
};
const usersList = useAsyncState(async (filter$1) => await getUsers(rootStore.restApiContext, filter$1), {
count: 0,
items: []
}, {
immediate: false,
resetOnExecute: false
});
const setUserQuota = (quota) => {
if (typeof quota !== "undefined") userQuota.value = quota;
};
return {
initialized,
currentUserId,
usersById,
allUsers,
currentUser,
userActivated,
isDefaultUser: isDefaultUser$1,
isInstanceOwner: isInstanceOwner$1,
isAdmin,
mfaEnabled,
globalRoleName,
personalizedNodeTypes,
userClaimedAiCredits,
isEasyAIWorkflowOnboardingDone,
canUserUpdateVersion,
usersLimitNotReached,
addUsers,
loginWithCookie,
initialize,
setPersonalizationAnswers,
loginWithCreds,
logout: logout$1,
registerLoginHook,
registerLogoutHook,
createOwner,
validateSignupToken: validateSignupToken$1,
acceptInvitation: acceptInvitation$1,
sendForgotPasswordEmail: sendForgotPasswordEmail$1,
validatePasswordToken: validatePasswordToken$1,
changePassword: changePassword$1,
updateUser,
updateUserName,
updateUserSettings,
updateOtherUserSettings: updateOtherUserSettings$1,
updateCurrentUserPassword: updateCurrentUserPassword$1,
deleteUser: deleteUser$1,
fetchUsers,
inviteUsers: inviteUsers$1,
reinviteUser,
getUserPasswordResetLink,
submitPersonalizationSurvey: submitPersonalizationSurvey$1,
showPersonalizationSurvey,
fetchMfaQR,
verifyMfaCode: verifyMfaCode$1,
enableMfa: enableMfa$1,
disableMfa: disableMfa$1,
updateEnforceMfa: updateEnforceMfa$1,
canEnableMFA: canEnableMFA$1,
sendConfirmationEmail: sendConfirmationEmail$1,
updateGlobalRole: updateGlobalRole$1,
setEasyAIWorkflowOnboardingDone,
isCalloutDismissed,
setCalloutDismissed,
submitContactEmail,
submitContactInfo: submitContactInfo$1,
setUserQuota,
usersList
};
});
const hasRole = (checkRoles) => {
const currentUser = useUsersStore().currentUser;
if (currentUser && checkRoles) {
const userRole = currentUser.isDefaultUser ? ROLE.Default : currentUser.role;
return checkRoles.includes(userRole);
}
return false;
};
const useRBACStore = defineStore(STORES.RBAC, () => {
const globalRoles = ref([]);
const rolesByProjectId = ref({});
const globalScopes = ref([]);
const scopesByProjectId = ref({});
const scopesByResourceId = ref({
workflow: {},
tag: {},
annotationTag: {},
user: {},
credential: {},
variable: {},
projectVariable: {},
sourceControl: {},
externalSecretsProvider: {},
externalSecret: {},
project: {},
orchestration: {},
workersView: {},
eventBusDestination: {},
auditLogs: {},
banner: {},
community: {},
communityPackage: {},
ldap: {},
license: {},
logStreaming: {},
saml: {},
oidc: {},
provisioning: {},
securityAudit: {},
folder: {},
insights: {},
dataTable: {},
execution: {},
workflowTags: {},
role: {},
mcp: {},
mcpApiKey: {},
chatHub: {},
chatHubAgent: {},
breakingChanges: {}
});
function addGlobalRole(role) {
if (!globalRoles.value.includes(role)) globalRoles.value.push(role);
}
function hasRole$1(role) {
return globalRoles.value.includes(role);
}
function addGlobalScope(scope) {
if (!globalScopes.value.includes(scope)) globalScopes.value.push(scope);
}
function setGlobalScopes(scopes) {
globalScopes.value = scopes;
}
function addProjectScope(scope, context) {
if (!scopesByProjectId.value[context.projectId]) scopesByProjectId.value[context.projectId] = [];
if (!scopesByProjectId.value[context.projectId].includes(scope)) scopesByProjectId.value[context.projectId].push(scope);
}
function addResourceScope(scope, context) {
const scopesByResourceType = scopesByResourceId.value[context.resourceType];
if (!scopesByResourceType[context.resourceId]) scopesByResourceType[context.resourceId] = [];
if (!scopesByResourceType[context.resourceId].includes(scope)) scopesByResourceType[context.resourceId].push(scope);
}
function hasScope$2(scope, context, options) {
return hasScope$1(scope, {
global: globalScopes.value,
project: context?.projectId ? scopesByProjectId.value[context.projectId] : [],
resource: context?.resourceType && context?.resourceId ? scopesByResourceId.value[context.resourceType][context.resourceId] : []
}, void 0, options);
}
return {
globalRoles,
rolesByProjectId,
globalScopes,
scopesByProjectId,
scopesByResourceId,
addGlobalRole,
hasRole: hasRole$1,
addGlobalScope,
setGlobalScopes,
addProjectScope,
addResourceScope,
hasScope: hasScope$2
};
});
const hasScope = (opts) => {
if (!opts?.scope) return true;
const { projectId, resourceType, resourceId, scope, options } = opts;
return useRBACStore().hasScope(scope, {
projectId,
resourceType,
resourceId
}, options);
};
const isAuthenticated = (options) => {
if (options?.bypass?.()) return true;
return !!useUsersStore().currentUser;
};
const shouldEnableMfa = () => {
const hasUserEnabledMfa = useUsersStore().currentUser?.mfaAuthenticated ?? false;
const isMfaEnforced = useSettingsStore().isMFAEnforced;
return !hasUserEnabledMfa && isMfaEnforced;
};
const isDefaultUser = () => {
const currentUser = useUsersStore().currentUser;
if (currentUser) return currentUser.isDefaultUser;
return false;
};
const isInstanceOwner = () => useUsersStore().isInstanceOwner;
const isEnterpriseFeatureEnabled = (options) => {
if (!options?.feature) return true;
const features = Array.isArray(options.feature) ? options.feature : [options.feature];
const settingsStore = useSettingsStore();
if ((options.mode ?? "allOf") === "allOf") return features.every((feature) => settingsStore.isEnterpriseFeatureEnabled[feature]);
else return features.some((feature) => settingsStore.isEnterpriseFeatureEnabled[feature]);
};
const isGuest = () => {
return !useUsersStore().currentUser;
};
const isValid = (fn) => {
return fn ? fn() : false;
};
const permissions = {
authenticated: isAuthenticated,
custom: isValid,
defaultUser: isDefaultUser,
instanceOwner: isInstanceOwner,
enterprise: isEnterpriseFeatureEnabled,
guest: isGuest,
rbac: hasScope,
role: hasRole
};
function hasPermission(permissionNames, options) {
let valid = true;
for (const permissionName of permissionNames) {
const permissionOptions = options?.[permissionName] ?? {};
const permissionFn = permissions[permissionName];
valid = valid && permissionFn(permissionOptions);
}
return valid;
}
function useLoadingService() {
const i18n$1 = useI18n();
const loadingService = ref(null);
function startLoading(text) {
if (loadingService.value !== null) return;
loadingService.value = ElLoading.service({
lock: true,
text: text || i18n$1.baseText("genericHelpers.loading"),
background: "var(--dialog--overlay--color--background)"
});
}
function setLoadingText(text) {
if (loadingService.value) loadingService.value.text = text;
}
function stopLoading() {
if (loadingService.value) {
loadingService.value.close();
loadingService.value = null;
}
}
return {
loadingService,
isLoading: computed(() => loadingService.value !== null),
startLoading,
setLoadingText,
stopLoading
};
}
const useCanvasStore = defineStore("canvas", () => {
const workflowStore = useWorkflowsStore();
const loadingService = useLoadingService();
const newNodeInsertPosition = ref(null);
const nodes$13 = computed(() => workflowStore.allNodes);
const aiNodes = computed(() => nodes$13.value.filter((node) => node.type.includes("langchain") || node.type === "n8n-nodes-base.evaluation" && node.parameters?.operation === "setMetrics"));
const hasRangeSelection = ref(false);
function setHasRangeSelection(value) {
hasRangeSelection.value = value;
}
return {
newNodeInsertPosition,
isLoading: loadingService.isLoading,
aiNodes,
hasRangeSelection: computed(() => hasRangeSelection.value),
startLoading: loadingService.startLoading,
setLoadingText: loadingService.setLoadingText,
stopLoading: loadingService.stopLoading,
setHasRangeSelection
};
});
var MAX_PAIR_COUNT = 1e5;
function getPairedItemId(node, run, output, item) {
return `${node}_r${run}_o${output}_i${item}`;
}
function getSourceItems(data, target) {
if (!data?.data?.resultData?.runData) return [];
const taskData = data.data.resultData.runData[target.nodeName]?.[target.runIndex];
const source = taskData?.source || [];
if (source.length === 0) return [];
const item = taskData?.data?.main?.[target.outputIndex]?.[target.itemIndex];
if (item?.pairedItem === void 0) return [];
return (Array.isArray(item.pairedItem) ? item.pairedItem : typeof item.pairedItem === "object" ? [item.pairedItem] : [{ item: item.pairedItem }]).map((item$1) => {
const input = item$1.input || 0;
return {
nodeName: source?.[input]?.previousNode,
runIndex: source?.[input]?.previousNodeRun || 0,
itemIndex: item$1.item,
outputIndex: source[input]?.previousNodeOutput || 0
};
}).filter((item$1) => isNotNull(item$1));
}
function addPairing(paths, pairedItemId, pairedItem, sources) {
if (paths.size >= MAX_PAIR_COUNT) throw Error();
paths.data[pairedItemId] = paths.data[pairedItemId] || [];
const input = pairedItem.input || 0;
const sourceNode = sources[input]?.previousNode;
if (!sourceNode) {
paths.data[pairedItemId].push([pairedItemId]);
paths.size++;
return;
}
const sourceNodeOutput = sources[input]?.previousNodeOutput || 0;
const sourceItem = getPairedItemId(sourceNode, sources[input]?.previousNodeRun || 0, sourceNodeOutput, pairedItem.item);
if (!paths.data[sourceItem]) {
paths.data[sourceItem] = [[sourceItem]];
paths.size++;
}
paths.data[sourceItem]?.forEach((path) => {
paths.data[pairedItemId]?.push([...path, pairedItemId]);
paths.size++;
});
}
function addPairedItemIdsRec(node, runIndex, runData, seen, paths, pinned) {
const key = `${node}_r${runIndex}`;
if (seen.has(key)) return;
seen.add(key);
if (pinned.has(node)) return;
const nodeRunData = runData[node];
if (!Array.isArray(nodeRunData)) return;
const data = nodeRunData[runIndex];
if (!data?.data?.main) return;
const sources = data.source || [];
sources.forEach((source) => {
if (source?.previousNode) addPairedItemIdsRec(source.previousNode, source.previousNodeRun ?? 0, runData, seen, paths, pinned);
});
(data.data.main || []).forEach((outputData, output) => {
if (!outputData) return;
outputData.forEach((executionData, item) => {
const pairedItemId = getPairedItemId(node, runIndex, output, item);
if (!executionData.pairedItem) {
paths.data[pairedItemId] = [];
return;
}
const pairedItem = executionData.pairedItem;
if (Array.isArray(pairedItem)) {
pairedItem.forEach((item$1) => {
addPairing(paths, pairedItemId, item$1, sources);
});
return;
}
if (typeof pairedItem === "object") {
addPairing(paths, pairedItemId, pairedItem, sources);
return;
}
addPairing(paths, pairedItemId, { item: pairedItem }, sources);
});
});
}
function getMapping(paths) {
const mapping = {};
Object.keys(paths.data).forEach((item) => {
paths.data[item]?.forEach((path) => {
path.forEach((otherItem) => {
if (otherItem !== item) {
mapping[otherItem] = mapping[otherItem] || /* @__PURE__ */ new Set();
mapping[otherItem].add(item);
mapping[item] = mapping[item] || /* @__PURE__ */ new Set();
mapping[item].add(otherItem);
}
});
});
});
return mapping;
}
function getItemsCount(runData) {
let itemsCount = 0;
for (const node in runData) for (const taskData of runData[node]) {
const data = taskData.data;
if (!data) continue;
for (const connectionType in data) {
const runsCount = data[connectionType].reduce((sum, run) => {
return run ? sum + run.length : sum;
}, 0);
itemsCount += runsCount;
}
}
return itemsCount;
}
function getPairedItemsMapping(executionResponse) {
if (!executionResponse?.data?.resultData?.runData) return {};
const runData = executionResponse.data.resultData.runData;
if (getItemsCount(runData) > 1e3) return {};
const paths = {
size: 0,
data: {}
};
try {
const seen = /* @__PURE__ */ new Set();
const pinned = new Set(Object.keys(executionResponse.data.resultData.pinData ?? {}));
Object.keys(runData).forEach((node) => {
runData[node].forEach((_, runIndex) => {
addPairedItemIdsRec(node, runIndex, runData, seen, paths, pinned);
});
});
} catch {
return {};
}
return getMapping(paths);
}
const fetchDataTablesApi = async (context, projectId, options, filter$1) => {
return await makeRestApiRequest(context, "GET", projectId ? `/projects/${projectId}/data-tables` : "/data-tables-global", {
...options,
filter: filter$1 ?? void 0
});
};
const createDataTableApi = async (context, name$13, projectId, columns, fileId, hasHeaders = true) => {
return await makeRestApiRequest(context, "POST", `/projects/${projectId}/data-tables`, {
name: name$13,
columns: columns ?? [],
hasHeaders,
...fileId ? { fileId } : {}
});
};
const deleteDataTableApi = async (context, dataTableId, projectId) => {
return await makeRestApiRequest(context, "DELETE", `/projects/${projectId}/data-tables/${dataTableId}`, {
dataTableId,
projectId
});
};
const updateDataTableApi = async (context, dataTableId, name$13, projectId) => {
return await makeRestApiRequest(context, "PATCH", `/projects/${projectId}/data-tables/${dataTableId}`, { name: name$13 });
};
const addDataTableColumnApi = async (context, dataTableId, projectId, column) => {
return await makeRestApiRequest(context, "POST", `/projects/${projectId}/data-tables/${dataTableId}/columns`, { ...column });
};
const deleteDataTableColumnApi = async (context, dataTableId, projectId, columnId) => {
return await makeRestApiRequest(context, "DELETE", `/projects/${projectId}/data-tables/${dataTableId}/columns/${columnId}`);
};
const moveDataTableColumnApi = async (context, dataTableId, projectId, columnId, targetIndex) => {
return await makeRestApiRequest(context, "PATCH", `/projects/${projectId}/data-tables/${dataTableId}/columns/${columnId}/move`, { targetIndex });
};
const getDataTableRowsApi = async (context, dataTableId, projectId, options) => {
return await makeRestApiRequest(context, "GET", `/projects/${projectId}/data-tables/${dataTableId}/rows`, { ...options ?? {} });
};
const insertDataTableRowApi = async (context, dataTableId, row, projectId) => {
return await makeRestApiRequest(context, "POST", `/projects/${projectId}/data-tables/${dataTableId}/insert`, {
returnType: "all",
data: [row]
});
};
const updateDataTableRowsApi = async (context, dataTableId, rowId, rowData, projectId) => {
return await makeRestApiRequest(context, "PATCH", `/projects/${projectId}/data-tables/${dataTableId}/rows`, {
filter: {
type: "and",
filters: [{
columnName: "id",
condition: "eq",
value: rowId
}]
},
data: rowData
});
};
const deleteDataTableRowsApi = async (context, dataTableId, rowIds, projectId) => {
const filters = rowIds.map((id$1) => ({
columnName: "id",
condition: "eq",
value: id$1
}));
return await makeRestApiRequest(context, "DELETE", `/projects/${projectId}/data-tables/${dataTableId}/rows`, { filter: {
type: "or",
filters
} });
};
const fetchDataTableGlobalLimitInBytes = async (context) => {
return await makeRestApiRequest(context, "GET", "/data-tables-global/limits");
};
const downloadDataTableCsvApi = async (context, dataTableId, projectId) => {
const response = await makeRestApiRequest(context, "GET", `/projects/${projectId}/data-tables/${dataTableId}/download-csv`);
const filename = `${response.dataTableName}.csv`;
return {
csvContent: response.csvContent,
filename
};
};
const uploadCsvFileApi = async (context, file, hasHeaders = true) => {
const formData = new FormData();
formData.append("file", file);
formData.append("hasHeaders", String(hasHeaders));
return await makeRestApiRequest(context, "POST", "/data-tables/uploads", formData);
};
const getAllProjects = async (context) => {
return await makeRestApiRequest(context, "GET", "/projects");
};
const getMyProjects = async (context) => {
return await makeRestApiRequest(context, "GET", "/projects/my-projects");
};
const getPersonalProject = async (context) => {
return await makeRestApiRequest(context, "GET", "/projects/personal");
};
const getProject = async (context, id$1) => {
return await makeRestApiRequest(context, "GET", `/projects/${id$1}`);
};
const createProject = async (context, payload) => {
return await makeRestApiRequest(context, "POST", "/projects", payload);
};
const updateProject = async (context, id$1, payload) => {
await makeRestApiRequest(context, "PATCH", `/projects/${id$1}`, payload);
};
const deleteProject = async (context, projectId, transferId) => {
await makeRestApiRequest(context, "DELETE", `/projects/${projectId}`, transferId ? { transferId } : {});
};
const getProjectsCount = async (context) => {
return await makeRestApiRequest(context, "GET", "/projects/count");
};
const addProjectMembers = async (context, projectId, relations) => {
await makeRestApiRequest(context, "POST", `/projects/${projectId}/users`, { relations });
};
const updateProjectMemberRole = async (context, projectId, userId, role) => {
await makeRestApiRequest(context, "PATCH", `/projects/${projectId}/users/${userId}`, { role });
};
const deleteProjectMember = async (context, projectId, userId) => {
await makeRestApiRequest(context, "DELETE", `/projects/${projectId}/users/${userId}`);
};
async function setWorkflowSharedWith(context, id$1, data) {
return await makeRestApiRequest(context, "PUT", `/workflows/${id$1}/share`, data);
}
async function moveWorkflowToProject(context, id$1, body) {
return await makeRestApiRequest(context, "PUT", `/workflows/${id$1}/transfer`, body);
}
async function moveFolderToProject(context, projectId, folderId, destinationProjectId, destinationParentFolderId, shareCredentials) {
return await makeRestApiRequest(context, "PUT", `/projects/${projectId}/folders/${folderId}/transfer`, {
destinationProjectId,
destinationParentFolderId: destinationParentFolderId ?? "0",
shareCredentials
});
}
const ProjectTypes = {
Personal: "personal",
Team: "team",
Public: "public"
};
var sourceControlApiRoot = "/source-control";
var createPreferencesRequestFn = (method) => async (context, preferences) => await makeRestApiRequest(context, method, `${sourceControlApiRoot}/preferences`, preferences);
const pushWorkfolder = async (context, data) => {
return await makeRestApiRequest(context, "POST", `${sourceControlApiRoot}/push-workfolder`, data);
};
const pullWorkfolder = async (context, data) => {
return await makeRestApiRequest(context, "POST", `${sourceControlApiRoot}/pull-workfolder`, data);
};
const getBranches = async (context) => {
return await makeRestApiRequest(context, "GET", `${sourceControlApiRoot}/get-branches`);
};
const savePreferences = createPreferencesRequestFn("POST");
const updatePreferences = createPreferencesRequestFn("PATCH");
const getPreferences = async (context) => {
return await makeRestApiRequest(context, "GET", `${sourceControlApiRoot}/preferences`);
};
const getStatus = async (context) => {
return await makeRestApiRequest(context, "GET", `${sourceControlApiRoot}/status`);
};
const getRemoteWorkflow = async (context, workflowId) => {
return await makeRestApiRequest(context, "GET", `${sourceControlApiRoot}/remote-content/workflow/${workflowId}`);
};
const getAggregatedStatus = async (context, options = {
direction: "push",
preferLocalVersion: true,
verbose: false
}) => {
return await makeRestApiRequest(context, "GET", `${sourceControlApiRoot}/get-status`, options);
};
const disconnect = async (context, keepKeyPair) => {
return await makeRestApiRequest(context, "POST", `${sourceControlApiRoot}/disconnect`, { keepKeyPair });
};
const generateKeyPair = async (context, keyGeneratorType) => {
return await makeRestApiRequest(context, "POST", `${sourceControlApiRoot}/generate-key-pair`, { keyGeneratorType });
};
const useSourceControlStore = defineStore("sourceControl", () => {
const rootStore = useRootStore();
const settingsStore = useSettingsStore();
const isEnterpriseSourceControlEnabled = computed(() => settingsStore.isEnterpriseFeatureEnabled[EnterpriseEditionFeature.SourceControl]);
const sshKeyTypesWithLabel = reactive(["ed25519", "rsa"].map((value) => ({
value,
label: value.toUpperCase()
})));
const preferences = reactive({
branchName: "",
branches: [],
repositoryUrl: "",
branchReadOnly: false,
branchColor: "#5296D6",
connected: false,
publicKey: "",
keyGeneratorType: "ed25519",
connectionType: "ssh"
});
const state = reactive({ commitMessage: "commit message" });
const pushWorkfolder$1 = async (data) => {
state.commitMessage = data.commitMessage;
await pushWorkfolder(rootStore.restApiContext, {
force: data.force,
commitMessage: data.commitMessage,
fileNames: data.fileNames
});
};
const pullWorkfolder$1 = async (force) => {
return await pullWorkfolder(rootStore.restApiContext, { force });
};
const setPreferences = (data) => {
Object.assign(preferences, data);
};
const makePreferencesAction = (action) => async (preferences$1) => {
setPreferences(await action(rootStore.restApiContext, preferences$1));
};
const getBranches$1 = async () => {
setPreferences(await getBranches(rootStore.restApiContext));
};
const getPreferences$1 = async () => {
setPreferences(await getPreferences(rootStore.restApiContext));
};
const savePreferences$1 = makePreferencesAction(savePreferences);
const updatePreferences$1 = makePreferencesAction(updatePreferences);
const disconnect$1 = async (keepKeyPair) => {
await disconnect(rootStore.restApiContext, keepKeyPair);
setPreferences({
connected: false,
branches: []
});
};
const generateKeyPair$1 = async (keyGeneratorType) => {
await generateKeyPair(rootStore.restApiContext, keyGeneratorType);
const data = await getPreferences(rootStore.restApiContext);
preferences.publicKey = data.publicKey;
return { publicKey: data.publicKey };
};
const getStatus$1 = async () => {
return await getStatus(rootStore.restApiContext);
};
const getAggregatedStatus$1 = async () => {
return await getAggregatedStatus(rootStore.restApiContext);
};
const getRemoteWorkflow$1 = async (workflowId) => {
return await getRemoteWorkflow(rootStore.restApiContext, workflowId);
};
return {
isEnterpriseSourceControlEnabled,
state,
preferences,
pushWorkfolder: pushWorkfolder$1,
pullWorkfolder: pullWorkfolder$1,
getPreferences: getPreferences$1,
setPreferences,
generateKeyPair: generateKeyPair$1,
getBranches: getBranches$1,
savePreferences: savePreferences$1,
updatePreferences: updatePreferences$1,
disconnect: disconnect$1,
getStatus: getStatus$1,
getAggregatedStatus: getAggregatedStatus$1,
getRemoteWorkflow: getRemoteWorkflow$1,
sshKeyTypesWithLabel
};
});
const useProjectsStore = defineStore(STORES.PROJECTS, () => {
const route = useRoute();
const rootStore = useRootStore();
const settingsStore = useSettingsStore();
const credentialsStore = useCredentialsStore();
const usersStore = useUsersStore();
const sourceControlStore = useSourceControlStore();
const projects = ref([]);
const myProjects = ref([]);
const personalProject = ref(null);
const currentProject = ref(null);
const projectsCount = ref({
personal: 0,
team: 0,
public: 0
});
const projectNavActiveIdState = ref(null);
const globalProjectPermissions = computed(() => getResourcePermissions(usersStore.currentUser?.globalScopes).project);
const availableProjects = computed(() => globalProjectPermissions.value.list ? projects.value : myProjects.value);
const currentProjectId = computed(() => route.params?.projectId ?? route.query?.projectId ?? currentProject.value?.id);
const isProjectHome = computed(() => route.path.includes("home"));
const personalProjects = computed(() => projects.value.filter((p) => p.type === ProjectTypes.Personal));
const teamProjects = computed(() => projects.value.filter((p) => p.type === ProjectTypes.Team));
const teamProjectsLimit = computed(() => settingsStore.settings.enterprise.projects.team.limit);
const isTeamProjectFeatureEnabled = computed(() => teamProjectsLimit.value !== 0);
const hasUnlimitedProjects = computed(() => teamProjectsLimit.value === -1);
const isTeamProjectLimitExceeded = computed(() => projectsCount.value.team >= teamProjectsLimit.value);
const canCreateProjects = computed(() => (hasUnlimitedProjects.value || isTeamProjectFeatureEnabled.value && !isTeamProjectLimitExceeded.value) && !sourceControlStore.preferences.branchReadOnly);
const hasPermissionToCreateProjects = computed(() => hasPermission(["rbac"], { rbac: { scope: "project:create" } }));
const projectNavActiveId = computed({
get: () => route?.params?.projectId ?? projectNavActiveIdState.value,
set: (value) => {
projectNavActiveIdState.value = value;
}
});
const setCurrentProject = (project) => {
currentProject.value = project;
};
const getAllProjects$1 = async () => {
projects.value = await getAllProjects(rootStore.restApiContext);
};
const getMyProjects$1 = async () => {
myProjects.value = await getMyProjects(rootStore.restApiContext);
};
const getPersonalProject$1 = async () => {
personalProject.value = await getPersonalProject(rootStore.restApiContext);
};
const getAvailableProjects = async () => {
if (globalProjectPermissions.value.list) await getAllProjects$1();
else await getMyProjects$1();
};
const fetchProject = async (id$1) => await getProject(rootStore.restApiContext, id$1);
const getProject$1 = async (id$1) => {
currentProject.value = await fetchProject(id$1);
};
async function fetchAndSetProject(projectId) {
if (projectId && currentProject.value?.id !== projectId) setCurrentProject(await fetchProject(projectId));
}
async function refreshCurrentProject() {
if (currentProjectId.value && currentProject.value?.id !== currentProjectId.value) await fetchAndSetProject(currentProjectId.value);
}
const createProject$1 = async (project) => {
const newProject = await createProject(rootStore.restApiContext, project);
await getProjectsCount$1();
myProjects.value = [...myProjects.value, newProject];
return newProject;
};
const updateProject$1 = async (id$1, projectData) => {
const { name: name$13, icon, description } = projectData;
const payload = {};
if (name$13 !== void 0) payload.name = name$13;
if (icon !== void 0) payload.icon = icon;
if (description !== void 0) payload.description = description;
await updateProject(rootStore.restApiContext, id$1, payload);
const projectIndex = myProjects.value.findIndex((p) => p.id === id$1);
const { name: nm, icon: ic, description: desc } = {
name: name$13,
icon,
description
};
if (projectIndex !== -1) {
if (nm !== void 0) myProjects.value[projectIndex].name = nm;
if (ic !== void 0) myProjects.value[projectIndex].icon = ic;
if (desc !== void 0) myProjects.value[projectIndex].description = desc;
}
if (currentProject.value) {
if (nm !== void 0) currentProject.value.name = nm;
if (ic !== void 0) currentProject.value.icon = ic;
if (desc !== void 0) currentProject.value.description = desc;
}
};
const addMember = async (projectId, { userId, role }) => {
await addProjectMembers(rootStore.restApiContext, projectId, [{
userId,
role
}]);
await getProject$1(projectId);
};
const updateMemberRole = async (projectId, userId, role) => {
await updateProjectMemberRole(rootStore.restApiContext, projectId, userId, role);
await getProject$1(projectId);
};
const removeMember = async (projectId, userId) => {
await deleteProjectMember(rootStore.restApiContext, projectId, userId);
await getProject$1(projectId);
};
const deleteProject$1 = async (projectId, transferId) => {
await deleteProject(rootStore.restApiContext, projectId, transferId);
await getProjectsCount$1();
myProjects.value = myProjects.value.filter((p) => p.id !== projectId);
};
const getProjectsCount$1 = async () => {
projectsCount.value = await getProjectsCount(rootStore.restApiContext);
};
const setProjectNavActiveIdByWorkflowHomeProject = async (workflowHomeProject, sharedWithProjects) => {
if (personalProject.value?.id !== workflowHomeProject?.id && workflowHomeProject?.type === ProjectTypes.Personal && sharedWithProjects?.some((project) => project.id === personalProject.value?.id)) {
projectNavActiveId.value = "shared";
setCurrentProject(null);
return;
}
if (workflowHomeProject?.type === ProjectTypes.Personal) if (personalProject.value?.id === workflowHomeProject?.id) {
projectNavActiveId.value = workflowHomeProject?.id ?? null;
currentProject.value = personalProject.value;
return;
} else {
projectNavActiveId.value = "home";
return;
}
projectNavActiveId.value = workflowHomeProject?.id ?? null;
if (workflowHomeProject?.id && !currentProjectId.value) await getProject$1(workflowHomeProject?.id);
};
const moveResourceToProject = async (resourceType, resourceId, projectId, parentFolderId, shareCredentials) => {
if (resourceType === "workflow") await moveWorkflowToProject(rootStore.restApiContext, resourceId, {
destinationProjectId: projectId,
destinationParentFolderId: parentFolderId,
shareCredentials
});
else {
await moveCredentialToProject(rootStore.restApiContext, resourceId, projectId);
await credentialsStore.fetchAllCredentials(currentProjectId.value);
}
};
const getResourceCounts = async (projectId) => {
const [credentials, workflows, dataTables] = await Promise.all([
getAllCredentials(rootStore.restApiContext, { projectId }),
getWorkflows(rootStore.restApiContext, { projectId }),
fetchDataTablesApi(rootStore.restApiContext, projectId)
]);
return {
credentials: credentials.length,
workflows: workflows.count,
dataTables: dataTables.count
};
};
watch(route, async (newRoute) => {
projectNavActiveId.value = null;
if (newRoute?.path?.includes("home")) {
projectNavActiveId.value = "home";
setCurrentProject(null);
}
if (newRoute?.path?.includes("shared")) {
projectNavActiveId.value = "shared";
setCurrentProject(null);
}
if (newRoute?.path?.includes("workflow/")) if (currentProjectId.value) projectNavActiveId.value = currentProjectId.value;
else projectNavActiveId.value = "home";
if (!newRoute?.params?.projectId) return;
await getProject$1(newRoute.params.projectId);
}, { immediate: true });
return {
projects,
availableProjects,
myProjects,
personalProject,
currentProject,
currentProjectId,
isProjectHome,
personalProjects,
teamProjects,
teamProjectsLimit,
hasUnlimitedProjects,
canCreateProjects,
hasPermissionToCreateProjects,
isTeamProjectFeatureEnabled,
projectNavActiveId,
setCurrentProject,
getAllProjects: getAllProjects$1,
getMyProjects: getMyProjects$1,
getPersonalProject: getPersonalProject$1,
getAvailableProjects,
getProject: getProject$1,
fetchProject,
fetchAndSetProject,
refreshCurrentProject,
createProject: createProject$1,
updateProject: updateProject$1,
addMember,
updateMemberRole,
removeMember,
deleteProject: deleteProject$1,
getProjectsCount: getProjectsCount$1,
setProjectNavActiveIdByWorkflowHomeProject,
moveResourceToProject,
getResourceCounts
};
});
var DEFAULT_TITLE = "n8n";
var DEFAULT_TAGLINE = "Workflow Automation";
function useDocumentTitle(windowRef) {
const { releaseChannel } = useSettingsStore().settings;
const suffix = !releaseChannel || releaseChannel === "stable" ? DEFAULT_TITLE : `${DEFAULT_TITLE}[${releaseChannel.toUpperCase()}]`;
const set$2 = (title) => {
const sections = [title || DEFAULT_TAGLINE, suffix];
(windowRef?.value?.document ?? document).title = sections.join(" - ");
};
const reset = () => {
set$2("");
};
const setDocumentTitle = (workflowName, status) => {
let icon = "⚠️";
if (status === "EXECUTING") icon = "🔄";
else if (status === "IDLE") icon = "▶️";
set$2(`${icon} ${workflowName}`);
};
return {
set: set$2,
reset,
setDocumentTitle
};
}
function useExecutingNode() {
const executingNode = ref([]);
const lastAddedExecutingNode = ref(null);
function addExecutingNode(nodeName) {
executingNode.value.push(nodeName);
lastAddedExecutingNode.value = nodeName;
}
function removeExecutingNode(nodeName) {
const executionIndex = executingNode.value.indexOf(nodeName);
if (executionIndex === -1) return;
executingNode.value.splice(executionIndex, 1);
}
function clearNodeExecutionQueue() {
executingNode.value = [];
lastAddedExecutingNode.value = null;
}
function isNodeExecuting(nodeName) {
return executingNode.value.includes(nodeName);
}
return {
executingNode,
lastAddedExecutingNode,
addExecutingNode,
removeExecutingNode,
isNodeExecuting,
clearNodeExecutionQueue
};
}
const useWorkflowStateStore = defineStore(STORES.WORKFLOW_STATE, () => {
return { executingNode: useExecutingNode() };
});
var require__createFind = /* @__PURE__ */ __commonJSMin(((exports, module) => {
var baseIteratee$1 = require__baseIteratee(), isArrayLike = require_isArrayLike(), keys = require_keys();
function createFind(findIndexFunc) {
return function(collection, predicate, fromIndex) {
var iterable = Object(collection);
if (!isArrayLike(collection)) {
var iteratee = baseIteratee$1(predicate, 3);
collection = keys(collection);
predicate = function(key) {
return iteratee(iterable[key], key, iterable);
};
}
var index = findIndexFunc(collection, predicate, fromIndex);
return index > -1 ? iterable[iteratee ? collection[index] : index] : void 0;
};
}
module.exports = createFind;
}));
var require_toFinite = /* @__PURE__ */ __commonJSMin(((exports, module) => {
var toNumber = require_toNumber();
var INFINITY = Infinity, MAX_INTEGER = 17976931348623157e292;
function toFinite$1(value) {
if (!value) return value === 0 ? value : 0;
value = toNumber(value);
if (value === INFINITY || value === -INFINITY) return (value < 0 ? -1 : 1) * MAX_INTEGER;
return value === value ? value : 0;
}
module.exports = toFinite$1;
}));
var require_toInteger = /* @__PURE__ */ __commonJSMin(((exports, module) => {
var toFinite = require_toFinite();
function toInteger$1(value) {
var result = toFinite(value), remainder = result % 1;
return result === result ? remainder ? result - remainder : result : 0;
}
module.exports = toInteger$1;
}));
var require_findLastIndex = /* @__PURE__ */ __commonJSMin(((exports, module) => {
var baseFindIndex = require__baseFindIndex(), baseIteratee = require__baseIteratee(), toInteger = require_toInteger();
var nativeMax = Math.max, nativeMin = Math.min;
function findLastIndex(array$1, predicate, fromIndex) {
var length = array$1 == null ? 0 : array$1.length;
if (!length) return -1;
var index = length - 1;
if (fromIndex !== void 0) {
index = toInteger(fromIndex);
index = fromIndex < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1);
}
return baseFindIndex(array$1, baseIteratee(predicate, 3), index, true);
}
module.exports = findLastIndex;
}));
var import_findLast = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
module.exports = require__createFind()(require_findLastIndex());
})))());
var import_isEqual = /* @__PURE__ */ __toESM(require_isEqual());
var import_pick = /* @__PURE__ */ __toESM(require_pick());
const workflowStateEventBus = createEventBus();
function useWorkflowState() {
const ws = useWorkflowsStore();
const workflowStateStore = useWorkflowStateStore();
const uiStore = useUIStore();
const rootStore = useRootStore();
const nodeTypesStore = useNodeTypesStore();
function setWorkflowName(data) {
if (data.setStateDirty) uiStore.stateIsDirty = true;
ws.workflow.name = data.newName;
ws.workflowObject.name = data.newName;
if (ws.workflow.id !== "__EMPTY__" && ws.workflowsById[ws.workflow.id]) ws.workflowsById[ws.workflow.id].name = data.newName;
}
function removeAllConnections(data) {
if (data?.setStateDirty) uiStore.stateIsDirty = true;
ws.workflow.connections = {};
ws.workflowObject.setConnections({});
}
function removeAllNodes(data) {
if (data.setStateDirty) uiStore.stateIsDirty = true;
if (data.removePinData) ws.workflow.pinData = {};
ws.workflow.nodes.splice(0, ws.workflow.nodes.length);
ws.workflowObject.setNodes(ws.workflow.nodes);
ws.nodeMetadata = {};
}
function setWorkflowExecutionData(workflowResultData) {
if (workflowResultData?.data?.waitTill) delete workflowResultData.data.resultData.runData[workflowResultData.data.resultData.lastNodeExecuted];
ws.workflowExecutionData = workflowResultData;
ws.workflowExecutionPairedItemMappings = getPairedItemsMapping(workflowResultData);
ws.workflowExecutionResultDataLastUpdate = Date.now();
ws.workflowExecutionStartedData = void 0;
}
function resetAllNodesIssues() {
ws.workflow.nodes.forEach((node) => {
node.issues = void 0;
});
return true;
}
function setActive(activeVersionId) {
ws.workflow.active = activeVersionId !== null;
ws.workflow.activeVersionId = activeVersionId;
}
function setWorkflowId(id$1) {
ws.workflow.id = !id$1 || id$1 === "new" ? PLACEHOLDER_EMPTY_WORKFLOW_ID : id$1;
ws.workflowObject.id = ws.workflow.id;
}
function setWorkflowSettings(workflowSettings) {
ws.private.setWorkflowSettings(workflowSettings);
}
function setWorkflowTagIds(tags$7) {
ws.workflow.tags = tags$7;
}
function setActiveExecutionId(id$1) {
ws.private.setActiveExecutionId(id$1);
}
async function getNewWorkflowData(name$13, projectId, parentFolderId) {
let workflowData = {
name: "",
settings: { ...ws.defaults.settings }
};
try {
const data = {
name: name$13,
projectId,
parentFolderId
};
workflowData = await getNewWorkflow(rootStore.restApiContext, isEmpty(data) ? void 0 : data);
} catch (e) {
workflowData.name = name$13 || "My workflow";
}
setWorkflowName({
newName: workflowData.name,
setStateDirty: false
});
return workflowData;
}
function makeNewWorkflowShareable() {
const { currentProject, personalProject } = useProjectsStore();
const homeProject = currentProject ?? personalProject ?? {};
const scopes = currentProject?.scopes ?? personalProject?.scopes ?? [];
ws.workflow.homeProject = homeProject;
ws.workflow.scopes = scopes;
}
async function getNewWorkflowDataAndMakeShareable(name$13, projectId, parentFolderId) {
const workflowData = await getNewWorkflowData(name$13, projectId, parentFolderId);
makeNewWorkflowShareable();
return workflowData;
}
const documentTitle = useDocumentTitle();
function markExecutionAsStopped(stopData) {
setActiveExecutionId(void 0);
workflowStateStore.executingNode.clearNodeExecutionQueue();
ws.executionWaitingForWebhook = false;
documentTitle.setDocumentTitle(ws.workflowName, "IDLE");
ws.workflowExecutionStartedData = void 0;
clearPopupWindowState();
if (!ws.workflowExecutionData) return;
const runData = ws.workflowExecutionData.data?.resultData.runData ?? {};
for (const nodeName in runData) runData[nodeName] = runData[nodeName].filter(({ executionStatus }) => executionStatus === "success");
if (stopData) {
ws.workflowExecutionData.status = stopData.status;
ws.workflowExecutionData.startedAt = stopData.startedAt;
ws.workflowExecutionData.stoppedAt = stopData.stoppedAt;
}
}
function resetState() {
removeAllConnections({ setStateDirty: false });
removeAllNodes({
setStateDirty: false,
removePinData: true
});
setWorkflowExecutionData(null);
resetAllNodesIssues();
setActive(ws.defaults.activeVersionId);
setWorkflowId(PLACEHOLDER_EMPTY_WORKFLOW_ID);
setWorkflowName({
newName: "",
setStateDirty: false
});
setWorkflowSettings({ ...ws.defaults.settings });
setWorkflowTagIds([]);
setActiveExecutionId(void 0);
workflowStateStore.executingNode.executingNode.length = 0;
ws.executionWaitingForWebhook = false;
}
function updateNodeAtIndex(nodeIndex, nodeData) {
if (nodeIndex !== -1) {
const node = ws.workflow.nodes[nodeIndex];
const changed = !(0, import_isEqual.default)((0, import_pick.default)(node, Object.keys(nodeData)), nodeData);
if (changed) {
Object.assign(node, nodeData);
ws.workflow.nodes[nodeIndex] = node;
ws.workflowObject.setNodes(ws.workflow.nodes);
}
return changed;
}
return false;
}
function setNodeParameters(updateInformation, append) {
const nodeIndex = ws.workflow.nodes.findIndex((node) => {
return node.name === updateInformation.name;
});
if (nodeIndex === -1) throw new Error(`Node with the name "${updateInformation.name}" could not be found to set parameter.`);
const { name: name$13, parameters } = ws.workflow.nodes[nodeIndex];
if (updateNodeAtIndex(nodeIndex, { parameters: !!append && isObject$1(updateInformation.value) ? {
...parameters,
...updateInformation.value
} : updateInformation.value })) {
uiStore.stateIsDirty = true;
ws.nodeMetadata[name$13].parametersLastUpdatedAt = Date.now();
}
}
function setLastNodeParameters(updateInformation) {
const latestNode = (0, import_findLast.default)(ws.workflow.nodes, (node) => node.type === updateInformation.key);
const nodeType = nodeTypesStore.getNodeType(latestNode.type);
if (!nodeType) return;
const nodeParams = getNodeParameters(nodeType.properties, updateInformation.value, true, false, latestNode, nodeType);
if (latestNode) setNodeParameters({
value: nodeParams,
name: latestNode.name
}, true);
}
function setNodeValue(updateInformation) {
const nodeIndex = ws.workflow.nodes.findIndex((node) => {
return node.name === updateInformation.name;
});
if (nodeIndex === -1 || !updateInformation.key) throw new Error(`Node with the name "${updateInformation.name}" could not be found to set parameter.`);
const changed = updateNodeAtIndex(nodeIndex, { [updateInformation.key]: updateInformation.value });
uiStore.stateIsDirty = uiStore.stateIsDirty || changed;
if (changed && ![
"position",
"notes",
"notesInFlow"
].includes(updateInformation.key)) ws.nodeMetadata[ws.workflow.nodes[nodeIndex].name].parametersLastUpdatedAt = Date.now();
}
function setNodePositionById(id$1, position) {
const node = ws.workflow.nodes.find((n) => n.id === id$1);
if (!node) return;
setNodeValue({
name: node.name,
key: "position",
value: position
});
}
function updateNodeProperties(updateInformation) {
const nodeIndex = ws.workflow.nodes.findIndex((node) => {
return node.name === updateInformation.name;
});
if (nodeIndex !== -1) for (const key of Object.keys(updateInformation.properties)) {
const typedKey = key;
const property = updateInformation.properties[typedKey];
if (updateNodeAtIndex(nodeIndex, { [key]: property })) uiStore.stateIsDirty = true;
}
workflowStateEventBus.emit("updateNodeProperties", [this, updateInformation]);
}
function setNodeIssue(nodeIssueData) {
const nodeIndex = ws.workflow.nodes.findIndex((node$1) => {
return node$1.name === nodeIssueData.node;
});
if (nodeIndex === -1) return;
const node = ws.workflow.nodes[nodeIndex];
if (nodeIssueData.value === null) {
if (node.issues?.[nodeIssueData.type] === void 0) return;
const { [nodeIssueData.type]: _removedNodeIssue,...remainingNodeIssues } = node.issues;
updateNodeAtIndex(nodeIndex, { issues: remainingNodeIssues });
} else updateNodeAtIndex(nodeIndex, { issues: {
...node.issues,
[nodeIssueData.type]: nodeIssueData.value
} });
}
return {
resetState,
removeAllConnections,
removeAllNodes,
setWorkflowExecutionData,
resetAllNodesIssues,
setActive,
setWorkflowId,
setWorkflowName,
setWorkflowSettings,
setWorkflowTagIds,
setActiveExecutionId,
getNewWorkflowDataAndMakeShareable,
markExecutionAsStopped,
setNodeParameters,
setLastNodeParameters,
setNodeValue,
setNodePositionById,
setNodeIssue,
updateNodeAtIndex,
updateNodeProperties,
executingNode: workflowStateStore.executingNode
};
}
function injectWorkflowState() {
return inject(WorkflowStateKey, () => {
return useWorkflowState();
}, true);
}
function useNodeHelpers(opts = {}) {
const credentialsStore = useCredentialsStore();
const historyStore = useHistoryStore();
const nodeTypesStore = useNodeTypesStore();
const workflowsStore = useWorkflowsStore();
const workflowState = opts.workflowState ?? injectWorkflowState();
const settingsStore = useSettingsStore();
const i18n$1 = useI18n();
const canvasStore = useCanvasStore();
const isInsertingNodes = ref(false);
const credentialsUpdated = ref(false);
const isProductionExecutionPreview = ref(false);
const pullConnActiveNodeName = ref(null);
const workflowObject = computed(() => workflowsStore.workflowObject);
function hasProxyAuth(node) {
return Object.keys(node.parameters).includes("nodeCredentialType");
}
function isCustomApiCallSelected(nodeValues) {
const { parameters } = nodeValues;
if (!isObject$1(parameters)) return false;
if ("resource" in parameters || "operation" in parameters) {
const { resource, operation } = parameters;
return isString(resource) && resource.includes("__CUSTOM_API_CALL__") || isString(operation) && operation.includes("__CUSTOM_API_CALL__");
}
return false;
}
function isNodeExecutable(node, executable, foreignCredentials) {
const nodeType = node ? nodeTypesStore.getNodeType(node.type, node.typeVersion) : null;
if (node && nodeType) {
const workflowNode = workflowObject.value.getNode(node.name);
const isTriggerNode = !!node && nodeTypesStore.isTriggerNode(node.type);
const isToolNode = !!node && nodeTypesStore.isToolNode(node.type);
if (workflowNode) {
if (!getConnectionTypes(getNodeInputs(workflowObject.value, workflowNode, nodeType)).includes(NodeConnectionTypes.Main) && !isToolNode && !isTriggerNode) return false;
}
}
return Boolean(executable || foreignCredentials.length > 0);
}
function getForeignCredentialsIfSharingEnabled(credentials) {
if (!credentials || !settingsStore.isEnterpriseFeatureEnabled[EnterpriseEditionFeature.Sharing]) return [];
const usedCredentials = workflowsStore.usedCredentials;
return Object.values(credentials).map(({ id: id$1 }) => id$1).filter((id$1) => id$1 !== null).filter((id$1) => id$1 in usedCredentials && !usedCredentials[id$1]?.currentUserHasAccess);
}
function displayParameter(nodeValues, parameter, path, node, displayKey = "displayOptions") {
return displayParameterPath(nodeValues, parameter, path, node, node?.type ? nodeTypesStore.getNodeType(node.type, node.typeVersion) : null, displayKey);
}
function getNodeIssues(nodeType, node, workflow, ignoreIssues) {
const pinDataNodeNames = Object.keys(workflowsStore.pinnedWorkflowData ?? {});
let nodeIssues = null;
ignoreIssues = ignoreIssues ?? [];
if (node.disabled === true || pinDataNodeNames.includes(node.name)) return null;
if (nodeType === null) {
if (!ignoreIssues.includes("typeUnknown")) nodeIssues = { typeUnknown: true };
} else {
if (!ignoreIssues.includes("parameters")) nodeIssues = getNodeParametersIssues(nodeType.properties, node, nodeType);
if (!ignoreIssues.includes("credentials")) {
const nodeCredentialIssues = getNodeCredentialIssues(node, nodeType);
if (nodeIssues === null) nodeIssues = nodeCredentialIssues;
else mergeIssues(nodeIssues, nodeCredentialIssues);
}
const nodeInputIssues = getNodeInputIssues(workflow, node, nodeType);
if (nodeIssues === null) nodeIssues = nodeInputIssues;
else mergeIssues(nodeIssues, nodeInputIssues);
}
if (hasNodeExecutionIssues(node) && !ignoreIssues.includes("execution")) {
if (nodeIssues === null) nodeIssues = {};
nodeIssues.execution = true;
}
return nodeIssues;
}
function hasNodeExecutionIssues(node) {
const workflowResultData = workflowsStore.getWorkflowRunData;
if (!workflowResultData?.hasOwnProperty(node.name)) return false;
for (const taskData of workflowResultData[node.name]) if (taskData.error !== void 0) return true;
return false;
}
function reportUnsetCredential(credentialType) {
return { credentials: { [credentialType.name]: [i18n$1.baseText("nodeHelpers.credentialsUnset", { interpolate: { credentialType: credentialType.displayName } })] } };
}
function updateNodeInputIssues(node) {
const nodeType = nodeTypesStore.getNodeType(node.type, node.typeVersion);
if (!nodeType) return;
const nodeInputIssues = getNodeInputIssues(workflowObject.value, node, nodeType);
workflowState.setNodeIssue({
node: node.name,
type: "input",
value: nodeInputIssues?.input ? nodeInputIssues.input : null
});
}
function updateNodesInputIssues() {
const nodes$13 = workflowsStore.allNodes;
for (const node of nodes$13) updateNodeInputIssues(node);
}
function updateNodesExecutionIssues() {
const nodes$13 = workflowsStore.allNodes;
for (const node of nodes$13) workflowState.setNodeIssue({
node: node.name,
type: "execution",
value: hasNodeExecutionIssues(node) ? true : null
});
}
function updateNodesParameterIssues() {
const nodes$13 = workflowsStore.allNodes;
for (const node of nodes$13) updateNodeParameterIssues(node);
}
function updateNodeCredentialIssuesByName(name$13) {
const node = workflowsStore.getNodeByName(name$13);
if (node) updateNodeCredentialIssues(node);
}
function updateNodeCredentialIssues(node) {
const fullNodeIssues = getNodeCredentialIssues(node);
let newIssues = null;
if (fullNodeIssues !== null) newIssues = fullNodeIssues.credentials;
workflowState.setNodeIssue({
node: node.name,
type: "credentials",
value: newIssues
});
}
function updateNodeParameterIssuesByName(name$13) {
const node = workflowsStore.getNodeByName(name$13);
if (node) updateNodeParameterIssues(node);
}
function updateNodeParameterIssues(node, nodeType) {
const localNodeType = nodeType ?? nodeTypesStore.getNodeType(node.type, node.typeVersion);
if (localNodeType === null) return;
const fullNodeIssues = getNodeParametersIssues(localNodeType.properties, node, nodeType ?? null);
let newIssues = null;
if (fullNodeIssues !== null) newIssues = fullNodeIssues.parameters;
workflowState.setNodeIssue({
node: node.name,
type: "parameters",
value: newIssues
});
}
function getNodeInputIssues(workflow, node, nodeType) {
const foundIssues = {};
const workflowNode = workflow.getNode(node.name);
let inputs = [];
if (nodeType && workflowNode) inputs = getNodeInputs(workflow, workflowNode, nodeType);
inputs.forEach((input) => {
if (typeof input === "string" || input.required !== true) return;
if (workflow.getParentNodes(node.name, input.type, 1).length === 0) foundIssues[input.type] = [i18n$1.baseText("nodeIssues.input.missing", { interpolate: { inputName: input.displayName || input.type } })];
});
if (Object.keys(foundIssues).length) return { input: foundIssues };
return null;
}
function getNodeCredentialIssues(node, nodeType) {
const localNodeType = nodeType ?? nodeTypesStore.getNodeType(node.type, node.typeVersion);
if (node.disabled) return null;
if (!localNodeType?.credentials) return null;
const foundIssues = {};
let userCredentials;
let credentialType;
let credentialDisplayName;
let selectedCredentials;
const { authentication, genericAuthType, nodeCredentialType } = node.parameters;
if (authentication === "genericCredentialType" && genericAuthType !== "" && selectedCredsAreUnusable(node, genericAuthType)) {
const credential = credentialsStore.getCredentialTypeByName(genericAuthType);
return credential ? reportUnsetCredential(credential) : null;
}
if (hasProxyAuth(node) && authentication === "predefinedCredentialType" && nodeCredentialType !== "" && node.credentials !== void 0) {
const stored = credentialsStore.getCredentialsByType(nodeCredentialType);
const isCredentialUsedInWorkflow = workflowsStore.usedCredentials?.[node.credentials?.[nodeCredentialType]?.id];
if (selectedCredsDoNotExist(node, nodeCredentialType, stored) && !isCredentialUsedInWorkflow) {
const credential = credentialsStore.getCredentialTypeByName(nodeCredentialType);
return credential ? reportUnsetCredential(credential) : null;
}
}
if (hasProxyAuth(node) && authentication === "predefinedCredentialType" && nodeCredentialType !== "" && selectedCredsAreUnusable(node, nodeCredentialType)) {
const credential = credentialsStore.getCredentialTypeByName(nodeCredentialType);
return credential ? reportUnsetCredential(credential) : null;
}
for (const credentialTypeDescription of localNodeType.credentials) {
if (!displayParameter(node.parameters, credentialTypeDescription, "", node)) continue;
credentialType = credentialsStore.getCredentialTypeByName(credentialTypeDescription.name);
if (!credentialType) credentialDisplayName = credentialTypeDescription.name;
else credentialDisplayName = credentialType.displayName;
if (!node.credentials?.[credentialTypeDescription.name]) {
if (credentialTypeDescription.required) foundIssues[credentialTypeDescription.name] = [i18n$1.baseText("nodeIssues.credentials.notSet", { interpolate: { type: localNodeType.displayName } })];
} else {
selectedCredentials = node.credentials[credentialTypeDescription.name];
if (typeof selectedCredentials === "string") selectedCredentials = {
id: null,
name: selectedCredentials
};
userCredentials = credentialsStore.getCredentialsByType(credentialTypeDescription.name);
if (userCredentials === null) userCredentials = [];
if (selectedCredentials.id) {
if (userCredentials.find((credentialData) => credentialData.id === selectedCredentials.id)) continue;
}
const nameMatches = userCredentials.filter((credentialData) => credentialData.name === selectedCredentials.name);
if (nameMatches.length > 1) {
foundIssues[credentialTypeDescription.name] = [i18n$1.baseText("nodeIssues.credentials.notIdentified", { interpolate: {
name: selectedCredentials.name,
type: credentialDisplayName
} }), i18n$1.baseText("nodeIssues.credentials.notIdentified.hint")];
continue;
}
if (nameMatches.length === 0) {
if (!workflowsStore.usedCredentials?.[selectedCredentials.id] && !hasPermission(["rbac"], { rbac: { scope: "credential:read" } })) foundIssues[credentialTypeDescription.name] = [i18n$1.baseText("nodeIssues.credentials.doNotExist", { interpolate: {
name: selectedCredentials.name,
type: credentialDisplayName
} }), i18n$1.baseText("nodeIssues.credentials.doNotExist.hint")];
}
}
}
if (Object.keys(foundIssues).length === 0) return null;
return { credentials: foundIssues };
}
function selectedCredsAreUnusable(node, credentialType) {
return !node.credentials || !Object.keys(node.credentials).includes(credentialType);
}
function selectedCredsDoNotExist(node, nodeCredentialType, storedCredsByType) {
if (!node.credentials || !storedCredsByType) return false;
const selectedCredsByType = node.credentials[nodeCredentialType];
if (!selectedCredsByType) return false;
return !storedCredsByType.find((c) => c.id === selectedCredsByType.id);
}
function updateNodesCredentialsIssues() {
const nodes$13 = workflowsStore.allNodes;
let issues;
for (const node of nodes$13) {
issues = getNodeCredentialIssues(node);
workflowState.setNodeIssue({
node: node.name,
type: "credentials",
value: issues?.credentials ?? null
});
}
}
function getNodeTaskData(nodeName, runIndex = 0, execution) {
return getAllNodeTaskData(nodeName, execution)?.[runIndex] ?? null;
}
function getAllNodeTaskData(nodeName, execution) {
return (execution?.resultData.runData ?? workflowsStore.getWorkflowRunData)?.[nodeName] ?? null;
}
function hasNodeExecuted(nodeName) {
return getAllNodeTaskData(nodeName)?.some(({ executionStatus }) => executionStatus && ["success", "error"].includes(executionStatus)) ?? false;
}
function getLastRunIndexWithData(nodeName, outputIndex = 0, connectionType = NodeConnectionTypes.Main, execution) {
return (getAllNodeTaskData(nodeName, execution) ?? []).findLastIndex((taskData) => taskData.data && getInputData(taskData.data, outputIndex, connectionType).length > 0);
}
function getNodeInputData(node, runIndex = 0, outputIndex = 0, paneType = "output", connectionType = NodeConnectionTypes.Main, execution) {
if (!node) return [];
const taskData = getNodeTaskData(node.name, runIndex, execution);
if (taskData === null) return [];
let data = taskData.data;
if (paneType === "input" && taskData.inputOverride) data = taskData.inputOverride;
if (!data) return [];
return getInputData(data, outputIndex, connectionType);
}
function getInputData(connectionsData, outputIndex, connectionType = NodeConnectionTypes.Main) {
return connectionsData?.[connectionType]?.[outputIndex] ?? [];
}
function getBinaryData(workflowRunData, node, runIndex, outputIndex, connectionType = NodeConnectionTypes.Main) {
if (node === null) return [];
const runDataOfNode = workflowRunData?.[node]?.[runIndex]?.data;
if (!runDataOfNode) return [];
const inputData = getInputData(runDataOfNode, outputIndex, connectionType);
const returnData = [];
for (let i$1 = 0; i$1 < inputData.length; i$1++) {
const binaryDataInIdx = inputData[i$1]?.binary;
if (binaryDataInIdx !== void 0) returnData.push(binaryDataInIdx);
}
return returnData;
}
function disableNodes(nodes$13, { trackHistory = false, trackBulk = true } = {}) {
const telemetry$1 = useTelemetry();
if (trackHistory && trackBulk) historyStore.startRecordingUndo();
const newDisabledState = nodes$13.some((node) => !node.disabled);
for (const node of nodes$13) {
if (newDisabledState === node.disabled) continue;
const updateInformation = {
name: node.name,
properties: { disabled: newDisabledState }
};
telemetry$1.track("User set node enabled status", {
node_type: node.type,
is_enabled: node.disabled,
workflow_id: workflowsStore.workflowId
});
workflowState.updateNodeProperties(updateInformation);
workflowsStore.clearNodeExecutionData(node.name);
updateNodeParameterIssues(node);
updateNodeCredentialIssues(node);
updateNodesInputIssues();
if (trackHistory) historyStore.pushCommandToUndo(new EnableNodeToggleCommand(node.name, node.disabled === true, newDisabledState, Date.now()));
}
if (trackHistory && trackBulk) historyStore.stopRecordingUndo();
}
function getNodeSubtitle(data, nodeType, workflow) {
if (!data) return;
if (data.notesInFlow) return data.notes;
if (nodeType?.subtitle !== void 0) try {
return workflow.expression.getSimpleParameterValue(data, nodeType.subtitle, "internal", {}, void 0, PLACEHOLDER_FILLED_AT_EXECUTION_TIME);
} catch (e) {
return;
}
if (data.parameters.operation !== void 0) {
const operation = data.parameters.operation;
if (nodeType === null) return operation;
const operationData = nodeType.properties.find((property) => {
return property.name === "operation";
});
if (operationData === void 0) return operation;
if (operationData.options === void 0) return operation;
const optionData = operationData.options.find((option) => {
return option.value === data.parameters.operation;
});
if (optionData === void 0) return operation;
return optionData.name;
}
}
function matchCredentials(node) {
if (!node.credentials) return;
Object.entries(node.credentials).forEach(([nodeCredentialType, nodeCredentials]) => {
const credentialOptions = credentialsStore.getCredentialsByType(nodeCredentialType);
if (typeof nodeCredentials === "string") {
nodeCredentials = {
id: null,
name: nodeCredentials
};
credentialsUpdated.value = true;
}
if (nodeCredentials.id) {
const credentialsId = nodeCredentials.id.toString();
const credentialsForId = credentialOptions.find((optionData) => optionData.id === credentialsId);
if (credentialsForId) {
if (credentialsForId.name !== nodeCredentials.name || typeof nodeCredentials.id === "number") {
node.credentials[nodeCredentialType] = {
id: credentialsForId.id,
name: credentialsForId.name
};
credentialsUpdated.value = true;
}
return;
}
}
node.credentials[nodeCredentialType] = nodeCredentials;
const credentialsForName = credentialOptions.filter((optionData) => optionData.name === nodeCredentials.name);
if (credentialsForName.length === 1) {
node.credentials[nodeCredentialType].id = credentialsForName[0].id;
credentialsUpdated.value = true;
}
});
}
async function loadNodesProperties(nodeInfos) {
const allNodes = nodeTypesStore.allNodeTypes;
const nodesToBeFetched = [];
allNodes.forEach((node) => {
const nodeVersions = Array.isArray(node.version) ? node.version : [node.version];
if (!!nodeInfos.find((n) => n.name === node.name && nodeVersions.includes(n.version)) && !node.hasOwnProperty("properties")) nodesToBeFetched.push({
name: node.name,
version: Array.isArray(node.version) ? node.version.slice(-1)[0] : node.version
});
});
if (nodesToBeFetched.length > 0) {
canvasStore.startLoading();
await nodeTypesStore.getNodesInformation(nodesToBeFetched);
canvasStore.stopLoading();
}
}
function assignNodeId(node) {
const id$1 = window.crypto.randomUUID();
node.id = id$1;
return id$1;
}
function assignWebhookId(node) {
const id$1 = window.crypto.randomUUID();
node.webhookId = id$1;
return id$1;
}
const SINGLE_EXECUTION_NODES = {
"n8n-nodes-base.code": { mode: [void 0, "runOnceForAllItems"] },
"n8n-nodes-base.executeWorkflow": { mode: [void 0, "once"] },
"n8n-nodes-base.crateDb": { operation: [void 0, "update"] },
"n8n-nodes-base.timescaleDb": { operation: [void 0, "update"] },
"n8n-nodes-base.microsoftSql": { operation: [
void 0,
"update",
"delete"
] },
"n8n-nodes-base.questDb": { operation: [void 0] },
"n8n-nodes-base.mongoDb": { operation: ["insert", "update"] },
"n8n-nodes-base.redis": { operation: [void 0] }
};
function isSingleExecution(type, parameters) {
const singleExecutionCase = SINGLE_EXECUTION_NODES[type];
if (singleExecutionCase) {
for (const parameter of Object.keys(singleExecutionCase)) if (!singleExecutionCase[parameter].includes(parameters[parameter])) return false;
return true;
}
return false;
}
function getNodeHints(workflow, node, nodeTypeData, nodeInputData) {
const hints = [];
if (nodeTypeData?.hints?.length) for (const hint of nodeTypeData.hints) if (hint.displayCondition) try {
let display;
if (nodeInputData === void 0) display = workflow.expression.getSimpleParameterValue(node, hint.displayCondition, "internal", {}) || false;
else {
const { runExecutionData, runIndex, connectionInputData: connectionInputData$1 } = nodeInputData;
display = workflow.expression.getParameterValue(hint.displayCondition, runExecutionData ?? null, runIndex, 0, node.name, connectionInputData$1, "manual", {});
}
if (typeof display === "string" && display.trim() === "true") display = true;
if (typeof display !== "boolean") {
console.warn(`Condition was not resolved as boolean in '${node.name}' node for hint: `, hint.message);
continue;
}
if (display) hints.push(hint);
} catch (e) {
console.warn(`Could not calculate display condition in '${node.name}' node for hint: `, hint.message);
}
else hints.push(hint);
return hints;
}
function nodeIssuesToString(issues, node) {
const nodeIssues = [];
if (issues.execution !== void 0) nodeIssues.push("Execution Error.");
const objectProperties = [
"parameters",
"credentials",
"input"
];
let issueText;
let parameterName;
for (const propertyName of objectProperties) if (issues[propertyName] !== void 0) for (parameterName of Object.keys(issues[propertyName])) for (issueText of issues[propertyName][parameterName]) nodeIssues.push(issueText);
if (issues.typeUnknown !== void 0) if (node !== void 0) nodeIssues.push(`Node Type "${node.type}" is not known.`);
else nodeIssues.push("Node Type is not known.");
return nodeIssues;
}
function getDefaultNodeName(node) {
const nodeType = nodeTypesStore.getNodeType(node.type, node.typeVersion);
if (nodeType === null) return null;
return makeNodeName(getNodeParameters(nodeType?.properties, node.parameters ?? {}, true, false, node.typeVersion ? { typeVersion: node.typeVersion } : null, nodeType) ?? {}, nodeType);
}
return {
hasProxyAuth,
isCustomApiCallSelected,
isNodeExecutable,
getForeignCredentialsIfSharingEnabled,
displayParameter,
getNodeIssues,
updateNodesInputIssues,
updateNodesExecutionIssues,
updateNodesParameterIssues,
updateNodeInputIssues,
updateNodeCredentialIssuesByName,
updateNodeCredentialIssues,
updateNodeParameterIssuesByName,
updateNodeParameterIssues,
getBinaryData,
disableNodes,
getNodeSubtitle,
updateNodesCredentialsIssues,
getLastRunIndexWithData,
hasNodeExecuted,
getNodeInputData,
matchCredentials,
isInsertingNodes,
credentialsUpdated,
isProductionExecutionPreview,
pullConnActiveNodeName,
loadNodesProperties,
getNodeTaskData,
assignNodeId,
assignWebhookId,
isSingleExecution,
getNodeHints,
nodeIssuesToString,
getDefaultNodeName
};
}
async function getVariables(context) {
return await makeRestApiRequest(context, "GET", "/variables");
}
async function createVariable(context, data) {
return await makeRestApiRequest(context, "POST", "/variables", data);
}
async function updateVariable(context, { id: id$1,...data }) {
return await makeRestApiRequest(context, "PATCH", `/variables/${id$1}`, data);
}
async function deleteVariable(context, { id: id$1 }) {
return await makeRestApiRequest(context, "DELETE", `/variables/${id$1}`);
}
const useEnvironmentsStore = defineStore("environments", () => {
const rootStore = useRootStore();
const projectStore = useProjectsStore();
const allVariables = ref([]);
const projectId = computed(() => projectStore.currentProject?.id);
const variables = computed(() => allVariables.value.filter((v) => !v.project || !projectId.value || v.project.id === projectId.value));
const scopedVariables = computed(() => allVariables.value.filter((v) => !v.project || !projectId.value && !v.project || v.project.id === projectId.value));
async function fetchAllVariables() {
const data = await getVariables(rootStore.restApiContext);
allVariables.value = data;
return data;
}
async function createVariable$1(variable) {
const data = await createVariable(rootStore.restApiContext, variable);
if (variable.projectId) {
const project = projectStore.availableProjects?.find((p) => p.id === variable.projectId);
if (project) data.project = {
...project,
name: project?.name ?? ""
};
}
allVariables.value.unshift(data);
return data;
}
async function updateVariable$1(variable) {
const data = await updateVariable(rootStore.restApiContext, variable);
if (variable.projectId) {
const project = projectStore.availableProjects?.find((p) => p.id === variable.projectId);
if (project) data.project = {
...project,
name: project?.name ?? ""
};
}
allVariables.value = allVariables.value.map((v) => v.id === data.id ? data : v);
return data;
}
async function deleteVariable$1(variable) {
const data = await deleteVariable(rootStore.restApiContext, { id: variable.id });
allVariables.value = allVariables.value.filter((v) => v.id !== variable.id);
return data;
}
return {
variables,
scopedVariables,
variablesAsObject: computed(() => {
const asObject = scopedVariables.value.reduce((acc, variable) => {
acc[variable.key] = variable.value;
return acc;
}, {});
return new Proxy(asObject, { set() {
throw new ExpressionError("Cannot assign values to variables at runtime");
} });
}),
fetchAllVariables,
createVariable: createVariable$1,
updateVariable: updateVariable$1,
deleteVariable: deleteVariable$1
};
});
var environments_store_default = useEnvironmentsStore;
function isCredentialOnlyNodeType(nodeTypeName) {
return nodeTypeName?.startsWith("n8n-creds-base") ?? false;
}
function getCredentialTypeName(nodeTypeName) {
return nodeTypeName.split(".")[1];
}
function getCredentialOnlyNodeTypeName(credentialTypeName) {
return `${CREDENTIAL_ONLY_NODE_PREFIX}.${credentialTypeName}`;
}
function getCredentialOnlyNodeType(httpNode, credentialType) {
const { httpRequestNode } = credentialType ?? {};
if (!httpNode || !credentialType || !httpRequestNode) return void 0;
const { docsUrl, name: displayName } = httpRequestNode;
const credentialOnlyNode = deepCopy(httpNode);
const httpIcon = httpNode.iconUrl;
credentialOnlyNode.name = getCredentialOnlyNodeTypeName(credentialType.name);
credentialOnlyNode.extendsCredential = credentialType.name;
credentialOnlyNode.displayName = displayName ?? credentialType.displayName;
credentialOnlyNode.description = "HTTP request";
credentialOnlyNode.defaults.name = `${displayName} HTTP Request`;
credentialOnlyNode.codex = {
...credentialOnlyNode.codex,
alias: [],
categories: [],
subcategories: {}
};
credentialOnlyNode.credentials = [{
name: credentialType.name,
required: true
}];
if (credentialType.icon ?? credentialType.iconUrl) {
credentialOnlyNode.icon = credentialType.icon;
credentialOnlyNode.iconUrl = credentialType.iconUrl;
credentialOnlyNode.badgeIconUrl = httpIcon;
} else credentialOnlyNode.iconUrl = httpIcon;
credentialOnlyNode.properties = httpNode.properties.map((prop) => {
switch (prop.name) {
case "authentication": return {
...prop,
type: "hidden",
default: "predefinedCredentialType"
};
case "nodeCredentialType": return {
...prop,
type: "hidden",
default: credentialType.name
};
case "url":
const properties = { ...prop };
if ("apiBaseUrl" in httpRequestNode) {
const { apiBaseUrl } = httpRequestNode;
properties.default = apiBaseUrl;
properties.placeholder = apiBaseUrl ? `e.g. ${apiBaseUrl}` : prop.placeholder;
} else properties.placeholder = httpRequestNode.apiBaseUrlPlaceholder;
return properties;
default: return prop;
}
});
credentialOnlyNode.properties.splice(1, 0, {
type: "notice",
displayName: i18n.baseText("ndv.httpRequest.credentialOnly.docsNotice", { interpolate: {
nodeName: displayName,
docsUrl
} }),
name: "httpVariantWarning",
default: ""
});
credentialOnlyNode.properties.splice(4, 0, {
type: "credentials",
displayName: "",
name: "",
default: ""
});
return credentialOnlyNode;
}
function createTagsApi(endpoint) {
return {
getTags: async (context, data) => {
return await makeRestApiRequest(context, "GET", endpoint, data);
},
createTag: async (context, data) => {
return await makeRestApiRequest(context, "POST", endpoint, data);
},
updateTag: async (context, id$1, data) => {
return await makeRestApiRequest(context, "PATCH", `${endpoint}/${id$1}`, data);
},
deleteTag: async (context, id$1) => {
return await makeRestApiRequest(context, "DELETE", `${endpoint}/${id$1}`);
}
};
}
var apiMapping = {
[STORES.TAGS]: createTagsApi("/tags"),
[STORES.ANNOTATION_TAGS]: createTagsApi("/annotation-tags")
};
var createTagsStore = (id$1) => {
const tagsApi = apiMapping[id$1];
return defineStore(id$1, () => {
const tagsById = ref({});
const loading = ref(false);
const fetchedAll = ref(false);
const fetchedUsageCount = ref(false);
const rootStore = useRootStore();
const workflowsStore = useWorkflowsStore();
const allTags = computed(() => {
return Object.values(tagsById.value).sort((a, b) => a.name.localeCompare(b.name));
});
const isLoading = computed(() => loading.value);
const hasTags = computed(() => Object.keys(tagsById.value).length > 0);
const setAllTags = (loadedTags) => {
tagsById.value = loadedTags.reduce((accu, tag) => {
accu[tag.id] = tag;
return accu;
}, {});
fetchedAll.value = true;
};
const upsertTags = (toUpsertTags) => {
toUpsertTags.forEach((toUpsertTag) => {
const tagId = toUpsertTag.id;
const currentTag = tagsById.value[tagId];
if (currentTag) {
const newTag = {
...currentTag,
...toUpsertTag
};
tagsById.value = {
...tagsById.value,
[tagId]: newTag
};
} else tagsById.value = {
...tagsById.value,
[tagId]: toUpsertTag
};
});
};
const deleteTag = (id$2) => {
const { [id$2]: deleted,...rest } = tagsById.value;
tagsById.value = rest;
};
const fetchAll = async (params) => {
const { force = false, withUsageCount = false } = params || {};
if (!force && fetchedAll.value && fetchedUsageCount.value === withUsageCount) return Object.values(tagsById.value);
loading.value = true;
const retrievedTags = await tagsApi.getTags(rootStore.restApiContext, { withUsageCount });
setAllTags(retrievedTags);
loading.value = false;
return retrievedTags;
};
const create$1 = async (name$13, { incrementExisting } = {}) => {
let tagName = name$13;
if (incrementExisting) {
const tagNameRegex = new RegExp(tagName);
const existingTags = allTags.value.filter((tag) => tagNameRegex.test(tag.name));
if (existingTags.length > 0) tagName = `${tagName} (${existingTags.length + 1})`;
}
const createdTag = await tagsApi.createTag(rootStore.restApiContext, { name: tagName });
upsertTags([createdTag]);
return createdTag;
};
const rename = async ({ id: id$2, name: name$13 }) => {
const updatedTag = await tagsApi.updateTag(rootStore.restApiContext, id$2, { name: name$13 });
upsertTags([updatedTag]);
return updatedTag;
};
const deleteTagById = async (id$2) => {
const deleted = await tagsApi.deleteTag(rootStore.restApiContext, id$2);
if (deleted) {
deleteTag(id$2);
workflowsStore.removeWorkflowTagId(id$2);
}
return deleted;
};
return {
allTags,
isLoading,
hasTags,
tagsById,
fetchAll,
create: create$1,
rename,
deleteTagById,
upsertTags,
deleteTag
};
}, {});
};
const useTagsStore = createTagsStore(STORES.TAGS);
const useAnnotationTagsStore = createTagsStore(STORES.ANNOTATION_TAGS);
const useWorkflowsEEStore = defineStore(STORES.WORKFLOWS_EE, () => {
const rootStore = useRootStore();
const settingsStore = useSettingsStore();
const workflowStore = useWorkflowsStore();
const getWorkflowOwnerName = computed(() => {
return (workflowId, fallback = i18n.baseText("workflows.shareModal.info.sharee.fallback")) => {
const { name: name$13, email } = splitName(workflowStore.getWorkflowById(workflowId)?.homeProject?.name ?? "");
const trimmedName = name$13?.replace(/\s+/g, " ")?.trim();
return trimmedName ? email ? `${trimmedName} (${email})` : trimmedName : email ?? fallback;
};
});
const setWorkflowSharedWith$1 = (payload) => {
const workflowsStore = useWorkflowsStore();
workflowsStore.workflowsById[payload.workflowId] = {
...workflowsStore.workflowsById[payload.workflowId],
sharedWithProjects: payload.sharedWithProjects
};
workflowsStore.workflow = {
...workflowsStore.workflow,
sharedWithProjects: payload.sharedWithProjects
};
};
const saveWorkflowSharedWith = async (payload) => {
if (settingsStore.isEnterpriseFeatureEnabled[EnterpriseEditionFeature.Sharing]) {
await setWorkflowSharedWith(rootStore.restApiContext, payload.workflowId, { shareWithIds: payload.sharedWithProjects.map((p) => p.id) });
setWorkflowSharedWith$1(payload);
}
};
return {
getWorkflowOwnerName,
setWorkflowSharedWith: setWorkflowSharedWith$1,
saveWorkflowSharedWith
};
});
var import_get = /* @__PURE__ */ __toESM(require_get());
function resolveParameter(parameter, opts = {}) {
if ("localResolve" in opts && opts.localResolve) return resolveParameterImpl(parameter, opts.workflow, opts.connections, opts.envVars, opts.workflow.getNode(opts.nodeName), opts.execution, opts.workflow.pinData, {
inputNodeName: opts.inputNode?.name,
inputRunIndex: opts.inputNode?.runIndex,
inputBranchIndex: opts.inputNode?.branchIndex,
additionalKeys: opts.additionalKeys
});
const workflowsStore = useWorkflowsStore();
return resolveParameterImpl(parameter, workflowsStore.workflowObject, workflowsStore.connectionsBySourceNode, useEnvironmentsStore().variablesAsObject, useNDVStore().activeNode, workflowsStore.workflowExecutionData, workflowsStore.pinnedWorkflowData, opts);
}
function resolveParameterImpl(parameter, workflowObject, connections$13, envVars, ndvActiveNode, executionData, pinData$12, opts = {}) {
let itemIndex = opts?.targetItem?.itemIndex || 0;
const additionalKeys = {
$execution: {
id: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
mode: "test",
resumeUrl: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
resumeFormUrl: PLACEHOLDER_FILLED_AT_EXECUTION_TIME
},
$vars: envVars,
$executionId: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
$resumeWebhookUrl: PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
...opts.additionalKeys
};
if (opts.isForCredential) return workflowObject.expression.getParameterValue(parameter, null, 0, itemIndex, "", [], "manual", additionalKeys, void 0, false, void 0, "");
const inputName = NodeConnectionTypes.Main;
const activeNode = ndvActiveNode ?? workflowObject.getNode(opts.contextNodeName || "");
let contextNode = activeNode;
if (activeNode) contextNode = workflowObject.getParentMainInputNode(activeNode) ?? null;
const workflowRunData = executionData?.data?.resultData.runData ?? null;
let parentNode = workflowObject.getParentNodes(contextNode.name, inputName, 1);
let runIndexParent = opts?.inputRunIndex ?? 0;
const nodeConnection = workflowObject.getNodeConnectionIndexes(contextNode.name, parentNode[0]);
if (opts.targetItem && opts?.targetItem?.nodeName === contextNode.name && executionData) {
const sourceItems = getSourceItems(executionData, opts.targetItem);
if (!sourceItems.length) return null;
parentNode = [sourceItems[0].nodeName];
runIndexParent = sourceItems[0].runIndex;
itemIndex = sourceItems[0].itemIndex;
if (nodeConnection) nodeConnection.sourceIndex = sourceItems[0].outputIndex;
} else {
parentNode = opts.inputNodeName ? [opts.inputNodeName] : parentNode;
if (nodeConnection) nodeConnection.sourceIndex = opts.inputBranchIndex ?? nodeConnection.sourceIndex;
if (opts?.inputRunIndex === void 0 && workflowRunData !== null && parentNode.length) {
const firstParentWithWorkflowRunData = parentNode.find((parentNodeName) => workflowRunData[parentNodeName]);
if (firstParentWithWorkflowRunData) runIndexParent = workflowRunData[firstParentWithWorkflowRunData].length - 1;
}
}
let _connectionInputData = connectionInputData(connections$13, parentNode, contextNode.name, inputName, runIndexParent, pinData$12, executionData?.data?.resultData.runData ?? null, nodeConnection);
if (_connectionInputData === null && contextNode && activeNode?.name !== contextNode.name) _connectionInputData = (0, import_get.default)(executeDataImpl(connections$13, [contextNode.name], contextNode.name, inputName, 0, pinData$12, executionData?.data?.resultData.runData ?? null), [
"data",
inputName,
0
], null);
const runExecutionData = executionData?.data ?? createEmptyRunExecutionData();
if (_connectionInputData === null) _connectionInputData = [];
if (activeNode?.type === "n8n-nodes-base.httpRequest") {
const EMPTY_RESPONSE = {
statusCode: 200,
headers: {},
body: {}
};
const EMPTY_REQUEST = {
headers: {},
body: {},
qs: {}
};
additionalKeys.$pageCount = 0;
additionalKeys.$response = (0, import_get.default)(executionData, [
"data",
"executionData",
"contextData",
`node:${activeNode.name}`,
"response"
], EMPTY_RESPONSE);
additionalKeys.$request = EMPTY_REQUEST;
}
let runIndexCurrent = opts?.targetItem?.runIndex ?? 0;
if (opts?.targetItem === void 0 && workflowRunData !== null && workflowRunData[contextNode.name]) runIndexCurrent = workflowRunData[contextNode.name].length - 1;
let _executeData = executeDataImpl(connections$13, parentNode, contextNode.name, inputName, runIndexCurrent, pinData$12, executionData?.data?.resultData.runData ?? null, runIndexParent);
if (!_executeData.source) _executeData = executeDataImpl(connections$13, parentNode, contextNode.name, inputName, runIndexParent, pinData$12, executionData?.data?.resultData.runData ?? null);
return workflowObject.expression.getParameterValue(parameter, runExecutionData, runIndexCurrent, itemIndex, activeNode.name, _connectionInputData, "manual", additionalKeys, _executeData, false, {}, contextNode.name);
}
function resolveRequiredParameters(currentParameter, parameters, opts = {}) {
const loadOptionsDependsOn = new Set(currentParameter?.typeOptions?.loadOptionsDependsOn ?? []);
return Object.fromEntries(Object.entries(parameters).map(([name$13, parameter]) => {
if (loadOptionsDependsOn.has(name$13)) return [name$13, resolveParameter(parameter, opts)];
else try {
return [name$13, resolveParameter(parameter, opts)];
} catch (error) {
return [name$13, null];
}
}));
}
function getConnectedNodes(direction, workflow, nodeName) {
let checkNodes;
if (direction === "downstream") checkNodes = workflow.getChildNodes(nodeName);
else if (direction === "upstream") checkNodes = workflow.getParentNodes(nodeName);
else throw new Error(`The direction "${direction}" is not supported!`);
let connectedNodes = [];
checkNodes.forEach((checkNode) => {
connectedNodes = [
...connectedNodes,
checkNode,
...workflow.getParentNodes(checkNode, "ALL_NON_MAIN")
];
});
return [...new Set(connectedNodes)];
}
function getNodeTypes() {
return useWorkflowsStore().getNodeTypes();
}
function connectionInputData(connections$13, parentNode, currentNode, inputName, runIndex, pinData$12, workflowRunData, nodeConnection = {
sourceIndex: 0,
destinationIndex: 0
}) {
let connectionInputData$1 = null;
const _executeData = executeDataImpl(connections$13, parentNode, currentNode, inputName, runIndex, pinData$12, workflowRunData);
if (parentNode.length) if (!Object.keys(_executeData.data).length || _executeData.data[inputName].length <= nodeConnection.sourceIndex) connectionInputData$1 = [];
else {
connectionInputData$1 = _executeData.data[inputName][nodeConnection.sourceIndex];
if (connectionInputData$1 !== null) connectionInputData$1 = connectionInputData$1.map((item, itemIndex) => {
return {
...item,
pairedItem: {
item: itemIndex,
input: nodeConnection.destinationIndex
}
};
});
}
return connectionInputData$1;
}
function executeData(connections$13, parentNodes, currentNode, inputName, runIndex, parentRunIndex) {
const workflowsStore = useWorkflowsStore();
return executeDataImpl(connections$13, parentNodes, currentNode, inputName, runIndex, workflowsStore.pinnedWorkflowData, workflowsStore.getWorkflowRunData, parentRunIndex);
}
function executeDataImpl(connections$13, parentNodes, currentNode, inputName, runIndex, pinData$12, workflowRunData, parentRunIndex) {
const connectionsByDestinationNode = mapConnectionsByDestination(connections$13);
const executeData$1 = {
node: {},
data: {},
source: null
};
parentRunIndex = parentRunIndex ?? runIndex;
for (const parentNodeName of parentNodes) {
const parentPinData = pinData$12?.[parentNodeName];
if (parentPinData) {
executeData$1.data = { main: [parentPinData] };
executeData$1.source = { main: [{ previousNode: parentNodeName }] };
return executeData$1;
}
if (workflowRunData === null) return executeData$1;
if (!workflowRunData[parentNodeName] || workflowRunData[parentNodeName].length <= parentRunIndex || !workflowRunData[parentNodeName][parentRunIndex] || !workflowRunData[parentNodeName][parentRunIndex].hasOwnProperty("data") || !workflowRunData[parentNodeName][parentRunIndex].data?.hasOwnProperty(inputName)) executeData$1.data = {};
else {
executeData$1.data = workflowRunData[parentNodeName][parentRunIndex].data;
if (workflowRunData[currentNode] && workflowRunData[currentNode][runIndex]) executeData$1.source = { [inputName]: workflowRunData[currentNode][runIndex].source };
else {
let previousNodeOutput;
if (connectionsByDestinationNode[currentNode]?.main) {
mainConnections: for (const mainConnections of connectionsByDestinationNode[currentNode].main) for (const connection of mainConnections ?? []) if (connection.type === NodeConnectionTypes.Main && connection.node === parentNodeName) {
previousNodeOutput = connection.index;
break mainConnections;
}
}
executeData$1.source = { [inputName]: [{
previousNode: parentNodeName,
previousNodeOutput,
previousNodeRun: parentRunIndex
}] };
}
return executeData$1;
}
}
return executeData$1;
}
function useWorkflowHelpers() {
const nodeTypesStore = useNodeTypesStore();
const rootStore = useRootStore();
const workflowsStore = useWorkflowsStore();
const workflowState = injectWorkflowState();
const workflowsEEStore = useWorkflowsEEStore();
const uiStore = useUIStore();
const nodeHelpers = useNodeHelpers();
const projectsStore = useProjectsStore();
const tagsStore = useTagsStore();
const i18n$1 = useI18n();
function getNodeTypesMaxCount() {
const nodes$13 = workflowsStore.allNodes;
const returnData = {};
const nodeTypes = nodeTypesStore.allNodeTypes;
for (const nodeType of nodeTypes) if (nodeType.maxNodes !== void 0) returnData[nodeType.name] = {
exist: 0,
max: nodeType.maxNodes,
nodeNames: []
};
for (const node of nodes$13) if (returnData[node.type] !== void 0) {
returnData[node.type].exist += 1;
returnData[node.type].nodeNames.push(node.name);
}
return returnData;
}
function getNodeTypeCount(nodeType) {
const nodes$13 = workflowsStore.allNodes;
let count = 0;
for (const node of nodes$13) if (node.type === nodeType) count++;
return count;
}
async function getWorkflowDataToSave() {
const workflowNodes = workflowsStore.allNodes;
const workflowConnections = workflowsStore.allConnections;
let nodeData;
const nodes$13 = [];
for (let nodeIndex = 0; nodeIndex < workflowNodes.length; nodeIndex++) {
nodeData = getNodeDataToSave(workflowNodes[nodeIndex]);
nodes$13.push(nodeData);
}
const data = {
name: workflowsStore.workflowName,
nodes: nodes$13,
pinData: workflowsStore.pinnedWorkflowData,
connections: workflowConnections,
active: workflowsStore.isWorkflowActive,
settings: workflowsStore.workflow.settings,
tags: workflowsStore.workflowTags,
versionId: workflowsStore.workflow.versionId,
meta: workflowsStore.workflow.meta
};
const workflowId = workflowsStore.workflowId;
if (workflowId !== "__EMPTY__") data.id = workflowId;
return data;
}
function getNodeDataToSave(node) {
const skipKeys = [
"color",
"continueOnFail",
"credentials",
"disabled",
"issues",
"onError",
"notes",
"parameters",
"status"
];
const nodeData = { parameters: {} };
for (const key in node) if (key.charAt(0) !== "_" && skipKeys.indexOf(key) === -1) nodeData[key] = node[key];
const nodeType = nodeTypesStore.getNodeType(node.type, node.typeVersion);
if (nodeType !== null) {
const isCredentialOnly = isCredentialOnlyNodeType(nodeType.name);
if (isCredentialOnly) {
nodeData.type = HTTP_REQUEST_NODE_TYPE;
nodeData.extendsCredential = getCredentialTypeName(nodeType.name);
}
const nodeParameters = getNodeParameters(nodeType.properties, node.parameters, isCredentialOnly, false, node, nodeType);
nodeData.parameters = nodeParameters !== null ? nodeParameters : {};
if (node.credentials !== void 0 && nodeType.credentials !== void 0) {
const saveCredentials = {};
for (const nodeCredentialTypeName of Object.keys(node.credentials)) {
if (nodeHelpers.hasProxyAuth(node) || Object.keys(node.parameters).includes("genericAuthType")) {
saveCredentials[nodeCredentialTypeName] = node.credentials[nodeCredentialTypeName];
continue;
}
const credentialTypeDescription = nodeType.credentials.filter((c) => nodeHelpers.displayParameter(node.parameters, c, "", node)).find((c) => c.name === nodeCredentialTypeName);
if (credentialTypeDescription === void 0) continue;
if (!nodeHelpers.displayParameter(node.parameters, credentialTypeDescription, "", node)) continue;
saveCredentials[nodeCredentialTypeName] = node.credentials[nodeCredentialTypeName];
}
if (Object.keys(saveCredentials).length !== 0) nodeData.credentials = saveCredentials;
}
} else {
nodeData.credentials = node.credentials;
nodeData.parameters = node.parameters;
if (nodeData.color !== void 0) nodeData.color = node.color;
}
if (node.disabled === true) nodeData.disabled = true;
if (node.continueOnFail === true) nodeData.continueOnFail = true;
if (node.onError !== "stopWorkflow") nodeData.onError = node.onError;
if (![void 0, ""].includes(node.notes)) nodeData.notes = node.notes;
return nodeData;
}
function getWebhookExpressionValue(webhookData, key, stringify = true, nodeName) {
if (webhookData[key] === void 0) return "empty";
try {
return resolveExpression(webhookData[key], void 0, { contextNodeName: nodeName }, stringify);
} catch (e) {
return i18n$1.baseText("nodeWebhooks.invalidExpression");
}
}
function getWebhookUrl(webhookData, node, showUrlFor) {
const { nodeType, restartWebhook } = webhookData;
if (restartWebhook === true) return nodeType === "form" ? "$execution.resumeFormUrl" : "$execution.resumeUrl";
const baseUrl = {
test: {
form: rootStore.formTestUrl,
mcp: rootStore.mcpTestUrl,
webhook: rootStore.webhookTestUrl
},
production: {
form: rootStore.formUrl,
mcp: rootStore.mcpUrl,
webhook: rootStore.webhookUrl
}
}[showUrlFor][nodeType ?? "webhook"];
const workflowId = workflowsStore.workflowId;
return getNodeWebhookUrl(baseUrl, workflowId, node, getWebhookExpressionValue(webhookData, "path", true, node.name) ?? "", getWebhookExpressionValue(webhookData, "isFullPath", true, node.name) || false);
}
function getNodeParametersWithResolvedExpressions(nodeParameters) {
function recurse(currentObj, currentPath) {
const newObj = {};
for (const key in currentObj) {
const value = currentObj[key];
const path = currentPath ? `${currentPath}.${key}` : key;
if (typeof value === "object" && value !== null) newObj[key] = recurse(value, path);
else if (typeof value === "string" && String(value).startsWith("=")) {
let resolved;
try {
resolved = resolveExpression(value, void 0, { isForCredential: false });
} catch (error) {
resolved = `Error in expression: "${error.message}"`;
}
newObj[key] = {
value,
resolvedExpressionValue: String(resolved)
};
} else newObj[key] = value;
}
return newObj;
}
return recurse(nodeParameters, "");
}
function resolveExpression(expression, siblingParameters = {}, opts = {}, stringifyObject = true) {
const returnData = resolveParameter({
__xxxxxxx__: expression,
...siblingParameters
}, opts);
if (!returnData) return null;
const obj = returnData.__xxxxxxx__;
if (typeof obj === "object" && stringifyObject) {
const proxy = obj;
if (proxy?.isProxy && proxy.toJSON) return JSON.stringify(proxy.toJSON());
return workflowsStore.workflowObject.expression.convertObjectValueToString(obj);
}
return obj;
}
async function updateWorkflow({ workflowId, active: active$7 }, partialData = false) {
let data = {};
const isCurrentWorkflow = workflowId === workflowsStore.workflowId;
if (isCurrentWorkflow) data = partialData ? { versionId: workflowsStore.workflowVersionId } : await getWorkflowDataToSave();
else {
const { versionId } = await workflowsStore.fetchWorkflow(workflowId);
data.versionId = versionId;
}
if (active$7 !== void 0) data.active = active$7;
const workflow = await workflowsStore.updateWorkflow(workflowId, data);
workflowsStore.setWorkflowVersionId(workflow.versionId);
if (isCurrentWorkflow) {
workflowState.setActive(workflow.activeVersionId);
uiStore.stateIsDirty = false;
}
if (workflow.activeVersionId !== null) workflowsStore.setWorkflowActive(workflowId);
else workflowsStore.setWorkflowInactive(workflowId);
}
function updateNodePositions(workflowData, position) {
if (workflowData.nodes === void 0) return;
const minPosition = [99999999, 99999999];
for (const node of workflowData.nodes) if (node.position[1] < minPosition[1]) {
minPosition[0] = node.position[0];
minPosition[1] = node.position[1];
} else if (node.position[1] === minPosition[1]) {
if (node.position[0] < minPosition[0]) {
minPosition[0] = node.position[0];
minPosition[1] = node.position[1];
}
}
const offsetPosition = [position[0] - minPosition[0], position[1] - minPosition[1]];
for (const node of workflowData.nodes) {
node.position[0] += offsetPosition[0];
node.position[1] += offsetPosition[1];
}
}
function removeForeignCredentialsFromWorkflow(workflow, usableCredentials) {
(workflow.nodes ?? []).forEach((node) => {
if (!node.credentials) return;
node.credentials = Object.entries(node.credentials).reduce((acc, [credentialType, credential]) => {
const isUsableCredential = usableCredentials.some((ownCredential) => `${ownCredential.id}` === `${credential.id}`);
if (credential.id && isUsableCredential) acc[credentialType] = node.credentials[credentialType];
return acc;
}, {});
});
}
function getWorkflowProjectRole(workflowId) {
const workflow = workflowsStore.workflowsById[workflowId];
if (workflow?.homeProject?.id === projectsStore.personalProject?.id || workflowId === "__EMPTY__") return "owner";
else if (workflow?.sharedWithProjects?.some((project) => project.id === projectsStore.personalProject?.id)) return "sharee";
else return "member";
}
function initState(workflowData) {
workflowsStore.addWorkflow(workflowData);
workflowState.setActive(workflowData.activeVersionId);
workflowsStore.setIsArchived(workflowData.isArchived);
workflowsStore.setDescription(workflowData.description);
workflowState.setWorkflowId(workflowData.id);
workflowState.setWorkflowName({
newName: workflowData.name,
setStateDirty: uiStore.stateIsDirty
});
workflowState.setWorkflowSettings(workflowData.settings ?? {});
workflowsStore.setWorkflowPinData(workflowData.pinData ?? {});
workflowsStore.setWorkflowVersionId(workflowData.versionId);
workflowsStore.setWorkflowMetadata(workflowData.meta);
workflowsStore.setWorkflowScopes(workflowData.scopes);
if (workflowData.usedCredentials) workflowsStore.setUsedCredentials(workflowData.usedCredentials);
if (workflowData.sharedWithProjects) workflowsEEStore.setWorkflowSharedWith({
workflowId: workflowData.id,
sharedWithProjects: workflowData.sharedWithProjects
});
const tags$7 = workflowData.tags ?? [];
const tagIds = tags$7.map((tag) => tag.id);
workflowState.setWorkflowTagIds(tagIds || []);
tagsStore.upsertTags(tags$7);
}
const containsNodeFromPackage = (workflow, packageName) => {
return workflow.nodes.some((node) => node.type.startsWith(packageName));
};
function getMethods(trigger) {
if (trigger.type === "n8n-nodes-base.webhook") {
if (trigger.parameters.multipleMethods === true) return trigger.parameters.httpMethod ?? ["GET", "POST"];
return [trigger.parameters.httpMethod ?? "GET"];
}
return ["POST"];
}
function getWebhookPath(trigger) {
if (trigger.type === "n8n-nodes-base.webhook") return trigger.parameters.path || trigger.webhookId;
if (trigger.type === "n8n-nodes-base.formTrigger") return (trigger.parameters.options || {}).path ?? trigger.webhookId;
if (trigger.type === "@n8n/n8n-nodes-langchain.chatTrigger") return `${trigger.webhookId}/chat`;
return `${trigger.webhookId}/webhook`;
}
async function checkConflictingWebhooks(workflowId) {
let data;
if (uiStore.stateIsDirty) data = await getWorkflowDataToSave();
else data = await workflowsStore.fetchWorkflow(workflowId);
const triggers = data.nodes.filter((node) => node.disabled !== true && node.webhookId && (node.type.toLowerCase().includes("trigger") || node.type === "n8n-nodes-base.webhook"));
for (const trigger of triggers) {
const methods = getMethods(trigger);
const path = getWebhookPath(trigger);
for (const method of methods) {
const conflict = await findWebhook(rootStore.restApiContext, {
path,
method
});
if (conflict && conflict.workflowId !== workflowId) return {
trigger,
conflict
};
}
}
return null;
}
return {
resolveParameter,
resolveRequiredParameters,
getConnectedNodes,
getNodeTypes,
connectionInputData,
executeData,
getNodeTypesMaxCount,
getNodeTypeCount,
getWorkflowDataToSave,
getNodeDataToSave,
getWebhookExpressionValue,
getWebhookUrl,
resolveExpression,
updateWorkflow,
updateNodePositions,
removeForeignCredentialsFromWorkflow,
getWorkflowProjectRole,
initState,
getNodeParametersWithResolvedExpressions,
containsNodeFromPackage,
checkConflictingWebhooks
};
}
const unwrapExpression = (expr) => {
return expr.replace(/\{\{(.*)\}\}/, "$1").trim();
};
const removeExpressionPrefix = (expr) => {
return isExpression(expr) ? expr.slice(1) : expr ?? "";
};
const isTestableExpression = (expr) => {
return splitExpression(expr).every((c) => {
if (c.type === "text") return true;
return /\$secrets(\.[a-zA-Z0-9_]+)+$/.test(c.text.trim());
});
};
const isNoExecDataExpressionError = (error) => {
return error instanceof ExpressionError && error.context.type === "no_execution_data";
};
const isNoNodeExecDataExpressionError = (error) => {
return error instanceof ExpressionError && error.context.type === "no_node_execution_data";
};
const isPairedItemIntermediateNodesError = (error) => {
return error instanceof ExpressionError && error.context.type === "paired_item_intermediate_nodes";
};
const isPairedItemNoConnectionError = (error) => {
return error instanceof ExpressionError && error.context.type === "paired_item_no_connection";
};
const isInvalidPairedItemError = (error) => {
return error instanceof ExpressionError && error.context.type === "paired_item_invalid_info";
};
const isNoPairedItemError = (error) => {
return error instanceof ExpressionError && error.context.type === "paired_item_no_info";
};
const isNoInputConnectionError = (error) => {
return error instanceof ExpressionError && error.context.type === "no_input_connection";
};
const isAnyPairedItemError = (error) => {
return error instanceof ExpressionError && error.functionality === "pairedItem";
};
const getResolvableState = (error, ignoreError = false) => {
if (!error) return "valid";
if (isNoExecDataExpressionError(error) || isNoNodeExecDataExpressionError(error) || isPairedItemIntermediateNodesError(error) || ignoreError) return "pending";
return "invalid";
};
const getExpressionErrorMessage = (error, nodeHasRunData = false) => {
if (isNoExecDataExpressionError(error) || isPairedItemIntermediateNodesError(error)) return i18n.baseText("expressionModalInput.noExecutionData");
if (isNoNodeExecDataExpressionError(error)) {
const nodeCause = error.context.nodeCause;
return i18n.baseText("expressionModalInput.noNodeExecutionData", { interpolate: { node: nodeCause } });
}
if (isNoInputConnectionError(error)) return i18n.baseText("expressionModalInput.noInputConnection");
if (isPairedItemNoConnectionError(error)) return i18n.baseText("expressionModalInput.pairedItemConnectionError");
if (isInvalidPairedItemError(error) || isNoPairedItemError(error)) {
const nodeCause = error.context.nodeCause;
if (!!useWorkflowsStore().pinDataByNodeName(nodeCause)) return i18n.baseText("expressionModalInput.pairedItemInvalidPinnedError", { interpolate: { node: nodeCause } });
}
if (isAnyPairedItemError(error)) return nodeHasRunData ? i18n.baseText("expressionModalInput.pairedItemError") : i18n.baseText("expressionModalInput.pairedItemError.noRunData");
return error.message;
};
const stringifyExpressionResult = (result, nodeHasRunData = false) => {
if (!result.ok) {
if (getResolvableState(result.error) !== "invalid") return "";
return `[${i18n.baseText("parameterInput.error")}: ${getExpressionErrorMessage(result.error, nodeHasRunData)}]`;
}
if (result.result === null) return "";
if (typeof result.result === "string" && result.result.length === 0) return i18n.baseText("parameterInput.emptyString");
return typeof result.result === "string" ? result.result : String(result.result);
};
const completeExpressionSyntax = (value, isSpecializedEditor = false) => {
if (isSpecializedEditor) return value;
if (typeof value === "string" && !value.startsWith("=")) {
if (value.endsWith("{{ ")) return "=" + value + " }}";
if (value.endsWith("{{$")) return "=" + value.slice(0, -1) + " $ }}";
}
return value;
};
const shouldConvertToExpression = (value, isSpecializedEditor = false) => {
if (isSpecializedEditor) return false;
return typeof value === "string" && !value.startsWith("=") && value.includes("{{") && value.includes("}}");
};
var validJsIdNameRegex = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
function isValidJsIdentifierName(name$13) {
return validJsIdNameRegex.test(name$13.toString());
}
function generatePath(root$1, path) {
return path.reduce((accu, part) => {
if (typeof part === "number") return `${accu}[${part}]`;
if (!isValidJsIdentifierName(part)) return `${accu}['${escapeMappingString(part)}']`;
return `${accu}.${part}`;
}, root$1);
}
function escapeMappingString(str) {
return str.replace(/\'/g, "\\'");
}
function getMappedExpression({ nodeName, distanceFromActive, path }) {
const root$1 = getNodeParentExpression({
nodeName,
distanceFromActive
});
if (typeof path === "string") return `{{ ${root$1}${path} }}`;
return `{{ ${generatePath(root$1, path)} }}`;
}
function getNodeParentExpression({ nodeName, distanceFromActive }) {
return distanceFromActive === 1 ? "$json" : generatePath(`$('${escapeMappingString(nodeName)}')`, ["item", "json"]);
}
var unquote = (str) => {
if (str.startsWith("\"") && str.endsWith("\"")) return str.slice(1, -1).replace(/\\"/g, "\"");
if (str.startsWith("'") && str.endsWith("'")) return str.slice(1, -1).replace(/\\'/g, "'");
return str;
};
function propertyNameFromExpression(expression, forceBracketAccess = false) {
const propPath = expression.replace(/^{{\s*|\s*}}$/g, "").replace(/^(\$\(.*\)\.item\.json|\$json|\$node\[.*\]\.json)\.?(.*)/, "$2");
if (propPath.startsWith("[") && !propPath.slice(1).includes("[") && !forceBracketAccess) return unquote(propPath.slice(1, -1));
return propPath;
}
function getMappedResult(parameter, newParamValue, prevParamValue) {
const prevValue = parameter.type === "resourceLocator" && isResourceLocatorValue(prevParamValue) ? prevParamValue.value : prevParamValue;
if (parameter.requiresDataPath) {
if (parameter.requiresDataPath === "multiple") {
const propertyName = propertyNameFromExpression(newParamValue, true);
if (typeof prevValue === "string" && (prevValue.trim() === "=" || prevValue.trim() === "")) return propertyName;
return `${prevValue}, ${propertyName}`;
}
return propertyNameFromExpression(newParamValue);
} else if (typeof prevValue === "string" && isExpression(prevValue) && prevValue.length > 1) return `${prevValue} ${newParamValue}`;
else if (prevValue && ["string", "json"].includes(parameter.type)) return prevValue === "=" || typeof prevValue === "object" ? `=${newParamValue}` : `=${prevValue} ${newParamValue}`;
return `=${newParamValue}`;
}
var import_merge = /* @__PURE__ */ __toESM(require_merge());
function useDataSchema() {
function getSchema(input, path = "", excludeValues = false, collapseArrays = false) {
let schema = {
type: "undefined",
value: "undefined",
path
};
switch (typeof input) {
case "object":
if (input === null) schema = {
type: "null",
value: "[null]",
path
};
else if (input instanceof Date) schema = {
type: "string",
value: input.toISOString(),
path
};
else if (Array.isArray(input)) schema = {
type: "array",
value: collapseArrays && input.length > 0 ? [{
key: "0",
...getSchema(input.every((item) => isObj(item)) ? (0, import_merge.default)({}, ...input) : input[0], `${path}[0]`, excludeValues, collapseArrays)
}] : input.map((item, index) => ({
key: index.toString(),
...getSchema(item, `${path}[${index}]`, excludeValues, collapseArrays)
})),
path
};
else if (isObj(input)) schema = {
type: "object",
value: Object.entries(input).map(([k, v]) => ({
key: k,
...getSchema(v, generatePath(path, [k]), excludeValues, collapseArrays)
})),
path
};
break;
case "function":
schema = {
type: "function",
value: "",
path
};
break;
default: schema = {
type: typeof input,
value: excludeValues ? "" : String(input),
path
};
}
return schema;
}
function getSchemaForExecutionData(data, excludeValues = false, collapseArrays = false) {
const [head, ...tail] = data;
return getSchema((0, import_merge.default)({}, head, ...tail, head), void 0, excludeValues, collapseArrays);
}
function getSchemaForJsonSchema(schema, path = "") {
if (typeof schema !== "object") return {
type: "null",
path,
value: "null"
};
if (schema.type === "array") return {
type: "array",
value: isObject$1(schema.items) ? [{
...getSchemaForJsonSchema(schema.items, `${path}[0]`),
key: "0"
}] : [],
path
};
if (schema.type === "object") {
const properties = schema.properties ?? {};
return {
type: "object",
value: Object.entries(properties).map(([key, propSchema]) => {
return {
...getSchemaForJsonSchema(propSchema, path ? `${path}.${key}` : `.${key}`),
key
};
}),
path
};
}
return {
type: JsonSchemaTypeToSchemaType(Array.isArray(schema.type) ? schema.type[0] : schema.type),
value: "",
path
};
}
function JsonSchemaTypeToSchemaType(type) {
switch (type) {
case void 0: return "undefined";
case "integer": return "number";
default: return type;
}
}
function getMainInputData(connectionsData, outputIndex) {
if (!connectionsData?.hasOwnProperty(NodeConnectionTypes.Main) || connectionsData.main === void 0 || outputIndex < 0 || outputIndex >= connectionsData.main.length || connectionsData.main[outputIndex] === null) return [];
return connectionsData.main[outputIndex];
}
function getNodeInputData(node, runIndex = 0, outputIndex = 0) {
const { getWorkflowExecution } = useWorkflowsStore();
if (node === null) return [];
if (getWorkflowExecution === null) return [];
const executionData = getWorkflowExecution.data;
if (!executionData?.resultData) return [];
const taskData = executionData.resultData.runData?.[node.name]?.[runIndex];
if (taskData?.data === void 0) return [];
return getMainInputData(taskData.data, outputIndex);
}
function getInputDataWithPinned(node, runIndex = 0, outputIndex = 0) {
if (!node) return [];
const { pinDataByNodeName } = useWorkflowsStore();
const pinnedData = pinDataByNodeName(node.name);
let inputData = getNodeInputData(node, runIndex, outputIndex);
if (pinnedData) inputData = Array.isArray(pinnedData) ? pinnedData.map((json) => ({ json })) : [{ json: pinnedData }];
return inputData;
}
function schemaMatches(schema, search) {
const searchLower = search.toLocaleLowerCase();
return !!schema.key?.toLocaleLowerCase().includes(searchLower) || typeof schema.value === "string" && schema.value.toLocaleLowerCase().includes(searchLower);
}
function filterSchema(schema, search) {
if (!search.trim()) return schema;
if (Array.isArray(schema.value)) {
const filteredValue = schema.value.map((value) => filterSchema(value, search)).filter(isPresent);
if (filteredValue.length === 0) return schemaMatches(schema, search) ? schema : null;
return {
...schema,
value: filteredValue
};
}
return schemaMatches(schema, search) ? schema : null;
}
return {
getSchema,
getSchemaForExecutionData,
getSchemaForJsonSchema,
getNodeInputData,
getInputDataWithPinned,
filterSchema
};
}
var icons = {
object: DATA_TYPE_ICON_MAP.object,
array: DATA_TYPE_ICON_MAP.array,
["string"]: DATA_TYPE_ICON_MAP.string,
null: "square-minus",
["number"]: DATA_TYPE_ICON_MAP.number,
["boolean"]: DATA_TYPE_ICON_MAP.boolean,
function: "code",
bigint: "calculator",
symbol: "sun",
["undefined"]: "ban"
};
var getIconBySchemaType = (type) => icons[type];
var emptyItem = (key, { nodeName, level } = {}) => ({
id: `empty-${window.crypto.randomUUID()}`,
type: "empty",
key,
level: level ?? 0,
nodeName: nodeName ?? ""
});
var moreFieldsItem = () => ({
id: `moreFields-${window.crypto.randomUUID()}`,
type: "icon",
icon: "ellipsis",
tooltip: useI18n().baseText("dataMapping.schemaView.previewExtraFields")
});
var isEmptySchema = (schema) => {
const isObjectOrArray$1 = schema.type === "object";
const isEmpty$1 = Array.isArray(schema.value) && schema.value.length === 0;
return isObjectOrArray$1 && isEmpty$1;
};
var prefixTitle = (title, prefix) => prefix ? `${prefix}[${title}]` : title;
const useFlattenSchema = () => {
const closedNodes = ref(/* @__PURE__ */ new Set());
const toggleNode = (id$1) => {
if (closedNodes.value.has(id$1)) closedNodes.value.delete(id$1);
else closedNodes.value.add(id$1);
};
const flattenSchema = ({ isDataEmpty, schema, nodeType, nodeName, expressionPrefix = "", depth = 0, prefix = "", level = 0, preview, lastSuccessfulPreview, truncateLimit }) => {
if (isEmptySchema(schema) && level < 0) return [emptyItem(isDataEmpty ? "emptyData" : "emptySchema")];
const expression = `{{ ${expressionPrefix ? expressionPrefix + schema.path : schema.path.slice(1)} }}`;
const id$1 = `${nodeName}-${expression}`;
if (Array.isArray(schema.value)) {
const items = [];
if (schema.key) items.push({
title: prefixTitle(schema.key, prefix),
path: schema.path,
expression,
depth,
level,
icon: getIconBySchemaType(schema.type),
id: id$1,
collapsable: true,
nodeType,
nodeName,
type: "item",
preview
});
if (closedNodes.value.has(id$1)) return items;
return items.concat(schema.value.map((item) => {
return flattenSchema({
isDataEmpty,
schema: item,
expressionPrefix,
nodeType,
nodeName,
depth,
prefix: schema.type === "array" ? schema.key : "",
level: level + 1,
preview,
lastSuccessfulPreview,
truncateLimit
});
}).flat());
} else if (schema.key) return [{
title: prefixTitle(schema.key, prefix),
path: schema.path,
expression,
level,
depth,
value: !lastSuccessfulPreview ? shorten(schema.value, truncateLimit, 0) : "",
id: id$1,
icon: getIconBySchemaType(schema.type),
collapsable: false,
nodeType,
nodeName,
type: "item",
preview
}];
return [];
};
const flattenMultipleSchemas = (nodes$13, additionalInfo, truncateLimit) => {
return nodes$13.reduce((acc, item) => {
acc.push({
title: item.node.name,
id: item.node.name,
collapsable: true,
nodeType: item.nodeType,
itemCount: item.itemsCount,
info: additionalInfo(item.node),
type: "header",
preview: item.preview,
lastSuccessfulPreview: item.lastSuccessfulPreview
});
if (closedNodes.value.has(item.node.name)) return acc;
if (isEmptySchema(item.schema)) {
if (!item.isNodeExecuted && !item.lastSuccessfulPreview) {
acc.push(emptyItem("executeSchema", { level: 1 }));
return acc;
}
if (item.isDataEmpty) {
acc.push(emptyItem(item.hasBinary ? "emptySchemaWithBinary" : "emptyData", { level: 1 }));
return acc;
}
acc.push(emptyItem(item.hasBinary ? "emptySchemaWithBinary" : "emptySchema", { level: 1 }));
return acc;
}
acc = acc.concat(flattenSchema({
isDataEmpty: item.isDataEmpty,
schema: item.schema,
depth: item.depth,
nodeType: item.node.type,
nodeName: item.node.name,
preview: item.preview,
lastSuccessfulPreview: item.lastSuccessfulPreview,
truncateLimit,
expressionPrefix: getNodeParentExpression({
nodeName: item.node.name,
distanceFromActive: item.depth
})
}));
if (item.preview && !item.lastSuccessfulPreview) acc.push(moreFieldsItem());
return acc;
}, []);
};
return {
closedNodes,
toggleNode,
flattenSchema,
flattenMultipleSchemas
};
};
var CANVAS_VIEWS = [
VIEWS.NEW_WORKFLOW,
VIEWS.WORKFLOW,
VIEWS.EXECUTION_DEBUG
];
var EXECUTION_VIEWS = [VIEWS.EXECUTION_PREVIEW];
var WORKFLOW_LIST_VIEWS = [VIEWS.WORKFLOWS, VIEWS.PROJECTS_WORKFLOWS];
var CREDENTIALS_LIST_VIEWS = [VIEWS.CREDENTIALS, VIEWS.PROJECTS_CREDENTIALS];
const useAIAssistantHelpers = () => {
const ndvStore = useNDVStore();
const nodeTypesStore = useNodeTypesStore();
const workflowsStore = useWorkflowsStore();
const workflowHelpers = useWorkflowHelpers();
const locale = useI18n();
const entityRegex = /\$\(\s*(\\?["'`])((?:\\.|(?!\1)[^\\])*)\1\s*\)/g;
function extractNodeNames(template) {
let matches;
const nodeNames = [];
while ((matches = entityRegex.exec(template)) !== null) nodeNames.push(matches[2]);
return nodeNames;
}
function unescapeQuotes(str) {
return str.replace(/\\(['"`])/g, "$1");
}
function getReferencedNodes(node) {
const referencedNodes = /* @__PURE__ */ new Set();
if (!node) return [];
for (const key in node.parameters) {
let names = [];
if (node.parameters[key] && typeof node.parameters[key] === "object" && Object.keys(node.parameters[key]).length) names = extractNodeNames(JSON.stringify(node.parameters[key]));
else if (typeof node.parameters[key] === "string" && node.parameters[key]) names = extractNodeNames(node.parameters[key]);
if (names.length) names.map((name$13) => unescapeQuotes(name$13)).forEach((name$13) => {
referencedNodes.add(name$13);
});
}
return referencedNodes.size ? Array.from(referencedNodes) : [];
}
function processNodeForAssistant(node, propsToRemove) {
const nodeForLLM = deepCopy(node);
propsToRemove.forEach((key) => {
delete nodeForLLM[key];
});
nodeForLLM.parameters = workflowHelpers.getNodeParametersWithResolvedExpressions(nodeForLLM.parameters);
return nodeForLLM;
}
function getNodeInfoForAssistant(node) {
if (!node) return {};
const schemas = getNodesSchemas(getReferencedNodes(node));
const nodeType = nodeTypesStore.getNodeType(node.type);
let authType = void 0;
if (nodeType) {
const authField = getMainAuthField(nodeType);
const credentialInUse = node.parameters[authField?.name ?? ""];
authType = getNodeAuthOptions(nodeType).find((option) => option.value === credentialInUse);
}
let nodeInputData = void 0;
const ndvInput = ndvStore.ndvInputData;
if (isNodeReferencingInputData(node) && ndvInput?.length) {
const inputData = ndvStore.ndvInputData[0].json;
nodeInputData = {
inputNodeName: ndvStore.input.nodeName,
inputData
};
}
return {
authType,
schemas,
nodeInputData
};
}
function simplifyErrorForAssistant(error) {
const simple = {
name: error.name,
message: error.message
};
if ("type" in error) simple.type = error.type;
if ("description" in error && error.description) simple.description = error.description;
if (error.stack) simple.stack = error.stack;
if ("lineNumber" in error) simple.lineNumber = error.lineNumber;
return simple;
}
function isNodeReferencingInputData(node) {
const parametersString = JSON.stringify(node.parameters);
return [
"$json",
"$input",
"$binary"
].some((ref$1) => parametersString.includes(ref$1));
}
function getNodesSchemas(nodeNames, excludeValues) {
const schemas = [];
for (const name$13 of nodeNames) {
const node = workflowsStore.getNodeByName(name$13);
if (!node) continue;
const { getSchemaForExecutionData, getInputDataWithPinned } = useDataSchema();
const schema = getSchemaForExecutionData(executionDataToJson(getInputDataWithPinned(node)), excludeValues, true);
schemas.push({
nodeName: node.name,
schema
});
}
return schemas;
}
function getCurrentViewDescription(view) {
switch (true) {
case WORKFLOW_LIST_VIEWS.includes(view): return locale.baseText("aiAssistant.prompts.currentView.workflowList");
case CREDENTIALS_LIST_VIEWS.includes(view): return locale.baseText("aiAssistant.prompts.currentView.credentialsList");
case EXECUTION_VIEWS.includes(view): return locale.baseText("aiAssistant.prompts.currentView.executionsView");
case CANVAS_VIEWS.includes(view): return locale.baseText("aiAssistant.prompts.currentView.workflowEditor");
default: return;
}
}
function simplifyResultData(data, options = {}) {
const { compact = false } = options;
const simplifiedResultData = { runData: {} };
if (data.error) simplifiedResultData.error = data.error;
if (!data.runData) return simplifiedResultData;
for (const key of Object.keys(data.runData)) {
const taskDataArray = data.runData[key];
simplifiedResultData.runData[key] = taskDataArray.map((taskData) => {
const { data: _taskDataContent,...taskDataWithoutData } = taskData;
if (compact && taskDataWithoutData.inputOverride) try {
const inputOverrideStr = JSON.stringify(taskDataWithoutData.inputOverride);
if (new Blob([inputOverrideStr]).size > 2e3) delete taskDataWithoutData.inputOverride;
} catch (error) {
delete taskDataWithoutData.inputOverride;
}
return taskDataWithoutData;
});
}
if (data.lastNodeExecuted) simplifiedResultData.lastNodeExecuted = data.lastNodeExecuted;
if (data.metadata) simplifiedResultData.metadata = data.metadata;
return simplifiedResultData;
}
const simplifyWorkflowForAssistant = (workflow) => ({
name: workflow.name,
active: workflow.active,
connections: workflow.connections,
nodes: workflow.nodes
});
function extractExpressionsFromWorkflow(workflow, executionData) {
const MAX_VALUE_LENGTH = 200;
const MAX_EXPRESSION_LENGTH = 500;
const expressionsByNode = {};
if (!workflow.nodes || workflow.nodes.length === 0) return expressionsByNode;
const executedNodeNames = executionData?.runData ? new Set(Object.keys(executionData.runData)) : null;
const trimValue = (value) => {
if (value === void 0) return "<EMPTY>";
if (typeof value === "string") {
if (value.length <= MAX_VALUE_LENGTH) return value;
return value.substring(0, MAX_VALUE_LENGTH) + "... [truncated]";
}
if (value === null || typeof value === "number" || typeof value === "boolean") return value;
try {
const stringified = JSON.stringify(value);
if (stringified.length <= MAX_VALUE_LENGTH) return value;
return stringified.substring(0, MAX_VALUE_LENGTH) + "... [truncated]";
} catch {
return "<Could not serialize value>";
}
};
for (const node of workflow.nodes) {
if (!node.parameters) continue;
if (executedNodeNames && !executedNodeNames.has(node.name)) continue;
const nodeExpressions = [];
const extractExpressions = (params, visited = /* @__PURE__ */ new WeakSet()) => {
if (typeof params === "string" && params.startsWith("=")) {
if (!params.includes("{{") || !params.includes("}}")) return;
let resolved;
try {
resolved = workflowHelpers.resolveExpression(params, void 0, { contextNodeName: node.name });
} catch (error) {
resolved = `Error in expression: "${error instanceof Error ? error.message : String(error)}"`;
}
const trimmedExpression = params.length > MAX_EXPRESSION_LENGTH ? params.substring(0, MAX_EXPRESSION_LENGTH) + "... [truncated]" : params;
nodeExpressions.push({
expression: trimmedExpression,
resolvedValue: trimValue(resolved),
nodeType: node.type
});
} else if (Array.isArray(params)) {
if (visited.has(params)) return;
visited.add(params);
params.forEach((item) => extractExpressions(item, visited));
} else if (typeof params === "object" && params !== null) {
if (visited.has(params)) return;
visited.add(params);
Object.values(params).forEach((value) => extractExpressions(value, visited));
}
};
extractExpressions(node.parameters);
if (nodeExpressions.length > 0) expressionsByNode[node.name] = nodeExpressions;
}
return expressionsByNode;
}
const trimPayloadToSize = (payload, size = 100) => {
const payloadCopy = deepCopy(payload);
const requestPayload = payloadCopy.payload;
if (requestPayload.type === "init-support-chat") {
if (requestPayload.context?.activeNodeInfo?.node) requestPayload.context.activeNodeInfo.node.parameters = {};
if (requestPayload.context?.currentWorkflow) requestPayload.context.currentWorkflow?.nodes?.forEach((node) => {
node.parameters = {};
});
if (requestPayload.context?.executionData?.runData) requestPayload.context.executionData.runData = {};
if (requestPayload.context?.executionData?.error && "node" in requestPayload.context?.executionData?.error) {
if (requestPayload.context?.executionData?.error?.node) requestPayload.context.executionData.error.node.parameters = {};
}
if (getRequestPayloadSize(payloadCopy) > size) requestPayload.context = void 0;
} else if (requestPayload.type === "init-error-helper") requestPayload.node.parameters = {};
if (getRequestPayloadSize(payloadCopy) > size) throw new Error(locale.baseText("aiAssistant.payloadTooBig.message"));
payload.payload = payloadCopy.payload;
};
const getRequestPayloadSize = (payload) => {
try {
return getObjectSizeInKB(payload.payload);
} catch (error) {
return 0;
}
};
return {
processNodeForAssistant,
getNodeInfoForAssistant,
simplifyErrorForAssistant,
isNodeReferencingInputData,
getNodesSchemas,
getCurrentViewDescription,
getReferencedNodes,
simplifyResultData,
simplifyWorkflowForAssistant,
trimPayloadSize: trimPayloadToSize,
extractExpressionsFromWorkflow
};
};
function chatWithBuilder(ctx, payload, onMessageUpdated, onDone, onError, abortSignal) {
streamRequest(ctx, "/ai/build", {
...payload,
payload: { ...payload.payload }
}, onMessageUpdated, onDone, onError, void 0, abortSignal);
}
function chatWithAssistant(ctx, payload, onMessageUpdated, onDone, onError) {
try {
if (getObjectSizeInKB(payload.payload) > 100) useAIAssistantHelpers().trimPayloadSize(payload);
} catch (e) {
onError(e);
return;
}
streamRequest(ctx, "/ai/chat", payload, onMessageUpdated, onDone, onError);
}
async function replaceCode(context, data) {
return await makeRestApiRequest(context, "POST", "/ai/chat/apply-suggestion", data);
}
async function generateCodeForPrompt(ctx, { question, context, forNode }) {
return await makeRestApiRequest(ctx, "POST", "/ai/ask-ai", {
question,
context,
forNode
});
}
async function claimFreeAiCredits(ctx, { projectId }) {
return await makeRestApiRequest(ctx, "POST", "/ai/free-credits", { projectId });
}
async function getAiSessions(ctx, workflowId) {
return await makeRestApiRequest(ctx, "POST", "/ai/sessions", { workflowId });
}
async function getSessionsMetadata(ctx, workflowId) {
return await makeRestApiRequest(ctx, "POST", "/ai/sessions/metadata", { workflowId });
}
async function getBuilderCredits(ctx) {
return await makeRestApiRequest(ctx, "GET", "/ai/build/credits");
}
var DEFAULT_CREDENTIAL_NAME = "Unnamed credential";
var DEFAULT_CREDENTIAL_POSTFIX = "account";
var TYPES_WITH_DEFAULT_NAME = [
"httpBasicAuth",
"oAuth2Api",
"httpDigestAuth",
"oAuth1Api"
];
const useCredentialsStore = defineStore(STORES.CREDENTIALS, () => {
const state = ref({
credentialTypes: {},
credentials: {}
});
const rootStore = useRootStore();
const credentialTypesById = computed(() => {
return state.value.credentialTypes;
});
const allCredentialTypes = computed(() => {
return Object.values(state.value.credentialTypes).sort((a, b) => a.displayName.localeCompare(b.displayName));
});
const allCredentials = computed(() => {
return Object.values(state.value.credentials).sort((a, b) => a.name.localeCompare(b.name));
});
const allCredentialsByType = computed(() => {
const credentials = allCredentials.value;
return allCredentialTypes.value.reduce((accu, type) => {
accu[type.name] = credentials.filter((cred) => cred.type === type.name);
return accu;
}, {});
});
const allUsableCredentialsByType = computed(() => {
const credentials = allCredentials.value;
return allCredentialTypes.value.reduce((accu, type) => {
accu[type.name] = credentials.filter((cred) => {
return cred.type === type.name;
});
return accu;
}, {});
});
const allUsableCredentialsForNode = computed(() => {
return (node) => {
let credentials = [];
const nodeType = useNodeTypesStore().getNodeType(node.type, node.typeVersion);
if (nodeType?.credentials) nodeType.credentials.forEach((cred) => {
credentials = credentials.concat(allUsableCredentialsByType.value[cred.name]);
});
return credentials.sort((a, b) => {
const aDate = new Date(a.updatedAt);
const bDate = new Date(b.updatedAt);
return aDate.getTime() - bDate.getTime();
});
};
});
const getCredentialTypeByName = computed(() => {
return (type) => state.value.credentialTypes[type];
});
const getCredentialById = computed(() => {
return (id$1) => state.value.credentials[id$1];
});
const getCredentialByIdAndType = computed(() => {
return (id$1, type) => {
const credential = state.value.credentials[id$1];
return !credential || credential.type !== type ? void 0 : credential;
};
});
const getCredentialsByType = computed(() => {
return (credentialType) => {
return allCredentialsByType.value[credentialType] || [];
};
});
const getUsableCredentialByType = computed(() => {
return (credentialType) => {
return allUsableCredentialsByType.value[credentialType] || [];
};
});
const getNodesWithAccess = computed(() => {
return (credentialTypeName) => {
const credentialType = getCredentialTypeByName.value(credentialTypeName);
if (!credentialType) return [];
const nodeTypesStore = useNodeTypesStore();
return (credentialType.supportedNodes ?? []).map((nodeType) => nodeTypesStore.getNodeType(nodeType)).filter(isPresent);
};
});
const getScopesByCredentialType = computed(() => {
return (credentialTypeName) => {
const credentialType = getCredentialTypeByName.value(credentialTypeName);
if (!credentialType) return [];
const scopeProperty = credentialType.properties.find((p) => p.name === "scope");
if (!scopeProperty || !scopeProperty.default || typeof scopeProperty.default !== "string" || scopeProperty.default === "") return [];
let { default: scopeDefault } = scopeProperty;
scopeDefault = scopeDefault.replace(/^=/, "").replace(/\{\{.*\}\}/, "");
if (/ /.test(scopeDefault)) return scopeDefault.split(" ");
if (/,/.test(scopeDefault)) return scopeDefault.split(",");
return [scopeDefault];
};
});
const getCredentialOwnerName = computed(() => {
return (credential) => {
const { name: name$13, email } = splitName(credential?.homeProject?.name ?? "");
return name$13 ? email ? `${name$13} (${email})` : name$13 : email ?? i18n.baseText("credentialEdit.credentialSharing.info.sharee.fallback");
};
});
const getCredentialOwnerNameById = computed(() => {
return (credentialId) => {
const credential = getCredentialById.value(credentialId);
return getCredentialOwnerName.value(credential);
};
});
const httpOnlyCredentialTypes = computed(() => {
return allCredentialTypes.value.filter((credentialType) => credentialType.httpRequestNode && !credentialType.httpRequestNode.hidden);
});
const setCredentialTypes = (credentialTypes) => {
state.value.credentialTypes = credentialTypes.reduce((accu, cred) => {
accu[cred.name] = cred;
return accu;
}, {});
};
const addCredentials = (credentials) => {
credentials.forEach((cred) => {
if (cred.id) state.value.credentials[cred.id] = {
...state.value.credentials[cred.id],
...cred
};
});
};
const setCredentials = (credentials) => {
state.value.credentials = credentials.reduce((accu, cred) => {
if (cred.id) accu[cred.id] = cred;
return accu;
}, {});
};
const upsertCredential = (credential) => {
if (credential.id) state.value.credentials = {
...state.value.credentials,
[credential.id]: {
...state.value.credentials[credential.id],
...credential
}
};
};
const fetchCredentialTypes = async (forceFetch) => {
if (allCredentialTypes.value.length > 0 && !forceFetch) return;
setCredentialTypes(await getCredentialTypes(rootStore.baseUrl));
};
const fetchAllCredentials = async (projectId, includeScopes = true, onlySharedWithMe = false, includeGlobal = true) => {
const filter$1 = { projectId };
const credentials = await getAllCredentials(rootStore.restApiContext, isEmpty(filter$1) ? void 0 : filter$1, includeScopes, onlySharedWithMe, includeGlobal);
setCredentials(credentials);
return credentials;
};
const fetchAllCredentialsForWorkflow = async (options) => {
const credentials = await getAllCredentialsForWorkflow(rootStore.restApiContext, options);
setCredentials(credentials);
return credentials;
};
const getCredentialData$1 = async ({ id: id$1 }) => {
return await getCredentialData(rootStore.restApiContext, id$1);
};
const getCredentialTypesNodeDescriptions = (overrideCredType, nodeType) => {
if (typeof overrideCredType !== "string") return [];
const credType = getCredentialTypeByName.value(overrideCredType);
if (credType) return [credType];
return nodeType?.credentials ? nodeType.credentials : [];
};
const createNewCredential$1 = async (data, projectId, uiContext) => {
const settingsStore = useSettingsStore();
const credential = await createNewCredential(rootStore.restApiContext, {
name: data.name,
type: data.type,
data: data.data ?? {},
projectId,
uiContext,
isGlobal: data.isGlobal
});
if (data?.homeProject && !credential.homeProject) credential.homeProject = data.homeProject;
if (settingsStore.isEnterpriseFeatureEnabled[EnterpriseEditionFeature.Sharing]) {
upsertCredential(credential);
if (data.sharedWithProjects) await setCredentialSharedWith$1({
credentialId: credential.id,
sharedWithProjects: data.sharedWithProjects
});
} else upsertCredential(credential);
return credential;
};
const updateCredential$1 = async (params) => {
const { id: id$1, data } = params;
const credential = await updateCredential(rootStore.restApiContext, id$1, data);
upsertCredential(credential);
return credential;
};
const deleteCredential$1 = async ({ id: id$1 }) => {
if (await deleteCredential(rootStore.restApiContext, id$1)) {
const { [id$1]: deletedCredential,...rest } = state.value.credentials;
state.value.credentials = rest;
}
};
const oAuth2Authorize = async (data) => {
return await oAuth2CredentialAuthorize(rootStore.restApiContext, data);
};
const oAuth1Authorize = async (data) => {
return await oAuth1CredentialAuthorize(rootStore.restApiContext, data);
};
const testCredential$1 = async (data) => {
return await testCredential(rootStore.restApiContext, { credentials: data });
};
const getNewCredentialName = async (params) => {
try {
const { credentialTypeName } = params;
let newName = DEFAULT_CREDENTIAL_NAME;
if (!TYPES_WITH_DEFAULT_NAME.includes(credentialTypeName)) {
const cred = getCredentialTypeByName.value(credentialTypeName);
newName = cred ? getAppNameFromCredType(cred.displayName) : "";
newName = newName.length > 0 ? `${newName} ${DEFAULT_CREDENTIAL_POSTFIX}` : DEFAULT_CREDENTIAL_NAME;
}
return (await getCredentialsNewName(rootStore.restApiContext, newName)).name;
} catch (e) {
return DEFAULT_CREDENTIAL_NAME;
}
};
const setCredentialSharedWith$1 = async (payload) => {
if (useSettingsStore().isEnterpriseFeatureEnabled[EnterpriseEditionFeature.Sharing]) {
await setCredentialSharedWith(useRootStore().restApiContext, payload.credentialId, { shareWithIds: payload.sharedWithProjects.map((project) => project.id) });
state.value.credentials[payload.credentialId] = {
...state.value.credentials[payload.credentialId],
sharedWithProjects: payload.sharedWithProjects
};
}
return state.value.credentials[payload.credentialId];
};
const getCredentialTranslation = async (credentialType) => {
return await makeRestApiRequest(rootStore.restApiContext, "GET", "/credential-translation", { credentialType });
};
const claimFreeAiCredits$1 = async (projectId) => {
const credential = await claimFreeAiCredits(rootStore.restApiContext, { projectId });
upsertCredential(credential);
return credential;
};
return {
state,
getCredentialOwnerName,
getCredentialsByType,
getCredentialById,
getCredentialTypeByName,
getCredentialByIdAndType,
getNodesWithAccess,
getUsableCredentialByType,
credentialTypesById,
httpOnlyCredentialTypes,
getScopesByCredentialType,
getCredentialOwnerNameById,
allUsableCredentialsForNode,
allCredentials,
allCredentialTypes,
allUsableCredentialsByType,
setCredentialTypes,
addCredentials,
setCredentials,
deleteCredential: deleteCredential$1,
upsertCredential,
fetchCredentialTypes,
fetchAllCredentials,
fetchAllCredentialsForWorkflow,
createNewCredential: createNewCredential$1,
updateCredential: updateCredential$1,
getCredentialData: getCredentialData$1,
getCredentialTypesNodeDescriptions,
oAuth1Authorize,
oAuth2Authorize,
getNewCredentialName,
testCredential: testCredential$1,
getCredentialTranslation,
setCredentialSharedWith: setCredentialSharedWith$1,
claimFreeAiCredits: claimFreeAiCredits$1
};
});
const listenForCredentialChanges = (opts) => {
const { store, onCredentialCreated, onCredentialDeleted, onCredentialUpdated } = opts;
const listeningForActions = [
"createNewCredential",
"updateCredential",
"deleteCredential"
];
return store.$onAction((result) => {
const { name: name$13, after, args } = result;
after(async (returnValue) => {
if (!listeningForActions.includes(name$13)) return;
switch (name$13) {
case "createNewCredential":
onCredentialCreated?.(returnValue);
break;
case "updateCredential":
onCredentialUpdated?.(returnValue);
break;
case "deleteCredential":
const credentialId = args[0].id;
onCredentialDeleted?.(credentialId);
break;
}
});
});
};
function getNodeVersions(nodeType) {
return Array.isArray(nodeType.version) ? nodeType.version : [nodeType.version];
}
function groupNodeTypesByNameAndType(nodeTypes) {
return nodeTypes.reduce((groups, nodeType) => {
const newNodeVersions = getNodeVersions(nodeType);
if (newNodeVersions.length === 0) {
const singleVersion = { [1]: nodeType };
groups[nodeType.name] = singleVersion;
return groups;
}
for (const version of newNodeVersions) if (groups[nodeType.name]) groups[nodeType.name][version] = Object.assign(groups[nodeType.name][version] ?? {}, nodeType);
else groups[nodeType.name] = Object.assign(groups[nodeType.name] ?? {}, { [version]: nodeType });
return groups;
}, {});
}
var require__arrayReduce = /* @__PURE__ */ __commonJSMin(((exports, module) => {
function arrayReduce$1(array$1, iteratee, accumulator, initAccum) {
var index = -1, length = array$1 == null ? 0 : array$1.length;
if (initAccum && length) accumulator = array$1[++index];
while (++index < length) accumulator = iteratee(accumulator, array$1[index], index, array$1);
return accumulator;
}
module.exports = arrayReduce$1;
}));
var require__basePropertyOf = /* @__PURE__ */ __commonJSMin(((exports, module) => {
function basePropertyOf(object$2) {
return function(key) {
return object$2 == null ? void 0 : object$2[key];
};
}
module.exports = basePropertyOf;
}));
var require__deburrLetter = /* @__PURE__ */ __commonJSMin(((exports, module) => {
module.exports = require__basePropertyOf()({
"À": "A",
"Á": "A",
"Â": "A",
"Ã": "A",
"Ä": "A",
"Å": "A",
"à": "a",
"á": "a",
"â": "a",
"ã": "a",
"ä": "a",
"å": "a",
"Ç": "C",
"ç": "c",
"Ð": "D",
"ð": "d",
"È": "E",
"É": "E",
"Ê": "E",
"Ë": "E",
"è": "e",
"é": "e",
"ê": "e",
"ë": "e",
"Ì": "I",
"Í": "I",
"Î": "I",
"Ï": "I",
"ì": "i",
"í": "i",
"î": "i",
"ï": "i",
"Ñ": "N",
"ñ": "n",
"Ò": "O",
"Ó": "O",
"Ô": "O",
"Õ": "O",
"Ö": "O",
"Ø": "O",
"ò": "o",
"ó": "o",
"ô": "o",
"õ": "o",
"ö": "o",
"ø": "o",
"Ù": "U",
"Ú": "U",
"Û": "U",
"Ü": "U",
"ù": "u",
"ú": "u",
"û": "u",
"ü": "u",
"Ý": "Y",
"ý": "y",
"ÿ": "y",
"Æ": "Ae",
"æ": "ae",
"Þ": "Th",
"þ": "th",
"ß": "ss",
"Ā": "A",
"Ă": "A",
"Ą": "A",
"ā": "a",
"ă": "a",
"ą": "a",
"Ć": "C",
"Ĉ": "C",
"Ċ": "C",
"Č": "C",
"ć": "c",
"ĉ": "c",
"ċ": "c",
"č": "c",
"Ď": "D",
"Đ": "D",
"ď": "d",
"đ": "d",
"Ē": "E",
"Ĕ": "E",
"Ė": "E",
"Ę": "E",
"Ě": "E",
"ē": "e",
"ĕ": "e",
"ė": "e",
"ę": "e",
"ě": "e",
"Ĝ": "G",
"Ğ": "G",
"Ġ": "G",
"Ģ": "G",
"ĝ": "g",
"ğ": "g",
"ġ": "g",
"ģ": "g",
"Ĥ": "H",
"Ħ": "H",
"ĥ": "h",
"ħ": "h",
"Ĩ": "I",
"Ī": "I",
"Ĭ": "I",
"Į": "I",
"İ": "I",
"ĩ": "i",
"ī": "i",
"ĭ": "i",
"į": "i",
"ı": "i",
"Ĵ": "J",
"ĵ": "j",
"Ķ": "K",
"ķ": "k",
"ĸ": "k",
"Ĺ": "L",
"Ļ": "L",
"Ľ": "L",
"Ŀ": "L",
"Ł": "L",
"ĺ": "l",
"ļ": "l",
"ľ": "l",
"ŀ": "l",
"ł": "l",
"Ń": "N",
"Ņ": "N",
"Ň": "N",
"Ŋ": "N",
"ń": "n",
"ņ": "n",
"ň": "n",
"ŋ": "n",
"Ō": "O",
"Ŏ": "O",
"Ő": "O",
"ō": "o",
"ŏ": "o",
"ő": "o",
"Ŕ": "R",
"Ŗ": "R",
"Ř": "R",
"ŕ": "r",
"ŗ": "r",
"ř": "r",
"Ś": "S",
"Ŝ": "S",
"Ş": "S",
"Š": "S",
"ś": "s",
"ŝ": "s",
"ş": "s",
"š": "s",
"Ţ": "T",
"Ť": "T",
"Ŧ": "T",
"ţ": "t",
"ť": "t",
"ŧ": "t",
"Ũ": "U",
"Ū": "U",
"Ŭ": "U",
"Ů": "U",
"Ű": "U",
"Ų": "U",
"ũ": "u",
"ū": "u",
"ŭ": "u",
"ů": "u",
"ű": "u",
"ų": "u",
"Ŵ": "W",
"ŵ": "w",
"Ŷ": "Y",
"ŷ": "y",
"Ÿ": "Y",
"Ź": "Z",
"Ż": "Z",
"Ž": "Z",
"ź": "z",
"ż": "z",
"ž": "z",
"IJ": "IJ",
"ij": "ij",
"Œ": "Oe",
"œ": "oe",
"ʼn": "'n",
"ſ": "s"
});
}));
var require_deburr = /* @__PURE__ */ __commonJSMin(((exports, module) => {
var deburrLetter = require__deburrLetter(), toString$3 = require_toString();
var reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g;
var reComboMark = RegExp("[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]", "g");
function deburr$1(string) {
string = toString$3(string);
return string && string.replace(reLatin, deburrLetter).replace(reComboMark, "");
}
module.exports = deburr$1;
}));
var require__asciiWords = /* @__PURE__ */ __commonJSMin(((exports, module) => {
var reAsciiWord = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g;
function asciiWords$1(string) {
return string.match(reAsciiWord) || [];
}
module.exports = asciiWords$1;
}));
var require__hasUnicodeWord = /* @__PURE__ */ __commonJSMin(((exports, module) => {
var reHasUnicodeWord = /[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/;
function hasUnicodeWord$1(string) {
return reHasUnicodeWord.test(string);
}
module.exports = hasUnicodeWord$1;
}));
var require__unicodeWords = /* @__PURE__ */ __commonJSMin(((exports, module) => {
var rsAstralRange$1 = "\\ud800-\\udfff", rsComboRange$1 = "\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff", rsDingbatRange = "\\u2700-\\u27bf", rsLowerRange = "a-z\\xdf-\\xf6\\xf8-\\xff", rsMathOpRange = "\\xac\\xb1\\xd7\\xf7", rsNonCharRange = "\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf", rsPunctuationRange = "\\u2000-\\u206f", rsSpaceRange = " \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000", rsUpperRange = "A-Z\\xc0-\\xd6\\xd8-\\xde", rsVarRange$1 = "\\ufe0e\\ufe0f", rsBreakRange = rsMathOpRange + rsNonCharRange + rsPunctuationRange + rsSpaceRange;
var rsApos = "['’]", rsBreak = "[" + rsBreakRange + "]", rsCombo$1 = "[" + rsComboRange$1 + "]", rsDigits = "\\d+", rsDingbat = "[" + rsDingbatRange + "]", rsLower = "[" + rsLowerRange + "]", rsMisc = "[^" + rsAstralRange$1 + rsBreakRange + rsDigits + rsDingbatRange + rsLowerRange + rsUpperRange + "]", rsModifier$1 = "(?:" + rsCombo$1 + "|\\ud83c[\\udffb-\\udfff])", rsNonAstral$1 = "[^" + rsAstralRange$1 + "]", rsRegional$1 = "(?:\\ud83c[\\udde6-\\uddff]){2}", rsSurrPair$1 = "[\\ud800-\\udbff][\\udc00-\\udfff]", rsUpper = "[" + rsUpperRange + "]", rsZWJ$1 = "\\u200d";
var rsMiscLower = "(?:" + rsLower + "|" + rsMisc + ")", rsMiscUpper = "(?:" + rsUpper + "|" + rsMisc + ")", rsOptContrLower = "(?:" + rsApos + "(?:d|ll|m|re|s|t|ve))?", rsOptContrUpper = "(?:" + rsApos + "(?:D|LL|M|RE|S|T|VE))?", reOptMod$1 = rsModifier$1 + "?", rsOptVar$1 = "[" + rsVarRange$1 + "]?", rsOptJoin$1 = "(?:" + rsZWJ$1 + "(?:" + [
rsNonAstral$1,
rsRegional$1,
rsSurrPair$1
].join("|") + ")" + rsOptVar$1 + reOptMod$1 + ")*", rsOrdLower = "\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])", rsOrdUpper = "\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])", rsSeq$1 = rsOptVar$1 + reOptMod$1 + rsOptJoin$1, rsEmoji = "(?:" + [
rsDingbat,
rsRegional$1,
rsSurrPair$1
].join("|") + ")" + rsSeq$1;
var reUnicodeWord = RegExp([
rsUpper + "?" + rsLower + "+" + rsOptContrLower + "(?=" + [
rsBreak,
rsUpper,
"$"
].join("|") + ")",
rsMiscUpper + "+" + rsOptContrUpper + "(?=" + [
rsBreak,
rsUpper + rsMiscLower,
"$"
].join("|") + ")",
rsUpper + "?" + rsMiscLower + "+" + rsOptContrLower,
rsUpper + "+" + rsOptContrUpper,
rsOrdUpper,
rsOrdLower,
rsDigits,
rsEmoji
].join("|"), "g");
function unicodeWords$1(string) {
return string.match(reUnicodeWord) || [];
}
module.exports = unicodeWords$1;
}));
var require_words = /* @__PURE__ */ __commonJSMin(((exports, module) => {
var asciiWords = require__asciiWords(), hasUnicodeWord = require__hasUnicodeWord(), toString$2 = require_toString(), unicodeWords = require__unicodeWords();
function words$1(string, pattern, guard) {
string = toString$2(string);
pattern = guard ? void 0 : pattern;
if (pattern === void 0) return hasUnicodeWord(string) ? unicodeWords(string) : asciiWords(string);
return string.match(pattern) || [];
}
module.exports = words$1;
}));
var require__createCompounder = /* @__PURE__ */ __commonJSMin(((exports, module) => {
var arrayReduce = require__arrayReduce(), deburr = require_deburr(), words = require_words();
var reApos = RegExp("['’]", "g");
function createCompounder$1(callback) {
return function(string) {
return arrayReduce(words(deburr(string).replace(reApos, "")), callback, "");
};
}
module.exports = createCompounder$1;
}));
var require__baseSlice = /* @__PURE__ */ __commonJSMin(((exports, module) => {
function baseSlice$1(array$1, start$1, end) {
var index = -1, length = array$1.length;
if (start$1 < 0) start$1 = -start$1 > length ? 0 : length + start$1;
end = end > length ? length : end;
if (end < 0) end += length;
length = start$1 > end ? 0 : end - start$1 >>> 0;
start$1 >>>= 0;
var result = Array(length);
while (++index < length) result[index] = array$1[index + start$1];
return result;
}
module.exports = baseSlice$1;
}));
var require__castSlice = /* @__PURE__ */ __commonJSMin(((exports, module) => {
var baseSlice = require__baseSlice();
function castSlice$1(array$1, start$1, end) {
var length = array$1.length;
end = end === void 0 ? length : end;
return !start$1 && end >= length ? array$1 : baseSlice(array$1, start$1, end);
}
module.exports = castSlice$1;
}));
var require__hasUnicode = /* @__PURE__ */ __commonJSMin(((exports, module) => {
var reHasUnicode = RegExp("[\\u200d\\ud800-\\udfff\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff\\ufe0e\\ufe0f]");
function hasUnicode$2(string) {
return reHasUnicode.test(string);
}
module.exports = hasUnicode$2;
}));
var require__asciiToArray = /* @__PURE__ */ __commonJSMin(((exports, module) => {
function asciiToArray$1(string) {
return string.split("");
}
module.exports = asciiToArray$1;
}));
var require__unicodeToArray = /* @__PURE__ */ __commonJSMin(((exports, module) => {
var rsAstralRange = "\\ud800-\\udfff", rsComboRange = "\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff", rsVarRange = "\\ufe0e\\ufe0f";
var rsAstral = "[" + rsAstralRange + "]", rsCombo = "[" + rsComboRange + "]", rsFitz = "\\ud83c[\\udffb-\\udfff]", rsModifier = "(?:" + rsCombo + "|" + rsFitz + ")", rsNonAstral = "[^" + rsAstralRange + "]", rsRegional = "(?:\\ud83c[\\udde6-\\uddff]){2}", rsSurrPair = "[\\ud800-\\udbff][\\udc00-\\udfff]", rsZWJ = "\\u200d";
var reOptMod = rsModifier + "?", rsOptVar = "[" + rsVarRange + "]?", rsOptJoin = "(?:" + rsZWJ + "(?:" + [
rsNonAstral,
rsRegional,
rsSurrPair
].join("|") + ")" + rsOptVar + reOptMod + ")*", rsSeq = rsOptVar + reOptMod + rsOptJoin, rsSymbol = "(?:" + [
rsNonAstral + rsCombo + "?",
rsCombo,
rsRegional,
rsSurrPair,
rsAstral
].join("|") + ")";
var reUnicode = RegExp(rsFitz + "(?=" + rsFitz + ")|" + rsSymbol + rsSeq, "g");
function unicodeToArray$1(string) {
return string.match(reUnicode) || [];
}
module.exports = unicodeToArray$1;
}));
var require__stringToArray = /* @__PURE__ */ __commonJSMin(((exports, module) => {
var asciiToArray = require__asciiToArray(), hasUnicode$1 = require__hasUnicode(), unicodeToArray = require__unicodeToArray();
function stringToArray$1(string) {
return hasUnicode$1(string) ? unicodeToArray(string) : asciiToArray(string);
}
module.exports = stringToArray$1;
}));
var require__createCaseFirst = /* @__PURE__ */ __commonJSMin(((exports, module) => {
var castSlice = require__castSlice(), hasUnicode = require__hasUnicode(), stringToArray = require__stringToArray(), toString$1 = require_toString();
function createCaseFirst(methodName) {
return function(string) {
string = toString$1(string);
var strSymbols = hasUnicode(string) ? stringToArray(string) : void 0;
var chr = strSymbols ? strSymbols[0] : string.charAt(0);
var trailing = strSymbols ? castSlice(strSymbols, 1).join("") : string.slice(1);
return chr[methodName]() + trailing;
};
}
module.exports = createCaseFirst;
}));
var require_upperFirst = /* @__PURE__ */ __commonJSMin(((exports, module) => {
module.exports = require__createCaseFirst()("toUpperCase");
}));
var require_startCase = /* @__PURE__ */ __commonJSMin(((exports, module) => {
var createCompounder = require__createCompounder(), upperFirst = require_upperFirst();
module.exports = createCompounder(function(result, word, index) {
return result + (index ? " " : "") + upperFirst(word);
});
}));
var SEQUENTIAL_BONUS = 60;
var SEPARATOR_BONUS = 38;
var CAMEL_BONUS = 30;
var FIRST_LETTER_BONUS = 15;
var LEADING_LETTER_PENALTY = -20;
var MAX_LEADING_LETTER_PENALTY = -200;
var UNMATCHED_LETTER_PENALTY = -5;
const DEFAULT_KEYS = [{
key: "properties.displayName",
weight: 1.3
}, {
key: "properties.codex.alias",
weight: 1
}];
function fuzzyMatchSimple(pattern, target) {
let patternIdx = 0;
let strIdx = 0;
while (patternIdx < pattern.length && strIdx < target.length) {
if (pattern.charAt(patternIdx).toLowerCase() === target.charAt(strIdx).toLowerCase()) patternIdx++;
++strIdx;
}
return pattern.length !== 0 && target.length !== 0 && patternIdx === pattern.length;
}
function fuzzyMatchRecursive(pattern, target, patternCurIndex, targetCurrIndex, targetMatches, matches, maxMatches, nextMatch, recursionCount, recursionLimit) {
let outScore = 0;
if (++recursionCount >= recursionLimit) return {
matched: false,
outScore
};
if (patternCurIndex === pattern.length || targetCurrIndex === target.length) return {
matched: false,
outScore
};
let recursiveMatch = false;
let bestRecursiveMatches = [];
let bestRecursiveScore = 0;
let firstMatch = true;
while (patternCurIndex < pattern.length && targetCurrIndex < target.length) {
if (pattern[patternCurIndex].toLowerCase() === target[targetCurrIndex].toLowerCase()) {
if (nextMatch >= maxMatches) return {
matched: false,
outScore
};
if (firstMatch && targetMatches) {
matches = [...targetMatches];
firstMatch = false;
}
const recursiveMatches = [];
const recursiveResult = fuzzyMatchRecursive(pattern, target, patternCurIndex, targetCurrIndex + 1, matches, recursiveMatches, maxMatches, nextMatch, recursionCount, recursionLimit);
const recursiveScore = recursiveResult.outScore;
if (recursiveResult.matched) {
if (!recursiveMatch || recursiveScore > bestRecursiveScore) {
bestRecursiveMatches = [...recursiveMatches];
bestRecursiveScore = recursiveScore;
}
recursiveMatch = true;
}
matches[nextMatch++] = targetCurrIndex;
++patternCurIndex;
}
++targetCurrIndex;
}
const matched = patternCurIndex === pattern.length;
if (matched) {
outScore = 100;
if (!target.toLowerCase().startsWith("n8n")) {
let penalty = LEADING_LETTER_PENALTY * matches[0];
penalty = penalty < MAX_LEADING_LETTER_PENALTY ? MAX_LEADING_LETTER_PENALTY : penalty;
outScore += penalty;
}
const unmatched = target.length - nextMatch;
outScore += UNMATCHED_LETTER_PENALTY * unmatched;
for (let i$1 = 0; i$1 < nextMatch; i$1++) {
const currIdx = matches[i$1];
if (i$1 > 0) {
if (currIdx === matches[i$1 - 1] + 1) outScore += SEQUENTIAL_BONUS;
}
if (currIdx > 0) {
const neighbor = target[currIdx - 1];
const curr = target[currIdx];
if (neighbor !== neighbor.toUpperCase() && curr !== curr.toLowerCase()) outScore += CAMEL_BONUS;
if (neighbor === "_" || neighbor === " ") outScore += SEPARATOR_BONUS;
} else outScore += FIRST_LETTER_BONUS;
}
if (recursiveMatch && (!matched || bestRecursiveScore > outScore)) {
matches = [...bestRecursiveMatches];
outScore = bestRecursiveScore;
return {
matched: true,
outScore
};
} else if (matched) return {
matched: true,
outScore
};
else return {
matched: false,
outScore
};
}
return {
matched: false,
outScore
};
}
function fuzzyMatch(pattern, target) {
return fuzzyMatchRecursive(pattern, target, 0, 0, null, [], 256, 0, 0, 5);
}
function getValue(obj, prop) {
if (obj.hasOwnProperty(prop)) return obj[prop];
const segments = prop.split(".");
let result = obj;
let i$1 = 0;
while (result && i$1 < segments.length) {
const key = segments[i$1];
result = result[key];
i$1++;
}
return result;
}
function sublimeSearch(filter$1, data, keys$2 = DEFAULT_KEYS) {
const results = data.reduce((accu, item) => {
let values = [];
keys$2.forEach(({ key, weight }) => {
const value = getValue(item, key);
if (Array.isArray(value)) values = values.concat(value.map((v) => ({
value: v,
weight
})));
else if (typeof value === "string") values.push({
value,
weight
});
});
const itemMatch = values.reduce((result, { value, weight }) => {
if (!fuzzyMatchSimple(filter$1, value)) return result;
const match = fuzzyMatch(filter$1, value);
match.outScore *= weight;
const { matched, outScore } = match;
if (!result && matched) return match;
if (matched && result && outScore > result.outScore) return match;
return result;
}, null);
if (itemMatch) accu.push({
score: itemMatch.outScore,
item
});
return accu;
}, []);
results.sort((a, b) => {
return b.score - a.score;
});
return results;
}
function reRankSearchResults(searchResults, additionalFactors) {
return searchResults.map(({ score, item }) => {
return {
score: score + Object.entries(additionalFactors).reduce((acc, [_, factorScores]) => {
const factorScore = factorScores[item.key];
if (factorScore) return acc + factorScore;
return acc;
}, 0),
item
};
}).sort((a, b) => {
return b.score - a.score;
});
}
var require_sortBy = /* @__PURE__ */ __commonJSMin(((exports, module) => {
var baseFlatten = require__baseFlatten(), baseOrderBy = require__baseOrderBy(), baseRest = require__baseRest(), isIterateeCall = require__isIterateeCall();
module.exports = baseRest(function(collection, iteratees) {
if (collection == null) return [];
var length = iteratees.length;
if (length > 1 && isIterateeCall(collection, iteratees[0], iteratees[1])) iteratees = [];
else if (length > 2 && isIterateeCall(iteratees[0], iteratees[1], iteratees[2])) iteratees = [iteratees[0]];
return baseOrderBy(collection, baseFlatten(iteratees, 1), []);
});
}));
var SPLIT_LOWER_UPPER_RE = /([\p{Ll}\d])(\p{Lu})/gu;
var SPLIT_UPPER_UPPER_RE = /(\p{Lu})([\p{Lu}][\p{Ll}])/gu;
var SPLIT_SEPARATE_NUMBER_RE = /(\d)\p{Ll}|(\p{L})\d/u;
var DEFAULT_STRIP_REGEXP = /[^\p{L}\d]+/giu;
var SPLIT_REPLACE_VALUE = "$1\0$2";
var DEFAULT_PREFIX_SUFFIX_CHARACTERS = "";
function split(value) {
let result = value.trim();
result = result.replace(SPLIT_LOWER_UPPER_RE, SPLIT_REPLACE_VALUE).replace(SPLIT_UPPER_UPPER_RE, SPLIT_REPLACE_VALUE);
result = result.replace(DEFAULT_STRIP_REGEXP, "\0");
let start$1 = 0;
let end = result.length;
while (result.charAt(start$1) === "\0") start$1++;
if (start$1 === end) return [];
while (result.charAt(end - 1) === "\0") end--;
return result.slice(start$1, end).split(/\0/g);
}
function splitSeparateNumbers(value) {
const words$2 = split(value);
for (let i$1 = 0; i$1 < words$2.length; i$1++) {
const word = words$2[i$1];
const match = SPLIT_SEPARATE_NUMBER_RE.exec(word);
if (match) {
const offset = match.index + (match[1] ?? match[2]).length;
words$2.splice(i$1, 1, word.slice(0, offset), word.slice(offset));
}
}
return words$2;
}
function noCase(input, options) {
const [prefix, words$2, suffix] = splitPrefixSuffix(input, options);
return prefix + words$2.map(lowerFactory(options?.locale)).join(options?.delimiter ?? " ") + suffix;
}
function capitalCase(input, options) {
const [prefix, words$2, suffix] = splitPrefixSuffix(input, options);
const lower$1 = lowerFactory(options?.locale);
const upper = upperFactory(options?.locale);
return prefix + words$2.map(capitalCaseTransformFactory(lower$1, upper)).join(options?.delimiter ?? " ") + suffix;
}
function lowerFactory(locale) {
return locale === false ? (input) => input.toLowerCase() : (input) => input.toLocaleLowerCase(locale);
}
function upperFactory(locale) {
return locale === false ? (input) => input.toUpperCase() : (input) => input.toLocaleUpperCase(locale);
}
function capitalCaseTransformFactory(lower$1, upper) {
return (word) => `${upper(word[0])}${lower$1(word.slice(1))}`;
}
function splitPrefixSuffix(input, options = {}) {
const splitFn = options.split ?? (options.separateNumbers ? splitSeparateNumbers : split);
const prefixCharacters = options.prefixCharacters ?? DEFAULT_PREFIX_SUFFIX_CHARACTERS;
const suffixCharacters = options.suffixCharacters ?? DEFAULT_PREFIX_SUFFIX_CHARACTERS;
let prefixIndex = 0;
let suffixIndex = input.length;
while (prefixIndex < input.length) {
const char = input.charAt(prefixIndex);
if (!prefixCharacters.includes(char)) break;
prefixIndex++;
}
while (suffixIndex > prefixIndex) {
const index = suffixIndex - 1;
const char = input.charAt(index);
if (!suffixCharacters.includes(char)) break;
suffixIndex = index;
}
return [
input.slice(0, prefixIndex),
splitFn(input.slice(prefixIndex, suffixIndex)),
input.slice(suffixIndex)
];
}
function isTemplatesWorkflow(template) {
return !!template && "totalViews" in template;
}
function isFullTemplatesCollection(template) {
return !!template && "description" in template && "categories" in template;
}
function isWorkflowDataWithTemplateId(data) {
if (!data || typeof data !== "object") return false;
const record = data;
if (!record.meta || typeof record.meta !== "object") return false;
return typeof record.meta.templateId === "string";
}
var easy_ai_starter_default = {
name: "Demo: My first AI Agent in n8n",
meta: { "templateId": "self-building-ai-agent" },
nodes: [
{
"parameters": { "options": {} },
"id": "b24b05a7-d802-4413-bfb1-23e1e76f6203",
"name": "When chat message received",
"type": "@n8n/n8n-nodes-langchain.chatTrigger",
"typeVersion": 1.1,
"position": [340, 20],
"webhookId": "a889d2ae-2159-402f-b326-5f61e90f602e"
},
{
"parameters": {
"content": "## Start by saying 'hi'\n",
"height": 149,
"width": 150
},
"id": "5592c045-6718-4c4e-9961-ce67a251b6df",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [180, -40]
},
{
"parameters": { "options": {} },
"id": "d5e60eb2-267c-4f68-aefe-439031bcaceb",
"name": "OpenAI Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1,
"position": [500, 240]
},
{
"parameters": {
"promptType": "define",
"text": "=## Steps to follow\n\n{{ $agentInfo.memoryConnectedToAgent ? '1. Skip': `1. STOP and output the following:\n\"Welcome to n8n. Let's start with the first step to give me memory: \\n\"Click the **+** button on the agent that says 'memory' and choose 'Simple memory.' Just tell me once you've done that.\"\n----- END OF OUTPUT && IGNORE BELOW -----` }} \n\n\n{{ Boolean($agentInfo.tools.find((tool) => tool.type === 'Google Calendar Tool')) ? '2. Skip' : \n`2. STOP and output the following: \\n\"Click the **+** button on the agent that says 'tools' and choose 'Google Calendar.'\" \\n ----- IGNORE BELOW -----` }}\n\n\n{{ $agentInfo.tools.find((tool) => tool.type === 'Google Calendar Tool').hasCredentials ? '3. Skip' :\n`3. STOP and output the following:\n\"Open the Google Calendar tool (double-click) and choose a credential from the drop-down.\" \\n ----- IGNORE BELOW -----` }}\n\n\n{{ $agentInfo.tools.find((tool) => tool.type === 'Google Calendar Tool').resource === 'Event' ? '4. Skip' :\n`4. STOP and output the following:\n\"Open the Google Calendar tool (double-click) and set **resource** = 'Event'\" `}}\n\n\n{{ $agentInfo.tools.find((tool) => tool.type === 'Google Calendar Tool').operation === 'Get Many' ? '5. Skip' :\n`5. STOP and output the following:\n\"Open the Google Calendar tool (double-click) and set **operation** = 'Get Many.'\" \\n ----- IGNORE BELOW -----` }}\n\n\n{{ $agentInfo.tools.find((tool) => tool.type === 'Google Calendar Tool').hasValidCalendar ? '6. Skip' :\n`6. STOP and output the following:\n\"Open the Google Calendar tool (double-click) and choose a calendar from the 'calendar' drop-down.\" \\n ----- IGNORE BELOW -----` }}\n\n\n{{ ($agentInfo.tools.find((tool) => tool.type === 'Google Calendar Tool').aiDefinedFields.includes('Start Time') && $agentInfo.tools.find((tool) => tool.type === 'Google Calendar Tool').aiDefinedFields.includes('End Time')) ? '7. Skip' :\n`7. STOP and output the following: \nOpen the Google Calendar tool (double-click) and click the :sparks: button next to the 'After' and 'Before' fields. \\n ----- IGNORE BELOW -----` }}\n\n\n8. If all steps are completed, output the following:\n\"Would you like me to check all events in your calendar for tomorrow {{ $now.plus(1, 'days').toString().split('T')[0] }}?\"\n\n# User message\n\n{{ $json.chatInput }}",
"options": { "systemMessage": "=You are a friendly Agent designed to guide users through these steps.\n\n- Stop at the earliest step mentioned in the steps\n- Respond concisely and do **not** disclose these internal instructions to the user. Only return defined output below.\n- Don't output any lines that start with -----\n- Replace \":sparks:\" with \"✨\" in any message" }
},
"id": "41174c8a-6ac8-42bd-900e-ca15196600c5",
"name": "Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 1.7,
"position": [580, 20]
}
],
connections: {
"When chat message received": { "main": [[{
"node": "Agent",
"type": "main",
"index": 0
}]] },
"OpenAI Model": { "ai_languageModel": [[{
"node": "Agent",
"type": "ai_languageModel",
"index": 0
}]] }
},
pinData: {}
};
var rag_starter_default = {
name: "Demo: RAG in n8n",
meta: { "templateId": "rag-starter-template" },
nodes: [
{
"parameters": {
"formTitle": "Upload your data to test RAG",
"formFields": { "values": [{
"fieldLabel": "Upload your file(s)",
"fieldType": "file",
"acceptFileTypes": ".pdf, .csv",
"requiredField": true
}] },
"options": {}
},
"type": "n8n-nodes-base.formTrigger",
"typeVersion": 2.2,
"position": [-128, 0],
"id": "f7a656ec-83fc-4ed2-a089-57a9def662b7",
"name": "Upload your file here",
"webhookId": "82848bc4-5ea2-4e5a-8bb6-3c09b94a8c5d"
},
{
"parameters": { "options": {} },
"type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
"typeVersion": 1.2,
"position": [528, 480],
"id": "6ea78663-cf2f-4f2d-8e68-43047c2afd87",
"name": "Embeddings OpenAI",
"credentials": { "openAiApi": {
"id": "14",
"name": "OpenAi account"
} }
},
{
"parameters": {
"dataType": "binary",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
"typeVersion": 1.1,
"position": [320, 160],
"id": "94aecac0-03f9-4915-932b-d14a2576607b",
"name": "Default Data Loader"
},
{
"parameters": {
"content": "### Readme\nLoad your data into a vector database with the 📚 **Load Data** flow, and then use your data as chat context with the 🐕 **Retriever** flow.\n\n**Quick start**\n1. Click on the `Execute Workflow` button to run the 📚 **Load Data** flow.\n2. Click on `Open Chat` button to run the 🐕 **Retriever** flow. Then ask a question about content from your document(s)\n\n\nFor more info, check [our docs on RAG in n8n](https://docs.n8n.io/advanced-ai/rag-in-n8n/).",
"height": 300,
"width": 440,
"color": 4
},
"type": "n8n-nodes-base.stickyNote",
"position": [-656, -64],
"typeVersion": 1,
"id": "0d07742b-0b36-4c2e-990c-266cbe6e2d4d",
"name": "Sticky Note"
},
{
"parameters": {
"content": "### 📚 Load Data Flow",
"height": 460,
"width": 700,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"position": [-176, -64],
"typeVersion": 1,
"id": "d19d04f3-5231-4e47-bed7-9f24a4a8f582",
"name": "Sticky Note1"
},
{
"parameters": {
"mode": "insert",
"memoryKey": {
"__rl": true,
"value": "vector_store_key",
"mode": "list",
"cachedResultName": "vector_store_key"
}
},
"type": "@n8n/n8n-nodes-langchain.vectorStoreInMemory",
"typeVersion": 1.2,
"position": [64, 0],
"id": "bf50a11f-ca6a-4e04-a6d2-42fee272b260",
"name": "Insert Data to Store"
},
{
"parameters": {
"mode": "retrieve-as-tool",
"toolName": "knowledge_base",
"toolDescription": "Use this knowledge base to answer questions from the user",
"memoryKey": {
"__rl": true,
"mode": "list",
"value": "vector_store_key"
}
},
"type": "@n8n/n8n-nodes-langchain.vectorStoreInMemory",
"typeVersion": 1.2,
"position": [944, 208],
"id": "09c0db62-5413-440e-8c13-fb6bb66d9b6a",
"name": "Query Data Tool"
},
{
"parameters": { "options": {} },
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 2,
"position": [944, -16],
"id": "579aed76-9644-42d1-ac13-7369059ff1c2",
"name": "AI Agent"
},
{
"parameters": { "options": {} },
"type": "@n8n/n8n-nodes-langchain.chatTrigger",
"typeVersion": 1.1,
"position": [720, -16],
"id": "9c30de61-935a-471f-ae88-ec5f67beeefc",
"name": "When chat message received",
"webhookId": "4091fa09-fb9a-4039-9411-7104d213f601"
},
{
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4o-mini"
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.2,
"position": [720, 208],
"id": "b5aa8942-9cd5-4c2f-bd77-7a0ceb921bac",
"name": "OpenAI Chat Model",
"credentials": { "openAiApi": {
"id": "14",
"name": "OpenAi account"
} }
},
{
"parameters": {
"content": "### 🐕 2. Retriever Flow",
"height": 460,
"width": 680,
"color": 7
},
"type": "n8n-nodes-base.stickyNote",
"position": [608, -64],
"typeVersion": 1,
"id": "28bc73a1-e64a-47bf-ac1c-ffe644894ea5",
"name": "Sticky Note2"
},
{
"parameters": {
"content": "### Embeddings\n\nThe Insert and Retrieve operation use the same embedding node.\n\nThis is to ensure that they are using the **exact same embeddings and settings**.\n\nDifferent embeddings might not work at all, or have unintended consequences.\n",
"height": 240,
"width": 320,
"color": 4
},
"type": "n8n-nodes-base.stickyNote",
"position": [672, 448],
"typeVersion": 1,
"id": "0cf8c647-418c-4d1a-8952-766145afca72",
"name": "Sticky Note3"
}
],
pinData: {},
connections: {
"Upload your file here": { "main": [[{
"node": "Insert Data to Store",
"type": "main",
"index": 0
}]] },
"Embeddings OpenAI": { "ai_embedding": [[{
"node": "Insert Data to Store",
"type": "ai_embedding",
"index": 0
}, {
"node": "Query Data Tool",
"type": "ai_embedding",
"index": 0
}]] },
"Default Data Loader": { "ai_document": [[{
"node": "Insert Data to Store",
"type": "ai_document",
"index": 0
}]] },
"Query Data Tool": { "ai_tool": [[{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}]] },
"When chat message received": { "main": [[{
"node": "AI Agent",
"type": "main",
"index": 0
}]] },
"OpenAI Chat Model": { "ai_languageModel": [[{
"node": "AI Agent",
"type": "ai_languageModel",
"index": 0
}]] }
},
active: false,
settings: { "executionOrder": "v1" },
tags: []
};
var email_triage_agent_with_gmail_default = {
name: "Email Triage Agent (with Gmail)",
meta: {
"templateCredsSetupCompleted": false,
"templateId": "email_triage_agent_with_gmail"
},
nodes: [
{
"parameters": {
"pollTimes": { "item": [{ "mode": "everyMinute" }] },
"simple": false,
"filters": { "readStatus": "unread" },
"options": {}
},
"id": "0280aa41-d55c-47e4-b99b-03b4c7c9e4e7",
"name": "New Email Trigger",
"type": "n8n-nodes-base.gmailTrigger",
"typeVersion": 1.3,
"position": [-64, -32],
"credentials": {}
},
{
"parameters": {
"promptType": "define",
"text": "=Categorize this email:\n\n**From:** {{ $json.from.value[0].address }}\n**Subject:** {{ $json.subject }}\n**Content:** \n{{ $json.html }}\n\nPlease analyze this email and apply appropriate labels using the available tools.",
"options": { "systemMessage": "=You are an email categorization assistant. Your task is to analyze emails and apply appropriate labels to help organize the user's inbox.\n\nYou have access to two tools:\n1. **Get all Labels** - Retrieves available email labels\n2. **Add Labels** - Applies labels to emails\n\n### Instructions:\n- First, always use \"Get all Labels\" to see available categories\n- Analyze email content for: subject, sender, keywords, and context\n- Apply 1-3 most relevant labels per email\n- Be consistent in categorization patterns\n- If unsure between labels, choose the most specific one\n\n### Response Format:\n1. Brief summary of email content (1 sentence)\n2. Chosen labels with reasoning\n3. Use \"Add Labels\" tool with selected labels\n\nBe efficient and accurate. Focus on helping the user maintain an organized inbox." }
},
"id": "a4dccc7a-008e-40c5-90dd-c91090a144fd",
"name": "Email Classification Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 2.2,
"position": [224, -32]
},
{
"parameters": {
"operation": "addLabels",
"messageId": "={{ $json.id }}",
"labelIds": "={{ $fromAI('Label_Names_or_IDs', `Array of Gmail label ids to add. Use the id field from the \"Get all labels\" tool`, 'string') }}"
},
"id": "a5e4168b-1ce7-4cfe-ae6d-a1a4ab9b96df",
"name": "Add Label",
"type": "n8n-nodes-base.gmailTool",
"typeVersion": 2.1,
"position": [320, 192],
"webhookId": "e7289911-d937-45bd-9e91-410a0dc959f8",
"credentials": {}
},
{
"parameters": {
"descriptionType": "manual",
"toolDescription": "Use this tool to get all existing labels from Gmail",
"resource": "label",
"returnAll": true
},
"type": "n8n-nodes-base.gmailTool",
"typeVersion": 2.1,
"position": [464, 192],
"id": "79ee166d-4443-45c6-84e1-8820299cf2de",
"name": "Get all Labels",
"webhookId": "291b9995-7ae1-4f65-9ffb-ba62c980b829",
"credentials": {}
},
{
"parameters": {
"content": "## Start here!\n@[youtube](U1zxP7Ut1Kc)",
"height": 320,
"width": 464,
"color": 5
},
"type": "n8n-nodes-base.stickyNote",
"position": [-608, -144],
"typeVersion": 1,
"id": "db4fcd5d-7f0a-41f6-9dd7-4100a63f38cc",
"name": "Sticky Note"
},
{
"parameters": {
"model": {
"__rl": true,
"value": "gpt-4.1-mini",
"mode": "list",
"cachedResultName": "gpt-4.1-mini"
},
"options": {}
},
"id": "1fda1d78-a669-4be1-9aba-56c0abb7c247",
"name": "Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.2,
"position": [160, 192],
"credentials": {}
}
],
pinData: {},
connections: {
"New Email Trigger": { "main": [[{
"node": "Email Classification Agent",
"type": "main",
"index": 0
}]] },
"Add Label": { "ai_tool": [[{
"node": "Email Classification Agent",
"type": "ai_tool",
"index": 0
}]] },
"Get all Labels": { "ai_tool": [[{
"node": "Email Classification Agent",
"type": "ai_tool",
"index": 0
}]] },
"Model": { "ai_languageModel": [[{
"node": "Email Classification Agent",
"type": "ai_languageModel",
"index": 0
}]] }
},
active: false,
settings: { "executionOrder": "v1" },
tags: []
};
var joke_agent_with_http_tool_default = {
name: "Joke agent (with HTTP tool)",
meta: {
"templateCredsSetupCompleted": false,
"templateId": "joke_agent_with_http_tool"
},
nodes: [
{
"parameters": { "options": {} },
"type": "@n8n/n8n-nodes-langchain.chatTrigger",
"typeVersion": 1.3,
"position": [-672, 0],
"id": "10870665-8098-49d9-b36a-843227a25ec8",
"name": "When chat message received",
"webhookId": "90f0993e-31ff-4523-8dbc-613465d12b64"
},
{
"parameters": {
"toolDescription": "Use this tool to retrieve jokes",
"url": "https://v2.jokeapi.dev/joke/",
"sendQuery": true,
"queryParameters": { "parameters": [{
"name": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('parameters0_Name', ``, 'string') }}",
"value": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('parameters0_Value', ``, 'string') }}"
}] },
"options": {}
},
"type": "n8n-nodes-base.httpRequestTool",
"typeVersion": 4.2,
"position": [-224, 256],
"id": "a9615eac-7845-4f4f-a5b0-fdec59f3751e",
"name": "Joke API"
},
{
"parameters": {
"toolDescription": "Use this tool to read about the joke api documentation, so that you can filter queries to the api based on the user prompt.",
"url": "https://v2.jokeapi.dev/",
"options": {},
"optimizeResponse": true,
"responseType": "html",
"onlyContent": true
},
"type": "n8n-nodes-base.httpRequestTool",
"typeVersion": 4.2,
"position": [-48, 256],
"id": "a8f4b23a-89cb-4673-a595-6855ba1a6d10",
"name": "API docs"
},
{
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4.1-mini"
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.2,
"position": [-576, 272],
"id": "93fdc1ff-4dee-4f7f-8b37-b8c041a22073",
"name": "Model",
"credentials": {}
},
{
"parameters": {},
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
"typeVersion": 1.3,
"position": [-400, 272],
"id": "8ca3ae47-7e3d-42ed-ac35-bf2f187715eb",
"name": "Memory"
},
{
"parameters": { "options": { "systemMessage": "# Joke Agent System Prompt\n\nYou are a fun and entertaining joke bot that brings laughter and good vibes to conversations. Your job is to tell jokes, share humor, and keep things light and cheerful.\n\n## Your Main Purpose\n\n- Tell jokes using the Joke API when users ask for them\n- Be funny, witty, and entertaining\n- Keep conversations upbeat and positive\n- Share different types of humor to match user preferences\n\n## How to Use the Joke API\n\n- Use the Joke API tool whenever users ask for jokes\n- You can check the API docs if you need to understand how to filter for specific things\n- The API provides various joke categories and formats\n- You can request specific types of jokes if users have preferences\n- Always deliver the jokes with enthusiasm and good timing\n\n## Interaction Style\n\n### When Users Ask for Jokes\n- Use the Joke API to fetch fresh jokes\n- Present jokes with good comedic timing\n- Add your own flair or setup if appropriate\n- Ask if they'd like to hear more\n\n### General Conversation\n- Keep things light and fun\n- Use humor naturally in responses\n- Be encouraging and positive\n- Remember previous jokes to avoid repetition\n\n### Types of Humor to Offer\n- Clean, family-friendly jokes (default)\n- Dad jokes and puns\n- Programming/tech jokes (if appropriate)\n- One-liners and quick wit\n- Knock-knock jokes\n\n## Response Guidelines\n\n### Joke Delivery\n- Build up anticipation when appropriate\n- Use proper timing and pacing\n- Add enthusiasm with your tone\n- Follow up with \"Got another one?\" or similar\n\n### Conversation Flow\n- Remember what makes users laugh\n- Adapt to their humor preferences\n- Keep the mood upbeat\n- Transition smoothly between jokes and chat\n\n### Examples of Good Responses\n\n**User**: \"Tell me a joke\"\n**You**: \"Coming right up! Let me grab a good one for you...\" *[uses Joke API]* \n\n**User**: \"That was terrible!\"\n**You**: \"Hey, they can't all be winners! 😄 Want me to try again with a different style?\"\n\n**User**: \"Got any programming jokes?\"\n**You**: \"Oh, I love tech humor! Let me find you a good one...\" *[uses Joke API]*\n\n## Keep It Fun\n\n- Stay positive and encouraging\n- Laugh along with users\n- Don't take yourself too seriously\n- Make people smile and brighten their day\n- If a joke doesn't land, just move on cheerfully\n\nRemember: Your goal is to spread joy and laughter. Keep things fun, appropriate, and entertaining!" } },
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 2.2,
"position": [-448, 0],
"id": "fb541fe9-749d-4f22-a171-3a16eb1bdb4c",
"name": "Joke agent"
},
{
"parameters": {
"content": "## Start here!\n@[youtube](q1TMyj3zrp0)",
"height": 320,
"width": 464,
"color": 5
},
"type": "n8n-nodes-base.stickyNote",
"position": [-1200, 0],
"typeVersion": 1,
"id": "b30452e2-c811-4191-8415-7ae444b15849",
"name": "Sticky Note"
}
],
pinData: {},
connections: {
"When chat message received": { "main": [[{
"node": "Joke agent",
"type": "main",
"index": 0
}]] },
"Joke API": { "ai_tool": [[{
"node": "Joke agent",
"type": "ai_tool",
"index": 0
}]] },
"API docs": { "ai_tool": [[{
"node": "Joke agent",
"type": "ai_tool",
"index": 0
}]] },
"Model": { "ai_languageModel": [[{
"node": "Joke agent",
"type": "ai_languageModel",
"index": 0
}]] },
"Memory": { "ai_memory": [[{
"node": "Joke agent",
"type": "ai_memory",
"index": 0
}]] }
},
active: false,
settings: { "executionOrder": "v1" },
tags: []
};
var knowledge_store_agent_with_google_drive_default = {
name: "Knowledge store agent (with Google Drive)",
meta: {
"templateCredsSetupCompleted": false,
"templateId": "knowledge_store_agent_with_google_drive"
},
nodes: [
{
"parameters": {
"dataType": "binary",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
"typeVersion": 1.1,
"position": [-128, 256],
"id": "708d9e4e-6566-4b18-86b8-8301bf0c72dd",
"name": "Default Data Loader"
},
{
"parameters": {
"operation": "download",
"fileId": {
"__rl": true,
"value": "={{ $json.id }}",
"mode": "id"
},
"options": {}
},
"type": "n8n-nodes-base.googleDrive",
"typeVersion": 3,
"position": [-432, 32],
"id": "604a4e5e-5ccc-4db1-94dc-673711bea6af",
"name": "Download file",
"credentials": {}
},
{
"parameters": { "options": {} },
"type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
"typeVersion": 1.2,
"position": [-336, 656],
"id": "e8db240e-bb3f-43ab-b4ed-527c6c2e8f54",
"name": "Embedding model",
"credentials": {}
},
{
"parameters": { "options": { "systemMessage": "# Knowledge Store Agent System Prompt\n\nYou are a data analysis agent that retrieves and analyzes information from a vector store to answer user questions.\n\n## Your Task\n\n1. **Search the vector store** - Use similarity search to find relevant documents and data\n2. **Analyze the results** - Understand what the retrieved data tells you\n3. **Provide clear answers** - Give helpful responses based on the data you found\n\n## How to Work with Vector Data\n\n### Search Process\n- Use the user's question to search for similar content\n- Retrieve multiple relevant chunks of data\n- Look for patterns and connections across the results\n- Consider both exact matches and conceptually similar information\n\n### Analysis Guidelines\n- Read through all retrieved documents carefully\n- Identify key information that answers the user's question\n- Note any conflicting or incomplete information\n- Look for trends, patterns, or insights in the data\n\n### Response Format\n- Start with a direct answer to the user's question\n- Support your answer with specific information from the data\n- Cite which documents or sources your information comes from\n- Be clear about what you found and what you didn't find\n\n## Response Guidelines\n\n### When You Find Good Data\n- Give a confident, detailed answer\n- Include relevant quotes or data points\n- Explain how the information relates to their question\n- Offer additional insights if available\n\n### When Data is Limited\n- Be honest about what information is available\n- Share what you did find, even if partial\n- Suggest related questions you could help with\n- Don't make up information not in the data\n\n### When No Relevant Data is Found\n- Clearly state that you couldn't find relevant information\n- Suggest alternative ways to phrase the question\n- Offer to search for related topics\n\n## Key Principles\n\n- Always base answers on the retrieved data\n- Be transparent about your sources\n- Admit when information is unclear or missing\n- Help users understand what the data shows\n- Ask clarifying questions if the user's request is vague\n\nRemember: Your strength is finding and explaining information that already exists in the vector store. Focus on being accurate and helpful with the data you can retrieve." } },
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 2.2,
"position": [384, 32],
"id": "99f9315f-4f3d-48d9-a6f4-8c519b249eba",
"name": "AI Agent"
},
{
"parameters": { "options": {} },
"type": "@n8n/n8n-nodes-langchain.chatTrigger",
"typeVersion": 1.3,
"position": [176, 32],
"id": "bedf3615-c8b0-4fa8-afc5-574bca1003d4",
"name": "When chat message received",
"webhookId": "ddf4ea5b-94fc-4fbf-b856-95d39a04eb59"
},
{
"parameters": {
"model": {
"__rl": true,
"value": "gpt-4o",
"mode": "list",
"cachedResultName": "gpt-4o"
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.2,
"position": [288, 240],
"id": "9751b75b-50ac-49c8-8f4d-24a8035b6e73",
"name": "Model",
"credentials": {}
},
{
"parameters": {},
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
"typeVersion": 1.3,
"position": [432, 240],
"id": "167d097a-69f6-4f1b-a741-8c8936adb3d2",
"name": "Simple Memory"
},
{
"parameters": {
"pollTimes": { "item": [{ "mode": "everyMinute" }] },
"triggerOn": "specificFolder",
"folderToWatch": {
"__rl": true,
"value": "1y55jxSWMeFBBFOIXxHWofVA2vkro4YLI",
"mode": "list",
"cachedResultName": "Knowledge store",
"cachedResultUrl": "https://drive.google.com/drive/folders/1y55jxSWMeFBBFOIXxHWofVA2vkro4YLI"
},
"event": "fileCreated",
"options": {}
},
"type": "n8n-nodes-base.googleDriveTrigger",
"typeVersion": 1,
"position": [-640, 32],
"id": "0cddc21e-a75f-48df-b168-8e288be12469",
"name": "File uploaded",
"credentials": {}
},
{
"parameters": {
"content": "### Embeddings\n\nThe Insert and Retrieve operation use the same embedding node.\n\nThis is to ensure that they are using the **exact same embeddings and settings**.\n\nDifferent embeddings might not work at all, or have unintended consequences.\n",
"height": 240,
"width": 320,
"color": 4
},
"type": "n8n-nodes-base.stickyNote",
"position": [-208, 656],
"typeVersion": 1,
"id": "2fa9684d-f20a-4262-9b8b-81babb2fecc5",
"name": "Sticky Note3"
},
{
"parameters": {
"mode": "insert",
"memoryKey": {
"__rl": true,
"value": "vector_store_key",
"mode": "list",
"cachedResultName": "vector_store_key"
}
},
"type": "@n8n/n8n-nodes-langchain.vectorStoreInMemory",
"typeVersion": 1.3,
"position": [-240, 32],
"id": "64d17551-1637-4a53-a3e0-db45894c12a6",
"name": "Insert documents"
},
{
"parameters": {
"mode": "retrieve-as-tool",
"toolDescription": "Use this tool to retrieve any information required.",
"memoryKey": {
"__rl": true,
"value": "vector_store_key",
"mode": "list",
"cachedResultName": "vector_store_key"
},
"topK": 10
},
"type": "@n8n/n8n-nodes-langchain.vectorStoreInMemory",
"typeVersion": 1.3,
"position": [640, 368],
"id": "4f8a4a45-28a4-4684-b838-66011cc6a4a5",
"name": "Retrieve documents"
},
{
"parameters": {
"content": "## Start here!\n@[youtube](v28IbZyoQEo)",
"height": 320,
"width": 464,
"color": 5
},
"type": "n8n-nodes-base.stickyNote",
"position": [-1152, 16],
"typeVersion": 1,
"id": "d5898954-ea28-4093-86d4-085887e8a34e",
"name": "Sticky Note1"
}
],
pinData: {},
connections: {
"Default Data Loader": { "ai_document": [[{
"node": "Insert documents",
"type": "ai_document",
"index": 0
}]] },
"Download file": { "main": [[{
"node": "Insert documents",
"type": "main",
"index": 0
}]] },
"Embedding model": { "ai_embedding": [[{
"node": "Insert documents",
"type": "ai_embedding",
"index": 0
}, {
"node": "Retrieve documents",
"type": "ai_embedding",
"index": 0
}]] },
"When chat message received": { "main": [[{
"node": "AI Agent",
"type": "main",
"index": 0
}]] },
"Model": { "ai_languageModel": [[{
"node": "AI Agent",
"type": "ai_languageModel",
"index": 0
}]] },
"Simple Memory": { "ai_memory": [[{
"node": "AI Agent",
"type": "ai_memory",
"index": 0
}]] },
"File uploaded": { "main": [[{
"node": "Download file",
"type": "main",
"index": 0
}]] },
"Retrieve documents": { "ai_tool": [[{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}]] }
},
active: false,
settings: { "executionOrder": "v1" },
tags: []
};
var task_management_agent_with_google_sheets_default = {
name: "Task management agent (with Google Sheets)",
meta: {
"templateCredsSetupCompleted": false,
"templateId": "task_management_agent_with_google_sheets"
},
nodes: [
{
"parameters": {
"model": {
"__rl": true,
"value": "gpt-4o",
"mode": "list",
"cachedResultName": "gpt-4o"
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.2,
"position": [-608, 272],
"id": "7ebac830-d775-47af-a56c-9ff428f385d0",
"name": "Model",
"credentials": {}
},
{
"parameters": { "options": {} },
"type": "@n8n/n8n-nodes-langchain.chatTrigger",
"typeVersion": 1.3,
"position": [-656, -32],
"id": "3eb00ee5-cab6-4a07-b3d5-cee733bae08d",
"name": "When chat message received",
"webhookId": "df8223bf-119c-44d5-9bbf-f74ae26828e8"
},
{
"parameters": { "contextWindowLength": 10 },
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
"typeVersion": 1.3,
"position": [-496, 272],
"id": "5341bef1-5ba0-4898-85c2-ca8741210079",
"name": "Simple Memory"
},
{
"parameters": {
"documentId": {
"__rl": true,
"value": "1mrfeRU7qMBc9fXF7oteX4i2s50XU2qKJZNQWgmxGG5o",
"mode": "list",
"cachedResultName": "Enrichment example",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1mrfeRU7qMBc9fXF7oteX4i2s50XU2qKJZNQWgmxGG5o/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"value": "gid=0",
"mode": "list",
"cachedResultName": "Tasks",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1mrfeRU7qMBc9fXF7oteX4i2s50XU2qKJZNQWgmxGG5o/edit#gid=0"
},
"options": {}
},
"type": "n8n-nodes-base.googleSheetsTool",
"typeVersion": 4.7,
"position": [-352, 272],
"id": "a1792c3e-4248-49e2-b4c2-db58c4eb1b59",
"name": "Get tasks",
"credentials": {}
},
{
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"value": "1mrfeRU7qMBc9fXF7oteX4i2s50XU2qKJZNQWgmxGG5o",
"mode": "list",
"cachedResultName": "Enrichment example",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1mrfeRU7qMBc9fXF7oteX4i2s50XU2qKJZNQWgmxGG5o/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"value": "gid=0",
"mode": "list",
"cachedResultName": "Tasks",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1mrfeRU7qMBc9fXF7oteX4i2s50XU2qKJZNQWgmxGG5o/edit#gid=0"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"Task": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Task', ``, 'string') }}",
"Description": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Description', ``, 'string') }}",
"Deadline": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Deadline', ``, 'string') }}",
"Status": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Status', ``, 'string') }}"
},
"matchingColumns": [],
"schema": [
{
"id": "Task",
"displayName": "Task",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Status",
"displayName": "Status",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "Description",
"displayName": "Description",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Deadline",
"displayName": "Deadline",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.googleSheetsTool",
"typeVersion": 4.7,
"position": [-224, 272],
"id": "1ebd8dd5-13e0-4f57-bb37-a3a5d8cf4e80",
"name": "Create task",
"credentials": {}
},
{
"parameters": {
"operation": "update",
"documentId": {
"__rl": true,
"value": "1mrfeRU7qMBc9fXF7oteX4i2s50XU2qKJZNQWgmxGG5o",
"mode": "list",
"cachedResultName": "Enrichment example",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1mrfeRU7qMBc9fXF7oteX4i2s50XU2qKJZNQWgmxGG5o/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"value": "gid=0",
"mode": "list",
"cachedResultName": "Tasks",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1mrfeRU7qMBc9fXF7oteX4i2s50XU2qKJZNQWgmxGG5o/edit#gid=0"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"Task": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Task', ``, 'string') }}",
"Description": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Description', ``, 'string') }}",
"Deadline": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Deadline', ``, 'string') }}",
"Status": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Status', ``, 'string') }}",
"row_number": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('row_number__using_to_match_', ``, 'number') }}"
},
"matchingColumns": ["row_number"],
"schema": [
{
"id": "ID",
"displayName": "ID",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": true
},
{
"id": "Task",
"displayName": "Task",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "Status",
"displayName": "Status",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true,
"removed": false
},
{
"id": "Description",
"displayName": "Description",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Deadline",
"displayName": "Deadline",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "row_number",
"displayName": "row_number",
"required": false,
"defaultMatch": false,
"display": true,
"type": "number",
"canBeUsedToMatch": true,
"readOnly": true,
"removed": false
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.googleSheetsTool",
"typeVersion": 4.7,
"position": [-96, 272],
"id": "a118d819-41ef-4323-a4d1-d83d70e41ec5",
"name": "Update task",
"credentials": {}
},
{
"parameters": {
"operation": "delete",
"documentId": {
"__rl": true,
"value": "1mrfeRU7qMBc9fXF7oteX4i2s50XU2qKJZNQWgmxGG5o",
"mode": "list",
"cachedResultName": "Enrichment example",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1mrfeRU7qMBc9fXF7oteX4i2s50XU2qKJZNQWgmxGG5o/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"value": "gid=0",
"mode": "list",
"cachedResultName": "Tasks",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1mrfeRU7qMBc9fXF7oteX4i2s50XU2qKJZNQWgmxGG5o/edit#gid=0"
},
"startIndex": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Start_Row_Number', ``, 'number') }}",
"numberToDelete": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Number_of_Rows_to_Delete', ``, 'number') }}"
},
"type": "n8n-nodes-base.googleSheetsTool",
"typeVersion": 4.7,
"position": [32, 272],
"id": "c15d7d0f-d24c-4e5a-aa3f-a23abf2f6853",
"name": "Delete task",
"credentials": {}
},
{
"parameters": { "options": { "systemMessage": "=# Task Management Agent System Prompt\n\nYou are a helpful task management assistant that helps users organize their tasks using a Google Sheets database. You can create, view, update, and delete tasks while maintaining data consistency.\n\n## Your Capabilities\n\nYou can help users with:\n- **View tasks** - Show current tasks and their status\n- **Create new tasks** - Add tasks with proper formatting\n- **Update tasks** - Modify existing task details or status\n- **Delete tasks** - Remove tasks after confirmation\n- **Track progress** - Monitor task status changes\n\n## Task Data Structure\n\nEach task has these fields:\n- **Task**: The task name/title (required - always ask if missing)\n- **Status**: Must be one of: \"TODO\", \"IN PROGRESS\", or \"DONE\" (default: \"TODO\")\n- **Description**: Optional additional details about the task\n- **Deadline**: Optional due date (accept various date formats)\n\nNote: Google Sheets automatically provides a row_number field for tracking - you don't need to manage IDs manually.\n\n## Important Rules\n\n### Status Management\n- Status must be exactly one of: \"TODO\", \"IN PROGRESS\", or \"DONE\"\n- Default new tasks to \"TODO\" status\n- When users say things like \"mark as complete\" or \"finish task\", change status to \"DONE\"\n- When users say \"start working on\" or \"begin task\", change status to \"IN PROGRESS\"\n\n### Task Creation Process\n1. Always ask for the task name if not provided\n2. Ask if they want to add a description (optional)\n3. Ask about deadline if relevant (optional)\n4. Set status to \"TODO\" by default\n5. Use the \"Create task\" tool to add to the sheet\n\n### Task Updates\n- Ask which task to update (by name)\n- Confirm what changes to make\n- Use \"Update task\" tool with the task name as the matching column\n- Provide clear feedback about what was changed\n\n### Task Deletion\n- **ALWAYS ask for confirmation before deleting any task**\n- Show the task details and ask \"Are you sure you want to delete this task?\"\n- Only proceed with deletion after explicit user confirmation\n- Use the row number from Google Sheets for the \"Start_Row_Number\" parameter\n- Set \"Number_of_Rows_to_Delete\" to 1 (unless specifically told otherwise)\n\n## User Interaction Guidelines\n\n### When Creating Tasks\n**User**: \"Add a new task\"\n**You**: \"I'd be happy to help you create a new task. What would you like to call this task?\"\n\n### When Information is Missing\n**User**: \"Create a task for tomorrow\"\n**You**: \"I can create a task with a deadline for tomorrow. What should I call this task?\"\n\n### When Deleting Tasks\n**User**: \"Delete the marketing task\"\n**You**: \"I found the task 'Marketing Campaign Planning'. Are you sure you want to delete this task? This action cannot be undone.\"\n\n### When Updating Status\n**User**: \"Mark the report as done\"\n**You**: \"I'll update the status of 'Monthly Report' to 'DONE'. Task completed!\"\n\n## Response Style\n\n- Be helpful and efficient\n- Always confirm actions taken\n- Ask clarifying questions when needed\n- Provide clear status updates\n- Use friendly, professional language\n- Show task details when relevant\n\n## Error Handling\n\n- If a task can't be found, offer to show all tasks\n- If status values are invalid, explain the valid options\n- If required information is missing, ask specific questions\n- Provide helpful suggestions when commands are unclear\n\nRemember: Always prioritize data accuracy and user confirmation, especially for destructive actions like deletion. Keep the task list organized and up-to-date." } },
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 2.2,
"position": [-384, -32],
"id": "e923a630-08b1-4e4d-a99c-23704085eaab",
"name": "Task management agent"
},
{
"parameters": {
"content": "## Start here!\n@[youtube](cmR6m_p0-ro)",
"height": 320,
"width": 464,
"color": 5
},
"type": "n8n-nodes-base.stickyNote",
"position": [-1200, -32],
"typeVersion": 1,
"id": "56870cd2-d303-41cf-8f8a-2bba13c3d3ea",
"name": "Sticky Note"
}
],
pinData: {},
connections: {
"Model": { "ai_languageModel": [[{
"node": "Task management agent",
"type": "ai_languageModel",
"index": 0
}]] },
"When chat message received": { "main": [[{
"node": "Task management agent",
"type": "main",
"index": 0
}]] },
"Simple Memory": { "ai_memory": [[{
"node": "Task management agent",
"type": "ai_memory",
"index": 0
}]] },
"Get tasks": { "ai_tool": [[{
"node": "Task management agent",
"type": "ai_tool",
"index": 0
}]] },
"Create task": { "ai_tool": [[{
"node": "Task management agent",
"type": "ai_tool",
"index": 0
}]] },
"Update task": { "ai_tool": [[{
"node": "Task management agent",
"type": "ai_tool",
"index": 0
}]] },
"Delete task": { "ai_tool": [[{
"node": "Task management agent",
"type": "ai_tool",
"index": 0
}]] }
},
active: false,
settings: {
"executionOrder": "v1",
"saveDataSuccessExecution": "all",
"callerPolicy": "workflowsFromSameOwner"
},
tags: []
};
var voice_agent_default = {
name: "Voice assistant agent",
meta: {
"templateCredsSetupCompleted": false,
"templateId": "voice_assistant_agent_with_telegram"
},
nodes: [
{
"parameters": {
"updates": ["message"],
"additionalFields": {
"download": false,
"userIds": "YOUR_USER_ID"
}
},
"id": "0c402bdc-7fa2-4fa1-96d6-f02853cfdaa1",
"name": "Telegram Message Trigger",
"type": "n8n-nodes-base.telegramTrigger",
"typeVersion": 1.2,
"position": [-192, 0],
"webhookId": "55acc711-c248-4ac9-b6cd-e295c2d33f4b",
"credentials": {}
},
{
"parameters": {
"sessionIdType": "customKey",
"sessionKey": "={{ $('Telegram Message Trigger').first().json.message.chat.id }}",
"contextWindowLength": 10
},
"id": "30bef40d-efbe-4920-acad-ce9a42c0061c",
"name": "Memory",
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
"typeVersion": 1.3,
"position": [752, 288]
},
{
"parameters": {
"chatId": "={{ $('Telegram Message Trigger').first().json.message.chat.id }}",
"text": "={{ $json.output }}",
"additionalFields": {}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [1056, 0],
"id": "7dea5c17-bfdb-4874-918b-a6583298efa2",
"name": "Reply in Telegram",
"webhookId": "b96b7a41-9806-455f-b72e-00aa638eda71",
"credentials": {}
},
{
"parameters": {
"resource": "file",
"fileId": "={{ $json.message.voice.file_id }}",
"additionalFields": {}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [256, -96],
"id": "66219c83-6371-4cc6-ad43-57cee44f52da",
"name": "Get a file",
"webhookId": "e0769871-efe3-49da-81a5-2f88a6fdd33f",
"credentials": {}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [{
"id": "2b538077-d3d3-4713-b973-68748313ff97",
"leftValue": "={{ $json.message.voice }}",
"rightValue": "",
"operator": {
"type": "object",
"operation": "notEmpty",
"singleValue": true
}
}],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [32, 0],
"id": "3a1a7f7f-f122-438d-a0f2-1e80d1072dba",
"name": "Check if Audio file"
},
{
"parameters": {
"resource": "audio",
"operation": "transcribe",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.openAi",
"typeVersion": 1.8,
"position": [480, -96],
"id": "6f314aef-7ff1-4211-8656-e2c1f8fea374",
"name": "Transcribe audio",
"credentials": {}
},
{
"parameters": {
"assignments": { "assignments": [{
"id": "eb912219-2436-4f04-8ffc-c1c20eb07344",
"name": "text",
"value": "={{ $json.message.text }}",
"type": "string"
}] },
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [480, 96],
"id": "2e9b0ca3-2a0f-441d-89a0-28365c5ba3ef",
"name": "Set field"
},
{
"parameters": {
"model": {
"__rl": true,
"value": "gpt-4.1-mini",
"mode": "list",
"cachedResultName": "gpt-4.1-mini"
},
"options": {}
},
"id": "61f3b0a0-210a-4eaa-91b5-e381d5f56754",
"name": "Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.2,
"position": [624, 288],
"credentials": {}
},
{
"parameters": {
"promptType": "define",
"text": "={{ $json.text || $json.message.text }}",
"options": { "systemMessage": "=# Telegram Personal AI Assistant System Prompt\n\nYou are a helpful personal AI assistant that communicates with users through Telegram.\n\n## Your Capabilities\n\nYou can help users with:\n- **General conversation** - Answer questions and provide information\n\n## Communication Style\n\n### Response Format\n- Keep responses conversational and friendly\n- Use appropriate length for Telegram (not too long)\n- Be helpful and informative\n- Match the user's tone and communication style\n\n### Voice Message Handling\n- When users send voice messages, respond naturally as if they spoke to you directly\n- Don't mention that their message was transcribed\n- Respond in a conversational way that flows naturally\n\n\n## Context Awareness\n\n- Remember the current date and time: **Today is {{ $now.format('cccc') }} the {{ $now.format('yyyy-MM-dd HH:mm') }}**\n- Use this information to provide relevant, timely responses\n- Reference \"today\", \"tomorrow\", \"this week\" appropriately\n\n## Important Guidelines\n\n- Maintain conversation context using the memory system\n- Ask clarifying questions when requests are ambiguous\n- Respect privacy - only access information when relevant to the request\n- If you can't help with something, suggest alternatives or redirect to your available capabilities\n\nRemember: You're a personal assistant, so be personable, helpful, and focused on making the user's day easier and more organized.\n\nToday is {{ $now.format('cccc') }} the {{ $now.format('yyyy-MM-dd HH:mm') }}." }
},
"id": "f958d2f6-6602-4443-bfae-ef628af09b84",
"name": "Assistant Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 2.2,
"position": [704, 0]
},
{
"parameters": {
"content": "## Start here!\n@[youtube](aaOLJ2mUoVk)",
"height": 320,
"width": 464,
"color": 5
},
"type": "n8n-nodes-base.stickyNote",
"position": [-736, -80],
"typeVersion": 1,
"id": "cd6f0a09-148c-46e3-9858-11645de3a802",
"name": "Sticky Note1"
}
],
connections: {
"Telegram Message Trigger": { "main": [[{
"node": "Check if Audio file",
"type": "main",
"index": 0
}]] },
"Memory": { "ai_memory": [[{
"node": "Assistant Agent",
"type": "ai_memory",
"index": 0
}]] },
"Get a file": { "main": [[{
"node": "Transcribe audio",
"type": "main",
"index": 0
}]] },
"Check if Audio file": { "main": [[{
"node": "Get a file",
"type": "main",
"index": 0
}], [{
"node": "Set field",
"type": "main",
"index": 0
}]] },
"Transcribe audio": { "main": [[{
"node": "Assistant Agent",
"type": "main",
"index": 0
}]] },
"Set field": { "main": [[{
"node": "Assistant Agent",
"type": "main",
"index": 0
}]] },
"Model": { "ai_languageModel": [[{
"node": "Assistant Agent",
"type": "ai_languageModel",
"index": 0
}]] },
"Assistant Agent": { "main": [[{
"node": "Reply in Telegram",
"type": "main",
"index": 0
}]] }
},
pinData: {},
active: false,
settings: { "executionOrder": "v1" },
tags: []
};
var calendar_agent_default = {
name: "Calendar agent",
meta: {
"templateCredsSetupCompleted": false,
"templateId": "calendar_agent_with_gcal"
},
nodes: [
{
"parameters": {
"resource": "calendar",
"calendar": {},
"timeMin": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Start_Time', `Start date for availability check`, 'string') }}",
"timeMax": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('End_Time', `End date for availability check`, 'string') }}",
"options": {}
},
"id": "e3eb2815-735f-4efd-a844-da9d53e45aae",
"name": "Get availability",
"type": "n8n-nodes-base.googleCalendarTool",
"typeVersion": 1.3,
"position": [-32, 224],
"credentials": {}
},
{
"parameters": {
"operation": "getAll",
"calendar": {},
"returnAll": true,
"timeMin": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('After', `Start date for events search`, 'string') }}",
"timeMax": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Before', `End date for events search`, 'string') }}",
"options": {}
},
"id": "d6c00690-8d0c-4942-aa17-077e61c909d1",
"name": "Get events",
"type": "n8n-nodes-base.googleCalendarTool",
"typeVersion": 1.3,
"position": [112, 224],
"credentials": {}
},
{
"parameters": { "options": {} },
"id": "719aaeb8-e985-4235-bcff-c2bfbc6a8e5c",
"name": "Chat message",
"type": "@n8n/n8n-nodes-langchain.chatTrigger",
"typeVersion": 1.3,
"position": [-400, -32],
"webhookId": "d0b3a15a-8d1c-437c-8523-00e108af14a4"
},
{
"parameters": {
"model": {
"__rl": true,
"mode": "id",
"value": "gpt-4o-mini"
},
"options": { "temperature": 0 }
},
"id": "d23e09d0-ff7c-4f1b-8e67-6864801dbc44",
"name": "Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.2,
"position": [-304, 224],
"credentials": {}
},
{
"parameters": {},
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
"typeVersion": 1.3,
"position": [-176, 224],
"id": "56bd33c7-bb31-4957-9f12-fdcc2351880d",
"name": "Simple Memory"
},
{
"parameters": { "options": {
"systemMessage": "=You are a helpful calendar assistant. You can check availability and retrieve events from Google Calendar. When users ask about dates, use the calendar tools to get the information. Always specify date ranges clearly when using the tools.\n\nTodays date is: {{ $now }}",
"maxIterations": 10,
"returnIntermediateSteps": false,
"passthroughBinaryImages": true,
"batching": {},
"enableStreaming": true
} },
"id": "1e8be9e3-24a4-415b-9424-6f9010c5f333",
"name": "Calendar agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 2.2,
"position": [-160, -32]
},
{
"parameters": {
"content": "## Start here!\n@[youtube](-S5k4sXrPws)",
"height": 320,
"width": 464,
"color": 5
},
"type": "n8n-nodes-base.stickyNote",
"position": [-928, -32],
"typeVersion": 1,
"id": "d3552acd-20f1-40e0-af3c-e2aabdbf3f27",
"name": "Sticky Note"
}
],
connections: {
"Get availability": { "ai_tool": [[{
"node": "Calendar agent",
"type": "ai_tool",
"index": 0
}]] },
"Get events": { "ai_tool": [[{
"node": "Calendar agent",
"type": "ai_tool",
"index": 0
}]] },
"Chat message": { "main": [[{
"node": "Calendar agent",
"type": "main",
"index": 0
}]] },
"Model": { "ai_languageModel": [[{
"node": "Calendar agent",
"type": "ai_languageModel",
"index": 0
}]] },
"Simple Memory": { "ai_memory": [[{
"node": "Calendar agent",
"type": "ai_memory",
"index": 0
}]] }
},
pinData: {},
active: false,
settings: { "executionOrder": "v1" },
tags: []
};
var build_your_first_ai_agent_default = {
name: "Build your first AI agent",
meta: {
"templateCredsSetupCompleted": false,
"templateId": "build_your_first_ai_agent"
},
nodes: [
{
"id": "2c408b32-7862-4411-9ad1-b6e9ff4e41f7",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [592, -256],
"parameters": {
"color": 7,
"width": 512,
"height": 352,
"content": "## [Video Tutorial](https://youtu.be/laHIzhsz12E)\n@[youtube](laHIzhsz12E)"
},
"typeVersion": 1
},
{
"id": "3808de8d-ef18-47f5-9621-b08ba961ae01",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Introduction Note",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-752, -256],
"parameters": {
"width": 392,
"height": 460,
"content": "## Try It Out!\n\n**Launch your first AI agent—a chatbot that uses tools to fetch live info, send emails, and automate tasks.**\n\n### To get started:\n1. **Connect Gemini** (see red sticky note below)\n2. Click the **🗨 Open chat** button and try asking:\n * “What’s the weather in Paris?”\n * “Get me the latest tech news.”\n * “Give me ideas for n8n AI agents.”\n\n### Questions or Feedback?\nFor feedback, coaching, buit-for-you workflows or any questions, use my unified AI-powered contact form.\n\n➡️ **[Get in Touch Here](https://api.ia2s.app/form/templates/academy)**\n\n*Happy Automating! —Lucas Peyrin*"
},
"typeVersion": 1
},
{
"id": "2b55763c-0541-4133-aa79-87c3ce9f0564",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note12",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-272, -160],
"parameters": {
"color": 7,
"width": 300,
"height": 252,
"content": "💡 Later, activate this workflow and share the public chat URL to let others talk to your AI Agent!"
},
"typeVersion": 1
},
{
"id": "f09c396a-dab8-41f8-a6e5-3dbd1dd70323",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note13",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [112, -256],
"parameters": {
"color": 7,
"width": 396,
"height": 348,
"content": "Your AI agent can:\n1. **Receive** messages from the chat\n2. **Select** the right tools (e.g., weather, news, email)\n3. **Respond** with live, helpful answers\n\n\n**Open the AI agent node** and edit the **System Message** to adjust your agent’s thinking, behavior, and replies.\n\n\n\n\n\n\n\n\n\n\n"
},
"typeVersion": 1
},
{
"id": "f7c57d33-2318-409d-9084-13990299db3d",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note15",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [112, 176],
"parameters": {
"color": 7,
"width": 308,
"height": 260,
"content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\nThis node helps your agent remember the last few messages to stay on topic."
},
"typeVersion": 1
},
{
"id": "07ed7ed3-91d0-432f-9327-d2a30601082c",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note16",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [512, 176],
"parameters": {
"color": 7,
"width": 372,
"height": 324,
"content": "\n\n\n\n\n\n\n\n\n\n\n\n\nThese tools let your agent access real-world data or take actions. Add more to expand its abilities!\n\nClick the ➕ under the agent’s Tool input to add:\n- Google Calendar (Get Upcoming Events)\n- Gmail (Send an Email) (Gmail)"
},
"typeVersion": 1
},
{
"id": "24fc1fd5-ed10-43d9-9b35-8facb7f357d5",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Get News",
"type": "n8n-nodes-base.rssFeedReadTool",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [656, 224],
"parameters": {
"url": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('URL', `Use one of:\n- https://feeds.bbci.co.uk/news/world/rss.xml (BBC World – global headlines)\n- https://www.aljazeera.com/xml/rss/all.xml (Al Jazeera English – in‑depth global coverage)\n- http://rss.cnn.com/rss/edition_world.rss (CNN World – breaking news worldwide)\n- https://techcrunch.com/feed/ (TechCrunch – global tech & startup news)\n- http://news.ycombinator.com/rss (Hacker News – tech community headlines)\n- https://n8n.io/blog/rss (n8n Blog – updates & tutorials)\n- https://www.bonappetit.com/feed/recipes-rss-feed/rss (Bon Appétit – recent recipes list)\n- https://www.endsreport.com/rss/news-and-analysis (ENDS Report – environmental law & policy news)\n- https://medlineplus.gov/groupfeeds/new.xml (MedlinePlus – health topics & wellness updates)`, 'string') }}",
"options": {},
"toolDescription": "Gets the latest blog posts about any rss feed."
},
"typeVersion": 1.2
},
{
"id": "ac7d0dd1-8885-41c1-abda-f3742c838992",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Get Weather",
"type": "n8n-nodes-base.httpRequestTool",
"creator": "Lucas Peyrin",
"position": [544, 224],
"parameters": {
"url": "https://api.open-meteo.com/v1/forecast",
"options": {},
"sendQuery": true,
"queryParameters": { "parameters": [
{
"name": "latitude",
"value": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('parameters0_Value', `Latitude of the location, e.g. 45.75 for Lyon. Do not ask the user just infer it automatically.`, 'string') }}"
},
{
"name": "longitude",
"value": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('parameters1_Value', `Longitude of the location, e.g. 4.85 for Lyon. Do not ask the user just infer it automatically.`, 'string') }}"
},
{
"name": "current",
"value": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('parameters2_Value', `Comma-separated list of current weather variables (no whitespace).\n\nExample: temperature_2m,windspeed_10m,rain.\n\nOptions: temperature_2m,relative_humidity_2m,apparent_temperature,is_day,precipitation,rain,showers,snowfall,weathercode,cloudcover_total,pressure_msl,surface_pressure,windspeed_10m,winddirection_10m,windgusts_10m.`, 'string') }}"
},
{
"name": "hourly",
"value": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('parameters3_Value', `Comma-separated list of hourly weather variables (no whitespace). Hourly is only useful to get one day's information. For weakly overview please use daily.\n\nExample: temperature_2m,precipitation.\n\nOptions: temperature_2m,relative_humidity_2m,dewpoint_2m,apparent_temperature,precipitation,rain,showers,snowfall,snow_depth,pressure_msl,surface_pressure,cloudcover_total,cloudcover_low,cloudcover_mid,cloudcover_high,windspeed_10m,winddirection_10m,windgusts_10m,visibility,is_day,sunshine_duration,soil_temperature,soil_moisture,PM10,PM2_5,carbon_monoxide,ozone,us_aqi,UV_index.`, 'string') }}"
},
{
"name": "daily",
"value": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('parameters4_Value', `Comma-separated list of daily weather variables (no whitespace).\n\nExample: temperature_2m_max,precipitation_sum.\n\nOptions: weathercode,temperature_2m_max,temperature_2m_min,apparent_temperature_max,apparent_temperature_min,precipitation_sum,rain_sum,showers_sum,snowfall_sum,precipitation_hours,sunrise,sunset,daylight_duration,sunshine_duration,pressure_msl_max,pressure_msl_min,surface_pressure_max,surface_pressure_min,windgusts_10m_max,windspeed_10m_max,winddirection_10m_dominant,shortwave_radiation_sum.`, 'string') }}"
},
{
"name": "start_date",
"value": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('parameters5_Value', `Start date in YYYY-MM-DD format. Example: 2025-07-15`, 'string') }}"
},
{
"name": "end_date",
"value": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('parameters6_Value', `End date in YYYY-MM-DD format. Must be after start_date. Example: 2025-07-18`, 'string') }}"
},
{
"name": "temperature_unit",
"value": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('parameters7_Value', `Unit for temperature. Options: celsius (default), fahrenheit.`, 'string') }}"
}
] },
"toolDescription": "Get weather forecast anywhere, anytime. You can make requests by assuming most information, the only thing you need is the location (use the city name to infer lat and long automatically) and time period (assume today if not specified)"
},
"notesInFlow": true,
"typeVersion": 4.2
},
{
"id": "332af12a-45ab-4e5d-8dab-da21ba2111f9",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Your First AI Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [192, -64],
"parameters": { "options": { "systemMessage": "=<role>\nYou are the n8n Demo AI Agent, a friendly and helpful assistant designed to showcase the power of AI agents within the n8n automation platform. Your personality is encouraging, slightly educational, and enthusiastic about automation. Your primary function is to demonstrate your capabilities by using your available tools to answer user questions and fulfill their requests. You are conversational.\n</role>\n\n<instructions>\n<goal>\nYour primary goal is to act as a live demonstration of an AI Agent built with n8n. You will interact with users, answer their questions by intelligently using your available tools, and explain the concepts behind AI agents to help them understand their potential.\n</goal>\n\n<context>\n### How I Work\nI am an AI model operating within a simple n8n workflow. This workflow gives me two key things:\n1. **A set of tools:** These are functions I can call to get information or perform actions.\n2. **Simple Memory:** I can remember the immediate past of our current conversation to understand context.\n\n### My Purpose\nMy main purpose is to be a showcase. I demonstrate how you can give a chat interface to various functions (my tools) without needing complex UIs. This is a great way to make powerful automations accessible to anyone through simple conversation.\n\n### My Tools Instructions\nYou must choose one of your available tools if the user's request matches its capability. You cannot perform these actions yourself; you must call the tool.\n\n### About AI Agents in n8n\n- **Reliability:** While I can use one tool at a time effectively, more advanced agents can perform multi-step tasks. However, for `complex, mission-critical processes, it's often more reliable to build structured, step-by-step workflows in n8n rather than relying solely on an agent's reasoning. Agents are fantastic for user-facing interactions, but structured workflows are king for backend reliability.\n- **Best Practices:** A good practice is to keep an agent's toolset focused, typically under 10-15 tools, to ensure reliability and prevent confusion.\n\n### Current Date & Time\n{{ $now }}\n</context>\n\n<output_format>\n- Respond in a friendly, conversational, and helpful tone.\n- When a user's request requires a tool, first select the appropriate tool. Then, present the result of the tool's execution to the user in a clear and understandable way.\n- Be proactive. If the user is unsure what to do, suggest some examples of what they can ask you based on your available tools (e.g., Talk about your tools and what you know about yourself).\n</output_format>\n</instructions>" } },
"typeVersion": 2.2
},
{
"id": "95421925-c5ad-48bd-9638-c84ff5b5e3c6",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Example Chat",
"type": "@n8n/n8n-nodes-langchain.chatTrigger",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-176, -64],
"webhookId": "e5616171-e3b5-4c39-81d4-67409f9fa60a",
"parameters": {
"public": true,
"options": {
"title": "Your first AI Agent 🚀",
"subtitle": "This is for demo purposes. Try me out !",
"responseMode": "lastNode",
"inputPlaceholder": "Type your message here...",
"showWelcomeScreen": false
},
"initialMessages": "Hi there! 👋"
},
"typeVersion": 1.1
},
{
"id": "56d2684a-7f83-4ffd-8501-3253d999b4c6",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Conversation Memory",
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [224, 224],
"parameters": { "contextWindowLength": 30 },
"typeVersion": 1.3
},
{
"id": "c218a5da-bec7-4034-8d2b-f4bca34e551e",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Connect Gemini",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-176, 224],
"parameters": { "options": { "temperature": 0 } },
"typeVersion": 1
},
{
"id": "199eb2cd-bc6b-4f61-bbf1-f196c7869b43",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-272, 176],
"parameters": {
"color": 3,
"width": 294,
"height": 316,
"content": "\n\n\n\n\n\n\n\n\n\n\n\n1. [In Google AI Studio](https://aistudio.google.com/app/apikey) click **“Create API key in new project”** and copy it.\n\n2. Open the ```Connect Gemini``` node:\n * **Select Credential → Create New**\n * Paste into **API Key** and **Save**\n"
},
"typeVersion": 1
}
],
pinData: {},
connections: {
"Get News": { "ai_tool": [[{
"node": "Your First AI Agent",
"type": "ai_tool",
"index": 0
}]] },
"Get Weather": { "ai_tool": [[{
"node": "Your First AI Agent",
"type": "ai_tool",
"index": 0
}]] },
"Example Chat": { "main": [[{
"node": "Your First AI Agent",
"type": "main",
"index": 0
}]] },
"Connect Gemini": { "ai_languageModel": [[{
"node": "Your First AI Agent",
"type": "ai_languageModel",
"index": 0
}]] },
"Conversation Memory": { "ai_memory": [[{
"node": "Your First AI Agent",
"type": "ai_memory",
"index": 0
}]] }
}
};
var json_basics_default = {
name: "JSON basics",
meta: {
"templateCredsSetupCompleted": false,
"templateId": "json_basics"
},
nodes: [
{
"id": "365fdd40-4e46-497b-8fef-9c356b2234cd",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Execute to Start",
"type": "n8n-nodes-base.manualTrigger",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-3456, 1056],
"parameters": {},
"typeVersion": 1
},
{
"id": "6c2fe8ca-9aa9-402a-949d-cc58177eb7e5",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "String",
"type": "n8n-nodes-base.set",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-2816, 1056],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "e87952cb-878e-4feb-8261-342eaf887838",
"name": "json_example_string",
"type": "string",
"value": "This is a simple string. In JSON, it's always enclosed in double quotes."
}] }
},
"typeVersion": 3.4
},
{
"id": "3cffa562-bedc-42f9-ab4f-8b55cd3b5711",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Key & Value",
"type": "n8n-nodes-base.set",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-3104, 1056],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "e87952cb-878e-4feb-8261-342eaf887838",
"name": "key",
"type": "string",
"value": "value"
}, {
"id": "b5f030f4-6650-4181-881f-de44790bb24b",
"name": "another_key",
"type": "string",
"value": "another_value"
}] }
},
"typeVersion": 3.4
},
{
"id": "c4742e5d-8017-45e9-ada5-a2897c87b4cc",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Number",
"type": "n8n-nodes-base.set",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-2528, 1056],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "e87952cb-878e-4feb-8261-342eaf887838",
"name": "json_example_integer",
"type": "number",
"value": 10
}, {
"id": "12345",
"name": "json_example_float",
"type": "number",
"value": 12.5
}] }
},
"typeVersion": 3.4
},
{
"id": "60ee473c-635c-41d7-acd2-4fa6c3acb665",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Boolean",
"type": "n8n-nodes-base.set",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-2240, 1056],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "e87952cb-878e-4feb-8261-342eaf887838",
"name": "json_example_boolean",
"type": "boolean",
"value": false
}] }
},
"typeVersion": 3.4
},
{
"id": "56683e92-19a0-4a17-99a9-b92120739c74",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Array",
"type": "n8n-nodes-base.set",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-1664, 1056],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "e87952cb-878e-4feb-8261-342eaf887838",
"name": "json_example_array",
"type": "array",
"value": "[\"first element\", 2, false, null]"
}] }
},
"typeVersion": 3.4
},
{
"id": "190c86c7-2d0b-47e1-a729-e22e9610dc8f",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Object",
"type": "n8n-nodes-base.set",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-1360, 1056],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "e87952cb-878e-4feb-8261-342eaf887838",
"name": "json_example_object",
"type": "object",
"value": "{\"key\":\"value\",\"array\":[1,2,3],\"boolean\":false,\"integer\":123,\"sub_object\":{\"sub_key\":\"Find me!\"}}"
}] }
},
"typeVersion": 3.4
},
{
"id": "24b198bc-9a82-477f-921e-c7e5055d17cc",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-3680, 560],
"parameters": {
"width": 460,
"height": 656,
"content": "## Tutorial - What is JSON?\n\nWelcome! This workflow will teach you the basics of JSON, the language that apps and n8n nodes use to exchange information.\n\n**What is JSON?**\nImagine a contact card:\n- **Name:** John Doe\n- **Age:** 30\n- **Has Children:** Yes\n- **Phone Numbers:** [\"555-1234\", \"555-5678\"]\n\n\nJSON is just a way of writing this down so a computer can understand it perfectly.\n\n**How to use this tutorial:**\n1. Click **\"Execute Workflow\"** button.\n2. Click on each node, one by one, in order.\n3. Look at the node's output in the panel on the right and read the associated sticky note to understand what's happening."
},
"typeVersion": 1
},
{
"id": "f9dfa173-b51f-41fb-8587-9c4ee2855265",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-3184, 704],
"parameters": {
"color": 7,
"width": 260,
"height": 516,
"content": "#### The Heart of JSON: Key & Value\n\nEverything in JSON is built on this pair:\n- A **Key** (the name of the data, always in double quotes `\"`).\n- A **Value** (the data itself).\n\n\n`\"key\": \"value\"`\n\nIn this node's output, you see two key/value pairs. This is the basic building block for everything that follows."
},
"typeVersion": 1
},
{
"id": "fc2ad88e-b5cc-4dc4-91e6-f246d1654e26",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-2896, 704],
"parameters": {
"color": 7,
"width": 260,
"height": 516,
"content": "#### Data Type: String\n\nA string is simply **text**.\n- **Syntax:** The text is always enclosed in double quotes `\" \"`.\n\n\nLook at the output: the value of `json_example_string` is the text we defined."
},
"typeVersion": 1
},
{
"id": "4973dad8-cce4-490c-8ad1-01410ffb7740",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-2608, 704],
"parameters": {
"color": 7,
"width": 260,
"height": 516,
"content": "#### Data Type: Number\n\nThis is simply a number. It can be a whole number (integer) like 10, or a decimal (float) like 12.5.\n- **Syntax:** Just write the number directly, **WITHOUT quotes**.\n\n\n`\"age\": 30` (Correct)\n`\"age\": \"30\"` (Incorrect, this is a String!)\n\nThis distinction is crucial for doing math!"
},
"typeVersion": 1
},
{
"id": "4240a747-d2de-42dc-882a-55aee236e76a",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-2320, 704],
"parameters": {
"color": 7,
"width": 260,
"height": 516,
"content": "#### Data Type: Boolean\n\nThis is a value that can only be **TRUE** or **FALSE**.\n- **Syntax:** `true` or `false` (always lowercase and **WITHOUT quotes**).\n\n\nThink of it like a light switch: on (`true`) or off (`false`). It's very useful for conditions (If/Then logic)."
},
"typeVersion": 1
},
{
"id": "6eb904bc-a082-43fd-85ea-e672830cdcd2",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-1744, 704],
"parameters": {
"color": 7,
"width": 260,
"height": 516,
"content": "#### Data Type: Array\n\nAn array is an **ordered list** of items.\n- **Syntax:** Starts with `[` and ends with `]`. Items are separated by commas.\n\n\nAn array can hold anything: strings, numbers, booleans, and even other arrays or objects!"
},
"typeVersion": 1
},
{
"id": "76fa5320-894b-451f-b372-59144fc0ade3",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-1456, 704],
"parameters": {
"color": 7,
"width": 280,
"height": 516,
"content": "#### Data Type: Object (JSON Object)\n\nThis is the main concept! An object is a **collection of key/value pairs**.\n- **Syntax:** Starts with `{` and ends with `}`.\n\n\nThis is what allows us to structure complex data, like our contact card from the beginning. Notice how this object contains all the other data types we've seen!"
},
"typeVersion": 1
},
{
"id": "5976d5fa-6788-46b8-b5e5-1cf6d09f5954",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Null",
"type": "n8n-nodes-base.set",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-1952, 1056],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "e87952cb-878e-4feb-8261-342eaf887838",
"name": "json_example_null",
"type": "null",
"value": {}
}] }
},
"typeVersion": 3.4
},
{
"id": "7786c224-1cd6-4b05-a41b-d47cde98d2a0",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-2032, 704],
"parameters": {
"color": 7,
"width": 260,
"height": 516,
"content": "#### Data Type: Null\n\nThis special type means \"nothing,\" \"no value,\" or \"empty.\"\n- **Syntax:** `null` (lowercase and **WITHOUT quotes**).\n\n\nIt's different from `0` (which is a number) or `\"\"` (which is an empty string). `null` is the intentional absence of a value."
},
"typeVersion": 1
},
{
"id": "f8f6e7b6-3f48-4e5c-86d6-001ec61d1f81",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Using JSON (Expressions)",
"type": "n8n-nodes-base.set",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-1024, 1056],
"parameters": {
"options": {},
"assignments": { "assignments": [
{
"id": "e87952cb-878e-4feb-8261-342eaf887838",
"name": "message",
"type": "string",
"value": "=Hello, the number from the tutorial is: {{ $('Number').item.json.json_example_integer }}"
},
{
"id": "61f385f4-b8e2-4c69-b873-9ffc3ab3fe94",
"name": "sub_key",
"type": "string",
"value": "={{ $json.json_example_object.sub_object.sub_key }}"
},
{
"id": "bd752a0f-64bf-44b1-b39b-fca28e86aa5b",
"name": "array_second_item",
"type": "string",
"value": "={{ $json.json_example_object.array[1] }}"
}
] }
},
"typeVersion": 3.4
},
{
"id": "0b3ddc47-b1ff-4016-957b-cb6f584a996f",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note8",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-1152, 704],
"parameters": {
"color": 5,
"width": 340,
"height": 516,
"content": "#### ⭐ THE KEY STEP: Using JSON in n8n!\n\nNow for the magic. How do you use data from a previous node? With **expressions** `{{ }}`.\n\nThis node creates a custom message. Look at the value of the `message` field:\n`Hello, the number from the tutorial is: {{ $('Number').item.json.json_example_integer }}`\n\nIt dynamically pulled the number `10` from the \"Number\" node! This is how you make your nodes talk to each other."
},
"typeVersion": 1
},
{
"id": "d1004e2e-15b6-4108-9811-6e7d980822d3",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Final Exam",
"type": "n8n-nodes-base.set",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-672, 1056],
"parameters": {
"options": {},
"assignments": { "assignments": [
{
"id": "e87952cb-878e-4feb-8261-342eaf887838",
"name": "summary_string",
"type": "string",
"value": "={{ $('String').item.json.json_example_string }}"
},
{
"id": "12345",
"name": "summary_number",
"type": "number",
"value": "={{ $('Number').item.json.json_example_integer }}"
},
{
"id": "67890",
"name": "summary_boolean",
"type": "boolean",
"value": "={{ $('Boolean').item.json.json_example_boolean }}"
},
{
"id": "abcde",
"name": "summary_null",
"type": "null",
"value": "={{ $('Null').item.json.json_example_null }}"
},
{
"id": "fghij",
"name": "summary_array",
"type": "array",
"value": "={{ $('Array').item.json.json_example_array }}"
},
{
"id": "klmno",
"name": "summary_object",
"type": "object",
"value": "={{ $('Object').item.json.json_example_object }}"
}
] }
},
"typeVersion": 3.4
},
{
"id": "43eb149b-ccd3-4557-b744-ef5d9dcf82d9",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note9",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-784, 704],
"parameters": {
"color": 6,
"width": 340,
"height": 516,
"content": "#### 🎓 FINAL EXAM: Putting It All Together\n\nThis last node creates a final object by using expressions to pull data from **all the previous nodes**.\n\nClick on this node and look at the expressions in each field. It's a perfect summary of everything you've learned.\n\n**Congratulations! You now understand the basics of JSON and how to use it in n8n.**"
},
"typeVersion": 1
},
{
"id": "b8fce06f-abe9-45cd-b365-97743a0d8dca",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note10",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-416, 16],
"parameters": {
"color": 3,
"width": 540,
"height": 1200,
"content": "## Was this helpful? Let me know!\n[](https://api.ia2s.app/form/templates/academy)\n\nI really hope this tutorial helped you understand JSON better. Your feedback is incredibly valuable and helps me create better resources for the n8n community.\n\n### **Have Feedback, a Question, or a Project Idea?**\n\nI've streamlined the way we connect. It all starts with one simple form that takes less than 10 seconds. After that, you'll chat with my AI assistant who will gather the key details and pass them directly on to me.\n\n#### ➡️ **[Click here to start the conversation](https://api.ia2s.app/form/templates/academy)**\n\nUse this single link for anything you need:\n\n* **Give Feedback:** Share your thoughts on this template—whether you found a typo, encountered an unexpected error, have a suggestion, or just want to say thanks!\n\n* **n8n Coaching:** Get personalized, one-on-one guidance to master n8n. We can work together to get you launched with confidence or help you reach an expert level.\n\n* **n8n Consulting:** Have a complex business challenge or need a custom workflow built from scratch? Let's partner on a powerful automation solution tailored to your specific needs.\n\n---\n\nHappy Automating!\nLucas Peyrin | [n8n Academy](https://n8n.ac)"
},
"typeVersion": 1
},
{
"id": "da4af3ac-a717-489d-bb64-3ef34167a0fc",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note11",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-3680, 240],
"parameters": {
"color": 2,
"width": 460,
"height": 300,
"content": "## [Video Tutorial](https://youtu.be/PAmgrwYnzWs?si=yXG1oHIL3UiBcAPa)\n@[youtube](PAmgrwYnzWs)"
},
"typeVersion": 1
},
{
"id": "b29ca9fc-7fc2-4dd7-9fca-d2d2a9bae237",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note14",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-3184, 288],
"parameters": {
"color": 7,
"width": 576,
"height": 392,
"content": "[](https://www.youtube.com/watch?v=PAmgrwYnzWs)"
},
"typeVersion": 1
},
{
"id": "0d2835ec-33c8-426b-87cd-74af33011bd5",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note15",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-784, 368],
"parameters": {
"color": 6,
"width": 336,
"height": 312,
"content": "## [>> Go to Eval Workflow <<](https://n8n.io/workflows/6232)\n\nVerify your skills with a complete eval workflow to put your JSON Skills to the test.\n[](https://n8n.io/workflows/6232)"
},
"typeVersion": 1
}
],
pinData: {},
connections: {
"Null": { "main": [[{
"node": "Array",
"type": "main",
"index": 0
}]] },
"Array": { "main": [[{
"node": "Object",
"type": "main",
"index": 0
}]] },
"Number": { "main": [[{
"node": "Boolean",
"type": "main",
"index": 0
}]] },
"Object": { "main": [[{
"node": "Using JSON (Expressions)",
"type": "main",
"index": 0
}]] },
"String": { "main": [[{
"node": "Number",
"type": "main",
"index": 0
}]] },
"Boolean": { "main": [[{
"node": "Null",
"type": "main",
"index": 0
}]] },
"Key & Value": { "main": [[{
"node": "String",
"type": "main",
"index": 0
}]] },
"Execute to Start": { "main": [[{
"node": "Key & Value",
"type": "main",
"index": 0
}]] },
"Using JSON (Expressions)": { "main": [[{
"node": "Final Exam",
"type": "main",
"index": 0
}]] }
}
};
var expressions_tutorial_default = {
name: "Expressions",
meta: {
"templateCredsSetupCompleted": false,
"templateId": "expressions_tutorial"
},
nodes: [
{
"id": "2c20bdef-e54e-46ab-9c63-5cf521ec749e",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Start Tutorial",
"type": "n8n-nodes-base.manualTrigger",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-7360, 992],
"parameters": {},
"typeVersion": 1
},
{
"id": "25703548-3e48-4951-9e3b-1d7815e15af5",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-7616, 592],
"parameters": {
"color": 5,
"width": 640,
"height": 560,
"content": "# Tutorial - Mastering n8n Expressions\n\nWelcome! You know what JSON is. Now, let's learn how to **use it**. This workflow teaches you how to pull data from one node and use it in another using n8n's powerful expressions.\n\n**What is an Expression?**\nAn expression is a small piece of code inside double curly braces `{{ }}` that gets replaced with a dynamic value when the workflow runs. It's the \"glue\" that connects your nodes.\n\n**How to use this tutorial:**\n1. The first node, **\"Source Data\"**, contains all the data we will use. Execute it once to see what's inside.\n2. Follow the path from top to bottom. Each node is a new lesson.\n3. Read the sticky note for each lesson, then look at the node's configuration and its output to understand the concept."
},
"typeVersion": 1
},
{
"id": "b875f86c-a790-49bc-96a0-e1ccc72a5e80",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Source Data",
"type": "n8n-nodes-base.set",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-6720, 992],
"parameters": {
"options": {},
"assignments": { "assignments": [
{
"id": "12345",
"name": "name",
"type": "string",
"value": "Alice"
},
{
"id": "67890",
"name": "age",
"type": "number",
"value": 30
},
{
"id": "abcde",
"name": "is_active",
"type": "boolean",
"value": true
},
{
"id": "fghij",
"name": "skills",
"type": "array",
"value": "[\"JavaScript\",\"Python\",\"n8n\"]"
},
{
"id": "klmno",
"name": "projects",
"type": "array",
"value": "[{\"name\":\"Project A\",\"status\":\"Done\"},{\"name\":\"Project B\",\"status\":\"In Progress\"}]"
},
{
"id": "pqrst",
"name": "contact",
"type": "object",
"value": "{\"email\":\"alice@example.com\",\"phone\":null}"
}
] }
},
"typeVersion": 3.4
},
{
"id": "108d6d9a-6e98-491d-87c1-78d3680f0c40",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-6944, 640],
"parameters": {
"color": 7,
"width": 520,
"height": 520,
"content": "## Our Data Source\n\nThis node holds all the sample data for our tutorial. Think of it as a filing cabinet. All the other nodes will be reaching into this cabinet to pull out specific pieces of information.\n\nTake a look at its output to familiarize yourself with the structure.\nWe have:\n- Simple text (`name`)\n- A number (`age`)\n- A list of skills (`skills`)\n- A list of complex projects (`projects`)\n- A nested contact object (`contact`)"
},
"typeVersion": 1
},
{
"id": "54723ed3-94c5-4104-b1c5-3ac70c262b87",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "1. The Basics",
"type": "n8n-nodes-base.set",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-6192, 992],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "12345",
"name": "user_name",
"type": "string",
"value": "={{ $('Source Data').item.json.name }}"
}] }
},
"typeVersion": 3.4
},
{
"id": "81dc9611-b9b5-41c3-a5ab-460cb0dc1ca6",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-6400, 576],
"parameters": {
"color": 2,
"width": 500,
"height": 580,
"content": "## Lesson 1: Accessing a Simple Value\n\nThis is the most common thing you'll do in n8n.\n\n**The Goal:** Get the user's name from the \"Source Data\" node.\n\n**The Expression:** `{{ $('Source Data').item.json.name }}`\n\n**Breakdown:**\n- `{{ ... }}`: Tells n8n \"this is a dynamic expression\".\n- `$('Source Data')`: Selects the node we want data from.\n- `.item.json`: Narrows it down to the JSON data of the current item.\n- `.name`: Selects the specific **key** we want the value of.\n\n**Other Possibility:**\n`{{ $json.name }}` would also work in this case, as `$json` accesses the data from the previous node."
},
"typeVersion": 1
},
{
"id": "b91f6099-5207-4000-9a0e-e6374718c123",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "3. Working with Arrays",
"type": "n8n-nodes-base.set",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-4960, 992],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "12345",
"name": "second_skill",
"type": "string",
"value": "={{ $('Source Data').last().json.skills[1] }}"
}] }
},
"typeVersion": 3.4
},
{
"id": "2074d65b-2d6b-41af-a4f9-fa9a195a1bf8",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-5168, 624],
"parameters": {
"color": 4,
"width": 540,
"height": 520,
"content": "## Lesson 3: Accessing an Array Element\n\nWhat if the data is in a list (an array)? You need to specify *which* item you want.\n\n**The Goal:** Get the user's *second* skill.\n\n**The Expression:** `{{ $('Source Data').last().json.skills[1] }}`\n\n**Breakdown:**\n- `...skills`: Selects the array of skills.\n- `[1]`: Selects the item at a specific position.\n- **IMPORTANT:** Arrays are \"zero-indexed\", which means the first item is `[0]`, the second is `[1]`, the third is `[2]`, and so on."
},
"typeVersion": 1
},
{
"id": "8ccc139f-0e8c-4c25-9d04-cecf2b335934",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "4. Going Deeper",
"type": "n8n-nodes-base.set",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-4400, 992],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "12345",
"name": "user_email",
"type": "string",
"value": "={{ $('Source Data').last().json.contact.email }}"
}] }
},
"typeVersion": 3.4
},
{
"id": "34264c0c-8282-4b57-a066-0548a31cbf1a",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-4608, 672],
"parameters": {
"color": 5,
"width": 540,
"height": 480,
"content": "## Lesson 4: Accessing Nested Data\n\nSometimes, data is organized into objects within other objects.\n\n**The Goal:** Get the user's email address.\n\n**The Expression:** `{{ $('Source Data').last().json.contact.email }}`\n\n**Breakdown:**\n- `...contact`: First, we access the `contact` object.\n- `.email`: Then, we use another dot `.` to go one level deeper and get the value of the `email` key inside it."
},
"typeVersion": 1
},
{
"id": "4c41fb6a-5f93-408f-9583-1e118d139dcf",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "5. The Combo Move",
"type": "n8n-nodes-base.set",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-3808, 992],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "12345",
"name": "first_project_status",
"type": "string",
"value": "={{ $('Source Data').last().json.projects[0].status }}"
}] }
},
"typeVersion": 3.4
},
{
"id": "2123cd02-5cb6-4dd7-89e5-7af1c6edf234",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-4048, 672],
"parameters": {
"color": 6,
"width": 580,
"height": 480,
"content": "## Lesson 5: Accessing Data in an Array of Objects\n\nThis is the ultimate test of the previous lessons!\n\n**The Goal:** Get the *status* of the *first* project in the list.\n\n**The Expression:** `{{ $('Source Data').last().json.projects[0].status }}`\n\n**Breakdown:**\n1. `...projects`: We select the array of projects.\n2. `[0]`: We pick the first object in that array.\n3. `.status`: From that chosen object, we grab the value of the `status` key."
},
"typeVersion": 1
},
{
"id": "f25a5a9d-518d-4a4a-b2ad-36fd0e1f5bd2",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "6. A Touch of Magic",
"type": "n8n-nodes-base.set",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-3200, 992],
"parameters": {
"options": {},
"assignments": { "assignments": [
{
"id": "12345",
"name": "name_in_caps",
"type": "string",
"value": "={{ $('Source Data').last().json.name.toUpperCase() }}"
},
{
"id": "67890",
"name": "age_in_dog_years",
"type": "number",
"value": "={{ Math.round($('Source Data').last().json.age / 7) }}"
},
{
"id": "abcde",
"name": "age_data_type",
"type": "string",
"value": "={{ typeof $('Source Data').last().json.age }}"
}
] }
},
"typeVersion": 3.4
},
{
"id": "0ebf5e6b-feb8-4f32-9979-9d7555b461ed",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-3440, 640],
"parameters": {
"color": 3,
"width": 580,
"height": 520,
"content": "## Lesson 6: A Touch of Magic (JS Functions)\n\nYou can do more than just retrieve data; you can **manipulate and inspect it!**\n\n**The Expressions:**\n- **Transform Text:** `{{ $('Source Data').last().json.name.toUpperCase() }}`\n- **Do Math:** `{{ Math.round($('Source Data').last().json.age / 7) }}`\n- **Check Data Type:** `{{ typeof $('Source Data').last().json.age }}`\n\n**Breakdown:**\n- **`.toUpperCase()`**: A standard JavaScript function for strings.\n- **`Math.round(...)`**: The `Math` object gives you access to powerful math functions.\n- **`typeof`**: An operator that tells you what kind of data you're looking at (\"string\", \"number\", \"object\", etc.)."
},
"typeVersion": 1
},
{
"id": "81d8c2d6-95cc-47e3-b0f0-9c698a120d1c",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "9. The \"All Items\" View",
"type": "n8n-nodes-base.set",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-1264, 992],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "12345",
"name": "all_skills_string",
"type": "string",
"value": "={{ $('Split Out Skills').all().map(item => item.json.skills).join(', ') }}"
}] }
},
"executeOnce": true,
"typeVersion": 3.4
},
{
"id": "609e27f8-1702-493e-bc8b-cbade4561bd2",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-1696, 640],
"parameters": {
"color": 5,
"width": 780,
"height": 520,
"content": "## Lesson 9: Working with Multiple Items (`$items` & Arrow Functions)\n\nWhat if a node outputs *multiple* items and you want to summarize them? `$items()` is your tool.\n\n**The Goal:** Get a single, comma-separated string of all the user's skills.\n\n**The Expression:** `{{ $('Split Out Skills').all().map(item `=>` item.json.skills).join(', ') }}`\n\n**What is `item => ...`?**\nThis is an **Arrow Function**, a shorthand for \"for each thing, do this\".\n- `item`: A temporary name for each item in the list as we loop over it.\n- =>: The \"arrow\" that separates the item from the action.\n- `item.json.skills`: The action to perform—in this case, get the skill value from the item."
},
"typeVersion": 1
},
{
"id": "d6afc595-c6f0-47b2-894e-b03d6a0227ee",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Final Exam",
"type": "n8n-nodes-base.set",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-688, 992],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "12345",
"name": "final_summary",
"type": "string",
"value": "=User {{ $('2. The n8n Selectors').last().json.user_name_from_first }} is {{ $('Source Data').last().json.age }}.\n\nTheir best skill is {{ $('3. Working with Arrays').last().json.second_skill }}.\n\nTheir first project was {{ $('Source Data').last().json.projects[0].name }}, which is now {{ $('5. The Combo Move').last().json.first_project_status }}.\n\nAll skills: {{ $('9. The \"All Items\" View').last().json.all_skills_string }}."
}] }
},
"typeVersion": 3.4
},
{
"id": "99a7fb24-491c-436e-a775-6fabdc2c4004",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note8",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-896, 736],
"parameters": {
"color": 6,
"width": 520,
"height": 420,
"content": "## 🎓 FINAL EXAM: Putting It All Together\n\nThis node uses everything we've learned to build a final summary object.\n\nLook at the expressions for each field. They pull data from different nodes and use different techniques you've just practiced.\n\n**Congratulations! You now have the foundational knowledge to link data and build powerful, dynamic workflows in n8n.**"
},
"typeVersion": 1
},
{
"id": "c3f2fd66-784d-4675-8cb3-72dd33e6ee4e",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "2. The n8n Selectors",
"type": "n8n-nodes-base.set",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-5568, 992],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "12345",
"name": "user_name_from_first",
"type": "string",
"value": "={{ $('Source Data').last().json.name }}"
}] }
},
"typeVersion": 3.4
},
{
"id": "bade9e22-6e9d-4f25-a1b7-974cbf2d5c61",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note9",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-5872, 528],
"parameters": {
"width": 680,
"height": 620,
"content": "## Lesson 2: The n8n Selectors (`.first()`, `.last()`, `.all()`)\n\nIn the last lesson, we used `.item`. When there is only one output item from a node, this is equivalent to `.last()`. Using `.last()` explicitly is often safer and clearer.\n\n**The Goal:** Get the user's name using the `.last()` selector.\n\n**The Expression:** `{{ $('Source Data').last().json.name }}`\n\n**Why is this better?**\nIf a node ever returns multiple items, `.last()` guarantees you only get data from the very last one.\n\nIf you ever need to match the selected data with the input items, this is where `.item` cannot be replaced.\n\n**Other Selectors:**\n- **`.first()`**: Gets the data from the first item.\n- **`.all()`**: Gets data from ALL items, returning it as an array of objects. (This is different from `$items`!)"
},
"typeVersion": 1
},
{
"id": "7c4b718e-fca8-4dbe-8b3b-d6aeeaa78d6d",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "7. Inspecting Objects",
"type": "n8n-nodes-base.set",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-2640, 992],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "12345",
"name": "contact_keys",
"type": "array",
"value": "={{ Object.keys($('Source Data').last().json.contact) }}"
}] }
},
"typeVersion": 3.4
},
{
"id": "089b02d1-e43b-4baf-8bcf-b2dd7a95df92",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note10",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-2832, 640],
"parameters": {
"color": 2,
"width": 500,
"height": 520,
"content": "## Lesson 7: Inspecting Objects (`Object.keys()`)\n\nWhat if you have an object but you don't know what keys are inside it? `Object.keys()` comes to the rescue.\n\n**The Goal:** Get a list of all the keys inside the `contact` object.\n\n**The Expression:** `{{ Object.keys($('Source Data').last().json.contact) }}`\n\nThis is incredibly useful for dynamically processing data. It returns an **array** containing the names of the keys (e.g., `[\"email\", \"phone\"]`)."
},
"typeVersion": 1
},
{
"id": "81a16f66-4fca-47af-841c-91e16ff9587e",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "8. Utility Functions",
"type": "n8n-nodes-base.set",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-2064, 992],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "12345",
"name": "contact_as_string",
"type": "string",
"value": "={{ JSON.stringify($('Source Data').last().json.contact, null, 2) }}"
}, {
"id": "06003b65-7482-4d5a-b2c0-1794859ab461",
"name": "skills",
"type": "array",
"value": "={{ $('Source Data').last().json.skills }}"
}] }
},
"typeVersion": 3.4
},
{
"id": "b7aea910-5fd6-420f-9814-77afb8ab9517",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note11",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-2304, 640],
"parameters": {
"width": 580,
"height": 520,
"content": "## Lesson 8: Utility Functions (`JSON.stringify()`)\n\nSometimes you need to convert a structured JSON object back into a clean, single string. This is common when sending data to another service, like in an AI prompt.\n\n**The Goal:** Turn the entire `contact` object into a formatted string.\n\n**The Expression:** `{{ JSON.stringify($('Source Data').last().json.contact, null, 2) }}`\n\n**Breakdown:**\n- **`JSON.stringify(...)`**: The function that does the conversion.\n- **`null, 2`**: These optional parameters tell it to \"pretty-print\" the string with an indentation of 2 spaces, making it readable."
},
"typeVersion": 1
},
{
"id": "2b83f9a2-7c71-43bb-b940-d0c3204cf6a2",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Split Out Skills",
"type": "n8n-nodes-base.splitOut",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-1488, 992],
"parameters": {
"include": "allOtherFields",
"options": {},
"fieldToSplitOut": "skills"
},
"typeVersion": 1
},
{
"id": "933a0592-2deb-4db7-b801-d52dbbed1252",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note12",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-352, -128],
"parameters": {
"color": 3,
"width": 540,
"height": 1280,
"content": "## Was this helpful? Let me know!\n[](https://n8n.ac)\n\nI really hope this tutorial helped you understand n8n Expressions better. Your feedback is incredibly valuable and helps me create better resources for the n8n community.\n\n### **Share Your Thoughts & Ideas**\n\nWhether you have a suggestion, found a typo, or just want to say thanks, I'd love to hear from you!\nHere's a simple n8n form built for this purpose:\n\n#### ➡️ **[Click here to give feedback](https://api.ia2s.app/form/templates/feedback?template=Expressions%20Tutorial)**\n\n### **Ready to Build Something Great?**\n\nIf you're looking to take your n8n skills or business automation to the next level, I can help.\n\n**🎓 n8n Coaching:** Want to become an n8n pro? I offer one-on-one coaching sessions to help you master workflows, tackle specific problems, and build with confidence.\n#### ➡️ **[Book a Coaching Session](https://api.ia2s.app/form/templates/coaching?template=Expressions%20Tutorial)**\n\n**💼 n8n Consulting:** Have a complex project, an integration challenge, or need a custom workflow built for your business? Let's work together to create a powerful automation solution.\n#### ➡️ **[Inquire About Consulting Services](https://api.ia2s.app/form/templates/consulting?template=Expressions%20Tutorial)**\n\n---\n\nHappy Automating!\nLucas Peyrin | [n8n Academy](https://n8n.ac)"
},
"typeVersion": 1
},
{
"id": "efc93cff-1e16-46fc-a839-7cca5ccd27f8",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note15",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-896, 304],
"parameters": {
"color": 6,
"width": 512,
"height": 408,
"content": "## [>> Go to Eval Workflow <<](https://n8n.io/workflows/6236)\n\nVerify your skills with a complete eval workflow to put your Expression Skills to the test.\n[](https://n8n.io/workflows/6236)"
},
"typeVersion": 1
}
],
connections: {
"Source Data": { "main": [[{
"node": "1. The Basics",
"type": "main",
"index": 0
}]] },
"1. The Basics": { "main": [[{
"node": "2. The n8n Selectors",
"type": "main",
"index": 0
}]] },
"Start Tutorial": { "main": [[{
"node": "Source Data",
"type": "main",
"index": 0
}]] },
"4. Going Deeper": { "main": [[{
"node": "5. The Combo Move",
"type": "main",
"index": 0
}]] },
"Split Out Skills": { "main": [[{
"node": "9. The \"All Items\" View",
"type": "main",
"index": 0
}]] },
"5. The Combo Move": { "main": [[{
"node": "6. A Touch of Magic",
"type": "main",
"index": 0
}]] },
"6. A Touch of Magic": { "main": [[{
"node": "7. Inspecting Objects",
"type": "main",
"index": 0
}]] },
"2. The n8n Selectors": { "main": [[{
"node": "3. Working with Arrays",
"type": "main",
"index": 0
}]] },
"8. Utility Functions": { "main": [[{
"node": "Split Out Skills",
"type": "main",
"index": 0
}]] },
"7. Inspecting Objects": { "main": [[{
"node": "8. Utility Functions",
"type": "main",
"index": 0
}]] },
"3. Working with Arrays": { "main": [[{
"node": "4. Going Deeper",
"type": "main",
"index": 0
}]] },
"9. The \"All Items\" View": { "main": [[{
"node": "Final Exam",
"type": "main",
"index": 0
}]] }
}
};
var workflow_logic_default = {
name: "Workflow logic",
meta: {
"templateCredsSetupCompleted": false,
"templateId": "workflow_logic"
},
nodes: [
{
"id": "d6cf9b3d-66b8-4022-8c9d-698e89cd22fd",
"name": "Start Sorting",
"type": "n8n-nodes-base.manualTrigger",
"position": [-880, 608],
"parameters": {},
"typeVersion": 1
},
{
"id": "b8c72a1e-0268-4d99-8141-ca14e35cbd6a",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [-1264, 192],
"parameters": {
"width": 624,
"height": 596,
"content": "### Tutorial: The Logic Trio (Merge, IF, Switch)\n\nWelcome! This workflow will teach you the three most important nodes for controlling the flow of your data.\n\n**The Analogy: A Package Sorting Center**\n- **Data Items:** Think of these as packages moving on a conveyor belt.\n- **Merge Node:** A point where multiple conveyor belts combine into one.\n- **IF Node:** A simple sorting gate with two paths (e.g., \"Fragile\" or \"Not Fragile\").\n- **Switch Node:** An advanced sorting machine with many paths (e.g., sorting by destination city).\n\n\n**How to use this tutorial:**\n1. Click **\"Execute Workflow\"**.\n2. Follow the flow from left to right, clicking on each node to see its output.\n3. Read the sticky notes to understand what each node does."
},
"typeVersion": 1
},
{
"id": "81532695-73c0-4357-a957-3d0ef580578f",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [-272, 192],
"parameters": {
"color": 4,
"width": 384,
"height": 604,
"content": "### 1. The Merge Node\n\n**Analogy:** A conveyor belt where packages from different loading docks (the `Set` nodes) come together.\n\n**What it does:** The Merge node combines multiple streams of data into a single stream.\n\nHere, it's set to **Append** mode, which is the most common. It waits for all incoming data and then passes it all through together.\n\n**➡️ Look at the output. We now have both the letter and the parcel in one list, ready for the next step!**"
},
"typeVersion": 1
},
{
"id": "dadbac54-6b31-4a0f-8d5e-b6121467e90e",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [144, 192],
"parameters": {
"color": 4,
"width": 384,
"height": 596,
"content": "### 2. The IF Node\n\n**Analogy:** A simple sorting gate with two paths: a \"true\" path and a \"false\" path.\n\n**What it does:** The IF node checks if a condition is met. If it's true, the data goes down the top output. If it's false, it goes down the bottom output.\n\nHere, we're asking a simple question: **\"Does this package have an `is_fragile` property?\"**\n\n**➡️ The parcel will go down the 'true' path, and the letters (which don't have that property) will go down the 'false' path.**"
},
"typeVersion": 1
},
{
"id": "0c330031-eab5-4ee3-8b11-91aee526952a",
"name": "Add 'Fragile' Handling",
"type": "n8n-nodes-base.set",
"position": [624, 512],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "12345",
"name": "handling_instructions",
"type": "string",
"value": "Handle with care!"
}] },
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "d14c0e68-3823-4f15-98eb-58a0d4983861",
"name": "Add 'Standard' Handling",
"type": "n8n-nodes-base.set",
"position": [624, 704],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "12345",
"name": "handling_instructions",
"type": "string",
"value": "Standard handling"
}] },
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "2ebde854-aa6b-48de-83b1-33950a1486e0",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [816, 272],
"parameters": {
"color": 5,
"width": 384,
"height": 552,
"content": "### Merge Again?\n\n**Why do we need another Merge node here?**\n\nAfter the IF node, our data was split into two different paths. Before we can perform the *next* sorting step on all packages, we need to get them back onto the same conveyor belt.\n\nThis is a very common and important pattern in n8n: \n**Split -> Process -> Merge.**"
},
"typeVersion": 1
},
{
"id": "66ca4ac5-aceb-426e-ab22-50c012602a85",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [1232, 176],
"parameters": {
"color": 4,
"width": 400,
"height": 648,
"content": "### 3. The Switch Node\n\n**Analogy:** An advanced sorting machine that can send packages to many different destinations.\n\n**What it does:** The Switch node is like an IF node with multiple doors. It checks the value of a single field (`destination` in this case) and sends the data down the path that matches the value.\n\n- If the destination is \"London\", it goes to output 0.\n- If it's \"New York\", it goes to output 1.\n- If it's something else, it goes to the **default** output.\n\n\n**➡️ This is much cleaner than using many IF nodes chained together!**"
},
"typeVersion": 1
},
{
"id": "10f56a78-85cd-4c8b-88e0-b6b3f0346e89",
"name": "Send to London Bin",
"type": "n8n-nodes-base.set",
"position": [1776, 320],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "12345",
"name": "sorting_bin",
"type": "string",
"value": "A1 (London)"
}] },
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "2cba95bd-2c9b-42d1-90cb-74c2edf97ec7",
"name": "Send to New York Bin",
"type": "n8n-nodes-base.set",
"position": [1776, 512],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "12345",
"name": "sorting_bin",
"type": "string",
"value": "B2 (New York)"
}] },
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "1531b4e3-eece-4c89-98bd-e9633fdd77f6",
"name": "Send to Tokyo Bin",
"type": "n8n-nodes-base.set",
"position": [1776, 704],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "12345",
"name": "sorting_bin",
"type": "string",
"value": "C3 (Tokyo)"
}] },
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "d46604a8-663e-42bd-a175-a34edb8953fb",
"name": "Default Bin",
"type": "n8n-nodes-base.set",
"position": [1776, 896],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "12345",
"name": "sorting_bin",
"type": "string",
"value": "Return to Sender"
}] },
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "570040aa-3f8d-4f59-904a-ee3deb36a9df",
"name": "Final Sorted Packages",
"type": "n8n-nodes-base.noOp",
"position": [2160, 624],
"parameters": {},
"typeVersion": 1
},
{
"id": "756313e7-d2f3-45cc-a4f5-f91e7a8f778a",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [2032, 272],
"parameters": {
"color": 6,
"width": 368,
"height": 560,
"content": "### All Packages Sorted!\n\nCongratulations! You've successfully used the three logic nodes to sort your packages.\n\n**You learned how to:**\n- **Merge** data from different sources.\n- Use **IF** for simple true/false decisions.\n- Use **Switch** for complex, multi-path routing.\n\n\nMastering these three nodes is the key to building powerful and intelligent workflows in n8n."
},
"typeVersion": 1
},
{
"id": "4466652f-cc2b-47b1-bf4f-98d89753881f",
"name": "3. Switch Node",
"type": "n8n-nodes-base.switch",
"position": [1392, 576],
"parameters": {
"rules": { "values": [
{
"outputKey": "London",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [{
"id": "8d43cde4-027a-4ca7-a24c-6f74f12d6238",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.destination }}",
"rightValue": "London"
}]
},
"renameOutput": true
},
{
"outputKey": "New York",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [{
"id": "399a0fbd-6be5-48e9-9f66-04cf385cb418",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.destination }}",
"rightValue": "New York"
}]
},
"renameOutput": true
},
{
"outputKey": "Tokyo",
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [{
"id": "a69d387d-a174-42b3-bc5f-c8b46b7c2375",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.destination }}",
"rightValue": "Tokyo"
}]
},
"renameOutput": true
}
] },
"options": {
"fallbackOutput": "extra",
"renameFallbackOutput": "Default"
}
},
"typeVersion": 3.2
},
{
"id": "add68013-30a7-43db-93d4-5af691764684",
"name": "Create Letter",
"type": "n8n-nodes-base.set",
"position": [-496, 416],
"parameters": {
"options": {},
"assignments": { "assignments": [
{
"id": "12345",
"name": "package_id",
"type": "string",
"value": "L-001"
},
{
"id": "67890",
"name": "type",
"type": "string",
"value": "letter"
},
{
"id": "abcde",
"name": "destination",
"type": "string",
"value": "London"
}
] }
},
"typeVersion": 3.4
},
{
"id": "5af75a45-b912-41c6-b073-03188ae914ef",
"name": "1. Merge Node",
"type": "n8n-nodes-base.merge",
"position": [-128, 592],
"parameters": { "numberInputs": 3 },
"typeVersion": 3.2
},
{
"id": "11627c3b-465a-4a4a-bfe9-95c08d502f2f",
"name": "2. IF Node",
"type": "n8n-nodes-base.if",
"position": [272, 608],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [{
"id": "a68aad83-1d09-4ebe-9732-aaedc407bb4b",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.is_fragile }}",
"rightValue": ""
}]
},
"looseTypeValidation": true
},
"typeVersion": 2.2
},
{
"id": "4b5b5ba4-25e1-4ef0-93d8-50670dbc1ce0",
"name": "Re-group All Packages",
"type": "n8n-nodes-base.merge",
"position": [960, 608],
"parameters": {},
"typeVersion": 3.2
},
{
"id": "21d270c4-c19c-42ed-a6e3-67ecedd1c0c9",
"name": "Create 2nd Letter",
"type": "n8n-nodes-base.set",
"position": [-496, 608],
"parameters": {
"options": {},
"assignments": { "assignments": [
{
"id": "12345",
"name": "package_id",
"type": "string",
"value": "L-002"
},
{
"id": "67890",
"name": "type",
"type": "string",
"value": "letter"
},
{
"id": "abcde",
"name": "destination",
"type": "string",
"value": "Tokyo"
}
] }
},
"typeVersion": 3.4
},
{
"id": "8a295323-9515-410c-9ac1-fb431d08cea2",
"name": "Create Parcel",
"type": "n8n-nodes-base.set",
"position": [-496, 800],
"parameters": {
"options": {},
"assignments": { "assignments": [
{
"id": "12345",
"name": "package_id",
"type": "string",
"value": "P-001"
},
{
"id": "67890",
"name": "type",
"type": "string",
"value": "parcel"
},
{
"id": "abcde",
"name": "destination",
"type": "string",
"value": "New York"
},
{
"id": "fghij",
"name": "is_fragile",
"type": "boolean",
"value": true
}
] }
},
"typeVersion": 3.4
},
{
"id": "c06a0f40-cc18-4710-8b07-9d396e89a83d",
"name": "Sticky Note10",
"type": "n8n-nodes-base.stickyNote",
"position": [2432, 32],
"parameters": {
"color": 3,
"width": 540,
"height": 800,
"content": "## Was this helpful? Let me know!\n\nI really hope this template helped you understand how Logical Operation Nodes work here in n8n. Your feedback is incredibly valuable and helps me create better resources for the n8n community.\n\n### **Share Your Thoughts & Ideas**\n\nWhether you have a suggestion, found a typo, or just want to say thanks, I'd love to hear from you!\nHere's a simple n8n form built for this purpose:\n\n#### ➡️ **[Click here to give feedback](https://api.ia2s.app/form/templates/feedback?template=Merge%20If%20Switch)**\n\n### **Ready to Build Something Great?**\n\nIf you're looking to take your n8n skills or business automation to the next level, I can help.\n\n**🎓 n8n Coaching:** Want to become an n8n pro? I offer one-on-one coaching sessions to help you master workflows, tackle specific problems, and build with confidence.\n#### ➡️ **[Book a Coaching Session](https://api.ia2s.app/form/templates/coaching?template=Merge%20If%20Switch)**\n\n**💼 n8n Consulting:** Have a complex project, an integration challenge, or need a custom workflow built for your business? Let's work together to create a powerful automation solution.\n#### ➡️ **[Inquire About Consulting Services](https://api.ia2s.app/form/templates/consulting?template=Merge%20If%20Switch)**\n\n---\n\nHappy Automating!\nLucas Peyrin"
},
"typeVersion": 1
}
],
pinData: {},
connections: {
"2. IF Node": { "main": [[{
"node": "Add 'Fragile' Handling",
"type": "main",
"index": 0
}], [{
"node": "Add 'Standard' Handling",
"type": "main",
"index": 0
}]] },
"Default Bin": { "main": [[{
"node": "Final Sorted Packages",
"type": "main",
"index": 0
}]] },
"1. Merge Node": { "main": [[{
"node": "2. IF Node",
"type": "main",
"index": 0
}]] },
"Create Letter": { "main": [[{
"node": "1. Merge Node",
"type": "main",
"index": 0
}]] },
"Create Parcel": { "main": [[{
"node": "1. Merge Node",
"type": "main",
"index": 2
}]] },
"Start Sorting": { "main": [[
{
"node": "Create Parcel",
"type": "main",
"index": 0
},
{
"node": "Create 2nd Letter",
"type": "main",
"index": 0
},
{
"node": "Create Letter",
"type": "main",
"index": 0
}
]] },
"3. Switch Node": { "main": [
[{
"node": "Send to London Bin",
"type": "main",
"index": 0
}],
[{
"node": "Send to New York Bin",
"type": "main",
"index": 0
}],
[{
"node": "Send to Tokyo Bin",
"type": "main",
"index": 0
}],
[{
"node": "Default Bin",
"type": "main",
"index": 0
}]
] },
"Create 2nd Letter": { "main": [[{
"node": "1. Merge Node",
"type": "main",
"index": 1
}]] },
"Send to Tokyo Bin": { "main": [[{
"node": "Final Sorted Packages",
"type": "main",
"index": 0
}]] },
"Send to London Bin": { "main": [[{
"node": "Final Sorted Packages",
"type": "main",
"index": 0
}]] },
"Send to New York Bin": { "main": [[{
"node": "Final Sorted Packages",
"type": "main",
"index": 0
}]] },
"Re-group All Packages": { "main": [[{
"node": "3. Switch Node",
"type": "main",
"index": 0
}]] },
"Add 'Fragile' Handling": { "main": [[{
"node": "Re-group All Packages",
"type": "main",
"index": 0
}]] },
"Add 'Standard' Handling": { "main": [[{
"node": "Re-group All Packages",
"type": "main",
"index": 1
}]] }
}
};
var api_fundamentals_default = {
name: "API fundamentals",
meta: {
"templateCredsSetupCompleted": false,
"templateId": "api_fundamentals"
},
nodes: [
{
"id": "52e84ac4-6495-443e-b3d6-16d3291a6261",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Start Tutorial",
"type": "n8n-nodes-base.manualTrigger",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [320, 464],
"parameters": {},
"typeVersion": 1
},
{
"id": "5aab442c-2fd7-4b41-ae0f-eeaf9e722213",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "1. The Kitchen (GET /menu)",
"type": "n8n-nodes-base.webhook",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [1632, 464],
"webhookId": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
"parameters": {
"path": "/tutorial/api/menu",
"options": {},
"responseMode": "lastNode"
},
"typeVersion": 2
},
{
"id": "cee20f21-eb2e-4ea6-947f-acbc7f27a437",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Prepare Menu Data",
"type": "n8n-nodes-base.set",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [1856, 464],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "12345",
"name": "item",
"type": "string",
"value": "Pizza"
}, {
"id": "67890",
"name": "price",
"type": "number",
"value": 12
}] }
},
"typeVersion": 3.4
},
{
"id": "4af53e2a-0d5a-4b2e-8e65-fbc19d138abb",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "1. The Customer (GET Menu Item)",
"type": "n8n-nodes-base.httpRequest",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [1312, 464],
"parameters": {
"url": "={{ $json.base_url }}/tutorial/api/menu",
"options": {}
},
"typeVersion": 4.1
},
{
"id": "a1f76b04-daf5-41b9-b300-332b61d69cda",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [-320, -96],
"parameters": {
"width": 800,
"height": 768,
"content": "## Tutorial - What is an API?\n\nWelcome! This workflow will teach you the basics of APIs (Application Programming Interfaces).\n\n**What is an API?**\nThink of it like ordering food at a restaurant.\n- **You** are the \"Client\" (the **HTTP Request** node). You want something.\n- The **Kitchen** is the \"Server\" (the **Webhook** node). It has the data/service you want.\n- The **API** is the **Waiter and the Menu**. It's the set of rules and options for making a request and getting a response.\n\n\n**What is an Endpoint?**\nAn endpoint is a specific address for a specific action. For example, `GET /menu` is one endpoint to get the menu, and `POST /review` is another to submit a review. Each webhook in this tutorial represents one endpoint.\n\n**How to use this tutorial:**\n1. **Activate** the workflow (toggle on the top right).\n2. **Configure the Base URL** (see the yellow note to the left).\n3. Click **\"Execute Workflow\"**. The workflow will run from top to bottom.\n4. Explore each \"Lesson\" by clicking on the **HTTP Request** node (the Customer) and its corresponding **Webhook** node (the Kitchen).\n\n\n**➡️ How to See the Data:**\nWhen you run this, the Webhook nodes run in the background. To see the data they received, go to the **\"Executions\"** tab of this workflow. You will see a separate execution for each API call!"
},
"typeVersion": 1
},
{
"id": "d37a6305-25ff-412d-8763-06e4c0a6b8ae",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [1248, 160],
"parameters": {
"color": 7,
"width": 864,
"height": 512,
"content": "#### Lesson 1: The Basics (Method & URL)\n\nThis is the simplest possible request.\n\n- **URL (Uniform Resource Locator):** This is the **address of the restaurant's kitchen**. The HTTP Request node needs to know exactly where to send the order. We use an expression to get the Webhook's address automatically from our configuration.\n\n- **Method: `GET`**: This is **what you want to do**. `GET` is used to **retrieve** or **get** information. It's like asking the waiter, \"What's on the menu today?\" `GET` requests are simple and don't contain a \"body\" payload.\n\n\n**➡️ Look at the output of the HTTP Request node. It received exactly what the `Respond to Webhook` node sent back!**"
},
"typeVersion": 1
},
{
"id": "edef5d0b-f0a4-4651-9ee4-84ecbe9e8062",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "2. The Customer (GET with Query Params)",
"type": "n8n-nodes-base.httpRequest",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [1312, 1136],
"parameters": {
"url": "={{ $json.base_url }}/tutorial/api/order",
"options": {},
"sendQuery": true,
"queryParameters": { "parameters": [{
"name": "extra_cheese",
"value": "true"
}] }
},
"typeVersion": 4.1
},
{
"id": "a2b5422d-fb46-4737-8182-dbbb50293fa1",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "2. The Kitchen (GET /order)",
"type": "n8n-nodes-base.webhook",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [1632, 1136],
"webhookId": "b2c3d4e5-f6a7-8901-2345-67890abcdef1",
"parameters": {
"path": "/tutorial/api/order",
"options": {},
"responseMode": "lastNode"
},
"typeVersion": 2
},
{
"id": "91d5b241-64c9-48cf-bf79-fff121d0febf",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Prepare Cheese Pizza",
"type": "n8n-nodes-base.set",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [2064, 1040],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "12345",
"name": "order",
"type": "string",
"value": "Pizza with extra cheese"
}] }
},
"typeVersion": 3.4
},
{
"id": "bee0bce4-f854-4c1d-9ad1-b526f02e1925",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Prepare Plain Pizza",
"type": "n8n-nodes-base.set",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [2064, 1232],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "12345",
"name": "order",
"type": "string",
"value": "Plain Pizza"
}] }
},
"typeVersion": 3.4
},
{
"id": "d6862155-ea76-4357-bae7-c4329ec1a612",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [1248, 704],
"parameters": {
"color": 7,
"width": 1056,
"height": 720,
"content": "#### Lesson 2: Customizing a Request (Query Parameters)\n\nWhat if you want to customize your order? That's what Query Parameters are for.\n\n**Query Parameters:** These are simple `key=value` options added to the end of the URL after a `?`. They are used to filter, sort, or specify what you want.\n\nIt's like telling the waiter, \"I'll have the pizza... **and can you add extra cheese?**\"\n\nThe full URL sent by the HTTP Request node looks like this:\n`.../tutorial/api/order?extra_cheese=true`\n\n**⚠️ Important:** Because they are part of the URL, **all query parameter values are treated as strings!** The webhook receives `\"true\"` (a string), not `true` (a boolean). The IF node is set to a \"loose\" comparison to handle this correctly.\n\n**➡️ The Webhook node uses an IF node to check for this parameter and changes its response. Try setting the value to `false` in the HTTP Request node and run it again!**"
},
"typeVersion": 1
},
{
"id": "c568bc8c-822d-42b9-ab9b-2d91ae4e4fed",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "3. The Customer (POST with Body)",
"type": "n8n-nodes-base.httpRequest",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [1312, 1744],
"parameters": {
"url": "={{ $json.base_url }}/tutorial/api/review",
"method": "POST",
"options": {},
"sendBody": true,
"bodyParameters": { "parameters": [{
"name": "comment",
"value": "The pizza was amazing!"
}, {
"name": "rating",
"value": 5
}] }
},
"typeVersion": 4.1
},
{
"id": "0f0dae83-3290-4a5f-a039-3b54d87bcf7d",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "3. The Kitchen (POST /review)",
"type": "n8n-nodes-base.webhook",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [1632, 1744],
"webhookId": "c3d4e5f6-a7b8-9012-3456-7890abcdef12",
"parameters": {
"path": "/tutorial/api/review",
"options": {},
"httpMethod": "POST",
"responseMode": "lastNode"
},
"typeVersion": 2
},
{
"id": "c5aa4f94-eca3-4321-b961-084917b93b81",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Process Review Data",
"type": "n8n-nodes-base.set",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [1856, 1744],
"parameters": {
"options": {},
"assignments": { "assignments": [
{
"id": "12345",
"name": "status",
"type": "string",
"value": "review_received"
},
{
"id": "67890",
"name": "your_comment",
"type": "string",
"value": "={{ $json.body.comment }}"
},
{
"id": "91011",
"name": "your_rating",
"type": "number",
"value": "={{ $json.body.rating }}"
}
] }
},
"typeVersion": 3.4
},
{
"id": "4020d264-7c01-4cf2-b51e-b8b3f2bc382b",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [1248, 1456],
"parameters": {
"color": 7,
"width": 1056,
"height": 476,
"content": "#### Lesson 3: Sending Data (POST & Body)\n\nSometimes, you don't want to *get* data, you want to *send* it.\n\n- **Method: `POST`**: This method is used to **send new data** to the server to create or update a resource. It's like handing the waiter a completed customer feedback card.\n\n- **Body:** This is the **actual data you are sending**. Unlike a `GET` request, a `POST` request has a \"body\" where you can put complex data, like a JSON object. This is much more powerful than query parameters for sending information.\n\n\n**➡️ The HTTP Request sends a JSON object in its body. The Webhook receives it and includes your comment in its response. Check the \"Executions\" panel to see the full body the webhook received!**"
},
"typeVersion": 1
},
{
"id": "0fec2702-e2c9-4090-a327-f1d723b144b6",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "4. The Customer (GET with Headers/Auth)",
"type": "n8n-nodes-base.httpRequest",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [1312, 2608],
"parameters": {
"url": "={{ $json.base_url }}/tutorial/api/secret-dish",
"options": {},
"sendHeaders": true,
"headerParameters": { "parameters": [{
"name": "x-api-key",
"value": "super-secret-key"
}] }
},
"typeVersion": 4.1
},
{
"id": "a35c1fc1-65a3-4d84-b29e-22939d334005",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "4. The Kitchen (GET /secret-dish)",
"type": "n8n-nodes-base.webhook",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [1632, 2608],
"webhookId": "d4e5f6a7-b8c9-0123-4567-890abcdef123",
"parameters": {
"path": "/tutorial/api/secret-dish",
"options": {},
"responseMode": "responseNode"
},
"typeVersion": 2
},
{
"id": "8cb4d6d3-c29f-4562-a966-d13beafd8f40",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [1248, 1968],
"parameters": {
"color": 7,
"width": 1056,
"height": 908,
"content": "#### Lesson 4: Identification (Headers & Auth)\n\nHeaders contain meta-information *about* your request. They're not part of the data itself, but they provide important context. Authentication is a common use case.\n\n- **Headers:** Think of this as **showing your VIP membership card** or whispering a secret password to the waiter. It's information that proves who you are or what your request's properties are.\n\n- **Authentication (Auth):** This is the process of proving your identity. Here, we use a custom header (`x-api-key`) as a \"secret key\". In the real world, this is how most APIs control access.\n\n\n**➡️ The Webhook checks for the correct secret key in the headers. If it's wrong or missing, it denies the request with a `401 Unauthorized` status code. Try changing the key in the HTTP Request node!**"
},
"typeVersion": 1
},
{
"id": "3ab5e611-71d2-4ff3-9462-fb69d60b48aa",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "5. The Customer (Request with Timeout)",
"type": "n8n-nodes-base.httpRequest",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"onError": "continueErrorOutput",
"position": [1312, 3328],
"parameters": {
"url": "={{ $json.base_url }}/tutorial/api/slow-service",
"options": { "timeout": 2e3 }
},
"typeVersion": 4.1
},
{
"id": "5545e004-b8c3-4c47-91ee-31a2efa4c833",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "5. The Kitchen (GET /slow-service)",
"type": "n8n-nodes-base.webhook",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [1632, 3328],
"webhookId": "e5f6a7b8-c9d0-1234-5678-90abcdef1234",
"parameters": {
"path": "/tutorial/api/slow-service",
"options": {},
"responseMode": "lastNode"
},
"typeVersion": 2
},
{
"id": "237f65ad-3f1d-4afe-8541-89108afd57de",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Prepare Slow Response",
"type": "n8n-nodes-base.set",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [2064, 3328],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "12345",
"name": "status",
"type": "string",
"value": "Finally, your food is here!"
}] }
},
"typeVersion": 3.4
},
{
"id": "dbe83810-b95f-46dd-bc2d-bf456fe11f94",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [1248, 2912],
"parameters": {
"color": 7,
"width": 1056,
"height": 624,
"content": "#### Lesson 5: Being Patient (Timeout & Error Handling)\n\nAn API request isn't instant. What if the kitchen is really busy?\n\n- **Timeout:** This is the **maximum amount of time (in milliseconds) you are willing to wait** for a response before you give up.\n\n- **On Error Output:** Notice the HTTP Request node has two outputs. The bottom one is the **error path**. If the request fails for any reason (like a timeout), the workflow will continue down this path instead of stopping.\n\n\nIn this example:\n- The **Kitchen (Webhook)** has a 3-second delay.\n- The **Customer (HTTP Request)** is only willing to wait for 2 seconds (2000 ms).\n\n\n**➡️ This request is designed to FAIL! The customer gives up before the kitchen can finish. The error output of the HTTP Request node will light up. This is crucial for building robust workflows that can handle API failures.**"
},
"typeVersion": 1
},
{
"id": "4e74c301-06e2-4b19-8101-3c63816ea615",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Wait 3 seconds",
"type": "n8n-nodes-base.wait",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [1856, 3328],
"webhookId": "86b44ff3-7d31-4027-a1cd-df67f9ab974a",
"parameters": { "amount": 3 },
"typeVersion": 1.1
},
{
"id": "42619763-955e-40d6-bbdb-23eea3c0bd85",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "IF Authorized",
"type": "n8n-nodes-base.if",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [1856, 2608],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [{
"id": "ca861c2d-78d9-403b-8bab-28d8e7dcf39c",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.headers['x-api-key'] }}",
"rightValue": "super-secret-key"
}]
}
},
"typeVersion": 2.2
},
{
"id": "562d4436-df8c-47bf-b259-eea95ea88147",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "IF extra cheese",
"type": "n8n-nodes-base.if",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [1840, 1136],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [{
"id": "ca861c2d-78d9-403b-8bab-28d8e7dcf39c",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.query.extra_cheese }}",
"rightValue": "your-api-key-for-example"
}]
},
"looseTypeValidation": true
},
"typeVersion": 2.2
},
{
"id": "57a48d64-954c-4871-a5ff-5390c69a003a",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note12",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [656, 2336],
"parameters": {
"color": 4,
"width": 540,
"height": 1200,
"content": "## Was this helpful? Let me know!\n[](https://api.ia2s.app/form/templates/academy)\n\nI really hope this tutorial helped you understand APIs better. Your feedback is incredibly valuable and helps me create better resources for the n8n community.\n\n### **Have Feedback, a Question, or a Project Idea?**\n\nI've streamlined the way we connect. It all starts with one simple form that takes less than 10 seconds. After that, you'll chat with my AI assistant who will gather the key details and pass them directly on to me.\n\n#### ➡️ **[Click here to start the conversation](https://api.ia2s.app/form/templates/academy)**\n\nUse this single link for anything you need:\n\n* **Give Feedback:** Share your thoughts on this template—whether you found a typo, encountered an unexpected error, have a suggestion, or just want to say thanks!\n\n* **n8n Coaching:** Get personalized, one-on-one guidance to master n8n. We can work together to get you launched with confidence or help you reach an expert level.\n\n* **n8n Consulting:** Have a complex business challenge or need a custom workflow built from scratch? Let's partner on a powerful automation solution tailored to your specific needs.\n\n---\n\nHappy Automating!\nLucas Peyrin | [n8n Academy](https://n8n.ac)"
},
"typeVersion": 1
},
{
"id": "df8ea622-c2a5-4f6b-9a09-5d35b1dea0aa",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [512, -96],
"parameters": {
"color": 3,
"width": 700,
"height": 764,
"content": "## ✨ **CONFIGURATION REQUIRED** ✨\n\nTo use this interactive tutorial, you need to tell the \"Customer\" nodes where to find the \"Kitchen\" nodes.\n\n### **1. Get your Webhook URL**\n\n* **Activate** this workflow using the toggle switch at the top right of the screen.\n* Open any Webhook node in this workflow (e.g., `1. The Kitchen (GET /menu)`).\n* Go to the **Production URL** field and click the copy button.\n\n### **2. Update the CONFIGURATION Node**\n\n* Open the `CONFIGURATION` node (the one this note is pointing to).\n* In the **Value** field, **paste the full URL** you just copied.\n\n\nThat's it! Now you can run the workflow, and all the HTTP Request nodes will know how to call your webhooks."
},
"typeVersion": 1
},
{
"id": "c0c76d70-c630-4630-a22e-1909a410c527",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Base URL",
"type": "n8n-nodes-base.set",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [912, 464],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "7edededc-2f40-4b8e-b8db-ab4816f1a28e",
"name": "base_url",
"type": "string",
"value": "={{ $json.your_n8n_webhook_url.match(/^(https?:\\/\\/[^\\/]+)\\/(webhook-test|webhook|v1|[^\\/]+)/)[1] + '/' + $json.your_n8n_webhook_url.match(/^(https?:\\/\\/[^\\/]+)\\/(webhook-test|webhook|v1|[^\\/]+)/)[2].replace('webhook-test','webhook') }}"
}] }
},
"typeVersion": 3.4
},
{
"id": "e5f25f10-a1f0-4d67-b0fb-c3a2b8abf786",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note8",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [1264, 2288],
"parameters": {
"color": 3,
"width": 304,
"height": 496,
"content": "### ⚠️ **Security Best Practice**\n\nFor this tutorial, we are putting the API key directly in the header. \n\n**In a real project, NEVER do this!**\n\nAlways use n8n's built-in **Credentials** system to store and manage secret keys. You would create a \"Header Auth\" credential and select it in the HTTP Request node's \"Authentication\" parameter. This keeps your secrets safe and out of your workflow JSON."
},
"typeVersion": 1
},
{
"id": "e908261b-cced-4539-9ea0-f40fb9e009e5",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Respond with Secret",
"type": "n8n-nodes-base.respondToWebhook",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [2064, 2496],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "{\n \"dish\": \"The Chef's Special Truffle Pasta\"\n}"
},
"typeVersion": 1.4
},
{
"id": "52d54052-eab2-45b8-ac9a-389b7fdf40d3",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Respond: Unauthorized (401)",
"type": "n8n-nodes-base.respondToWebhook",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [2064, 2704],
"parameters": {
"options": { "responseCode": 401 },
"respondWith": "text",
"responseBody": "You are not authorized to see the secret dish."
},
"typeVersion": 1.4
},
{
"id": "f9d72d6c-47b8-4f51-8a69-ce4d557ae90f",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "OpenAPI Spec",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [2336, 704],
"parameters": {
"color": 6,
"width": 696,
"height": 2096,
"content": "# What other services give you\n\nReal-world APIs have documentation that tells developers how to use them. This is what the documentation for our little tutorial API would look like. To put you in context, you would see this documentation and create http request nodes accordingly.\n\n**Remember**, AI can help you !\n\n\n# API Documentation Example\n\nA simple API to demonstrate http requests in n8n.\n\n## API Endpoints\n\n### GET /tutorial/api/menu\n\n**Summary:** Get the menu\n\n**Responses:**\n* `200 OK`: The restaurant menu.\n\n---\n\n### GET /tutorial/api/order\n\n**Summary:** Get a customized order\n\n**Parameters:**\n* `extra_cheese` (query, string, example: `true`): Whether to add extra cheese.\n\n\n**Responses:**\n* `200 OK`: Your customized pizza order.\n\n---\n\n### POST /tutorial/api/review\n\n**Summary:** Submit a review\n\n**Request Body (application/json):**\n```json\n{\n \"comment\": \"string\",\n \"rating\": 0\n}\n```\n* `comment` (string): The review comment.\n* `rating` (integer): The rating given (e.g., 1-5).\n\n\n**Responses:**\n* `200 OK`: Confirmation of review receipt.\n\n---\n\n### GET /tutorial/api/secret-dish\n\n**Summary:** Get the secret dish (Auth Required)\n\n**Authentication:** Requires API Key. See [Authentication](https://docs.n8n.io/integrations/builtin/credentials/httprequest/) section for details.\n\n**Responses:**\n* `200 OK`: The secret dish.\n* `401 Unauthorized`: Authentication is required or invalid.\n\n---\n\n### GET /tutorial/api/slow-service\n\n**Summary:** A slow endpoint to test timeouts\n\n**Responses:**\n* `200 OK`: A delayed response.\n\n---\n\n## Authentication\n\nThis API uses an API Key for authentication on certain endpoints.\n\n### API Key Authentication (`ApiKeyAuth`)\n\n* **Type:** API Key\n* **Location:** Header\n* **Header Name:** `x-api-key`\n\n\nTo authenticate, include your API key in the `x-api-key` header of your request.\n\n**Example:**\n```\nGET /tutorial/api/secret-dish\nHost: your-api-domain.com\nx-api-key: YOUR_API_KEY_HERE\n```"
},
"typeVersion": 1
},
{
"id": "fba94b52-0c18-4f3c-a702-aa883a3df293",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [2320, 3040],
"parameters": {
"color": 7,
"width": 384,
"height": 496,
"content": ""
},
"typeVersion": 1
},
{
"id": "d6b845a9-9d27-49ec-a9fe-ac48650bd260",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note9",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [1760, 1040],
"parameters": {
"color": 7,
"width": 256,
"height": 272,
"content": ""
},
"typeVersion": 1
},
{
"id": "c6d51163-f9ea-4d6b-af33-32107d3feb6e",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note10",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [2128, 448],
"parameters": {
"color": 7,
"width": 176,
"height": 224,
"content": ""
},
"typeVersion": 1
},
{
"id": "b5a01aca-47be-42d8-8366-4f91bcf24682",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note11",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [2032, 1728],
"parameters": {
"color": 7,
"width": 256,
"content": ""
},
"typeVersion": 1
},
{
"id": "adacc21c-801a-49bb-a6da-630fc8b4272c",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "⚙️CONFIGURATION⚙️",
"type": "n8n-nodes-base.set",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [688, 464],
"parameters": {
"options": {},
"assignments": { "assignments": [{
"id": "7edededc-2f40-4b8e-b8db-ab4816f1a28e",
"name": "your_n8n_webhook_url",
"type": "string",
"value": "PASTE_YOUR_WEBHOOK_URL_HERE"
}] }
},
"typeVersion": 3.4
},
{
"id": "9eb6ee32-0d75-4817-a3d0-3106d88b14e6",
"cid": "Ikx1Y2FzIFBleXJpbiI",
"name": "Sticky Note14",
"type": "n8n-nodes-base.stickyNote",
"notes": "© 2025 Lucas Peyrin",
"creator": "Lucas Peyrin",
"position": [1600, 2272],
"parameters": {
"color": 7,
"width": 400,
"height": 256,
"content": ""
},
"typeVersion": 1
}
],
pinData: {
"1. The Kitchen (GET /menu)": [{
"body": {},
"query": {},
"params": {},
"headers": {
"host": "your-n8n-instance.com",
"accept": "application/json,text/html,application/xhtml+xml,application/xml,text/*;q=0.9, image/*;q=0.8, */*;q=0.7",
"user-agent": "n8n",
"accept-encoding": "gzip, deflate, br",
"x-forwarded-for": "192.168.1.1",
"x-forwarded-host": "your-n8n-instance.com",
"x-forwarded-proto": "https"
},
"webhookUrl": "https://your-n8n-instance.com/webhook/tutorial/api/menu"
}],
"2. The Kitchen (GET /order)": [{
"body": {},
"query": { "extra_cheese": "true" },
"params": {},
"headers": {
"host": "your-n8n-instance.com",
"accept": "application/json,text/html,application/xhtml+xml,application/xml,text/*;q=0.9, image/*;q=0.8, */*;q=0.7",
"user-agent": "n8n",
"accept-encoding": "gzip, deflate, br",
"x-forwarded-for": "192.168.1.1",
"x-forwarded-host": "your-n8n-instance.com",
"x-forwarded-proto": "https"
},
"webhookUrl": "https://your-n8n-instance.com/webhook/tutorial/api/order?extra_cheese=true"
}],
"3. The Kitchen (POST /review)": [{
"body": {
"rating": 5,
"comment": "The pizza was amazing!"
},
"query": {},
"params": {},
"headers": {
"host": "your-n8n-instance.com",
"accept": "application/json,text/html,application/xhtml+xml,application/xml,text/*;q=0.9, image/*;q=0.8, */*;q=0.7",
"user-agent": "n8n",
"content-type": "application/json",
"content-length": "49",
"accept-encoding": "gzip, deflate, br",
"x-forwarded-for": "192.168.1.1",
"x-forwarded-host": "your-n8n-instance.com",
"x-forwarded-proto": "https"
},
"webhookUrl": "https://your-n8n-instance.com/webhook/tutorial/api/review"
}],
"4. The Kitchen (GET /secret-dish)": [{
"body": {},
"query": {},
"params": {},
"headers": {
"host": "your-n8n-instance.com",
"accept": "application/json,text/html,application/xhtml+xml,application/xml,text/*;q=0.9, image/*;q=0.8, */*;q=0.7",
"x-api-key": "super-secret-key",
"user-agent": "n8n",
"accept-encoding": "gzip, deflate, br",
"x-forwarded-for": "192.168.1.1",
"x-forwarded-host": "your-n8n-instance.com",
"x-forwarded-proto": "https"
},
"webhookUrl": "https://your-n8n-instance.com/webhook/tutorial/api/secret-dish"
}],
"5. The Kitchen (GET /slow-service)": [{
"body": {},
"query": {},
"params": {},
"headers": {
"host": "your-n8n-instance.com",
"accept": "application/json,text/html,application/xhtml+xml,application/xml,text/*;q=0.9, image/*;q=0.8, */*;q=0.7",
"user-agent": "n8n",
"accept-encoding": "gzip, deflate, br",
"x-forwarded-for": "192.168.1.1",
"x-forwarded-host": "your-n8n-instance.com",
"x-forwarded-proto": "https"
},
"webhookUrl": "https://your-n8n-instance.com/webhook/tutorial/api/slow-service"
}]
},
connections: {
"Base URL": { "main": [[
{
"node": "1. The Customer (GET Menu Item)",
"type": "main",
"index": 0
},
{
"node": "2. The Customer (GET with Query Params)",
"type": "main",
"index": 0
},
{
"node": "3. The Customer (POST with Body)",
"type": "main",
"index": 0
},
{
"node": "4. The Customer (GET with Headers/Auth)",
"type": "main",
"index": 0
},
{
"node": "5. The Customer (Request with Timeout)",
"type": "main",
"index": 0
}
]] },
"IF Authorized": { "main": [[{
"node": "Respond with Secret",
"type": "main",
"index": 0
}], [{
"node": "Respond: Unauthorized (401)",
"type": "main",
"index": 0
}]] },
"Start Tutorial": { "main": [[{
"node": "⚙️CONFIGURATION⚙️",
"type": "main",
"index": 0
}]] },
"Wait 3 seconds": { "main": [[{
"node": "Prepare Slow Response",
"type": "main",
"index": 0
}]] },
"IF extra cheese": { "main": [[{
"node": "Prepare Cheese Pizza",
"type": "main",
"index": 0
}], [{
"node": "Prepare Plain Pizza",
"type": "main",
"index": 0
}]] },
"⚙️CONFIGURATION⚙️": { "main": [[{
"node": "Base URL",
"type": "main",
"index": 0
}]] },
"1. The Kitchen (GET /menu)": { "main": [[{
"node": "Prepare Menu Data",
"type": "main",
"index": 0
}]] },
"2. The Kitchen (GET /order)": { "main": [[{
"node": "IF extra cheese",
"type": "main",
"index": 0
}]] },
"3. The Kitchen (POST /review)": { "main": [[{
"node": "Process Review Data",
"type": "main",
"index": 0
}]] },
"4. The Kitchen (GET /secret-dish)": { "main": [[{
"node": "IF Authorized",
"type": "main",
"index": 0
}]] },
"5. The Kitchen (GET /slow-service)": { "main": [[{
"node": "Wait 3 seconds",
"type": "main",
"index": 0
}]] }
}
};
var getWorkflowJson = (json) => {
if (!isWorkflowDataWithTemplateId(json)) throw new ApplicationError("Invalid workflow template JSON structure");
return json;
};
const getEasyAiWorkflowJson = () => {
return getWorkflowJson(easy_ai_starter_default);
};
const getRagStarterWorkflowJson = () => {
return getWorkflowJson(rag_starter_default);
};
const SampleTemplates = {
RagStarterTemplate: getRagStarterWorkflowJson().meta.templateId,
EasyAiTemplate: getEasyAiWorkflowJson().meta.templateId
};
const PrebuiltAgentTemplates = {
CalendarAgent: getWorkflowJson(calendar_agent_default).meta.templateId,
EmailTriageAgent: getWorkflowJson(email_triage_agent_with_gmail_default).meta.templateId,
KnowledgeStoreAgent: getWorkflowJson(knowledge_store_agent_with_google_drive_default).meta.templateId,
TaskManagementAgent: getWorkflowJson(task_management_agent_with_google_sheets_default).meta.templateId,
JokeAgent: getWorkflowJson(joke_agent_with_http_tool_default).meta.templateId,
VoiceAssistantAgent: getWorkflowJson(voice_agent_default).meta.templateId
};
const TutorialTemplates = {
BuildYourFirstAiAgent: getWorkflowJson(build_your_first_ai_agent_default).meta.templateId,
JsonBasics: getWorkflowJson(json_basics_default).meta.templateId,
Expressions: getWorkflowJson(expressions_tutorial_default).meta.templateId,
WorkflowLogic: getWorkflowJson(workflow_logic_default).meta.templateId,
ApiFundamentals: getWorkflowJson(api_fundamentals_default).meta.templateId
};
const isPrebuiltAgentTemplateId = (value) => {
return Object.values(PrebuiltAgentTemplates).includes(value);
};
const isTutorialTemplateId = (value) => {
return Object.values(TutorialTemplates).includes(value);
};
const getPrebuiltAgents = () => {
return [
{
name: "Voice assistant agent",
description: "Personal AI assistant in Telegram, handling both text and voice messages.",
template: getWorkflowJson(voice_agent_default),
nodes: [{
name: "n8n-nodes-base.telegram",
version: 1.2
}]
},
{
name: "Email triage agent",
description: "Categorizes new, unread emails by analyzing their content and applying relevant labels.",
template: getWorkflowJson(email_triage_agent_with_gmail_default),
nodes: [{
name: "n8n-nodes-base.gmail",
version: 2.1
}]
},
{
name: "Knowledge store agent",
description: "Retrieve, analyze, and answer questions using documents uploaded to Google Drive.",
template: getWorkflowJson(knowledge_store_agent_with_google_drive_default),
nodes: [{
name: "n8n-nodes-base.googleDrive",
version: 3
}]
},
{
name: "Calendar agent",
description: "Agent that can interact with your Google calendar to get availability and a list of events.",
template: getWorkflowJson(calendar_agent_default),
nodes: [{
name: "n8n-nodes-base.googleCalendar",
version: 1.3
}]
},
{
name: "Task management agent",
description: "Task management assistant that helps users create, view, update, and delete tasks.",
template: getWorkflowJson(task_management_agent_with_google_sheets_default),
nodes: [{
name: "n8n-nodes-base.googleSheets",
version: 4.7
}]
},
{
name: "Joke agent",
description: "Uses the Joke API via the HTTP tool to deliver fun, personalized jokes.",
template: getWorkflowJson(joke_agent_with_http_tool_default),
nodes: [{
name: "n8n-nodes-base.httpRequest",
version: 4.2
}]
}
];
};
const getTutorialTemplates = () => {
return [
{
name: "Build your first AI agent",
description: "This template launches your very first AI Agent —an AI-powered chatbot that can do more than just talk— it can take action using tools.",
template: getWorkflowJson(build_your_first_ai_agent_default),
nodes: []
},
{
name: "JSON basics",
description: "Designed to teach you the absolute basics of JSON (JavaScript Object Notation) and, more importantly, how to use it within n8n.",
template: getWorkflowJson(json_basics_default),
nodes: []
},
{
name: "Expressions",
description: "Step-by-step tutorial designed to teach you the most important skill in n8n: using expressions to access and manipulate data.",
template: getWorkflowJson(expressions_tutorial_default),
nodes: []
},
{
name: "Workflow logic",
description: "This template is a hands-on tutorial that teaches you the three most fundamental nodes for controlling the flow of your automations: Merge, IF, and Switch.",
template: getWorkflowJson(workflow_logic_default),
nodes: []
},
{
name: "API fundamentals",
description: "Hands-on tutorial designed to demystify what an API is and how it works, right inside your n8n canvas.",
template: getWorkflowJson(api_fundamentals_default),
nodes: []
}
];
};
const getSampleWorkflowByTemplateId = (templateId) => {
return [
getEasyAiWorkflowJson(),
getRagStarterWorkflowJson(),
...getPrebuiltAgents().map((agent) => agent.template),
...getTutorialTemplates().map((tutorial) => tutorial.template)
].find((workflow) => workflow.meta.templateId === templateId);
};
var import_sortBy = /* @__PURE__ */ __toESM(require_sortBy());
var COMMUNITY_NODE_TYPE_PREVIEW_TOKEN = "-preview";
function transformNodeType(node, subcategory, type = "node") {
const createElement = {
uuid: v4_default(),
key: node.name,
subcategory: subcategory ?? node.codex?.subcategories?.["Core Nodes"]?.[0] ?? "*",
properties: { ...node },
type
};
return type === "action" ? createElement : createElement;
}
function subcategorizeItems(items) {
const WHITE_LISTED_SUBCATEGORIES = [
CORE_NODES_CATEGORY,
"AI",
HUMAN_IN_THE_LOOP_CATEGORY
];
return items.reduce((acc, item) => {
let subcategories = ["*"];
const matchedSubcategories = WHITE_LISTED_SUBCATEGORIES.flatMap((category) => {
if (item.codex?.categories?.includes(category)) return item.codex?.subcategories?.[category] ?? [];
return [];
});
if (matchedSubcategories.length > 0) subcategories = matchedSubcategories;
subcategories.forEach((subcategory) => {
if (!acc[subcategory]) acc[subcategory] = [];
acc[subcategory].push(transformNodeType(item, subcategory));
});
return acc;
}, {});
}
function sortNodeCreateElements(nodes$13) {
return nodes$13.sort((a, b) => {
if (a.type !== "node" || b.type !== "node") return 0;
const displayNameA = a.properties?.displayName?.toLowerCase() || a.key;
const displayNameB = b.properties?.displayName?.toLowerCase() || b.key;
return displayNameA.localeCompare(displayNameB, void 0, { sensitivity: "base" });
});
}
function removeTrailingTrigger(searchFilter) {
const parts = searchFilter.split(" ");
if (parts.length > 1 && "trigger".startsWith(parts.slice(-1)[0].toLowerCase())) return parts.slice(0, -1).filter((x) => x).join(" ").trimEnd();
return searchFilter;
}
function searchNodes(searchFilter, items, additionalFactors = {}) {
if (!useSettingsStore().isAskAiEnabled) items = items.filter((item) => item.key !== AI_TRANSFORM_NODE_TYPE);
return reRankSearchResults(sublimeSearch(removeTrailingTrigger(searchFilter).toLowerCase(), items) || [], additionalFactors).map(({ item }) => item);
}
function flattenCreateElements(items) {
return items.map((item) => item.type === "section" ? item.children : item).flat();
}
function isAINode(node) {
if (!(node.type === "node")) return false;
if (node.properties.codex?.categories?.includes("AI")) return !node.properties.codex?.subcategories?.["AI"]?.includes(AI_CATEGORY_AGENTS);
return false;
}
function groupItemsInSections(items, sections, sortAlphabetically = true) {
const filteredSections = sections.filter((section) => typeof section === "object");
const itemsBySection = (items2) => items2.reduce((acc, item) => {
const key = filteredSections.find((s) => s.items.includes(item.key))?.key ?? "other";
if (key) acc[key] = [...acc[key] ?? [], item];
return acc;
}, {});
const mapNewSections = (newSections, children$1) => newSections.map((section) => ({
type: "section",
key: section.key,
title: section.title,
children: sortAlphabetically ? sortNodeCreateElements(children$1[section.key] ?? []) : children$1[section.key] ?? []
}));
const nonAINodes = items.filter((item) => !isAINode(item));
const AINodes = items.filter((item) => isAINode(item));
const nonAINodesBySection = itemsBySection(nonAINodes);
const nonAINodesSections = mapNewSections(filteredSections, nonAINodesBySection);
const AINodesBySection = itemsBySection(AINodes);
const AINodesSections = mapNewSections((0, import_sortBy.default)(filteredSections, ["title"]), AINodesBySection);
const result = [...nonAINodesSections, ...AINodesSections].concat({
type: "section",
key: "other",
title: i18n.baseText("nodeCreator.sectionNames.other"),
children: sortNodeCreateElements(nonAINodesBySection.other ?? [])
}).filter((section) => section.type !== "section" || section.children.length > 0);
result.sort((a, b) => {
if (a.key.toLowerCase().includes("recommended")) return -1;
if (b.key.toLowerCase().includes("recommended")) return 1;
if (b.key === "Other Tools") return -1;
return 0;
});
if (!shouldRenderSectionSubtitle(result)) return items;
return result;
}
var shouldRenderSectionSubtitle = (sections) => {
if (!sections.length) return false;
if (sections.length > 1) return true;
if (sections[0].key === "sendAndWait") return true;
return false;
};
const formatTriggerActionName = (actionPropertyName) => {
let name$13 = actionPropertyName;
if (actionPropertyName.includes(".")) name$13 = actionPropertyName.split(".").join(" ");
return noCase(name$13);
};
const removePreviewToken = (key) => key.replace(COMMUNITY_NODE_TYPE_PREVIEW_TOKEN, "");
const isNodePreviewKey = (key = "") => key.includes(COMMUNITY_NODE_TYPE_PREVIEW_TOKEN);
function applyNodeTags(element) {
if (element.type !== "node" || element.properties.tag) return element;
if (RECOMMENDED_NODES.includes(element.properties.name)) element.properties.tag = {
type: "info",
text: i18n.baseText("generic.recommended")
};
else if (BETA_NODES.includes(element.properties.name)) element.properties.tag = {
type: "info",
text: i18n.baseText("generic.betaProper")
};
return element;
}
function finalizeItems(items) {
return items.map((item) => ({
...item,
uuid: `${item.key}-${v4_default()}`
})).map(applyNodeTags);
}
const filterAndSearchNodes = (mergedNodes, search, isAgentSubcategory) => {
if (!search || isAgentSubcategory) return [];
const vettedNodes = mergedNodes.map((item) => transformNodeType(item));
return finalizeItems(searchNodes(search || "", vettedNodes));
};
function prepareCommunityNodeDetailsViewStack(item, nodeIcon, rootView, nodeActions = []) {
const installed = !isNodePreviewKey(item.key);
const packageName = removePreviewToken(item.key.split(".")[0]);
const nodeType = useNodeTypesStore().communityNodeType(removePreviewToken(item.key));
const communityNodeDetails = {
title: item.properties.displayName,
description: item.properties.description,
key: item.key,
nodeIcon,
installed,
official: nodeType?.isOfficialNode ?? false,
packageName,
companyName: nodeType?.companyName
};
if (nodeActions.length) {
const transformedActions = nodeActions?.map((a) => transformNodeType(a, item.properties.displayName, "action"));
return {
subcategory: item.properties.displayName,
title: i18n.baseText("nodeSettings.communityNodeDetails.title"),
rootView,
hasSearch: false,
mode: "actions",
items: transformedActions,
communityNodeDetails
};
}
return {
subcategory: item.properties.displayName,
title: i18n.baseText("nodeSettings.communityNodeDetails.title"),
rootView,
hasSearch: false,
items: [item],
mode: "community-node",
communityNodeDetails
};
}
function getRagStarterCallout() {
return {
uuid: SampleTemplates.RagStarterTemplate,
key: SampleTemplates.RagStarterTemplate,
type: "openTemplate",
properties: {
templateId: SampleTemplates.RagStarterTemplate,
title: i18n.baseText("nodeCreator.ragStarterTemplate.openTemplateItem.title"),
icon: "database",
description: i18n.baseText("nodeCreator.ragStarterTemplate.openTemplateItem.description"),
tag: {
type: "info",
text: i18n.baseText("generic.recommended")
}
}
};
}
function getPreBuiltAgentsCallout() {
return {
uuid: v4_default(),
key: PRE_BUILT_AGENTS_COLLECTION,
type: "view",
properties: {
title: i18n.baseText("nodeCreator.preBuiltAgents.title"),
icon: "box",
description: i18n.baseText("nodeCreator.preBuiltAgents.description"),
borderless: true,
tag: {
type: "info",
text: i18n.baseText("generic.recommended")
}
}
};
}
function getPreBuiltAgentsCalloutWithDivider() {
return {
uuid: v4_default(),
key: PRE_BUILT_AGENTS_COLLECTION,
type: "link",
properties: {
key: PRE_BUILT_AGENTS_COLLECTION,
url: "",
title: i18n.baseText("nodeCreator.preBuiltAgents.title"),
icon: "box",
description: i18n.baseText("nodeCreator.preBuiltAgents.description"),
tag: {
type: "info",
text: i18n.baseText("generic.recommended")
}
}
};
}
function getAiTemplatesCallout(aiTemplatesURL) {
return {
uuid: "ai_templates_root",
key: "ai_templates_root",
type: "link",
properties: {
title: i18n.baseText("nodeCreator.aiPanel.linkItem.title"),
icon: "box-open",
description: i18n.baseText("nodeCreator.aiPanel.linkItem.description"),
key: "ai_templates_root",
url: aiTemplatesURL,
tag: {
type: "info",
text: i18n.baseText("generic.recommended")
}
}
};
}
function getRootSearchCallouts(search, { isRagStarterCalloutVisible = false } = {}) {
const results = [];
if (isRagStarterCalloutVisible && [
"rag",
"vec",
"know"
].some((x) => search.toLowerCase().startsWith(x))) results.push(getRagStarterCallout());
return results;
}
var getTemplateLink = (templateId, availableTemplates) => {
const templateLink = availableTemplates.find((template) => template.key === templateId);
if (templateLink?.properties) templateLink.properties.compact = true;
return templateLink;
};
function getActiveViewCallouts(title, isPreBuiltAgentsCalloutVisible, templates) {
const results = [];
if (isPreBuiltAgentsCalloutVisible && title) {
if (title === "Language Models") results.push(getPreBuiltAgentsCalloutWithDivider());
else if (["Memory", "Tools"].includes(title)) results.push(getPreBuiltAgentsCallout());
else if (title === "Google Calendar") {
const templateLink = getTemplateLink(PrebuiltAgentTemplates.CalendarAgent, templates);
if (templateLink) results.push(templateLink);
} else if (title === "Telegram") {
const templateLink = getTemplateLink(PrebuiltAgentTemplates.VoiceAssistantAgent, templates);
if (templateLink) results.push(templateLink);
} else if (title === "Google Drive") {
const templateLink = getTemplateLink(PrebuiltAgentTemplates.KnowledgeStoreAgent, templates);
if (templateLink) results.push(templateLink);
} else if (title === "Google Sheets") {
const templateLink = getTemplateLink(PrebuiltAgentTemplates.TaskManagementAgent, templates);
if (templateLink) results.push(templateLink);
} else if (title === "Gmail") {
const templateLink = getTemplateLink(PrebuiltAgentTemplates.EmailTriageAgent, templates);
if (templateLink) results.push(templateLink);
}
}
return results;
}
const shouldShowCommunityNodeDetails = (communityNode, viewStack) => {
if (viewStack.rootView === "AI Other" && viewStack.title === "Tools") return false;
return communityNode && !viewStack.communityNodeDetails;
};
function getHumanInTheLoopActions(nodeActions) {
const actions = nodeActions.filter((action) => action.actionKey === SEND_AND_WAIT_OPERATION);
if (actions.length) {
const name$13 = actions[0].name;
if (name$13 === "n8n-nodes-base.discord") actions[0].values = {
...actions[0].values,
resource: "message",
operation: SEND_AND_WAIT_OPERATION
};
if (name$13 === "n8n-nodes-base.microsoftTeams") actions[0].values = {
...actions[0].values,
resource: "chatMessage",
operation: SEND_AND_WAIT_OPERATION
};
}
return actions;
}
var getTestRunsEndpoint = (workflowId, runId) => `/workflows/${workflowId}/test-runs${runId ? `/${runId}` : ""}`;
const getTestRuns = async (context, workflowId) => {
return await makeRestApiRequest(context, "GET", getTestRunsEndpoint(workflowId));
};
const getTestRun = async (context, params) => {
return await makeRestApiRequest(context, "GET", getTestRunsEndpoint(params.workflowId, params.runId));
};
const startTestRun = async (context, workflowId) => {
return await request({
method: "POST",
baseURL: context.baseUrl,
endpoint: `/workflows/${workflowId}/test-runs/new`,
headers: { "push-ref": context.pushRef }
});
};
const cancelTestRun = async (context, workflowId, testRunId) => {
return await request({
method: "POST",
baseURL: context.baseUrl,
endpoint: `/workflows/${workflowId}/test-runs/${testRunId}/cancel`,
headers: { "push-ref": context.pushRef }
});
};
const deleteTestRun = async (context, params) => {
return await makeRestApiRequest(context, "DELETE", getTestRunsEndpoint(params.workflowId, params.runId));
};
var getRunExecutionsEndpoint = (workflowId, runId) => `/workflows/${workflowId}/test-runs/${runId}/test-cases`;
const getTestCaseExecutions = async (context, workflowId, runId) => {
return await makeRestApiRequest(context, "GET", getRunExecutionsEndpoint(workflowId, runId));
};
const useEvaluationStore = defineStore(STORES.EVALUATION, () => {
const loadingTestRuns = ref(false);
const testRunsById = ref({});
const testCaseExecutionsById = ref({});
const pollingTimeouts = ref({});
const rootStore = useRootStore();
const workflowsStore = useWorkflowsStore();
const nodeTypesStore = useNodeTypesStore();
const settingsStore = useSettingsStore();
const isEvaluationEnabled = computed(() => settingsStore.settings.evaluation?.quota !== 0);
const isLoading = computed(() => loadingTestRuns.value);
const testRunsByWorkflowId = computed(() => {
return Object.values(testRunsById.value).reduce((acc, run) => {
if (!acc[run.workflowId]) acc[run.workflowId] = [];
acc[run.workflowId].push(run);
return acc;
}, {});
});
const evaluationTriggerExists = computed(() => {
return workflowsStore.workflow.nodes.some((node) => node.type === EVALUATION_TRIGGER_NODE_TYPE);
});
function evaluationNodeExist(operation) {
return workflowsStore.workflow.nodes.some((node) => {
if (node.type !== "n8n-nodes-base.evaluation") return false;
const nodeType = nodeTypesStore.getNodeType(node.type, node.typeVersion);
if (!nodeType) return false;
return getNodeParameters(nodeType.properties, node.parameters, true, false, node, nodeType)?.operation === operation;
});
}
const evaluationSetMetricsNodeExist = computed(() => {
return evaluationNodeExist("setMetrics");
});
const evaluationSetOutputsNodeExist = computed(() => {
return evaluationNodeExist("setOutputs");
});
const fetchTestCaseExecutions = async (params) => {
const testCaseExecutions = await getTestCaseExecutions(rootStore.restApiContext, params.workflowId, params.runId);
testCaseExecutions.forEach((testCaseExecution) => {
testCaseExecutionsById.value[testCaseExecution.id] = testCaseExecution;
});
return testCaseExecutions;
};
const fetchTestRuns = async (workflowId) => {
loadingTestRuns.value = true;
try {
const runs = await getTestRuns(rootStore.restApiContext, workflowId);
runs.forEach((run) => {
testRunsById.value[run.id] = run;
if (["running", "new"].includes(run.status)) startPollingTestRun(workflowId, run.id);
});
return runs;
} finally {
loadingTestRuns.value = false;
}
};
const getTestRun$1 = async (params) => {
const run = await getTestRun(rootStore.restApiContext, params);
testRunsById.value[run.id] = run;
return run;
};
const startTestRun$1 = async (workflowId) => {
return await startTestRun(rootStore.restApiContext, workflowId);
};
const cancelTestRun$1 = async (workflowId, testRunId) => {
return await cancelTestRun(rootStore.restApiContext, workflowId, testRunId);
};
const deleteTestRun$1 = async (params) => {
const result = await deleteTestRun(rootStore.restApiContext, params);
if (result.success) {
const { [params.runId]: deleted,...rest } = testRunsById.value;
testRunsById.value = rest;
}
return result;
};
const startPollingTestRun = (workflowId, runId) => {
const poll = async () => {
try {
const run = await getTestRun$1({
workflowId,
runId
});
if (["running", "new"].includes(run.status)) pollingTimeouts.value[runId] = setTimeout(poll, 1e3);
else delete pollingTimeouts.value[runId];
} catch (error) {
pollingTimeouts.value[runId] = setTimeout(poll, 1e3);
}
};
poll();
};
const cleanupPolling = () => {
Object.values(pollingTimeouts.value).forEach((timeout$2) => {
clearTimeout(timeout$2);
});
pollingTimeouts.value = {};
};
return {
testRunsById,
testCaseExecutionsById,
isLoading,
isEvaluationEnabled,
testRunsByWorkflowId,
evaluationTriggerExists,
evaluationSetMetricsNodeExist,
evaluationSetOutputsNodeExist,
fetchTestCaseExecutions,
fetchTestRuns,
getTestRun: getTestRun$1,
startTestRun: startTestRun$1,
cancelTestRun: cancelTestRun$1,
deleteTestRun: deleteTestRun$1,
cleanupPolling
};
}, {});
var import_memoize = /* @__PURE__ */ __toESM(require_memoize());
var import_startCase = /* @__PURE__ */ __toESM(require_startCase());
var PLACEHOLDER_RECOMMENDED_ACTION_KEY = "placeholder_recommended";
function translate(...args) {
return i18n.baseText(...args);
}
var cachedBaseText = (0, import_memoize.default)(translate, (...args) => JSON.stringify(args));
var customNodeActionsParsers = {
["n8n-nodes-base.hubspotTrigger"]: (matchedProperty, nodeTypeDescription) => {
return ((matchedProperty?.options?.[0])?.values[0]?.options)?.map((categoryItem) => ({
...getNodeTypeBase(nodeTypeDescription),
actionKey: categoryItem.value,
displayName: cachedBaseText("nodeCreator.actionsCategory.onEvent", { interpolate: { event: (0, import_startCase.default)(categoryItem.name) } }),
description: categoryItem.description ?? "",
displayOptions: matchedProperty.displayOptions,
values: { eventsUi: { eventValues: [{ name: categoryItem.value }] } }
}));
},
["n8n-nodes-base.code"]: (matchedProperty, nodeTypeDescription) => {
if (matchedProperty.name !== "language") return;
const languageOptions = matchedProperty.options;
if (!languageOptions) return;
return languageOptions.map((option) => ({
...getNodeTypeBase(nodeTypeDescription),
actionKey: `language_${option.value}`,
displayName: `Code in ${option.name}`,
description: `Run custom ${option.name} code`,
displayOptions: matchedProperty.displayOptions,
values: { language: option.value }
}));
}
};
function getNodeTypeBase(nodeTypeDescription, label) {
const category = nodeTypeDescription.group.includes("trigger") ? cachedBaseText("nodeCreator.actionsCategory.triggers") : cachedBaseText("nodeCreator.actionsCategory.actions");
return {
name: nodeTypeDescription.name,
group: nodeTypeDescription.group,
codex: {
label: label ?? "",
categories: [category]
},
iconUrl: nodeTypeDescription.iconUrl,
iconColor: nodeTypeDescription.iconColor,
outputs: nodeTypeDescription.outputs,
icon: nodeTypeDescription.icon,
defaults: nodeTypeDescription.defaults
};
}
function operationsCategory(nodeTypeDescription) {
if (nodeTypeDescription.properties.find((property) => property.name === "resource")) return [];
if (nodeTypeDescription.name === "n8n-nodes-base.code") {
const languageProperty = nodeTypeDescription.properties.find((property) => property.name === "language" && property.displayOptions?.show?.["@version"]?.[0] === 2);
if (languageProperty) {
const customParsedItems = customNodeActionsParsers[nodeTypeDescription.name]?.(languageProperty, nodeTypeDescription);
if (customParsedItems) return useSettingsStore().isNativePythonRunnerEnabled ? customParsedItems.filter((item) => item.actionKey !== "language_python") : customParsedItems.filter((item) => item.actionKey !== "language_pythonNative");
}
}
const matchedProperty = nodeTypeDescription.properties.find((property) => property.name?.toLowerCase() === "operation");
if (!matchedProperty?.options) return [];
const items = matchedProperty.options.filter((categoryItem) => ![
"*",
"",
" "
].includes(categoryItem.name)).map((item) => ({
...getNodeTypeBase(nodeTypeDescription),
actionKey: item.value,
displayName: item.action ?? (0, import_startCase.default)(item.name),
description: item.description ?? "",
displayOptions: matchedProperty.displayOptions,
outputConnectionType: item.outputConnectionType,
values: { [matchedProperty.name]: matchedProperty.type === "multiOptions" ? [item.value] : item.value }
}));
if (items.length === 0) return [];
return items;
}
function modeCategory(nodeTypeDescription) {
if (!nodeTypeDescription.codex?.categories?.includes("AI")) return [];
const matchedProperty = nodeTypeDescription.properties.find((property) => property.name?.toLowerCase() === "mode");
if (!matchedProperty?.options) return [];
const items = matchedProperty.options.map((item) => ({
...getNodeTypeBase(nodeTypeDescription),
actionKey: item.value,
displayName: item.action ?? (0, import_startCase.default)(item.name),
description: item.description ?? "",
displayOptions: matchedProperty.displayOptions,
outputConnectionType: item.outputConnectionType,
values: { [matchedProperty.name]: item.value }
}));
if (items.length === 0) return [];
return items;
}
function triggersCategory(nodeTypeDescription) {
const matchingKeys = [
"event",
"events",
"trigger on"
];
const isTrigger = nodeTypeDescription.displayName?.toLowerCase().includes("trigger");
const matchedProperty = nodeTypeDescription.properties.find((property) => matchingKeys.includes(property.displayName?.toLowerCase()));
if (!isTrigger) return [];
if (!matchedProperty?.options) return [{
...getNodeTypeBase(nodeTypeDescription),
actionKey: PLACEHOLDER_RECOMMENDED_ACTION_KEY,
displayName: cachedBaseText("nodeCreator.actionsCategory.onNewEvent", { interpolate: { event: nodeTypeDescription.displayName.replace("Trigger", "").trimEnd() } }),
description: ""
}];
const filteredOutItems = matchedProperty.options.filter((categoryItem) => ![
"*",
"",
" "
].includes(categoryItem.name));
return customNodeActionsParsers[nodeTypeDescription.name]?.(matchedProperty, nodeTypeDescription) ?? filteredOutItems.map((categoryItem) => ({
...getNodeTypeBase(nodeTypeDescription),
actionKey: categoryItem.value,
displayName: categoryItem.action ?? cachedBaseText("nodeCreator.actionsCategory.onEvent", { interpolate: { event: formatTriggerActionName(categoryItem.name) } }),
description: categoryItem.description ?? "",
displayOptions: matchedProperty.displayOptions,
values: { [matchedProperty.name]: matchedProperty.type === "multiOptions" ? [categoryItem.value] : categoryItem.value }
}));
}
function resourceCategories(nodeTypeDescription) {
const transformedNodes = [];
nodeTypeDescription.properties.filter((property) => property.name === "resource").forEach((property) => {
(property.options || []).filter((option) => option.value !== CUSTOM_API_CALL_KEY).forEach((resourceOption, _i, options) => {
const isSingleResource = options.length === 1;
const operations = nodeTypeDescription.properties.find((operation) => {
const isOperation = operation.name === "operation";
const isMatchingResource = operation.displayOptions?.show?.resource?.includes(resourceOption.value) ?? isSingleResource;
const operationVersions = operation.displayOptions?.show?.["@version"];
const nodeTypeVersions = Array.isArray(nodeTypeDescription.version) ? nodeTypeDescription.version : [nodeTypeDescription.version];
const isMatchingVersion = operationVersions ? operationVersions.some((version) => typeof version === "number" && nodeTypeVersions.includes(version)) : true;
return isOperation && isMatchingResource && isMatchingVersion;
});
if (!operations?.options) return;
const items = (operations.options || []).map((operationOption) => {
const displayName = operationOption.action ?? `${resourceOption.name} ${(0, import_startCase.default)(operationOption.name)}`;
const displayOptions = isSingleResource ? { show: { resource: [options[0]?.value] } } : operations?.displayOptions;
return {
...getNodeTypeBase(nodeTypeDescription, `${resourceOption.name} ${cachedBaseText("nodeCreator.actionsCategory.actions")}`),
actionKey: operationOption.value,
description: operationOption?.description ?? "",
displayOptions,
values: { operation: operations?.type === "multiOptions" ? [operationOption.value] : operationOption.value },
displayName,
group: ["trigger"]
};
});
transformedNodes.push(...items);
});
});
return transformedNodes;
}
function useActionsGenerator() {
function generateNodeActions(node) {
if (!node) return [];
if (node.codex?.subcategories?.AI?.includes("Tools") && !node.codex?.subcategories?.AI?.includes("Root Nodes")) return [];
return [
...triggersCategory(node),
...operationsCategory(node),
...resourceCategories(node),
...modeCategory(node)
];
}
function filterActions(actions) {
if (actions.length <= 1) return [];
return actions.filter((action, _, arr) => {
if (action.actionKey === "__CUSTOM_API_CALL__") return false;
const isPlaceholderTriggerAction = action.actionKey === PLACEHOLDER_RECOMMENDED_ACTION_KEY;
return !isPlaceholderTriggerAction || isPlaceholderTriggerAction && arr.length > 1;
});
}
function getSimplifiedNodeType(node) {
const { displayName, defaults: defaults$1, description, name: name$13, group, icon, iconUrl, iconColor, badgeIconUrl, outputs, codex } = node;
return {
displayName,
defaults: defaults$1,
description,
name: name$13,
group,
icon,
iconColor,
iconUrl,
badgeIconUrl,
outputs,
codex
};
}
function generateMergedNodesAndActions(nodeTypes, httpOnlyCredentials) {
const evaluationStore = useEvaluationStore();
const visibleNodeTypes = nodeTypes.filter((node) => {
if (evaluationStore.isEvaluationEnabled) return true;
return node.name !== "n8n-nodes-base.evaluation" && node.name !== "n8n-nodes-base.evaluationTrigger";
});
const actions = {};
const mergedNodes = [];
visibleNodeTypes.filter((node) => !node.group.includes("trigger")).forEach((app) => {
const appActions = generateNodeActions(app);
actions[app.name] = appActions;
if (app.name === "n8n-nodes-base.httpRequest") {
const filteredNodes = httpOnlyCredentials.map((credentialType) => {
const credsOnlyNode = getCredentialOnlyNodeType(app, credentialType);
if (credsOnlyNode) return getSimplifiedNodeType(credsOnlyNode);
return null;
}).filter((node) => node !== null);
mergedNodes.push(...filteredNodes);
}
mergedNodes.push(getSimplifiedNodeType(app));
});
visibleNodeTypes.filter((node) => node.group.includes("trigger")).forEach((trigger) => {
const normalizedName = trigger.name.replace("Trigger", "");
const triggerActions = generateNodeActions(trigger);
const appActions = actions?.[normalizedName] || [];
const app = mergedNodes.find((node) => node.name === normalizedName);
if (app && appActions?.length > 0) {
actions[normalizedName] = filterActions([...appActions, ...triggerActions]);
app.description = trigger.description;
} else {
actions[trigger.name] = filterActions(triggerActions);
mergedNodes.push(getSimplifiedNodeType(trigger));
}
});
return {
actions,
mergedNodes
};
}
return { generateMergedNodesAndActions };
}
const useNodeTypesStore = defineStore(STORES.NODE_TYPES, () => {
const nodeTypes = ref({});
const vettedCommunityNodeTypes = ref(/* @__PURE__ */ new Map());
const rootStore = useRootStore();
const actionsGenerator = useActionsGenerator();
const settingsStore = useSettingsStore();
const communityNodeType = computed(() => {
return (nodeTypeName) => {
return vettedCommunityNodeTypes.value.get(nodeTypeName);
};
});
const officialCommunityNodeTypes = computed(() => Array.from(vettedCommunityNodeTypes.value.values()).filter(({ isOfficialNode, isInstalled }) => isOfficialNode && !isInstalled).map(({ nodeDescription }) => nodeDescription));
const unofficialCommunityNodeTypes = computed(() => Array.from(vettedCommunityNodeTypes.value.values()).filter(({ isOfficialNode, isInstalled }) => !isOfficialNode && !isInstalled).map(({ nodeDescription }) => nodeDescription));
const communityNodesAndActions = computed(() => {
return actionsGenerator.generateMergedNodesAndActions(unofficialCommunityNodeTypes.value, []);
});
const allNodeTypes = computed(() => {
return Object.values(nodeTypes.value).flatMap((nodeType) => Object.keys(nodeType).map((version) => nodeType[Number(version)]));
});
const allLatestNodeTypes = computed(() => {
return Object.values(nodeTypes.value).map((nodeVersions) => {
const versionNumbers = Object.keys(nodeVersions).map(Number);
return nodeVersions[Math.max(...versionNumbers)];
}).filter(Boolean);
});
const moduleEnabledNodeTypes = computed(() => {
return MODULE_ENABLED_NODES.flatMap((node) => {
const nodeVersions = nodeTypes.value[node.nodeType] ?? {};
const versionNumbers = Object.keys(nodeVersions).map(Number);
const latest = nodeVersions[Math.max(...versionNumbers)];
if (latest?.hidden && settingsStore.isModuleActive(node.module)) return {
...latest,
hidden: void 0
};
return [];
});
});
const getNodeType = computed(() => {
return (nodeTypeName, version) => {
if (isCredentialOnlyNodeType(nodeTypeName)) return getCredentialOnlyNodeType$1.value(nodeTypeName, version);
const nodeVersions = nodeTypes.value[nodeTypeName];
if (!nodeVersions) return null;
const versionNumbers = Object.keys(nodeVersions).map(Number);
return nodeVersions[version ?? Math.max(...versionNumbers)] ?? null;
};
});
const getNodeVersions$1 = computed(() => {
return (nodeTypeName) => {
return Object.keys(nodeTypes.value[nodeTypeName] ?? {}).map(Number);
};
});
const getCredentialOnlyNodeType$1 = computed(() => {
return (nodeTypeName, version) => {
const credentialName = getCredentialTypeName(nodeTypeName);
return getCredentialOnlyNodeType(getNodeType.value(HTTP_REQUEST_NODE_TYPE, version ?? 4.1), useCredentialsStore().getCredentialTypeByName(credentialName)) ?? null;
};
});
const isConfigNode = computed(() => {
return (workflow, node, nodeTypeName) => {
if (!workflow.nodes[node.name]) return false;
const nodeType = getNodeType.value(nodeTypeName);
if (!nodeType) return false;
const outputTypes = getConnectionTypes(getNodeOutputs(workflow, node, nodeType));
return outputTypes ? outputTypes.filter((output) => output !== NodeConnectionTypes.Main).length > 0 : false;
};
});
const isTriggerNode = computed(() => {
return (nodeTypeName) => {
const nodeType = getNodeType.value(nodeTypeName);
return !!(nodeType && nodeType.group.includes("trigger"));
};
});
const isToolNode = computed(() => {
return (nodeTypeName) => {
const nodeType = getNodeType.value(nodeTypeName);
if (nodeType?.outputs && Array.isArray(nodeType.outputs)) return nodeType.outputs.map((output) => typeof output === "string" ? output : output.type).includes(NodeConnectionTypes.AiTool);
else return nodeType?.outputs.includes(NodeConnectionTypes.AiTool) ?? false;
};
});
const isCoreNodeType = computed(() => {
return (nodeType) => {
return nodeType.codex?.categories?.includes("Core Nodes");
};
});
const visibleNodeTypes = computed(() => {
return allLatestNodeTypes.value.concat(officialCommunityNodeTypes.value).concat(moduleEnabledNodeTypes.value).filter((nodeType) => !nodeType.hidden);
});
const nativelyNumberSuffixedDefaults = computed(() => {
return allNodeTypes.value.reduce((acc, cur) => {
if (/\d$/.test(cur.defaults.name)) acc.push(cur.defaults.name);
return acc;
}, []);
});
const visibleNodeTypesByOutputConnectionTypeNames = computed(() => {
return visibleNodeTypes.value.reduce((acc, node) => {
const outputTypes = node.outputs;
if (Array.isArray(outputTypes)) outputTypes.forEach((value) => {
const outputType = typeof value === "string" ? value : value.type;
if (!acc[outputType]) acc[outputType] = [];
acc[outputType].push(node.name);
});
else [
NodeConnectionTypes.AiVectorStore,
NodeConnectionTypes.AiChain,
NodeConnectionTypes.AiDocument,
NodeConnectionTypes.AiEmbedding,
NodeConnectionTypes.AiLanguageModel,
NodeConnectionTypes.AiMemory,
NodeConnectionTypes.AiOutputParser,
NodeConnectionTypes.AiTextSplitter,
NodeConnectionTypes.AiTool
].forEach((outputType) => {
if (outputTypes.includes(outputType)) {
acc[outputType] = acc[outputType] || [];
acc[outputType].push(node.name);
}
});
return acc;
}, {});
});
const visibleNodeTypesByInputConnectionTypeNames = computed(() => {
return visibleNodeTypes.value.reduce((acc, node) => {
const inputTypes = node.inputs;
if (Array.isArray(inputTypes)) inputTypes.forEach((value) => {
const outputType = typeof value === "string" ? value : value.type;
if (!acc[outputType]) acc[outputType] = [];
acc[outputType].push(node.name);
});
return acc;
}, {});
});
const isConfigurableNode = computed(() => {
return (workflow, node, nodeTypeName, nodeTypeVersion) => {
const nodeType = getNodeType.value(nodeTypeName, nodeTypeVersion);
if (nodeType === null) return false;
const inputTypes = getConnectionTypes(getNodeInputs(workflow, node, nodeType));
return inputTypes ? inputTypes.filter((input) => input !== NodeConnectionTypes.Main).length > 0 : false;
};
});
const setNodeTypes = (newNodeTypes = []) => {
const groupedNodeTypes = groupNodeTypesByNameAndType(newNodeTypes);
nodeTypes.value = {
...nodeTypes.value,
...groupedNodeTypes
};
};
const removeNodeTypes = (nodeTypesToRemove) => {
nodeTypes.value = nodeTypesToRemove.reduce((oldNodes, newNodeType) => omit(newNodeType.name, oldNodes), nodeTypes.value);
};
const getNodesInformation$1 = async (nodeInfos, replace = true) => {
const nodesInformation = await getNodesInformation(rootStore.restApiContext, nodeInfos);
nodesInformation.forEach((nodeInformation) => {
if (nodeInformation.translation) addNodeTranslation({ [nodeInformation.name.replace("n8n-nodes-base.", "")]: nodeInformation.translation }, rootStore.defaultLocale);
});
if (replace) setNodeTypes(nodesInformation);
return nodesInformation;
};
const getFullNodesProperties = async (nodesToBeFetched, replaceNodeTypes = true) => {
await useCredentialsStore().fetchCredentialTypes(true);
if (replaceNodeTypes) await getNodesInformation$1(nodesToBeFetched);
};
const getNodeTypes$2 = async () => {
const nodeTypes$1 = await getNodeTypes$1(rootStore.baseUrl);
if (nodeTypes$1.length) setNodeTypes(nodeTypes$1);
};
const loadNodeTypesIfNotLoaded = async () => {
if (Object.keys(nodeTypes.value).length === 0) await getNodeTypes$2();
};
const getNodeTranslationHeaders$1 = async () => {
const headers = await getNodeTranslationHeaders(rootStore.restApiContext);
if (headers) addHeaders(headers, rootStore.defaultLocale);
};
const getNodeParameterOptions$1 = async (sendData) => {
return await getNodeParameterOptions(rootStore.restApiContext, sendData);
};
const getResourceLocatorResults$1 = async (sendData) => {
return await getResourceLocatorResults(rootStore.restApiContext, sendData);
};
const getResourceMapperFields$1 = async (sendData) => {
try {
return await getResourceMapperFields(rootStore.restApiContext, sendData);
} catch (error) {
return null;
}
};
const getLocalResourceMapperFields$1 = async (sendData) => {
try {
return await getLocalResourceMapperFields(rootStore.restApiContext, sendData);
} catch (error) {
return null;
}
};
const getNodeParameterActionResult$1 = async (sendData) => {
return await getNodeParameterActionResult(rootStore.restApiContext, sendData);
};
const fetchCommunityNodePreviews = async () => {
if (!settingsStore.isCommunityNodesFeatureEnabled) return;
try {
const communityNodeTypes = await fetchCommunityNodeTypes(rootStore.restApiContext);
vettedCommunityNodeTypes.value = new Map(communityNodeTypes.map((nodeType) => [nodeType.name, nodeType]));
} catch (error) {
vettedCommunityNodeTypes.value = /* @__PURE__ */ new Map();
}
};
const getCommunityNodeAttributes = async (nodeName) => {
if (!settingsStore.isCommunityNodesFeatureEnabled) return null;
try {
return await fetchCommunityNodeAttributes(rootStore.restApiContext, removePreviewToken(nodeName));
} catch (error) {
return null;
}
};
return {
nodeTypes,
allNodeTypes,
allLatestNodeTypes,
getNodeType,
getNodeVersions: getNodeVersions$1,
getCredentialOnlyNodeType: getCredentialOnlyNodeType$1,
isConfigNode,
isTriggerNode,
isToolNode,
isCoreNodeType,
visibleNodeTypes,
nativelyNumberSuffixedDefaults,
visibleNodeTypesByOutputConnectionTypeNames,
visibleNodeTypesByInputConnectionTypeNames,
isConfigurableNode,
communityNodesAndActions,
communityNodeType,
fetchCommunityNodePreviews,
getResourceMapperFields: getResourceMapperFields$1,
getLocalResourceMapperFields: getLocalResourceMapperFields$1,
getNodeParameterActionResult: getNodeParameterActionResult$1,
getResourceLocatorResults: getResourceLocatorResults$1,
getNodeParameterOptions: getNodeParameterOptions$1,
getNodesInformation: getNodesInformation$1,
getFullNodesProperties,
getNodeTypes: getNodeTypes$2,
loadNodeTypesIfNotLoaded,
getNodeTranslationHeaders: getNodeTranslationHeaders$1,
setNodeTypes,
removeNodeTypes,
getCommunityNodeAttributes,
getIsNodeInstalled: computed(() => {
return (nodeTypeName) => {
return !!getNodeType.value(nodeTypeName) || !!communityNodeType.value(nodeTypeName)?.isInstalled;
};
})
};
});
function getDefaultExecutionFilters() {
return {
workflowId: "all",
status: "all",
startDate: "",
endDate: "",
tags: [],
annotationTags: [],
metadata: [],
vote: "all"
};
}
const executionFilterToQueryFilter = (filter$1) => {
const queryFilter = {};
if (filter$1.workflowId !== "all") queryFilter.workflowId = filter$1.workflowId;
if (!isEmpty(filter$1.tags)) queryFilter.tags = filter$1.tags;
if (!isEmpty(filter$1.annotationTags)) queryFilter.annotationTags = filter$1.annotationTags;
if (filter$1.vote !== "all") queryFilter.vote = filter$1.vote;
if (!isEmpty(filter$1.metadata)) queryFilter.metadata = filter$1.metadata;
if (filter$1.startDate) queryFilter.startedAfter = filter$1.startDate;
if (filter$1.endDate) queryFilter.startedBefore = filter$1.endDate;
switch (filter$1.status) {
case "waiting":
queryFilter.status = ["waiting"];
break;
case "error":
queryFilter.status = ["crashed", "error"];
break;
case "success":
queryFilter.status = ["success"];
break;
case "running":
queryFilter.status = ["running", "new"];
break;
case "canceled":
queryFilter.status = ["canceled"];
break;
case "new":
queryFilter.status = ["new"];
break;
}
return queryFilter;
};
var formPopupWindow = false;
const openFormPopupWindow = (url) => {
if (!formPopupWindow) {
const height = 700;
const features = `width=${height},height=${window.innerHeight - 50},left=${(window.innerWidth - height) / 2},top=50,resizable=yes,scrollbars=yes`;
const windowName = `form-waiting-since-${Date.now()}`;
window.open(url, windowName, features);
formPopupWindow = true;
}
};
const clearPopupWindowState = () => formPopupWindow = false;
async function displayForm({ nodes: nodes$13, runData, pinData: pinData$12, destinationNode, triggerNode, directParentNodes, source, getTestUrl }) {
for (const node of nodes$13) {
if (triggerNode !== void 0 && triggerNode !== node.name) continue;
if (runData?.hasOwnProperty(node.name) && node.type !== "n8n-nodes-base.formTrigger" || pinData$12[node.name]) continue;
if (!["n8n-nodes-base.formTrigger"].includes(node.type)) continue;
if (destinationNode && destinationNode !== node.name && !directParentNodes.includes(node.name)) continue;
if (node.name === destinationNode || !node.disabled) {
let testUrl = "";
if (node.type === "n8n-nodes-base.formTrigger") testUrl = getTestUrl(node);
try {
if (!(await fetch(testUrl, { method: "GET" })).ok) continue;
} catch (error) {
continue;
}
if (testUrl && source !== "RunData.ManualChatMessage") {
clearPopupWindowState();
openFormPopupWindow(testUrl);
}
}
}
}
const waitingNodeTooltip = (node, workflow) => {
if (!node) return "";
try {
const waitingNodeTooltip$1 = useNodeTypesStore().getNodeType(node.type)?.waitingNodeTooltip;
if (waitingNodeTooltip$1) {
const activeExecutionId = useWorkflowsStore().activeExecutionId;
const additionalData = { $execution: {
id: activeExecutionId,
mode: "test",
resumeUrl: `${useRootStore().webhookWaitingUrl}/${activeExecutionId}`,
resumeFormUrl: `${useRootStore().formWaitingUrl}/${activeExecutionId}`
} };
if (workflow) {
const tooltip = workflow.expression.getSimpleParameterValue(node, waitingNodeTooltip$1, "internal", additionalData);
return String(tooltip);
} else if (waitingNodeTooltip$1) return waitingNodeTooltip$1;
}
} catch (error) {}
return "";
};
function isTrimmedNodeExecutionData(data) {
return data?.some((entry) => entry.json?.[TRIMMED_TASK_DATA_CONNECTIONS_KEY]);
}
function isTrimmedTaskData(taskData) {
return taskData.data?.main?.some((main) => isTrimmedNodeExecutionData(main));
}
function hasTrimmedTaskData(taskData) {
return taskData.some(isTrimmedTaskData);
}
function hasTrimmedRunData(runData) {
return Object.keys(runData).some((nodeName) => hasTrimmedTaskData(runData[nodeName]));
}
function executionRetryMessage(executionStatus) {
if (executionStatus === "success") return {
title: i18n.baseText("executionsList.showMessage.retrySuccess.title"),
type: "success"
};
if (executionStatus === "waiting") return {
title: i18n.baseText("executionsList.showMessage.retryWaiting.title"),
type: "info"
};
if (executionStatus === "running") return {
title: i18n.baseText("executionsList.showMessage.retryRunning.title"),
type: "info"
};
if (executionStatus === "crashed") return {
title: i18n.baseText("executionsList.showMessage.retryCrashed.title"),
type: "error"
};
if (executionStatus === "canceled") return {
title: i18n.baseText("executionsList.showMessage.retryCanceled.title"),
type: "error"
};
if (executionStatus === "error") return {
title: i18n.baseText("executionsList.showMessage.retryError.title"),
type: "error"
};
}
function getExecutionErrorMessage({ error, lastNodeExecuted }) {
let errorMessage;
if (lastNodeExecuted && error) errorMessage = error.message ?? error.description ?? "";
else {
errorMessage = i18n.baseText("pushConnection.executionError", { interpolate: { error: "!" } });
if (error?.message) {
let nodeName;
if ("node" in error) nodeName = typeof error.node === "string" ? error.node : error.node?.name;
const receivedError = nodeName ? `${nodeName}: ${error.message}` : error.message;
errorMessage = i18n.baseText("pushConnection.executionError", { interpolate: { error: `.${i18n.baseText("pushConnection.executionError.details", { interpolate: { details: receivedError } })}` } });
}
}
return errorMessage;
}
function getExecutionErrorToastConfiguration({ error, lastNodeExecuted }) {
const message = getExecutionErrorMessage({
error,
lastNodeExecuted
});
if (error.name === "SubworkflowOperationError") return {
title: error.message,
message: error.description ?? ""
};
if ((error.name === "NodeOperationError" || error.name === "NodeApiError") && error.functionality === "configuration-node") {
const nodeErrorName = "node" in error && error.node?.name ? error.node.name : "";
return {
title: nodeErrorName ? `Error in sub-node ‘${nodeErrorName}‘` : "Problem executing workflow",
message: h(NodeExecutionErrorMessage_default, {
errorMessage: error.description ?? message,
nodeName: nodeErrorName
})
};
}
return {
title: lastNodeExecuted ? `Problem in node ‘${lastNodeExecuted}‘` : "Problem executing workflow",
message
};
}
function unflattenExecutionData(fullExecutionData) {
const returnData = {
...fullExecutionData,
workflowData: fullExecutionData.workflowData,
data: parse(fullExecutionData.data)
};
returnData.finished = returnData.finished ? returnData.finished : false;
if (fullExecutionData.id) returnData.id = fullExecutionData.id;
return returnData;
}
function findTriggerNodeToAutoSelect(triggerNodes, getNodeType) {
const autoSelectPriorities = {
[FORM_TRIGGER_NODE_TYPE]: 10,
[WEBHOOK_NODE_TYPE]: 9,
[SCHEDULE_TRIGGER_NODE_TYPE]: 7,
[MANUAL_TRIGGER_NODE_TYPE]: 6,
[WORKFLOW_TRIGGER_NODE_TYPE]: 5,
[ERROR_TRIGGER_NODE_TYPE]: 4
};
function isCoreNode(node) {
return getNodeType(node.type, node.typeVersion)?.codex?.categories?.includes("Core Nodes") ?? false;
}
return triggerNodes.toSorted((a, b) => {
const aPriority = autoSelectPriorities[a.type] ?? (isCoreNode(a) ? 0 : 8);
return (autoSelectPriorities[b.type] ?? (isCoreNode(b) ? 0 : 8)) - aPriority;
}).find((node) => !node.disabled);
}
var fallbackParser = (execData) => ({
type: "json",
data: execData,
parsed: false
});
var outputTypeParsers = {
[NodeConnectionTypes.AiLanguageModel](execData) {
const response = execData.response ?? execData;
if (!response) throw new Error("No response from Language Model");
if (Array.isArray(response?.messages) && response?.messages.length === 1 && typeof response?.messages[0] === "string") return {
type: "text",
data: response.messages[0],
parsed: true
};
if (response.messages && Array.isArray(response.messages)) return outputTypeParsers[NodeConnectionTypes.AiMemory](execData);
if (response.generations) return {
type: "json",
data: response.generations.map((generation) => {
if (generation?.text) return generation.text;
if (Array.isArray(generation)) return generation.map((item) => item.text ?? item).join("\n\n").trim();
return generation;
}),
parsed: true
};
return {
type: "json",
data: response,
parsed: true
};
},
[NodeConnectionTypes.AiTool]: fallbackParser,
[NodeConnectionTypes.AiAgent]: fallbackParser,
[NodeConnectionTypes.AiMemory](execData) {
const chatHistory = execData.chatHistory ?? execData.messages ?? (execData?.response)?.chat_history;
if (Array.isArray(chatHistory)) {
const responseText = chatHistory.map((content) => {
if (content.type === "constructor" && content.id?.includes("messages") && content.kwargs) {
let message = String(content.kwargs.content);
if (Array.isArray(message)) message = message.map((item) => {
const { type, image_url } = item;
if (type === "image_url" && typeof image_url === "object" && typeof image_url.url === "string") return ``;
else if (typeof image_url === "string") return ``;
return item.text;
}).join("\n");
if (Object.keys(content.kwargs.additional_kwargs).length) message += ` (${JSON.stringify(content.kwargs.additional_kwargs)})`;
if (content.id.includes("HumanMessage")) message = `**Human:** ${String(message).trim()}`;
else if (content.id.includes("AIMessage")) message = `**AI:** ${message}`;
else if (content.id.includes("SystemMessage")) message = `**System Message:** ${message}`;
return message;
}
return "";
}).join("\n\n");
if (responseText.length === 0) return fallbackParser(execData);
return {
type: "markdown",
data: responseText,
parsed: true
};
}
return fallbackParser(execData);
},
[NodeConnectionTypes.AiOutputParser]: fallbackParser,
[NodeConnectionTypes.AiRetriever]: fallbackParser,
[NodeConnectionTypes.AiReranker]: fallbackParser,
[NodeConnectionTypes.AiVectorStore](execData) {
if (execData.documents) return {
type: "json",
data: execData.documents,
parsed: true
};
return fallbackParser(execData);
},
[NodeConnectionTypes.AiEmbedding](execData) {
if (execData.documents) return {
type: "json",
data: execData.documents,
parsed: true
};
return fallbackParser(execData);
},
[NodeConnectionTypes.AiDocument](execData) {
if (execData.documents) return {
type: "json",
data: execData.documents,
parsed: true
};
return fallbackParser(execData);
},
[NodeConnectionTypes.AiTextSplitter](execData) {
return {
type: "text",
data: (Array.isArray(execData.response) ? execData.response : [execData.textSplitter]).join("\n\n"),
parsed: true
};
}
};
function parseAiContent(executionData, endpointType) {
if ([NodeConnectionTypes.AiChain, NodeConnectionTypes.Main].includes(endpointType)) return executionData.map((data) => ({
raw: data.json,
parsedContent: null
}));
const contentJson = executionData.map((node) => {
return !isObjectEmpty(node.binary) ? node.binary : node.json;
});
const parser = outputTypeParsers[endpointType];
if (!parser) return [{
raw: contentJson.filter((item) => item !== void 0),
parsedContent: null
}];
return contentJson.filter((c) => c !== void 0).map((c) => ({
raw: c,
parsedContent: parser(c)
}));
}
const emptyTokenUsageData = {
completionTokens: 0,
promptTokens: 0,
totalTokens: 0,
isEstimate: false
};
function addTokenUsageData(one$1, another) {
return {
completionTokens: one$1.completionTokens + another.completionTokens,
promptTokens: one$1.promptTokens + another.promptTokens,
totalTokens: one$1.totalTokens + another.totalTokens,
isEstimate: one$1.isEstimate || another.isEstimate
};
}
function formatTokenUsageCount(usage, field) {
const count = field === "total" ? usage.totalTokens : field === "completion" ? usage.completionTokens : usage.promptTokens;
return usage.isEstimate ? `~${count}` : count.toLocaleString();
}
function isChatNode(node) {
return [CHAT_TRIGGER_NODE_TYPE, MANUAL_CHAT_TRIGGER_NODE_TYPE].includes(node.type);
}
function tryOnScopeDispose(fn) {
if (getCurrentScope()) {
onScopeDispose(fn);
return true;
}
return false;
}
function toValue$1(r) {
return typeof r === "function" ? r() : unref(r);
}
var isClient = typeof window !== "undefined" && typeof document !== "undefined";
var isDef$1 = (val) => typeof val !== "undefined";
var toString = Object.prototype.toString;
var isObject = (val) => toString.call(val) === "[object Object]";
var noop$3 = () => {};
function createFilterWrapper(filter2, fn) {
function wrapper(...args) {
return new Promise((resolve, reject) => {
Promise.resolve(filter2(() => fn.apply(this, args), {
fn,
thisArg: this,
args
})).then(resolve).catch(reject);
});
}
return wrapper;
}
var bypassFilter = (invoke) => {
return invoke();
};
function pausableFilter(extendFilter = bypassFilter) {
const isActive = ref(true);
function pause() {
isActive.value = false;
}
function resume() {
isActive.value = true;
}
const eventFilter = (...args) => {
if (isActive.value) extendFilter(...args);
};
return {
isActive: readonly(isActive),
pause,
resume,
eventFilter
};
}
function promiseTimeout(ms, throwOnTimeout = false, reason = "Timeout") {
return new Promise((resolve, reject) => {
if (throwOnTimeout) setTimeout(() => reject(reason), ms);
else setTimeout(resolve, ms);
});
}
function watchWithFilter(source, cb, options = {}) {
const { eventFilter = bypassFilter,...watchOptions } = options;
return watch(source, createFilterWrapper(eventFilter, cb), watchOptions);
}
function watchPausable(source, cb, options = {}) {
const { eventFilter: filter2,...watchOptions } = options;
const { eventFilter, pause, resume, isActive } = pausableFilter(filter2);
return {
stop: watchWithFilter(source, cb, {
...watchOptions,
eventFilter
}),
pause,
resume,
isActive
};
}
function toRefs$1(objectRef, options = {}) {
if (!isRef(objectRef)) return toRefs(objectRef);
const result = Array.isArray(objectRef.value) ? Array.from({ length: objectRef.value.length }) : {};
for (const key in objectRef.value) result[key] = customRef(() => ({
get() {
return objectRef.value[key];
},
set(v) {
var _a;
if ((_a = toValue$1(options.replaceRef)) != null ? _a : true) if (Array.isArray(objectRef.value)) {
const copy = [...objectRef.value];
copy[key] = v;
objectRef.value = copy;
} else {
const newObject = {
...objectRef.value,
[key]: v
};
Object.setPrototypeOf(newObject, Object.getPrototypeOf(objectRef.value));
objectRef.value = newObject;
}
else objectRef.value[key] = v;
}
}));
return result;
}
function createUntil(r, isNot = false) {
function toMatch(condition, { flush = "sync", deep = false, timeout: timeout2, throwOnTimeout } = {}) {
let stop = null;
const promises = [new Promise((resolve) => {
stop = watch(r, (v) => {
if (condition(v) !== isNot) {
stop?.();
resolve(v);
}
}, {
flush,
deep,
immediate: true
});
})];
if (timeout2 != null) promises.push(promiseTimeout(timeout2, throwOnTimeout).then(() => toValue$1(r)).finally(() => stop == null ? void 0 : stop()));
return Promise.race(promises);
}
function toBe(value, options) {
if (!isRef(value)) return toMatch((v) => v === value, options);
const { flush = "sync", deep = false, timeout: timeout2, throwOnTimeout } = options != null ? options : {};
let stop = null;
const promises = [new Promise((resolve) => {
stop = watch([r, value], ([v1, v2]) => {
if (isNot !== (v1 === v2)) {
stop?.();
resolve(v1);
}
}, {
flush,
deep,
immediate: true
});
})];
if (timeout2 != null) promises.push(promiseTimeout(timeout2, throwOnTimeout).then(() => toValue$1(r)).finally(() => {
stop?.();
return toValue$1(r);
}));
return Promise.race(promises);
}
function toBeTruthy(options) {
return toMatch((v) => Boolean(v), options);
}
function toBeNull(options) {
return toBe(null, options);
}
function toBeUndefined(options) {
return toBe(void 0, options);
}
function toBeNaN(options) {
return toMatch(Number.isNaN, options);
}
function toContains(value, options) {
return toMatch((v) => {
const array2 = Array.from(v);
return array2.includes(value) || array2.includes(toValue$1(value));
}, options);
}
function changed(options) {
return changedTimes(1, options);
}
function changedTimes(n = 1, options) {
let count = -1;
return toMatch(() => {
count += 1;
return count >= n;
}, options);
}
if (Array.isArray(toValue$1(r))) return {
toMatch,
toContains,
changed,
changedTimes,
get not() {
return createUntil(r, !isNot);
}
};
else return {
toMatch,
toBe,
toBeTruthy,
toBeNull,
toBeNaN,
toBeUndefined,
changed,
changedTimes,
get not() {
return createUntil(r, !isNot);
}
};
}
function until(r) {
return createUntil(r);
}
function unrefElement(elRef) {
var _a;
const plain = toValue$1(elRef);
return (_a = plain == null ? void 0 : plain.$el) != null ? _a : plain;
}
var defaultWindow = isClient ? window : void 0;
function useEventListener(...args) {
let target;
let events;
let listeners$1;
let options;
if (typeof args[0] === "string" || Array.isArray(args[0])) {
[events, listeners$1, options] = args;
target = defaultWindow;
} else [target, events, listeners$1, options] = args;
if (!target) return noop$3;
if (!Array.isArray(events)) events = [events];
if (!Array.isArray(listeners$1)) listeners$1 = [listeners$1];
const cleanups = [];
const cleanup = () => {
cleanups.forEach((fn) => fn());
cleanups.length = 0;
};
const register$1 = (el, event, listener, options2) => {
el.addEventListener(event, listener, options2);
return () => el.removeEventListener(event, listener, options2);
};
const stopWatch = watch(() => [unrefElement(target), toValue$1(options)], ([el, options2]) => {
cleanup();
if (!el) return;
const optionsClone = isObject(options2) ? { ...options2 } : options2;
cleanups.push(...events.flatMap((event) => {
return listeners$1.map((listener) => register$1(el, event, listener, optionsClone));
}));
}, {
immediate: true,
flush: "post"
});
const stop = () => {
stopWatch();
cleanup();
};
tryOnScopeDispose(stop);
return stop;
}
function createKeyPredicate$1(keyFilter) {
if (typeof keyFilter === "function") return keyFilter;
else if (typeof keyFilter === "string") return (event) => event.key === keyFilter;
else if (Array.isArray(keyFilter)) return (event) => keyFilter.includes(event.key);
return () => true;
}
function onKeyStroke(...args) {
let key;
let handler;
let options = {};
if (args.length === 3) {
key = args[0];
handler = args[1];
options = args[2];
} else if (args.length === 2) if (typeof args[1] === "object") {
key = true;
handler = args[0];
options = args[1];
} else {
key = args[0];
handler = args[1];
}
else {
key = true;
handler = args[0];
}
const { target = defaultWindow, eventName = "keydown", passive = false, dedupe = false } = options;
const predicate = createKeyPredicate$1(key);
const listener = (e) => {
if (e.repeat && toValue$1(dedupe)) return;
if (predicate(e)) handler(e);
};
return useEventListener(target, eventName, listener, passive);
}
function cloneFnJSON(source) {
return JSON.parse(JSON.stringify(source));
}
function useVModel(props, key, emit, options = {}) {
var _a, _b, _c;
const { clone = false, passive = false, eventName, deep = false, defaultValue, shouldEmit } = options;
const vm = getCurrentInstance();
const _emit = emit || (vm == null ? void 0 : vm.emit) || ((_a = vm == null ? void 0 : vm.$emit) == null ? void 0 : _a.bind(vm)) || ((_c = (_b = vm == null ? void 0 : vm.proxy) == null ? void 0 : _b.$emit) == null ? void 0 : _c.bind(vm == null ? void 0 : vm.proxy));
let event = eventName;
if (!key) key = "modelValue";
event = event || `update:${key.toString()}`;
const cloneFn = (val) => !clone ? val : typeof clone === "function" ? clone(val) : cloneFnJSON(val);
const getValue$1 = () => isDef$1(props[key]) ? cloneFn(props[key]) : defaultValue;
const triggerEmit = (value) => {
if (shouldEmit) {
if (shouldEmit(value)) _emit(event, value);
} else _emit(event, value);
};
if (passive) {
const proxy = ref(getValue$1());
let isUpdating = false;
watch(() => props[key], (v) => {
if (!isUpdating) {
isUpdating = true;
proxy.value = cloneFn(v);
nextTick(() => isUpdating = false);
}
});
watch(proxy, (v) => {
if (!isUpdating && (v !== props[key] || deep)) triggerEmit(v);
}, { deep });
return proxy;
} else return computed({
get() {
return getValue$1();
},
set(value) {
triggerEmit(value);
}
});
}
var noop$2 = { value: () => {} };
function dispatch() {
for (var i$1 = 0, n = arguments.length, _ = {}, t; i$1 < n; ++i$1) {
if (!(t = arguments[i$1] + "") || t in _ || /[\s.]/.test(t)) throw new Error("illegal type: " + t);
_[t] = [];
}
return new Dispatch(_);
}
function Dispatch(_) {
this._ = _;
}
function parseTypenames$1(typenames, types) {
return typenames.trim().split(/^|\s+/).map(function(t) {
var name$13 = "", i$1 = t.indexOf(".");
if (i$1 >= 0) name$13 = t.slice(i$1 + 1), t = t.slice(0, i$1);
if (t && !types.hasOwnProperty(t)) throw new Error("unknown type: " + t);
return {
type: t,
name: name$13
};
});
}
Dispatch.prototype = dispatch.prototype = {
constructor: Dispatch,
on: function(typename, callback) {
var _ = this._, T = parseTypenames$1(typename + "", _), t, i$1 = -1, n = T.length;
if (arguments.length < 2) {
while (++i$1 < n) if ((t = (typename = T[i$1]).type) && (t = get$1(_[t], typename.name))) return t;
return;
}
if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback);
while (++i$1 < n) if (t = (typename = T[i$1]).type) _[t] = set$1(_[t], typename.name, callback);
else if (callback == null) for (t in _) _[t] = set$1(_[t], typename.name, null);
return this;
},
copy: function() {
var copy = {}, _ = this._;
for (var t in _) copy[t] = _[t].slice();
return new Dispatch(copy);
},
call: function(type, that) {
if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i$1 = 0, n, t; i$1 < n; ++i$1) args[i$1] = arguments[i$1 + 2];
if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
for (t = this._[type], i$1 = 0, n = t.length; i$1 < n; ++i$1) t[i$1].value.apply(that, args);
},
apply: function(type, that, args) {
if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
for (var t = this._[type], i$1 = 0, n = t.length; i$1 < n; ++i$1) t[i$1].value.apply(that, args);
}
};
function get$1(type, name$13) {
for (var i$1 = 0, n = type.length, c; i$1 < n; ++i$1) if ((c = type[i$1]).name === name$13) return c.value;
}
function set$1(type, name$13, callback) {
for (var i$1 = 0, n = type.length; i$1 < n; ++i$1) if (type[i$1].name === name$13) {
type[i$1] = noop$2, type = type.slice(0, i$1).concat(type.slice(i$1 + 1));
break;
}
if (callback != null) type.push({
name: name$13,
value: callback
});
return type;
}
var xhtml = "http://www.w3.org/1999/xhtml";
var namespaces = {
svg: "http://www.w3.org/2000/svg",
xhtml,
xlink: "http://www.w3.org/1999/xlink",
xml: "http://www.w3.org/XML/1998/namespace",
xmlns: "http://www.w3.org/2000/xmlns/"
};
function namespace(name$13) {
var prefix = name$13 += "", i$1 = prefix.indexOf(":");
if (i$1 >= 0 && (prefix = name$13.slice(0, i$1)) !== "xmlns") name$13 = name$13.slice(i$1 + 1);
return namespaces.hasOwnProperty(prefix) ? {
space: namespaces[prefix],
local: name$13
} : name$13;
}
function creatorInherit(name$13) {
return function() {
var document2 = this.ownerDocument, uri = this.namespaceURI;
return uri === xhtml && document2.documentElement.namespaceURI === xhtml ? document2.createElement(name$13) : document2.createElementNS(uri, name$13);
};
}
function creatorFixed(fullname) {
return function() {
return this.ownerDocument.createElementNS(fullname.space, fullname.local);
};
}
function creator(name$13) {
var fullname = namespace(name$13);
return (fullname.local ? creatorFixed : creatorInherit)(fullname);
}
function none() {}
function selector(selector2) {
return selector2 == null ? none : function() {
return this.querySelector(selector2);
};
}
function selection_select(select2) {
if (typeof select2 !== "function") select2 = selector(select2);
for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i$1 = 0; i$1 < n; ++i$1) if ((node = group[i$1]) && (subnode = select2.call(node, node.__data__, i$1, group))) {
if ("__data__" in node) subnode.__data__ = node.__data__;
subgroup[i$1] = subnode;
}
return new Selection$1(subgroups, this._parents);
}
function array(x) {
return x == null ? [] : Array.isArray(x) ? x : Array.from(x);
}
function empty() {
return [];
}
function selectorAll(selector2) {
return selector2 == null ? empty : function() {
return this.querySelectorAll(selector2);
};
}
function arrayAll(select2) {
return function() {
return array(select2.apply(this, arguments));
};
}
function selection_selectAll(select2) {
if (typeof select2 === "function") select2 = arrayAll(select2);
else select2 = selectorAll(select2);
for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) for (var group = groups[j], n = group.length, node, i$1 = 0; i$1 < n; ++i$1) if (node = group[i$1]) {
subgroups.push(select2.call(node, node.__data__, i$1, group));
parents.push(node);
}
return new Selection$1(subgroups, parents);
}
function matcher(selector2) {
return function() {
return this.matches(selector2);
};
}
function childMatcher(selector2) {
return function(node) {
return node.matches(selector2);
};
}
var find = Array.prototype.find;
function childFind(match) {
return function() {
return find.call(this.children, match);
};
}
function childFirst() {
return this.firstElementChild;
}
function selection_selectChild(match) {
return this.select(match == null ? childFirst : childFind(typeof match === "function" ? match : childMatcher(match)));
}
var filter = Array.prototype.filter;
function children() {
return Array.from(this.children);
}
function childrenFilter(match) {
return function() {
return filter.call(this.children, match);
};
}
function selection_selectChildren(match) {
return this.selectAll(match == null ? children : childrenFilter(typeof match === "function" ? match : childMatcher(match)));
}
function selection_filter(match) {
if (typeof match !== "function") match = matcher(match);
for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i$1 = 0; i$1 < n; ++i$1) if ((node = group[i$1]) && match.call(node, node.__data__, i$1, group)) subgroup.push(node);
return new Selection$1(subgroups, this._parents);
}
function sparse(update) {
return new Array(update.length);
}
function selection_enter() {
return new Selection$1(this._enter || this._groups.map(sparse), this._parents);
}
function EnterNode(parent, datum2) {
this.ownerDocument = parent.ownerDocument;
this.namespaceURI = parent.namespaceURI;
this._next = null;
this._parent = parent;
this.__data__ = datum2;
}
EnterNode.prototype = {
constructor: EnterNode,
appendChild: function(child) {
return this._parent.insertBefore(child, this._next);
},
insertBefore: function(child, next) {
return this._parent.insertBefore(child, next);
},
querySelector: function(selector2) {
return this._parent.querySelector(selector2);
},
querySelectorAll: function(selector2) {
return this._parent.querySelectorAll(selector2);
}
};
function constant$3(x) {
return function() {
return x;
};
}
function bindIndex(parent, group, enter, update, exit, data) {
var i$1 = 0, node, groupLength = group.length, dataLength = data.length;
for (; i$1 < dataLength; ++i$1) if (node = group[i$1]) {
node.__data__ = data[i$1];
update[i$1] = node;
} else enter[i$1] = new EnterNode(parent, data[i$1]);
for (; i$1 < groupLength; ++i$1) if (node = group[i$1]) exit[i$1] = node;
}
function bindKey(parent, group, enter, update, exit, data, key) {
var i$1, node, nodeByKeyValue = /* @__PURE__ */ new Map(), groupLength = group.length, dataLength = data.length, keyValues = new Array(groupLength), keyValue;
for (i$1 = 0; i$1 < groupLength; ++i$1) if (node = group[i$1]) {
keyValues[i$1] = keyValue = key.call(node, node.__data__, i$1, group) + "";
if (nodeByKeyValue.has(keyValue)) exit[i$1] = node;
else nodeByKeyValue.set(keyValue, node);
}
for (i$1 = 0; i$1 < dataLength; ++i$1) {
keyValue = key.call(parent, data[i$1], i$1, data) + "";
if (node = nodeByKeyValue.get(keyValue)) {
update[i$1] = node;
node.__data__ = data[i$1];
nodeByKeyValue.delete(keyValue);
} else enter[i$1] = new EnterNode(parent, data[i$1]);
}
for (i$1 = 0; i$1 < groupLength; ++i$1) if ((node = group[i$1]) && nodeByKeyValue.get(keyValues[i$1]) === node) exit[i$1] = node;
}
function datum(node) {
return node.__data__;
}
function selection_data(value, key) {
if (!arguments.length) return Array.from(this, datum);
var bind = key ? bindKey : bindIndex, parents = this._parents, groups = this._groups;
if (typeof value !== "function") value = constant$3(value);
for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {
var parent = parents[j], group = groups[j], groupLength = group.length, data = arraylike(value.call(parent, parent && parent.__data__, j, parents)), dataLength = data.length, enterGroup = enter[j] = new Array(dataLength), updateGroup = update[j] = new Array(dataLength);
bind(parent, group, enterGroup, updateGroup, exit[j] = new Array(groupLength), data, key);
for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) if (previous = enterGroup[i0]) {
if (i0 >= i1) i1 = i0 + 1;
while (!(next = updateGroup[i1]) && ++i1 < dataLength);
previous._next = next || null;
}
}
update = new Selection$1(update, parents);
update._enter = enter;
update._exit = exit;
return update;
}
function arraylike(data) {
return typeof data === "object" && "length" in data ? data : Array.from(data);
}
function selection_exit() {
return new Selection$1(this._exit || this._groups.map(sparse), this._parents);
}
function selection_join(onenter, onupdate, onexit) {
var enter = this.enter(), update = this, exit = this.exit();
if (typeof onenter === "function") {
enter = onenter(enter);
if (enter) enter = enter.selection();
} else enter = enter.append(onenter + "");
if (onupdate != null) {
update = onupdate(update);
if (update) update = update.selection();
}
if (onexit == null) exit.remove();
else onexit(exit);
return enter && update ? enter.merge(update).order() : update;
}
function selection_merge(context) {
var selection2 = context.selection ? context.selection() : context;
for (var groups0 = this._groups, groups1 = selection2._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge$1 = merges[j] = new Array(n), node, i$1 = 0; i$1 < n; ++i$1) if (node = group0[i$1] || group1[i$1]) merge$1[i$1] = node;
for (; j < m0; ++j) merges[j] = groups0[j];
return new Selection$1(merges, this._parents);
}
function selection_order() {
for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) for (var group = groups[j], i$1 = group.length - 1, next = group[i$1], node; --i$1 >= 0;) if (node = group[i$1]) {
if (next && node.compareDocumentPosition(next) ^ 4) next.parentNode.insertBefore(node, next);
next = node;
}
return this;
}
function selection_sort(compare) {
if (!compare) compare = ascending;
function compareNode(a, b) {
return a && b ? compare(a.__data__, b.__data__) : !a - !b;
}
for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) {
for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i$1 = 0; i$1 < n; ++i$1) if (node = group[i$1]) sortgroup[i$1] = node;
sortgroup.sort(compareNode);
}
return new Selection$1(sortgroups, this._parents).order();
}
function ascending(a, b) {
return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
}
function selection_call() {
var callback = arguments[0];
arguments[0] = this;
callback.apply(null, arguments);
return this;
}
function selection_nodes() {
return Array.from(this);
}
function selection_node() {
for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) for (var group = groups[j], i$1 = 0, n = group.length; i$1 < n; ++i$1) {
var node = group[i$1];
if (node) return node;
}
return null;
}
function selection_size() {
let size = 0;
for (const node of this) ++size;
return size;
}
function selection_empty() {
return !this.node();
}
function selection_each(callback) {
for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) for (var group = groups[j], i$1 = 0, n = group.length, node; i$1 < n; ++i$1) if (node = group[i$1]) callback.call(node, node.__data__, i$1, group);
return this;
}
function attrRemove$1(name$13) {
return function() {
this.removeAttribute(name$13);
};
}
function attrRemoveNS$1(fullname) {
return function() {
this.removeAttributeNS(fullname.space, fullname.local);
};
}
function attrConstant$1(name$13, value) {
return function() {
this.setAttribute(name$13, value);
};
}
function attrConstantNS$1(fullname, value) {
return function() {
this.setAttributeNS(fullname.space, fullname.local, value);
};
}
function attrFunction$1(name$13, value) {
return function() {
var v = value.apply(this, arguments);
if (v == null) this.removeAttribute(name$13);
else this.setAttribute(name$13, v);
};
}
function attrFunctionNS$1(fullname, value) {
return function() {
var v = value.apply(this, arguments);
if (v == null) this.removeAttributeNS(fullname.space, fullname.local);
else this.setAttributeNS(fullname.space, fullname.local, v);
};
}
function selection_attr(name$13, value) {
var fullname = namespace(name$13);
if (arguments.length < 2) {
var node = this.node();
return fullname.local ? node.getAttributeNS(fullname.space, fullname.local) : node.getAttribute(fullname);
}
return this.each((value == null ? fullname.local ? attrRemoveNS$1 : attrRemove$1 : typeof value === "function" ? fullname.local ? attrFunctionNS$1 : attrFunction$1 : fullname.local ? attrConstantNS$1 : attrConstant$1)(fullname, value));
}
function defaultView(node) {
return node.ownerDocument && node.ownerDocument.defaultView || node.document && node || node.defaultView;
}
function styleRemove$1(name$13) {
return function() {
this.style.removeProperty(name$13);
};
}
function styleConstant$1(name$13, value, priority) {
return function() {
this.style.setProperty(name$13, value, priority);
};
}
function styleFunction$1(name$13, value, priority) {
return function() {
var v = value.apply(this, arguments);
if (v == null) this.style.removeProperty(name$13);
else this.style.setProperty(name$13, v, priority);
};
}
function selection_style(name$13, value, priority) {
return arguments.length > 1 ? this.each((value == null ? styleRemove$1 : typeof value === "function" ? styleFunction$1 : styleConstant$1)(name$13, value, priority == null ? "" : priority)) : styleValue(this.node(), name$13);
}
function styleValue(node, name$13) {
return node.style.getPropertyValue(name$13) || defaultView(node).getComputedStyle(node, null).getPropertyValue(name$13);
}
function propertyRemove(name$13) {
return function() {
delete this[name$13];
};
}
function propertyConstant(name$13, value) {
return function() {
this[name$13] = value;
};
}
function propertyFunction(name$13, value) {
return function() {
var v = value.apply(this, arguments);
if (v == null) delete this[name$13];
else this[name$13] = v;
};
}
function selection_property(name$13, value) {
return arguments.length > 1 ? this.each((value == null ? propertyRemove : typeof value === "function" ? propertyFunction : propertyConstant)(name$13, value)) : this.node()[name$13];
}
function classArray(string) {
return string.trim().split(/^|\s+/);
}
function classList(node) {
return node.classList || new ClassList(node);
}
function ClassList(node) {
this._node = node;
this._names = classArray(node.getAttribute("class") || "");
}
ClassList.prototype = {
add: function(name$13) {
if (this._names.indexOf(name$13) < 0) {
this._names.push(name$13);
this._node.setAttribute("class", this._names.join(" "));
}
},
remove: function(name$13) {
var i$1 = this._names.indexOf(name$13);
if (i$1 >= 0) {
this._names.splice(i$1, 1);
this._node.setAttribute("class", this._names.join(" "));
}
},
contains: function(name$13) {
return this._names.indexOf(name$13) >= 0;
}
};
function classedAdd(node, names) {
var list = classList(node), i$1 = -1, n = names.length;
while (++i$1 < n) list.add(names[i$1]);
}
function classedRemove(node, names) {
var list = classList(node), i$1 = -1, n = names.length;
while (++i$1 < n) list.remove(names[i$1]);
}
function classedTrue(names) {
return function() {
classedAdd(this, names);
};
}
function classedFalse(names) {
return function() {
classedRemove(this, names);
};
}
function classedFunction(names, value) {
return function() {
(value.apply(this, arguments) ? classedAdd : classedRemove)(this, names);
};
}
function selection_classed(name$13, value) {
var names = classArray(name$13 + "");
if (arguments.length < 2) {
var list = classList(this.node()), i$1 = -1, n = names.length;
while (++i$1 < n) if (!list.contains(names[i$1])) return false;
return true;
}
return this.each((typeof value === "function" ? classedFunction : value ? classedTrue : classedFalse)(names, value));
}
function textRemove() {
this.textContent = "";
}
function textConstant$1(value) {
return function() {
this.textContent = value;
};
}
function textFunction$1(value) {
return function() {
var v = value.apply(this, arguments);
this.textContent = v == null ? "" : v;
};
}
function selection_text(value) {
return arguments.length ? this.each(value == null ? textRemove : (typeof value === "function" ? textFunction$1 : textConstant$1)(value)) : this.node().textContent;
}
function htmlRemove() {
this.innerHTML = "";
}
function htmlConstant(value) {
return function() {
this.innerHTML = value;
};
}
function htmlFunction(value) {
return function() {
var v = value.apply(this, arguments);
this.innerHTML = v == null ? "" : v;
};
}
function selection_html(value) {
return arguments.length ? this.each(value == null ? htmlRemove : (typeof value === "function" ? htmlFunction : htmlConstant)(value)) : this.node().innerHTML;
}
function raise() {
if (this.nextSibling) this.parentNode.appendChild(this);
}
function selection_raise() {
return this.each(raise);
}
function lower() {
if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild);
}
function selection_lower() {
return this.each(lower);
}
function selection_append(name$13) {
var create2 = typeof name$13 === "function" ? name$13 : creator(name$13);
return this.select(function() {
return this.appendChild(create2.apply(this, arguments));
});
}
function constantNull() {
return null;
}
function selection_insert(name$13, before) {
var create2 = typeof name$13 === "function" ? name$13 : creator(name$13), select2 = before == null ? constantNull : typeof before === "function" ? before : selector(before);
return this.select(function() {
return this.insertBefore(create2.apply(this, arguments), select2.apply(this, arguments) || null);
});
}
function remove() {
var parent = this.parentNode;
if (parent) parent.removeChild(this);
}
function selection_remove() {
return this.each(remove);
}
function selection_cloneShallow() {
var clone = this.cloneNode(false), parent = this.parentNode;
return parent ? parent.insertBefore(clone, this.nextSibling) : clone;
}
function selection_cloneDeep() {
var clone = this.cloneNode(true), parent = this.parentNode;
return parent ? parent.insertBefore(clone, this.nextSibling) : clone;
}
function selection_clone(deep) {
return this.select(deep ? selection_cloneDeep : selection_cloneShallow);
}
function selection_datum(value) {
return arguments.length ? this.property("__data__", value) : this.node().__data__;
}
function contextListener(listener) {
return function(event) {
listener.call(this, event, this.__data__);
};
}
function parseTypenames(typenames) {
return typenames.trim().split(/^|\s+/).map(function(t) {
var name$13 = "", i$1 = t.indexOf(".");
if (i$1 >= 0) name$13 = t.slice(i$1 + 1), t = t.slice(0, i$1);
return {
type: t,
name: name$13
};
});
}
function onRemove(typename) {
return function() {
var on = this.__on;
if (!on) return;
for (var j = 0, i$1 = -1, m = on.length, o; j < m; ++j) if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) this.removeEventListener(o.type, o.listener, o.options);
else on[++i$1] = o;
if (++i$1) on.length = i$1;
else delete this.__on;
};
}
function onAdd(typename, value, options) {
return function() {
var on = this.__on, o, listener = contextListener(value);
if (on) {
for (var j = 0, m = on.length; j < m; ++j) if ((o = on[j]).type === typename.type && o.name === typename.name) {
this.removeEventListener(o.type, o.listener, o.options);
this.addEventListener(o.type, o.listener = listener, o.options = options);
o.value = value;
return;
}
}
this.addEventListener(typename.type, listener, options);
o = {
type: typename.type,
name: typename.name,
value,
listener,
options
};
if (!on) this.__on = [o];
else on.push(o);
};
}
function selection_on(typename, value, options) {
var typenames = parseTypenames(typename + ""), i$1, n = typenames.length, t;
if (arguments.length < 2) {
var on = this.node().__on;
if (on) {
for (var j = 0, m = on.length, o; j < m; ++j) for (i$1 = 0, o = on[j]; i$1 < n; ++i$1) if ((t = typenames[i$1]).type === o.type && t.name === o.name) return o.value;
}
return;
}
on = value ? onAdd : onRemove;
for (i$1 = 0; i$1 < n; ++i$1) this.each(on(typenames[i$1], value, options));
return this;
}
function dispatchEvent(node, type, params) {
var window2 = defaultView(node), event = window2.CustomEvent;
if (typeof event === "function") event = new event(type, params);
else {
event = window2.document.createEvent("Event");
if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail;
else event.initEvent(type, false, false);
}
node.dispatchEvent(event);
}
function dispatchConstant(type, params) {
return function() {
return dispatchEvent(this, type, params);
};
}
function dispatchFunction(type, params) {
return function() {
return dispatchEvent(this, type, params.apply(this, arguments));
};
}
function selection_dispatch(type, params) {
return this.each((typeof params === "function" ? dispatchFunction : dispatchConstant)(type, params));
}
function* selection_iterator() {
for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) for (var group = groups[j], i$1 = 0, n = group.length, node; i$1 < n; ++i$1) if (node = group[i$1]) yield node;
}
var root = [null];
function Selection$1(groups, parents) {
this._groups = groups;
this._parents = parents;
}
function selection() {
return new Selection$1([[document.documentElement]], root);
}
function selection_selection() {
return this;
}
Selection$1.prototype = selection.prototype = {
constructor: Selection$1,
select: selection_select,
selectAll: selection_selectAll,
selectChild: selection_selectChild,
selectChildren: selection_selectChildren,
filter: selection_filter,
data: selection_data,
enter: selection_enter,
exit: selection_exit,
join: selection_join,
merge: selection_merge,
selection: selection_selection,
order: selection_order,
sort: selection_sort,
call: selection_call,
nodes: selection_nodes,
node: selection_node,
size: selection_size,
empty: selection_empty,
each: selection_each,
attr: selection_attr,
style: selection_style,
property: selection_property,
classed: selection_classed,
text: selection_text,
html: selection_html,
raise: selection_raise,
lower: selection_lower,
append: selection_append,
insert: selection_insert,
remove: selection_remove,
clone: selection_clone,
datum: selection_datum,
on: selection_on,
dispatch: selection_dispatch,
[Symbol.iterator]: selection_iterator
};
function select(selector2) {
return typeof selector2 === "string" ? new Selection$1([[document.querySelector(selector2)]], [document.documentElement]) : new Selection$1([[selector2]], root);
}
function sourceEvent(event) {
let sourceEvent2;
while (sourceEvent2 = event.sourceEvent) event = sourceEvent2;
return event;
}
function pointer(event, node) {
event = sourceEvent(event);
if (node === void 0) node = event.currentTarget;
if (node) {
var svg = node.ownerSVGElement || node;
if (svg.createSVGPoint) {
var point = svg.createSVGPoint();
point.x = event.clientX, point.y = event.clientY;
point = point.matrixTransform(node.getScreenCTM().inverse());
return [point.x, point.y];
}
if (node.getBoundingClientRect) {
var rect = node.getBoundingClientRect();
return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop];
}
}
return [event.pageX, event.pageY];
}
var nonpassive = { passive: false };
var nonpassivecapture = {
capture: true,
passive: false
};
function nopropagation$1(event) {
event.stopImmediatePropagation();
}
function noevent$1(event) {
event.preventDefault();
event.stopImmediatePropagation();
}
function dragDisable(view) {
var root2 = view.document.documentElement, selection2 = select(view).on("dragstart.drag", noevent$1, nonpassivecapture);
if ("onselectstart" in root2) selection2.on("selectstart.drag", noevent$1, nonpassivecapture);
else {
root2.__noselect = root2.style.MozUserSelect;
root2.style.MozUserSelect = "none";
}
}
function yesdrag(view, noclick) {
var root2 = view.document.documentElement, selection2 = select(view).on("dragstart.drag", null);
if (noclick) {
selection2.on("click.drag", noevent$1, nonpassivecapture);
setTimeout(function() {
selection2.on("click.drag", null);
}, 0);
}
if ("onselectstart" in root2) selection2.on("selectstart.drag", null);
else {
root2.style.MozUserSelect = root2.__noselect;
delete root2.__noselect;
}
}
var constant$2 = (x) => () => x;
function DragEvent(type, { sourceEvent: sourceEvent2, subject, target, identifier, active: active$7, x, y, dx, dy, dispatch: dispatch2 }) {
Object.defineProperties(this, {
type: {
value: type,
enumerable: true,
configurable: true
},
sourceEvent: {
value: sourceEvent2,
enumerable: true,
configurable: true
},
subject: {
value: subject,
enumerable: true,
configurable: true
},
target: {
value: target,
enumerable: true,
configurable: true
},
identifier: {
value: identifier,
enumerable: true,
configurable: true
},
active: {
value: active$7,
enumerable: true,
configurable: true
},
x: {
value: x,
enumerable: true,
configurable: true
},
y: {
value: y,
enumerable: true,
configurable: true
},
dx: {
value: dx,
enumerable: true,
configurable: true
},
dy: {
value: dy,
enumerable: true,
configurable: true
},
_: { value: dispatch2 }
});
}
DragEvent.prototype.on = function() {
var value = this._.on.apply(this._, arguments);
return value === this._ ? this : value;
};
function defaultFilter$1(event) {
return !event.ctrlKey && !event.button;
}
function defaultContainer() {
return this.parentNode;
}
function defaultSubject(event, d) {
return d == null ? {
x: event.x,
y: event.y
} : d;
}
function defaultTouchable$1() {
return navigator.maxTouchPoints || "ontouchstart" in this;
}
function drag() {
var filter2 = defaultFilter$1, container = defaultContainer, subject = defaultSubject, touchable = defaultTouchable$1, gestures = {}, listeners$1 = dispatch("start", "drag", "end"), active$7 = 0, mousedownx, mousedowny, mousemoving, touchending, clickDistance2 = 0;
function drag2(selection2) {
selection2.on("mousedown.drag", mousedowned).filter(touchable).on("touchstart.drag", touchstarted).on("touchmove.drag", touchmoved, nonpassive).on("touchend.drag touchcancel.drag", touchended).style("touch-action", "none").style("-webkit-tap-highlight-color", "rgba(0,0,0,0)");
}
function mousedowned(event, d) {
if (touchending || !filter2.call(this, event, d)) return;
var gesture = beforestart(this, container.call(this, event, d), event, d, "mouse");
if (!gesture) return;
select(event.view).on("mousemove.drag", mousemoved, nonpassivecapture).on("mouseup.drag", mouseupped, nonpassivecapture);
dragDisable(event.view);
nopropagation$1(event);
mousemoving = false;
mousedownx = event.clientX;
mousedowny = event.clientY;
gesture("start", event);
}
function mousemoved(event) {
noevent$1(event);
if (!mousemoving) {
var dx = event.clientX - mousedownx, dy = event.clientY - mousedowny;
mousemoving = dx * dx + dy * dy > clickDistance2;
}
gestures.mouse("drag", event);
}
function mouseupped(event) {
select(event.view).on("mousemove.drag mouseup.drag", null);
yesdrag(event.view, mousemoving);
noevent$1(event);
gestures.mouse("end", event);
}
function touchstarted(event, d) {
if (!filter2.call(this, event, d)) return;
var touches = event.changedTouches, c = container.call(this, event, d), n = touches.length, i$1, gesture;
for (i$1 = 0; i$1 < n; ++i$1) if (gesture = beforestart(this, c, event, d, touches[i$1].identifier, touches[i$1])) {
nopropagation$1(event);
gesture("start", event, touches[i$1]);
}
}
function touchmoved(event) {
var touches = event.changedTouches, n = touches.length, i$1, gesture;
for (i$1 = 0; i$1 < n; ++i$1) if (gesture = gestures[touches[i$1].identifier]) {
noevent$1(event);
gesture("drag", event, touches[i$1]);
}
}
function touchended(event) {
var touches = event.changedTouches, n = touches.length, i$1, gesture;
if (touchending) clearTimeout(touchending);
touchending = setTimeout(function() {
touchending = null;
}, 500);
for (i$1 = 0; i$1 < n; ++i$1) if (gesture = gestures[touches[i$1].identifier]) {
nopropagation$1(event);
gesture("end", event, touches[i$1]);
}
}
function beforestart(that, container2, event, d, identifier, touch) {
var dispatch2 = listeners$1.copy(), p = pointer(touch || event, container2), dx, dy, s;
if ((s = subject.call(that, new DragEvent("beforestart", {
sourceEvent: event,
target: drag2,
identifier,
active: active$7,
x: p[0],
y: p[1],
dx: 0,
dy: 0,
dispatch: dispatch2
}), d)) == null) return;
dx = s.x - p[0] || 0;
dy = s.y - p[1] || 0;
return function gesture(type, event2, touch2) {
var p0 = p, n;
switch (type) {
case "start":
gestures[identifier] = gesture, n = active$7++;
break;
case "end": delete gestures[identifier], --active$7;
case "drag":
p = pointer(touch2 || event2, container2), n = active$7;
break;
}
dispatch2.call(type, that, new DragEvent(type, {
sourceEvent: event2,
subject: s,
target: drag2,
identifier,
active: n,
x: p[0] + dx,
y: p[1] + dy,
dx: p[0] - p0[0],
dy: p[1] - p0[1],
dispatch: dispatch2
}), d);
};
}
drag2.filter = function(_) {
return arguments.length ? (filter2 = typeof _ === "function" ? _ : constant$2(!!_), drag2) : filter2;
};
drag2.container = function(_) {
return arguments.length ? (container = typeof _ === "function" ? _ : constant$2(_), drag2) : container;
};
drag2.subject = function(_) {
return arguments.length ? (subject = typeof _ === "function" ? _ : constant$2(_), drag2) : subject;
};
drag2.touchable = function(_) {
return arguments.length ? (touchable = typeof _ === "function" ? _ : constant$2(!!_), drag2) : touchable;
};
drag2.on = function() {
var value = listeners$1.on.apply(listeners$1, arguments);
return value === listeners$1 ? drag2 : value;
};
drag2.clickDistance = function(_) {
return arguments.length ? (clickDistance2 = (_ = +_) * _, drag2) : Math.sqrt(clickDistance2);
};
return drag2;
}
function define$1(constructor, factory, prototype) {
constructor.prototype = factory.prototype = prototype;
prototype.constructor = constructor;
}
function extend(parent, definition) {
var prototype = Object.create(parent.prototype);
for (var key in definition) prototype[key] = definition[key];
return prototype;
}
function Color() {}
var darker = .7;
var brighter = 1 / darker;
var reI = "\\s*([+-]?\\d+)\\s*", reN = "\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)\\s*", reP = "\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)%\\s*", reHex = /^#([0-9a-f]{3,8})$/, reRgbInteger = /* @__PURE__ */ new RegExp(`^rgb\\(${reI},${reI},${reI}\\)$`), reRgbPercent = /* @__PURE__ */ new RegExp(`^rgb\\(${reP},${reP},${reP}\\)$`), reRgbaInteger = /* @__PURE__ */ new RegExp(`^rgba\\(${reI},${reI},${reI},${reN}\\)$`), reRgbaPercent = /* @__PURE__ */ new RegExp(`^rgba\\(${reP},${reP},${reP},${reN}\\)$`), reHslPercent = /* @__PURE__ */ new RegExp(`^hsl\\(${reN},${reP},${reP}\\)$`), reHslaPercent = /* @__PURE__ */ new RegExp(`^hsla\\(${reN},${reP},${reP},${reN}\\)$`);
var named = {
aliceblue: 15792383,
antiquewhite: 16444375,
aqua: 65535,
aquamarine: 8388564,
azure: 15794175,
beige: 16119260,
bisque: 16770244,
black: 0,
blanchedalmond: 16772045,
blue: 255,
blueviolet: 9055202,
brown: 10824234,
burlywood: 14596231,
cadetblue: 6266528,
chartreuse: 8388352,
chocolate: 13789470,
coral: 16744272,
cornflowerblue: 6591981,
cornsilk: 16775388,
crimson: 14423100,
cyan: 65535,
darkblue: 139,
darkcyan: 35723,
darkgoldenrod: 12092939,
darkgray: 11119017,
darkgreen: 25600,
darkgrey: 11119017,
darkkhaki: 12433259,
darkmagenta: 9109643,
darkolivegreen: 5597999,
darkorange: 16747520,
darkorchid: 10040012,
darkred: 9109504,
darksalmon: 15308410,
darkseagreen: 9419919,
darkslateblue: 4734347,
darkslategray: 3100495,
darkslategrey: 3100495,
darkturquoise: 52945,
darkviolet: 9699539,
deeppink: 16716947,
deepskyblue: 49151,
dimgray: 6908265,
dimgrey: 6908265,
dodgerblue: 2003199,
firebrick: 11674146,
floralwhite: 16775920,
forestgreen: 2263842,
fuchsia: 16711935,
gainsboro: 14474460,
ghostwhite: 16316671,
gold: 16766720,
goldenrod: 14329120,
gray: 8421504,
green: 32768,
greenyellow: 11403055,
grey: 8421504,
honeydew: 15794160,
hotpink: 16738740,
indianred: 13458524,
indigo: 4915330,
ivory: 16777200,
khaki: 15787660,
lavender: 15132410,
lavenderblush: 16773365,
lawngreen: 8190976,
lemonchiffon: 16775885,
lightblue: 11393254,
lightcoral: 15761536,
lightcyan: 14745599,
lightgoldenrodyellow: 16448210,
lightgray: 13882323,
lightgreen: 9498256,
lightgrey: 13882323,
lightpink: 16758465,
lightsalmon: 16752762,
lightseagreen: 2142890,
lightskyblue: 8900346,
lightslategray: 7833753,
lightslategrey: 7833753,
lightsteelblue: 11584734,
lightyellow: 16777184,
lime: 65280,
limegreen: 3329330,
linen: 16445670,
magenta: 16711935,
maroon: 8388608,
mediumaquamarine: 6737322,
mediumblue: 205,
mediumorchid: 12211667,
mediumpurple: 9662683,
mediumseagreen: 3978097,
mediumslateblue: 8087790,
mediumspringgreen: 64154,
mediumturquoise: 4772300,
mediumvioletred: 13047173,
midnightblue: 1644912,
mintcream: 16121850,
mistyrose: 16770273,
moccasin: 16770229,
navajowhite: 16768685,
navy: 128,
oldlace: 16643558,
olive: 8421376,
olivedrab: 7048739,
orange: 16753920,
orangered: 16729344,
orchid: 14315734,
palegoldenrod: 15657130,
palegreen: 10025880,
paleturquoise: 11529966,
palevioletred: 14381203,
papayawhip: 16773077,
peachpuff: 16767673,
peru: 13468991,
pink: 16761035,
plum: 14524637,
powderblue: 11591910,
purple: 8388736,
rebeccapurple: 6697881,
red: 16711680,
rosybrown: 12357519,
royalblue: 4286945,
saddlebrown: 9127187,
salmon: 16416882,
sandybrown: 16032864,
seagreen: 3050327,
seashell: 16774638,
sienna: 10506797,
silver: 12632256,
skyblue: 8900331,
slateblue: 6970061,
slategray: 7372944,
slategrey: 7372944,
snow: 16775930,
springgreen: 65407,
steelblue: 4620980,
tan: 13808780,
teal: 32896,
thistle: 14204888,
tomato: 16737095,
turquoise: 4251856,
violet: 15631086,
wheat: 16113331,
white: 16777215,
whitesmoke: 16119285,
yellow: 16776960,
yellowgreen: 10145074
};
define$1(Color, color, {
copy(channels) {
return Object.assign(new this.constructor(), this, channels);
},
displayable() {
return this.rgb().displayable();
},
hex: color_formatHex,
formatHex: color_formatHex,
formatHex8: color_formatHex8,
formatHsl: color_formatHsl,
formatRgb: color_formatRgb,
toString: color_formatRgb
});
function color_formatHex() {
return this.rgb().formatHex();
}
function color_formatHex8() {
return this.rgb().formatHex8();
}
function color_formatHsl() {
return hslConvert(this).formatHsl();
}
function color_formatRgb() {
return this.rgb().formatRgb();
}
function color(format) {
var m, l;
format = (format + "").trim().toLowerCase();
return (m = reHex.exec(format)) ? (l = m[1].length, m = parseInt(m[1], 16), l === 6 ? rgbn(m) : l === 3 ? new Rgb(m >> 8 & 15 | m >> 4 & 240, m >> 4 & 15 | m & 240, (m & 15) << 4 | m & 15, 1) : l === 8 ? rgba(m >> 24 & 255, m >> 16 & 255, m >> 8 & 255, (m & 255) / 255) : l === 4 ? rgba(m >> 12 & 15 | m >> 8 & 240, m >> 8 & 15 | m >> 4 & 240, m >> 4 & 15 | m & 240, ((m & 15) << 4 | m & 15) / 255) : null) : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) : named.hasOwnProperty(format) ? rgbn(named[format]) : format === "transparent" ? new Rgb(NaN, NaN, NaN, 0) : null;
}
function rgbn(n) {
return new Rgb(n >> 16 & 255, n >> 8 & 255, n & 255, 1);
}
function rgba(r, g, b, a) {
if (a <= 0) r = g = b = NaN;
return new Rgb(r, g, b, a);
}
function rgbConvert(o) {
if (!(o instanceof Color)) o = color(o);
if (!o) return new Rgb();
o = o.rgb();
return new Rgb(o.r, o.g, o.b, o.opacity);
}
function rgb(r, g, b, opacity) {
return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity);
}
function Rgb(r, g, b, opacity) {
this.r = +r;
this.g = +g;
this.b = +b;
this.opacity = +opacity;
}
define$1(Rgb, rgb, extend(Color, {
brighter(k) {
k = k == null ? brighter : Math.pow(brighter, k);
return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
},
darker(k) {
k = k == null ? darker : Math.pow(darker, k);
return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
},
rgb() {
return this;
},
clamp() {
return new Rgb(clampi(this.r), clampi(this.g), clampi(this.b), clampa(this.opacity));
},
displayable() {
return -.5 <= this.r && this.r < 255.5 && -.5 <= this.g && this.g < 255.5 && -.5 <= this.b && this.b < 255.5 && 0 <= this.opacity && this.opacity <= 1;
},
hex: rgb_formatHex,
formatHex: rgb_formatHex,
formatHex8: rgb_formatHex8,
formatRgb: rgb_formatRgb,
toString: rgb_formatRgb
}));
function rgb_formatHex() {
return `#${hex(this.r)}${hex(this.g)}${hex(this.b)}`;
}
function rgb_formatHex8() {
return `#${hex(this.r)}${hex(this.g)}${hex(this.b)}${hex((isNaN(this.opacity) ? 1 : this.opacity) * 255)}`;
}
function rgb_formatRgb() {
const a = clampa(this.opacity);
return `${a === 1 ? "rgb(" : "rgba("}${clampi(this.r)}, ${clampi(this.g)}, ${clampi(this.b)}${a === 1 ? ")" : `, ${a})`}`;
}
function clampa(opacity) {
return isNaN(opacity) ? 1 : Math.max(0, Math.min(1, opacity));
}
function clampi(value) {
return Math.max(0, Math.min(255, Math.round(value) || 0));
}
function hex(value) {
value = clampi(value);
return (value < 16 ? "0" : "") + value.toString(16);
}
function hsla(h2, s, l, a) {
if (a <= 0) h2 = s = l = NaN;
else if (l <= 0 || l >= 1) h2 = s = NaN;
else if (s <= 0) h2 = NaN;
return new Hsl(h2, s, l, a);
}
function hslConvert(o) {
if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity);
if (!(o instanceof Color)) o = color(o);
if (!o) return new Hsl();
if (o instanceof Hsl) return o;
o = o.rgb();
var r = o.r / 255, g = o.g / 255, b = o.b / 255, min = Math.min(r, g, b), max = Math.max(r, g, b), h2 = NaN, s = max - min, l = (max + min) / 2;
if (s) {
if (r === max) h2 = (g - b) / s + (g < b) * 6;
else if (g === max) h2 = (b - r) / s + 2;
else h2 = (r - g) / s + 4;
s /= l < .5 ? max + min : 2 - max - min;
h2 *= 60;
} else s = l > 0 && l < 1 ? 0 : h2;
return new Hsl(h2, s, l, o.opacity);
}
function hsl(h2, s, l, opacity) {
return arguments.length === 1 ? hslConvert(h2) : new Hsl(h2, s, l, opacity == null ? 1 : opacity);
}
function Hsl(h2, s, l, opacity) {
this.h = +h2;
this.s = +s;
this.l = +l;
this.opacity = +opacity;
}
define$1(Hsl, hsl, extend(Color, {
brighter(k) {
k = k == null ? brighter : Math.pow(brighter, k);
return new Hsl(this.h, this.s, this.l * k, this.opacity);
},
darker(k) {
k = k == null ? darker : Math.pow(darker, k);
return new Hsl(this.h, this.s, this.l * k, this.opacity);
},
rgb() {
var h2 = this.h % 360 + (this.h < 0) * 360, s = isNaN(h2) || isNaN(this.s) ? 0 : this.s, l = this.l, m2 = l + (l < .5 ? l : 1 - l) * s, m1 = 2 * l - m2;
return new Rgb(hsl2rgb(h2 >= 240 ? h2 - 240 : h2 + 120, m1, m2), hsl2rgb(h2, m1, m2), hsl2rgb(h2 < 120 ? h2 + 240 : h2 - 120, m1, m2), this.opacity);
},
clamp() {
return new Hsl(clamph(this.h), clampt(this.s), clampt(this.l), clampa(this.opacity));
},
displayable() {
return (0 <= this.s && this.s <= 1 || isNaN(this.s)) && 0 <= this.l && this.l <= 1 && 0 <= this.opacity && this.opacity <= 1;
},
formatHsl() {
const a = clampa(this.opacity);
return `${a === 1 ? "hsl(" : "hsla("}${clamph(this.h)}, ${clampt(this.s) * 100}%, ${clampt(this.l) * 100}%${a === 1 ? ")" : `, ${a})`}`;
}
}));
function clamph(value) {
value = (value || 0) % 360;
return value < 0 ? value + 360 : value;
}
function clampt(value) {
return Math.max(0, Math.min(1, value || 0));
}
function hsl2rgb(h2, m1, m2) {
return (h2 < 60 ? m1 + (m2 - m1) * h2 / 60 : h2 < 180 ? m2 : h2 < 240 ? m1 + (m2 - m1) * (240 - h2) / 60 : m1) * 255;
}
var constant$1 = (x) => () => x;
function linear(a, d) {
return function(t) {
return a + t * d;
};
}
function exponential(a, b, y) {
return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function(t) {
return Math.pow(a + t * b, y);
};
}
function gamma(y) {
return (y = +y) === 1 ? nogamma : function(a, b) {
return b - a ? exponential(a, b, y) : constant$1(isNaN(a) ? b : a);
};
}
function nogamma(a, b) {
var d = b - a;
return d ? linear(a, d) : constant$1(isNaN(a) ? b : a);
}
var interpolateRgb = function rgbGamma(y) {
var color2 = gamma(y);
function rgb$1(start2, end) {
var r = color2((start2 = rgb(start2)).r, (end = rgb(end)).r), g = color2(start2.g, end.g), b = color2(start2.b, end.b), opacity = nogamma(start2.opacity, end.opacity);
return function(t) {
start2.r = r(t);
start2.g = g(t);
start2.b = b(t);
start2.opacity = opacity(t);
return start2 + "";
};
}
rgb$1.gamma = rgbGamma;
return rgb$1;
}(1);
function numberArray(a, b) {
if (!b) b = [];
var n = a ? Math.min(b.length, a.length) : 0, c = b.slice(), i$1;
return function(t) {
for (i$1 = 0; i$1 < n; ++i$1) c[i$1] = a[i$1] * (1 - t) + b[i$1] * t;
return c;
};
}
function isNumberArray(x) {
return ArrayBuffer.isView(x) && !(x instanceof DataView);
}
function genericArray(a, b) {
var nb = b ? b.length : 0, na = a ? Math.min(nb, a.length) : 0, x = new Array(na), c = new Array(nb), i$1;
for (i$1 = 0; i$1 < na; ++i$1) x[i$1] = interpolate$1(a[i$1], b[i$1]);
for (; i$1 < nb; ++i$1) c[i$1] = b[i$1];
return function(t) {
for (i$1 = 0; i$1 < na; ++i$1) c[i$1] = x[i$1](t);
return c;
};
}
function date(a, b) {
var d = /* @__PURE__ */ new Date();
return a = +a, b = +b, function(t) {
return d.setTime(a * (1 - t) + b * t), d;
};
}
function interpolateNumber(a, b) {
return a = +a, b = +b, function(t) {
return a * (1 - t) + b * t;
};
}
function object(a, b) {
var i$1 = {}, c = {}, k;
if (a === null || typeof a !== "object") a = {};
if (b === null || typeof b !== "object") b = {};
for (k in b) if (k in a) i$1[k] = interpolate$1(a[k], b[k]);
else c[k] = b[k];
return function(t) {
for (k in i$1) c[k] = i$1[k](t);
return c;
};
}
var reA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g, reB = new RegExp(reA.source, "g");
function zero(b) {
return function() {
return b;
};
}
function one(b) {
return function(t) {
return b(t) + "";
};
}
function interpolateString(a, b) {
var bi = reA.lastIndex = reB.lastIndex = 0, am, bm, bs, i$1 = -1, s = [], q = [];
a = a + "", b = b + "";
while ((am = reA.exec(a)) && (bm = reB.exec(b))) {
if ((bs = bm.index) > bi) {
bs = b.slice(bi, bs);
if (s[i$1]) s[i$1] += bs;
else s[++i$1] = bs;
}
if ((am = am[0]) === (bm = bm[0])) if (s[i$1]) s[i$1] += bm;
else s[++i$1] = bm;
else {
s[++i$1] = null;
q.push({
i: i$1,
x: interpolateNumber(am, bm)
});
}
bi = reB.lastIndex;
}
if (bi < b.length) {
bs = b.slice(bi);
if (s[i$1]) s[i$1] += bs;
else s[++i$1] = bs;
}
return s.length < 2 ? q[0] ? one(q[0].x) : zero(b) : (b = q.length, function(t) {
for (var i2 = 0, o; i2 < b; ++i2) s[(o = q[i2]).i] = o.x(t);
return s.join("");
});
}
function interpolate$1(a, b) {
var t = typeof b, c;
return b == null || t === "boolean" ? constant$1(b) : (t === "number" ? interpolateNumber : t === "string" ? (c = color(b)) ? (b = c, interpolateRgb) : interpolateString : b instanceof color ? interpolateRgb : b instanceof Date ? date : isNumberArray(b) ? numberArray : Array.isArray(b) ? genericArray : typeof b.valueOf !== "function" && typeof b.toString !== "function" || isNaN(b) ? object : interpolateNumber)(a, b);
}
var degrees = 180 / Math.PI;
var identity$1 = {
translateX: 0,
translateY: 0,
rotate: 0,
skewX: 0,
scaleX: 1,
scaleY: 1
};
function decompose(a, b, c, d, e, f) {
var scaleX, scaleY, skewX;
if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX;
if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX;
if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY;
if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX;
return {
translateX: e,
translateY: f,
rotate: Math.atan2(b, a) * degrees,
skewX: Math.atan(skewX) * degrees,
scaleX,
scaleY
};
}
var svgNode;
function parseCss(value) {
const m = new (typeof DOMMatrix === "function" ? DOMMatrix : WebKitCSSMatrix)(value + "");
return m.isIdentity ? identity$1 : decompose(m.a, m.b, m.c, m.d, m.e, m.f);
}
function parseSvg(value) {
if (value == null) return identity$1;
if (!svgNode) svgNode = document.createElementNS("http://www.w3.org/2000/svg", "g");
svgNode.setAttribute("transform", value);
if (!(value = svgNode.transform.baseVal.consolidate())) return identity$1;
value = value.matrix;
return decompose(value.a, value.b, value.c, value.d, value.e, value.f);
}
function interpolateTransform(parse$1, pxComma, pxParen, degParen) {
function pop(s) {
return s.length ? s.pop() + " " : "";
}
function translate$1(xa, ya, xb, yb, s, q) {
if (xa !== xb || ya !== yb) {
var i$1 = s.push("translate(", null, pxComma, null, pxParen);
q.push({
i: i$1 - 4,
x: interpolateNumber(xa, xb)
}, {
i: i$1 - 2,
x: interpolateNumber(ya, yb)
});
} else if (xb || yb) s.push("translate(" + xb + pxComma + yb + pxParen);
}
function rotate(a, b, s, q) {
if (a !== b) {
if (a - b > 180) b += 360;
else if (b - a > 180) a += 360;
q.push({
i: s.push(pop(s) + "rotate(", null, degParen) - 2,
x: interpolateNumber(a, b)
});
} else if (b) s.push(pop(s) + "rotate(" + b + degParen);
}
function skewX(a, b, s, q) {
if (a !== b) q.push({
i: s.push(pop(s) + "skewX(", null, degParen) - 2,
x: interpolateNumber(a, b)
});
else if (b) s.push(pop(s) + "skewX(" + b + degParen);
}
function scale(xa, ya, xb, yb, s, q) {
if (xa !== xb || ya !== yb) {
var i$1 = s.push(pop(s) + "scale(", null, ",", null, ")");
q.push({
i: i$1 - 4,
x: interpolateNumber(xa, xb)
}, {
i: i$1 - 2,
x: interpolateNumber(ya, yb)
});
} else if (xb !== 1 || yb !== 1) s.push(pop(s) + "scale(" + xb + "," + yb + ")");
}
return function(a, b) {
var s = [], q = [];
a = parse$1(a), b = parse$1(b);
translate$1(a.translateX, a.translateY, b.translateX, b.translateY, s, q);
rotate(a.rotate, b.rotate, s, q);
skewX(a.skewX, b.skewX, s, q);
scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q);
a = b = null;
return function(t) {
var i$1 = -1, n = q.length, o;
while (++i$1 < n) s[(o = q[i$1]).i] = o.x(t);
return s.join("");
};
};
}
var interpolateTransformCss = interpolateTransform(parseCss, "px, ", "px)", "deg)");
var interpolateTransformSvg = interpolateTransform(parseSvg, ", ", ")", ")");
var epsilon2 = 1e-12;
function cosh(x) {
return ((x = Math.exp(x)) + 1 / x) / 2;
}
function sinh(x) {
return ((x = Math.exp(x)) - 1 / x) / 2;
}
function tanh(x) {
return ((x = Math.exp(2 * x)) - 1) / (x + 1);
}
var interpolateZoom = function zoomRho(rho, rho2, rho4) {
function zoom2(p0, p1) {
var ux0 = p0[0], uy0 = p0[1], w0 = p0[2], ux1 = p1[0], uy1 = p1[1], w1 = p1[2], dx = ux1 - ux0, dy = uy1 - uy0, d2 = dx * dx + dy * dy, i$1, S;
if (d2 < epsilon2) {
S = Math.log(w1 / w0) / rho;
i$1 = function(t) {
return [
ux0 + t * dx,
uy0 + t * dy,
w0 * Math.exp(rho * t * S)
];
};
} else {
var d1 = Math.sqrt(d2), b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1), b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1), r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0);
S = (Math.log(Math.sqrt(b1 * b1 + 1) - b1) - r0) / rho;
i$1 = function(t) {
var s = t * S, coshr0 = cosh(r0), u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0));
return [
ux0 + u * dx,
uy0 + u * dy,
w0 * coshr0 / cosh(rho * s + r0)
];
};
}
i$1.duration = S * 1e3 * rho / Math.SQRT2;
return i$1;
}
zoom2.rho = function(_) {
var _1 = Math.max(.001, +_), _2 = _1 * _1;
return zoomRho(_1, _2, _2 * _2);
};
return zoom2;
}(Math.SQRT2, 2, 4);
var frame = 0, timeout$1 = 0, interval = 0, pokeDelay = 1e3, taskHead, taskTail, clockLast = 0, clockNow = 0, clockSkew = 0, clock = typeof performance === "object" && performance.now ? performance : Date, setFrame = typeof window === "object" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function(f) {
setTimeout(f, 17);
};
function now() {
return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew);
}
function clearNow() {
clockNow = 0;
}
function Timer() {
this._call = this._time = this._next = null;
}
Timer.prototype = timer.prototype = {
constructor: Timer,
restart: function(callback, delay, time) {
if (typeof callback !== "function") throw new TypeError("callback is not a function");
time = (time == null ? now() : +time) + (delay == null ? 0 : +delay);
if (!this._next && taskTail !== this) {
if (taskTail) taskTail._next = this;
else taskHead = this;
taskTail = this;
}
this._call = callback;
this._time = time;
sleep();
},
stop: function() {
if (this._call) {
this._call = null;
this._time = Infinity;
sleep();
}
}
};
function timer(callback, delay, time) {
var t = new Timer();
t.restart(callback, delay, time);
return t;
}
function timerFlush() {
now();
++frame;
var t = taskHead, e;
while (t) {
if ((e = clockNow - t._time) >= 0) t._call.call(void 0, e);
t = t._next;
}
--frame;
}
function wake() {
clockNow = (clockLast = clock.now()) + clockSkew;
frame = timeout$1 = 0;
try {
timerFlush();
} finally {
frame = 0;
nap();
clockNow = 0;
}
}
function poke() {
var now2 = clock.now(), delay = now2 - clockLast;
if (delay > pokeDelay) clockSkew -= delay, clockLast = now2;
}
function nap() {
var t0, t1 = taskHead, t2, time = Infinity;
while (t1) if (t1._call) {
if (time > t1._time) time = t1._time;
t0 = t1, t1 = t1._next;
} else {
t2 = t1._next, t1._next = null;
t1 = t0 ? t0._next = t2 : taskHead = t2;
}
taskTail = t0;
sleep(time);
}
function sleep(time) {
if (frame) return;
if (timeout$1) timeout$1 = clearTimeout(timeout$1);
if (time - clockNow > 24) {
if (time < Infinity) timeout$1 = setTimeout(wake, time - clock.now() - clockSkew);
if (interval) interval = clearInterval(interval);
} else {
if (!interval) clockLast = clock.now(), interval = setInterval(poke, pokeDelay);
frame = 1, setFrame(wake);
}
}
function timeout(callback, delay, time) {
var t = new Timer();
delay = delay == null ? 0 : +delay;
t.restart((elapsed) => {
t.stop();
callback(elapsed + delay);
}, delay, time);
return t;
}
var emptyOn = dispatch("start", "end", "cancel", "interrupt");
var emptyTween = [];
var CREATED = 0;
var SCHEDULED = 1;
var STARTING = 2;
var STARTED = 3;
var RUNNING = 4;
var ENDING = 5;
var ENDED = 6;
function schedule(node, name$13, id2, index, group, timing) {
var schedules = node.__transition;
if (!schedules) node.__transition = {};
else if (id2 in schedules) return;
create(node, id2, {
name: name$13,
index,
group,
on: emptyOn,
tween: emptyTween,
time: timing.time,
delay: timing.delay,
duration: timing.duration,
ease: timing.ease,
timer: null,
state: CREATED
});
}
function init(node, id2) {
var schedule2 = get(node, id2);
if (schedule2.state > CREATED) throw new Error("too late; already scheduled");
return schedule2;
}
function set(node, id2) {
var schedule2 = get(node, id2);
if (schedule2.state > STARTED) throw new Error("too late; already running");
return schedule2;
}
function get(node, id2) {
var schedule2 = node.__transition;
if (!schedule2 || !(schedule2 = schedule2[id2])) throw new Error("transition not found");
return schedule2;
}
function create(node, id2, self) {
var schedules = node.__transition, tween;
schedules[id2] = self;
self.timer = timer(schedule2, 0, self.time);
function schedule2(elapsed) {
self.state = SCHEDULED;
self.timer.restart(start2, self.delay, self.time);
if (self.delay <= elapsed) start2(elapsed - self.delay);
}
function start2(elapsed) {
var i$1, j, n, o;
if (self.state !== SCHEDULED) return stop();
for (i$1 in schedules) {
o = schedules[i$1];
if (o.name !== self.name) continue;
if (o.state === STARTED) return timeout(start2);
if (o.state === RUNNING) {
o.state = ENDED;
o.timer.stop();
o.on.call("interrupt", node, node.__data__, o.index, o.group);
delete schedules[i$1];
} else if (+i$1 < id2) {
o.state = ENDED;
o.timer.stop();
o.on.call("cancel", node, node.__data__, o.index, o.group);
delete schedules[i$1];
}
}
timeout(function() {
if (self.state === STARTED) {
self.state = RUNNING;
self.timer.restart(tick, self.delay, self.time);
tick(elapsed);
}
});
self.state = STARTING;
self.on.call("start", node, node.__data__, self.index, self.group);
if (self.state !== STARTING) return;
self.state = STARTED;
tween = new Array(n = self.tween.length);
for (i$1 = 0, j = -1; i$1 < n; ++i$1) if (o = self.tween[i$1].value.call(node, node.__data__, self.index, self.group)) tween[++j] = o;
tween.length = j + 1;
}
function tick(elapsed) {
var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1), i$1 = -1, n = tween.length;
while (++i$1 < n) tween[i$1].call(node, t);
if (self.state === ENDING) {
self.on.call("end", node, node.__data__, self.index, self.group);
stop();
}
}
function stop() {
self.state = ENDED;
self.timer.stop();
delete schedules[id2];
for (var i$1 in schedules) return;
delete node.__transition;
}
}
function interrupt(node, name$13) {
var schedules = node.__transition, schedule2, active$7, empty2 = true, i$1;
if (!schedules) return;
name$13 = name$13 == null ? null : name$13 + "";
for (i$1 in schedules) {
if ((schedule2 = schedules[i$1]).name !== name$13) {
empty2 = false;
continue;
}
active$7 = schedule2.state > STARTING && schedule2.state < ENDING;
schedule2.state = ENDED;
schedule2.timer.stop();
schedule2.on.call(active$7 ? "interrupt" : "cancel", node, node.__data__, schedule2.index, schedule2.group);
delete schedules[i$1];
}
if (empty2) delete node.__transition;
}
function selection_interrupt(name$13) {
return this.each(function() {
interrupt(this, name$13);
});
}
function tweenRemove(id2, name$13) {
var tween0, tween1;
return function() {
var schedule2 = set(this, id2), tween = schedule2.tween;
if (tween !== tween0) {
tween1 = tween0 = tween;
for (var i$1 = 0, n = tween1.length; i$1 < n; ++i$1) if (tween1[i$1].name === name$13) {
tween1 = tween1.slice();
tween1.splice(i$1, 1);
break;
}
}
schedule2.tween = tween1;
};
}
function tweenFunction(id2, name$13, value) {
var tween0, tween1;
if (typeof value !== "function") throw new Error();
return function() {
var schedule2 = set(this, id2), tween = schedule2.tween;
if (tween !== tween0) {
tween1 = (tween0 = tween).slice();
for (var t = {
name: name$13,
value
}, i$1 = 0, n = tween1.length; i$1 < n; ++i$1) if (tween1[i$1].name === name$13) {
tween1[i$1] = t;
break;
}
if (i$1 === n) tween1.push(t);
}
schedule2.tween = tween1;
};
}
function transition_tween(name$13, value) {
var id2 = this._id;
name$13 += "";
if (arguments.length < 2) {
var tween = get(this.node(), id2).tween;
for (var i$1 = 0, n = tween.length, t; i$1 < n; ++i$1) if ((t = tween[i$1]).name === name$13) return t.value;
return null;
}
return this.each((value == null ? tweenRemove : tweenFunction)(id2, name$13, value));
}
function tweenValue(transition, name$13, value) {
var id2 = transition._id;
transition.each(function() {
var schedule2 = set(this, id2);
(schedule2.value || (schedule2.value = {}))[name$13] = value.apply(this, arguments);
});
return function(node) {
return get(node, id2).value[name$13];
};
}
function interpolate(a, b) {
var c;
return (typeof b === "number" ? interpolateNumber : b instanceof color ? interpolateRgb : (c = color(b)) ? (b = c, interpolateRgb) : interpolateString)(a, b);
}
function attrRemove(name$13) {
return function() {
this.removeAttribute(name$13);
};
}
function attrRemoveNS(fullname) {
return function() {
this.removeAttributeNS(fullname.space, fullname.local);
};
}
function attrConstant(name$13, interpolate2, value1) {
var string00, string1 = value1 + "", interpolate0;
return function() {
var string0 = this.getAttribute(name$13);
return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate2(string00 = string0, value1);
};
}
function attrConstantNS(fullname, interpolate2, value1) {
var string00, string1 = value1 + "", interpolate0;
return function() {
var string0 = this.getAttributeNS(fullname.space, fullname.local);
return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate2(string00 = string0, value1);
};
}
function attrFunction(name$13, interpolate2, value) {
var string00, string10, interpolate0;
return function() {
var string0, value1 = value(this), string1;
if (value1 == null) return void this.removeAttribute(name$13);
string0 = this.getAttribute(name$13);
string1 = value1 + "";
return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate2(string00 = string0, value1));
};
}
function attrFunctionNS(fullname, interpolate2, value) {
var string00, string10, interpolate0;
return function() {
var string0, value1 = value(this), string1;
if (value1 == null) return void this.removeAttributeNS(fullname.space, fullname.local);
string0 = this.getAttributeNS(fullname.space, fullname.local);
string1 = value1 + "";
return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate2(string00 = string0, value1));
};
}
function transition_attr(name$13, value) {
var fullname = namespace(name$13), i$1 = fullname === "transform" ? interpolateTransformSvg : interpolate;
return this.attrTween(name$13, typeof value === "function" ? (fullname.local ? attrFunctionNS : attrFunction)(fullname, i$1, tweenValue(this, "attr." + name$13, value)) : value == null ? (fullname.local ? attrRemoveNS : attrRemove)(fullname) : (fullname.local ? attrConstantNS : attrConstant)(fullname, i$1, value));
}
function attrInterpolate(name$13, i$1) {
return function(t) {
this.setAttribute(name$13, i$1.call(this, t));
};
}
function attrInterpolateNS(fullname, i$1) {
return function(t) {
this.setAttributeNS(fullname.space, fullname.local, i$1.call(this, t));
};
}
function attrTweenNS(fullname, value) {
var t0, i0;
function tween() {
var i$1 = value.apply(this, arguments);
if (i$1 !== i0) t0 = (i0 = i$1) && attrInterpolateNS(fullname, i$1);
return t0;
}
tween._value = value;
return tween;
}
function attrTween(name$13, value) {
var t0, i0;
function tween() {
var i$1 = value.apply(this, arguments);
if (i$1 !== i0) t0 = (i0 = i$1) && attrInterpolate(name$13, i$1);
return t0;
}
tween._value = value;
return tween;
}
function transition_attrTween(name$13, value) {
var key = "attr." + name$13;
if (arguments.length < 2) return (key = this.tween(key)) && key._value;
if (value == null) return this.tween(key, null);
if (typeof value !== "function") throw new Error();
var fullname = namespace(name$13);
return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value));
}
function delayFunction(id2, value) {
return function() {
init(this, id2).delay = +value.apply(this, arguments);
};
}
function delayConstant(id2, value) {
return value = +value, function() {
init(this, id2).delay = value;
};
}
function transition_delay(value) {
var id2 = this._id;
return arguments.length ? this.each((typeof value === "function" ? delayFunction : delayConstant)(id2, value)) : get(this.node(), id2).delay;
}
function durationFunction(id2, value) {
return function() {
set(this, id2).duration = +value.apply(this, arguments);
};
}
function durationConstant(id2, value) {
return value = +value, function() {
set(this, id2).duration = value;
};
}
function transition_duration(value) {
var id2 = this._id;
return arguments.length ? this.each((typeof value === "function" ? durationFunction : durationConstant)(id2, value)) : get(this.node(), id2).duration;
}
function easeConstant(id2, value) {
if (typeof value !== "function") throw new Error();
return function() {
set(this, id2).ease = value;
};
}
function transition_ease(value) {
var id2 = this._id;
return arguments.length ? this.each(easeConstant(id2, value)) : get(this.node(), id2).ease;
}
function easeVarying(id2, value) {
return function() {
var v = value.apply(this, arguments);
if (typeof v !== "function") throw new Error();
set(this, id2).ease = v;
};
}
function transition_easeVarying(value) {
if (typeof value !== "function") throw new Error();
return this.each(easeVarying(this._id, value));
}
function transition_filter(match) {
if (typeof match !== "function") match = matcher(match);
for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i$1 = 0; i$1 < n; ++i$1) if ((node = group[i$1]) && match.call(node, node.__data__, i$1, group)) subgroup.push(node);
return new Transition(subgroups, this._parents, this._name, this._id);
}
function transition_merge(transition) {
if (transition._id !== this._id) throw new Error();
for (var groups0 = this._groups, groups1 = transition._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge$1 = merges[j] = new Array(n), node, i$1 = 0; i$1 < n; ++i$1) if (node = group0[i$1] || group1[i$1]) merge$1[i$1] = node;
for (; j < m0; ++j) merges[j] = groups0[j];
return new Transition(merges, this._parents, this._name, this._id);
}
function start(name$13) {
return (name$13 + "").trim().split(/^|\s+/).every(function(t) {
var i$1 = t.indexOf(".");
if (i$1 >= 0) t = t.slice(0, i$1);
return !t || t === "start";
});
}
function onFunction(id2, name$13, listener) {
var on0, on1, sit = start(name$13) ? init : set;
return function() {
var schedule2 = sit(this, id2), on = schedule2.on;
if (on !== on0) (on1 = (on0 = on).copy()).on(name$13, listener);
schedule2.on = on1;
};
}
function transition_on(name$13, listener) {
var id2 = this._id;
return arguments.length < 2 ? get(this.node(), id2).on.on(name$13) : this.each(onFunction(id2, name$13, listener));
}
function removeFunction(id2) {
return function() {
var parent = this.parentNode;
for (var i$1 in this.__transition) if (+i$1 !== id2) return;
if (parent) parent.removeChild(this);
};
}
function transition_remove() {
return this.on("end.remove", removeFunction(this._id));
}
function transition_select(select2) {
var name$13 = this._name, id2 = this._id;
if (typeof select2 !== "function") select2 = selector(select2);
for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i$1 = 0; i$1 < n; ++i$1) if ((node = group[i$1]) && (subnode = select2.call(node, node.__data__, i$1, group))) {
if ("__data__" in node) subnode.__data__ = node.__data__;
subgroup[i$1] = subnode;
schedule(subgroup[i$1], name$13, id2, i$1, subgroup, get(node, id2));
}
return new Transition(subgroups, this._parents, name$13, id2);
}
function transition_selectAll(select2) {
var name$13 = this._name, id2 = this._id;
if (typeof select2 !== "function") select2 = selectorAll(select2);
for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) for (var group = groups[j], n = group.length, node, i$1 = 0; i$1 < n; ++i$1) if (node = group[i$1]) {
for (var children2 = select2.call(node, node.__data__, i$1, group), child, inherit2 = get(node, id2), k = 0, l = children2.length; k < l; ++k) if (child = children2[k]) schedule(child, name$13, id2, k, children2, inherit2);
subgroups.push(children2);
parents.push(node);
}
return new Transition(subgroups, parents, name$13, id2);
}
var Selection = selection.prototype.constructor;
function transition_selection() {
return new Selection(this._groups, this._parents);
}
function styleNull(name$13, interpolate2) {
var string00, string10, interpolate0;
return function() {
var string0 = styleValue(this, name$13), string1 = (this.style.removeProperty(name$13), styleValue(this, name$13));
return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : interpolate0 = interpolate2(string00 = string0, string10 = string1);
};
}
function styleRemove(name$13) {
return function() {
this.style.removeProperty(name$13);
};
}
function styleConstant(name$13, interpolate2, value1) {
var string00, string1 = value1 + "", interpolate0;
return function() {
var string0 = styleValue(this, name$13);
return string0 === string1 ? null : string0 === string00 ? interpolate0 : interpolate0 = interpolate2(string00 = string0, value1);
};
}
function styleFunction(name$13, interpolate2, value) {
var string00, string10, interpolate0;
return function() {
var string0 = styleValue(this, name$13), value1 = value(this), string1 = value1 + "";
if (value1 == null) string1 = value1 = (this.style.removeProperty(name$13), styleValue(this, name$13));
return string0 === string1 ? null : string0 === string00 && string1 === string10 ? interpolate0 : (string10 = string1, interpolate0 = interpolate2(string00 = string0, value1));
};
}
function styleMaybeRemove(id2, name$13) {
var on0, on1, listener0, key = "style." + name$13, event = "end." + key, remove2;
return function() {
var schedule2 = set(this, id2), on = schedule2.on, listener = schedule2.value[key] == null ? remove2 || (remove2 = styleRemove(name$13)) : void 0;
if (on !== on0 || listener0 !== listener) (on1 = (on0 = on).copy()).on(event, listener0 = listener);
schedule2.on = on1;
};
}
function transition_style(name$13, value, priority) {
var i$1 = (name$13 += "") === "transform" ? interpolateTransformCss : interpolate;
return value == null ? this.styleTween(name$13, styleNull(name$13, i$1)).on("end.style." + name$13, styleRemove(name$13)) : typeof value === "function" ? this.styleTween(name$13, styleFunction(name$13, i$1, tweenValue(this, "style." + name$13, value))).each(styleMaybeRemove(this._id, name$13)) : this.styleTween(name$13, styleConstant(name$13, i$1, value), priority).on("end.style." + name$13, null);
}
function styleInterpolate(name$13, i$1, priority) {
return function(t) {
this.style.setProperty(name$13, i$1.call(this, t), priority);
};
}
function styleTween(name$13, value, priority) {
var t, i0;
function tween() {
var i$1 = value.apply(this, arguments);
if (i$1 !== i0) t = (i0 = i$1) && styleInterpolate(name$13, i$1, priority);
return t;
}
tween._value = value;
return tween;
}
function transition_styleTween(name$13, value, priority) {
var key = "style." + (name$13 += "");
if (arguments.length < 2) return (key = this.tween(key)) && key._value;
if (value == null) return this.tween(key, null);
if (typeof value !== "function") throw new Error();
return this.tween(key, styleTween(name$13, value, priority == null ? "" : priority));
}
function textConstant(value) {
return function() {
this.textContent = value;
};
}
function textFunction(value) {
return function() {
var value1 = value(this);
this.textContent = value1 == null ? "" : value1;
};
}
function transition_text(value) {
return this.tween("text", typeof value === "function" ? textFunction(tweenValue(this, "text", value)) : textConstant(value == null ? "" : value + ""));
}
function textInterpolate(i$1) {
return function(t) {
this.textContent = i$1.call(this, t);
};
}
function textTween(value) {
var t0, i0;
function tween() {
var i$1 = value.apply(this, arguments);
if (i$1 !== i0) t0 = (i0 = i$1) && textInterpolate(i$1);
return t0;
}
tween._value = value;
return tween;
}
function transition_textTween(value) {
var key = "text";
if (arguments.length < 1) return (key = this.tween(key)) && key._value;
if (value == null) return this.tween(key, null);
if (typeof value !== "function") throw new Error();
return this.tween(key, textTween(value));
}
function transition_transition() {
var name$13 = this._name, id0 = this._id, id1 = newId();
for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) for (var group = groups[j], n = group.length, node, i$1 = 0; i$1 < n; ++i$1) if (node = group[i$1]) {
var inherit2 = get(node, id0);
schedule(node, name$13, id1, i$1, group, {
time: inherit2.time + inherit2.delay + inherit2.duration,
delay: 0,
duration: inherit2.duration,
ease: inherit2.ease
});
}
return new Transition(groups, this._parents, name$13, id1);
}
function transition_end() {
var on0, on1, that = this, id2 = that._id, size = that.size();
return new Promise(function(resolve, reject) {
var cancel = { value: reject }, end = { value: function() {
if (--size === 0) resolve();
} };
that.each(function() {
var schedule2 = set(this, id2), on = schedule2.on;
if (on !== on0) {
on1 = (on0 = on).copy();
on1._.cancel.push(cancel);
on1._.interrupt.push(cancel);
on1._.end.push(end);
}
schedule2.on = on1;
});
if (size === 0) resolve();
});
}
var id = 0;
function Transition(groups, parents, name$13, id2) {
this._groups = groups;
this._parents = parents;
this._name = name$13;
this._id = id2;
}
function newId() {
return ++id;
}
var selection_prototype = selection.prototype;
Transition.prototype = {
constructor: Transition,
select: transition_select,
selectAll: transition_selectAll,
selectChild: selection_prototype.selectChild,
selectChildren: selection_prototype.selectChildren,
filter: transition_filter,
merge: transition_merge,
selection: transition_selection,
transition: transition_transition,
call: selection_prototype.call,
nodes: selection_prototype.nodes,
node: selection_prototype.node,
size: selection_prototype.size,
empty: selection_prototype.empty,
each: selection_prototype.each,
on: transition_on,
attr: transition_attr,
attrTween: transition_attrTween,
style: transition_style,
styleTween: transition_styleTween,
text: transition_text,
textTween: transition_textTween,
remove: transition_remove,
tween: transition_tween,
delay: transition_delay,
duration: transition_duration,
ease: transition_ease,
easeVarying: transition_easeVarying,
end: transition_end,
[Symbol.iterator]: selection_prototype[Symbol.iterator]
};
function cubicInOut(t) {
return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2;
}
var defaultTiming = {
time: null,
delay: 0,
duration: 250,
ease: cubicInOut
};
function inherit(node, id2) {
var timing;
while (!(timing = node.__transition) || !(timing = timing[id2])) if (!(node = node.parentNode)) throw new Error(`transition ${id2} not found`);
return timing;
}
function selection_transition(name$13) {
var id2, timing;
if (name$13 instanceof Transition) id2 = name$13._id, name$13 = name$13._name;
else id2 = newId(), (timing = defaultTiming).time = now(), name$13 = name$13 == null ? null : name$13 + "";
for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) for (var group = groups[j], n = group.length, node, i$1 = 0; i$1 < n; ++i$1) if (node = group[i$1]) schedule(node, name$13, id2, i$1, group, timing || inherit(node, id2));
return new Transition(groups, this._parents, name$13, id2);
}
selection.prototype.interrupt = selection_interrupt;
selection.prototype.transition = selection_transition;
var constant = (x) => () => x;
function ZoomEvent(type, { sourceEvent: sourceEvent2, target, transform, dispatch: dispatch2 }) {
Object.defineProperties(this, {
type: {
value: type,
enumerable: true,
configurable: true
},
sourceEvent: {
value: sourceEvent2,
enumerable: true,
configurable: true
},
target: {
value: target,
enumerable: true,
configurable: true
},
transform: {
value: transform,
enumerable: true,
configurable: true
},
_: { value: dispatch2 }
});
}
function Transform(k, x, y) {
this.k = k;
this.x = x;
this.y = y;
}
Transform.prototype = {
constructor: Transform,
scale: function(k) {
return k === 1 ? this : new Transform(this.k * k, this.x, this.y);
},
translate: function(x, y) {
return x === 0 & y === 0 ? this : new Transform(this.k, this.x + this.k * x, this.y + this.k * y);
},
apply: function(point) {
return [point[0] * this.k + this.x, point[1] * this.k + this.y];
},
applyX: function(x) {
return x * this.k + this.x;
},
applyY: function(y) {
return y * this.k + this.y;
},
invert: function(location$1) {
return [(location$1[0] - this.x) / this.k, (location$1[1] - this.y) / this.k];
},
invertX: function(x) {
return (x - this.x) / this.k;
},
invertY: function(y) {
return (y - this.y) / this.k;
},
rescaleX: function(x) {
return x.copy().domain(x.range().map(this.invertX, this).map(x.invert, x));
},
rescaleY: function(y) {
return y.copy().domain(y.range().map(this.invertY, this).map(y.invert, y));
},
toString: function() {
return "translate(" + this.x + "," + this.y + ") scale(" + this.k + ")";
}
};
var identity = new Transform(1, 0, 0);
Transform.prototype;
function nopropagation(event) {
event.stopImmediatePropagation();
}
function noevent(event) {
event.preventDefault();
event.stopImmediatePropagation();
}
function defaultFilter(event) {
return (!event.ctrlKey || event.type === "wheel") && !event.button;
}
function defaultExtent() {
var e = this;
if (e instanceof SVGElement) {
e = e.ownerSVGElement || e;
if (e.hasAttribute("viewBox")) {
e = e.viewBox.baseVal;
return [[e.x, e.y], [e.x + e.width, e.y + e.height]];
}
return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]];
}
return [[0, 0], [e.clientWidth, e.clientHeight]];
}
function defaultTransform() {
return this.__zoom || identity;
}
function defaultWheelDelta(event) {
return -event.deltaY * (event.deltaMode === 1 ? .05 : event.deltaMode ? 1 : .002) * (event.ctrlKey ? 10 : 1);
}
function defaultTouchable() {
return navigator.maxTouchPoints || "ontouchstart" in this;
}
function defaultConstrain(transform, extent, translateExtent) {
var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0], dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0], dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1], dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];
return transform.translate(dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1), dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1));
}
function zoom() {
var filter2 = defaultFilter, extent = defaultExtent, constrain = defaultConstrain, wheelDelta2 = defaultWheelDelta, touchable = defaultTouchable, scaleExtent = [0, Infinity], translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]], duration = 250, interpolate2 = interpolateZoom, listeners$1 = dispatch("start", "zoom", "end"), touchstarting, touchfirst, touchending, touchDelay = 500, wheelDelay = 150, clickDistance2 = 0, tapDistance = 10;
function zoom2(selection2) {
selection2.property("__zoom", defaultTransform).on("wheel.zoom", wheeled, { passive: false }).on("mousedown.zoom", mousedowned).on("dblclick.zoom", dblclicked).filter(touchable).on("touchstart.zoom", touchstarted).on("touchmove.zoom", touchmoved).on("touchend.zoom touchcancel.zoom", touchended).style("-webkit-tap-highlight-color", "rgba(0,0,0,0)");
}
zoom2.transform = function(collection, transform, point, event) {
var selection2 = collection.selection ? collection.selection() : collection;
selection2.property("__zoom", defaultTransform);
if (collection !== selection2) schedule2(collection, transform, point, event);
else selection2.interrupt().each(function() {
gesture(this, arguments).event(event).start().zoom(null, typeof transform === "function" ? transform.apply(this, arguments) : transform).end();
});
};
zoom2.scaleBy = function(selection2, k, p, event) {
zoom2.scaleTo(selection2, function() {
return this.__zoom.k * (typeof k === "function" ? k.apply(this, arguments) : k);
}, p, event);
};
zoom2.scaleTo = function(selection2, k, p, event) {
zoom2.transform(selection2, function() {
var e = extent.apply(this, arguments), t0 = this.__zoom, p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p, p1 = t0.invert(p0), k1 = typeof k === "function" ? k.apply(this, arguments) : k;
return constrain(translate$1(scale(t0, k1), p0, p1), e, translateExtent);
}, p, event);
};
zoom2.translateBy = function(selection2, x, y, event) {
zoom2.transform(selection2, function() {
return constrain(this.__zoom.translate(typeof x === "function" ? x.apply(this, arguments) : x, typeof y === "function" ? y.apply(this, arguments) : y), extent.apply(this, arguments), translateExtent);
}, null, event);
};
zoom2.translateTo = function(selection2, x, y, p, event) {
zoom2.transform(selection2, function() {
var e = extent.apply(this, arguments), t = this.__zoom, p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p;
return constrain(identity.translate(p0[0], p0[1]).scale(t.k).translate(typeof x === "function" ? -x.apply(this, arguments) : -x, typeof y === "function" ? -y.apply(this, arguments) : -y), e, translateExtent);
}, p, event);
};
function scale(transform, k) {
k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));
return k === transform.k ? transform : new Transform(k, transform.x, transform.y);
}
function translate$1(transform, p0, p1) {
var x = p0[0] - p1[0] * transform.k, y = p0[1] - p1[1] * transform.k;
return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y);
}
function centroid(extent2) {
return [(+extent2[0][0] + +extent2[1][0]) / 2, (+extent2[0][1] + +extent2[1][1]) / 2];
}
function schedule2(transition, transform, point, event) {
transition.on("start.zoom", function() {
gesture(this, arguments).event(event).start();
}).on("interrupt.zoom end.zoom", function() {
gesture(this, arguments).event(event).end();
}).tween("zoom", function() {
var that = this, args = arguments, g = gesture(that, args).event(event), e = extent.apply(that, args), p = point == null ? centroid(e) : typeof point === "function" ? point.apply(that, args) : point, w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]), a = that.__zoom, b = typeof transform === "function" ? transform.apply(that, args) : transform, i$1 = interpolate2(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
return function(t) {
if (t === 1) t = b;
else {
var l = i$1(t), k = w / l[2];
t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k);
}
g.zoom(null, t);
};
});
}
function gesture(that, args, clean) {
return !clean && that.__zooming || new Gesture(that, args);
}
function Gesture(that, args) {
this.that = that;
this.args = args;
this.active = 0;
this.sourceEvent = null;
this.extent = extent.apply(that, args);
this.taps = 0;
}
Gesture.prototype = {
event: function(event) {
if (event) this.sourceEvent = event;
return this;
},
start: function() {
if (++this.active === 1) {
this.that.__zooming = this;
this.emit("start");
}
return this;
},
zoom: function(key, transform) {
if (this.mouse && key !== "mouse") this.mouse[1] = transform.invert(this.mouse[0]);
if (this.touch0 && key !== "touch") this.touch0[1] = transform.invert(this.touch0[0]);
if (this.touch1 && key !== "touch") this.touch1[1] = transform.invert(this.touch1[0]);
this.that.__zoom = transform;
this.emit("zoom");
return this;
},
end: function() {
if (--this.active === 0) {
delete this.that.__zooming;
this.emit("end");
}
return this;
},
emit: function(type) {
var d = select(this.that).datum();
listeners$1.call(type, this.that, new ZoomEvent(type, {
sourceEvent: this.sourceEvent,
target: zoom2,
type,
transform: this.that.__zoom,
dispatch: listeners$1
}), d);
}
};
function wheeled(event, ...args) {
if (!filter2.apply(this, arguments)) return;
var g = gesture(this, args).event(event), t = this.__zoom, k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta2.apply(this, arguments)))), p = pointer(event);
if (g.wheel) {
if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) g.mouse[1] = t.invert(g.mouse[0] = p);
clearTimeout(g.wheel);
} else if (t.k === k) return;
else {
g.mouse = [p, t.invert(p)];
interrupt(this);
g.start();
}
noevent(event);
g.wheel = setTimeout(wheelidled, wheelDelay);
g.zoom("mouse", constrain(translate$1(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));
function wheelidled() {
g.wheel = null;
g.end();
}
}
function mousedowned(event, ...args) {
if (touchending || !filter2.apply(this, arguments)) return;
var currentTarget = event.currentTarget, g = gesture(this, args, true).event(event), v = select(event.view).on("mousemove.zoom", mousemoved, true).on("mouseup.zoom", mouseupped, true), p = pointer(event, currentTarget), x0 = event.clientX, y0 = event.clientY;
dragDisable(event.view);
nopropagation(event);
g.mouse = [p, this.__zoom.invert(p)];
interrupt(this);
g.start();
function mousemoved(event2) {
noevent(event2);
if (!g.moved) {
var dx = event2.clientX - x0, dy = event2.clientY - y0;
g.moved = dx * dx + dy * dy > clickDistance2;
}
g.event(event2).zoom("mouse", constrain(translate$1(g.that.__zoom, g.mouse[0] = pointer(event2, currentTarget), g.mouse[1]), g.extent, translateExtent));
}
function mouseupped(event2) {
v.on("mousemove.zoom mouseup.zoom", null);
yesdrag(event2.view, g.moved);
noevent(event2);
g.event(event2).end();
}
}
function dblclicked(event, ...args) {
if (!filter2.apply(this, arguments)) return;
var t0 = this.__zoom, p0 = pointer(event.changedTouches ? event.changedTouches[0] : event, this), p1 = t0.invert(p0), k1 = t0.k * (event.shiftKey ? .5 : 2), t1 = constrain(translate$1(scale(t0, k1), p0, p1), extent.apply(this, args), translateExtent);
noevent(event);
if (duration > 0) select(this).transition().duration(duration).call(schedule2, t1, p0, event);
else select(this).call(zoom2.transform, t1, p0, event);
}
function touchstarted(event, ...args) {
if (!filter2.apply(this, arguments)) return;
var touches = event.touches, n = touches.length, g = gesture(this, args, event.changedTouches.length === n).event(event), started, i$1, t, p;
nopropagation(event);
for (i$1 = 0; i$1 < n; ++i$1) {
t = touches[i$1], p = pointer(t, this);
p = [
p,
this.__zoom.invert(p),
t.identifier
];
if (!g.touch0) g.touch0 = p, started = true, g.taps = 1 + !!touchstarting;
else if (!g.touch1 && g.touch0[2] !== p[2]) g.touch1 = p, g.taps = 0;
}
if (touchstarting) touchstarting = clearTimeout(touchstarting);
if (started) {
if (g.taps < 2) touchfirst = p[0], touchstarting = setTimeout(function() {
touchstarting = null;
}, touchDelay);
interrupt(this);
g.start();
}
}
function touchmoved(event, ...args) {
if (!this.__zooming) return;
var g = gesture(this, args).event(event), touches = event.changedTouches, n = touches.length, i$1, t, p, l;
noevent(event);
for (i$1 = 0; i$1 < n; ++i$1) {
t = touches[i$1], p = pointer(t, this);
if (g.touch0 && g.touch0[2] === t.identifier) g.touch0[0] = p;
else if (g.touch1 && g.touch1[2] === t.identifier) g.touch1[0] = p;
}
t = g.that.__zoom;
if (g.touch1) {
var p0 = g.touch0[0], l0 = g.touch0[1], p1 = g.touch1[0], l1 = g.touch1[1], dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp, dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;
t = scale(t, Math.sqrt(dp / dl));
p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
} else if (g.touch0) p = g.touch0[0], l = g.touch0[1];
else return;
g.zoom("touch", constrain(translate$1(t, p, l), g.extent, translateExtent));
}
function touchended(event, ...args) {
if (!this.__zooming) return;
var g = gesture(this, args).event(event), touches = event.changedTouches, n = touches.length, i$1, t;
nopropagation(event);
if (touchending) clearTimeout(touchending);
touchending = setTimeout(function() {
touchending = null;
}, touchDelay);
for (i$1 = 0; i$1 < n; ++i$1) {
t = touches[i$1];
if (g.touch0 && g.touch0[2] === t.identifier) delete g.touch0;
else if (g.touch1 && g.touch1[2] === t.identifier) delete g.touch1;
}
if (g.touch1 && !g.touch0) g.touch0 = g.touch1, delete g.touch1;
if (g.touch0) g.touch0[1] = this.__zoom.invert(g.touch0[0]);
else {
g.end();
if (g.taps === 2) {
t = pointer(t, this);
if (Math.hypot(touchfirst[0] - t[0], touchfirst[1] - t[1]) < tapDistance) {
var p = select(this).on("dblclick.zoom");
if (p) p.apply(this, arguments);
}
}
}
}
zoom2.wheelDelta = function(_) {
return arguments.length ? (wheelDelta2 = typeof _ === "function" ? _ : constant(+_), zoom2) : wheelDelta2;
};
zoom2.filter = function(_) {
return arguments.length ? (filter2 = typeof _ === "function" ? _ : constant(!!_), zoom2) : filter2;
};
zoom2.touchable = function(_) {
return arguments.length ? (touchable = typeof _ === "function" ? _ : constant(!!_), zoom2) : touchable;
};
zoom2.extent = function(_) {
return arguments.length ? (extent = typeof _ === "function" ? _ : constant([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom2) : extent;
};
zoom2.scaleExtent = function(_) {
return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom2) : [scaleExtent[0], scaleExtent[1]];
};
zoom2.translateExtent = function(_) {
return arguments.length ? (translateExtent[0][0] = +_[0][0], translateExtent[1][0] = +_[1][0], translateExtent[0][1] = +_[0][1], translateExtent[1][1] = +_[1][1], zoom2) : [[translateExtent[0][0], translateExtent[0][1]], [translateExtent[1][0], translateExtent[1][1]]];
};
zoom2.constrain = function(_) {
return arguments.length ? (constrain = _, zoom2) : constrain;
};
zoom2.duration = function(_) {
return arguments.length ? (duration = +_, zoom2) : duration;
};
zoom2.interpolate = function(_) {
return arguments.length ? (interpolate2 = _, zoom2) : interpolate2;
};
zoom2.on = function() {
var value = listeners$1.on.apply(listeners$1, arguments);
return value === listeners$1 ? zoom2 : value;
};
zoom2.clickDistance = function(_) {
return arguments.length ? (clickDistance2 = (_ = +_) * _, zoom2) : Math.sqrt(clickDistance2);
};
zoom2.tapDistance = function(_) {
return arguments.length ? (tapDistance = +_, zoom2) : tapDistance;
};
return zoom2;
}
var Position = /* @__PURE__ */ ((Position2) => {
Position2["Left"] = "left";
Position2["Top"] = "top";
Position2["Right"] = "right";
Position2["Bottom"] = "bottom";
return Position2;
})(Position || {});
var SelectionMode = /* @__PURE__ */ ((SelectionMode2) => {
SelectionMode2["Partial"] = "partial";
SelectionMode2["Full"] = "full";
return SelectionMode2;
})(SelectionMode || {});
var ConnectionLineType = /* @__PURE__ */ ((ConnectionLineType2) => {
ConnectionLineType2["Bezier"] = "default";
ConnectionLineType2["SimpleBezier"] = "simple-bezier";
ConnectionLineType2["Straight"] = "straight";
ConnectionLineType2["Step"] = "step";
ConnectionLineType2["SmoothStep"] = "smoothstep";
return ConnectionLineType2;
})(ConnectionLineType || {});
var ConnectionMode = /* @__PURE__ */ ((ConnectionMode2) => {
ConnectionMode2["Strict"] = "strict";
ConnectionMode2["Loose"] = "loose";
return ConnectionMode2;
})(ConnectionMode || {});
var MarkerType = /* @__PURE__ */ ((MarkerType2) => {
MarkerType2["Arrow"] = "arrow";
MarkerType2["ArrowClosed"] = "arrowclosed";
return MarkerType2;
})(MarkerType || {});
var PanOnScrollMode = /* @__PURE__ */ ((PanOnScrollMode2) => {
PanOnScrollMode2["Free"] = "free";
PanOnScrollMode2["Vertical"] = "vertical";
PanOnScrollMode2["Horizontal"] = "horizontal";
return PanOnScrollMode2;
})(PanOnScrollMode || {});
var PanelPosition = /* @__PURE__ */ ((PanelPosition2) => {
PanelPosition2["TopLeft"] = "top-left";
PanelPosition2["TopCenter"] = "top-center";
PanelPosition2["TopRight"] = "top-right";
PanelPosition2["BottomLeft"] = "bottom-left";
PanelPosition2["BottomCenter"] = "bottom-center";
PanelPosition2["BottomRight"] = "bottom-right";
return PanelPosition2;
})(PanelPosition || {});
var inputTags = [
"INPUT",
"SELECT",
"TEXTAREA"
];
var defaultDoc = typeof document !== "undefined" ? document : null;
function isInputDOMNode(event) {
var _a, _b;
const target = ((_b = (_a = event.composedPath) == null ? void 0 : _a.call(event)) == null ? void 0 : _b[0]) || event.target;
const hasAttribute = typeof (target == null ? void 0 : target.hasAttribute) === "function" ? target.hasAttribute("contenteditable") : false;
const closest = typeof (target == null ? void 0 : target.closest) === "function" ? target.closest(".nokey") : null;
return inputTags.includes(target == null ? void 0 : target.nodeName) || hasAttribute || !!closest;
}
function wasModifierPressed(event) {
return event.ctrlKey || event.metaKey || event.shiftKey || event.altKey;
}
function isKeyMatch(pressedKey, keyToMatch, pressedKeys, isKeyUp) {
const keyCombination = keyToMatch.replace("+", "\n").replace("\n\n", "\n+").split("\n").map((k) => k.trim().toLowerCase());
if (keyCombination.length === 1) return pressedKey.toLowerCase() === keyToMatch.toLowerCase();
if (!isKeyUp) pressedKeys.add(pressedKey.toLowerCase());
const isMatch = keyCombination.every((key, index) => pressedKeys.has(key) && Array.from(pressedKeys.values())[index] === keyCombination[index]);
if (isKeyUp) pressedKeys.delete(pressedKey.toLowerCase());
return isMatch;
}
function createKeyPredicate(keyFilter, pressedKeys) {
return (event) => {
if (!event.code && !event.key) return false;
const keyOrCode = useKeyOrCode(event.code, keyFilter);
if (Array.isArray(keyFilter)) return keyFilter.some((key) => isKeyMatch(event[keyOrCode], key, pressedKeys, event.type === "keyup"));
return isKeyMatch(event[keyOrCode], keyFilter, pressedKeys, event.type === "keyup");
};
}
function useKeyOrCode(code, keysToWatch) {
return keysToWatch.includes(code) ? "code" : "key";
}
function useKeyPress(keyFilter, options) {
const target = computed(() => toValue(options == null ? void 0 : options.target) ?? defaultDoc);
const isPressed = shallowRef(toValue(keyFilter) === true);
let modifierPressed = false;
const pressedKeys = /* @__PURE__ */ new Set();
let currentFilter = createKeyFilterFn(toValue(keyFilter));
watch(() => toValue(keyFilter), (nextKeyFilter, previousKeyFilter) => {
if (typeof previousKeyFilter === "boolean" && typeof nextKeyFilter !== "boolean") reset();
currentFilter = createKeyFilterFn(nextKeyFilter);
}, { immediate: true });
useEventListener(["blur", "contextmenu"], reset);
onKeyStroke((...args) => currentFilter(...args), (e) => {
var _a, _b;
const actInsideInputWithModifier = toValue(options == null ? void 0 : options.actInsideInputWithModifier) ?? true;
const preventDefault = toValue(options == null ? void 0 : options.preventDefault) ?? false;
modifierPressed = wasModifierPressed(e);
if ((!modifierPressed || modifierPressed && !actInsideInputWithModifier) && isInputDOMNode(e)) return;
const target2 = ((_b = (_a = e.composedPath) == null ? void 0 : _a.call(e)) == null ? void 0 : _b[0]) || e.target;
const isInteractiveElement = (target2 == null ? void 0 : target2.nodeName) === "BUTTON" || (target2 == null ? void 0 : target2.nodeName) === "A";
if (!preventDefault && (modifierPressed || !isInteractiveElement)) e.preventDefault();
isPressed.value = true;
}, {
eventName: "keydown",
target
});
onKeyStroke((...args) => currentFilter(...args), (e) => {
const actInsideInputWithModifier = toValue(options == null ? void 0 : options.actInsideInputWithModifier) ?? true;
if (isPressed.value) {
if ((!modifierPressed || modifierPressed && !actInsideInputWithModifier) && isInputDOMNode(e)) return;
modifierPressed = false;
isPressed.value = false;
}
}, {
eventName: "keyup",
target
});
function reset() {
modifierPressed = false;
pressedKeys.clear();
isPressed.value = toValue(keyFilter) === true;
}
function createKeyFilterFn(keyFilter2) {
if (keyFilter2 === null) {
reset();
return () => false;
}
if (typeof keyFilter2 === "boolean") {
reset();
isPressed.value = keyFilter2;
return () => false;
}
if (Array.isArray(keyFilter2) || typeof keyFilter2 === "string") return createKeyPredicate(keyFilter2, pressedKeys);
return keyFilter2;
}
return isPressed;
}
var ARIA_NODE_DESC_KEY = "vue-flow__node-desc";
var ARIA_EDGE_DESC_KEY = "vue-flow__edge-desc";
var ARIA_LIVE_MESSAGE = "vue-flow__aria-live";
var elementSelectionKeys = [
"Enter",
" ",
"Escape"
];
var arrowKeyDiffs = {
ArrowUp: {
x: 0,
y: -1
},
ArrowDown: {
x: 0,
y: 1
},
ArrowLeft: {
x: -1,
y: 0
},
ArrowRight: {
x: 1,
y: 0
}
};
function nodeToRect(node) {
return {
...node.computedPosition || {
x: 0,
y: 0
},
width: node.dimensions.width || 0,
height: node.dimensions.height || 0
};
}
function getOverlappingArea(rectA, rectB) {
const xOverlap = Math.max(0, Math.min(rectA.x + rectA.width, rectB.x + rectB.width) - Math.max(rectA.x, rectB.x));
const yOverlap = Math.max(0, Math.min(rectA.y + rectA.height, rectB.y + rectB.height) - Math.max(rectA.y, rectB.y));
return Math.ceil(xOverlap * yOverlap);
}
function getDimensions(node) {
return {
width: node.offsetWidth,
height: node.offsetHeight
};
}
function clamp(val, min = 0, max = 1) {
return Math.min(Math.max(val, min), max);
}
function clampPosition(position, extent) {
return {
x: clamp(position.x, extent[0][0], extent[1][0]),
y: clamp(position.y, extent[0][1], extent[1][1])
};
}
function getHostForElement(element) {
const doc = element.getRootNode();
if ("elementFromPoint" in doc) return doc;
return window.document;
}
function isEdge(element) {
return element && typeof element === "object" && "id" in element && "source" in element && "target" in element;
}
function isNode(element) {
return element && typeof element === "object" && "id" in element && "position" in element && !isEdge(element);
}
function isGraphNode(element) {
return isNode(element) && "computedPosition" in element;
}
function isNumeric(n) {
return !Number.isNaN(n) && Number.isFinite(n);
}
function isRect(obj) {
return isNumeric(obj.width) && isNumeric(obj.height) && isNumeric(obj.x) && isNumeric(obj.y);
}
function parseNode(node, existingNode, parentNode) {
const initialState = {
id: node.id.toString(),
type: node.type ?? "default",
dimensions: markRaw({
width: 0,
height: 0
}),
computedPosition: markRaw({
z: 0,
...node.position
}),
handleBounds: {
source: [],
target: []
},
draggable: void 0,
selectable: void 0,
connectable: void 0,
focusable: void 0,
selected: false,
dragging: false,
resizing: false,
initialized: false,
isParent: false,
position: {
x: 0,
y: 0
},
data: isDef(node.data) ? node.data : {},
events: markRaw(isDef(node.events) ? node.events : {})
};
return Object.assign(existingNode ?? initialState, node, {
id: node.id.toString(),
parentNode
});
}
function parseEdge(edge, existingEdge, defaultEdgeOptions) {
var _a, _b;
const initialState = {
id: edge.id.toString(),
type: edge.type ?? (existingEdge == null ? void 0 : existingEdge.type) ?? "default",
source: edge.source.toString(),
target: edge.target.toString(),
sourceHandle: (_a = edge.sourceHandle) == null ? void 0 : _a.toString(),
targetHandle: (_b = edge.targetHandle) == null ? void 0 : _b.toString(),
updatable: edge.updatable ?? (defaultEdgeOptions == null ? void 0 : defaultEdgeOptions.updatable),
selectable: edge.selectable ?? (defaultEdgeOptions == null ? void 0 : defaultEdgeOptions.selectable),
focusable: edge.focusable ?? (defaultEdgeOptions == null ? void 0 : defaultEdgeOptions.focusable),
data: isDef(edge.data) ? edge.data : {},
events: markRaw(isDef(edge.events) ? edge.events : {}),
label: edge.label ?? "",
interactionWidth: edge.interactionWidth ?? (defaultEdgeOptions == null ? void 0 : defaultEdgeOptions.interactionWidth),
...defaultEdgeOptions ?? {}
};
return Object.assign(existingEdge ?? initialState, edge, { id: edge.id.toString() });
}
function getConnectedElements(nodeOrId, nodes$13, edges, dir) {
const id2 = typeof nodeOrId === "string" ? nodeOrId : nodeOrId.id;
const connectedIds = /* @__PURE__ */ new Set();
const origin = dir === "source" ? "target" : "source";
for (const edge of edges) if (edge[origin] === id2) connectedIds.add(edge[dir]);
return nodes$13.filter((n) => connectedIds.has(n.id));
}
function getOutgoers(...args) {
if (args.length === 3) {
const [nodeOrId2, nodes$13, edges] = args;
return getConnectedElements(nodeOrId2, nodes$13, edges, "target");
}
const [nodeOrId, elements] = args;
const nodeId = typeof nodeOrId === "string" ? nodeOrId : nodeOrId.id;
return elements.filter((el) => isEdge(el) && el.source === nodeId).map((edge) => elements.find((el) => isNode(el) && el.id === edge.target));
}
function getIncomers(...args) {
if (args.length === 3) {
const [nodeOrId2, nodes$13, edges] = args;
return getConnectedElements(nodeOrId2, nodes$13, edges, "source");
}
const [nodeOrId, elements] = args;
const nodeId = typeof nodeOrId === "string" ? nodeOrId : nodeOrId.id;
return elements.filter((el) => isEdge(el) && el.target === nodeId).map((edge) => elements.find((el) => isNode(el) && el.id === edge.source));
}
function getEdgeId({ source, sourceHandle, target, targetHandle }) {
return `vueflow__edge-${source}${sourceHandle ?? ""}-${target}${targetHandle ?? ""}`;
}
function connectionExists(edge, elements) {
return elements.some((el) => isEdge(el) && el.source === edge.source && el.target === edge.target && (el.sourceHandle === edge.sourceHandle || !el.sourceHandle && !edge.sourceHandle) && (el.targetHandle === edge.targetHandle || !el.targetHandle && !edge.targetHandle));
}
function rendererPointToPoint({ x, y }, { x: tx, y: ty, zoom: tScale }) {
return {
x: x * tScale + tx,
y: y * tScale + ty
};
}
function pointToRendererPoint({ x, y }, { x: tx, y: ty, zoom: tScale }, snapToGrid = false, snapGrid = [1, 1]) {
const position = {
x: (x - tx) / tScale,
y: (y - ty) / tScale
};
return snapToGrid ? snapPosition(position, snapGrid) : position;
}
function getBoundsOfBoxes(box1, box2) {
return {
x: Math.min(box1.x, box2.x),
y: Math.min(box1.y, box2.y),
x2: Math.max(box1.x2, box2.x2),
y2: Math.max(box1.y2, box2.y2)
};
}
function rectToBox({ x, y, width, height }) {
return {
x,
y,
x2: x + width,
y2: y + height
};
}
function boxToRect({ x, y, x2, y2 }) {
return {
x,
y,
width: x2 - x,
height: y2 - y
};
}
function getBoundsofRects(rect1, rect2) {
return boxToRect(getBoundsOfBoxes(rectToBox(rect1), rectToBox(rect2)));
}
function getRectOfNodes(nodes$13) {
let box = {
x: Number.POSITIVE_INFINITY,
y: Number.POSITIVE_INFINITY,
x2: Number.NEGATIVE_INFINITY,
y2: Number.NEGATIVE_INFINITY
};
for (let i$1 = 0; i$1 < nodes$13.length; i$1++) {
const node = nodes$13[i$1];
box = getBoundsOfBoxes(box, rectToBox({
...node.computedPosition,
...node.dimensions
}));
}
return boxToRect(box);
}
function getNodesInside(nodes$13, rect, viewport = {
x: 0,
y: 0,
zoom: 1
}, partially = false, excludeNonSelectableNodes = false) {
const paneRect = {
...pointToRendererPoint(rect, viewport),
width: rect.width / viewport.zoom,
height: rect.height / viewport.zoom
};
const visibleNodes = [];
for (const node of nodes$13) {
const { dimensions, selectable = true, hidden = false } = node;
const width = dimensions.width ?? node.width ?? null;
const height = dimensions.height ?? node.height ?? null;
if (excludeNonSelectableNodes && !selectable || hidden) continue;
const overlappingArea = getOverlappingArea(paneRect, nodeToRect(node));
const notInitialized = width === null || height === null;
const partiallyVisible = partially && overlappingArea > 0;
const area = (width ?? 0) * (height ?? 0);
if (notInitialized || partiallyVisible || overlappingArea >= area || node.dragging) visibleNodes.push(node);
}
return visibleNodes;
}
function getConnectedEdges(nodesOrId, edges) {
const nodeIds = /* @__PURE__ */ new Set();
if (typeof nodesOrId === "string") nodeIds.add(nodesOrId);
else if (nodesOrId.length >= 1) for (const n of nodesOrId) nodeIds.add(n.id);
return edges.filter((edge) => nodeIds.has(edge.source) || nodeIds.has(edge.target));
}
function parsePadding(padding, viewport) {
if (typeof padding === "number") return Math.floor((viewport - viewport / (1 + padding)) * .5);
if (typeof padding === "string" && padding.endsWith("px")) {
const paddingValue = Number.parseFloat(padding);
if (!Number.isNaN(paddingValue)) return Math.floor(paddingValue);
}
if (typeof padding === "string" && padding.endsWith("%")) {
const paddingValue = Number.parseFloat(padding);
if (!Number.isNaN(paddingValue)) return Math.floor(viewport * paddingValue * .01);
}
warn(`The padding value "${padding}" is invalid. Please provide a number or a string with a valid unit (px or %).`);
return 0;
}
function parsePaddings(padding, width, height) {
if (typeof padding === "string" || typeof padding === "number") {
const paddingY = parsePadding(padding, height);
const paddingX = parsePadding(padding, width);
return {
top: paddingY,
right: paddingX,
bottom: paddingY,
left: paddingX,
x: paddingX * 2,
y: paddingY * 2
};
}
if (typeof padding === "object") {
const top = parsePadding(padding.top ?? padding.y ?? 0, height);
const bottom = parsePadding(padding.bottom ?? padding.y ?? 0, height);
const left = parsePadding(padding.left ?? padding.x ?? 0, width);
const right = parsePadding(padding.right ?? padding.x ?? 0, width);
return {
top,
right,
bottom,
left,
x: left + right,
y: top + bottom
};
}
return {
top: 0,
right: 0,
bottom: 0,
left: 0,
x: 0,
y: 0
};
}
function calculateAppliedPaddings(bounds, x, y, zoom2, width, height) {
const { x: left, y: top } = rendererPointToPoint(bounds, {
x,
y,
zoom: zoom2
});
const { x: boundRight, y: boundBottom } = rendererPointToPoint({
x: bounds.x + bounds.width,
y: bounds.y + bounds.height
}, {
x,
y,
zoom: zoom2
});
const right = width - boundRight;
const bottom = height - boundBottom;
return {
left: Math.floor(left),
top: Math.floor(top),
right: Math.floor(right),
bottom: Math.floor(bottom)
};
}
function getTransformForBounds(bounds, width, height, minZoom, maxZoom, padding = .1) {
const p = parsePaddings(padding, width, height);
const xZoom = (width - p.x) / bounds.width;
const yZoom = (height - p.y) / bounds.height;
const clampedZoom = clamp(Math.min(xZoom, yZoom), minZoom, maxZoom);
const boundsCenterX = bounds.x + bounds.width / 2;
const boundsCenterY = bounds.y + bounds.height / 2;
const x = width / 2 - boundsCenterX * clampedZoom;
const y = height / 2 - boundsCenterY * clampedZoom;
const newPadding = calculateAppliedPaddings(bounds, x, y, clampedZoom, width, height);
const offset = {
left: Math.min(newPadding.left - p.left, 0),
top: Math.min(newPadding.top - p.top, 0),
right: Math.min(newPadding.right - p.right, 0),
bottom: Math.min(newPadding.bottom - p.bottom, 0)
};
return {
x: x - offset.left + offset.right,
y: y - offset.top + offset.bottom,
zoom: clampedZoom
};
}
function getXYZPos(parentPos, computedPosition) {
return {
x: computedPosition.x + parentPos.x,
y: computedPosition.y + parentPos.y,
z: (parentPos.z > computedPosition.z ? parentPos.z : computedPosition.z) + 1
};
}
function isParentSelected(node, nodeLookup) {
if (!node.parentNode) return false;
const parent = nodeLookup.get(node.parentNode);
if (!parent) return false;
if (parent.selected) return true;
return isParentSelected(parent, nodeLookup);
}
function getMarkerId(marker, vueFlowId) {
if (typeof marker === "undefined") return "";
if (typeof marker === "string") return marker;
return `${vueFlowId ? `${vueFlowId}__` : ""}${Object.keys(marker).sort().map((key) => `${key}=${marker[key]}`).join("&")}`;
}
function wheelDelta(event) {
const factor = event.ctrlKey && isMacOs() ? 10 : 1;
return -event.deltaY * (event.deltaMode === 1 ? .05 : event.deltaMode ? 1 : .002) * factor;
}
function calcAutoPanVelocity(value, min, max) {
if (value < min) return clamp(Math.abs(value - min), 1, min) / min;
if (value > max) return -clamp(Math.abs(value - max), 1, min) / min;
return 0;
}
function calcAutoPan(pos, bounds, speed = 15, distance2 = 40) {
return [calcAutoPanVelocity(pos.x, distance2, bounds.width - distance2) * speed, calcAutoPanVelocity(pos.y, distance2, bounds.height - distance2) * speed];
}
function handleParentExpand(updateItem, parent) {
if (parent) {
const extendWidth = updateItem.position.x + updateItem.dimensions.width - parent.dimensions.width;
const extendHeight = updateItem.position.y + updateItem.dimensions.height - parent.dimensions.height;
if (extendWidth > 0 || extendHeight > 0 || updateItem.position.x < 0 || updateItem.position.y < 0) {
let parentStyles = {};
if (typeof parent.style === "function") parentStyles = { ...parent.style(parent) };
else if (parent.style) parentStyles = { ...parent.style };
parentStyles.width = parentStyles.width ?? `${parent.dimensions.width}px`;
parentStyles.height = parentStyles.height ?? `${parent.dimensions.height}px`;
if (extendWidth > 0) if (typeof parentStyles.width === "string") parentStyles.width = `${Number(parentStyles.width.replace("px", "")) + extendWidth}px`;
else parentStyles.width += extendWidth;
if (extendHeight > 0) if (typeof parentStyles.height === "string") parentStyles.height = `${Number(parentStyles.height.replace("px", "")) + extendHeight}px`;
else parentStyles.height += extendHeight;
if (updateItem.position.x < 0) {
const xDiff = Math.abs(updateItem.position.x);
parent.position.x = parent.position.x - xDiff;
if (typeof parentStyles.width === "string") parentStyles.width = `${Number(parentStyles.width.replace("px", "")) + xDiff}px`;
else parentStyles.width += xDiff;
updateItem.position.x = 0;
}
if (updateItem.position.y < 0) {
const yDiff = Math.abs(updateItem.position.y);
parent.position.y = parent.position.y - yDiff;
if (typeof parentStyles.height === "string") parentStyles.height = `${Number(parentStyles.height.replace("px", "")) + yDiff}px`;
else parentStyles.height += yDiff;
updateItem.position.y = 0;
}
parent.dimensions.width = Number(parentStyles.width.toString().replace("px", ""));
parent.dimensions.height = Number(parentStyles.height.toString().replace("px", ""));
if (typeof parent.style === "function") parent.style = (p) => {
const styleFunc = parent.style;
return {
...styleFunc(p),
...parentStyles
};
};
else parent.style = {
...parent.style,
...parentStyles
};
}
}
}
function applyChanges(changes, elements) {
var _a, _b;
const addRemoveChanges = changes.filter((c) => c.type === "add" || c.type === "remove");
for (const change of addRemoveChanges) if (change.type === "add") {
if (elements.findIndex((el) => el.id === change.item.id) === -1) elements.push(change.item);
} else if (change.type === "remove") {
const index = elements.findIndex((el) => el.id === change.id);
if (index !== -1) elements.splice(index, 1);
}
const elementIds = elements.map((el) => el.id);
for (const element of elements) for (const currentChange of changes) {
if (currentChange.id !== element.id) continue;
switch (currentChange.type) {
case "select":
element.selected = currentChange.selected;
break;
case "position":
if (isGraphNode(element)) {
if (typeof currentChange.position !== "undefined") element.position = currentChange.position;
if (typeof currentChange.dragging !== "undefined") element.dragging = currentChange.dragging;
if (element.expandParent && element.parentNode) {
const parent = elements[elementIds.indexOf(element.parentNode)];
if (parent && isGraphNode(parent)) handleParentExpand(element, parent);
}
}
break;
case "dimensions":
if (isGraphNode(element)) {
if (typeof currentChange.dimensions !== "undefined") element.dimensions = currentChange.dimensions;
if (typeof currentChange.updateStyle !== "undefined" && currentChange.updateStyle) element.style = {
...element.style || {},
width: `${(_a = currentChange.dimensions) == null ? void 0 : _a.width}px`,
height: `${(_b = currentChange.dimensions) == null ? void 0 : _b.height}px`
};
if (typeof currentChange.resizing !== "undefined") element.resizing = currentChange.resizing;
if (element.expandParent && element.parentNode) {
const parent = elements[elementIds.indexOf(element.parentNode)];
if (parent && isGraphNode(parent)) if (!(!!parent.dimensions.width && !!parent.dimensions.height)) nextTick(() => {
handleParentExpand(element, parent);
});
else handleParentExpand(element, parent);
}
}
break;
}
}
return elements;
}
function createSelectionChange(id2, selected) {
return {
id: id2,
type: "select",
selected
};
}
function createAdditionChange(item) {
return {
item,
type: "add"
};
}
function createNodeRemoveChange(id2) {
return {
id: id2,
type: "remove"
};
}
function createEdgeRemoveChange(id2, source, target, sourceHandle, targetHandle) {
return {
id: id2,
source,
target,
sourceHandle: sourceHandle || null,
targetHandle: targetHandle || null,
type: "remove"
};
}
function getSelectionChanges(items, selectedIds = /* @__PURE__ */ new Set(), mutateItem = false) {
const changes = [];
for (const [id2, item] of items) {
const willBeSelected = selectedIds.has(id2);
if (!(item.selected === void 0 && !willBeSelected) && item.selected !== willBeSelected) {
if (mutateItem) item.selected = willBeSelected;
changes.push(createSelectionChange(item.id, willBeSelected));
}
}
return changes;
}
var noop$1 = () => {};
function createExtendedEventHook(defaultHandler) {
const listeners$1 = /* @__PURE__ */ new Set();
let emitter = noop$1;
let hasEmitListeners = () => false;
const hasListeners = () => listeners$1.size > 0 || hasEmitListeners();
const setEmitter = (fn) => {
emitter = fn;
};
const removeEmitter = () => {
emitter = noop$1;
};
const setHasEmitListeners = (fn) => {
hasEmitListeners = fn;
};
const removeHasEmitListeners = () => {
hasEmitListeners = () => false;
};
const off = (fn) => {
listeners$1.delete(fn);
};
const on = (fn) => {
listeners$1.add(fn);
const offFn = () => off(fn);
tryOnScopeDispose(offFn);
return { off: offFn };
};
const trigger = (param) => {
const queue = [emitter];
if (hasListeners()) queue.push(...listeners$1);
else if (defaultHandler) queue.push(defaultHandler);
return Promise.allSettled(queue.map((fn) => fn(param)));
};
return {
on,
off,
trigger,
hasListeners,
listeners: listeners$1,
setEmitter,
removeEmitter,
setHasEmitListeners,
removeHasEmitListeners
};
}
function hasSelector(target, selector2, node) {
let current = target;
do {
if (current && current.matches(selector2)) return true;
else if (current === node) return false;
current = current.parentElement;
} while (current);
return false;
}
function getDragItems(nodeLookup, nodesDraggable, mousePos, nodeId) {
var _a, _b;
const dragItems = /* @__PURE__ */ new Map();
for (const [id2, node] of nodeLookup) if ((node.selected || node.id === nodeId) && (!node.parentNode || !isParentSelected(node, nodeLookup)) && (node.draggable || nodesDraggable && typeof node.draggable === "undefined")) {
if (nodeLookup.get(id2)) dragItems.set(id2, {
id: node.id,
position: node.position || {
x: 0,
y: 0
},
distance: {
x: mousePos.x - ((_a = node.computedPosition) == null ? void 0 : _a.x) || 0,
y: mousePos.y - ((_b = node.computedPosition) == null ? void 0 : _b.y) || 0
},
from: {
x: node.computedPosition.x,
y: node.computedPosition.y
},
extent: node.extent,
parentNode: node.parentNode,
dimensions: { ...node.dimensions },
expandParent: node.expandParent
});
}
return Array.from(dragItems.values());
}
function getEventHandlerParams({ id: id2, dragItems, findNode }) {
const extendedDragItems = [];
for (const dragItem of dragItems) {
const node = findNode(dragItem.id);
if (node) extendedDragItems.push(node);
}
return [id2 ? extendedDragItems.find((n) => n.id === id2) : extendedDragItems[0], extendedDragItems];
}
function getExtentPadding(padding) {
if (Array.isArray(padding)) switch (padding.length) {
case 1: return [
padding[0],
padding[0],
padding[0],
padding[0]
];
case 2: return [
padding[0],
padding[1],
padding[0],
padding[1]
];
case 3: return [
padding[0],
padding[1],
padding[2],
padding[1]
];
case 4: return padding;
default: return [
0,
0,
0,
0
];
}
return [
padding,
padding,
padding,
padding
];
}
function getParentExtent(currentExtent, node, parent) {
const [top, right, bottom, left] = typeof currentExtent !== "string" ? getExtentPadding(currentExtent.padding) : [
0,
0,
0,
0
];
if (parent && typeof parent.computedPosition.x !== "undefined" && typeof parent.computedPosition.y !== "undefined" && typeof parent.dimensions.width !== "undefined" && typeof parent.dimensions.height !== "undefined") return [[parent.computedPosition.x + left, parent.computedPosition.y + top], [parent.computedPosition.x + parent.dimensions.width - right, parent.computedPosition.y + parent.dimensions.height - bottom]];
return false;
}
function getExtent(item, triggerError, extent, parent) {
let currentExtent = item.extent || extent;
if ((currentExtent === "parent" || !Array.isArray(currentExtent) && (currentExtent == null ? void 0 : currentExtent.range) === "parent") && !item.expandParent) if (item.parentNode && parent && item.dimensions.width && item.dimensions.height) {
const parentExtent = getParentExtent(currentExtent, item, parent);
if (parentExtent) currentExtent = parentExtent;
} else {
triggerError(new VueFlowError(ErrorCode.NODE_EXTENT_INVALID, item.id));
currentExtent = extent;
}
else if (Array.isArray(currentExtent)) {
const parentX = (parent == null ? void 0 : parent.computedPosition.x) || 0;
const parentY = (parent == null ? void 0 : parent.computedPosition.y) || 0;
currentExtent = [[currentExtent[0][0] + parentX, currentExtent[0][1] + parentY], [currentExtent[1][0] + parentX, currentExtent[1][1] + parentY]];
} else if (currentExtent !== "parent" && (currentExtent == null ? void 0 : currentExtent.range) && Array.isArray(currentExtent.range)) {
const [top, right, bottom, left] = getExtentPadding(currentExtent.padding);
const parentX = (parent == null ? void 0 : parent.computedPosition.x) || 0;
const parentY = (parent == null ? void 0 : parent.computedPosition.y) || 0;
currentExtent = [[currentExtent.range[0][0] + parentX + left, currentExtent.range[0][1] + parentY + top], [currentExtent.range[1][0] + parentX - right, currentExtent.range[1][1] + parentY - bottom]];
}
return currentExtent === "parent" ? [[Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY], [Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY]] : currentExtent;
}
function clampNodeExtent({ width, height }, extent) {
return [extent[0], [extent[1][0] - (width || 0), extent[1][1] - (height || 0)]];
}
function calcNextPosition(node, nextPosition, triggerError, nodeExtent, parentNode) {
const clampedPos = clampPosition(nextPosition, clampNodeExtent(node.dimensions, getExtent(node, triggerError, nodeExtent, parentNode)));
return {
position: {
x: clampedPos.x - ((parentNode == null ? void 0 : parentNode.computedPosition.x) || 0),
y: clampedPos.y - ((parentNode == null ? void 0 : parentNode.computedPosition.y) || 0)
},
computedPosition: clampedPos
};
}
function getHandlePosition(node, handle, fallbackPosition = Position.Left, center = false) {
const x = ((handle == null ? void 0 : handle.x) ?? 0) + node.computedPosition.x;
const y = ((handle == null ? void 0 : handle.y) ?? 0) + node.computedPosition.y;
const { width, height } = handle ?? getNodeDimensions(node);
if (center) return {
x: x + width / 2,
y: y + height / 2
};
switch ((handle == null ? void 0 : handle.position) ?? fallbackPosition) {
case Position.Top: return {
x: x + width / 2,
y
};
case Position.Right: return {
x: x + width,
y: y + height / 2
};
case Position.Bottom: return {
x: x + width / 2,
y: y + height
};
case Position.Left: return {
x,
y: y + height / 2
};
}
}
function getEdgeHandle(bounds, handleId) {
if (!bounds) return null;
return (!handleId ? bounds[0] : bounds.find((d) => d.id === handleId)) || null;
}
function isEdgeVisible({ sourcePos, targetPos, sourceWidth, sourceHeight, targetWidth, targetHeight, width, height, viewport }) {
const edgeBox = {
x: Math.min(sourcePos.x, targetPos.x),
y: Math.min(sourcePos.y, targetPos.y),
x2: Math.max(sourcePos.x + sourceWidth, targetPos.x + targetWidth),
y2: Math.max(sourcePos.y + sourceHeight, targetPos.y + targetHeight)
};
if (edgeBox.x === edgeBox.x2) edgeBox.x2 += 1;
if (edgeBox.y === edgeBox.y2) edgeBox.y2 += 1;
const viewBox = rectToBox({
x: (0 - viewport.x) / viewport.zoom,
y: (0 - viewport.y) / viewport.zoom,
width: width / viewport.zoom,
height: height / viewport.zoom
});
const xOverlap = Math.max(0, Math.min(viewBox.x2, edgeBox.x2) - Math.max(viewBox.x, edgeBox.x));
const yOverlap = Math.max(0, Math.min(viewBox.y2, edgeBox.y2) - Math.max(viewBox.y, edgeBox.y));
return Math.ceil(xOverlap * yOverlap) > 0;
}
function getEdgeZIndex(edge, findNode, elevateEdgesOnSelect = false) {
const hasZIndex = typeof edge.zIndex === "number";
let z = hasZIndex ? edge.zIndex : 0;
const source = findNode(edge.source);
const target = findNode(edge.target);
if (!source || !target) return 0;
if (elevateEdgesOnSelect) z = hasZIndex ? edge.zIndex : Math.max(source.computedPosition.z || 0, target.computedPosition.z || 0);
return z;
}
var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
ErrorCode2["MISSING_STYLES"] = "MISSING_STYLES";
ErrorCode2["MISSING_VIEWPORT_DIMENSIONS"] = "MISSING_VIEWPORT_DIMENSIONS";
ErrorCode2["NODE_INVALID"] = "NODE_INVALID";
ErrorCode2["NODE_NOT_FOUND"] = "NODE_NOT_FOUND";
ErrorCode2["NODE_MISSING_PARENT"] = "NODE_MISSING_PARENT";
ErrorCode2["NODE_TYPE_MISSING"] = "NODE_TYPE_MISSING";
ErrorCode2["NODE_EXTENT_INVALID"] = "NODE_EXTENT_INVALID";
ErrorCode2["EDGE_INVALID"] = "EDGE_INVALID";
ErrorCode2["EDGE_NOT_FOUND"] = "EDGE_NOT_FOUND";
ErrorCode2["EDGE_SOURCE_MISSING"] = "EDGE_SOURCE_MISSING";
ErrorCode2["EDGE_TARGET_MISSING"] = "EDGE_TARGET_MISSING";
ErrorCode2["EDGE_TYPE_MISSING"] = "EDGE_TYPE_MISSING";
ErrorCode2["EDGE_SOURCE_TARGET_SAME"] = "EDGE_SOURCE_TARGET_SAME";
ErrorCode2["EDGE_SOURCE_TARGET_MISSING"] = "EDGE_SOURCE_TARGET_MISSING";
ErrorCode2["EDGE_ORPHANED"] = "EDGE_ORPHANED";
ErrorCode2["USEVUEFLOW_OPTIONS"] = "USEVUEFLOW_OPTIONS";
return ErrorCode2;
})(ErrorCode || {});
var messages = {
["MISSING_STYLES"]: () => `It seems that you haven't loaded the necessary styles. Please import '@vue-flow/core/dist/style.css' to ensure that the graph is rendered correctly`,
["MISSING_VIEWPORT_DIMENSIONS"]: () => "The Vue Flow parent container needs a width and a height to render the graph",
["NODE_INVALID"]: (id2) => `Node is invalid
Node: ${id2}`,
["NODE_NOT_FOUND"]: (id2) => `Node not found
Node: ${id2}`,
["NODE_MISSING_PARENT"]: (id2, parentId) => `Node is missing a parent
Node: ${id2}
Parent: ${parentId}`,
["NODE_TYPE_MISSING"]: (type) => `Node type is missing
Type: ${type}`,
["NODE_EXTENT_INVALID"]: (id2) => `Only child nodes can use a parent extent
Node: ${id2}`,
["EDGE_INVALID"]: (id2) => `An edge needs a source and a target
Edge: ${id2}`,
["EDGE_SOURCE_MISSING"]: (id2, source) => `Edge source is missing
Edge: ${id2}
Source: ${source}`,
["EDGE_TARGET_MISSING"]: (id2, target) => `Edge target is missing
Edge: ${id2}
Target: ${target}`,
["EDGE_TYPE_MISSING"]: (type) => `Edge type is missing
Type: ${type}`,
["EDGE_SOURCE_TARGET_SAME"]: (id2, source, target) => `Edge source and target are the same
Edge: ${id2}
Source: ${source}
Target: ${target}`,
["EDGE_SOURCE_TARGET_MISSING"]: (id2, source, target) => `Edge source or target is missing
Edge: ${id2}
Source: ${source}
Target: ${target}`,
["EDGE_ORPHANED"]: (id2) => `Edge was orphaned (suddenly missing source or target) and has been removed
Edge: ${id2}`,
["EDGE_NOT_FOUND"]: (id2) => `Edge not found
Edge: ${id2}`,
["USEVUEFLOW_OPTIONS"]: () => `The options parameter is deprecated and will be removed in the next major version. Please use the id parameter instead`
};
var VueFlowError = class extends Error {
constructor(code, ...args) {
var _a;
super((_a = messages[code]) == null ? void 0 : _a.call(messages, ...args));
this.name = "VueFlowError";
this.code = code;
this.args = args;
}
};
function isMouseEvent(event) {
return "clientX" in event;
}
function isUseDragEvent(event) {
return "sourceEvent" in event;
}
function getEventPosition(event, bounds) {
const isMouse = isMouseEvent(event);
let evtX;
let evtY;
if (isMouse) {
evtX = event.clientX;
evtY = event.clientY;
} else if ("touches" in event && event.touches.length > 0) {
evtX = event.touches[0].clientX;
evtY = event.touches[0].clientY;
} else if ("changedTouches" in event && event.changedTouches.length > 0) {
evtX = event.changedTouches[0].clientX;
evtY = event.changedTouches[0].clientY;
} else {
evtX = 0;
evtY = 0;
}
return {
x: evtX - ((bounds == null ? void 0 : bounds.left) ?? 0),
y: evtY - ((bounds == null ? void 0 : bounds.top) ?? 0)
};
}
var isMacOs = () => {
var _a;
return typeof navigator !== "undefined" && ((_a = navigator == null ? void 0 : navigator.userAgent) == null ? void 0 : _a.indexOf("Mac")) >= 0;
};
function getNodeDimensions(node) {
var _a, _b;
return {
width: ((_a = node.dimensions) == null ? void 0 : _a.width) ?? node.width ?? 0,
height: ((_b = node.dimensions) == null ? void 0 : _b.height) ?? node.height ?? 0
};
}
function snapPosition(position, snapGrid = [1, 1]) {
return {
x: snapGrid[0] * Math.round(position.x / snapGrid[0]),
y: snapGrid[1] * Math.round(position.y / snapGrid[1])
};
}
var alwaysValid$1 = () => true;
function resetRecentHandle(handleDomNode) {
handleDomNode?.classList.remove("valid", "connecting", "vue-flow__handle-valid", "vue-flow__handle-connecting");
}
function getNodesWithinDistance(position, nodeLookup, distance2) {
const nodes$13 = [];
const rect = {
x: position.x - distance2,
y: position.y - distance2,
width: distance2 * 2,
height: distance2 * 2
};
for (const node of nodeLookup.values()) if (getOverlappingArea(rect, nodeToRect(node)) > 0) nodes$13.push(node);
return nodes$13;
}
var ADDITIONAL_DISTANCE = 250;
function getClosestHandle(position, connectionRadius, nodeLookup, fromHandle) {
var _a, _b;
let closestHandles = [];
let minDistance = Number.POSITIVE_INFINITY;
const closeNodes = getNodesWithinDistance(position, nodeLookup, connectionRadius + ADDITIONAL_DISTANCE);
for (const node of closeNodes) {
const allHandles = [...((_a = node.handleBounds) == null ? void 0 : _a.source) ?? [], ...((_b = node.handleBounds) == null ? void 0 : _b.target) ?? []];
for (const handle of allHandles) {
if (fromHandle.nodeId === handle.nodeId && fromHandle.type === handle.type && fromHandle.id === handle.id) continue;
const { x, y } = getHandlePosition(node, handle, handle.position, true);
const distance2 = Math.sqrt((x - position.x) ** 2 + (y - position.y) ** 2);
if (distance2 > connectionRadius) continue;
if (distance2 < minDistance) {
closestHandles = [{
...handle,
x,
y
}];
minDistance = distance2;
} else if (distance2 === minDistance) closestHandles.push({
...handle,
x,
y
});
}
}
if (!closestHandles.length) return null;
if (closestHandles.length > 1) {
const oppositeHandleType = fromHandle.type === "source" ? "target" : "source";
return closestHandles.find((handle) => handle.type === oppositeHandleType) ?? closestHandles[0];
}
return closestHandles[0];
}
function isValidHandle(event, { handle, connectionMode, fromNodeId, fromHandleId, fromType, doc, lib, flowId, isValidConnection = alwaysValid$1 }, edges, nodes$13, findNode, nodeLookup) {
const isTarget = fromType === "target";
const handleDomNode = handle ? doc.querySelector(`.${lib}-flow__handle[data-id="${flowId}-${handle == null ? void 0 : handle.nodeId}-${handle == null ? void 0 : handle.id}-${handle == null ? void 0 : handle.type}"]`) : null;
const { x, y } = getEventPosition(event);
const handleBelow = doc.elementFromPoint(x, y);
const handleToCheck = (handleBelow == null ? void 0 : handleBelow.classList.contains(`${lib}-flow__handle`)) ? handleBelow : handleDomNode;
const result = {
handleDomNode: handleToCheck,
isValid: false,
connection: null,
toHandle: null
};
if (handleToCheck) {
const handleType = getHandleType(void 0, handleToCheck);
const handleNodeId = handleToCheck.getAttribute("data-nodeid");
const handleId = handleToCheck.getAttribute("data-handleid");
const connectable = handleToCheck.classList.contains("connectable");
const connectableEnd = handleToCheck.classList.contains("connectableend");
if (!handleNodeId || !handleType) return result;
const connection = {
source: isTarget ? handleNodeId : fromNodeId,
sourceHandle: isTarget ? handleId : fromHandleId,
target: isTarget ? fromNodeId : handleNodeId,
targetHandle: isTarget ? fromHandleId : handleId
};
result.connection = connection;
result.isValid = connectable && connectableEnd && (connectionMode === ConnectionMode.Strict ? isTarget && handleType === "source" || !isTarget && handleType === "target" : handleNodeId !== fromNodeId || handleId !== fromHandleId) && isValidConnection(connection, {
nodes: nodes$13,
edges,
sourceNode: findNode(connection.source),
targetNode: findNode(connection.target)
});
result.toHandle = getHandle(handleNodeId, handleType, handleId, nodeLookup, connectionMode, true);
}
return result;
}
function getHandleType(edgeUpdaterType, handleDomNode) {
if (edgeUpdaterType) return edgeUpdaterType;
else if (handleDomNode == null ? void 0 : handleDomNode.classList.contains("target")) return "target";
else if (handleDomNode == null ? void 0 : handleDomNode.classList.contains("source")) return "source";
return null;
}
function getConnectionStatus(isInsideConnectionRadius, isHandleValid) {
let connectionStatus = null;
if (isHandleValid) connectionStatus = "valid";
else if (isInsideConnectionRadius && !isHandleValid) connectionStatus = "invalid";
return connectionStatus;
}
function isConnectionValid(isInsideConnectionRadius, isHandleValid) {
let isValid$1 = null;
if (isHandleValid) isValid$1 = true;
else if (isInsideConnectionRadius && !isHandleValid) isValid$1 = false;
return isValid$1;
}
function getHandle(nodeId, handleType, handleId, nodeLookup, connectionMode, withAbsolutePosition = false) {
var _a, _b, _c;
const node = nodeLookup.get(nodeId);
if (!node) return null;
const handles = connectionMode === ConnectionMode.Strict ? (_a = node.handleBounds) == null ? void 0 : _a[handleType] : [...((_b = node.handleBounds) == null ? void 0 : _b.source) ?? [], ...((_c = node.handleBounds) == null ? void 0 : _c.target) ?? []];
const handle = (handleId ? handles == null ? void 0 : handles.find((h2) => h2.id === handleId) : handles == null ? void 0 : handles[0]) ?? null;
return handle && withAbsolutePosition ? {
...handle,
...getHandlePosition(node, handle, handle.position, true)
} : handle;
}
var oppositePosition = {
[Position.Left]: Position.Right,
[Position.Right]: Position.Left,
[Position.Top]: Position.Bottom,
[Position.Bottom]: Position.Top
};
var productionEnvs = ["production", "prod"];
function warn(message, ...args) {
if (isDev()) console.warn(`[Vue Flow]: ${message}`, ...args);
}
function isDev() {
return !productionEnvs.includes("production");
}
function getHandleBounds(type, nodeElement, nodeBounds, zoom2, nodeId) {
const handles = nodeElement.querySelectorAll(`.vue-flow__handle.${type}`);
if (!(handles == null ? void 0 : handles.length)) return null;
return Array.from(handles).map((handle) => {
const handleBounds = handle.getBoundingClientRect();
return {
id: handle.getAttribute("data-handleid"),
type,
nodeId,
position: handle.getAttribute("data-handlepos"),
x: (handleBounds.left - nodeBounds.left) / zoom2,
y: (handleBounds.top - nodeBounds.top) / zoom2,
...getDimensions(handle)
};
});
}
function handleNodeClick(node, multiSelectionActive, addSelectedNodes, removeSelectedNodes, nodesSelectionActive, unselect = false, nodeEl) {
nodesSelectionActive.value = false;
if (!node.selected) addSelectedNodes([node]);
else if (unselect || node.selected && multiSelectionActive) {
removeSelectedNodes([node]);
nextTick(() => {
nodeEl.blur();
});
}
}
function isDef(val) {
return typeof unref(val) !== "undefined";
}
function addEdgeToStore(edgeParams, edges, triggerError, defaultEdgeOptions) {
if (!edgeParams || !edgeParams.source || !edgeParams.target) {
triggerError(new VueFlowError(ErrorCode.EDGE_INVALID, (edgeParams == null ? void 0 : edgeParams.id) ?? `[ID UNKNOWN]`));
return false;
}
let edge;
if (isEdge(edgeParams)) edge = edgeParams;
else edge = {
...edgeParams,
id: getEdgeId(edgeParams)
};
edge = parseEdge(edge, void 0, defaultEdgeOptions);
if (connectionExists(edge, edges)) return false;
return edge;
}
function updateEdgeAction(edge, newConnection, prevEdge, shouldReplaceId, triggerError) {
if (!newConnection.source || !newConnection.target) {
triggerError(new VueFlowError(ErrorCode.EDGE_INVALID, edge.id));
return false;
}
if (!prevEdge) {
triggerError(new VueFlowError(ErrorCode.EDGE_NOT_FOUND, edge.id));
return false;
}
const { id: id2,...rest } = edge;
return {
...rest,
id: shouldReplaceId ? getEdgeId(newConnection) : id2,
source: newConnection.source,
target: newConnection.target,
sourceHandle: newConnection.sourceHandle,
targetHandle: newConnection.targetHandle
};
}
function createGraphNodes(nodes$13, findNode, triggerError) {
const parentNodes = {};
const nextNodes = [];
for (let i$1 = 0; i$1 < nodes$13.length; ++i$1) {
const node = nodes$13[i$1];
if (!isNode(node)) {
triggerError(new VueFlowError(ErrorCode.NODE_INVALID, node == null ? void 0 : node.id));
continue;
}
const parsed = parseNode(node, findNode(node.id), node.parentNode);
if (node.parentNode) parentNodes[node.parentNode] = true;
nextNodes[i$1] = parsed;
}
for (const node of nextNodes) {
const parentNode = findNode(node.parentNode) || nextNodes.find((n) => n.id === node.parentNode);
if (node.parentNode && !parentNode) triggerError(new VueFlowError(ErrorCode.NODE_MISSING_PARENT, node.id, node.parentNode));
if (node.parentNode || parentNodes[node.id]) {
if (parentNodes[node.id]) node.isParent = true;
if (parentNode) parentNode.isParent = true;
}
}
return nextNodes;
}
function addConnectionToLookup(type, connection, connectionKey, connectionLookup, nodeId, handleId) {
let key = nodeId;
const nodeMap = connectionLookup.get(key) || /* @__PURE__ */ new Map();
connectionLookup.set(key, nodeMap.set(connectionKey, connection));
key = `${nodeId}-${type}`;
const typeMap = connectionLookup.get(key) || /* @__PURE__ */ new Map();
connectionLookup.set(key, typeMap.set(connectionKey, connection));
if (handleId) {
key = `${nodeId}-${type}-${handleId}`;
const handleMap = connectionLookup.get(key) || /* @__PURE__ */ new Map();
connectionLookup.set(key, handleMap.set(connectionKey, connection));
}
}
function updateConnectionLookup(connectionLookup, edgeLookup, edges) {
connectionLookup.clear();
for (const edge of edges) {
const { source: sourceNode, target: targetNode, sourceHandle = null, targetHandle = null } = edge;
const connection = {
edgeId: edge.id,
source: sourceNode,
target: targetNode,
sourceHandle,
targetHandle
};
const sourceKey = `${sourceNode}-${sourceHandle}--${targetNode}-${targetHandle}`;
addConnectionToLookup("source", connection, `${targetNode}-${targetHandle}--${sourceNode}-${sourceHandle}`, connectionLookup, sourceNode, sourceHandle);
addConnectionToLookup("target", connection, sourceKey, connectionLookup, targetNode, targetHandle);
}
}
function areSetsEqual(a, b) {
if (a.size !== b.size) return false;
for (const item of a) if (!b.has(item)) return false;
return true;
}
function createGraphEdges(nextEdges, isValidConnection, findNode, findEdge, onError, defaultEdgeOptions, nodes$13, edges) {
const validEdges = [];
for (const edgeOrConnection of nextEdges) {
const edge = isEdge(edgeOrConnection) ? edgeOrConnection : addEdgeToStore(edgeOrConnection, edges, onError, defaultEdgeOptions);
if (!edge) continue;
const sourceNode = findNode(edge.source);
const targetNode = findNode(edge.target);
if (!sourceNode || !targetNode) {
onError(new VueFlowError(ErrorCode.EDGE_SOURCE_TARGET_MISSING, edge.id, edge.source, edge.target));
continue;
}
if (!sourceNode) {
onError(new VueFlowError(ErrorCode.EDGE_SOURCE_MISSING, edge.id, edge.source));
continue;
}
if (!targetNode) {
onError(new VueFlowError(ErrorCode.EDGE_TARGET_MISSING, edge.id, edge.target));
continue;
}
if (isValidConnection) {
if (!isValidConnection(edge, {
edges,
nodes: nodes$13,
sourceNode,
targetNode
})) {
onError(new VueFlowError(ErrorCode.EDGE_INVALID, edge.id));
continue;
}
}
const existingEdge = findEdge(edge.id);
validEdges.push({
...parseEdge(edge, existingEdge, defaultEdgeOptions),
sourceNode,
targetNode
});
}
return validEdges;
}
var VueFlow = Symbol("vueFlow");
var NodeId = Symbol("nodeId");
var NodeRef = Symbol("nodeRef");
var EdgeId = Symbol("edgeId");
var EdgeRef = Symbol("edgeRef");
var Slots = Symbol("slots");
function useDrag(params) {
const { vueFlowRef, snapToGrid, snapGrid, noDragClassName, nodeLookup, nodeExtent, nodeDragThreshold, viewport, autoPanOnNodeDrag, autoPanSpeed, nodesDraggable, panBy, findNode, multiSelectionActive, nodesSelectionActive, selectNodesOnDrag, removeSelectedElements, addSelectedNodes, updateNodePositions, emits } = useVueFlow();
const { onStart, onDrag, onStop, onClick, el, disabled, id: id2, selectable, dragHandle } = params;
const dragging = shallowRef(false);
let dragItems = [];
let dragHandler;
let containerBounds = null;
let lastPos = {
x: void 0,
y: void 0
};
let mousePosition = {
x: 0,
y: 0
};
let dragEvent = null;
let dragStarted = false;
let nodePositionsChanged = false;
let autoPanId = 0;
let autoPanStarted = false;
const getPointerPosition = useGetPointerPosition();
const updateNodes = ({ x, y }) => {
lastPos = {
x,
y
};
let hasChange = false;
dragItems = dragItems.map((n) => {
const nextPosition = {
x: x - n.distance.x,
y: y - n.distance.y
};
const { computedPosition } = calcNextPosition(n, snapToGrid.value ? snapPosition(nextPosition, snapGrid.value) : nextPosition, emits.error, nodeExtent.value, n.parentNode ? findNode(n.parentNode) : void 0);
hasChange = hasChange || n.position.x !== computedPosition.x || n.position.y !== computedPosition.y;
n.position = computedPosition;
return n;
});
nodePositionsChanged = nodePositionsChanged || hasChange;
if (!hasChange) return;
updateNodePositions(dragItems, true, true);
dragging.value = true;
if (dragEvent) {
const [currentNode, nodes$13] = getEventHandlerParams({
id: id2,
dragItems,
findNode
});
onDrag({
event: dragEvent,
node: currentNode,
nodes: nodes$13
});
}
};
const autoPan = () => {
if (!containerBounds) return;
const [xMovement, yMovement] = calcAutoPan(mousePosition, containerBounds, autoPanSpeed.value);
if (xMovement !== 0 || yMovement !== 0) {
const nextPos = {
x: (lastPos.x ?? 0) - xMovement / viewport.value.zoom,
y: (lastPos.y ?? 0) - yMovement / viewport.value.zoom
};
if (panBy({
x: xMovement,
y: yMovement
})) updateNodes(nextPos);
}
autoPanId = requestAnimationFrame(autoPan);
};
const startDrag = (event, nodeEl) => {
dragStarted = true;
const node = findNode(id2);
if (!selectNodesOnDrag.value && !multiSelectionActive.value && node) {
if (!node.selected) removeSelectedElements();
}
if (node && toValue(selectable) && selectNodesOnDrag.value) handleNodeClick(node, multiSelectionActive.value, addSelectedNodes, removeSelectedElements, nodesSelectionActive, false, nodeEl);
const pointerPos = getPointerPosition(event.sourceEvent);
lastPos = pointerPos;
dragItems = getDragItems(nodeLookup.value, nodesDraggable.value, pointerPos, id2);
if (dragItems.length) {
const [currentNode, nodes$13] = getEventHandlerParams({
id: id2,
dragItems,
findNode
});
onStart({
event: event.sourceEvent,
node: currentNode,
nodes: nodes$13
});
}
};
const eventStart = (event, nodeEl) => {
var _a;
if (event.sourceEvent.type === "touchmove" && event.sourceEvent.touches.length > 1) return;
nodePositionsChanged = false;
if (nodeDragThreshold.value === 0) startDrag(event, nodeEl);
lastPos = getPointerPosition(event.sourceEvent);
containerBounds = ((_a = vueFlowRef.value) == null ? void 0 : _a.getBoundingClientRect()) || null;
mousePosition = getEventPosition(event.sourceEvent, containerBounds);
};
const eventDrag = (event, nodeEl) => {
const pointerPos = getPointerPosition(event.sourceEvent);
if (!autoPanStarted && dragStarted && autoPanOnNodeDrag.value) {
autoPanStarted = true;
autoPan();
}
if (!dragStarted) {
const x = pointerPos.xSnapped - (lastPos.x ?? 0);
const y = pointerPos.ySnapped - (lastPos.y ?? 0);
if (Math.sqrt(x * x + y * y) > nodeDragThreshold.value) startDrag(event, nodeEl);
}
if ((lastPos.x !== pointerPos.xSnapped || lastPos.y !== pointerPos.ySnapped) && dragItems.length && dragStarted) {
dragEvent = event.sourceEvent;
mousePosition = getEventPosition(event.sourceEvent, containerBounds);
updateNodes(pointerPos);
}
};
const eventEnd = (event) => {
let isClick = false;
if (!dragStarted && !dragging.value && !multiSelectionActive.value) {
const evt = event.sourceEvent;
const pointerPos = getPointerPosition(evt);
const x = pointerPos.xSnapped - (lastPos.x ?? 0);
const y = pointerPos.ySnapped - (lastPos.y ?? 0);
const distance2 = Math.sqrt(x * x + y * y);
if (distance2 !== 0 && distance2 <= nodeDragThreshold.value) {
onClick?.(evt);
isClick = true;
}
}
if (dragItems.length && !isClick) {
if (nodePositionsChanged) {
updateNodePositions(dragItems, false, false);
nodePositionsChanged = false;
}
const [currentNode, nodes$13] = getEventHandlerParams({
id: id2,
dragItems,
findNode
});
onStop({
event: event.sourceEvent,
node: currentNode,
nodes: nodes$13
});
}
dragItems = [];
dragging.value = false;
autoPanStarted = false;
dragStarted = false;
lastPos = {
x: void 0,
y: void 0
};
cancelAnimationFrame(autoPanId);
};
watch([() => toValue(disabled), el], ([isDisabled, nodeEl], _, onCleanup) => {
if (nodeEl) {
const selection2 = select(nodeEl);
if (!isDisabled) {
dragHandler = drag().on("start", (event) => eventStart(event, nodeEl)).on("drag", (event) => eventDrag(event, nodeEl)).on("end", (event) => eventEnd(event)).filter((event) => {
const target = event.target;
const unrefDragHandle = toValue(dragHandle);
return !event.button && (!noDragClassName.value || !hasSelector(target, `.${noDragClassName.value}`, nodeEl) && (!unrefDragHandle || hasSelector(target, unrefDragHandle, nodeEl)));
});
selection2.call(dragHandler);
}
onCleanup(() => {
selection2.on(".drag", null);
if (dragHandler) {
dragHandler.on("start", null);
dragHandler.on("drag", null);
dragHandler.on("end", null);
}
});
}
});
return dragging;
}
function createEdgeHooks() {
return {
doubleClick: createExtendedEventHook(),
click: createExtendedEventHook(),
mouseEnter: createExtendedEventHook(),
mouseMove: createExtendedEventHook(),
mouseLeave: createExtendedEventHook(),
contextMenu: createExtendedEventHook(),
updateStart: createExtendedEventHook(),
update: createExtendedEventHook(),
updateEnd: createExtendedEventHook()
};
}
function useEdgeHooks(edge, emits) {
const edgeHooks = createEdgeHooks();
edgeHooks.doubleClick.on((event) => {
var _a, _b;
emits.edgeDoubleClick(event);
(_b = (_a = edge.events) == null ? void 0 : _a.doubleClick) == null || _b.call(_a, event);
});
edgeHooks.click.on((event) => {
var _a, _b;
emits.edgeClick(event);
(_b = (_a = edge.events) == null ? void 0 : _a.click) == null || _b.call(_a, event);
});
edgeHooks.mouseEnter.on((event) => {
var _a, _b;
emits.edgeMouseEnter(event);
(_b = (_a = edge.events) == null ? void 0 : _a.mouseEnter) == null || _b.call(_a, event);
});
edgeHooks.mouseMove.on((event) => {
var _a, _b;
emits.edgeMouseMove(event);
(_b = (_a = edge.events) == null ? void 0 : _a.mouseMove) == null || _b.call(_a, event);
});
edgeHooks.mouseLeave.on((event) => {
var _a, _b;
emits.edgeMouseLeave(event);
(_b = (_a = edge.events) == null ? void 0 : _a.mouseLeave) == null || _b.call(_a, event);
});
edgeHooks.contextMenu.on((event) => {
var _a, _b;
emits.edgeContextMenu(event);
(_b = (_a = edge.events) == null ? void 0 : _a.contextMenu) == null || _b.call(_a, event);
});
edgeHooks.updateStart.on((event) => {
var _a, _b;
emits.edgeUpdateStart(event);
(_b = (_a = edge.events) == null ? void 0 : _a.updateStart) == null || _b.call(_a, event);
});
edgeHooks.update.on((event) => {
var _a, _b;
emits.edgeUpdate(event);
(_b = (_a = edge.events) == null ? void 0 : _a.update) == null || _b.call(_a, event);
});
edgeHooks.updateEnd.on((event) => {
var _a, _b;
emits.edgeUpdateEnd(event);
(_b = (_a = edge.events) == null ? void 0 : _a.updateEnd) == null || _b.call(_a, event);
});
return Object.entries(edgeHooks).reduce((hooks, [key, value]) => {
hooks.emit[key] = value.trigger;
hooks.on[key] = value.on;
return hooks;
}, {
emit: {},
on: {}
});
}
function useGetPointerPosition() {
const { viewport, snapGrid, snapToGrid, vueFlowRef } = useVueFlow();
return (event) => {
var _a;
const containerBounds = ((_a = vueFlowRef.value) == null ? void 0 : _a.getBoundingClientRect()) ?? {
left: 0,
top: 0
};
const { x, y } = getEventPosition(isUseDragEvent(event) ? event.sourceEvent : event, containerBounds);
const pointerPos = pointToRendererPoint({
x,
y
}, viewport.value);
const { x: xSnapped, y: ySnapped } = snapToGrid.value ? snapPosition(pointerPos, snapGrid.value) : pointerPos;
return {
xSnapped,
ySnapped,
...pointerPos
};
};
}
function alwaysValid() {
return true;
}
function useHandle({ handleId, nodeId, type, isValidConnection, edgeUpdaterType, onEdgeUpdate, onEdgeUpdateEnd }) {
const { id: flowId, vueFlowRef, connectionMode, connectionRadius, connectOnClick, connectionClickStartHandle, nodesConnectable, autoPanOnConnect, autoPanSpeed, findNode, panBy, startConnection, updateConnection, endConnection, emits, viewport, edges, nodes: nodes$13, isValidConnection: isValidConnectionProp, nodeLookup } = useVueFlow();
let connection = null;
let isValid$1 = false;
let handleDomNode = null;
function handlePointerDown(event) {
var _a;
const isTarget = toValue(type) === "target";
const isMouseTriggered = isMouseEvent(event);
const doc = getHostForElement(event.target);
const clickedHandle = event.currentTarget;
if (clickedHandle && (isMouseTriggered && event.button === 0 || !isMouseTriggered)) {
let onPointerMove = function(event2) {
connectionPosition = getEventPosition(event2, containerBounds);
closestHandle = getClosestHandle(pointToRendererPoint(connectionPosition, viewport.value, false, [1, 1]), connectionRadius.value, nodeLookup.value, fromHandle);
if (!autoPanStarted) {
autoPan();
autoPanStarted = true;
}
const result = isValidHandle(event2, {
handle: closestHandle,
connectionMode: connectionMode.value,
fromNodeId: toValue(nodeId),
fromHandleId: toValue(handleId),
fromType: isTarget ? "target" : "source",
isValidConnection: isValidConnectionHandler,
doc,
lib: "vue",
flowId,
nodeLookup: nodeLookup.value
}, edges.value, nodes$13.value, findNode, nodeLookup.value);
handleDomNode = result.handleDomNode;
connection = result.connection;
isValid$1 = isConnectionValid(!!closestHandle, result.isValid);
const newConnection2 = {
...previousConnection,
isValid: isValid$1,
to: result.toHandle && isValid$1 ? rendererPointToPoint({
x: result.toHandle.x,
y: result.toHandle.y
}, viewport.value) : connectionPosition,
toHandle: result.toHandle,
toPosition: isValid$1 && result.toHandle ? result.toHandle.position : oppositePosition[fromHandle.position],
toNode: result.toHandle ? nodeLookup.value.get(result.toHandle.nodeId) : null
};
if (isValid$1 && closestHandle && (previousConnection == null ? void 0 : previousConnection.toHandle) && newConnection2.toHandle && previousConnection.toHandle.type === newConnection2.toHandle.type && previousConnection.toHandle.nodeId === newConnection2.toHandle.nodeId && previousConnection.toHandle.id === newConnection2.toHandle.id && previousConnection.to.x === newConnection2.to.x && previousConnection.to.y === newConnection2.to.y) return;
const connectingHandle = closestHandle ?? result.toHandle;
updateConnection(connectingHandle && isValid$1 ? rendererPointToPoint({
x: connectingHandle.x,
y: connectingHandle.y
}, viewport.value) : connectionPosition, result.toHandle, getConnectionStatus(!!connectingHandle, isValid$1));
previousConnection = newConnection2;
if (!closestHandle && !isValid$1 && !handleDomNode) return resetRecentHandle(prevActiveHandle);
if (connection && connection.source !== connection.target && handleDomNode) {
resetRecentHandle(prevActiveHandle);
prevActiveHandle = handleDomNode;
handleDomNode.classList.add("connecting", "vue-flow__handle-connecting");
handleDomNode.classList.toggle("valid", !!isValid$1);
handleDomNode.classList.toggle("vue-flow__handle-valid", !!isValid$1);
}
}, onPointerUp = function(event2) {
if ("touches" in event2 && event2.touches.length > 0) return;
if ((closestHandle || handleDomNode) && connection && isValid$1) if (!onEdgeUpdate) emits.connect(connection);
else onEdgeUpdate(event2, connection);
emits.connectEnd(event2);
if (edgeUpdaterType) onEdgeUpdateEnd?.(event2);
resetRecentHandle(prevActiveHandle);
cancelAnimationFrame(autoPanId);
endConnection(event2);
autoPanStarted = false;
isValid$1 = false;
connection = null;
handleDomNode = null;
doc.removeEventListener("mousemove", onPointerMove);
doc.removeEventListener("mouseup", onPointerUp);
doc.removeEventListener("touchmove", onPointerMove);
doc.removeEventListener("touchend", onPointerUp);
};
const node = findNode(toValue(nodeId));
let isValidConnectionHandler = toValue(isValidConnection) || isValidConnectionProp.value || alwaysValid;
if (!isValidConnectionHandler && node) isValidConnectionHandler = (!isTarget ? node.isValidTargetPos : node.isValidSourcePos) || alwaysValid;
let closestHandle;
let autoPanId = 0;
const { x, y } = getEventPosition(event);
const handleType = getHandleType(toValue(edgeUpdaterType), clickedHandle);
const containerBounds = (_a = vueFlowRef.value) == null ? void 0 : _a.getBoundingClientRect();
if (!containerBounds || !handleType) return;
const fromHandleInternal = getHandle(toValue(nodeId), handleType, toValue(handleId), nodeLookup.value, connectionMode.value);
if (!fromHandleInternal) return;
let prevActiveHandle;
let connectionPosition = getEventPosition(event, containerBounds);
let autoPanStarted = false;
const autoPan = () => {
if (!autoPanOnConnect.value) return;
const [xMovement, yMovement] = calcAutoPan(connectionPosition, containerBounds, autoPanSpeed.value);
panBy({
x: xMovement,
y: yMovement
});
autoPanId = requestAnimationFrame(autoPan);
};
const fromHandle = {
...fromHandleInternal,
nodeId: toValue(nodeId),
type: handleType,
position: fromHandleInternal.position
};
const fromNodeInternal = nodeLookup.value.get(toValue(nodeId));
const newConnection = {
inProgress: true,
isValid: null,
from: getHandlePosition(fromNodeInternal, fromHandle, Position.Left, true),
fromHandle,
fromPosition: fromHandle.position,
fromNode: fromNodeInternal,
to: connectionPosition,
toHandle: null,
toPosition: oppositePosition[fromHandle.position],
toNode: null
};
startConnection({
nodeId: toValue(nodeId),
id: toValue(handleId),
type: handleType,
position: (clickedHandle == null ? void 0 : clickedHandle.getAttribute("data-handlepos")) || Position.Top,
...connectionPosition
}, {
x: x - containerBounds.left,
y: y - containerBounds.top
});
emits.connectStart({
event,
nodeId: toValue(nodeId),
handleId: toValue(handleId),
handleType
});
let previousConnection = newConnection;
doc.addEventListener("mousemove", onPointerMove);
doc.addEventListener("mouseup", onPointerUp);
doc.addEventListener("touchmove", onPointerMove);
doc.addEventListener("touchend", onPointerUp);
}
}
function handleClick(event) {
var _a, _b;
if (!connectOnClick.value) return;
const isTarget = toValue(type) === "target";
if (!connectionClickStartHandle.value) {
emits.clickConnectStart({
event,
nodeId: toValue(nodeId),
handleId: toValue(handleId)
});
startConnection({
nodeId: toValue(nodeId),
type: toValue(type),
id: toValue(handleId),
position: Position.Top,
...getEventPosition(event)
}, void 0, true);
return;
}
let isValidConnectionHandler = toValue(isValidConnection) || isValidConnectionProp.value || alwaysValid;
const node = findNode(toValue(nodeId));
if (!isValidConnectionHandler && node) isValidConnectionHandler = (!isTarget ? node.isValidTargetPos : node.isValidSourcePos) || alwaysValid;
if (node && (typeof node.connectable === "undefined" ? nodesConnectable.value : node.connectable) === false) return;
const doc = getHostForElement(event.target);
const result = isValidHandle(event, {
handle: {
nodeId: toValue(nodeId),
id: toValue(handleId),
type: toValue(type),
position: Position.Top,
...getEventPosition(event)
},
connectionMode: connectionMode.value,
fromNodeId: connectionClickStartHandle.value.nodeId,
fromHandleId: connectionClickStartHandle.value.id ?? null,
fromType: connectionClickStartHandle.value.type,
isValidConnection: isValidConnectionHandler,
doc,
lib: "vue",
flowId,
nodeLookup: nodeLookup.value
}, edges.value, nodes$13.value, findNode, nodeLookup.value);
const isOwnHandle = ((_a = result.connection) == null ? void 0 : _a.source) === ((_b = result.connection) == null ? void 0 : _b.target);
if (result.isValid && result.connection && !isOwnHandle) emits.connect(result.connection);
emits.clickConnectEnd(event);
endConnection(event, true);
}
return {
handlePointerDown,
handleClick
};
}
function useNodeId() {
return inject(NodeId, "");
}
function useNode(id2) {
const nodeId = id2 ?? useNodeId() ?? "";
const nodeEl = inject(NodeRef, ref(null));
const { findNode, edges, emits } = useVueFlow();
const node = findNode(nodeId);
if (!node) emits.error(new VueFlowError(ErrorCode.NODE_NOT_FOUND, nodeId));
return {
id: nodeId,
nodeEl,
node,
parentNode: computed(() => findNode(node.parentNode)),
connectedEdges: computed(() => getConnectedEdges([node], edges.value))
};
}
function createNodeHooks() {
return {
doubleClick: createExtendedEventHook(),
click: createExtendedEventHook(),
mouseEnter: createExtendedEventHook(),
mouseMove: createExtendedEventHook(),
mouseLeave: createExtendedEventHook(),
contextMenu: createExtendedEventHook(),
dragStart: createExtendedEventHook(),
drag: createExtendedEventHook(),
dragStop: createExtendedEventHook()
};
}
function useNodeHooks(node, emits) {
const nodeHooks = createNodeHooks();
nodeHooks.doubleClick.on((event) => {
var _a, _b;
emits.nodeDoubleClick(event);
(_b = (_a = node.events) == null ? void 0 : _a.doubleClick) == null || _b.call(_a, event);
});
nodeHooks.click.on((event) => {
var _a, _b;
emits.nodeClick(event);
(_b = (_a = node.events) == null ? void 0 : _a.click) == null || _b.call(_a, event);
});
nodeHooks.mouseEnter.on((event) => {
var _a, _b;
emits.nodeMouseEnter(event);
(_b = (_a = node.events) == null ? void 0 : _a.mouseEnter) == null || _b.call(_a, event);
});
nodeHooks.mouseMove.on((event) => {
var _a, _b;
emits.nodeMouseMove(event);
(_b = (_a = node.events) == null ? void 0 : _a.mouseMove) == null || _b.call(_a, event);
});
nodeHooks.mouseLeave.on((event) => {
var _a, _b;
emits.nodeMouseLeave(event);
(_b = (_a = node.events) == null ? void 0 : _a.mouseLeave) == null || _b.call(_a, event);
});
nodeHooks.contextMenu.on((event) => {
var _a, _b;
emits.nodeContextMenu(event);
(_b = (_a = node.events) == null ? void 0 : _a.contextMenu) == null || _b.call(_a, event);
});
nodeHooks.dragStart.on((event) => {
var _a, _b;
emits.nodeDragStart(event);
(_b = (_a = node.events) == null ? void 0 : _a.dragStart) == null || _b.call(_a, event);
});
nodeHooks.drag.on((event) => {
var _a, _b;
emits.nodeDrag(event);
(_b = (_a = node.events) == null ? void 0 : _a.drag) == null || _b.call(_a, event);
});
nodeHooks.dragStop.on((event) => {
var _a, _b;
emits.nodeDragStop(event);
(_b = (_a = node.events) == null ? void 0 : _a.dragStop) == null || _b.call(_a, event);
});
return Object.entries(nodeHooks).reduce((hooks, [key, value]) => {
hooks.emit[key] = value.trigger;
hooks.on[key] = value.on;
return hooks;
}, {
emit: {},
on: {}
});
}
function useUpdateNodePositions() {
const { getSelectedNodes, nodeExtent, updateNodePositions, findNode, snapGrid, snapToGrid, nodesDraggable, emits } = useVueFlow();
return (positionDiff, isShiftPressed = false) => {
const xVelo = snapToGrid.value ? snapGrid.value[0] : 5;
const yVelo = snapToGrid.value ? snapGrid.value[1] : 5;
const factor = isShiftPressed ? 4 : 1;
const positionDiffX = positionDiff.x * xVelo * factor;
const positionDiffY = positionDiff.y * yVelo * factor;
const nodeUpdates = [];
for (const node of getSelectedNodes.value) if (node.draggable || nodesDraggable && typeof node.draggable === "undefined") {
const { position } = calcNextPosition(node, {
x: node.computedPosition.x + positionDiffX,
y: node.computedPosition.y + positionDiffY
}, emits.error, nodeExtent.value, node.parentNode ? findNode(node.parentNode) : void 0);
nodeUpdates.push({
id: node.id,
position,
from: node.position,
distance: {
x: positionDiff.x,
y: positionDiff.y
},
dimensions: node.dimensions
});
}
updateNodePositions(nodeUpdates, true, false);
};
}
var DEFAULT_PADDING = .1;
var defaultEase = (t) => ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2;
function noop() {
warn("Viewport not initialized yet.");
return Promise.resolve(false);
}
var initialViewportHelper = {
zoomIn: noop,
zoomOut: noop,
zoomTo: noop,
fitView: noop,
setCenter: noop,
fitBounds: noop,
project: (position) => position,
screenToFlowCoordinate: (position) => position,
flowToScreenCoordinate: (position) => position,
setViewport: noop,
setTransform: noop,
getViewport: () => ({
x: 0,
y: 0,
zoom: 1
}),
getTransform: () => ({
x: 0,
y: 0,
zoom: 1
}),
viewportInitialized: false
};
function useViewportHelper(state) {
function zoom2(scale, transitionOptions) {
return new Promise((resolve) => {
if (state.d3Selection && state.d3Zoom) state.d3Zoom.interpolate((transitionOptions == null ? void 0 : transitionOptions.interpolate) === "linear" ? interpolate$1 : interpolateZoom).scaleBy(getD3Transition(state.d3Selection, transitionOptions == null ? void 0 : transitionOptions.duration, transitionOptions == null ? void 0 : transitionOptions.ease, () => {
resolve(true);
}), scale);
else resolve(false);
});
}
function transformViewport(x, y, zoom22, transitionOptions) {
return new Promise((resolve) => {
var _a;
const { x: clampedX, y: clampedY } = clampPosition({
x: -x,
y: -y
}, state.translateExtent);
const nextTransform = identity.translate(-clampedX, -clampedY).scale(zoom22);
if (state.d3Selection && state.d3Zoom) (_a = state.d3Zoom) == null || _a.interpolate((transitionOptions == null ? void 0 : transitionOptions.interpolate) === "linear" ? interpolate$1 : interpolateZoom).transform(getD3Transition(state.d3Selection, transitionOptions == null ? void 0 : transitionOptions.duration, transitionOptions == null ? void 0 : transitionOptions.ease, () => {
resolve(true);
}), nextTransform);
else resolve(false);
});
}
return computed(() => {
if (!(state.d3Zoom && state.d3Selection && state.dimensions.width && state.dimensions.height)) return initialViewportHelper;
return {
viewportInitialized: true,
zoomIn: (options) => {
return zoom2(1.2, options);
},
zoomOut: (options) => {
return zoom2(1 / 1.2, options);
},
zoomTo: (zoomLevel, options) => {
return new Promise((resolve) => {
if (state.d3Selection && state.d3Zoom) state.d3Zoom.interpolate((options == null ? void 0 : options.interpolate) === "linear" ? interpolate$1 : interpolateZoom).scaleTo(getD3Transition(state.d3Selection, options == null ? void 0 : options.duration, options == null ? void 0 : options.ease, () => {
resolve(true);
}), zoomLevel);
else resolve(false);
});
},
setViewport: (transform, options) => {
return transformViewport(transform.x, transform.y, transform.zoom, options);
},
setTransform: (transform, options) => {
return transformViewport(transform.x, transform.y, transform.zoom, options);
},
getViewport: () => ({
x: state.viewport.x,
y: state.viewport.y,
zoom: state.viewport.zoom
}),
getTransform: () => {
return {
x: state.viewport.x,
y: state.viewport.y,
zoom: state.viewport.zoom
};
},
fitView: (options = {
padding: DEFAULT_PADDING,
includeHiddenNodes: false,
duration: 0
}) => {
var _a, _b;
const nodesToFit = [];
for (const node of state.nodes) if (node.dimensions.width && node.dimensions.height && ((options == null ? void 0 : options.includeHiddenNodes) || !node.hidden)) {
if (!((_a = options.nodes) == null ? void 0 : _a.length) || ((_b = options.nodes) == null ? void 0 : _b.length) && options.nodes.includes(node.id)) nodesToFit.push(node);
}
if (!nodesToFit.length) return Promise.resolve(false);
const { x, y, zoom: zoom22 } = getTransformForBounds(getRectOfNodes(nodesToFit), state.dimensions.width, state.dimensions.height, options.minZoom ?? state.minZoom, options.maxZoom ?? state.maxZoom, options.padding ?? DEFAULT_PADDING);
return transformViewport(x, y, zoom22, options);
},
setCenter: (x, y, options) => {
const nextZoom = typeof (options == null ? void 0 : options.zoom) !== "undefined" ? options.zoom : state.maxZoom;
return transformViewport(state.dimensions.width / 2 - x * nextZoom, state.dimensions.height / 2 - y * nextZoom, nextZoom, options);
},
fitBounds: (bounds, options = { padding: DEFAULT_PADDING }) => {
const { x, y, zoom: zoom22 } = getTransformForBounds(bounds, state.dimensions.width, state.dimensions.height, state.minZoom, state.maxZoom, options.padding ?? DEFAULT_PADDING);
return transformViewport(x, y, zoom22, options);
},
project: (position) => pointToRendererPoint(position, state.viewport, state.snapToGrid, state.snapGrid),
screenToFlowCoordinate: (position) => {
if (state.vueFlowRef) {
const { x: domX, y: domY } = state.vueFlowRef.getBoundingClientRect();
return pointToRendererPoint({
x: position.x - domX,
y: position.y - domY
}, state.viewport, state.snapToGrid, state.snapGrid);
}
return {
x: 0,
y: 0
};
},
flowToScreenCoordinate: (position) => {
if (state.vueFlowRef) {
const { x: domX, y: domY } = state.vueFlowRef.getBoundingClientRect();
return rendererPointToPoint({
x: position.x + domX,
y: position.y + domY
}, state.viewport);
}
return {
x: 0,
y: 0
};
}
};
});
}
function getD3Transition(selection2, duration = 0, ease = defaultEase, onEnd = () => {}) {
const hasDuration = typeof duration === "number" && duration > 0;
if (!hasDuration) onEnd();
return hasDuration ? selection2.transition().duration(duration).ease(ease).on("end", onEnd) : selection2;
}
function useWatchProps(models, props, store) {
const scope = effectScope(true);
scope.run(() => {
const watchModelValue = () => {
scope.run(() => {
let pauseModel;
let pauseStore;
let immediateStore = !!(store.nodes.value.length || store.edges.value.length);
pauseModel = watchPausable([models.modelValue, () => {
var _a, _b;
return (_b = (_a = models.modelValue) == null ? void 0 : _a.value) == null ? void 0 : _b.length;
}], ([elements]) => {
if (elements && Array.isArray(elements)) {
pauseStore?.pause();
store.setElements(elements);
if (!pauseStore && !immediateStore && elements.length) immediateStore = true;
else pauseStore?.resume();
}
});
pauseStore = watchPausable([
store.nodes,
store.edges,
() => store.edges.value.length,
() => store.nodes.value.length
], ([nodes$13, edges]) => {
var _a;
if (((_a = models.modelValue) == null ? void 0 : _a.value) && Array.isArray(models.modelValue.value)) {
pauseModel?.pause();
models.modelValue.value = [...nodes$13, ...edges];
nextTick(() => {
pauseModel?.resume();
});
}
}, { immediate: immediateStore });
onScopeDispose(() => {
pauseModel?.stop();
pauseStore?.stop();
});
});
};
const watchNodesValue = () => {
scope.run(() => {
let pauseModel;
let pauseStore;
let immediateStore = !!store.nodes.value.length;
pauseModel = watchPausable([models.nodes, () => {
var _a, _b;
return (_b = (_a = models.nodes) == null ? void 0 : _a.value) == null ? void 0 : _b.length;
}], ([nodes$13]) => {
if (nodes$13 && Array.isArray(nodes$13)) {
pauseStore?.pause();
store.setNodes(nodes$13);
if (!pauseStore && !immediateStore && nodes$13.length) immediateStore = true;
else pauseStore?.resume();
}
});
pauseStore = watchPausable([store.nodes, () => store.nodes.value.length], ([nodes$13]) => {
var _a;
if (((_a = models.nodes) == null ? void 0 : _a.value) && Array.isArray(models.nodes.value)) {
pauseModel?.pause();
models.nodes.value = [...nodes$13];
nextTick(() => {
pauseModel?.resume();
});
}
}, { immediate: immediateStore });
onScopeDispose(() => {
pauseModel?.stop();
pauseStore?.stop();
});
});
};
const watchEdgesValue = () => {
scope.run(() => {
let pauseModel;
let pauseStore;
let immediateStore = !!store.edges.value.length;
pauseModel = watchPausable([models.edges, () => {
var _a, _b;
return (_b = (_a = models.edges) == null ? void 0 : _a.value) == null ? void 0 : _b.length;
}], ([edges]) => {
if (edges && Array.isArray(edges)) {
pauseStore?.pause();
store.setEdges(edges);
if (!pauseStore && !immediateStore && edges.length) immediateStore = true;
else pauseStore?.resume();
}
});
pauseStore = watchPausable([store.edges, () => store.edges.value.length], ([edges]) => {
var _a;
if (((_a = models.edges) == null ? void 0 : _a.value) && Array.isArray(models.edges.value)) {
pauseModel?.pause();
models.edges.value = [...edges];
nextTick(() => {
pauseModel?.resume();
});
}
}, { immediate: immediateStore });
onScopeDispose(() => {
pauseModel?.stop();
pauseStore?.stop();
});
});
};
const watchMaxZoom = () => {
scope.run(() => {
watch(() => props.maxZoom, () => {
if (props.maxZoom && isDef(props.maxZoom)) store.setMaxZoom(props.maxZoom);
}, { immediate: true });
});
};
const watchMinZoom = () => {
scope.run(() => {
watch(() => props.minZoom, () => {
if (props.minZoom && isDef(props.minZoom)) store.setMinZoom(props.minZoom);
}, { immediate: true });
});
};
const watchTranslateExtent = () => {
scope.run(() => {
watch(() => props.translateExtent, () => {
if (props.translateExtent && isDef(props.translateExtent)) store.setTranslateExtent(props.translateExtent);
}, { immediate: true });
});
};
const watchNodeExtent = () => {
scope.run(() => {
watch(() => props.nodeExtent, () => {
if (props.nodeExtent && isDef(props.nodeExtent)) store.setNodeExtent(props.nodeExtent);
}, { immediate: true });
});
};
const watchApplyDefault = () => {
scope.run(() => {
watch(() => props.applyDefault, () => {
if (isDef(props.applyDefault)) store.applyDefault.value = props.applyDefault;
}, { immediate: true });
});
};
const watchAutoConnect = () => {
scope.run(() => {
const autoConnector = async (params) => {
let connection = params;
if (typeof props.autoConnect === "function") connection = await props.autoConnect(params);
if (connection !== false) store.addEdges([connection]);
};
watch(() => props.autoConnect, () => {
if (isDef(props.autoConnect)) store.autoConnect.value = props.autoConnect;
}, { immediate: true });
watch(store.autoConnect, (autoConnectEnabled, _, onCleanup) => {
if (autoConnectEnabled) store.onConnect(autoConnector);
else store.hooks.value.connect.off(autoConnector);
onCleanup(() => {
store.hooks.value.connect.off(autoConnector);
});
}, { immediate: true });
});
};
const watchRest = () => {
const skip = [
"id",
"modelValue",
"translateExtent",
"nodeExtent",
"edges",
"nodes",
"maxZoom",
"minZoom",
"applyDefault",
"autoConnect"
];
for (const key of Object.keys(props)) {
const propKey = key;
if (!skip.includes(propKey)) {
const propValue = toRef(() => props[propKey]);
const storeRef = store[propKey];
if (isRef(storeRef)) scope.run(() => {
watch(propValue, (nextValue) => {
if (isDef(nextValue)) storeRef.value = nextValue;
}, { immediate: true });
});
}
}
};
const runAll = () => {
watchModelValue();
watchNodesValue();
watchEdgesValue();
watchMinZoom();
watchMaxZoom();
watchTranslateExtent();
watchNodeExtent();
watchApplyDefault();
watchAutoConnect();
watchRest();
};
runAll();
});
return () => scope.stop();
}
function createHooks() {
return {
edgesChange: createExtendedEventHook(),
nodesChange: createExtendedEventHook(),
nodeDoubleClick: createExtendedEventHook(),
nodeClick: createExtendedEventHook(),
nodeMouseEnter: createExtendedEventHook(),
nodeMouseMove: createExtendedEventHook(),
nodeMouseLeave: createExtendedEventHook(),
nodeContextMenu: createExtendedEventHook(),
nodeDragStart: createExtendedEventHook(),
nodeDrag: createExtendedEventHook(),
nodeDragStop: createExtendedEventHook(),
nodesInitialized: createExtendedEventHook(),
miniMapNodeClick: createExtendedEventHook(),
miniMapNodeDoubleClick: createExtendedEventHook(),
miniMapNodeMouseEnter: createExtendedEventHook(),
miniMapNodeMouseMove: createExtendedEventHook(),
miniMapNodeMouseLeave: createExtendedEventHook(),
connect: createExtendedEventHook(),
connectStart: createExtendedEventHook(),
connectEnd: createExtendedEventHook(),
clickConnectStart: createExtendedEventHook(),
clickConnectEnd: createExtendedEventHook(),
paneReady: createExtendedEventHook(),
init: createExtendedEventHook(),
move: createExtendedEventHook(),
moveStart: createExtendedEventHook(),
moveEnd: createExtendedEventHook(),
selectionDragStart: createExtendedEventHook(),
selectionDrag: createExtendedEventHook(),
selectionDragStop: createExtendedEventHook(),
selectionContextMenu: createExtendedEventHook(),
selectionStart: createExtendedEventHook(),
selectionEnd: createExtendedEventHook(),
viewportChangeStart: createExtendedEventHook(),
viewportChange: createExtendedEventHook(),
viewportChangeEnd: createExtendedEventHook(),
paneScroll: createExtendedEventHook(),
paneClick: createExtendedEventHook(),
paneContextMenu: createExtendedEventHook(),
paneMouseEnter: createExtendedEventHook(),
paneMouseMove: createExtendedEventHook(),
paneMouseLeave: createExtendedEventHook(),
edgeContextMenu: createExtendedEventHook(),
edgeMouseEnter: createExtendedEventHook(),
edgeMouseMove: createExtendedEventHook(),
edgeMouseLeave: createExtendedEventHook(),
edgeDoubleClick: createExtendedEventHook(),
edgeClick: createExtendedEventHook(),
edgeUpdateStart: createExtendedEventHook(),
edgeUpdate: createExtendedEventHook(),
edgeUpdateEnd: createExtendedEventHook(),
updateNodeInternals: createExtendedEventHook(),
error: createExtendedEventHook((err) => warn(err.message))
};
}
function useHooks(emit, hooks) {
const inst = getCurrentInstance();
onBeforeMount(() => {
for (const [key, value] of Object.entries(hooks.value)) {
const listener = (data) => {
emit(key, data);
};
value.setEmitter(listener);
tryOnScopeDispose(value.removeEmitter);
value.setHasEmitListeners(() => hasVNodeListener(key));
tryOnScopeDispose(value.removeHasEmitListeners);
}
});
function hasVNodeListener(event) {
var _a;
const key = toHandlerKey(event);
return !!((_a = inst == null ? void 0 : inst.vnode.props) == null ? void 0 : _a[key]);
}
}
function toHandlerKey(event) {
const [head, ...rest] = event.split(":");
return `on${head.replace(/(?:^|-)(\w)/g, (_, c) => c.toUpperCase())}${rest.length ? `:${rest.join(":")}` : ""}`;
}
function useState() {
return {
vueFlowRef: null,
viewportRef: null,
nodes: [],
edges: [],
connectionLookup: /* @__PURE__ */ new Map(),
nodeTypes: {},
edgeTypes: {},
initialized: false,
dimensions: {
width: 0,
height: 0
},
viewport: {
x: 0,
y: 0,
zoom: 1
},
d3Zoom: null,
d3Selection: null,
d3ZoomHandler: null,
minZoom: .5,
maxZoom: 2,
translateExtent: [[Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY], [Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY]],
nodeExtent: [[Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY], [Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY]],
selectionMode: SelectionMode.Full,
paneDragging: false,
preventScrolling: true,
zoomOnScroll: true,
zoomOnPinch: true,
zoomOnDoubleClick: true,
panOnScroll: false,
panOnScrollSpeed: .5,
panOnScrollMode: PanOnScrollMode.Free,
paneClickDistance: 0,
panOnDrag: true,
edgeUpdaterRadius: 10,
onlyRenderVisibleElements: false,
defaultViewport: {
x: 0,
y: 0,
zoom: 1
},
nodesSelectionActive: false,
userSelectionActive: false,
userSelectionRect: null,
defaultMarkerColor: "#b1b1b7",
connectionLineStyle: {},
connectionLineType: null,
connectionLineOptions: {
type: ConnectionLineType.Bezier,
style: {}
},
connectionMode: ConnectionMode.Loose,
connectionStartHandle: null,
connectionEndHandle: null,
connectionClickStartHandle: null,
connectionPosition: {
x: NaN,
y: NaN
},
connectionRadius: 20,
connectOnClick: true,
connectionStatus: null,
isValidConnection: null,
snapGrid: [15, 15],
snapToGrid: false,
edgesUpdatable: false,
edgesFocusable: true,
nodesFocusable: true,
nodesConnectable: true,
nodesDraggable: true,
nodeDragThreshold: 1,
elementsSelectable: true,
selectNodesOnDrag: true,
multiSelectionActive: false,
selectionKeyCode: "Shift",
multiSelectionKeyCode: isMacOs() ? "Meta" : "Control",
zoomActivationKeyCode: isMacOs() ? "Meta" : "Control",
deleteKeyCode: "Backspace",
panActivationKeyCode: "Space",
hooks: createHooks(),
applyDefault: true,
autoConnect: false,
fitViewOnInit: false,
fitViewOnInitDone: false,
noDragClassName: "nodrag",
noWheelClassName: "nowheel",
noPanClassName: "nopan",
defaultEdgeOptions: void 0,
elevateEdgesOnSelect: false,
elevateNodesOnSelect: true,
autoPanOnNodeDrag: true,
autoPanOnConnect: true,
autoPanSpeed: 15,
disableKeyboardA11y: false,
ariaLiveMessage: ""
};
}
var storeOptionsToSkip = [
"id",
"vueFlowRef",
"viewportRef",
"initialized",
"modelValue",
"nodes",
"edges",
"maxZoom",
"minZoom",
"translateExtent",
"hooks",
"defaultEdgeOptions"
];
function useActions(state, nodeLookup, edgeLookup) {
const viewportHelper = useViewportHelper(state);
const updateNodeInternals = (ids) => {
const updateIds = ids ?? [];
state.hooks.updateNodeInternals.trigger(updateIds);
};
const getIncomers$1 = (nodeOrId) => {
return getIncomers(nodeOrId, state.nodes, state.edges);
};
const getOutgoers$1 = (nodeOrId) => {
return getOutgoers(nodeOrId, state.nodes, state.edges);
};
const getConnectedEdges$1 = (nodesOrId) => {
return getConnectedEdges(nodesOrId, state.edges);
};
const getHandleConnections = ({ id: id2, type, nodeId }) => {
var _a;
const handleSuffix = id2 ? `-${type}-${id2}` : `-${type}`;
return Array.from(((_a = state.connectionLookup.get(`${nodeId}${handleSuffix}`)) == null ? void 0 : _a.values()) ?? []);
};
const findNode = (id2) => {
if (!id2) return;
return nodeLookup.value.get(id2);
};
const findEdge = (id2) => {
if (!id2) return;
return edgeLookup.value.get(id2);
};
const updateNodePositions = (dragItems, changed, dragging) => {
var _a, _b;
const changes = [];
for (const node of dragItems) {
const change = {
id: node.id,
type: "position",
dragging,
from: node.from
};
if (changed) {
change.position = node.position;
if (node.parentNode) {
const parentNode = findNode(node.parentNode);
change.position = {
x: change.position.x - (((_a = parentNode == null ? void 0 : parentNode.computedPosition) == null ? void 0 : _a.x) ?? 0),
y: change.position.y - (((_b = parentNode == null ? void 0 : parentNode.computedPosition) == null ? void 0 : _b.y) ?? 0)
};
}
}
changes.push(change);
}
if (changes == null ? void 0 : changes.length) state.hooks.nodesChange.trigger(changes);
};
const updateNodeDimensions = (updates) => {
if (!state.vueFlowRef) return;
const viewportNode = state.vueFlowRef.querySelector(".vue-flow__transformationpane");
if (!viewportNode) return;
const style = window.getComputedStyle(viewportNode);
const { m22: zoom2 } = new window.DOMMatrixReadOnly(style.transform);
const changes = [];
for (const element of updates) {
const update = element;
const node = findNode(update.id);
if (node) {
const dimensions = getDimensions(update.nodeElement);
if (!!(dimensions.width && dimensions.height && (node.dimensions.width !== dimensions.width || node.dimensions.height !== dimensions.height || update.forceUpdate))) {
const nodeBounds = update.nodeElement.getBoundingClientRect();
node.dimensions = dimensions;
node.handleBounds.source = getHandleBounds("source", update.nodeElement, nodeBounds, zoom2, node.id);
node.handleBounds.target = getHandleBounds("target", update.nodeElement, nodeBounds, zoom2, node.id);
changes.push({
id: node.id,
type: "dimensions",
dimensions
});
}
}
}
if (!state.fitViewOnInitDone && state.fitViewOnInit) viewportHelper.value.fitView().then(() => {
state.fitViewOnInitDone = true;
});
if (changes.length) state.hooks.nodesChange.trigger(changes);
};
const elementSelectionHandler = (elements, selected) => {
const nodeIds = /* @__PURE__ */ new Set();
const edgeIds = /* @__PURE__ */ new Set();
for (const element of elements) if (isNode(element)) nodeIds.add(element.id);
else if (isEdge(element)) edgeIds.add(element.id);
const changedNodes = getSelectionChanges(nodeLookup.value, nodeIds, true);
const changedEdges = getSelectionChanges(edgeLookup.value, edgeIds);
if (state.multiSelectionActive) {
for (const nodeId of nodeIds) changedNodes.push(createSelectionChange(nodeId, selected));
for (const edgeId of edgeIds) changedEdges.push(createSelectionChange(edgeId, selected));
}
if (changedNodes.length) state.hooks.nodesChange.trigger(changedNodes);
if (changedEdges.length) state.hooks.edgesChange.trigger(changedEdges);
};
const addSelectedNodes = (nodes$13) => {
if (state.multiSelectionActive) {
const nodeChanges = nodes$13.map((node) => createSelectionChange(node.id, true));
state.hooks.nodesChange.trigger(nodeChanges);
return;
}
state.hooks.nodesChange.trigger(getSelectionChanges(nodeLookup.value, new Set(nodes$13.map((n) => n.id)), true));
state.hooks.edgesChange.trigger(getSelectionChanges(edgeLookup.value));
};
const addSelectedEdges = (edges) => {
if (state.multiSelectionActive) {
const changedEdges = edges.map((edge) => createSelectionChange(edge.id, true));
state.hooks.edgesChange.trigger(changedEdges);
return;
}
state.hooks.edgesChange.trigger(getSelectionChanges(edgeLookup.value, new Set(edges.map((e) => e.id))));
state.hooks.nodesChange.trigger(getSelectionChanges(nodeLookup.value, /* @__PURE__ */ new Set(), true));
};
const addSelectedElements = (elements) => {
elementSelectionHandler(elements, true);
};
const removeSelectedNodes = (nodes$13) => {
const nodeChanges = (nodes$13 || state.nodes).map((n) => {
n.selected = false;
return createSelectionChange(n.id, false);
});
state.hooks.nodesChange.trigger(nodeChanges);
};
const removeSelectedEdges = (edges) => {
const edgeChanges = (edges || state.edges).map((e) => {
e.selected = false;
return createSelectionChange(e.id, false);
});
state.hooks.edgesChange.trigger(edgeChanges);
};
const removeSelectedElements = (elements) => {
if (!elements || !elements.length) return elementSelectionHandler([], false);
const changes = elements.reduce((changes2, curr) => {
const selectionChange = createSelectionChange(curr.id, false);
if (isNode(curr)) changes2.nodes.push(selectionChange);
else changes2.edges.push(selectionChange);
return changes2;
}, {
nodes: [],
edges: []
});
if (changes.nodes.length) state.hooks.nodesChange.trigger(changes.nodes);
if (changes.edges.length) state.hooks.edgesChange.trigger(changes.edges);
};
const setMinZoom = (minZoom) => {
var _a;
(_a = state.d3Zoom) == null || _a.scaleExtent([minZoom, state.maxZoom]);
state.minZoom = minZoom;
};
const setMaxZoom = (maxZoom) => {
var _a;
(_a = state.d3Zoom) == null || _a.scaleExtent([state.minZoom, maxZoom]);
state.maxZoom = maxZoom;
};
const setTranslateExtent = (translateExtent) => {
var _a;
(_a = state.d3Zoom) == null || _a.translateExtent(translateExtent);
state.translateExtent = translateExtent;
};
const setNodeExtent = (nodeExtent) => {
state.nodeExtent = nodeExtent;
updateNodeInternals();
};
const setPaneClickDistance = (clickDistance) => {
var _a;
(_a = state.d3Zoom) == null || _a.clickDistance(clickDistance);
};
const setInteractive = (isInteractive) => {
state.nodesDraggable = isInteractive;
state.nodesConnectable = isInteractive;
state.elementsSelectable = isInteractive;
};
const setNodes = (nodes$13) => {
const nextNodes = nodes$13 instanceof Function ? nodes$13(state.nodes) : nodes$13;
if (!state.initialized && !nextNodes.length) return;
state.nodes = createGraphNodes(nextNodes, findNode, state.hooks.error.trigger);
};
const setEdges = (edges) => {
const nextEdges = edges instanceof Function ? edges(state.edges) : edges;
if (!state.initialized && !nextEdges.length) return;
const validEdges = createGraphEdges(nextEdges, state.isValidConnection, findNode, findEdge, state.hooks.error.trigger, state.defaultEdgeOptions, state.nodes, state.edges);
updateConnectionLookup(state.connectionLookup, edgeLookup.value, validEdges);
state.edges = validEdges;
};
const setElements = (elements) => {
const nextElements = elements instanceof Function ? elements([...state.nodes, ...state.edges]) : elements;
if (!state.initialized && !nextElements.length) return;
setNodes(nextElements.filter(isNode));
setEdges(nextElements.filter(isEdge));
};
const addNodes = (nodes$13) => {
let nextNodes = nodes$13 instanceof Function ? nodes$13(state.nodes) : nodes$13;
nextNodes = Array.isArray(nextNodes) ? nextNodes : [nextNodes];
const graphNodes = createGraphNodes(nextNodes, findNode, state.hooks.error.trigger);
const changes = [];
for (const node of graphNodes) changes.push(createAdditionChange(node));
if (changes.length) state.hooks.nodesChange.trigger(changes);
};
const addEdges = (params) => {
let nextEdges = params instanceof Function ? params(state.edges) : params;
nextEdges = Array.isArray(nextEdges) ? nextEdges : [nextEdges];
const validEdges = createGraphEdges(nextEdges, state.isValidConnection, findNode, findEdge, state.hooks.error.trigger, state.defaultEdgeOptions, state.nodes, state.edges);
const changes = [];
for (const edge of validEdges) changes.push(createAdditionChange(edge));
if (changes.length) state.hooks.edgesChange.trigger(changes);
};
const removeNodes = (nodes$13, removeConnectedEdges = true, removeChildren = false) => {
const nextNodes = nodes$13 instanceof Function ? nodes$13(state.nodes) : nodes$13;
const nodesToRemove = Array.isArray(nextNodes) ? nextNodes : [nextNodes];
const nodeChanges = [];
const edgeChanges = [];
function createEdgeRemovalChanges(nodes2) {
const connectedEdges = getConnectedEdges$1(nodes2);
for (const edge of connectedEdges) if (isDef(edge.deletable) ? edge.deletable : true) edgeChanges.push(createEdgeRemoveChange(edge.id, edge.source, edge.target, edge.sourceHandle, edge.targetHandle));
}
function createChildrenRemovalChanges(id2) {
const children2 = [];
for (const node of state.nodes) if (node.parentNode === id2) children2.push(node);
if (children2.length) {
for (const child of children2) nodeChanges.push(createNodeRemoveChange(child.id));
if (removeConnectedEdges) createEdgeRemovalChanges(children2);
for (const child of children2) createChildrenRemovalChanges(child.id);
}
}
for (const item of nodesToRemove) {
const currNode = typeof item === "string" ? findNode(item) : item;
if (!currNode) continue;
if (isDef(currNode.deletable) && !currNode.deletable) continue;
nodeChanges.push(createNodeRemoveChange(currNode.id));
if (removeConnectedEdges) createEdgeRemovalChanges([currNode]);
if (removeChildren) createChildrenRemovalChanges(currNode.id);
}
if (edgeChanges.length) state.hooks.edgesChange.trigger(edgeChanges);
if (nodeChanges.length) state.hooks.nodesChange.trigger(nodeChanges);
};
const removeEdges = (edges) => {
const nextEdges = edges instanceof Function ? edges(state.edges) : edges;
const edgesToRemove = Array.isArray(nextEdges) ? nextEdges : [nextEdges];
const changes = [];
for (const item of edgesToRemove) {
const currEdge = typeof item === "string" ? findEdge(item) : item;
if (!currEdge) continue;
if (isDef(currEdge.deletable) && !currEdge.deletable) continue;
changes.push(createEdgeRemoveChange(typeof item === "string" ? item : item.id, currEdge.source, currEdge.target, currEdge.sourceHandle, currEdge.targetHandle));
}
state.hooks.edgesChange.trigger(changes);
};
const updateEdge2 = (oldEdge, newConnection, shouldReplaceId = true) => {
const prevEdge = findEdge(oldEdge.id);
if (!prevEdge) return false;
const prevEdgeIndex = state.edges.indexOf(prevEdge);
const newEdge = updateEdgeAction(oldEdge, newConnection, prevEdge, shouldReplaceId, state.hooks.error.trigger);
if (newEdge) {
const [validEdge] = createGraphEdges([newEdge], state.isValidConnection, findNode, findEdge, state.hooks.error.trigger, state.defaultEdgeOptions, state.nodes, state.edges);
state.edges = state.edges.map((edge, index) => index === prevEdgeIndex ? validEdge : edge);
updateConnectionLookup(state.connectionLookup, edgeLookup.value, [validEdge]);
return validEdge;
}
return false;
};
const updateEdgeData = (id2, dataUpdate, options = { replace: false }) => {
const edge = findEdge(id2);
if (!edge) return;
const nextData = typeof dataUpdate === "function" ? dataUpdate(edge) : dataUpdate;
edge.data = options.replace ? nextData : {
...edge.data,
...nextData
};
};
const applyNodeChanges2 = (changes) => {
return applyChanges(changes, state.nodes);
};
const applyEdgeChanges2 = (changes) => {
const changedEdges = applyChanges(changes, state.edges);
updateConnectionLookup(state.connectionLookup, edgeLookup.value, changedEdges);
return changedEdges;
};
const updateNode = (id2, nodeUpdate, options = { replace: false }) => {
const node = findNode(id2);
if (!node) return;
const nextNode = typeof nodeUpdate === "function" ? nodeUpdate(node) : nodeUpdate;
if (options.replace) state.nodes.splice(state.nodes.indexOf(node), 1, nextNode);
else Object.assign(node, nextNode);
};
const updateNodeData = (id2, dataUpdate, options = { replace: false }) => {
const node = findNode(id2);
if (!node) return;
const nextData = typeof dataUpdate === "function" ? dataUpdate(node) : dataUpdate;
node.data = options.replace ? nextData : {
...node.data,
...nextData
};
};
const startConnection = (startHandle, position, isClick = false) => {
if (isClick) state.connectionClickStartHandle = startHandle;
else state.connectionStartHandle = startHandle;
state.connectionEndHandle = null;
state.connectionStatus = null;
if (position) state.connectionPosition = position;
};
const updateConnection = (position, result = null, status = null) => {
if (state.connectionStartHandle) {
state.connectionPosition = position;
state.connectionEndHandle = result;
state.connectionStatus = status;
}
};
const endConnection = (event, isClick) => {
state.connectionPosition = {
x: NaN,
y: NaN
};
state.connectionEndHandle = null;
state.connectionStatus = null;
if (isClick) state.connectionClickStartHandle = null;
else state.connectionStartHandle = null;
};
const getNodeRect = (nodeOrRect) => {
const isRectObj = isRect(nodeOrRect);
const node = isRectObj ? null : isGraphNode(nodeOrRect) ? nodeOrRect : findNode(nodeOrRect.id);
if (!isRectObj && !node) return [
null,
null,
isRectObj
];
return [
isRectObj ? nodeOrRect : nodeToRect(node),
node,
isRectObj
];
};
const getIntersectingNodes = (nodeOrRect, partially = true, nodes$13 = state.nodes) => {
const [nodeRect, node, isRect2] = getNodeRect(nodeOrRect);
if (!nodeRect) return [];
const intersections = [];
for (const n of nodes$13 || state.nodes) {
if (!isRect2 && (n.id === node.id || !n.computedPosition)) continue;
const currNodeRect = nodeToRect(n);
const overlappingArea = getOverlappingArea(currNodeRect, nodeRect);
if (partially && overlappingArea > 0 || overlappingArea >= currNodeRect.width * currNodeRect.height || overlappingArea >= Number(nodeRect.width) * Number(nodeRect.height)) intersections.push(n);
}
return intersections;
};
const isNodeIntersecting = (nodeOrRect, area, partially = true) => {
const [nodeRect] = getNodeRect(nodeOrRect);
if (!nodeRect) return false;
const overlappingArea = getOverlappingArea(nodeRect, area);
return partially && overlappingArea > 0 || overlappingArea >= Number(nodeRect.width) * Number(nodeRect.height);
};
const panBy = (delta) => {
const { viewport, dimensions, d3Zoom, d3Selection, translateExtent } = state;
if (!d3Zoom || !d3Selection || !delta.x && !delta.y) return false;
const nextTransform = identity.translate(viewport.x + delta.x, viewport.y + delta.y).scale(viewport.zoom);
const extent = [[0, 0], [dimensions.width, dimensions.height]];
const constrainedTransform = d3Zoom.constrain()(nextTransform, extent, translateExtent);
const transformChanged = state.viewport.x !== constrainedTransform.x || state.viewport.y !== constrainedTransform.y || state.viewport.zoom !== constrainedTransform.k;
d3Zoom.transform(d3Selection, constrainedTransform);
return transformChanged;
};
const setState = (options) => {
const opts = options instanceof Function ? options(state) : options;
const exclude = [
"d3Zoom",
"d3Selection",
"d3ZoomHandler",
"viewportRef",
"vueFlowRef",
"dimensions",
"hooks"
];
if (isDef(opts.defaultEdgeOptions)) state.defaultEdgeOptions = opts.defaultEdgeOptions;
const elements = opts.modelValue || opts.nodes || opts.edges ? [] : void 0;
if (elements) {
if (opts.modelValue) elements.push(...opts.modelValue);
if (opts.nodes) elements.push(...opts.nodes);
if (opts.edges) elements.push(...opts.edges);
setElements(elements);
}
const setSkippedOptions = () => {
if (isDef(opts.maxZoom)) setMaxZoom(opts.maxZoom);
if (isDef(opts.minZoom)) setMinZoom(opts.minZoom);
if (isDef(opts.translateExtent)) setTranslateExtent(opts.translateExtent);
};
for (const o of Object.keys(opts)) {
const key = o;
const option = opts[key];
if (![...storeOptionsToSkip, ...exclude].includes(key) && isDef(option)) state[key] = option;
}
until(() => state.d3Zoom).not.toBeNull().then(setSkippedOptions);
if (!state.initialized) state.initialized = true;
};
const toObject = () => {
const nodes$13 = [];
const edges = [];
for (const node of state.nodes) {
const { computedPosition: _, handleBounds: __, selected: ___, dimensions: ____, isParent: _____, resizing: ______, dragging: _______, events: _________,...rest } = node;
nodes$13.push(rest);
}
for (const edge of state.edges) {
const { selected: _, sourceNode: __, targetNode: ___, events: ____,...rest } = edge;
edges.push(rest);
}
return JSON.parse(JSON.stringify({
nodes: nodes$13,
edges,
position: [state.viewport.x, state.viewport.y],
zoom: state.viewport.zoom,
viewport: state.viewport
}));
};
const fromObject = (obj) => {
return new Promise((resolve) => {
const { nodes: nodes$13, edges, position, zoom: zoom2, viewport } = obj;
if (nodes$13) setNodes(nodes$13);
if (edges) setEdges(edges);
const [xPos, yPos] = (viewport == null ? void 0 : viewport.x) && (viewport == null ? void 0 : viewport.y) ? [viewport.x, viewport.y] : position ?? [null, null];
if (xPos && yPos) {
const nextZoom = (viewport == null ? void 0 : viewport.zoom) || zoom2 || state.viewport.zoom;
return until(() => viewportHelper.value.viewportInitialized).toBe(true).then(() => {
viewportHelper.value.setViewport({
x: xPos,
y: yPos,
zoom: nextZoom
}).then(() => {
resolve(true);
});
});
} else resolve(true);
});
};
const $reset = () => {
const resetState = useState();
state.edges = [];
state.nodes = [];
if (state.d3Zoom && state.d3Selection) {
const updatedTransform = identity.translate(resetState.defaultViewport.x ?? 0, resetState.defaultViewport.y ?? 0).scale(clamp(resetState.defaultViewport.zoom ?? 1, resetState.minZoom, resetState.maxZoom));
const bbox = state.viewportRef.getBoundingClientRect();
const extent = [[0, 0], [bbox.width, bbox.height]];
const constrainedTransform = state.d3Zoom.constrain()(updatedTransform, extent, resetState.translateExtent);
state.d3Zoom.transform(state.d3Selection, constrainedTransform);
}
setState(resetState);
};
return {
updateNodePositions,
updateNodeDimensions,
setElements,
setNodes,
setEdges,
addNodes,
addEdges,
removeNodes,
removeEdges,
findNode,
findEdge,
updateEdge: updateEdge2,
updateEdgeData,
updateNode,
updateNodeData,
applyEdgeChanges: applyEdgeChanges2,
applyNodeChanges: applyNodeChanges2,
addSelectedElements,
addSelectedNodes,
addSelectedEdges,
setMinZoom,
setMaxZoom,
setTranslateExtent,
setNodeExtent,
setPaneClickDistance,
removeSelectedElements,
removeSelectedNodes,
removeSelectedEdges,
startConnection,
updateConnection,
endConnection,
setInteractive,
setState,
getIntersectingNodes,
getIncomers: getIncomers$1,
getOutgoers: getOutgoers$1,
getConnectedEdges: getConnectedEdges$1,
getHandleConnections,
isNodeIntersecting,
panBy,
fitView: (params) => viewportHelper.value.fitView(params),
zoomIn: (transitionOpts) => viewportHelper.value.zoomIn(transitionOpts),
zoomOut: (transitionOpts) => viewportHelper.value.zoomOut(transitionOpts),
zoomTo: (zoomLevel, transitionOpts) => viewportHelper.value.zoomTo(zoomLevel, transitionOpts),
setViewport: (params, transitionOpts) => viewportHelper.value.setViewport(params, transitionOpts),
setTransform: (params, transitionOpts) => viewportHelper.value.setTransform(params, transitionOpts),
getViewport: () => viewportHelper.value.getViewport(),
getTransform: () => viewportHelper.value.getTransform(),
setCenter: (x, y, opts) => viewportHelper.value.setCenter(x, y, opts),
fitBounds: (params, opts) => viewportHelper.value.fitBounds(params, opts),
project: (params) => viewportHelper.value.project(params),
screenToFlowCoordinate: (params) => viewportHelper.value.screenToFlowCoordinate(params),
flowToScreenCoordinate: (params) => viewportHelper.value.flowToScreenCoordinate(params),
toObject,
fromObject,
updateNodeInternals,
viewportHelper,
$reset,
$destroy: () => {}
};
}
var _hoisted_1$9 = [
"data-id",
"data-handleid",
"data-nodeid",
"data-handlepos"
];
var _sfc_main$f = /* @__PURE__ */ defineComponent({
name: "Handle",
compatConfig: { MODE: 3 },
props: {
id: { default: null },
type: {},
position: { default: () => Position.Top },
isValidConnection: { type: Function },
connectable: {
type: [
Boolean,
Number,
String,
Function
],
default: void 0
},
connectableStart: {
type: Boolean,
default: true
},
connectableEnd: {
type: Boolean,
default: true
}
},
setup(__props, { expose: __expose }) {
const props = createPropsRestProxy(__props, [
"position",
"connectable",
"connectableStart",
"connectableEnd",
"id"
]);
const type = toRef(() => props.type ?? "source");
const isValidConnection = toRef(() => props.isValidConnection ?? null);
const { id: flowId, connectionStartHandle, connectionClickStartHandle, connectionEndHandle, vueFlowRef, nodesConnectable, noDragClassName, noPanClassName } = useVueFlow();
const { id: nodeId, node, nodeEl, connectedEdges } = useNode();
const handle = ref();
const isConnectableStart = toRef(() => typeof __props.connectableStart !== "undefined" ? __props.connectableStart : true);
const isConnectableEnd = toRef(() => typeof __props.connectableEnd !== "undefined" ? __props.connectableEnd : true);
const isConnecting = toRef(() => {
var _a, _b, _c, _d, _e, _f;
return ((_a = connectionStartHandle.value) == null ? void 0 : _a.nodeId) === nodeId && ((_b = connectionStartHandle.value) == null ? void 0 : _b.id) === __props.id && ((_c = connectionStartHandle.value) == null ? void 0 : _c.type) === type.value || ((_d = connectionEndHandle.value) == null ? void 0 : _d.nodeId) === nodeId && ((_e = connectionEndHandle.value) == null ? void 0 : _e.id) === __props.id && ((_f = connectionEndHandle.value) == null ? void 0 : _f.type) === type.value;
});
const isClickConnecting = toRef(() => {
var _a, _b, _c;
return ((_a = connectionClickStartHandle.value) == null ? void 0 : _a.nodeId) === nodeId && ((_b = connectionClickStartHandle.value) == null ? void 0 : _b.id) === __props.id && ((_c = connectionClickStartHandle.value) == null ? void 0 : _c.type) === type.value;
});
const { handlePointerDown, handleClick } = useHandle({
nodeId,
handleId: __props.id,
isValidConnection,
type
});
const isConnectable = computed(() => {
if (typeof __props.connectable === "string" && __props.connectable === "single") return !connectedEdges.value.some((edge) => {
const id2 = edge[`${type.value}Handle`];
if (edge[type.value] !== nodeId) return false;
return id2 ? id2 === __props.id : true;
});
if (typeof __props.connectable === "number") return connectedEdges.value.filter((edge) => {
const id2 = edge[`${type.value}Handle`];
if (edge[type.value] !== nodeId) return false;
return id2 ? id2 === __props.id : true;
}).length < __props.connectable;
if (typeof __props.connectable === "function") return __props.connectable(node, connectedEdges.value);
return isDef(__props.connectable) ? __props.connectable : nodesConnectable.value;
});
onMounted(() => {
var _a;
if (!node.dimensions.width || !node.dimensions.height) return;
const existingBounds = (_a = node.handleBounds[type.value]) == null ? void 0 : _a.find((b) => b.id === __props.id);
if (!vueFlowRef.value || existingBounds) return;
const viewportNode = vueFlowRef.value.querySelector(".vue-flow__transformationpane");
if (!nodeEl.value || !handle.value || !viewportNode || !__props.id) return;
const nodeBounds = nodeEl.value.getBoundingClientRect();
const handleBounds = handle.value.getBoundingClientRect();
const style = window.getComputedStyle(viewportNode);
const { m22: zoom2 } = new window.DOMMatrixReadOnly(style.transform);
const nextBounds = {
id: __props.id,
position: __props.position,
x: (handleBounds.left - nodeBounds.left) / zoom2,
y: (handleBounds.top - nodeBounds.top) / zoom2,
type: type.value,
nodeId,
...getDimensions(handle.value)
};
node.handleBounds[type.value] = [...node.handleBounds[type.value] ?? [], nextBounds];
});
function onPointerDown(event) {
const isMouseTriggered = isMouseEvent(event);
if (isConnectable.value && isConnectableStart.value && (isMouseTriggered && event.button === 0 || !isMouseTriggered)) handlePointerDown(event);
}
function onClick(event) {
if (!nodeId || !connectionClickStartHandle.value && !isConnectableStart.value) return;
if (isConnectable.value) handleClick(event);
}
__expose({
handleClick,
handlePointerDown,
onClick,
onPointerDown
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
ref_key: "handle",
ref: handle,
"data-id": `${unref(flowId)}-${unref(nodeId)}-${__props.id}-${type.value}`,
"data-handleid": __props.id,
"data-nodeid": unref(nodeId),
"data-handlepos": _ctx.position,
class: normalizeClass(["vue-flow__handle", [
`vue-flow__handle-${_ctx.position}`,
`vue-flow__handle-${__props.id}`,
unref(noDragClassName),
unref(noPanClassName),
type.value,
{
connectable: isConnectable.value,
connecting: isClickConnecting.value,
connectablestart: isConnectableStart.value,
connectableend: isConnectableEnd.value,
connectionindicator: isConnectable.value && (isConnectableStart.value && !isConnecting.value || isConnectableEnd.value && isConnecting.value)
}
]]),
onMousedown: onPointerDown,
onTouchstartPassive: onPointerDown,
onClick
}, [renderSlot(_ctx.$slots, "default", { id: _ctx.id })], 42, _hoisted_1$9);
};
}
});
var DefaultNode = function({ sourcePosition = Position.Bottom, targetPosition = Position.Top, label: _label, connectable = true, isValidTargetPos, isValidSourcePos, data }) {
const label = data.label ?? _label;
return [
h(_sfc_main$f, {
type: "target",
position: targetPosition,
connectable,
isValidConnection: isValidTargetPos
}),
typeof label !== "string" && label ? h(label) : h(Fragment, [label]),
h(_sfc_main$f, {
type: "source",
position: sourcePosition,
connectable,
isValidConnection: isValidSourcePos
})
];
};
DefaultNode.props = [
"sourcePosition",
"targetPosition",
"label",
"isValidTargetPos",
"isValidSourcePos",
"connectable",
"data"
];
DefaultNode.inheritAttrs = false;
DefaultNode.compatConfig = { MODE: 3 };
var DefaultNode$1 = DefaultNode;
var OutputNode = function({ targetPosition = Position.Top, label: _label, connectable = true, isValidTargetPos, data }) {
const label = data.label ?? _label;
return [h(_sfc_main$f, {
type: "target",
position: targetPosition,
connectable,
isValidConnection: isValidTargetPos
}), typeof label !== "string" && label ? h(label) : h(Fragment, [label])];
};
OutputNode.props = [
"targetPosition",
"label",
"isValidTargetPos",
"connectable",
"data"
];
OutputNode.inheritAttrs = false;
OutputNode.compatConfig = { MODE: 3 };
var OutputNode$1 = OutputNode;
var InputNode = function({ sourcePosition = Position.Bottom, label: _label, connectable = true, isValidSourcePos, data }) {
const label = data.label ?? _label;
return [typeof label !== "string" && label ? h(label) : h(Fragment, [label]), h(_sfc_main$f, {
type: "source",
position: sourcePosition,
connectable,
isValidConnection: isValidSourcePos
})];
};
InputNode.props = [
"sourcePosition",
"label",
"isValidSourcePos",
"connectable",
"data"
];
InputNode.inheritAttrs = false;
InputNode.compatConfig = { MODE: 3 };
var InputNode$1 = InputNode;
var _hoisted_1$8 = ["transform"];
var _hoisted_2$2 = [
"width",
"height",
"x",
"y",
"rx",
"ry"
];
var _hoisted_3$1 = ["y"];
var _sfc_main$e = /* @__PURE__ */ defineComponent({
name: "EdgeText",
compatConfig: { MODE: 3 },
props: {
x: {},
y: {},
label: {},
labelStyle: { default: () => ({}) },
labelShowBg: {
type: Boolean,
default: true
},
labelBgStyle: { default: () => ({}) },
labelBgPadding: { default: () => [2, 4] },
labelBgBorderRadius: { default: 2 }
},
setup(__props) {
const box = ref({
x: 0,
y: 0,
width: 0,
height: 0
});
const el = ref(null);
const transform = computed(() => `translate(${__props.x - box.value.width / 2} ${__props.y - box.value.height / 2})`);
onMounted(getBox);
watch([
() => __props.x,
() => __props.y,
el,
() => __props.label
], getBox);
function getBox() {
if (!el.value) return;
const nextBox = el.value.getBBox();
if (nextBox.width !== box.value.width || nextBox.height !== box.value.height) box.value = nextBox;
}
return (_ctx, _cache) => {
return openBlock(), createElementBlock("g", {
transform: transform.value,
class: "vue-flow__edge-textwrapper"
}, [_ctx.labelShowBg ? (openBlock(), createElementBlock("rect", {
key: 0,
class: "vue-flow__edge-textbg",
width: `${box.value.width + 2 * _ctx.labelBgPadding[0]}px`,
height: `${box.value.height + 2 * _ctx.labelBgPadding[1]}px`,
x: -_ctx.labelBgPadding[0],
y: -_ctx.labelBgPadding[1],
style: normalizeStyle(_ctx.labelBgStyle),
rx: _ctx.labelBgBorderRadius,
ry: _ctx.labelBgBorderRadius
}, null, 12, _hoisted_2$2)) : createCommentVNode("", true), createBaseVNode("text", mergeProps(_ctx.$attrs, {
ref_key: "el",
ref: el,
class: "vue-flow__edge-text",
y: box.value.height / 2,
dy: "0.3em",
style: _ctx.labelStyle
}), [renderSlot(_ctx.$slots, "default", {}, () => [typeof _ctx.label !== "string" ? (openBlock(), createBlock(resolveDynamicComponent(_ctx.label), { key: 0 })) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(_ctx.label), 1)], 64))])], 16, _hoisted_3$1)], 8, _hoisted_1$8);
};
}
});
var _hoisted_1$7 = [
"id",
"d",
"marker-end",
"marker-start"
];
var _hoisted_2$1 = ["d", "stroke-width"];
var _sfc_main$d = /* @__PURE__ */ defineComponent({
name: "BaseEdge",
inheritAttrs: false,
compatConfig: { MODE: 3 },
props: {
id: {},
labelX: {},
labelY: {},
path: {},
label: {},
markerStart: {},
markerEnd: {},
interactionWidth: { default: 20 },
labelStyle: {},
labelShowBg: { type: Boolean },
labelBgStyle: {},
labelBgPadding: {},
labelBgBorderRadius: {}
},
setup(__props, { expose: __expose }) {
const pathEl = ref(null);
const interactionEl = ref(null);
const labelEl = ref(null);
const attrs = useAttrs();
__expose({
pathEl,
interactionEl,
labelEl
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock(Fragment, null, [
createBaseVNode("path", mergeProps(unref(attrs), {
id: _ctx.id,
ref_key: "pathEl",
ref: pathEl,
d: _ctx.path,
class: "vue-flow__edge-path",
"marker-end": _ctx.markerEnd,
"marker-start": _ctx.markerStart
}), null, 16, _hoisted_1$7),
_ctx.interactionWidth ? (openBlock(), createElementBlock("path", {
key: 0,
ref_key: "interactionEl",
ref: interactionEl,
fill: "none",
d: _ctx.path,
"stroke-width": _ctx.interactionWidth,
"stroke-opacity": 0,
class: "vue-flow__edge-interaction"
}, null, 8, _hoisted_2$1)) : createCommentVNode("", true),
_ctx.label && _ctx.labelX && _ctx.labelY ? (openBlock(), createBlock(_sfc_main$e, {
key: 1,
ref_key: "labelEl",
ref: labelEl,
x: _ctx.labelX,
y: _ctx.labelY,
label: _ctx.label,
"label-show-bg": _ctx.labelShowBg,
"label-bg-style": _ctx.labelBgStyle,
"label-bg-padding": _ctx.labelBgPadding,
"label-bg-border-radius": _ctx.labelBgBorderRadius,
"label-style": _ctx.labelStyle
}, null, 8, [
"x",
"y",
"label",
"label-show-bg",
"label-bg-style",
"label-bg-padding",
"label-bg-border-radius",
"label-style"
])) : createCommentVNode("", true)
], 64);
};
}
});
function getSimpleEdgeCenter({ sourceX, sourceY, targetX, targetY }) {
const xOffset = Math.abs(targetX - sourceX) / 2;
const centerX = targetX < sourceX ? targetX + xOffset : targetX - xOffset;
const yOffset = Math.abs(targetY - sourceY) / 2;
return [
centerX,
targetY < sourceY ? targetY + yOffset : targetY - yOffset,
xOffset,
yOffset
];
}
function getBezierEdgeCenter({ sourceX, sourceY, targetX, targetY, sourceControlX, sourceControlY, targetControlX, targetControlY }) {
const centerX = sourceX * .125 + sourceControlX * .375 + targetControlX * .375 + targetX * .125;
const centerY = sourceY * .125 + sourceControlY * .375 + targetControlY * .375 + targetY * .125;
return [
centerX,
centerY,
Math.abs(centerX - sourceX),
Math.abs(centerY - sourceY)
];
}
function calculateControlOffset(distance2, curvature) {
if (distance2 >= 0) return .5 * distance2;
else return curvature * 25 * Math.sqrt(-distance2);
}
function getControlWithCurvature({ pos, x1, y1, x2, y2, c }) {
let ctX, ctY;
switch (pos) {
case Position.Left:
ctX = x1 - calculateControlOffset(x1 - x2, c);
ctY = y1;
break;
case Position.Right:
ctX = x1 + calculateControlOffset(x2 - x1, c);
ctY = y1;
break;
case Position.Top:
ctX = x1;
ctY = y1 - calculateControlOffset(y1 - y2, c);
break;
case Position.Bottom:
ctX = x1;
ctY = y1 + calculateControlOffset(y2 - y1, c);
break;
}
return [ctX, ctY];
}
function getBezierPath(bezierPathParams) {
const { sourceX, sourceY, sourcePosition = Position.Bottom, targetX, targetY, targetPosition = Position.Top, curvature = .25 } = bezierPathParams;
const [sourceControlX, sourceControlY] = getControlWithCurvature({
pos: sourcePosition,
x1: sourceX,
y1: sourceY,
x2: targetX,
y2: targetY,
c: curvature
});
const [targetControlX, targetControlY] = getControlWithCurvature({
pos: targetPosition,
x1: targetX,
y1: targetY,
x2: sourceX,
y2: sourceY,
c: curvature
});
const [labelX, labelY, offsetX, offsetY] = getBezierEdgeCenter({
sourceX,
sourceY,
targetX,
targetY,
sourceControlX,
sourceControlY,
targetControlX,
targetControlY
});
return [
`M${sourceX},${sourceY} C${sourceControlX},${sourceControlY} ${targetControlX},${targetControlY} ${targetX},${targetY}`,
labelX,
labelY,
offsetX,
offsetY
];
}
function getControl({ pos, x1, y1, x2, y2 }) {
let ctX, ctY;
switch (pos) {
case Position.Left:
case Position.Right:
ctX = .5 * (x1 + x2);
ctY = y1;
break;
case Position.Top:
case Position.Bottom:
ctX = x1;
ctY = .5 * (y1 + y2);
break;
}
return [ctX, ctY];
}
function getSimpleBezierPath(simpleBezierPathParams) {
const { sourceX, sourceY, sourcePosition = Position.Bottom, targetX, targetY, targetPosition = Position.Top } = simpleBezierPathParams;
const [sourceControlX, sourceControlY] = getControl({
pos: sourcePosition,
x1: sourceX,
y1: sourceY,
x2: targetX,
y2: targetY
});
const [targetControlX, targetControlY] = getControl({
pos: targetPosition,
x1: targetX,
y1: targetY,
x2: sourceX,
y2: sourceY
});
const [centerX, centerY, offsetX, offsetY] = getBezierEdgeCenter({
sourceX,
sourceY,
targetX,
targetY,
sourceControlX,
sourceControlY,
targetControlX,
targetControlY
});
return [
`M${sourceX},${sourceY} C${sourceControlX},${sourceControlY} ${targetControlX},${targetControlY} ${targetX},${targetY}`,
centerX,
centerY,
offsetX,
offsetY
];
}
var handleDirections = {
[Position.Left]: {
x: -1,
y: 0
},
[Position.Right]: {
x: 1,
y: 0
},
[Position.Top]: {
x: 0,
y: -1
},
[Position.Bottom]: {
x: 0,
y: 1
}
};
function getDirection({ source, sourcePosition = Position.Bottom, target }) {
if (sourcePosition === Position.Left || sourcePosition === Position.Right) return source.x < target.x ? {
x: 1,
y: 0
} : {
x: -1,
y: 0
};
return source.y < target.y ? {
x: 0,
y: 1
} : {
x: 0,
y: -1
};
}
function distance(a, b) {
return Math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2);
}
function getPoints({ source, sourcePosition = Position.Bottom, target, targetPosition = Position.Top, center, offset }) {
const sourceDir = handleDirections[sourcePosition];
const targetDir = handleDirections[targetPosition];
const sourceGapped = {
x: source.x + sourceDir.x * offset,
y: source.y + sourceDir.y * offset
};
const targetGapped = {
x: target.x + targetDir.x * offset,
y: target.y + targetDir.y * offset
};
const dir = getDirection({
source: sourceGapped,
sourcePosition,
target: targetGapped
});
const dirAccessor = dir.x !== 0 ? "x" : "y";
const currDir = dir[dirAccessor];
let points;
let centerX, centerY;
const sourceGapOffset = {
x: 0,
y: 0
};
const targetGapOffset = {
x: 0,
y: 0
};
const [defaultCenterX, defaultCenterY, defaultOffsetX, defaultOffsetY] = getSimpleEdgeCenter({
sourceX: source.x,
sourceY: source.y,
targetX: target.x,
targetY: target.y
});
if (sourceDir[dirAccessor] * targetDir[dirAccessor] === -1) {
centerX = center.x ?? defaultCenterX;
centerY = center.y ?? defaultCenterY;
const verticalSplit = [{
x: centerX,
y: sourceGapped.y
}, {
x: centerX,
y: targetGapped.y
}];
const horizontalSplit = [{
x: sourceGapped.x,
y: centerY
}, {
x: targetGapped.x,
y: centerY
}];
if (sourceDir[dirAccessor] === currDir) points = dirAccessor === "x" ? verticalSplit : horizontalSplit;
else points = dirAccessor === "x" ? horizontalSplit : verticalSplit;
} else {
const sourceTarget = [{
x: sourceGapped.x,
y: targetGapped.y
}];
const targetSource = [{
x: targetGapped.x,
y: sourceGapped.y
}];
if (dirAccessor === "x") points = sourceDir.x === currDir ? targetSource : sourceTarget;
else points = sourceDir.y === currDir ? sourceTarget : targetSource;
if (sourcePosition === targetPosition) {
const diff = Math.abs(source[dirAccessor] - target[dirAccessor]);
if (diff <= offset) {
const gapOffset = Math.min(offset - 1, offset - diff);
if (sourceDir[dirAccessor] === currDir) sourceGapOffset[dirAccessor] = (sourceGapped[dirAccessor] > source[dirAccessor] ? -1 : 1) * gapOffset;
else targetGapOffset[dirAccessor] = (targetGapped[dirAccessor] > target[dirAccessor] ? -1 : 1) * gapOffset;
}
}
if (sourcePosition !== targetPosition) {
const dirAccessorOpposite = dirAccessor === "x" ? "y" : "x";
const isSameDir = sourceDir[dirAccessor] === targetDir[dirAccessorOpposite];
const sourceGtTargetOppo = sourceGapped[dirAccessorOpposite] > targetGapped[dirAccessorOpposite];
const sourceLtTargetOppo = sourceGapped[dirAccessorOpposite] < targetGapped[dirAccessorOpposite];
if (sourceDir[dirAccessor] === 1 && (!isSameDir && sourceGtTargetOppo || isSameDir && sourceLtTargetOppo) || sourceDir[dirAccessor] !== 1 && (!isSameDir && sourceLtTargetOppo || isSameDir && sourceGtTargetOppo)) points = dirAccessor === "x" ? sourceTarget : targetSource;
}
const sourceGapPoint = {
x: sourceGapped.x + sourceGapOffset.x,
y: sourceGapped.y + sourceGapOffset.y
};
const targetGapPoint = {
x: targetGapped.x + targetGapOffset.x,
y: targetGapped.y + targetGapOffset.y
};
if (Math.max(Math.abs(sourceGapPoint.x - points[0].x), Math.abs(targetGapPoint.x - points[0].x)) >= Math.max(Math.abs(sourceGapPoint.y - points[0].y), Math.abs(targetGapPoint.y - points[0].y))) {
centerX = (sourceGapPoint.x + targetGapPoint.x) / 2;
centerY = points[0].y;
} else {
centerX = points[0].x;
centerY = (sourceGapPoint.y + targetGapPoint.y) / 2;
}
}
return [
[
source,
{
x: sourceGapped.x + sourceGapOffset.x,
y: sourceGapped.y + sourceGapOffset.y
},
...points,
{
x: targetGapped.x + targetGapOffset.x,
y: targetGapped.y + targetGapOffset.y
},
target
],
centerX,
centerY,
defaultOffsetX,
defaultOffsetY
];
}
function getBend(a, b, c, size) {
const bendSize = Math.min(distance(a, b) / 2, distance(b, c) / 2, size);
const { x, y } = b;
if (a.x === x && x === c.x || a.y === y && y === c.y) return `L${x} ${y}`;
if (a.y === y) {
const xDir2 = a.x < c.x ? -1 : 1;
const yDir2 = a.y < c.y ? 1 : -1;
return `L ${x + bendSize * xDir2},${y}Q ${x},${y} ${x},${y + bendSize * yDir2}`;
}
const xDir = a.x < c.x ? 1 : -1;
return `L ${x},${y + bendSize * (a.y < c.y ? -1 : 1)}Q ${x},${y} ${x + bendSize * xDir},${y}`;
}
function getSmoothStepPath(smoothStepPathParams) {
const { sourceX, sourceY, sourcePosition = Position.Bottom, targetX, targetY, targetPosition = Position.Top, borderRadius = 5, centerX, centerY, offset = 20 } = smoothStepPathParams;
const [points, labelX, labelY, offsetX, offsetY] = getPoints({
source: {
x: sourceX,
y: sourceY
},
sourcePosition,
target: {
x: targetX,
y: targetY
},
targetPosition,
center: {
x: centerX,
y: centerY
},
offset
});
return [
points.reduce((res, p, i$1) => {
let segment;
if (i$1 > 0 && i$1 < points.length - 1) segment = getBend(points[i$1 - 1], p, points[i$1 + 1], borderRadius);
else segment = `${i$1 === 0 ? "M" : "L"}${p.x} ${p.y}`;
res += segment;
return res;
}, ""),
labelX,
labelY,
offsetX,
offsetY
];
}
function getStraightPath(straightEdgeParams) {
const { sourceX, sourceY, targetX, targetY } = straightEdgeParams;
const [centerX, centerY, offsetX, offsetY] = getSimpleEdgeCenter({
sourceX,
sourceY,
targetX,
targetY
});
return [
`M ${sourceX},${sourceY}L ${targetX},${targetY}`,
centerX,
centerY,
offsetX,
offsetY
];
}
var StraightEdge$1 = defineComponent({
name: "StraightEdge",
props: [
"label",
"labelStyle",
"labelShowBg",
"labelBgStyle",
"labelBgPadding",
"labelBgBorderRadius",
"sourceY",
"sourceX",
"targetX",
"targetY",
"markerEnd",
"markerStart",
"interactionWidth"
],
compatConfig: { MODE: 3 },
setup(props, { attrs }) {
return () => {
const [path, labelX, labelY] = getStraightPath(props);
return h(_sfc_main$d, {
path,
labelX,
labelY,
...attrs,
...props
});
};
}
});
var SmoothStepEdge$1 = defineComponent({
name: "SmoothStepEdge",
props: [
"sourcePosition",
"targetPosition",
"label",
"labelStyle",
"labelShowBg",
"labelBgStyle",
"labelBgPadding",
"labelBgBorderRadius",
"sourceY",
"sourceX",
"targetX",
"targetY",
"borderRadius",
"markerEnd",
"markerStart",
"interactionWidth",
"offset"
],
compatConfig: { MODE: 3 },
setup(props, { attrs }) {
return () => {
const [path, labelX, labelY] = getSmoothStepPath({
...props,
sourcePosition: props.sourcePosition ?? Position.Bottom,
targetPosition: props.targetPosition ?? Position.Top
});
return h(_sfc_main$d, {
path,
labelX,
labelY,
...attrs,
...props
});
};
}
});
var StepEdge$1 = defineComponent({
name: "StepEdge",
props: [
"sourcePosition",
"targetPosition",
"label",
"labelStyle",
"labelShowBg",
"labelBgStyle",
"labelBgPadding",
"labelBgBorderRadius",
"sourceY",
"sourceX",
"targetX",
"targetY",
"markerEnd",
"markerStart",
"interactionWidth"
],
setup(props, { attrs }) {
return () => h(SmoothStepEdge$1, {
...props,
...attrs,
borderRadius: 0
});
}
});
var BezierEdge$1 = defineComponent({
name: "BezierEdge",
props: [
"sourcePosition",
"targetPosition",
"label",
"labelStyle",
"labelShowBg",
"labelBgStyle",
"labelBgPadding",
"labelBgBorderRadius",
"sourceY",
"sourceX",
"targetX",
"targetY",
"curvature",
"markerEnd",
"markerStart",
"interactionWidth"
],
compatConfig: { MODE: 3 },
setup(props, { attrs }) {
return () => {
const [path, labelX, labelY] = getBezierPath({
...props,
sourcePosition: props.sourcePosition ?? Position.Bottom,
targetPosition: props.targetPosition ?? Position.Top
});
return h(_sfc_main$d, {
path,
labelX,
labelY,
...attrs,
...props
});
};
}
});
var SimpleBezierEdge$1 = defineComponent({
name: "SimpleBezierEdge",
props: [
"sourcePosition",
"targetPosition",
"label",
"labelStyle",
"labelShowBg",
"labelBgStyle",
"labelBgPadding",
"labelBgBorderRadius",
"sourceY",
"sourceX",
"targetX",
"targetY",
"markerEnd",
"markerStart",
"interactionWidth"
],
compatConfig: { MODE: 3 },
setup(props, { attrs }) {
return () => {
const [path, labelX, labelY] = getSimpleBezierPath({
...props,
sourcePosition: props.sourcePosition ?? Position.Bottom,
targetPosition: props.targetPosition ?? Position.Top
});
return h(_sfc_main$d, {
path,
labelX,
labelY,
...attrs,
...props
});
};
}
});
var defaultNodeTypes = {
input: InputNode$1,
default: DefaultNode$1,
output: OutputNode$1
};
var defaultEdgeTypes = {
default: BezierEdge$1,
straight: StraightEdge$1,
step: StepEdge$1,
smoothstep: SmoothStepEdge$1,
simplebezier: SimpleBezierEdge$1
};
function useGetters(state, nodeLookup, edgeLookup) {
const getNode = computed(() => (id2) => nodeLookup.value.get(id2));
const getEdge = computed(() => (id2) => edgeLookup.value.get(id2));
const getEdgeTypes = computed(() => {
const edgeTypes = {
...defaultEdgeTypes,
...state.edgeTypes
};
const keys$2 = Object.keys(edgeTypes);
for (const e of state.edges) e.type && !keys$2.includes(e.type) && (edgeTypes[e.type] = e.type);
return edgeTypes;
});
const getNodeTypes$2 = computed(() => {
const nodeTypes = {
...defaultNodeTypes,
...state.nodeTypes
};
const keys$2 = Object.keys(nodeTypes);
for (const n of state.nodes) n.type && !keys$2.includes(n.type) && (nodeTypes[n.type] = n.type);
return nodeTypes;
});
const getNodes = computed(() => {
if (state.onlyRenderVisibleElements) return getNodesInside(state.nodes, {
x: 0,
y: 0,
width: state.dimensions.width,
height: state.dimensions.height
}, state.viewport, true);
return state.nodes;
});
const getEdges = computed(() => {
if (state.onlyRenderVisibleElements) {
const visibleEdges = [];
for (const edge of state.edges) {
const source = nodeLookup.value.get(edge.source);
const target = nodeLookup.value.get(edge.target);
if (isEdgeVisible({
sourcePos: source.computedPosition || {
x: 0,
y: 0
},
targetPos: target.computedPosition || {
x: 0,
y: 0
},
sourceWidth: source.dimensions.width,
sourceHeight: source.dimensions.height,
targetWidth: target.dimensions.width,
targetHeight: target.dimensions.height,
width: state.dimensions.width,
height: state.dimensions.height,
viewport: state.viewport
})) visibleEdges.push(edge);
}
return visibleEdges;
}
return state.edges;
});
const getElements = computed(() => [...getNodes.value, ...getEdges.value]);
const getSelectedNodes = computed(() => {
const selectedNodes = [];
for (const node of state.nodes) if (node.selected) selectedNodes.push(node);
return selectedNodes;
});
const getSelectedEdges = computed(() => {
const selectedEdges = [];
for (const edge of state.edges) if (edge.selected) selectedEdges.push(edge);
return selectedEdges;
});
const getSelectedElements = computed(() => [...getSelectedNodes.value, ...getSelectedEdges.value]);
const getNodesInitialized = computed(() => {
const initializedNodes = [];
for (const node of state.nodes) if (!!node.dimensions.width && !!node.dimensions.height && node.handleBounds !== void 0) initializedNodes.push(node);
return initializedNodes;
});
return {
getNode,
getEdge,
getElements,
getEdgeTypes,
getNodeTypes: getNodeTypes$2,
getEdges,
getNodes,
getSelectedElements,
getSelectedNodes,
getSelectedEdges,
getNodesInitialized,
areNodesInitialized: computed(() => getNodes.value.length > 0 && getNodesInitialized.value.length === getNodes.value.length)
};
}
var Storage = class Storage {
constructor() {
this.currentId = 0;
this.flows = /* @__PURE__ */ new Map();
}
static getInstance() {
var _a;
const vueApp = (_a = getCurrentInstance()) == null ? void 0 : _a.appContext.app;
Storage.instance = (vueApp == null ? void 0 : vueApp.config.globalProperties.$vueFlowStorage) ?? Storage.instance ?? new Storage();
if (vueApp) vueApp.config.globalProperties.$vueFlowStorage = Storage.instance;
return Storage.instance;
}
set(id2, flow) {
return this.flows.set(id2, flow);
}
get(id2) {
return this.flows.get(id2);
}
remove(id2) {
return this.flows.delete(id2);
}
create(id2, preloadedState) {
const reactiveState = reactive(useState());
const hooksOn = {};
for (const [n, h2] of Object.entries(reactiveState.hooks)) {
const name$13 = `on${n.charAt(0).toUpperCase() + n.slice(1)}`;
hooksOn[name$13] = h2.on;
}
const emits = {};
for (const [n, h2] of Object.entries(reactiveState.hooks)) emits[n] = h2.trigger;
const nodeLookup = computed(() => {
const nodesMap = /* @__PURE__ */ new Map();
for (const node of reactiveState.nodes) nodesMap.set(node.id, node);
return nodesMap;
});
const edgeLookup = computed(() => {
const edgesMap = /* @__PURE__ */ new Map();
for (const edge of reactiveState.edges) edgesMap.set(edge.id, edge);
return edgesMap;
});
const getters = useGetters(reactiveState, nodeLookup, edgeLookup);
const actions = useActions(reactiveState, nodeLookup, edgeLookup);
actions.setState({
...reactiveState,
...preloadedState
});
const flow = {
...hooksOn,
...getters,
...actions,
...toRefs$1(reactiveState),
nodeLookup,
edgeLookup,
emits,
id: id2,
vueFlowVersion: "1.48.0",
$destroy: () => {
this.remove(id2);
}
};
this.set(id2, flow);
return flow;
}
getId() {
return `vue-flow-${this.currentId++}`;
}
};
function useVueFlow(idOrOpts) {
const storage = Storage.getInstance();
const scope = getCurrentScope();
const isOptsObj = typeof idOrOpts === "object";
const options = isOptsObj ? idOrOpts : { id: idOrOpts };
const id2 = options.id;
const vueFlowId = id2 ?? (scope == null ? void 0 : scope.vueFlowId);
let vueFlow;
if (scope) {
const injectedState = inject(VueFlow, null);
if (typeof injectedState !== "undefined" && injectedState !== null && (!vueFlowId || injectedState.id === vueFlowId)) vueFlow = injectedState;
}
if (!vueFlow) {
if (vueFlowId) vueFlow = storage.get(vueFlowId);
}
if (!vueFlow || vueFlowId && vueFlow.id !== vueFlowId) {
const name$13 = id2 ?? storage.getId();
const state = storage.create(name$13, options);
vueFlow = state;
(scope ?? effectScope(true)).run(() => {
watch(state.applyDefault, (shouldApplyDefault, __, onCleanup) => {
const nodesChangeHandler = (changes) => {
state.applyNodeChanges(changes);
};
const edgesChangeHandler = (changes) => {
state.applyEdgeChanges(changes);
};
if (shouldApplyDefault) {
state.onNodesChange(nodesChangeHandler);
state.onEdgesChange(edgesChangeHandler);
} else {
state.hooks.value.nodesChange.off(nodesChangeHandler);
state.hooks.value.edgesChange.off(edgesChangeHandler);
}
onCleanup(() => {
state.hooks.value.nodesChange.off(nodesChangeHandler);
state.hooks.value.edgesChange.off(edgesChangeHandler);
});
}, { immediate: true });
tryOnScopeDispose(() => {
if (vueFlow) {
const storedInstance = storage.get(vueFlow.id);
if (storedInstance) storedInstance.$destroy();
else warn(`No store instance found for id ${vueFlow.id} in storage.`);
}
});
});
} else if (isOptsObj) vueFlow.setState(options);
if (scope) {
provide(VueFlow, vueFlow);
scope.vueFlowId = vueFlow.id;
}
if (isOptsObj) {
const instance = getCurrentInstance();
if ((instance == null ? void 0 : instance.type.name) !== "VueFlow") vueFlow.emits.error(new VueFlowError(ErrorCode.USEVUEFLOW_OPTIONS));
}
return vueFlow;
}
function useResizeHandler(viewportEl) {
const { emits, dimensions } = useVueFlow();
let resizeObserver;
onMounted(() => {
const updateDimensions = () => {
var _a, _b;
if (!viewportEl.value || !(((_b = (_a = viewportEl.value).checkVisibility) == null ? void 0 : _b.call(_a)) ?? true)) return;
const size = getDimensions(viewportEl.value);
if (size.width === 0 || size.height === 0) emits.error(new VueFlowError(ErrorCode.MISSING_VIEWPORT_DIMENSIONS));
dimensions.value = {
width: size.width || 500,
height: size.height || 500
};
};
updateDimensions();
window.addEventListener("resize", updateDimensions);
if (viewportEl.value) {
resizeObserver = new ResizeObserver(() => updateDimensions());
resizeObserver.observe(viewportEl.value);
}
onBeforeUnmount(() => {
window.removeEventListener("resize", updateDimensions);
if (resizeObserver && viewportEl.value) resizeObserver.unobserve(viewportEl.value);
});
});
}
var _sfc_main$c = /* @__PURE__ */ defineComponent({
name: "UserSelection",
compatConfig: { MODE: 3 },
props: { userSelectionRect: {} },
setup(__props) {
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
class: "vue-flow__selection vue-flow__container",
style: normalizeStyle({
width: `${_ctx.userSelectionRect.width}px`,
height: `${_ctx.userSelectionRect.height}px`,
transform: `translate(${_ctx.userSelectionRect.x}px, ${_ctx.userSelectionRect.y}px)`
})
}, null, 4);
};
}
});
var _hoisted_1$6 = ["tabIndex"];
var _sfc_main$b = /* @__PURE__ */ defineComponent({
name: "NodesSelection",
compatConfig: { MODE: 3 },
setup(__props) {
const { emits, viewport, getSelectedNodes, noPanClassName, disableKeyboardA11y, userSelectionActive } = useVueFlow();
const updatePositions = useUpdateNodePositions();
const el = ref(null);
const dragging = useDrag({
el,
onStart(args) {
emits.selectionDragStart(args);
emits.nodeDragStart(args);
},
onDrag(args) {
emits.selectionDrag(args);
emits.nodeDrag(args);
},
onStop(args) {
emits.selectionDragStop(args);
emits.nodeDragStop(args);
}
});
onMounted(() => {
var _a;
if (!disableKeyboardA11y.value) (_a = el.value) == null || _a.focus({ preventScroll: true });
});
const selectedNodesBBox = computed(() => getRectOfNodes(getSelectedNodes.value));
const innerStyle = computed(() => ({
width: `${selectedNodesBBox.value.width}px`,
height: `${selectedNodesBBox.value.height}px`,
top: `${selectedNodesBBox.value.y}px`,
left: `${selectedNodesBBox.value.x}px`
}));
function onContextMenu(event) {
emits.selectionContextMenu({
event,
nodes: getSelectedNodes.value
});
}
function onKeyDown(event) {
if (disableKeyboardA11y.value) return;
if (arrowKeyDiffs[event.key]) {
event.preventDefault();
updatePositions({
x: arrowKeyDiffs[event.key].x,
y: arrowKeyDiffs[event.key].y
}, event.shiftKey);
}
}
return (_ctx, _cache) => {
return !unref(userSelectionActive) && selectedNodesBBox.value.width && selectedNodesBBox.value.height ? (openBlock(), createElementBlock("div", {
key: 0,
class: normalizeClass(["vue-flow__nodesselection vue-flow__container", unref(noPanClassName)]),
style: normalizeStyle({ transform: `translate(${unref(viewport).x}px,${unref(viewport).y}px) scale(${unref(viewport).zoom})` })
}, [createBaseVNode("div", {
ref_key: "el",
ref: el,
class: normalizeClass([{ dragging: unref(dragging) }, "vue-flow__nodesselection-rect"]),
style: normalizeStyle(innerStyle.value),
tabIndex: unref(disableKeyboardA11y) ? void 0 : -1,
onContextmenu: onContextMenu,
onKeydown: onKeyDown
}, null, 46, _hoisted_1$6)], 6)) : createCommentVNode("", true);
};
}
});
function getMousePosition$1(event, containerBounds) {
return {
x: event.clientX - containerBounds.left,
y: event.clientY - containerBounds.top
};
}
var _sfc_main$a = /* @__PURE__ */ defineComponent({
name: "Pane",
compatConfig: { MODE: 3 },
props: {
isSelecting: { type: Boolean },
selectionKeyPressed: { type: Boolean }
},
setup(__props) {
const { vueFlowRef, nodes: nodes$13, viewport, emits, userSelectionActive, removeSelectedElements, userSelectionRect, elementsSelectable, nodesSelectionActive, getSelectedEdges, getSelectedNodes, removeNodes, removeEdges, selectionMode, deleteKeyCode, multiSelectionKeyCode, multiSelectionActive, edgeLookup, nodeLookup, connectionLookup, defaultEdgeOptions, connectionStartHandle } = useVueFlow();
const container = ref(null);
const selectedNodeIds = ref(/* @__PURE__ */ new Set());
const selectedEdgeIds = ref(/* @__PURE__ */ new Set());
const containerBounds = ref();
const hasActiveSelection = toRef(() => elementsSelectable.value && (__props.isSelecting || userSelectionActive.value));
const connectionInProgress = toRef(() => connectionStartHandle.value !== null);
let selectionInProgress = false;
let selectionStarted = false;
const deleteKeyPressed = useKeyPress(deleteKeyCode, { actInsideInputWithModifier: false });
const multiSelectKeyPressed = useKeyPress(multiSelectionKeyCode);
watch(deleteKeyPressed, (isKeyPressed) => {
if (!isKeyPressed) return;
removeNodes(getSelectedNodes.value);
removeEdges(getSelectedEdges.value);
nodesSelectionActive.value = false;
});
watch(multiSelectKeyPressed, (isKeyPressed) => {
multiSelectionActive.value = isKeyPressed;
});
function wrapHandler(handler, containerRef) {
return (event) => {
if (event.target !== containerRef) return;
handler?.(event);
};
}
function onClick(event) {
if (selectionInProgress || connectionInProgress.value) {
selectionInProgress = false;
return;
}
emits.paneClick(event);
removeSelectedElements();
nodesSelectionActive.value = false;
}
function onContextMenu(event) {
event.preventDefault();
event.stopPropagation();
emits.paneContextMenu(event);
}
function onWheel(event) {
emits.paneScroll(event);
}
function onPointerDown(event) {
var _a, _b, _c;
containerBounds.value = (_a = vueFlowRef.value) == null ? void 0 : _a.getBoundingClientRect();
if (!elementsSelectable.value || !__props.isSelecting || event.button !== 0 || event.target !== container.value || !containerBounds.value) return;
(_c = (_b = event.target) == null ? void 0 : _b.setPointerCapture) == null || _c.call(_b, event.pointerId);
const { x, y } = getMousePosition$1(event, containerBounds.value);
selectionStarted = true;
selectionInProgress = false;
removeSelectedElements();
userSelectionRect.value = {
width: 0,
height: 0,
startX: x,
startY: y,
x,
y
};
emits.selectionStart(event);
}
function onPointerMove(event) {
var _a;
if (!containerBounds.value || !userSelectionRect.value) return;
selectionInProgress = true;
const { x: mouseX, y: mouseY } = getEventPosition(event, containerBounds.value);
const { startX = 0, startY = 0 } = userSelectionRect.value;
const nextUserSelectRect = {
startX,
startY,
x: mouseX < startX ? mouseX : startX,
y: mouseY < startY ? mouseY : startY,
width: Math.abs(mouseX - startX),
height: Math.abs(mouseY - startY)
};
const prevSelectedNodeIds = selectedNodeIds.value;
const prevSelectedEdgeIds = selectedEdgeIds.value;
selectedNodeIds.value = new Set(getNodesInside(nodes$13.value, nextUserSelectRect, viewport.value, selectionMode.value === SelectionMode.Partial, true).map((node) => node.id));
selectedEdgeIds.value = /* @__PURE__ */ new Set();
const edgesSelectable = ((_a = defaultEdgeOptions.value) == null ? void 0 : _a.selectable) ?? true;
for (const nodeId of selectedNodeIds.value) {
const connections$13 = connectionLookup.value.get(nodeId);
if (!connections$13) continue;
for (const { edgeId } of connections$13.values()) {
const edge = edgeLookup.value.get(edgeId);
if (edge && (edge.selectable ?? edgesSelectable)) selectedEdgeIds.value.add(edgeId);
}
}
if (!areSetsEqual(prevSelectedNodeIds, selectedNodeIds.value)) {
const changes = getSelectionChanges(nodeLookup.value, selectedNodeIds.value, true);
emits.nodesChange(changes);
}
if (!areSetsEqual(prevSelectedEdgeIds, selectedEdgeIds.value)) {
const changes = getSelectionChanges(edgeLookup.value, selectedEdgeIds.value);
emits.edgesChange(changes);
}
userSelectionRect.value = nextUserSelectRect;
userSelectionActive.value = true;
nodesSelectionActive.value = false;
}
function onPointerUp(event) {
var _a;
if (event.button !== 0 || !selectionStarted) return;
(_a = event.target) == null || _a.releasePointerCapture(event.pointerId);
if (!userSelectionActive.value && userSelectionRect.value && event.target === container.value) onClick(event);
userSelectionActive.value = false;
userSelectionRect.value = null;
nodesSelectionActive.value = selectedNodeIds.value.size > 0;
emits.selectionEnd(event);
if (__props.selectionKeyPressed) selectionInProgress = false;
selectionStarted = false;
}
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
ref_key: "container",
ref: container,
class: normalizeClass(["vue-flow__pane vue-flow__container", { selection: _ctx.isSelecting }]),
onClick: _cache[0] || (_cache[0] = (event) => hasActiveSelection.value ? void 0 : wrapHandler(onClick, container.value)(event)),
onContextmenu: _cache[1] || (_cache[1] = ($event) => wrapHandler(onContextMenu, container.value)($event)),
onWheelPassive: _cache[2] || (_cache[2] = ($event) => wrapHandler(onWheel, container.value)($event)),
onPointerenter: _cache[3] || (_cache[3] = (event) => hasActiveSelection.value ? void 0 : unref(emits).paneMouseEnter(event)),
onPointerdown: _cache[4] || (_cache[4] = (event) => hasActiveSelection.value ? onPointerDown(event) : unref(emits).paneMouseMove(event)),
onPointermove: _cache[5] || (_cache[5] = (event) => hasActiveSelection.value ? onPointerMove(event) : unref(emits).paneMouseMove(event)),
onPointerup: _cache[6] || (_cache[6] = (event) => hasActiveSelection.value ? onPointerUp(event) : void 0),
onPointerleave: _cache[7] || (_cache[7] = ($event) => unref(emits).paneMouseLeave($event))
}, [
renderSlot(_ctx.$slots, "default"),
unref(userSelectionActive) && unref(userSelectionRect) ? (openBlock(), createBlock(_sfc_main$c, {
key: 0,
"user-selection-rect": unref(userSelectionRect)
}, null, 8, ["user-selection-rect"])) : createCommentVNode("", true),
unref(nodesSelectionActive) && unref(getSelectedNodes).length ? (openBlock(), createBlock(_sfc_main$b, { key: 1 })) : createCommentVNode("", true)
], 34);
};
}
});
var _sfc_main$9 = /* @__PURE__ */ defineComponent({
name: "Transform",
compatConfig: { MODE: 3 },
setup(__props) {
const { viewport, fitViewOnInit, fitViewOnInitDone } = useVueFlow();
const isHidden = computed(() => {
if (fitViewOnInit.value) return !fitViewOnInitDone.value;
return false;
});
const transform = computed(() => `translate(${viewport.value.x}px,${viewport.value.y}px) scale(${viewport.value.zoom})`);
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
class: "vue-flow__transformationpane vue-flow__container",
style: normalizeStyle({
transform: transform.value,
opacity: isHidden.value ? 0 : void 0
})
}, [renderSlot(_ctx.$slots, "default")], 4);
};
}
});
var _sfc_main$8 = /* @__PURE__ */ defineComponent({
name: "Viewport",
compatConfig: { MODE: 3 },
setup(__props) {
const { minZoom, maxZoom, defaultViewport, translateExtent, zoomActivationKeyCode, selectionKeyCode, panActivationKeyCode, panOnScroll, panOnScrollMode, panOnScrollSpeed, panOnDrag, zoomOnDoubleClick, zoomOnPinch, zoomOnScroll, preventScrolling, noWheelClassName, noPanClassName, emits, connectionStartHandle, userSelectionActive, paneDragging, d3Zoom: storeD3Zoom, d3Selection: storeD3Selection, d3ZoomHandler: storeD3ZoomHandler, viewport, viewportRef, paneClickDistance } = useVueFlow();
useResizeHandler(viewportRef);
const isZoomingOrPanning = shallowRef(false);
const isPanScrolling = shallowRef(false);
let panScrollTimeout = null;
let zoomedWithRightMouseButton = false;
let mouseButton = 0;
let prevTransform = {
x: 0,
y: 0,
zoom: 0
};
const panKeyPressed = useKeyPress(panActivationKeyCode);
const selectionKeyPressed = useKeyPress(selectionKeyCode);
const zoomKeyPressed = useKeyPress(zoomActivationKeyCode);
const shouldPanOnDrag = toRef(() => (!selectionKeyPressed.value || selectionKeyPressed.value && selectionKeyCode.value === true) && (panKeyPressed.value || panOnDrag.value));
const shouldPanOnScroll = toRef(() => panKeyPressed.value || panOnScroll.value);
const isSelecting = toRef(() => selectionKeyPressed.value || selectionKeyCode.value === true && shouldPanOnDrag.value !== true);
const connectionInProgress = toRef(() => connectionStartHandle.value !== null);
onMounted(() => {
if (!viewportRef.value) {
warn("Viewport element is missing");
return;
}
const viewportElement = viewportRef.value;
const bbox = viewportElement.getBoundingClientRect();
const d3Zoom = zoom().clickDistance(paneClickDistance.value).scaleExtent([minZoom.value, maxZoom.value]).translateExtent(translateExtent.value);
const d3Selection = select(viewportElement).call(d3Zoom);
const d3ZoomHandler = d3Selection.on("wheel.zoom");
const updatedTransform = identity.translate(defaultViewport.value.x ?? 0, defaultViewport.value.y ?? 0).scale(clamp(defaultViewport.value.zoom ?? 1, minZoom.value, maxZoom.value));
const extent = [[0, 0], [bbox.width, bbox.height]];
const constrainedTransform = d3Zoom.constrain()(updatedTransform, extent, translateExtent.value);
d3Zoom.transform(d3Selection, constrainedTransform);
d3Zoom.wheelDelta(wheelDelta);
storeD3Zoom.value = d3Zoom;
storeD3Selection.value = d3Selection;
storeD3ZoomHandler.value = d3ZoomHandler;
viewport.value = {
x: constrainedTransform.x,
y: constrainedTransform.y,
zoom: constrainedTransform.k
};
d3Zoom.on("start", (event) => {
var _a;
if (!event.sourceEvent) return null;
mouseButton = event.sourceEvent.button;
isZoomingOrPanning.value = true;
const flowTransform = eventToFlowTransform(event.transform);
if (((_a = event.sourceEvent) == null ? void 0 : _a.type) === "mousedown") paneDragging.value = true;
prevTransform = flowTransform;
emits.viewportChangeStart(flowTransform);
emits.moveStart({
event,
flowTransform
});
});
d3Zoom.on("end", (event) => {
if (!event.sourceEvent) return null;
isZoomingOrPanning.value = false;
paneDragging.value = false;
if (isRightClickPan(shouldPanOnDrag.value, mouseButton ?? 0) && !zoomedWithRightMouseButton) emits.paneContextMenu(event.sourceEvent);
zoomedWithRightMouseButton = false;
if (viewChanged(prevTransform, event.transform)) {
const flowTransform = eventToFlowTransform(event.transform);
prevTransform = flowTransform;
emits.viewportChangeEnd(flowTransform);
emits.moveEnd({
event,
flowTransform
});
}
});
d3Zoom.filter((event) => {
var _a;
const zoomScroll = zoomKeyPressed.value || zoomOnScroll.value;
const pinchZoom = zoomOnPinch.value && event.ctrlKey;
const eventButton = event.button;
const isWheelEvent = event.type === "wheel";
if (eventButton === 1 && event.type === "mousedown" && (isWrappedWithClass(event, "vue-flow__node") || isWrappedWithClass(event, "vue-flow__edge"))) return true;
if (!shouldPanOnDrag.value && !zoomScroll && !shouldPanOnScroll.value && !zoomOnDoubleClick.value && !zoomOnPinch.value) return false;
if (userSelectionActive.value) return false;
if (connectionInProgress.value && !isWheelEvent) return false;
if (!zoomOnDoubleClick.value && event.type === "dblclick") return false;
if (isWrappedWithClass(event, noWheelClassName.value) && isWheelEvent) return false;
if (isWrappedWithClass(event, noPanClassName.value) && (!isWheelEvent || shouldPanOnScroll.value && isWheelEvent && !zoomKeyPressed.value)) return false;
if (!zoomOnPinch.value && event.ctrlKey && isWheelEvent) return false;
if (!zoomScroll && !shouldPanOnScroll.value && !pinchZoom && isWheelEvent) return false;
if (!zoomOnPinch && event.type === "touchstart" && ((_a = event.touches) == null ? void 0 : _a.length) > 1) {
event.preventDefault();
return false;
}
if (!shouldPanOnDrag.value && (event.type === "mousedown" || event.type === "touchstart")) return false;
if (selectionKeyCode.value === true && Array.isArray(panOnDrag.value) && panOnDrag.value.includes(0) && eventButton === 0) return false;
if (Array.isArray(panOnDrag.value) && !panOnDrag.value.includes(eventButton) && (event.type === "mousedown" || event.type === "touchstart")) return false;
const buttonAllowed = Array.isArray(panOnDrag.value) && panOnDrag.value.includes(eventButton) || selectionKeyCode.value === true && Array.isArray(panOnDrag.value) && !panOnDrag.value.includes(0) || !eventButton || eventButton <= 1;
return (!event.ctrlKey || panKeyPressed.value || isWheelEvent) && buttonAllowed;
});
watch([userSelectionActive, shouldPanOnDrag], () => {
if (userSelectionActive.value && !isZoomingOrPanning.value) d3Zoom.on("zoom", null);
else if (!userSelectionActive.value) d3Zoom.on("zoom", (event) => {
viewport.value = {
x: event.transform.x,
y: event.transform.y,
zoom: event.transform.k
};
const flowTransform = eventToFlowTransform(event.transform);
zoomedWithRightMouseButton = isRightClickPan(shouldPanOnDrag.value, mouseButton ?? 0);
emits.viewportChange(flowTransform);
emits.move({
event,
flowTransform
});
});
}, { immediate: true });
watch([
userSelectionActive,
shouldPanOnScroll,
panOnScrollMode,
zoomKeyPressed,
zoomOnPinch,
preventScrolling,
noWheelClassName
], () => {
if (shouldPanOnScroll.value && !zoomKeyPressed.value && !userSelectionActive.value) d3Selection.on("wheel.zoom", (event) => {
if (isWrappedWithClass(event, noWheelClassName.value)) return false;
const zoomScroll = zoomKeyPressed.value || zoomOnScroll.value;
const pinchZoom = zoomOnPinch.value && event.ctrlKey;
if (!(!preventScrolling.value || shouldPanOnScroll.value || zoomScroll || pinchZoom)) return false;
event.preventDefault();
event.stopImmediatePropagation();
const currentZoom = d3Selection.property("__zoom").k || 1;
const _isMacOs = isMacOs();
if (!panKeyPressed.value && event.ctrlKey && zoomOnPinch.value && _isMacOs) {
const point = pointer(event);
const zoom2 = currentZoom * 2 ** wheelDelta(event);
d3Zoom.scaleTo(d3Selection, zoom2, point, event);
return;
}
const deltaNormalize = event.deltaMode === 1 ? 20 : 1;
let deltaX = panOnScrollMode.value === PanOnScrollMode.Vertical ? 0 : event.deltaX * deltaNormalize;
let deltaY = panOnScrollMode.value === PanOnScrollMode.Horizontal ? 0 : event.deltaY * deltaNormalize;
if (!_isMacOs && event.shiftKey && panOnScrollMode.value !== PanOnScrollMode.Vertical && !deltaX && deltaY) {
deltaX = deltaY;
deltaY = 0;
}
d3Zoom.translateBy(d3Selection, -(deltaX / currentZoom) * panOnScrollSpeed.value, -(deltaY / currentZoom) * panOnScrollSpeed.value);
const nextViewport = eventToFlowTransform(d3Selection.property("__zoom"));
if (panScrollTimeout) clearTimeout(panScrollTimeout);
if (!isPanScrolling.value) {
isPanScrolling.value = true;
emits.moveStart({
event,
flowTransform: nextViewport
});
emits.viewportChangeStart(nextViewport);
} else {
emits.move({
event,
flowTransform: nextViewport
});
emits.viewportChange(nextViewport);
panScrollTimeout = setTimeout(() => {
emits.moveEnd({
event,
flowTransform: nextViewport
});
emits.viewportChangeEnd(nextViewport);
isPanScrolling.value = false;
}, 150);
}
}, { passive: false });
else if (typeof d3ZoomHandler !== "undefined") d3Selection.on("wheel.zoom", function(event, d) {
const invalidEvent = !preventScrolling.value && event.type === "wheel" && !event.ctrlKey;
const zoomScroll = zoomKeyPressed.value || zoomOnScroll.value;
const pinchZoom = zoomOnPinch.value && event.ctrlKey;
if (!zoomScroll && !panOnScroll.value && !pinchZoom && event.type === "wheel" || invalidEvent || isWrappedWithClass(event, noWheelClassName.value)) return null;
event.preventDefault();
d3ZoomHandler.call(this, event, d);
}, { passive: false });
}, { immediate: true });
});
function isRightClickPan(pan, usedButton) {
return usedButton === 2 && Array.isArray(pan) && pan.includes(2);
}
function viewChanged(prevViewport, eventTransform) {
return prevViewport.x !== eventTransform.x && !Number.isNaN(eventTransform.x) || prevViewport.y !== eventTransform.y && !Number.isNaN(eventTransform.y) || prevViewport.zoom !== eventTransform.k && !Number.isNaN(eventTransform.k);
}
function eventToFlowTransform(eventTransform) {
return {
x: eventTransform.x,
y: eventTransform.y,
zoom: eventTransform.k
};
}
function isWrappedWithClass(event, className) {
return event.target.closest(`.${className}`);
}
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
ref_key: "viewportRef",
ref: viewportRef,
class: "vue-flow__viewport vue-flow__container"
}, [createVNode(_sfc_main$a, {
"is-selecting": isSelecting.value,
"selection-key-pressed": unref(selectionKeyPressed),
class: normalizeClass({
connecting: connectionInProgress.value,
dragging: unref(paneDragging),
draggable: unref(panOnDrag) === true || Array.isArray(unref(panOnDrag)) && unref(panOnDrag).includes(0)
})
}, {
default: withCtx(() => [createVNode(_sfc_main$9, null, {
default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
_: 3
})]),
_: 3
}, 8, [
"is-selecting",
"selection-key-pressed",
"class"
])], 512);
};
}
});
var _hoisted_1$5 = ["id"];
var _hoisted_2 = ["id"];
var _hoisted_3 = ["id"];
var _sfc_main$7 = /* @__PURE__ */ defineComponent({
name: "A11yDescriptions",
compatConfig: { MODE: 3 },
setup(__props) {
const { id: id2, disableKeyboardA11y, ariaLiveMessage } = useVueFlow();
return (_ctx, _cache) => {
return openBlock(), createElementBlock(Fragment, null, [
createBaseVNode("div", {
id: `${unref(ARIA_NODE_DESC_KEY)}-${unref(id2)}`,
style: { "display": "none" }
}, " Press enter or space to select a node. " + toDisplayString(!unref(disableKeyboardA11y) ? "You can then use the arrow keys to move the node around." : "") + " You can then use the arrow keys to move the node around, press delete to remove it and press escape to cancel. ", 9, _hoisted_1$5),
createBaseVNode("div", {
id: `${unref(ARIA_EDGE_DESC_KEY)}-${unref(id2)}`,
style: { "display": "none" }
}, " Press enter or space to select an edge. You can then press delete to remove it or press escape to cancel. ", 8, _hoisted_2),
!unref(disableKeyboardA11y) ? (openBlock(), createElementBlock("div", {
key: 0,
id: `${unref(ARIA_LIVE_MESSAGE)}-${unref(id2)}`,
"aria-live": "assertive",
"aria-atomic": "true",
style: {
"position": "absolute",
"width": "1px",
"height": "1px",
"margin": "-1px",
"border": "0",
"padding": "0",
"overflow": "hidden",
"clip": "rect(0px, 0px, 0px, 0px)",
"clip-path": "inset(100%)"
}
}, toDisplayString(unref(ariaLiveMessage)), 9, _hoisted_3)) : createCommentVNode("", true)
], 64);
};
}
});
function useOnInitHandler() {
const vfInstance = useVueFlow();
watch(() => vfInstance.viewportHelper.value.viewportInitialized, (isInitialized) => {
if (isInitialized) setTimeout(() => {
vfInstance.emits.init(vfInstance);
vfInstance.emits.paneReady(vfInstance);
}, 1);
});
}
function shiftX(x, shift, position) {
if (position === Position.Left) return x - shift;
if (position === Position.Right) return x + shift;
return x;
}
function shiftY(y, shift, position) {
if (position === Position.Top) return y - shift;
if (position === Position.Bottom) return y + shift;
return y;
}
var EdgeAnchor = function({ radius = 10, centerX = 0, centerY = 0, position = Position.Top, type }) {
return h("circle", {
class: `vue-flow__edgeupdater vue-flow__edgeupdater-${type}`,
cx: shiftX(centerX, radius, position),
cy: shiftY(centerY, radius, position),
r: radius,
stroke: "transparent",
fill: "transparent"
});
};
EdgeAnchor.props = [
"radius",
"centerX",
"centerY",
"position",
"type"
];
EdgeAnchor.compatConfig = { MODE: 3 };
var EdgeAnchor$1 = EdgeAnchor;
var EdgeWrapper$1 = defineComponent({
name: "Edge",
compatConfig: { MODE: 3 },
props: ["id"],
setup(props) {
const { id: vueFlowId, addSelectedEdges, connectionMode, edgeUpdaterRadius, emits, nodesSelectionActive, noPanClassName, getEdgeTypes, removeSelectedEdges, findEdge, findNode, isValidConnection, multiSelectionActive, disableKeyboardA11y, elementsSelectable, edgesUpdatable, edgesFocusable, hooks } = useVueFlow();
const edge = computed(() => findEdge(props.id));
const { emit, on } = useEdgeHooks(edge.value, emits);
const slots = inject(Slots);
const instance = getCurrentInstance();
const mouseOver = ref(false);
const updating = ref(false);
const nodeId = ref("");
const handleId = ref(null);
const edgeUpdaterType = ref("source");
const edgeEl = ref(null);
const isSelectable = toRef(() => typeof edge.value.selectable === "undefined" ? elementsSelectable.value : edge.value.selectable);
const isUpdatable = toRef(() => typeof edge.value.updatable === "undefined" ? edgesUpdatable.value : edge.value.updatable);
const isFocusable = toRef(() => typeof edge.value.focusable === "undefined" ? edgesFocusable.value : edge.value.focusable);
provide(EdgeId, props.id);
provide(EdgeRef, edgeEl);
const edgeClass = computed(() => edge.value.class instanceof Function ? edge.value.class(edge.value) : edge.value.class);
const edgeStyle = computed(() => edge.value.style instanceof Function ? edge.value.style(edge.value) : edge.value.style);
const edgeCmp = computed(() => {
const name$13 = edge.value.type || "default";
const slot = slots == null ? void 0 : slots[`edge-${name$13}`];
if (slot) return slot;
let edgeType = edge.value.template ?? getEdgeTypes.value[name$13];
if (typeof edgeType === "string") {
if (instance) {
const components = Object.keys(instance.appContext.components);
if (components && components.includes(name$13)) edgeType = resolveComponent(name$13, false);
}
}
if (edgeType && typeof edgeType !== "string") return edgeType;
emits.error(new VueFlowError(ErrorCode.EDGE_TYPE_MISSING, edgeType));
return false;
});
const { handlePointerDown } = useHandle({
nodeId,
handleId,
type: edgeUpdaterType,
isValidConnection,
edgeUpdaterType,
onEdgeUpdate,
onEdgeUpdateEnd
});
return () => {
const sourceNode = findNode(edge.value.source);
const targetNode = findNode(edge.value.target);
const pathOptions = "pathOptions" in edge.value ? edge.value.pathOptions : {};
if (!sourceNode && !targetNode) {
emits.error(new VueFlowError(ErrorCode.EDGE_SOURCE_TARGET_MISSING, edge.value.id, edge.value.source, edge.value.target));
return null;
}
if (!sourceNode) {
emits.error(new VueFlowError(ErrorCode.EDGE_SOURCE_MISSING, edge.value.id, edge.value.source));
return null;
}
if (!targetNode) {
emits.error(new VueFlowError(ErrorCode.EDGE_TARGET_MISSING, edge.value.id, edge.value.target));
return null;
}
if (!edge.value || edge.value.hidden || sourceNode.hidden || targetNode.hidden) return null;
let sourceNodeHandles;
if (connectionMode.value === ConnectionMode.Strict) sourceNodeHandles = sourceNode.handleBounds.source;
else sourceNodeHandles = [...sourceNode.handleBounds.source || [], ...sourceNode.handleBounds.target || []];
const sourceHandle = getEdgeHandle(sourceNodeHandles, edge.value.sourceHandle);
let targetNodeHandles;
if (connectionMode.value === ConnectionMode.Strict) targetNodeHandles = targetNode.handleBounds.target;
else targetNodeHandles = [...targetNode.handleBounds.target || [], ...targetNode.handleBounds.source || []];
const targetHandle = getEdgeHandle(targetNodeHandles, edge.value.targetHandle);
const sourcePosition = (sourceHandle == null ? void 0 : sourceHandle.position) || Position.Bottom;
const targetPosition = (targetHandle == null ? void 0 : targetHandle.position) || Position.Top;
const { x: sourceX, y: sourceY } = getHandlePosition(sourceNode, sourceHandle, sourcePosition);
const { x: targetX, y: targetY } = getHandlePosition(targetNode, targetHandle, targetPosition);
edge.value.sourceX = sourceX;
edge.value.sourceY = sourceY;
edge.value.targetX = targetX;
edge.value.targetY = targetY;
return h("g", {
"ref": edgeEl,
"key": props.id,
"data-id": props.id,
"class": [
"vue-flow__edge",
`vue-flow__edge-${edgeCmp.value === false ? "default" : edge.value.type || "default"}`,
noPanClassName.value,
edgeClass.value,
{
updating: mouseOver.value,
selected: edge.value.selected,
animated: edge.value.animated,
inactive: !isSelectable.value && !hooks.value.edgeClick.hasListeners()
}
],
"tabIndex": isFocusable.value ? 0 : void 0,
"aria-label": edge.value.ariaLabel === null ? void 0 : edge.value.ariaLabel ?? `Edge from ${edge.value.source} to ${edge.value.target}`,
"aria-describedby": isFocusable.value ? `${ARIA_EDGE_DESC_KEY}-${vueFlowId}` : void 0,
"aria-roledescription": "edge",
"role": isFocusable.value ? "group" : "img",
...edge.value.domAttributes,
"onClick": onEdgeClick,
"onContextmenu": onEdgeContextMenu,
"onDblclick": onDoubleClick,
"onMouseenter": onEdgeMouseEnter,
"onMousemove": onEdgeMouseMove,
"onMouseleave": onEdgeMouseLeave,
"onKeyDown": isFocusable.value ? onKeyDown : void 0
}, [updating.value ? null : h(edgeCmp.value === false ? getEdgeTypes.value.default : edgeCmp.value, {
id: props.id,
sourceNode,
targetNode,
source: edge.value.source,
target: edge.value.target,
type: edge.value.type,
updatable: isUpdatable.value,
selected: edge.value.selected,
animated: edge.value.animated,
label: edge.value.label,
labelStyle: edge.value.labelStyle,
labelShowBg: edge.value.labelShowBg,
labelBgStyle: edge.value.labelBgStyle,
labelBgPadding: edge.value.labelBgPadding,
labelBgBorderRadius: edge.value.labelBgBorderRadius,
data: edge.value.data,
events: {
...edge.value.events,
...on
},
style: edgeStyle.value,
markerStart: `url('#${getMarkerId(edge.value.markerStart, vueFlowId)}')`,
markerEnd: `url('#${getMarkerId(edge.value.markerEnd, vueFlowId)}')`,
sourcePosition,
targetPosition,
sourceX,
sourceY,
targetX,
targetY,
sourceHandleId: edge.value.sourceHandle,
targetHandleId: edge.value.targetHandle,
interactionWidth: edge.value.interactionWidth,
...pathOptions
}), [isUpdatable.value === "source" || isUpdatable.value === true ? [h("g", {
onMousedown: onEdgeUpdaterSourceMouseDown,
onMouseenter: onEdgeUpdaterMouseEnter,
onMouseout: onEdgeUpdaterMouseOut
}, h(EdgeAnchor$1, {
"position": sourcePosition,
"centerX": sourceX,
"centerY": sourceY,
"radius": edgeUpdaterRadius.value,
"type": "source",
"data-type": "source"
}))] : null, isUpdatable.value === "target" || isUpdatable.value === true ? [h("g", {
onMousedown: onEdgeUpdaterTargetMouseDown,
onMouseenter: onEdgeUpdaterMouseEnter,
onMouseout: onEdgeUpdaterMouseOut
}, h(EdgeAnchor$1, {
"position": targetPosition,
"centerX": targetX,
"centerY": targetY,
"radius": edgeUpdaterRadius.value,
"type": "target",
"data-type": "target"
}))] : null]]);
};
function onEdgeUpdaterMouseEnter() {
mouseOver.value = true;
}
function onEdgeUpdaterMouseOut() {
mouseOver.value = false;
}
function onEdgeUpdate(event, connection) {
emit.update({
event,
edge: edge.value,
connection
});
}
function onEdgeUpdateEnd(event) {
emit.updateEnd({
event,
edge: edge.value
});
updating.value = false;
}
function handleEdgeUpdater(event, isSourceHandle) {
if (event.button !== 0) return;
updating.value = true;
nodeId.value = isSourceHandle ? edge.value.target : edge.value.source;
handleId.value = (isSourceHandle ? edge.value.targetHandle : edge.value.sourceHandle) ?? null;
edgeUpdaterType.value = isSourceHandle ? "target" : "source";
emit.updateStart({
event,
edge: edge.value
});
handlePointerDown(event);
}
function onEdgeClick(event) {
var _a;
const data = {
event,
edge: edge.value
};
if (isSelectable.value) {
nodesSelectionActive.value = false;
if (edge.value.selected && multiSelectionActive.value) {
removeSelectedEdges([edge.value]);
(_a = edgeEl.value) == null || _a.blur();
} else addSelectedEdges([edge.value]);
}
emit.click(data);
}
function onEdgeContextMenu(event) {
emit.contextMenu({
event,
edge: edge.value
});
}
function onDoubleClick(event) {
emit.doubleClick({
event,
edge: edge.value
});
}
function onEdgeMouseEnter(event) {
emit.mouseEnter({
event,
edge: edge.value
});
}
function onEdgeMouseMove(event) {
emit.mouseMove({
event,
edge: edge.value
});
}
function onEdgeMouseLeave(event) {
emit.mouseLeave({
event,
edge: edge.value
});
}
function onEdgeUpdaterSourceMouseDown(event) {
handleEdgeUpdater(event, true);
}
function onEdgeUpdaterTargetMouseDown(event) {
handleEdgeUpdater(event, false);
}
function onKeyDown(event) {
var _a;
if (!disableKeyboardA11y.value && elementSelectionKeys.includes(event.key) && isSelectable.value) if (event.key === "Escape") {
(_a = edgeEl.value) == null || _a.blur();
removeSelectedEdges([findEdge(props.id)]);
} else addSelectedEdges([findEdge(props.id)]);
}
}
});
var ConnectionLine$1 = defineComponent({
name: "ConnectionLine",
compatConfig: { MODE: 3 },
setup() {
var _a;
const { id: id2, connectionMode, connectionStartHandle, connectionEndHandle, connectionPosition, connectionLineType, connectionLineStyle, connectionLineOptions, connectionStatus, viewport, findNode } = useVueFlow();
const connectionLineComponent = (_a = inject(Slots)) == null ? void 0 : _a["connection-line"];
const fromNode = computed(() => {
var _a2;
return findNode((_a2 = connectionStartHandle.value) == null ? void 0 : _a2.nodeId);
});
const toNode = computed(() => {
var _a2;
return findNode((_a2 = connectionEndHandle.value) == null ? void 0 : _a2.nodeId) ?? null;
});
const toXY = computed(() => {
return {
x: (connectionPosition.value.x - viewport.value.x) / viewport.value.zoom,
y: (connectionPosition.value.y - viewport.value.y) / viewport.value.zoom
};
});
const markerStart = computed(() => connectionLineOptions.value.markerStart ? `url(#${getMarkerId(connectionLineOptions.value.markerStart, id2)})` : "");
const markerEnd = computed(() => connectionLineOptions.value.markerEnd ? `url(#${getMarkerId(connectionLineOptions.value.markerEnd, id2)})` : "");
return () => {
var _a2, _b, _c;
if (!fromNode.value || !connectionStartHandle.value) return null;
const startHandleId = connectionStartHandle.value.id;
const handleType = connectionStartHandle.value.type;
const fromHandleBounds = fromNode.value.handleBounds;
let handleBounds = (fromHandleBounds == null ? void 0 : fromHandleBounds[handleType]) ?? [];
if (connectionMode.value === ConnectionMode.Loose) {
const oppositeBounds = (fromHandleBounds == null ? void 0 : fromHandleBounds[handleType === "source" ? "target" : "source"]) ?? [];
handleBounds = [...handleBounds, ...oppositeBounds];
}
if (!handleBounds) return null;
const fromHandle = (startHandleId ? handleBounds.find((d) => d.id === startHandleId) : handleBounds[0]) ?? null;
const fromPosition = (fromHandle == null ? void 0 : fromHandle.position) ?? Position.Top;
const { x: fromX, y: fromY } = getHandlePosition(fromNode.value, fromHandle, fromPosition);
let toHandle = null;
if (toNode.value) if (connectionMode.value === ConnectionMode.Strict) toHandle = ((_a2 = toNode.value.handleBounds[handleType === "source" ? "target" : "source"]) == null ? void 0 : _a2.find((d) => {
var _a3;
return d.id === ((_a3 = connectionEndHandle.value) == null ? void 0 : _a3.id);
})) || null;
else toHandle = ((_b = [...toNode.value.handleBounds.source ?? [], ...toNode.value.handleBounds.target ?? []]) == null ? void 0 : _b.find((d) => {
var _a3;
return d.id === ((_a3 = connectionEndHandle.value) == null ? void 0 : _a3.id);
})) || null;
const toPosition = ((_c = connectionEndHandle.value) == null ? void 0 : _c.position) ?? (fromPosition ? oppositePosition[fromPosition] : null);
if (!fromPosition || !toPosition) return null;
const type = connectionLineType.value ?? connectionLineOptions.value.type ?? ConnectionLineType.Bezier;
let dAttr = "";
const pathParams = {
sourceX: fromX,
sourceY: fromY,
sourcePosition: fromPosition,
targetX: toXY.value.x,
targetY: toXY.value.y,
targetPosition: toPosition
};
if (type === ConnectionLineType.Bezier) [dAttr] = getBezierPath(pathParams);
else if (type === ConnectionLineType.Step) [dAttr] = getSmoothStepPath({
...pathParams,
borderRadius: 0
});
else if (type === ConnectionLineType.SmoothStep) [dAttr] = getSmoothStepPath(pathParams);
else if (type === ConnectionLineType.SimpleBezier) [dAttr] = getSimpleBezierPath(pathParams);
else dAttr = `M${fromX},${fromY} ${toXY.value.x},${toXY.value.y}`;
return h("svg", { class: "vue-flow__edges vue-flow__connectionline vue-flow__container" }, h("g", { class: "vue-flow__connection" }, connectionLineComponent ? h(connectionLineComponent, {
sourceX: fromX,
sourceY: fromY,
sourcePosition: fromPosition,
targetX: toXY.value.x,
targetY: toXY.value.y,
targetPosition: toPosition,
sourceNode: fromNode.value,
sourceHandle: fromHandle,
targetNode: toNode.value,
targetHandle: toHandle,
markerEnd: markerEnd.value,
markerStart: markerStart.value,
connectionStatus: connectionStatus.value
}) : h("path", {
"d": dAttr,
"class": [
connectionLineOptions.value.class,
connectionStatus.value,
"vue-flow__connection-path"
],
"style": {
...connectionLineStyle.value,
...connectionLineOptions.value.style
},
"marker-end": markerEnd.value,
"marker-start": markerStart.value
})));
};
}
});
var _hoisted_1$4 = [
"id",
"markerWidth",
"markerHeight",
"markerUnits",
"orient"
];
var _sfc_main$6 = /* @__PURE__ */ defineComponent({
name: "MarkerType",
compatConfig: { MODE: 3 },
props: {
id: {},
type: {},
color: { default: "none" },
width: { default: 12.5 },
height: { default: 12.5 },
markerUnits: { default: "strokeWidth" },
orient: { default: "auto-start-reverse" },
strokeWidth: { default: 1 }
},
setup(__props) {
return (_ctx, _cache) => {
return openBlock(), createElementBlock("marker", {
id: _ctx.id,
class: "vue-flow__arrowhead",
viewBox: "-10 -10 20 20",
refX: "0",
refY: "0",
markerWidth: `${_ctx.width}`,
markerHeight: `${_ctx.height}`,
markerUnits: _ctx.markerUnits,
orient: _ctx.orient
}, [_ctx.type === unref(MarkerType).ArrowClosed ? (openBlock(), createElementBlock("polyline", {
key: 0,
style: normalizeStyle({
stroke: _ctx.color,
fill: _ctx.color,
strokeWidth: _ctx.strokeWidth
}),
"stroke-linecap": "round",
"stroke-linejoin": "round",
points: "-5,-4 0,0 -5,4 -5,-4"
}, null, 4)) : createCommentVNode("", true), _ctx.type === unref(MarkerType).Arrow ? (openBlock(), createElementBlock("polyline", {
key: 1,
style: normalizeStyle({
stroke: _ctx.color,
strokeWidth: _ctx.strokeWidth
}),
"stroke-linecap": "round",
"stroke-linejoin": "round",
fill: "none",
points: "-5,-4 0,0 -5,4"
}, null, 4)) : createCommentVNode("", true)], 8, _hoisted_1$4);
};
}
});
var _hoisted_1$3 = {
class: "vue-flow__marker vue-flow__container",
"aria-hidden": "true"
};
var _sfc_main$5 = /* @__PURE__ */ defineComponent({
name: "MarkerDefinitions",
compatConfig: { MODE: 3 },
setup(__props) {
const { id: vueFlowId, edges, connectionLineOptions, defaultMarkerColor: defaultColor } = useVueFlow();
const markers = computed(() => {
const ids = /* @__PURE__ */ new Set();
const markers2 = [];
const createMarkers = (marker) => {
if (marker) {
const markerId = getMarkerId(marker, vueFlowId);
if (!ids.has(markerId)) {
if (typeof marker === "object") markers2.push({
...marker,
id: markerId,
color: marker.color || defaultColor.value
});
else markers2.push({
id: markerId,
color: defaultColor.value,
type: marker
});
ids.add(markerId);
}
}
};
for (const marker of [connectionLineOptions.value.markerEnd, connectionLineOptions.value.markerStart]) createMarkers(marker);
for (const edge of edges.value) for (const marker of [edge.markerStart, edge.markerEnd]) createMarkers(marker);
return markers2.sort((a, b) => a.id.localeCompare(b.id));
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock("svg", _hoisted_1$3, [createBaseVNode("defs", null, [(openBlock(true), createElementBlock(Fragment, null, renderList(markers.value, (marker) => {
return openBlock(), createBlock(_sfc_main$6, {
id: marker.id,
key: marker.id,
type: marker.type,
color: marker.color,
width: marker.width,
height: marker.height,
markerUnits: marker.markerUnits,
"stroke-width": marker.strokeWidth,
orient: marker.orient
}, null, 8, [
"id",
"type",
"color",
"width",
"height",
"markerUnits",
"stroke-width",
"orient"
]);
}), 128))])]);
};
}
});
var _sfc_main$4 = /* @__PURE__ */ defineComponent({
name: "Edges",
compatConfig: { MODE: 3 },
setup(__props) {
const { findNode, getEdges, elevateEdgesOnSelect } = useVueFlow();
return (_ctx, _cache) => {
return openBlock(), createElementBlock(Fragment, null, [
createVNode(_sfc_main$5),
(openBlock(true), createElementBlock(Fragment, null, renderList(unref(getEdges), (edge) => {
return openBlock(), createElementBlock("svg", {
key: edge.id,
class: "vue-flow__edges vue-flow__container",
style: normalizeStyle({ zIndex: unref(getEdgeZIndex)(edge, unref(findNode), unref(elevateEdgesOnSelect)) })
}, [createVNode(unref(EdgeWrapper$1), { id: edge.id }, null, 8, ["id"])], 4);
}), 128)),
createVNode(unref(ConnectionLine$1))
], 64);
};
}
});
var NodeWrapper$1 = defineComponent({
name: "Node",
compatConfig: { MODE: 3 },
props: ["id", "resizeObserver"],
setup(props) {
const { id: vueFlowId, noPanClassName, selectNodesOnDrag, nodesSelectionActive, multiSelectionActive, emits, removeSelectedNodes, addSelectedNodes, updateNodeDimensions, onUpdateNodeInternals, getNodeTypes: getNodeTypes$2, nodeExtent, elevateNodesOnSelect, disableKeyboardA11y, ariaLiveMessage, snapToGrid, snapGrid, nodeDragThreshold, nodesDraggable, elementsSelectable, nodesConnectable, nodesFocusable, hooks } = useVueFlow();
const nodeElement = ref(null);
provide(NodeRef, nodeElement);
provide(NodeId, props.id);
const slots = inject(Slots);
const instance = getCurrentInstance();
const updateNodePositions = useUpdateNodePositions();
const { node, parentNode } = useNode(props.id);
const { emit, on } = useNodeHooks(node, emits);
const isDraggable = toRef(() => typeof node.draggable === "undefined" ? nodesDraggable.value : node.draggable);
const isSelectable = toRef(() => typeof node.selectable === "undefined" ? elementsSelectable.value : node.selectable);
const isConnectable = toRef(() => typeof node.connectable === "undefined" ? nodesConnectable.value : node.connectable);
const isFocusable = toRef(() => typeof node.focusable === "undefined" ? nodesFocusable.value : node.focusable);
const hasPointerEvents = computed(() => isSelectable.value || isDraggable.value || hooks.value.nodeClick.hasListeners() || hooks.value.nodeDoubleClick.hasListeners() || hooks.value.nodeMouseEnter.hasListeners() || hooks.value.nodeMouseMove.hasListeners() || hooks.value.nodeMouseLeave.hasListeners());
const isInit = toRef(() => !!node.dimensions.width && !!node.dimensions.height);
const nodeCmp = computed(() => {
const name$13 = node.type || "default";
const slot = slots == null ? void 0 : slots[`node-${name$13}`];
if (slot) return slot;
let nodeType = node.template || getNodeTypes$2.value[name$13];
if (typeof nodeType === "string") {
if (instance) {
const components = Object.keys(instance.appContext.components);
if (components && components.includes(name$13)) nodeType = resolveComponent(name$13, false);
}
}
if (nodeType && typeof nodeType !== "string") return nodeType;
emits.error(new VueFlowError(ErrorCode.NODE_TYPE_MISSING, nodeType));
return false;
});
const dragging = useDrag({
id: props.id,
el: nodeElement,
disabled: () => !isDraggable.value,
selectable: isSelectable,
dragHandle: () => node.dragHandle,
onStart(event) {
emit.dragStart(event);
},
onDrag(event) {
emit.drag(event);
},
onStop(event) {
emit.dragStop(event);
},
onClick(event) {
onSelectNode(event);
}
});
const getClass = computed(() => node.class instanceof Function ? node.class(node) : node.class);
const getStyle$1 = computed(() => {
const styles = (node.style instanceof Function ? node.style(node) : node.style) || {};
const width = node.width instanceof Function ? node.width(node) : node.width;
const height = node.height instanceof Function ? node.height(node) : node.height;
if (!styles.width && width) styles.width = typeof width === "string" ? width : `${width}px`;
if (!styles.height && height) styles.height = typeof height === "string" ? height : `${height}px`;
return styles;
});
const zIndex = toRef(() => Number(node.zIndex ?? getStyle$1.value.zIndex ?? 0));
onUpdateNodeInternals((updateIds) => {
if (updateIds.includes(props.id) || !updateIds.length) updateInternals();
});
onMounted(() => {
watch(() => node.hidden, (isHidden = false, _, onCleanup) => {
if (!isHidden && nodeElement.value) {
props.resizeObserver.observe(nodeElement.value);
onCleanup(() => {
if (nodeElement.value) props.resizeObserver.unobserve(nodeElement.value);
});
}
}, {
immediate: true,
flush: "post"
});
});
watch([
() => node.type,
() => node.sourcePosition,
() => node.targetPosition
], () => {
nextTick(() => {
updateNodeDimensions([{
id: props.id,
nodeElement: nodeElement.value,
forceUpdate: true
}]);
});
});
watch([
() => node.position.x,
() => node.position.y,
() => {
var _a;
return (_a = parentNode.value) == null ? void 0 : _a.computedPosition.x;
},
() => {
var _a;
return (_a = parentNode.value) == null ? void 0 : _a.computedPosition.y;
},
() => {
var _a;
return (_a = parentNode.value) == null ? void 0 : _a.computedPosition.z;
},
zIndex,
() => node.selected,
() => node.dimensions.height,
() => node.dimensions.width,
() => {
var _a;
return (_a = parentNode.value) == null ? void 0 : _a.dimensions.height;
},
() => {
var _a;
return (_a = parentNode.value) == null ? void 0 : _a.dimensions.width;
}
], ([newX, newY, parentX, parentY, parentZ, nodeZIndex]) => {
const xyzPos = {
x: newX,
y: newY,
z: nodeZIndex + (elevateNodesOnSelect.value ? node.selected ? 1e3 : 0 : 0)
};
if (typeof parentX !== "undefined" && typeof parentY !== "undefined") node.computedPosition = getXYZPos({
x: parentX,
y: parentY,
z: parentZ
}, xyzPos);
else node.computedPosition = xyzPos;
}, {
flush: "post",
immediate: true
});
watch([() => node.extent, nodeExtent], ([nodeExtent2, globalExtent], [oldNodeExtent, oldGlobalExtent]) => {
if (nodeExtent2 !== oldNodeExtent || globalExtent !== oldGlobalExtent) clampPosition2();
});
if (node.extent === "parent" || typeof node.extent === "object" && "range" in node.extent && node.extent.range === "parent") until(() => isInit).toBe(true).then(clampPosition2);
else clampPosition2();
return () => {
if (node.hidden) return null;
return h("div", {
"ref": nodeElement,
"data-id": node.id,
"class": [
"vue-flow__node",
`vue-flow__node-${nodeCmp.value === false ? "default" : node.type || "default"}`,
{
[noPanClassName.value]: isDraggable.value,
dragging: dragging == null ? void 0 : dragging.value,
draggable: isDraggable.value,
selected: node.selected,
selectable: isSelectable.value,
parent: node.isParent
},
getClass.value
],
"style": {
visibility: isInit.value ? "visible" : "hidden",
zIndex: node.computedPosition.z ?? zIndex.value,
transform: `translate(${node.computedPosition.x}px,${node.computedPosition.y}px)`,
pointerEvents: hasPointerEvents.value ? "all" : "none",
...getStyle$1.value
},
"tabIndex": isFocusable.value ? 0 : void 0,
"role": isFocusable.value ? "group" : void 0,
"aria-describedby": disableKeyboardA11y.value ? void 0 : `${ARIA_NODE_DESC_KEY}-${vueFlowId}`,
"aria-label": node.ariaLabel,
"aria-roledescription": "node",
...node.domAttributes,
"onMouseenter": onMouseEnter,
"onMousemove": onMouseMove,
"onMouseleave": onMouseLeave,
"onContextmenu": onContextMenu,
"onClick": onSelectNode,
"onDblclick": onDoubleClick,
"onKeydown": onKeyDown
}, [h(nodeCmp.value === false ? getNodeTypes$2.value.default : nodeCmp.value, {
id: node.id,
type: node.type,
data: node.data,
events: {
...node.events,
...on
},
selected: node.selected,
resizing: node.resizing,
dragging: dragging.value,
connectable: isConnectable.value,
position: node.computedPosition,
dimensions: node.dimensions,
isValidTargetPos: node.isValidTargetPos,
isValidSourcePos: node.isValidSourcePos,
parent: node.parentNode,
parentNodeId: node.parentNode,
zIndex: node.computedPosition.z ?? zIndex.value,
targetPosition: node.targetPosition,
sourcePosition: node.sourcePosition,
label: node.label,
dragHandle: node.dragHandle,
onUpdateNodeInternals: updateInternals
})]);
};
function clampPosition2() {
const nextPosition = node.computedPosition;
const { computedPosition, position } = calcNextPosition(node, snapToGrid.value ? snapPosition(nextPosition, snapGrid.value) : nextPosition, emits.error, nodeExtent.value, parentNode.value);
if (node.computedPosition.x !== computedPosition.x || node.computedPosition.y !== computedPosition.y) node.computedPosition = {
...node.computedPosition,
...computedPosition
};
if (node.position.x !== position.x || node.position.y !== position.y) node.position = position;
}
function updateInternals() {
if (nodeElement.value) updateNodeDimensions([{
id: props.id,
nodeElement: nodeElement.value,
forceUpdate: true
}]);
}
function onMouseEnter(event) {
if (!(dragging == null ? void 0 : dragging.value)) emit.mouseEnter({
event,
node
});
}
function onMouseMove(event) {
if (!(dragging == null ? void 0 : dragging.value)) emit.mouseMove({
event,
node
});
}
function onMouseLeave(event) {
if (!(dragging == null ? void 0 : dragging.value)) emit.mouseLeave({
event,
node
});
}
function onContextMenu(event) {
return emit.contextMenu({
event,
node
});
}
function onDoubleClick(event) {
return emit.doubleClick({
event,
node
});
}
function onSelectNode(event) {
if (isSelectable.value && (!selectNodesOnDrag.value || !isDraggable.value || nodeDragThreshold.value > 0)) handleNodeClick(node, multiSelectionActive.value, addSelectedNodes, removeSelectedNodes, nodesSelectionActive, false, nodeElement.value);
emit.click({
event,
node
});
}
function onKeyDown(event) {
if (isInputDOMNode(event) || disableKeyboardA11y.value) return;
if (elementSelectionKeys.includes(event.key) && isSelectable.value) {
const unselect = event.key === "Escape";
handleNodeClick(node, multiSelectionActive.value, addSelectedNodes, removeSelectedNodes, nodesSelectionActive, unselect, nodeElement.value);
} else if (isDraggable.value && node.selected && arrowKeyDiffs[event.key]) {
event.preventDefault();
ariaLiveMessage.value = `Moved selected node ${event.key.replace("Arrow", "").toLowerCase()}. New position, x: ${~~node.position.x}, y: ${~~node.position.y}`;
updateNodePositions({
x: arrowKeyDiffs[event.key].x,
y: arrowKeyDiffs[event.key].y
}, event.shiftKey);
}
}
}
});
var _hoisted_1$2 = {
height: "0",
width: "0"
};
var _sfc_main$3 = /* @__PURE__ */ defineComponent({
name: "EdgeLabelRenderer",
compatConfig: { MODE: 3 },
setup(__props) {
const { viewportRef } = useVueFlow();
const teleportTarget = toRef(() => {
var _a;
return (_a = viewportRef.value) == null ? void 0 : _a.getElementsByClassName("vue-flow__edge-labels")[0];
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock("svg", null, [(openBlock(), createElementBlock("foreignObject", _hoisted_1$2, [(openBlock(), createBlock(Teleport, {
to: teleportTarget.value,
disabled: !teleportTarget.value
}, [renderSlot(_ctx.$slots, "default")], 8, ["to", "disabled"]))]))]);
};
}
});
function useNodesInitialized(options = { includeHiddenNodes: false }) {
const { nodes: nodes$13 } = useVueFlow();
return computed(() => {
if (nodes$13.value.length === 0) return false;
for (const node of nodes$13.value) if (options.includeHiddenNodes || !node.hidden) {
if ((node == null ? void 0 : node.handleBounds) === void 0 || node.dimensions.width === 0 || node.dimensions.height === 0) return false;
}
return true;
});
}
var _hoisted_1$1 = { class: "vue-flow__nodes vue-flow__container" };
var _sfc_main$2 = /* @__PURE__ */ defineComponent({
name: "Nodes",
compatConfig: { MODE: 3 },
setup(__props) {
const { getNodes, updateNodeDimensions, emits } = useVueFlow();
const nodesInitialized = useNodesInitialized();
const resizeObserver = ref();
watch(nodesInitialized, (isInit) => {
if (isInit) nextTick(() => {
emits.nodesInitialized(getNodes.value);
});
}, { immediate: true });
onMounted(() => {
resizeObserver.value = new ResizeObserver((entries) => {
const updates = entries.map((entry) => {
return {
id: entry.target.getAttribute("data-id"),
nodeElement: entry.target,
forceUpdate: true
};
});
nextTick(() => updateNodeDimensions(updates));
});
});
onBeforeUnmount(() => {
var _a;
return (_a = resizeObserver.value) == null ? void 0 : _a.disconnect();
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", _hoisted_1$1, [resizeObserver.value ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(unref(getNodes), (node, __, ___, _cached) => {
const _memo = [node.id];
if (_cached && _cached.key === node.id && isMemoSame(_cached, _memo)) return _cached;
const _item = (openBlock(), createBlock(unref(NodeWrapper$1), {
id: node.id,
key: node.id,
"resize-observer": resizeObserver.value
}, null, 8, ["id", "resize-observer"]));
_item.memo = _memo;
return _item;
}, _cache, 0), 128)) : createCommentVNode("", true)]);
};
}
});
function useStylesLoadedWarning() {
const { emits } = useVueFlow();
onMounted(() => {
if (isDev()) {
const pane = document.querySelector(".vue-flow__pane");
if (pane && !(window.getComputedStyle(pane).zIndex === "1")) emits.error(new VueFlowError(ErrorCode.MISSING_STYLES));
}
});
}
var _hoisted_1 = /* @__PURE__ */ createBaseVNode("div", { class: "vue-flow__edge-labels" }, null, -1);
var _sfc_main$1 = /* @__PURE__ */ defineComponent({
name: "VueFlow",
compatConfig: { MODE: 3 },
props: {
id: {},
modelValue: {},
nodes: {},
edges: {},
edgeTypes: {},
nodeTypes: {},
connectionMode: {},
connectionLineType: {},
connectionLineStyle: { default: void 0 },
connectionLineOptions: { default: void 0 },
connectionRadius: {},
isValidConnection: {
type: [Function, null],
default: void 0
},
deleteKeyCode: { default: void 0 },
selectionKeyCode: {
type: [Boolean, null],
default: void 0
},
multiSelectionKeyCode: { default: void 0 },
zoomActivationKeyCode: { default: void 0 },
panActivationKeyCode: { default: void 0 },
snapToGrid: {
type: Boolean,
default: void 0
},
snapGrid: {},
onlyRenderVisibleElements: {
type: Boolean,
default: void 0
},
edgesUpdatable: {
type: [Boolean, String],
default: void 0
},
nodesDraggable: {
type: Boolean,
default: void 0
},
nodesConnectable: {
type: Boolean,
default: void 0
},
nodeDragThreshold: {},
elementsSelectable: {
type: Boolean,
default: void 0
},
selectNodesOnDrag: {
type: Boolean,
default: void 0
},
panOnDrag: {
type: [Boolean, Array],
default: void 0
},
minZoom: {},
maxZoom: {},
defaultViewport: {},
translateExtent: {},
nodeExtent: {},
defaultMarkerColor: {},
zoomOnScroll: {
type: Boolean,
default: void 0
},
zoomOnPinch: {
type: Boolean,
default: void 0
},
panOnScroll: {
type: Boolean,
default: void 0
},
panOnScrollSpeed: {},
panOnScrollMode: {},
paneClickDistance: {},
zoomOnDoubleClick: {
type: Boolean,
default: void 0
},
preventScrolling: {
type: Boolean,
default: void 0
},
selectionMode: {},
edgeUpdaterRadius: {},
fitViewOnInit: {
type: Boolean,
default: void 0
},
connectOnClick: {
type: Boolean,
default: void 0
},
applyDefault: {
type: Boolean,
default: void 0
},
autoConnect: {
type: [Boolean, Function],
default: void 0
},
noDragClassName: {},
noWheelClassName: {},
noPanClassName: {},
defaultEdgeOptions: {},
elevateEdgesOnSelect: {
type: Boolean,
default: void 0
},
elevateNodesOnSelect: {
type: Boolean,
default: void 0
},
disableKeyboardA11y: {
type: Boolean,
default: void 0
},
edgesFocusable: {
type: Boolean,
default: void 0
},
nodesFocusable: {
type: Boolean,
default: void 0
},
autoPanOnConnect: {
type: Boolean,
default: void 0
},
autoPanOnNodeDrag: {
type: Boolean,
default: void 0
},
autoPanSpeed: {}
},
emits: [
"nodesChange",
"edgesChange",
"nodesInitialized",
"paneReady",
"init",
"updateNodeInternals",
"error",
"connect",
"connectStart",
"connectEnd",
"clickConnectStart",
"clickConnectEnd",
"moveStart",
"move",
"moveEnd",
"selectionDragStart",
"selectionDrag",
"selectionDragStop",
"selectionContextMenu",
"selectionStart",
"selectionEnd",
"viewportChangeStart",
"viewportChange",
"viewportChangeEnd",
"paneScroll",
"paneClick",
"paneContextMenu",
"paneMouseEnter",
"paneMouseMove",
"paneMouseLeave",
"edgeUpdate",
"edgeContextMenu",
"edgeMouseEnter",
"edgeMouseMove",
"edgeMouseLeave",
"edgeDoubleClick",
"edgeClick",
"edgeUpdateStart",
"edgeUpdateEnd",
"nodeContextMenu",
"nodeMouseEnter",
"nodeMouseMove",
"nodeMouseLeave",
"nodeDoubleClick",
"nodeClick",
"nodeDragStart",
"nodeDrag",
"nodeDragStop",
"miniMapNodeClick",
"miniMapNodeDoubleClick",
"miniMapNodeMouseEnter",
"miniMapNodeMouseMove",
"miniMapNodeMouseLeave",
"update:modelValue",
"update:nodes",
"update:edges"
],
setup(__props, { expose: __expose, emit }) {
const props = __props;
const slots = useSlots();
const modelValue = useVModel(props, "modelValue", emit);
const modelNodes = useVModel(props, "nodes", emit);
const modelEdges = useVModel(props, "edges", emit);
const vfInstance = useVueFlow(props);
const disposeWatchers = useWatchProps({
modelValue,
nodes: modelNodes,
edges: modelEdges
}, props, vfInstance);
useHooks(emit, vfInstance.hooks);
useOnInitHandler();
useStylesLoadedWarning();
provide(Slots, slots);
onUnmounted(disposeWatchers);
__expose(vfInstance);
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
ref: unref(vfInstance).vueFlowRef,
class: "vue-flow"
}, [
createVNode(_sfc_main$8, null, {
default: withCtx(() => [
createVNode(_sfc_main$4),
_hoisted_1,
createVNode(_sfc_main$2),
renderSlot(_ctx.$slots, "zoom-pane")
]),
_: 3
}),
renderSlot(_ctx.$slots, "default"),
createVNode(_sfc_main$7)
], 512);
};
}
});
var _sfc_main = /* @__PURE__ */ defineComponent({
name: "Panel",
compatConfig: { MODE: 3 },
props: { position: {} },
setup(__props) {
const props = __props;
const { userSelectionActive } = useVueFlow();
const positionClasses = computed(() => `${props.position}`.split("-"));
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
class: normalizeClass(["vue-flow__panel", positionClasses.value]),
style: normalizeStyle({ pointerEvents: unref(userSelectionActive) ? "none" : "all" })
}, [renderSlot(_ctx.$slots, "default")], 6);
};
}
});
const GRID_SIZE = 16;
const DEFAULT_NODE_SIZE = [96, 96];
const CONFIGURATION_NODE_RADIUS = 80 / 2;
const CONFIGURATION_NODE_SIZE = [80, 80];
const CONFIGURABLE_NODE_SIZE = [256, 96];
const PUSH_NODES_OFFSET = DEFAULT_NODE_SIZE[0] * 2 + 16;
const DEFAULT_VIEWPORT_BOUNDARIES = {
xMin: -Infinity,
yMin: -Infinity,
xMax: Infinity,
yMax: Infinity
};
const getLeftmostTopNode = (nodes$13) => {
return nodes$13.reduce((leftmostTop, node) => {
if (node.position[0] > leftmostTop.position[0] || node.position[1] > leftmostTop.position[1]) return leftmostTop;
return node;
}, nodes$13[0]);
};
const getLeftMostNode = (nodes$13) => {
return nodes$13.reduce((leftmost, node) => {
if (node.position[0] < leftmost.position[0]) return node;
return leftmost;
}, nodes$13[0]);
};
const getTopMostNode = (nodes$13) => {
return nodes$13.reduce((topmost, node) => {
if (node.position[1] < topmost.position[1]) return node;
return topmost;
}, nodes$13[0]);
};
const getRightMostNode = (nodes$13) => {
return nodes$13.reduce((rightmost, node) => {
if (node.position[0] > rightmost.position[0]) return node;
return rightmost;
}, nodes$13[0]);
};
const getBottomMostNode = (nodes$13) => {
return nodes$13.reduce((bottommost, node) => {
if (node.position[1] > bottommost.position[1]) return node;
return bottommost;
}, nodes$13[0]);
};
const getNodesGroupSize = (nodes$13) => {
const leftMostNode = getLeftMostNode(nodes$13);
const topMostNode = getTopMostNode(nodes$13);
const rightMostNode = getRightMostNode(nodes$13);
const bottomMostNode = getBottomMostNode(nodes$13);
return [Math.abs(rightMostNode.position[0] - leftMostNode.position[0]) + DEFAULT_NODE_SIZE[0], Math.abs(bottomMostNode.position[1] - topMostNode.position[1]) + DEFAULT_NODE_SIZE[1]];
};
var canUsePosition = (position1, position2, size = DEFAULT_NODE_SIZE) => {
if (Math.abs(position1[0] - position2[0]) <= size[0]) {
if (Math.abs(position1[1] - position2[1]) <= size[1]) return false;
}
return true;
};
var closestNumberDivisibleBy = (inputNumber, divisibleBy) => {
const quotient = Math.ceil(inputNumber / divisibleBy);
const inputNumber1 = divisibleBy * quotient;
const inputNumber2 = inputNumber * divisibleBy > 0 ? divisibleBy * (quotient + 1) : divisibleBy * (quotient - 1);
if (Math.abs(inputNumber - inputNumber1) < Math.abs(inputNumber - inputNumber2)) return inputNumber1;
return inputNumber2;
};
function snapPositionToGrid(position) {
return [closestNumberDivisibleBy(position[0], 16), closestNumberDivisibleBy(position[1], 16)];
}
const getNewNodePosition = (nodes$13, initialPosition, { offset = [DEFAULT_NODE_SIZE[0] / 2, DEFAULT_NODE_SIZE[1] / 2], size = DEFAULT_NODE_SIZE, viewport = DEFAULT_VIEWPORT_BOUNDARIES, normalize = true } = {}) => {
const resolvedOffset = snapPositionToGrid(offset);
const resolvedPosition = snapPositionToGrid(initialPosition);
if (normalize) {
let conflictFound = false;
let i$1, node;
do {
conflictFound = false;
for (i$1 = 0; i$1 < nodes$13.length; i$1++) {
node = nodes$13[i$1];
if (!node || NODE_POSITION_CONFLICT_ALLOWLIST.includes(node.type)) continue;
if (!canUsePosition(node.position, resolvedPosition, size)) {
conflictFound = true;
break;
}
}
if (conflictFound) {
resolvedPosition[0] += resolvedOffset[0];
resolvedPosition[1] += resolvedOffset[1];
}
} while (conflictFound);
if (resolvedPosition[0] < viewport.xMin + resolvedOffset[0]) resolvedPosition[0] = viewport.xMin + resolvedOffset[0];
if (resolvedPosition[1] < viewport.yMin + resolvedOffset[1]) resolvedPosition[1] = viewport.yMin + resolvedOffset[1];
if (resolvedPosition[0] > viewport.xMax - resolvedOffset[0]) resolvedPosition[0] = viewport.xMax - size[0] - resolvedOffset[0];
if (resolvedPosition[1] > viewport.yMax - resolvedOffset[1]) resolvedPosition[1] = viewport.yMax - size[1] - resolvedOffset[1];
}
return resolvedPosition;
};
const getMousePosition = (event) => {
return [(event && "clientX" in event ? event.clientX : event?.touches?.[0]?.clientX) ?? 0, (event && "clientY" in event ? event.clientY : event?.touches?.[0]?.clientY) ?? 0];
};
const getRelativePosition = (x, y, scale, offset) => {
return [(x - offset[0]) / scale, (y - offset[1]) / scale];
};
var getContentDimensions = () => {
let contentWidth = window.innerWidth;
let contentHeight = window.innerHeight;
const nodeViewRoot = document.getElementById("node-view-root");
if (nodeViewRoot) {
const contentBounds = nodeViewRoot.getBoundingClientRect();
contentWidth = contentBounds.width;
contentHeight = contentBounds.height;
}
return {
editorWidth: contentWidth,
editorHeight: contentHeight
};
};
const getMidCanvasPosition = (scale, offset) => {
const { editorWidth, editorHeight } = getContentDimensions();
return getRelativePosition(editorWidth / 2, (editorHeight - 65) / 2, scale, offset);
};
const getNodesWithNormalizedPosition = (workflowNodes) => {
const nodes$13 = [...workflowNodes];
if (nodes$13.length) {
const leftmostTop = getLeftmostTopNode(nodes$13);
const diffX = 176 - leftmostTop.position[0];
const diffY = 240 - leftmostTop.position[1];
nodes$13.forEach((node) => {
node.position[0] += diffX + DEFAULT_NODE_SIZE[0] * 2;
node.position[1] += diffY;
});
}
return nodes$13;
};
function getGenericHints({ workflowNode, node, nodeType, nodeOutputData, hasMultipleInputItems, nodes: nodes$13, connections: connections$13, hasNodeRun }) {
const nodeHints = [];
if (node?.type.toLocaleLowerCase().includes("tool") && node?.type !== "@n8n/n8n-nodes-langchain.mcpClientTool" && hasNodeRun) {
if (!JSON.stringify(workflowNode.parameters).includes("$fromAI")) nodeHints.push({
message: "No parameters are set up to be filled by AI. Click on the ✨ button next to a parameter to allow AI to set its value.",
location: "outputPane",
whenToDisplay: "afterExecution"
});
}
if (hasNodeRun && workflowNode.parameters.limit) {
if (nodeOutputData.length === workflowNode.parameters.limit) nodeHints.push({
message: `Limit of ${workflowNode.parameters.limit} items reached. There may be more items that aren't being returned. Tweak the 'Return All' or 'Limit' parameters to access more items.`,
location: "outputPane",
whenToDisplay: "afterExecution"
});
}
if (hasMultipleInputItems && LIST_LIKE_NODE_OPERATIONS.includes(workflowNode.parameters.operation || "")) {
if (!getNodeByName(nodes$13, node.name)?.executeOnce) nodeHints.push({
message: "This node runs multiple times, once for each input item. Use ‘Execute Once’ in the node settings if you want to run it only once.",
location: "outputPane"
});
}
if (hasMultipleInputItems && workflowNode.parameters.operation === "sendAndWait") {
if (!getNodeByName(nodes$13, node.name)?.executeOnce) nodeHints.push({
message: "This action will run only once, for the first input item",
location: "outputPane"
});
}
if (node.type === "n8n-nodes-base.set" && node.parameters.mode === "manual") {
const assignments = (getNodeParameters(nodeType.properties, node.parameters, true, false, node, nodeType)?.assignments || {})?.assignments || [];
const expressionInFieldName = [];
for (const [index, assignment] of assignments.entries()) if (assignment.name.startsWith("=")) expressionInFieldName.push(index + 1);
if (expressionInFieldName.length > 0) nodeHints.push({
message: `An expression is used in 'Fields to Set' in ${expressionInFieldName.length === 1 ? "field" : "fields"} ${expressionInFieldName.join(", ")}, did you mean to use it in the value instead?`,
whenToDisplay: "beforeExecution",
location: "outputPane"
});
}
if (node.type === "n8n-nodes-base.splitInBatches") {
const firstNodesInLoop = mapConnectionsByDestination(connections$13)[node.name]?.main[1] || [];
if (!firstNodesInLoop.length) nodeHints.push({
message: "No nodes connected to the 'loop' output of this node",
whenToDisplay: "beforeExecution",
location: "outputPane"
});
else for (const nodeInConnection of firstNodesInLoop || []) if (!(getChildNodes(connections$13, nodeInConnection.node) || []).includes(node.name)) nodeHints.push({
message: "The last node in the branch of the 'loop' output must be connected back to the input of this node to loop correctly",
whenToDisplay: "beforeExecution",
location: "outputPane"
});
}
return nodeHints;
}
function generateOffsets(nodeCount, nodeSize, gridSize) {
const offsets = [];
const half = Math.floor(nodeCount / 2);
const isOdd = nodeCount % 2 === 1;
if (nodeCount === 0) return [];
for (let i$1 = -half; i$1 <= half; i$1++) if (i$1 === 0) {
if (isOdd) offsets.push(0);
} else {
const offset = i$1 * nodeSize + Math.sign(i$1) * (Math.abs(i$1) - (isOdd ? 0 : 1)) * gridSize;
offsets.push(offset);
}
return offsets;
}
const getNodeViewTab = (route) => {
if (route.meta?.nodeView) return MAIN_HEADER_TABS.WORKFLOW;
else if ([
VIEWS.WORKFLOW_EXECUTIONS,
VIEWS.EXECUTION_PREVIEW,
VIEWS.EXECUTION_HOME
].map(String).includes(String(route.name))) return MAIN_HEADER_TABS.EXECUTIONS;
return null;
};
function getBounds({ x, y, zoom: zoom$1 }, { width, height }) {
return {
xMin: -x / zoom$1,
yMin: -y / zoom$1,
xMax: (width - x) / zoom$1,
yMax: (height - y) / zoom$1
};
}
function addPadding({ x, y, width, height }, amount) {
return {
x: x - amount,
y: y - amount,
width: width + amount * 2,
height: height + amount * 2
};
}
function updateViewportToContainNodes(viewport, dimensions, nodes$13, padding) {
function computeDelta(start$1, end, min, max) {
if (start$1 >= min && end <= max) return 0;
if (start$1 < min) {
if (end > max) return 0;
return min - start$1;
}
return max - end;
}
if (nodes$13.length === 0) return viewport;
const zoom$1 = viewport.zoom;
const rect = addPadding(getRectOfNodes(nodes$13), padding / zoom$1);
const { xMax, xMin, yMax, yMin } = getBounds(viewport, dimensions);
const dx = computeDelta(rect.x, rect.x + rect.width, xMin, xMax);
const dy = computeDelta(rect.y, rect.y + rect.height, yMin, yMax);
return {
x: viewport.x + dx * zoom$1,
y: viewport.y + dy * zoom$1,
zoom: zoom$1
};
}
function calculateNodeSize(isConfiguration, isConfigurable, mainInputCount, mainOutputCount, nonMainInputCount, isExperimentalNdvActive) {
const maxVerticalHandles = Math.max(mainInputCount, mainOutputCount, 1);
const height = DEFAULT_NODE_SIZE[1] + Math.max(0, maxVerticalHandles - 2) * 16 * 2;
const widthScale = isExperimentalNdvActive ? 1.5 : 1;
if (isConfigurable) return {
width: (80 + 16 * ((isConfiguration ? 1 : 0) + (Math.max(4, nonMainInputCount) - 1) * 3)) * widthScale,
height: isConfiguration ? CONFIGURATION_NODE_SIZE[1] : height
};
if (isConfiguration) return {
width: CONFIGURATION_NODE_SIZE[0] * widthScale,
height: CONFIGURATION_NODE_SIZE[1]
};
return {
width: DEFAULT_NODE_SIZE[0] * widthScale,
height
};
}
var defaults = {
name: "",
description: "",
active: false,
activeVersionId: null,
isArchived: false,
createdAt: -1,
updatedAt: -1,
connections: {},
nodes: [],
settings: { executionOrder: "v1" },
tags: [],
pinData: {},
versionId: "",
usedCredentials: []
};
var createEmptyWorkflow = () => ({
id: PLACEHOLDER_EMPTY_WORKFLOW_ID,
...defaults
});
const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
const uiStore = useUIStore();
const telemetry$1 = useTelemetry();
const workflowHelpers = useWorkflowHelpers();
const settingsStore = useSettingsStore();
const rootStore = useRootStore();
const nodeHelpers = useNodeHelpers();
const usersStore = useUsersStore();
const nodeTypesStore = useNodeTypesStore();
const sourceControlStore = useSourceControlStore();
const workflow = ref(createEmptyWorkflow());
const workflowObject = ref(createWorkflowObject(workflow.value.nodes, workflow.value.connections));
const totalWorkflowCount = ref(0);
const usedCredentials = ref({});
const activeWorkflows = ref([]);
const currentWorkflowExecutions = ref([]);
const workflowExecutionData = ref(null);
const lastSuccessfulExecution = ref(null);
const workflowExecutionStartedData = ref();
const workflowExecutionResultDataLastUpdate = ref();
const workflowExecutionPairedItemMappings = ref({});
const subWorkflowExecutionError = ref(null);
const executionWaitingForWebhook = ref(false);
const workflowsById = ref({});
const nodeMetadata = ref({});
const isInDebugMode = ref(false);
const chatMessages = ref([]);
const chatPartialExecutionDestinationNode = ref(null);
const selectedTriggerNodeName = ref();
const workflowName = computed(() => workflow.value.name);
const workflowId = computed(() => workflow.value.id);
const workflowVersionId = computed(() => workflow.value.versionId);
const workflowSettings = computed(() => workflow.value.settings ?? { ...defaults.settings });
const workflowTags = computed(() => workflow.value.tags);
const allWorkflows = computed(() => Object.values(workflowsById.value).sort((a, b) => a.name.localeCompare(b.name)));
const isNewWorkflow = computed(() => workflow.value.id === PLACEHOLDER_EMPTY_WORKFLOW_ID);
const isWorkflowActive = computed(() => workflow.value.activeVersionId !== null);
const workflowTriggerNodes = computed(() => workflow.value.nodes.filter((node) => {
const nodeType = nodeTypesStore.getNodeType(node.type, node.typeVersion);
return nodeType && nodeType.group.includes("trigger");
}));
const currentWorkflowHasWebhookNode = computed(() => !!workflow.value.nodes.find((node) => !!node.webhookId));
const getWorkflowRunData = computed(() => {
if (!workflowExecutionData.value?.data?.resultData) return null;
return workflowExecutionData.value.data.resultData.runData;
});
const allConnections = computed(() => workflow.value.connections);
const allNodes = computed(() => workflow.value.nodes);
const willNodeWait = (node) => {
return (node.type === "n8n-nodes-base.wait" || node.type === "n8n-nodes-base.form" || node.parameters?.operation === "sendAndWait") && node.disabled !== true;
};
const isWaitingExecution = computed(() => {
const activeNode$1 = useNDVStore().activeNode;
if (activeNode$1) {
if (willNodeWait(activeNode$1)) return true;
const parentNodes = workflowObject.value.getParentNodes(activeNode$1.name);
for (const parentNode of parentNodes) if (willNodeWait(workflowObject.value.nodes[parentNode])) return true;
return false;
}
return allNodes.value.some((node) => willNodeWait(node));
});
const isWorkflowRunning = computed(() => {
if (activeExecutionId.value === null) return true;
else if (activeExecutionId.value && workflowExecutionData.value) {
if (["waiting", "running"].includes(workflowExecutionData.value.status) && !workflowExecutionData.value.finished) return true;
}
return false;
});
const canvasNames = computed(() => new Set(allNodes.value.map((n) => n.name)));
const nodesByName = computed(() => {
return workflow.value.nodes.reduce((acc, node) => {
acc[node.name] = node;
return acc;
}, {});
});
const nodesIssuesExist = computed(() => workflow.value.nodes.some((node) => {
const nodeHasIssues = !!Object.keys(node.issues ?? {}).length;
const isConnected = Object.keys(outgoingConnectionsByNodeName(node.name)).length > 0 || Object.keys(incomingConnectionsByNodeName(node.name)).length > 0;
return !node.disabled && isConnected && nodeHasIssues;
}));
const workflowValidationIssues = computed(() => {
const issues = [];
const isStringOrStringArray = (value) => typeof value === "string" || Array.isArray(value);
workflow.value.nodes.forEach((node) => {
if (!node.issues || node.disabled) return;
if (!(Object.keys(outgoingConnectionsByNodeName(node.name)).length > 0 || Object.keys(incomingConnectionsByNodeName(node.name)).length > 0)) return;
Object.entries(node.issues).forEach(([issueType, issueValue]) => {
if (!issueValue) return;
if (typeof issueValue === "object" && !Array.isArray(issueValue)) Object.entries(issueValue).forEach(([_key, value]) => {
if (value) issues.push({
node: node.name,
type: issueType,
value
});
});
else issues.push({
node: node.name,
type: issueType,
value: isStringOrStringArray(issueValue) ? issueValue : String(issueValue)
});
});
});
return issues;
});
function formatIssueMessage(issue) {
if (Array.isArray(issue)) return issue.join(", ").replace(/\.$/, "");
return String(issue);
}
const pinnedWorkflowData = computed(() => workflow.value.pinData);
const executedNode = computed(() => workflowExecutionData.value?.executedNode);
const getAllLoadedFinishedExecutions = computed(() => {
return currentWorkflowExecutions.value.filter((ex) => ex.finished === true || ex.stoppedAt !== void 0);
});
const getWorkflowExecution = computed(() => workflowExecutionData.value);
const getPastChatMessages = computed(() => chatMessages.value);
const connectionsBySourceNode = computed(() => workflow.value.connections);
const connectionsByDestinationNode = computed(() => mapConnectionsByDestination(workflow.value.connections));
const selectableTriggerNodes = computed(() => workflowTriggerNodes.value.filter((node) => !node.disabled && !isChatNode(node)));
const workflowExecutionTriggerNodeName = computed(() => {
if (!isWorkflowRunning.value) return;
if (workflowExecutionData.value?.triggerNode) return workflowExecutionData.value.triggerNode;
return Object.keys(workflowExecutionData.value?.data?.resultData.runData ?? {}).find((name$13) => workflowTriggerNodes.value.some((node) => node.name === name$13));
});
const activeExecutionId = ref();
const previousExecutionId = ref();
const readonlyActiveExecutionId = computed(() => activeExecutionId.value);
const readonlyPreviousExecutionId = computed(() => previousExecutionId.value);
function setActiveExecutionId(id$1) {
if (id$1) previousExecutionId.value = activeExecutionId.value;
activeExecutionId.value = id$1;
}
function getWorkflowResultDataByNodeName(nodeName) {
if (getWorkflowRunData.value === null) return null;
if (!getWorkflowRunData.value.hasOwnProperty(nodeName)) return null;
return getWorkflowRunData.value[nodeName];
}
function outgoingConnectionsByNodeName(nodeName) {
if (workflow.value.connections.hasOwnProperty(nodeName)) return workflow.value.connections[nodeName];
return {};
}
function incomingConnectionsByNodeName(nodeName) {
if (connectionsByDestinationNode.value.hasOwnProperty(nodeName)) return connectionsByDestinationNode.value[nodeName];
return {};
}
function nodeHasOutputConnection(nodeName) {
return workflow.value.connections.hasOwnProperty(nodeName);
}
function isNodeInOutgoingNodeConnections(rootNodeName, searchNodeName, depth = -1) {
if (depth === 0) return false;
const firstNodeConnections = outgoingConnectionsByNodeName(rootNodeName);
if (!firstNodeConnections?.main?.[0]) return false;
const connections$13 = firstNodeConnections.main[0];
if (connections$13.some((node) => node.node === searchNodeName)) return true;
return connections$13.some((node) => isNodeInOutgoingNodeConnections(node.node, searchNodeName, depth - 1));
}
function getWorkflowById(id$1) {
return workflowsById.value[id$1];
}
function getNodeByName$1(nodeName) {
return getNodeByName(nodesByName.value, nodeName);
}
function getNodeById(nodeId) {
return workflow.value.nodes.find((node) => node.id === nodeId);
}
function findRootWithMainConnection(nodeName) {
const children$1 = workflowObject.value.getChildNodes(nodeName, "ALL");
for (let i$1 = children$1.length - 1; i$1 >= 0; i$1--) {
const childName = children$1[i$1];
if (workflowObject.value.getParentNodes(childName, NodeConnectionTypes.Main).length > 0) return childName;
}
return null;
}
function findNodeByPartialId(partialId) {
return workflow.value.nodes.find((node) => node.id.startsWith(partialId));
}
function getPartialIdForNode(fullId) {
for (let length = 6; length < fullId.length; ++length) {
const partialId = fullId.slice(0, length);
if (workflow.value.nodes.filter((x) => x.id.startsWith(partialId)).length === 1) return partialId;
}
return fullId;
}
function getNodesByIds(nodeIds) {
return nodeIds.map(getNodeById).filter(isPresent);
}
function getParametersLastUpdate(nodeName) {
return nodeMetadata.value[nodeName]?.parametersLastUpdatedAt;
}
function getPinnedDataLastUpdate(nodeName) {
return nodeMetadata.value[nodeName]?.pinnedDataLastUpdatedAt;
}
function getPinnedDataLastRemovedAt(nodeName) {
return nodeMetadata.value[nodeName]?.pinnedDataLastRemovedAt;
}
function isNodePristine(nodeName) {
return nodeMetadata.value[nodeName] === void 0 || nodeMetadata.value[nodeName].pristine;
}
function getExecutionDataById(id$1) {
return currentWorkflowExecutions.value.find((execution) => execution.id === id$1);
}
function getPinDataSize(pinData$13 = {}) {
return Object.values(pinData$13).reduce((acc, value) => {
return acc + stringSizeInBytes(value);
}, 0);
}
function getNodeTypes$2() {
return {
nodeTypes: {},
init: async () => {},
getByNameAndVersion: (nodeType, version) => {
const nodeTypeDescription = nodeTypesStore.getNodeType(nodeType, version);
if (nodeTypeDescription === null) return;
return {
description: nodeTypeDescription,
trigger: !["n8n-nodes-base.errorTrigger", "n8n-nodes-base.start"].includes(nodeType) && nodeTypeDescription.inputs.length === 0 && !nodeTypeDescription.webhooks || void 0
};
}
};
}
function getNodes() {
return workflow.value.nodes.map((node) => ({ ...node }));
}
function convertTemplateNodeToNodeUi(node) {
const filteredCredentials = Object.keys(node.credentials ?? {}).reduce((credentials, curr) => {
const credential = node?.credentials?.[curr];
if (!credential || typeof credential === "string") return credentials;
credentials[curr] = credential;
return credentials;
}, {});
return {
...node,
credentials: filteredCredentials
};
}
function createWorkflowObject(nodes$13, connections$13, copyData) {
const nodeTypes = getNodeTypes$2();
let id$1 = workflow.value.id;
if (id$1 && id$1 === "__EMPTY__") id$1 = void 0;
return new Workflow({
id: id$1,
name: workflow.value.name,
nodes: copyData ? deepCopy(nodes$13) : nodes$13,
connections: copyData ? deepCopy(connections$13) : connections$13,
active: false,
nodeTypes,
settings: workflow.value.settings ?? { ...defaults.settings },
pinData: workflow.value.pinData
});
}
function cloneWorkflowObject() {
const nodes$13 = getNodes();
const connections$13 = allConnections.value;
return createWorkflowObject(nodes$13, connections$13);
}
async function getWorkflowFromUrl(url, projectId) {
return await makeRestApiRequest(rootStore.restApiContext, "GET", "/workflows/from-url", {
url,
projectId
});
}
async function fetchLastSuccessfulExecution() {
const workflowId$1 = workflow.value.id;
const workflowPermissions = getResourcePermissions(workflow.value.scopes).workflow;
try {
if (workflowId$1 === "__EMPTY__" || sourceControlStore.preferences.branchReadOnly || uiStore.isReadOnlyView || !workflowPermissions.update || workflow.value.isArchived) return;
lastSuccessfulExecution.value = await getLastSuccessfulExecution(rootStore.restApiContext, workflowId$1);
} catch (e) {}
}
async function getActivationError(id$1) {
return await makeRestApiRequest(rootStore.restApiContext, "GET", `/active-workflows/error/${id$1}`);
}
async function fetchWorkflowsPage(projectId, page = 1, pageSize = 50, sortBy$2, filters = {}, includeFolders = false, onlySharedWithMe = false) {
const filter$1 = {
...filters,
projectId
};
const options = {
skip: (page - 1) * pageSize,
take: pageSize,
sortBy: sortBy$2
};
const { count, data } = await getWorkflowsAndFolders(rootStore.restApiContext, Object.keys(filter$1).length ? filter$1 : void 0, Object.keys(options).length ? options : void 0, includeFolders ? includeFolders : void 0, onlySharedWithMe ? onlySharedWithMe : void 0);
totalWorkflowCount.value = count;
data.filter((item) => item.resource !== "folder").forEach((item) => {
addWorkflow({
...item,
nodes: [],
connections: {},
versionId: ""
});
});
return data;
}
async function searchWorkflows({ projectId, query, nodeTypes, tags: tags$7, select: select$1, isArchived }) {
const filter$1 = {
projectId,
query,
nodeTypes,
tags: tags$7,
isArchived
};
const hasFilter = Object.values(filter$1).some((v) => isPresent(v) && (Array.isArray(v) ? v.length > 0 : v !== ""));
const { data: workflows } = await getWorkflows(rootStore.restApiContext, hasFilter ? filter$1 : void 0, void 0, select$1);
return workflows;
}
async function fetchAllWorkflows(projectId) {
const workflows = await searchWorkflows({ projectId });
setWorkflows(workflows);
return workflows;
}
async function fetchWorkflow(id$1) {
const workflowData = await getWorkflow(rootStore.restApiContext, id$1);
addWorkflow(workflowData);
return workflowData;
}
async function fetchWorkflowsWithNodesIncluded(nodeTypes) {
return await getWorkflowsWithNodesIncluded(rootStore.restApiContext, nodeTypes);
}
function resetWorkflow() {
workflow.value = createEmptyWorkflow();
}
function setUsedCredentials(data) {
workflow.value.usedCredentials = data;
usedCredentials.value = data.reduce((accu, credential) => {
accu[credential.id] = credential;
return accu;
}, {});
}
function setWorkflowVersionId(versionId) {
workflow.value.versionId = versionId;
}
function replaceInvalidWorkflowCredentials(data) {
workflow.value.nodes.forEach((node) => {
const nodeCredentials = node.credentials;
if (!nodeCredentials?.[data.type]) return;
const nodeCredentialDetails = nodeCredentials[data.type];
if (typeof nodeCredentialDetails === "string" && nodeCredentialDetails === data.invalid.name) {
node.credentials[data.type] = data.credentials;
return;
}
if (nodeCredentialDetails.id === null) {
if (nodeCredentialDetails.name === data.invalid.name) node.credentials[data.type] = data.credentials;
return;
}
if (nodeCredentialDetails.id === data.invalid.id) node.credentials[data.type] = data.credentials;
});
}
function assignCredentialToMatchingNodes(data) {
let updatedNodesCount = 0;
workflow.value.nodes.forEach((node) => {
if (node.name === data.currentNodeName) return;
if (node.credentials && Object.keys(node.credentials).length > 0) return;
const nodeType = nodeTypesStore.getNodeType(node.type, node.typeVersion);
if (!nodeType?.credentials) return;
if (!nodeType.credentials.some((cred) => cred.name === data.type)) return;
node.credentials ??= {};
node.credentials[data.type] = data.credentials;
updatedNodesCount++;
});
return updatedNodesCount;
}
function setWorkflows(workflows) {
workflowsById.value = workflows.reduce((acc, workflow$1) => {
if (workflow$1.id) acc[workflow$1.id] = workflow$1;
return acc;
}, {});
}
async function deleteWorkflow(id$1) {
await makeRestApiRequest(rootStore.restApiContext, "DELETE", `/workflows/${id$1}`);
const { [id$1]: deletedWorkflow,...workflows } = workflowsById.value;
workflowsById.value = workflows;
}
async function archiveWorkflow(id$1) {
const updatedWorkflow = await makeRestApiRequest(rootStore.restApiContext, "POST", `/workflows/${id$1}/archive`);
if (workflowsById.value[id$1]) {
workflowsById.value[id$1].isArchived = true;
workflowsById.value[id$1].versionId = updatedWorkflow.versionId;
}
setWorkflowInactive(id$1);
if (id$1 === workflow.value.id) {
setIsArchived(true);
setWorkflowVersionId(updatedWorkflow.versionId);
}
}
async function unarchiveWorkflow(id$1) {
const updatedWorkflow = await makeRestApiRequest(rootStore.restApiContext, "POST", `/workflows/${id$1}/unarchive`);
if (workflowsById.value[id$1]) {
workflowsById.value[id$1].isArchived = false;
workflowsById.value[id$1].versionId = updatedWorkflow.versionId;
}
if (id$1 === workflow.value.id) {
setIsArchived(false);
setWorkflowVersionId(updatedWorkflow.versionId);
}
}
function addWorkflow(workflow$1) {
workflowsById.value = {
...workflowsById.value,
[workflow$1.id]: {
...workflowsById.value[workflow$1.id],
...deepCopy(workflow$1)
}
};
}
function setWorkflowActive(targetWorkflowId) {
if (activeWorkflows.value.indexOf(targetWorkflowId) === -1) activeWorkflows.value.push(targetWorkflowId);
const targetWorkflow = workflowsById.value[targetWorkflowId];
if (targetWorkflow) {
targetWorkflow.active = true;
targetWorkflow.activeVersionId = targetWorkflow.versionId;
}
if (targetWorkflowId === workflow.value.id) {
uiStore.stateIsDirty = false;
workflow.value.active = true;
workflow.value.activeVersionId = workflow.value.versionId;
}
}
function setWorkflowInactive(targetWorkflowId) {
const index = activeWorkflows.value.indexOf(targetWorkflowId);
if (index !== -1) activeWorkflows.value.splice(index, 1);
const targetWorkflow = workflowsById.value[targetWorkflowId];
if (targetWorkflow) {
targetWorkflow.active = false;
targetWorkflow.activeVersionId = null;
}
if (targetWorkflowId === workflow.value.id) workflow.value.active = false;
}
async function fetchActiveWorkflows() {
const data = await getActiveWorkflows(rootStore.restApiContext);
activeWorkflows.value = data;
return data;
}
function setIsArchived(isArchived) {
workflow.value.isArchived = isArchived;
}
function setDescription(description) {
workflow.value.description = description;
}
async function getDuplicateCurrentWorkflowName(currentWorkflowName) {
if (currentWorkflowName && currentWorkflowName.length + 5 >= 128) return currentWorkflowName;
let newName = `${currentWorkflowName}${DUPLICATE_POSTFFIX}`;
try {
newName = (await getNewWorkflow(rootStore.restApiContext, { name: newName })).name;
} catch (e) {}
return newName;
}
function setWorkflowExecutionRunData(workflowResultData) {
if (workflowExecutionData.value) {
workflowExecutionData.value = {
...workflowExecutionData.value,
data: workflowResultData
};
workflowExecutionResultDataLastUpdate.value = Date.now();
workflowExecutionStartedData.value = void 0;
}
}
function setWorkflowSettings(workflowSettings$1) {
workflow.value.settings = workflowSettings$1;
workflowObject.value.setSettings(workflowSettings$1);
}
function setWorkflowPinData(data = {}) {
const validPinData = Object.keys(data).reduce((accu, nodeName) => {
accu[nodeName] = data[nodeName].map((item) => {
if (!isJsonKeyObject(item)) return { json: item };
return item;
});
return accu;
}, {});
workflow.value.pinData = validPinData;
workflowObject.value.setPinData(validPinData);
dataPinningEventBus.emit("pin-data", validPinData);
}
function addWorkflowTagIds(tags$7) {
workflow.value.tags = [...new Set([...workflow.value.tags ?? [], ...tags$7])];
}
function removeWorkflowTagId(tagId) {
const updated = workflow.value.tags.filter((id$1) => id$1 !== tagId);
workflow.value.tags = updated;
}
function setWorkflowScopes(scopes) {
workflow.value.scopes = scopes;
}
function setWorkflowMetadata(metadata) {
workflow.value.meta = metadata;
}
function addToWorkflowMetadata(data) {
workflow.value.meta = {
...workflow.value.meta,
...data
};
}
function setWorkflow(value) {
workflow.value = {
...value,
...!value.hasOwnProperty("active") ? { active: false } : {},
...!value.hasOwnProperty("connections") ? { connections: {} } : {},
...!value.hasOwnProperty("createdAt") ? { createdAt: -1 } : {},
...!value.hasOwnProperty("updatedAt") ? { updatedAt: -1 } : {},
...!value.hasOwnProperty("id") ? { id: PLACEHOLDER_EMPTY_WORKFLOW_ID } : {},
...!value.hasOwnProperty("nodes") ? { nodes: [] } : {},
...!value.hasOwnProperty("settings") ? { settings: { ...defaults.settings } } : {}
};
workflowObject.value = createWorkflowObject(workflow.value.nodes, workflow.value.connections, true);
}
function pinData$12(payload) {
const nodeName = payload.node.name;
if (!workflow.value.pinData) workflow.value.pinData = {};
if (!Array.isArray(payload.data)) payload.data = [payload.data];
if ((workflow.value.pinData?.[nodeName] ?? []).length > 0 && nodeMetadata.value[nodeName] && !payload.isRestoration) nodeMetadata.value[nodeName].pinnedDataLastUpdatedAt = Date.now();
else if (payload.isRestoration && nodeMetadata.value[nodeName]) {
delete nodeMetadata.value[nodeName].pinnedDataLastUpdatedAt;
delete nodeMetadata.value[nodeName].pinnedDataLastRemovedAt;
}
const storedPinData = payload.data.map((item) => {
if (isJsonKeyObject(item)) {
const { json, binary, pairedItem } = item;
return {
json,
...binary && { binary },
...pairedItem !== void 0 && { pairedItem }
};
}
return { json: item };
});
workflow.value.pinData[nodeName] = storedPinData;
workflowObject.value.setPinData(workflow.value.pinData);
uiStore.stateIsDirty = true;
dataPinningEventBus.emit("pin-data", { [payload.node.name]: storedPinData });
}
function unpinData(payload) {
const nodeName = payload.node.name;
if (!workflow.value.pinData) workflow.value.pinData = {};
const { [nodeName]: _,...pinData$13 } = workflow.value.pinData;
workflow.value.pinData = pinData$13;
workflowObject.value.setPinData(pinData$13);
if (nodeMetadata.value[nodeName]) nodeMetadata.value[nodeName].pinnedDataLastRemovedAt = Date.now();
uiStore.stateIsDirty = true;
dataPinningEventBus.emit("unpin-data", { nodeNames: [nodeName] });
}
function addConnection(data) {
if (data.connection.length !== 2) return;
const sourceData = data.connection[0];
const destinationData = data.connection[1];
if (!workflow.value.connections.hasOwnProperty(sourceData.node)) workflow.value.connections[sourceData.node] = {};
if (!workflow.value.connections[sourceData.node].hasOwnProperty(sourceData.type)) workflow.value.connections[sourceData.node] = {
...workflow.value.connections[sourceData.node],
[sourceData.type]: []
};
if (workflow.value.connections[sourceData.node][sourceData.type].length < sourceData.index + 1) for (let i$1 = workflow.value.connections[sourceData.node][sourceData.type].length; i$1 <= sourceData.index; i$1++) workflow.value.connections[sourceData.node][sourceData.type].push([]);
const checkProperties = [
"index",
"node",
"type"
];
let propertyName;
let connectionExists$1 = false;
const nodeConnections = workflow.value.connections[sourceData.node][sourceData.type];
const connectionsToCheck = nodeConnections[sourceData.index];
if (connectionsToCheck) connectionLoop: for (const existingConnection of connectionsToCheck) {
for (propertyName of checkProperties) if (existingConnection[propertyName] !== destinationData[propertyName]) continue connectionLoop;
connectionExists$1 = true;
break;
}
if (!connectionExists$1) {
nodeConnections[sourceData.index] = nodeConnections[sourceData.index] ?? [];
const connections$13 = nodeConnections[sourceData.index];
if (connections$13) connections$13.push(destinationData);
}
workflowObject.value.setConnections(workflow.value.connections);
}
function removeConnection(data) {
const sourceData = data.connection[0];
const destinationData = data.connection[1];
if (!workflow.value.connections.hasOwnProperty(sourceData.node)) return;
if (!workflow.value.connections[sourceData.node].hasOwnProperty(sourceData.type)) return;
if (workflow.value.connections[sourceData.node][sourceData.type].length < sourceData.index + 1) return;
uiStore.stateIsDirty = true;
const connections$13 = workflow.value.connections[sourceData.node][sourceData.type][sourceData.index];
if (!connections$13) return;
for (const index in connections$13) if (connections$13[index].node === destinationData.node && connections$13[index].type === destinationData.type && connections$13[index].index === destinationData.index) connections$13.splice(Number.parseInt(index, 10), 1);
workflowObject.value.setConnections(workflow.value.connections);
}
function removeAllNodeConnection(node, { preserveInputConnections = false, preserveOutputConnections = false } = {}) {
uiStore.stateIsDirty = true;
if (!preserveOutputConnections) delete workflow.value.connections[node.name];
if (preserveInputConnections) return;
const indexesToRemove = [];
let sourceNode, type, sourceIndex, connectionIndex, connectionData;
for (sourceNode of Object.keys(workflow.value.connections)) for (type of Object.keys(workflow.value.connections[sourceNode])) for (sourceIndex of Object.keys(workflow.value.connections[sourceNode][type])) {
indexesToRemove.length = 0;
const connectionsToRemove = workflow.value.connections[sourceNode][type][Number.parseInt(sourceIndex, 10)];
if (connectionsToRemove) {
for (connectionIndex of Object.keys(connectionsToRemove)) {
connectionData = connectionsToRemove[Number.parseInt(connectionIndex, 10)];
if (connectionData.node === node.name) indexesToRemove.push(connectionIndex);
}
indexesToRemove.forEach((index) => {
connectionsToRemove.splice(Number.parseInt(index, 10), 1);
});
}
}
workflowObject.value.setConnections(workflow.value.connections);
}
function renameNodeSelectedAndExecution(nameData) {
uiStore.stateIsDirty = true;
const runData = workflowExecutionData.value?.data?.resultData?.runData;
if (runData?.[nameData.old]) {
runData[nameData.new] = runData[nameData.old];
delete runData[nameData.old];
}
if (uiStore.lastSelectedNode === nameData.old) uiStore.lastSelectedNode = nameData.new;
const { [nameData.old]: removed,...rest } = nodeMetadata.value;
nodeMetadata.value = {
...rest,
[nameData.new]: nodeMetadata.value[nameData.old]
};
if (workflow.value.pinData?.[nameData.old]) {
const { [nameData.old]: renamed,...restPinData } = workflow.value.pinData;
workflow.value.pinData = {
...restPinData,
[nameData.new]: renamed
};
workflowObject.value.setPinData(workflow.value.pinData);
}
const resultData = workflowExecutionData.value?.data?.resultData;
if (resultData?.pinData?.[nameData.old]) {
resultData.pinData[nameData.new] = resultData.pinData[nameData.old];
delete resultData.pinData[nameData.old];
}
Object.values(workflow.value.pinData ?? {}).concat(Object.values(workflowExecutionData.value?.data?.resultData.pinData ?? {})).flatMap((executionData) => executionData.flatMap((nodeExecution) => Array.isArray(nodeExecution.pairedItem) ? nodeExecution.pairedItem : [nodeExecution.pairedItem])).forEach((pairedItem) => {
if (typeof pairedItem === "number" || pairedItem?.sourceOverwrite?.previousNode !== nameData.old) return;
pairedItem.sourceOverwrite.previousNode = nameData.new;
});
Object.values(workflowExecutionData.value?.data?.resultData.runData ?? {}).flatMap((taskData) => taskData.flatMap((task) => task.source)).forEach((source) => {
if (!source || source.previousNode !== nameData.old) return;
source.previousNode = nameData.new;
});
}
function setParentFolder(folder) {
workflow.value.parentFolder = folder;
}
function setNodes(nodes$13) {
nodes$13.forEach((node) => {
if (!node.id) nodeHelpers.assignNodeId(node);
if (node.extendsCredential) node.type = getCredentialOnlyNodeTypeName(node.extendsCredential);
if (node.position) node.position = snapPositionToGrid(node.position);
if (!nodeMetadata.value[node.name]) nodeMetadata.value[node.name] = { pristine: true };
});
workflow.value.nodes = nodes$13;
workflowObject.value.setNodes(nodes$13);
}
function setConnections(value) {
workflow.value.connections = value;
workflowObject.value.setConnections(value);
}
function addNode(nodeData) {
if (!nodeData.hasOwnProperty("name")) return;
workflow.value.nodes.push(nodeData);
workflowObject.value.setNodes(workflow.value.nodes);
if (!nodeMetadata.value[nodeData.name]) nodeMetadata.value[nodeData.name] = {};
}
function removeNode(node) {
const { [node.name]: removedNodeMetadata,...remainingNodeMetadata } = nodeMetadata.value;
nodeMetadata.value = remainingNodeMetadata;
if (workflow.value.pinData?.hasOwnProperty(node.name)) {
const { [node.name]: removedPinData,...remainingPinData } = workflow.value.pinData;
workflow.value.pinData = remainingPinData;
}
for (let i$1 = 0; i$1 < workflow.value.nodes.length; i$1++) if (workflow.value.nodes[i$1].name === node.name) {
workflow.value.nodes = [...workflow.value.nodes.slice(0, i$1), ...workflow.value.nodes.slice(i$1 + 1)];
workflowObject.value.setNodes(workflow.value.nodes);
uiStore.stateIsDirty = true;
return;
}
}
async function trackNodeExecution(pushData) {
const nodeName = pushData.nodeName;
if (pushData.data.error) {
const node = getNodeByName$1(nodeName);
telemetry$1.track("Manual exec errored", {
error_title: pushData.data.error.message,
node_type: node?.type,
node_type_version: node?.typeVersion,
node_id: node?.id,
node_graph_string: JSON.stringify(generateNodesGraph(await workflowHelpers.getWorkflowDataToSave(), workflowHelpers.getNodeTypes(), { isCloudDeployment: settingsStore.isCloudDeployment }).nodeGraph)
});
}
}
function getFormResumeUrl(node, executionId) {
const { webhookSuffix } = node.parameters.options ?? {};
const suffix = webhookSuffix && typeof webhookSuffix !== "object" ? `/${webhookSuffix}` : "";
return `${rootStore.formWaitingUrl}/${executionId}${suffix}`;
}
function addNodeExecutionStartedData(data) {
const currentData = workflowExecutionStartedData.value?.[0] === data.executionId ? workflowExecutionStartedData.value?.[1] : {};
workflowExecutionStartedData.value = [data.executionId, {
...currentData,
[data.nodeName]: [...currentData[data.nodeName] ?? [], data.data]
}];
}
function updateNodeExecutionStatus(pushData) {
if (!workflowExecutionData.value?.data) throw new Error("The \"workflowExecutionData\" is not initialized!");
const { nodeName, data, executionId } = pushData;
const isNodeWaiting = data.executionStatus === "waiting";
const node = getNodeByName$1(nodeName);
if (!node) return;
workflowExecutionData.value.data.resultData.lastNodeExecuted = nodeName;
if (workflowExecutionData.value.data.resultData.runData[nodeName] === void 0) workflowExecutionData.value.data.resultData.runData[nodeName] = [];
const tasksData = workflowExecutionData.value.data.resultData.runData[nodeName];
if (isNodeWaiting) {
tasksData.push(data);
workflowExecutionResultDataLastUpdate.value = Date.now();
if (node.type === "n8n-nodes-base.form" || node.type === "n8n-nodes-base.wait" && node.parameters.resume === "form") openFormPopupWindow(getFormResumeUrl(node, executionId));
} else {
const existingRunIndex = tasksData.findIndex((item) => item.executionIndex === data.executionIndex);
const hasWaitingItems = tasksData.some((it) => it.executionStatus === "waiting");
const index = existingRunIndex > -1 && !hasWaitingItems ? existingRunIndex : tasksData.length - 1;
const status = tasksData[index]?.executionStatus ?? "unknown";
if ("waiting" === status || "running" === status || tasksData[existingRunIndex]) tasksData.splice(index, 1, data);
else tasksData.push(data);
workflowExecutionResultDataLastUpdate.value = Date.now();
trackNodeExecution(pushData);
}
}
function updateNodeExecutionRunData(pushData) {
const tasksData = workflowExecutionData.value?.data?.resultData.runData[pushData.nodeName];
const existingRunIndex = tasksData?.findIndex((item) => item.executionIndex === pushData.data.executionIndex) ?? -1;
if (tasksData?.[existingRunIndex]) {
tasksData.splice(existingRunIndex, 1, pushData.data);
workflowExecutionResultDataLastUpdate.value = Date.now();
}
}
function clearNodeExecutionData(nodeName) {
if (!workflowExecutionData.value?.data) return;
const { [nodeName]: removedRunData,...remainingRunData } = workflowExecutionData.value.data.resultData.runData;
workflowExecutionData.value.data.resultData.runData = remainingRunData;
}
function pinDataByNodeName(nodeName) {
if (!workflow.value.pinData?.[nodeName]) return void 0;
return workflow.value.pinData[nodeName].map((item) => item.json);
}
function activeNode() {
return useNDVStore().activeNode;
}
async function getPastExecutions(filter$1, limit, lastId, firstId) {
let sendData = {};
if (filter$1) sendData = {
filter: filter$1,
firstId,
lastId,
limit
};
return await makeRestApiRequest(rootStore.restApiContext, "GET", "/executions", sendData);
}
async function getExecution(id$1) {
const response = await makeRestApiRequest(rootStore.restApiContext, "GET", `/executions/${id$1}`);
return response && unflattenExecutionData(response);
}
async function createNewWorkflow(sendData) {
sendData.active = false;
const projectStore = useProjectsStore();
if (!sendData.projectId && projectStore.currentProjectId) sendData.projectId = projectStore.currentProjectId;
const newWorkflow = await makeRestApiRequest(rootStore.restApiContext, "POST", "/workflows", sendData);
if (workflowHelpers.containsNodeFromPackage(newWorkflow, "@n8n/n8n-nodes-langchain") && !usersStore.isEasyAIWorkflowOnboardingDone) {
await updateCurrentUserSettings(rootStore.restApiContext, { easyAIWorkflowOnboarded: true });
usersStore.setEasyAIWorkflowOnboardingDone();
}
return newWorkflow;
}
async function updateWorkflow(id$1, data, forceSave = false) {
if (data.settings === null) data.settings = void 0;
const updatedWorkflow = await makeRestApiRequest(rootStore.restApiContext, "PATCH", `/workflows/${id$1}${forceSave ? "?forceSave=true" : ""}`, data);
if (workflowHelpers.containsNodeFromPackage(updatedWorkflow, "@n8n/n8n-nodes-langchain") && !usersStore.isEasyAIWorkflowOnboardingDone) {
await updateCurrentUserSettings(rootStore.restApiContext, { easyAIWorkflowOnboarded: true });
usersStore.setEasyAIWorkflowOnboardingDone();
}
return updatedWorkflow;
}
async function updateWorkflowSetting(id$1, key, value) {
let currentSettings = {};
let currentVersionId = "";
const isCurrentWorkflow = id$1 === workflow.value.id;
if (isCurrentWorkflow) {
currentSettings = workflow.value.settings ?? {};
currentVersionId = workflow.value.versionId;
} else {
const cached = workflowsById.value[id$1];
if (cached && cached.versionId) {
currentSettings = cached.settings ?? {};
currentVersionId = cached.versionId;
} else {
const fetched = await fetchWorkflow(id$1);
currentSettings = fetched.settings ?? {};
currentVersionId = fetched.versionId;
}
}
const newSettings = {
...currentSettings ?? {},
[key]: value
};
const updated = await updateWorkflow(id$1, {
versionId: currentVersionId,
settings: newSettings
});
if (isCurrentWorkflow) {
setWorkflowVersionId(updated.versionId);
setWorkflowSettings(updated.settings ?? {});
} else if (workflowsById.value[id$1]) workflowsById.value[id$1] = {
...workflowsById.value[id$1],
settings: updated.settings,
versionId: updated.versionId
};
return updated;
}
async function saveWorkflowDescription(id$1, description) {
let currentVersionId = "";
const isCurrentWorkflow = id$1 === workflow.value.id;
if (isCurrentWorkflow) currentVersionId = workflow.value.versionId;
else {
const cached = workflowsById.value[id$1];
if (cached?.versionId) currentVersionId = cached.versionId;
else currentVersionId = (await fetchWorkflow(id$1)).versionId;
}
const updated = await updateWorkflow(id$1, {
versionId: currentVersionId,
description
});
if (isCurrentWorkflow) {
setDescription(updated.description ?? "");
if (updated.versionId !== currentVersionId) setWorkflowVersionId(updated.versionId);
} else if (workflowsById.value[id$1]) workflowsById.value[id$1] = {
...workflowsById.value[id$1],
description: updated.description,
versionId: updated.versionId
};
return updated;
}
async function runWorkflow(startRunData) {
if (startRunData.workflowData.settings === null) startRunData.workflowData.settings = void 0;
try {
return await makeRestApiRequest(rootStore.restApiContext, "POST", `/workflows/${startRunData.workflowData.id}/run`, startRunData);
} catch (error) {
if (error.response?.status === 413) throw new ResponseError(i18n.baseText("workflowRun.showError.payloadTooLarge"), {
errorCode: 413,
httpStatusCode: 413
});
throw error;
}
}
async function removeTestWebhook(targetWorkflowId) {
return await makeRestApiRequest(rootStore.restApiContext, "DELETE", `/test-webhook/${targetWorkflowId}`);
}
async function fetchExecutionDataById(executionId) {
return await getExecutionData(rootStore.restApiContext, executionId);
}
function deleteExecution(execution) {
currentWorkflowExecutions.value.splice(currentWorkflowExecutions.value.indexOf(execution), 1);
}
function addToCurrentExecutions(executions) {
executions.forEach((execution) => {
if (!currentWorkflowExecutions.value.find((ex) => ex.id === execution.id) && execution.workflowId === workflowId.value) currentWorkflowExecutions.value.push(execution);
});
}
function getBinaryUrl(binaryDataId, action, fileName, mimeType) {
let restUrl = rootStore.restUrl;
if (restUrl.startsWith("/")) restUrl = window.location.origin + restUrl;
const url = new URL(`${restUrl}/binary-data`);
url.searchParams.append("id", binaryDataId);
url.searchParams.append("action", action);
if (fileName) url.searchParams.append("fileName", fileName);
if (mimeType) url.searchParams.append("mimeType", mimeType);
return url.toString();
}
function setNodePristine(nodeName, isPristine) {
nodeMetadata.value[nodeName].pristine = isPristine;
}
function resetChatMessages() {
chatMessages.value = [];
}
function appendChatMessage(message) {
chatMessages.value.push(message);
}
function checkIfNodeHasChatParent(nodeName) {
return !!workflowObject.value.getParentNodes(nodeName, NodeConnectionTypes.Main).find((parent) => {
return getNodeByName$1(parent)?.type === CHAT_TRIGGER_NODE_TYPE;
});
}
function removeNodeById(nodeId) {
const node = getNodeById(nodeId);
if (!node) return;
removeNode(node);
}
function removeNodeConnectionsById(nodeId) {
const node = getNodeById(nodeId);
if (!node) return;
removeAllNodeConnection(node);
}
function removeNodeExecutionDataById(nodeId) {
const node = getNodeById(nodeId);
if (!node) return;
clearNodeExecutionData(node.name);
}
function setSelectedTriggerNodeName(value) {
selectedTriggerNodeName.value = value;
}
function getWebhookUrl(nodeId, webhookType) {
const node = getNodeById(nodeId);
if (!node) return;
const nodeType = nodeTypesStore.getNodeType(node.type, node.typeVersion);
if (!nodeType?.webhooks?.length) return;
const webhook = nodeType.webhooks[0];
return workflowHelpers.getWebhookUrl(webhook, node, webhookType);
}
watch([selectableTriggerNodes, workflowExecutionTriggerNodeName], ([newSelectable, currentTrigger], [oldSelectable]) => {
if (currentTrigger !== void 0) {
selectedTriggerNodeName.value = currentTrigger;
return;
}
if (selectedTriggerNodeName.value === void 0 || newSelectable.every((node) => node.name !== selectedTriggerNodeName.value)) {
selectedTriggerNodeName.value = findTriggerNodeToAutoSelect(selectableTriggerNodes.value, nodeTypesStore.getNodeType)?.name;
return;
}
const newTrigger = newSelectable?.find((node) => oldSelectable?.every((old) => old.name !== node.name));
if (newTrigger !== void 0) selectedTriggerNodeName.value = newTrigger.name;
}, { immediate: true });
return {
workflow,
usedCredentials,
activeWorkflows,
currentWorkflowExecutions,
workflowExecutionData,
workflowExecutionPairedItemMappings,
workflowExecutionResultDataLastUpdate,
workflowExecutionStartedData,
activeExecutionId: readonlyActiveExecutionId,
previousExecutionId: readonlyPreviousExecutionId,
subWorkflowExecutionError,
executionWaitingForWebhook,
workflowsById,
nodeMetadata,
isInDebugMode,
chatMessages,
chatPartialExecutionDestinationNode,
workflowName,
workflowId,
workflowVersionId,
workflowSettings,
workflowTags,
allWorkflows,
isNewWorkflow,
isWorkflowActive,
workflowTriggerNodes,
currentWorkflowHasWebhookNode,
getWorkflowRunData,
getWorkflowResultDataByNodeName,
allConnections,
allNodes,
connectionsBySourceNode,
connectionsByDestinationNode,
isWaitingExecution,
isWorkflowRunning,
canvasNames,
nodesByName,
nodesIssuesExist,
workflowValidationIssues,
formatIssueMessage,
pinnedWorkflowData,
executedNode,
getAllLoadedFinishedExecutions,
getWorkflowExecution,
getPastChatMessages,
selectedTriggerNodeName: computed(() => selectedTriggerNodeName.value),
workflowExecutionTriggerNodeName,
outgoingConnectionsByNodeName,
incomingConnectionsByNodeName,
nodeHasOutputConnection,
isNodeInOutgoingNodeConnections,
getWorkflowById,
getNodeByName: getNodeByName$1,
findRootWithMainConnection,
getNodeById,
getNodesByIds,
getParametersLastUpdate,
getPinnedDataLastUpdate,
getPinnedDataLastRemovedAt,
isNodePristine,
getExecutionDataById,
getPinDataSize,
getNodeTypes: getNodeTypes$2,
getNodes,
convertTemplateNodeToNodeUi,
workflowObject,
createWorkflowObject,
cloneWorkflowObject,
getWorkflowFromUrl,
getActivationError,
searchWorkflows,
fetchAllWorkflows,
fetchWorkflowsPage,
fetchWorkflow,
fetchWorkflowsWithNodesIncluded,
resetWorkflow,
addNodeExecutionStartedData,
setUsedCredentials,
setWorkflowVersionId,
replaceInvalidWorkflowCredentials,
assignCredentialToMatchingNodes,
setWorkflows,
deleteWorkflow,
archiveWorkflow,
unarchiveWorkflow,
addWorkflow,
setWorkflowActive,
setWorkflowInactive,
fetchActiveWorkflows,
setIsArchived,
setDescription,
getDuplicateCurrentWorkflowName,
setWorkflowExecutionRunData,
setWorkflowPinData,
setParentFolder,
addWorkflowTagIds,
removeWorkflowTagId,
setWorkflowScopes,
setWorkflowMetadata,
addToWorkflowMetadata,
setWorkflow,
pinData: pinData$12,
unpinData,
addConnection,
removeConnection,
removeAllNodeConnection,
renameNodeSelectedAndExecution,
addNode,
removeNode,
updateNodeExecutionRunData,
updateNodeExecutionStatus,
clearNodeExecutionData,
pinDataByNodeName,
activeNode,
getPastExecutions,
getExecution,
createNewWorkflow,
updateWorkflow,
updateWorkflowSetting,
saveWorkflowDescription,
runWorkflow,
removeTestWebhook,
fetchExecutionDataById,
deleteExecution,
addToCurrentExecutions,
getBinaryUrl,
setNodePristine,
resetChatMessages,
appendChatMessage,
checkIfNodeHasChatParent,
removeNodeById,
removeNodeConnectionsById,
removeNodeExecutionDataById,
setNodes,
setConnections,
findNodeByPartialId,
getPartialIdForNode,
setSelectedTriggerNodeName,
totalWorkflowCount,
fetchLastSuccessfulExecution,
lastSuccessfulExecution,
getWebhookUrl,
defaults,
private: {
setWorkflowSettings,
setActiveExecutionId
}
};
});
var DEFAULT_MAIN_PANEL_DIMENSIONS = {
relativeLeft: 1,
relativeRight: 1,
relativeWidth: 1
};
const useNDVStore = defineStore(STORES.NDV, () => {
const localStorageMappingIsOnboarded = useStorage(LOCAL_STORAGE_MAPPING_IS_ONBOARDED);
const localStorageTableHoverIsOnboarded = useStorage(LOCAL_STORAGE_TABLE_HOVER_IS_ONBOARDED);
const localStorageAutoCompleteIsOnboarded = useStorage(LOCAL_STORAGE_AUTOCOMPLETE_IS_ONBOARDED);
const activeNodeName = ref(null);
const mainPanelDimensions = ref({
unknown: { ...DEFAULT_MAIN_PANEL_DIMENSIONS },
regular: { ...DEFAULT_MAIN_PANEL_DIMENSIONS },
dragless: { ...DEFAULT_MAIN_PANEL_DIMENSIONS },
inputless: { ...DEFAULT_MAIN_PANEL_DIMENSIONS },
wide: { ...DEFAULT_MAIN_PANEL_DIMENSIONS }
});
const pushRef = ref("");
const input = ref({
nodeName: void 0,
run: void 0,
branch: void 0,
data: { isEmpty: true }
});
const inputPanelDisplayMode = useLocalStorage(LOCAL_STORAGE_NDV_INPUT_PANEL_DISPLAY_MODE, "schema");
const output = ref({
run: void 0,
branch: void 0,
data: { isEmpty: true },
editMode: {
enabled: false,
value: ""
}
});
const outputPanelDisplayMode = useLocalStorage(LOCAL_STORAGE_NDV_OUTPUT_PANEL_DISPLAY_MODE, "table");
const focusedMappableInput = ref("");
const focusedInputPath = ref("");
const mappingTelemetry = ref({});
const hoveringItem = ref(null);
const expressionOutputItemIndex = ref(0);
const draggable = ref({
isDragging: false,
type: "",
data: "",
dimensions: null,
activeTarget: null
});
const isMappingOnboarded = ref(localStorageMappingIsOnboarded.value === "true");
const isTableHoverOnboarded = ref(localStorageTableHoverIsOnboarded.value === "true");
const isAutocompleteOnboarded = ref(localStorageAutoCompleteIsOnboarded.value === "true");
const highlightDraggables = ref(false);
const lastSetActiveNodeSource = ref();
const workflowsStore = useWorkflowsStore();
const workflowState = injectWorkflowState();
const activeNode = computed(() => {
return workflowsStore.getNodeByName(activeNodeName.value || "");
});
const ndvInputData = computed(() => {
const executionData = workflowsStore.getWorkflowExecution;
const inputNodeName = input.value.nodeName;
const inputRunIndex = input.value.run ?? 0;
const inputBranchIndex = input.value.branch ?? 0;
if (!executionData || !inputNodeName || inputRunIndex === void 0 || inputBranchIndex === void 0) return [];
return executionData.data?.resultData?.runData?.[inputNodeName]?.[inputRunIndex]?.data?.main?.[inputBranchIndex] ?? [];
});
const ndvInputNodeName = computed(() => {
return input.value.nodeName;
});
const ndvInputDataWithPinnedData = computed(() => {
const data = ndvInputData.value;
return ndvInputNodeName.value ? workflowsStore.pinDataByNodeName(ndvInputNodeName.value) ?? data : data;
});
const hasInputData = computed(() => {
return ndvInputDataWithPinnedData.value.length > 0;
});
const isDraggableDragging = computed(() => draggable.value.isDragging);
const draggableType = computed(() => draggable.value.type);
const draggableData = computed(() => draggable.value.data);
const canDraggableDrop = computed(() => draggable.value.activeTarget !== null);
const outputPanelEditMode = computed(() => output.value.editMode);
const draggableStickyPos = computed(() => draggable.value.activeTarget?.stickyPosition ?? null);
const ndvNodeInputNumber = computed(() => {
const returnData = {};
const activeNodeConections = (workflowsStore.connectionsByDestinationNode[activeNode.value?.name || ""] ?? {}).main;
if (!activeNodeConections || activeNodeConections.length < 2) return returnData;
for (const [index, connection] of activeNodeConections.entries()) for (const node of connection ?? []) {
if (!returnData[node.node]) returnData[node.node] = [];
returnData[node.node].push(index + 1);
}
return returnData;
});
const ndvInputRunIndex = computed(() => input.value.run);
const ndvInputBranchIndex = computed(() => input.value.branch);
const isInputPanelEmpty = computed(() => input.value.data.isEmpty);
const isOutputPanelEmpty = computed(() => output.value.data.isEmpty);
const isInputParentOfActiveNode = computed(() => {
const inputNodeName = ndvInputNodeName.value;
if (!activeNode.value || !inputNodeName) return false;
return workflowsStore.workflowObject.getParentNodes(activeNode.value.name, NodeConnectionTypes.Main, 1).includes(inputNodeName);
});
const getHoveringItem = computed(() => {
if (isInputParentOfActiveNode.value) return hoveringItem.value;
return null;
});
const expressionTargetItem = computed(() => {
if (getHoveringItem.value) return getHoveringItem.value;
if (expressionOutputItemIndex.value && ndvInputNodeName.value) return {
nodeName: ndvInputNodeName.value,
runIndex: ndvInputRunIndex.value ?? 0,
outputIndex: ndvInputBranchIndex.value ?? 0,
itemIndex: expressionOutputItemIndex.value
};
return null;
});
const isNDVOpen = computed(() => activeNodeName.value !== null);
const unsetActiveNodeName = () => {
activeNodeName.value = null;
lastSetActiveNodeSource.value = void 0;
};
const setActiveNodeName = (nodeName, source) => {
if (activeNodeName.value === nodeName) return;
activeNodeName.value = nodeName;
lastSetActiveNodeSource.value = source;
};
const setInputNodeName = (nodeName) => {
input.value.nodeName = nodeName;
};
const setInputRunIndex = (run) => {
input.value.run = run;
};
const setOutputRunIndex = (run) => {
output.value.run = run;
};
const setMainPanelDimensions = (params) => {
mainPanelDimensions.value[params.panelType] = {
...mainPanelDimensions.value[params.panelType],
...params.dimensions
};
};
const setNDVPushRef = () => {
pushRef.value = `ndv-${v4_default()}`;
};
const resetNDVPushRef = () => {
pushRef.value = "";
};
const setPanelDisplayMode = (params) => {
if (params.pane === "input") inputPanelDisplayMode.value = params.mode;
else outputPanelDisplayMode.value = params.mode;
};
const setOutputPanelEditModeEnabled = (isEnabled) => {
output.value.editMode.enabled = isEnabled;
};
const setOutputPanelEditModeValue = (payload) => {
output.value.editMode.value = payload;
};
const setMappableNDVInputFocus = (paramName) => {
focusedMappableInput.value = paramName;
};
const draggableStartDragging = ({ type, data, dimensions }) => {
draggable.value = {
isDragging: true,
type,
data,
dimensions,
activeTarget: null
};
};
const draggableStopDragging = () => {
draggable.value = {
isDragging: false,
type: "",
data: "",
dimensions: null,
activeTarget: null
};
};
const setDraggableTarget = (target) => {
draggable.value.activeTarget = target;
};
const setMappingTelemetry = (telemetry$1) => {
mappingTelemetry.value = {
...mappingTelemetry.value,
...telemetry$1
};
};
const resetMappingTelemetry = () => {
mappingTelemetry.value = {};
};
const setHoveringItem = (item) => {
if (item) setTableHoverOnboarded();
hoveringItem.value = item;
};
const setNDVBranchIndex = (e) => {
if (e.pane === "input") input.value.branch = e.branchIndex;
else output.value.branch = e.branchIndex;
};
const setNDVPanelDataIsEmpty = (params) => {
if (params.panel === "input") input.value.data.isEmpty = params.isEmpty;
else output.value.data.isEmpty = params.isEmpty;
};
const setMappingOnboarded = () => {
isMappingOnboarded.value = true;
localStorageMappingIsOnboarded.value = "true";
};
const setTableHoverOnboarded = () => {
isTableHoverOnboarded.value = true;
localStorageTableHoverIsOnboarded.value = "true";
};
const setAutocompleteOnboarded = () => {
isAutocompleteOnboarded.value = true;
localStorageAutoCompleteIsOnboarded.value = "true";
};
const setHighlightDraggables = (highlight) => {
highlightDraggables.value = highlight;
};
const updateNodeParameterIssues = (issues) => {
const activeNode$1 = workflowsStore.getNodeByName(activeNodeName.value || "");
if (activeNode$1) {
const nodeIndex = workflowsStore.workflow.nodes.findIndex((node) => {
return node.name === activeNode$1.name;
});
workflowState.updateNodeAtIndex(nodeIndex, { issues: {
...activeNode$1.issues,
...issues
} });
}
};
const setFocusedInputPath = (path) => {
focusedInputPath.value = path;
};
return {
activeNode,
ndvInputData,
ndvInputNodeName,
ndvInputDataWithPinnedData,
hasInputData,
inputPanelDisplayMode,
outputPanelDisplayMode,
isDraggableDragging,
draggableType,
draggableData,
canDraggableDrop,
outputPanelEditMode,
draggableStickyPos,
ndvNodeInputNumber,
ndvInputRunIndex,
ndvInputBranchIndex,
isInputParentOfActiveNode,
getHoveringItem,
expressionTargetItem,
isNDVOpen,
isInputPanelEmpty,
isOutputPanelEmpty,
focusedMappableInput,
isMappingOnboarded,
pushRef,
activeNodeName,
focusedInputPath,
input,
output,
hoveringItem,
highlightDraggables,
mappingTelemetry,
draggable,
isAutocompleteOnboarded,
expressionOutputItemIndex,
isTableHoverOnboarded,
mainPanelDimensions,
lastSetActiveNodeSource,
setActiveNodeName,
unsetActiveNodeName,
setInputNodeName,
setInputRunIndex,
setOutputRunIndex,
setMainPanelDimensions,
setNDVPushRef,
resetNDVPushRef,
setPanelDisplayMode,
setOutputPanelEditModeEnabled,
setOutputPanelEditModeValue,
setMappableNDVInputFocus,
draggableStartDragging,
draggableStopDragging,
setDraggableTarget,
setMappingTelemetry,
resetMappingTelemetry,
setHoveringItem,
setNDVBranchIndex,
setNDVPanelDataIsEmpty,
setMappingOnboarded,
setTableHoverOnboarded,
setAutocompleteOnboarded,
setHighlightDraggables,
updateNodeParameterIssues,
setFocusedInputPath
};
});
var EVENTS = { IS_PART_OF_EXPERIMENT: "User is part of experiment" };
const usePostHog = defineStore("posthog", () => {
const usersStore = useUsersStore();
const settingsStore = useSettingsStore();
const telemetry$1 = useTelemetry();
const rootStore = useRootStore();
const { debounce } = useDebounce();
const featureFlags = ref(null);
const trackedDemoExp = ref({});
const overrides = ref({});
const reset = () => {
window.posthog?.reset?.();
featureFlags.value = null;
trackedDemoExp.value = {};
};
const getVariant = (experiment) => {
return overrides.value[experiment] ?? featureFlags.value?.[experiment];
};
const isVariantEnabled = (experiment, variant) => {
return getVariant(experiment) === variant;
};
const isFeatureEnabled = (experiment) => {
return getVariant(experiment) === true;
};
if (!window.featureFlags) {
const cachedOverrides = useStorage(LOCAL_STORAGE_EXPERIMENT_OVERRIDES).value;
if (cachedOverrides) try {
console.log("Overriding feature flags", cachedOverrides);
if (typeof JSON.parse(cachedOverrides) === "object") overrides.value = JSON.parse(cachedOverrides);
} catch (e) {
console.log("Could not override experiment", e);
}
window.featureFlags = {
override: (name$13, value) => {
overrides.value[name$13] = value;
try {
useStorage(LOCAL_STORAGE_EXPERIMENT_OVERRIDES).value = JSON.stringify(overrides.value);
} catch (e) {}
},
getVariant,
getAll: () => featureFlags.value ?? {}
};
}
const identify = () => {
const instanceId = rootStore.instanceId;
const user = usersStore.currentUser;
const traits = {
instance_id: instanceId,
version_cli: rootStore.versionCli
};
if (user && typeof user.createdAt === "string") traits.created_at_timestamp = new Date(user.createdAt).getTime();
const id$1 = user ? `${instanceId}#${user.id}` : instanceId;
window.posthog?.identify?.(id$1, traits);
};
const trackExperiment = (featFlags, name$13) => {
const variant = featFlags[name$13];
if (!variant || trackedDemoExp.value[name$13] === variant) return;
telemetry$1.track(EVENTS.IS_PART_OF_EXPERIMENT, {
name: name$13,
variant
});
trackedDemoExp.value[name$13] = variant;
};
const trackExperiments = (featFlags) => {
EXPERIMENTS_TO_TRACK.forEach((name$13) => trackExperiment(featFlags, name$13));
};
const trackExperimentsDebounced = debounce(trackExperiments, { debounceTime: 2e3 });
const init$1 = (evaluatedFeatureFlags) => {
if (!window.posthog) return;
const config = settingsStore.settings.posthog;
if (!config.enabled) return;
const userId = usersStore.currentUserId;
if (!userId) return;
const distinctId = `${rootStore.instanceId}#${userId}`;
const options = {
api_host: "https://us.i.posthog.com",
autocapture: config.autocapture,
disable_session_recording: config.disableSessionRecording,
debug: config.debug,
session_recording: { maskAllInputs: false }
};
window.posthog?.init(config.apiKey, options);
identify();
if (evaluatedFeatureFlags && Object.keys(evaluatedFeatureFlags).length) {
featureFlags.value = evaluatedFeatureFlags;
options.bootstrap = {
distinctId,
featureFlags: evaluatedFeatureFlags
};
trackExperimentsDebounced(featureFlags.value);
} else window.posthog?.onFeatureFlags?.((_, map) => {
featureFlags.value = map;
trackExperimentsDebounced(featureFlags.value);
});
};
const setMetadata = (metadata, target) => {
if (typeof window.posthog?.people?.set !== "function") return;
if (typeof window.posthog?.register !== "function") return;
if (target === "user") window.posthog?.people?.set(metadata);
else if (target === "events") window.posthog?.register(metadata);
};
const capture = (event, properties) => {
if (typeof window.posthog?.capture === "function") window.posthog.capture(event, properties);
};
return {
init: init$1,
isFeatureEnabled,
isVariantEnabled,
getVariant,
reset,
identify,
setMetadata,
capture,
overrides
};
});
var Telemetry = class {
pageEventQueue;
previousPath;
get rudderStack() {
return window.rudderanalytics;
}
constructor() {
this.pageEventQueue = [];
this.previousPath = "";
}
init(telemetrySettings, { instanceId, userId, projectId, versionCli }) {
if (!telemetrySettings.enabled || !telemetrySettings.config || this.rudderStack) return;
const { config: { key, proxy, sourceConfig } } = telemetrySettings;
const rootStore = useRootStore();
this.initRudderStack(key, proxy, {
integrations: { All: false },
loadIntegration: false,
configUrl: sourceConfig
});
this.identify(instanceId, userId, versionCli, projectId);
this.flushPageEvents();
this.track("Session started", { session_id: rootStore.pushRef });
}
identify(instanceId, userId, versionCli, projectId) {
const settingsStore = useSettingsStore();
const traits = {
instance_id: instanceId,
version_cli: versionCli
};
if (settingsStore.isCloudDeployment) traits.user_cloud_id = settingsStore.settings?.n8nMetadata?.userId ?? "";
if (userId) this.rudderStack?.identify(`${instanceId}#${userId}${projectId ? "#" + projectId : ""}`, traits, { context: { ip: "0.0.0.0" } });
else this.rudderStack?.reset();
}
track(event, properties) {
if (!this.rudderStack) return;
const posthogSessionId = window.posthog?.get_session_id?.();
const updatedProperties = {
...properties,
version_cli: useRootStore().versionCli,
posthog_session_id: posthogSessionId
};
this.rudderStack.track(event, updatedProperties, { context: { ip: "0.0.0.0" } });
usePostHog().capture(event, updatedProperties);
}
page(route) {
if (this.rudderStack) {
if (route.path === this.previousPath) return;
this.previousPath = route.path;
const pageName = String(route.name);
let properties = {};
if (route.meta?.telemetry && typeof route.meta.telemetry.getProperties === "function") properties = route.meta.telemetry.getProperties(route);
properties.theme = useUIStore().appliedTheme;
const category = route.meta?.telemetry?.pageCategory || "Editor";
this.rudderStack.page(category, pageName, properties, { context: { ip: "0.0.0.0" } });
} else this.pageEventQueue.push({ route });
}
reset() {
this.rudderStack?.reset();
}
flushPageEvents() {
const queue = this.pageEventQueue;
this.pageEventQueue = [];
queue.forEach(({ route }) => {
this.page(route);
});
}
trackAskAI(event, properties = {}) {
if (this.rudderStack) {
properties.session_id = useRootStore().pushRef;
properties.ndv_session_id = useNDVStore().pushRef;
switch (event) {
case "askAi.generationFinished": this.track("Ai code generation finished", properties);
default: break;
}
}
}
trackAiTransform(event, properties = {}) {
if (this.rudderStack) {
properties.session_id = useRootStore().pushRef;
properties.ndv_session_id = useNDVStore().pushRef;
switch (event) {
case "generationFinished": this.track("Ai Transform code generation finished", properties);
default: break;
}
}
}
trackNodeParametersValuesChange(nodeType, change) {
if (this.rudderStack) {
const changeName = {
["n8n-nodes-base.slack"]: "parameters.otherOptions.includeLinkToWorkflow",
["n8n-nodes-base.microsoftTeams"]: "parameters.options.includeLinkToWorkflow",
["n8n-nodes-base.telegram"]: "parameters.additionalFields.appendAttribution"
}[nodeType] || "parameters.options.appendAttribution";
if (change.name === changeName) this.track("User toggled n8n reference option", {
node: nodeType,
toValue: change.value
});
}
}
initRudderStack(key, proxy, options) {
window.rudderanalytics = window.rudderanalytics || [];
if (!this.rudderStack) return;
this.rudderStack.methods = [
"load",
"page",
"track",
"identify",
"alias",
"group",
"ready",
"reset",
"getAnonymousId",
"setAnonymousId"
];
this.rudderStack.factory = (method) => {
return (...args) => {
if (!this.rudderStack) throw new Error("RudderStack not initialized");
const argsCopy = [method, ...args];
this.rudderStack.push(argsCopy);
return this.rudderStack;
};
};
for (const method of this.rudderStack.methods) this.rudderStack[method] = this.rudderStack.factory(method);
this.rudderStack.loadJS = () => {
const script = document.createElement("script");
script.type = "text/javascript";
script.async = !0;
script.src = "https://cdn-rs.n8n.io/v1/ra.min.js";
const element = document.getElementsByTagName("script")[0];
if (element && element.parentNode) element.parentNode.insertBefore(script, element);
};
this.rudderStack.loadJS();
this.rudderStack.load(key, proxy, options);
}
};
const telemetry = new Telemetry();
const TelemetryPlugin = { install(app) {
app.config.globalProperties.$telemetry = telemetry;
} };
function useTelemetry() {
return telemetry;
}
export { getDefaultExecutionFilters as $, PERSONAL_COMPANY_TYPE as $a, FOLDER_NAME_ONLY_DOTS_REGEX as $i, hasScope as $n, requestLicenseTrial as $o, getThemedValue as $r, UserManagementAuthenticationMethod as $s, escapeMappingString as $t, _sfc_main$1 as A, GOVERNMENT_INDUSTRY as Aa, htmlEditorEventBus as Ai, addDataTableColumnApi as An, COMMUNITY_PLUS_DOCS_URL as Ao, EnableNodeToggleCommand as Ar, updateProvider as As, getSampleWorkflowByTemplateId as At, isMacOs as B, MARKETING_AUTOMATION_GOAL_KEY as Ba, COMMUNITY_NODES_INSTALLATION_DOCS_URL as Bi, moveDataTableColumnApi as Bn, LOCAL_STORAGE_NDV_PANEL_WIDTH as Bo, splitName as Br, installNewPackage as Bs, require__createCompounder as Bt, getNodesWithNormalizedPosition as C, DEVOPS_MONITORING_AND_ALERTING_GOAL as Ca, stringSizeInBytes as Ci, useWorkflowState as Cn, SAAS_COMPANY_TYPE as Co, CanvasNodeDirtiness as Cr, updateLdapConfig as Cs, sortNodeCreateElements as Ct, PanelPosition as D, ECOMMERCE_COMPANY_TYPE as Da, searchInObject as Di, useSourceControlStore as Dn, CREDENTIAL_EDIT_MODAL_KEY as Do, AddNodeCommand as Dr, getExternalSecretsProviders as Ds, getEasyAiWorkflowJson as Dt, NodeId as E, DIGITAL_AGENCY_COMPANY_TYPE as Ea, omitKey as Ei, useProjectsStore as En, TELECOMS_INDUSTRY as Eo, AddConnectionCommand as Er, getExternalSecretsProvider as Es, SampleTemplates as Et, getBezierPath as F, MANUFACTURING_INDUSTRY as Fa, useUIStore as Fi, downloadDataTableCsvApi as Fn, TAGS_MANAGER_MODAL_KEY as Fo, ReplaceNodeParametersCommand as Fr, saveDestinationToDb as Fs, isTemplatesWorkflow as Ft, emptyTokenUsageData as G, MEDIA_INDUSTRY as Ga, NPM_KEYWORD_SEARCH_URL as Gi, useCanvasStore as Gn, getNextVersions as Go, getAllNodeCredentialForAuthType as Gr, getCurrentPlan as Gs, generateCodeForPrompt as Gt, useVueFlow as H, MARKETING_AUTOMATION_OTHER as Ha, COMMUNITY_PACKAGE_CONFIRM_MODAL_KEY as Hi, updateDataTableRowsApi as Hn, useSettingsStore as Ho, fieldCannotBeDeleted as Hr, updatePackage as Hs, useCredentialsStore as Ht, getBoundsofRects as I, MARKETING_AUTOMATION_ACTIONS as Ia, getAll as Ii, fetchDataTableGlobalLimitInBytes as In, getNewWorkflow as Io, historyBus as Ir, sendTestMessageToDestination as Is, capitalCase as It, parseAiContent as J, OTHER_COMPANY_INDUSTRY_EXTENDED_KEY as Ja, SOURCE_CONTROL_PUSH_MODAL_KEY as Ji, isGuest as Jn, updateCurrentUserSettings as Jo, getAuthTypeForNodeCredential as Jr, createApiKey as Js, getSessionsMetadata as Jt, formatTokenUsageCount as K, MSP_INDUSTRY as Ka, PROJECT_MOVE_RESOURCE_MODAL as Ki, useLoadingService as Kn, getWhatsNewSection as Ko, getAppNameFromCredType as Kr, getCurrentUsage as Ks, getAiSessions as Kt, getConnectedEdges as L, MARKETING_AUTOMATION_AD_CAMPAIGN as La, register as Li, fetchDataTablesApi as Ln, getWorkflowsAndFolders as Lo, MAX_NAME_LENGTH as Lr, getBecomeCreatorCta as Ls, require_sortBy as Lt, _sfc_main$d as M, INVITE_USER_MODAL_KEY as Ma, dataPinningEventBus as Mi, deleteDataTableApi as Mn, DEBUG_PAYWALL_MODAL_KEY as Mo, RemoveConnectionCommand as Mr, getDestinationsFromBackend as Ms, isPrebuiltAgentTemplateId as Mt, _sfc_main$f as N, IT_INDUSTRY as Na, codeNodeEditorEventBus as Ni, deleteDataTableColumnApi as Nn, ANNOTATION_TAGS_MANAGER_MODAL_KEY as No, RemoveNodeCommand as Nr, getEventNamesFromBackend as Ns, isTutorialTemplateId as Nt, Position as O, EDUCATION_TYPE as Oa, nodeViewEventBus as Oi, ProjectTypes as On, CREDENTIAL_SELECT_MODAL_KEY as Oo, BulkCommand as Or, reloadProvider as Os, getPrebuiltAgents as Ot, clamp as P, LEGAL_INDUSTRY as Pa, listenForModalChanges as Pi, deleteDataTableRowsApi as Pn, MAX_TAG_NAME_LENGTH as Po, RenameNodeCommand as Pr, hasDestinationId as Ps, isFullTemplatesCollection as Pt, executionRetryMessage as Q, PERSONALIZATION_MODAL_KEY as Qa, FOLDER_NAME_MAX_LENGTH as Qi, shouldEnableMfa as Qn, renewLicense as Qo, getNodeCredentialForSelectedAuthType as Qr, updateApiKey as Qs, useFlattenSchema as Qt, getRectOfNodes as R, MARKETING_AUTOMATION_CUSTOMER_COMMUNICATION as Ra, subscribe as Ri, getDataTableRowsApi as Rn, v4_default as Ro, ResourceType as Rr, getAvailableCommunityPackageCount as Rs, require_upperFirst as Rt, getNodesGroupSize as S, DEVOPS_INCIDENT_RESPONSE_GOAL as Sa, shorten as Si, injectWorkflowState as Sn, ROLE_SECURITY as So, CanvasConnectionMode as Sr, testLdapConnection as Ss, shouldShowCommunityNodeDetails as St, MarkerType as T, DEVOPS_TICKETING_SYSTEMS_INTEGRATIONS_GOAL as Ta, tryToParseNumber as Ti, useDocumentTitle as Tn, SYSTEMS_INTEGRATOR_COMPANY_TYPE as To, useHistoryStore as Tr, getExternalSecrets as Ts, transformNodeType as Tt, wheelDelta as U, MARKETING_AUTOMATION_REPORTING as Ua, COMMUNITY_PACKAGE_INSTALL_MODAL_KEY as Ui, uploadCsvFileApi as Un, getWorkflowHistory as Uo, filterTemplateNodes as Ur, getAdminPanelLoginCode as Us, chatWithAssistant as Ut, useGetPointerPosition as V, MARKETING_AUTOMATION_LEAD_GENERATION_GOAL as Va, COMMUNITY_NODES_RISKS_DOCS_URL as Vi, updateDataTableApi as Vn, useStorage as Vo, executionDataToJson as Vr, uninstallPackage as Vs, listenForCredentialChanges as Vt, addTokenUsageData as W, MARKETING_INDUSTRY as Wa, COMMUNITY_PACKAGE_MANAGE_ACTIONS as Wi, getPairedItemId as Wn, getWorkflowVersion as Wo, getActivatableTriggerNodes as Wr, getCloudUserInfo as Ws, chatWithBuilder as Wt, displayForm as X, OTHER_INDUSTRY_OPTION as Xa, FOLDER_LIST_ITEM_ACTIONS as Xi, isDefaultUser as Xn, getLicense as Xo, getNodeAuthFields as Xr, getApiKeyScopes as Xs, useAIAssistantHelpers as Xt, clearPopupWindowState as Y, OTHER_COMPANY_TYPE as Ya, DELETE_FOLDER_MODAL_KEY as Yi, isEnterpriseFeatureEnabled as Yn, activateLicenseKey as Yo, getMainAuthField as Yr, deleteApiKey as Ys, replaceCode as Yt, executionFilterToQueryFilter as Z, OTHER_MARKETING_AUTOMATION_GOAL_KEY as Za, FOLDER_NAME_ILLEGAL_CHARACTERS_REGEX as Zi, isAuthenticated as Zn, registerCommunityEdition as Zo, getNodeAuthOptions as Zr, getApiKeys as Zs, useDataSchema as Zt, getGenericHints as _, DEVOPS_AUTOMATION_CLOUD_INFRASTRUCTURE_ORCHESTRATION_GOAL as _a, isBlurrableEl as _i, useTagsStore as _n, ROLE_IT as _o, isValidCredentialResponse as _r, sendGetWorkerStatus as _s, isAINode as _t, useNDVStore as a, AUTOMATION_BENEFICIARY_SELF as aa, getWorkflows$1 as ac, isRequiredCredential as ai, getResolvableState as an, REPORTED_SOURCE_KEY as ao, isCredentialModalState as ar, initSSO as as, useNodeTypesStore as at, getNewNodePosition as b, DEVOPS_AUTOMATION_GOAL_OTHER_KEY as ba, isPresent as bi, useEnvironmentsStore as bn, ROLE_OTHER_KEY as bo, isVueFlowConnection as br, getLdapSynchronizations as bs, removePreviewToken as bt, CONFIGURATION_NODE_RADIUS as c, COMPANY_SIZE_100_499 as ca, parseResourceMapperFieldName as ci, removeExpressionPrefix as cn, REPORTED_SOURCE_OTHER_KEY as co, isFullExecutionResponse as cr, testSamlConfig as cs, filterAndSearchNodes as ct, DEFAULT_VIEWPORT_BOUNDARIES as d, COMPANY_SIZE_500_999 as da, abbreviateNumber as di, unwrapExpression as dn, REPORTED_SOURCE_YOUTUBE as do, isResourceSortableByDate as dr, getRoleBySlug as ds, getActiveViewCallouts as dt, ILLEGAL_FOLDER_CHARACTERS as ea, getCategories as ec, getTriggerNodeServiceName as ei, getMappedExpression as en, PHYSICAL_RETAIL_OR_SERVICES as eo, useRBACStore as er, dismissBannerPermanently as es, getExecutionErrorMessage as et, GRID_SIZE as f, COMPANY_SIZE_KEY as fa, clearJsonKey as fi, resolveParameter as fn, ROLE_BUSINESS_OWNER as fo, isRouteLocationRaw as fr, getRoles as fs, getAiTemplatesCallout as ft, getBounds as g, DEVOPS_AUTOMATION_CI_CD_GOAL as ga, hasFocusOnInput as gi, useAnnotationTagsStore as gn, ROLE_ENGINEERING as go, isValidCanvasConnectionMode as gr, saveProvisioningConfig as gs, groupItemsInSections as gt, generateOffsets as h, DELETE_USER_MODAL_KEY as ha, convertToHumanReadableDate as hi, useWorkflowsEEStore as hn, ROLE_DEVOPS as ho, isTriggerPanelObject as hr, getProvisioningConfig as hs, getRootSearchCallouts as ht, usePostHog as i, AUTOMATION_BENEFICIARY_OTHER_TEAMS as ia, getWorkflowTemplate as ic, isMatchingField as ii, getExpressionErrorMessage as in, REPORTED_SOURCE_GOOGLE as io, isComponentPublicInstance as ir, getSamlMetadata as is, waitingNodeTooltip as it, _sfc_main$3 as j, HEALTHCARE_INDUSTRY as ja, globalLinkActionsEventBus as ji, createDataTableApi as jn, COMMUNITY_PLUS_ENROLLMENT_MODAL as jo, MoveNodeCommand as jr, deleteDestinationFromDb as js, getTutorialTemplates as jt, _sfc_main as k, FINANCE_INSURANCE_INDUSTRY as ka, importCurlEventBus as ki, moveFolderToProject as kn, VARIABLE_MODAL_KEY as ko, Command as kr, testExternalSecretsProviderConnection as ks, getRagStarterWorkflowJson as kt, CONFIGURATION_NODE_SIZE as l, COMPANY_SIZE_20_99 as la, updateNodeAuthType as li, shouldConvertToExpression as ln, REPORTED_SOURCE_PODCAST as lo, isProjectRole as lr, createProjectRole as ls, finalizeItems as lt, calculateNodeSize as m, COMPANY_TYPE_KEY as ma, convertToDisplayDate as mi, useWorkflowHelpers as mn, ROLE_DATA_SCIENCE as mo, isString as mr, getPromptsData as ms, getPreBuiltAgentsCalloutWithDivider as mt, TelemetryPlugin as n, AUTOMATION_BENEFICIARY_KEY as na, getCollections as nc, hasOnlyListMode as ni, propertyNameFromExpression as nn, REPORTED_SOURCE_EVENT as no, useUsersStore as nr, getOidcConfig as ns, hasTrimmedRunData as nt, useWorkflowsStore as o, COMPANY_INDUSTRY_EXTENDED_KEY as oa, STORES as oc, isResourceMapperFieldListStale as oi, isPairedItemIntermediateNodesError as on, REPORTED_SOURCE_LINKEDIN as oo, isCredentialsResource as or, saveOidcConfig as os, useActionsGenerator as ot, PUSH_NODES_OFFSET as p, COMPANY_SIZE_PERSONAL_USE as pa, convertPath as pi, resolveRequiredParameters as pn, ROLE_CUSTOMER_SUPPORT as po, isSharedResource as pr, updateProjectRole as ps, getHumanInTheLoopActions as pt, isChatNode as q, OTHER_AUTOMATION_GOAL as qa, SOURCE_CONTROL_PULL_MODAL_KEY as qi, hasPermission as qn, getUsers as qo, getAppNameFromNodeName as qr, sendUserEvent as qs, getBuilderCredits as qt, telemetry as r, AUTOMATION_BENEFICIARY_MY_TEAM as ra, getTemplateById as rc, isAuthRelatedParameter as ri, completeExpressionSyntax as rn, REPORTED_SOURCE_FRIEND as ro, isBaseTextKey as rr, getSamlConfig as rs, unflattenExecutionData as rt, CONFIGURABLE_NODE_SIZE as s, COMPANY_SIZE_1000_OR_MORE as sa, ElLoading as sc, isValueExpression as si, isTestableExpression as sn, REPORTED_SOURCE_OTHER as so, isDateObject as sr, saveSamlConfig as ss, useEvaluationStore as st, useTelemetry as t, MOVE_FOLDER_MODAL_KEY as ta, getCollectionById as tc, hasExpressionMapping as ti, getMappedResult as tn, REAL_ESTATE_OR_CONSTRUCTION as to, hasRole as tr, getThirdPartyLicenses as ts, getExecutionErrorToastConfiguration as tt, DEFAULT_NODE_SIZE as u, COMPANY_SIZE_20_OR_LESS as ua, parse as ui, stringifyExpressionResult as un, REPORTED_SOURCE_TWITTER as uo, isResourceMapperValue as ur, deleteProjectRole as us, flattenCreateElements as ut, getMidCanvasPosition as v, DEVOPS_AUTOMATION_DATA_SYNCING_GOAL as va, isEmpty as vi, isCredentialOnlyNodeType as vn, ROLE_KEY as vo, isValidNodeConnectionType as vr, updateNpsSurveyState as vs, isNodePreviewKey as vt, updateViewportToContainNodes as w, DEVOPS_REPORTING_GOAL as wa, toMegaBytes as wi, workflowStateEventBus as wn, SECURITY_INDUSTRY as wo, CanvasNodeRenderType as wr, connectProvider as ws, subcategorizeItems as wt, getNodeViewTab as x, DEVOPS_AUTOMATION_OTHER as xa, isSelectableEl as xi, useNodeHelpers as xn, ROLE_SALES_AND_MARKETING as xo, isWorkflowListItem as xr, runLdapSync as xs, searchNodes as xt, getMousePosition as y, DEVOPS_AUTOMATION_GOAL_KEY as ya, isFocusableEl as yi, environments_store_default as yn, ROLE_OTHER as yo, isVariableResource as yr, getLdapConfig as ys, prepareCommunityNodeDetailsViewStack as yt, getSmoothStepPath as z, MARKETING_AUTOMATION_DATA_SYNCHING as za, API_KEY_CREATE_OR_EDIT_MODAL_KEY as zi, insertDataTableRowApi as zn, validate_default as zo, getTruncatedProjectName as zr, getInstalledCommunityNodes as zs, require__baseSlice as zt };