mirror of
https://github.com/n8n-io/n8n.git
synced 2024-09-11 13:15:28 +03:00
refactor(editor): Fix NodeView/Canvas related TS errors (#9581)
Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
This commit is contained in:
parent
3298914bc4
commit
68420ca6be
@ -136,6 +136,8 @@ export type EndpointStyle = {
|
||||
|
||||
export type EndpointMeta = {
|
||||
__meta?: {
|
||||
nodeName: string;
|
||||
nodeId: string;
|
||||
index: number;
|
||||
totalEndpoints: number;
|
||||
endpointLabelLength: number;
|
||||
@ -247,7 +249,7 @@ export interface IWorkflowData {
|
||||
export interface IWorkflowDataUpdate {
|
||||
id?: string;
|
||||
name?: string;
|
||||
nodes?: Array<INode | IWorkflowTemplateNode>;
|
||||
nodes?: INode[];
|
||||
connections?: IConnections;
|
||||
settings?: IWorkflowSettings;
|
||||
active?: boolean;
|
||||
@ -268,7 +270,10 @@ export interface NewWorkflowResponse {
|
||||
}
|
||||
|
||||
export interface IWorkflowTemplateNode
|
||||
extends Pick<INodeUi, 'name' | 'type' | 'position' | 'parameters' | 'typeVersion' | 'webhookId'> {
|
||||
extends Pick<
|
||||
INodeUi,
|
||||
'name' | 'type' | 'position' | 'parameters' | 'typeVersion' | 'webhookId' | 'id' | 'disabled'
|
||||
> {
|
||||
// The credentials in a template workflow have a different type than in a regular workflow
|
||||
credentials?: IWorkflowTemplateNodeCredentials;
|
||||
}
|
||||
@ -1926,13 +1931,13 @@ export type NewConnectionInfo = {
|
||||
index: number;
|
||||
eventSource: NodeCreatorOpenSource;
|
||||
connection?: Connection;
|
||||
nodeCreatorView?: string;
|
||||
nodeCreatorView?: NodeFilterType;
|
||||
outputType?: NodeConnectionType;
|
||||
endpointUuid?: string;
|
||||
};
|
||||
|
||||
export type AIAssistantConnectionInfo = NewConnectionInfo & {
|
||||
stepName: string;
|
||||
stepName?: string;
|
||||
};
|
||||
|
||||
export type EnterpriseEditionFeatureKey =
|
||||
|
@ -5,12 +5,11 @@ import ChatComponent from '@n8n/chat/components/Chat.vue';
|
||||
import { ChatOptionsSymbol, ChatSymbol } from '@n8n/chat/constants';
|
||||
import type { Chat, ChatMessage, ChatOptions } from '@n8n/chat/types';
|
||||
import type { Ref } from 'vue';
|
||||
import { computed, provide, ref } from 'vue';
|
||||
import { computed, provide, ref, onMounted, onBeforeUnmount } from 'vue';
|
||||
import QuickReplies from './QuickReplies.vue';
|
||||
import { DateTime } from 'luxon';
|
||||
import { useAIStore } from '@/stores/ai.store';
|
||||
import { chatEventBus } from '@n8n/chat/event-buses';
|
||||
import { onMounted } from 'vue';
|
||||
import {
|
||||
AI_ASSISTANT_EXPERIMENT_URLS,
|
||||
AI_ASSISTANT_LOCAL_STORAGE_KEY,
|
||||
@ -19,7 +18,6 @@ import {
|
||||
import { useStorage } from '@/composables/useStorage';
|
||||
import { useMessage } from '@/composables/useMessage';
|
||||
import { useTelemetry } from '@/composables/useTelemetry';
|
||||
import { onBeforeUnmount } from 'vue';
|
||||
|
||||
const locale = useI18n();
|
||||
const telemetry = useTelemetry();
|
||||
@ -93,7 +91,7 @@ const thanksResponses: ChatMessage[] = [
|
||||
];
|
||||
|
||||
const initialMessageText = computed(() => {
|
||||
if (latestConnectionInfo.value) {
|
||||
if (latestConnectionInfo.value?.stepName) {
|
||||
return locale.baseText('aiAssistantChat.initialMessage.nextStep', {
|
||||
interpolate: { currentAction: latestConnectionInfo.value.stepName },
|
||||
});
|
||||
|
@ -54,7 +54,6 @@ import type {
|
||||
} from '@/Interface';
|
||||
import { useI18n } from '@/composables/useI18n';
|
||||
import { useTelemetry } from '@/composables/useTelemetry';
|
||||
import type { MessageBoxInputData } from 'element-plus';
|
||||
import type { BaseTextKey } from '../../plugins/i18n';
|
||||
|
||||
const props = defineProps<{
|
||||
@ -385,7 +384,7 @@ async function handleFileImport(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
async function onWorkflowMenuSelect(action: string): Promise<void> {
|
||||
async function onWorkflowMenuSelect(action: WORKFLOW_MENU_ACTIONS): Promise<void> {
|
||||
switch (action) {
|
||||
case WORKFLOW_MENU_ACTIONS.DUPLICATE: {
|
||||
uiStore.openModalWithData({
|
||||
@ -427,7 +426,7 @@ async function onWorkflowMenuSelect(action: string): Promise<void> {
|
||||
}
|
||||
case WORKFLOW_MENU_ACTIONS.IMPORT_FROM_URL: {
|
||||
try {
|
||||
const promptResponse = (await message.prompt(
|
||||
const promptResponse = await message.prompt(
|
||||
locale.baseText('mainSidebar.prompt.workflowUrl') + ':',
|
||||
locale.baseText('mainSidebar.prompt.importWorkflowFromUrl') + ':',
|
||||
{
|
||||
@ -436,9 +435,9 @@ async function onWorkflowMenuSelect(action: string): Promise<void> {
|
||||
inputErrorMessage: locale.baseText('mainSidebar.prompt.invalidUrl'),
|
||||
inputPattern: /^http[s]?:\/\/.*\.json$/i,
|
||||
},
|
||||
)) as MessageBoxInputData;
|
||||
);
|
||||
|
||||
if ((promptResponse as unknown as string) === 'cancel') {
|
||||
if (promptResponse.action === 'cancel') {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -204,8 +204,8 @@ export default function useCanvasMouseSelect() {
|
||||
uiStore.lastSelectedNode = null;
|
||||
uiStore.lastSelectedNodeOutputIndex = null;
|
||||
|
||||
canvasStore.lastSelectedConnection = null;
|
||||
canvasStore.newNodeInsertPosition = null;
|
||||
canvasStore.setLastSelectedConnection(undefined);
|
||||
}
|
||||
|
||||
const instance = computed(() => canvasStore.jsPlumbInstance);
|
||||
|
@ -70,7 +70,7 @@ export function useCanvasPanning(
|
||||
/**
|
||||
* Ends the panning process and removes the mousemove event listener
|
||||
*/
|
||||
function onMouseUp(_: MouseEvent) {
|
||||
function onMouseUp() {
|
||||
if (!uiStore.nodeViewMoveInProgress) {
|
||||
// If it is not active return directly.
|
||||
// Else normal node dragging will not work.
|
||||
@ -89,7 +89,7 @@ export function useCanvasPanning(
|
||||
* Handles the actual movement of the canvas during a mouse drag,
|
||||
* updating the position based on the current mouse position
|
||||
*/
|
||||
function onMouseMove(e: MouseEvent) {
|
||||
function onMouseMove(e: MouseEvent | TouchEvent) {
|
||||
const element = unref(elementRef);
|
||||
if (e.target && !(element === e.target || element?.contains(e.target as Node))) {
|
||||
return;
|
||||
@ -100,11 +100,11 @@ export function useCanvasPanning(
|
||||
}
|
||||
|
||||
// Signal that moving canvas is active if middle button is pressed and mouse is moved
|
||||
if (e.buttons === MOUSE_EVENT_BUTTONS.MIDDLE) {
|
||||
if (e instanceof MouseEvent && e.buttons === MOUSE_EVENT_BUTTONS.MIDDLE) {
|
||||
uiStore.nodeViewMoveInProgress = true;
|
||||
}
|
||||
|
||||
if (e.buttons === MOUSE_EVENT_BUTTONS.NONE) {
|
||||
if (e instanceof MouseEvent && e.buttons === MOUSE_EVENT_BUTTONS.NONE) {
|
||||
// Mouse button is not pressed anymore so stop selection mode
|
||||
// Happens normally when mouse leave the view pressed and then
|
||||
// comes back unpressed.
|
||||
|
@ -1,12 +1,19 @@
|
||||
import type { ElMessageBoxOptions } from 'element-plus';
|
||||
import type { ElMessageBoxOptions, Action, MessageBoxInputData } from 'element-plus';
|
||||
import { ElMessageBox as MessageBox } from 'element-plus';
|
||||
|
||||
export type MessageBoxConfirmResult = 'confirm' | 'cancel';
|
||||
|
||||
export function useMessage() {
|
||||
const handleCancelOrClose = (e: unknown) => {
|
||||
const handleCancelOrClose = (e: Action | Error): Action => {
|
||||
if (e instanceof Error) throw e;
|
||||
else return e;
|
||||
|
||||
return e;
|
||||
};
|
||||
|
||||
const handleCancelOrClosePrompt = (e: Error | Action): MessageBoxInputData => {
|
||||
if (e instanceof Error) throw e;
|
||||
|
||||
return { value: '', action: e };
|
||||
};
|
||||
|
||||
async function alert(
|
||||
@ -15,7 +22,7 @@ export function useMessage() {
|
||||
config?: ElMessageBoxOptions,
|
||||
) {
|
||||
const resolvedConfig = {
|
||||
...(config || (typeof configOrTitle === 'object' ? configOrTitle : {})),
|
||||
...(config ?? (typeof configOrTitle === 'object' ? configOrTitle : {})),
|
||||
cancelButtonClass: 'btn--cancel',
|
||||
confirmButtonClass: 'btn--confirm',
|
||||
};
|
||||
@ -32,24 +39,23 @@ export function useMessage() {
|
||||
message: ElMessageBoxOptions['message'],
|
||||
configOrTitle?: string | ElMessageBoxOptions,
|
||||
config?: ElMessageBoxOptions,
|
||||
): Promise<MessageBoxConfirmResult> {
|
||||
) {
|
||||
const resolvedConfig = {
|
||||
...(config || (typeof configOrTitle === 'object' ? configOrTitle : {})),
|
||||
...(config ?? (typeof configOrTitle === 'object' ? configOrTitle : {})),
|
||||
cancelButtonClass: 'btn--cancel',
|
||||
confirmButtonClass: 'btn--confirm',
|
||||
distinguishCancelAndClose: true,
|
||||
showClose: config?.showClose || false,
|
||||
showClose: config?.showClose ?? false,
|
||||
closeOnClickModal: false,
|
||||
};
|
||||
|
||||
if (typeof configOrTitle === 'string') {
|
||||
return await (MessageBox.confirm(message, configOrTitle, resolvedConfig).catch(
|
||||
return await MessageBox.confirm(message, configOrTitle, resolvedConfig).catch(
|
||||
handleCancelOrClose,
|
||||
) as unknown as Promise<MessageBoxConfirmResult>);
|
||||
);
|
||||
}
|
||||
return await (MessageBox.confirm(message, resolvedConfig).catch(
|
||||
handleCancelOrClose,
|
||||
) as unknown as Promise<MessageBoxConfirmResult>);
|
||||
|
||||
return await MessageBox.confirm(message, resolvedConfig).catch(handleCancelOrClose);
|
||||
}
|
||||
|
||||
async function prompt(
|
||||
@ -58,17 +64,17 @@ export function useMessage() {
|
||||
config?: ElMessageBoxOptions,
|
||||
) {
|
||||
const resolvedConfig = {
|
||||
...(config || (typeof configOrTitle === 'object' ? configOrTitle : {})),
|
||||
...(config ?? (typeof configOrTitle === 'object' ? configOrTitle : {})),
|
||||
cancelButtonClass: 'btn--cancel',
|
||||
confirmButtonClass: 'btn--confirm',
|
||||
};
|
||||
|
||||
if (typeof configOrTitle === 'string') {
|
||||
return await MessageBox.prompt(message, configOrTitle, resolvedConfig).catch(
|
||||
handleCancelOrClose,
|
||||
handleCancelOrClosePrompt,
|
||||
);
|
||||
}
|
||||
return await MessageBox.prompt(message, resolvedConfig).catch(handleCancelOrClose);
|
||||
return await MessageBox.prompt(message, resolvedConfig).catch(handleCancelOrClosePrompt);
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -39,6 +39,7 @@ import type {
|
||||
IWorkflowData,
|
||||
IWorkflowDataUpdate,
|
||||
IWorkflowDb,
|
||||
IWorkflowTemplateNode,
|
||||
TargetItem,
|
||||
XYPosition,
|
||||
} from '@/Interface';
|
||||
@ -307,7 +308,11 @@ function getNodes(): INodeUi[] {
|
||||
}
|
||||
|
||||
// Returns a workflow instance.
|
||||
function getWorkflow(nodes: INodeUi[], connections: IConnections, copyData?: boolean): Workflow {
|
||||
function getWorkflow(
|
||||
nodes: Array<INodeUi | IWorkflowTemplateNode>,
|
||||
connections: IConnections,
|
||||
copyData?: boolean,
|
||||
): Workflow {
|
||||
return useWorkflowsStore().getWorkflow(nodes, connections, copyData);
|
||||
}
|
||||
|
||||
|
@ -398,7 +398,6 @@ export const ROLE_OTHER = 'other';
|
||||
/** END OF PERSONALIZATION SURVEY */
|
||||
|
||||
export const MODAL_CANCEL = 'cancel';
|
||||
export const MODAL_CLOSE = 'close';
|
||||
export const MODAL_CONFIRM = 'confirm';
|
||||
|
||||
export const VALID_EMAIL_REGEX =
|
||||
|
34
packages/editor-ui/src/jsplumb.d.ts
vendored
Normal file
34
packages/editor-ui/src/jsplumb.d.ts
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
import type { Connection, Endpoint, EndpointRepresentation, AbstractConnector, Overlay } from '@jsplumb/core';
|
||||
import type { NodeConnectionType } from 'n8n-workflow';
|
||||
|
||||
declare module '@jsplumb/core' {
|
||||
interface EndpointRepresentation {
|
||||
canvas: HTMLElement;
|
||||
scope: NodeConnectionType;
|
||||
}
|
||||
interface AbstractConnector {
|
||||
canvas: HTMLElement;
|
||||
overrideTargetEndpoint: Endpoint;
|
||||
}
|
||||
interface Overlay {
|
||||
canvas: HTMLElement;
|
||||
}
|
||||
interface Connection {
|
||||
__meta: {
|
||||
sourceOutputIndex: number;
|
||||
targetNodeName: string;
|
||||
targetOutputIndex: number;
|
||||
sourceNodeName: string;
|
||||
};
|
||||
}
|
||||
interface Endpoint {
|
||||
scope: NodeConnectionType;
|
||||
__meta: {
|
||||
nodeName: string;
|
||||
nodeId: string;
|
||||
index: number;
|
||||
totalEndpoints: number;
|
||||
endpointLabelLength: number;
|
||||
};
|
||||
};
|
||||
}
|
@ -165,7 +165,7 @@ export const routes = [
|
||||
// Templates view remembers it's scroll position on back
|
||||
scrollOffset: 0,
|
||||
telemetry: {
|
||||
getProperties(route: RouteLocation) {
|
||||
getProperties() {
|
||||
const templatesStore = useTemplatesStore();
|
||||
return {
|
||||
wf_template_repo_session_id: templatesStore.currentSessionId,
|
||||
@ -474,7 +474,7 @@ export const routes = [
|
||||
},
|
||||
telemetry: {
|
||||
pageCategory: 'settings',
|
||||
getProperties(route: RouteLocation) {
|
||||
getProperties() {
|
||||
return {
|
||||
feature: 'usage',
|
||||
};
|
||||
@ -492,7 +492,7 @@ export const routes = [
|
||||
middleware: ['authenticated'],
|
||||
telemetry: {
|
||||
pageCategory: 'settings',
|
||||
getProperties(route: RouteLocation) {
|
||||
getProperties() {
|
||||
return {
|
||||
feature: 'personal',
|
||||
};
|
||||
@ -515,7 +515,7 @@ export const routes = [
|
||||
},
|
||||
telemetry: {
|
||||
pageCategory: 'settings',
|
||||
getProperties(route: RouteLocation) {
|
||||
getProperties() {
|
||||
return {
|
||||
feature: 'users',
|
||||
};
|
||||
@ -533,7 +533,7 @@ export const routes = [
|
||||
middleware: ['authenticated'],
|
||||
telemetry: {
|
||||
pageCategory: 'settings',
|
||||
getProperties(route: RouteLocation) {
|
||||
getProperties() {
|
||||
return {
|
||||
feature: 'api',
|
||||
};
|
||||
@ -556,7 +556,7 @@ export const routes = [
|
||||
},
|
||||
telemetry: {
|
||||
pageCategory: 'settings',
|
||||
getProperties(route: RouteLocation) {
|
||||
getProperties() {
|
||||
return {
|
||||
feature: 'environments',
|
||||
};
|
||||
@ -579,7 +579,7 @@ export const routes = [
|
||||
},
|
||||
telemetry: {
|
||||
pageCategory: 'settings',
|
||||
getProperties(route: RouteLocation) {
|
||||
getProperties() {
|
||||
return {
|
||||
feature: 'external-secrets',
|
||||
};
|
||||
@ -606,7 +606,7 @@ export const routes = [
|
||||
},
|
||||
telemetry: {
|
||||
pageCategory: 'settings',
|
||||
getProperties(route: RouteLocation) {
|
||||
getProperties() {
|
||||
return {
|
||||
feature: 'sso',
|
||||
};
|
||||
|
1
packages/editor-ui/src/shims.d.ts
vendored
1
packages/editor-ui/src/shims.d.ts
vendored
@ -22,6 +22,7 @@ declare global {
|
||||
BASE_PATH: string;
|
||||
REST_ENDPOINT: string;
|
||||
n8nExternalHooks?: PartialDeep<ExternalHooks>;
|
||||
preventNodeViewBeforeUnload?: boolean;
|
||||
}
|
||||
|
||||
namespace JSX {
|
||||
|
@ -52,7 +52,8 @@ export const useCanvasStore = defineStore('canvas', () => {
|
||||
|
||||
const jsPlumbInstanceRef = ref<BrowserJsPlumbInstance>();
|
||||
const isDragging = ref<boolean>(false);
|
||||
const lastSelectedConnection = ref<Connection | null>(null);
|
||||
const lastSelectedConnection = ref<Connection>();
|
||||
|
||||
const newNodeInsertPosition = ref<XYPosition | null>(null);
|
||||
|
||||
const nodes = computed<INodeUi[]>(() => workflowStore.allNodes);
|
||||
@ -68,6 +69,9 @@ export const useCanvasStore = defineStore('canvas', () => {
|
||||
const nodeViewScale = ref<number>(1);
|
||||
const canvasAddButtonPosition = ref<XYPosition>([1, 1]);
|
||||
const readOnlyEnv = computed(() => sourceControlStore.preferences.branchReadOnly);
|
||||
const lastSelectedConnectionComputed = computed<Connection | undefined>(
|
||||
() => lastSelectedConnection.value,
|
||||
);
|
||||
|
||||
watch(readOnlyEnv, (readOnly) => {
|
||||
if (jsPlumbInstanceRef.value) {
|
||||
@ -75,6 +79,10 @@ export const useCanvasStore = defineStore('canvas', () => {
|
||||
}
|
||||
});
|
||||
|
||||
const setLastSelectedConnection = (connection: Connection | undefined) => {
|
||||
lastSelectedConnection.value = connection;
|
||||
};
|
||||
|
||||
const setRecenteredCanvasAddButtonPosition = (offset?: XYPosition) => {
|
||||
const position = getMidCanvasPosition(nodeViewScale.value, offset ?? [0, 0]);
|
||||
|
||||
@ -314,11 +322,12 @@ export const useCanvasStore = defineStore('canvas', () => {
|
||||
isDemo,
|
||||
nodeViewScale,
|
||||
canvasAddButtonPosition,
|
||||
lastSelectedConnection,
|
||||
newNodeInsertPosition,
|
||||
jsPlumbInstance,
|
||||
isLoading: loadingService.isLoading,
|
||||
aiNodes,
|
||||
lastSelectedConnection: lastSelectedConnectionComputed,
|
||||
setLastSelectedConnection,
|
||||
startLoading: loadingService.startLoading,
|
||||
setLoadingText: loadingService.setLoadingText,
|
||||
stopLoading: loadingService.stopLoading,
|
||||
|
@ -30,6 +30,7 @@ import type {
|
||||
NodeMetadataMap,
|
||||
WorkflowMetadata,
|
||||
IExecutionFlattedResponse,
|
||||
IWorkflowTemplateNode,
|
||||
} from '@/Interface';
|
||||
import { defineStore } from 'pinia';
|
||||
import type {
|
||||
@ -312,9 +313,31 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
|
||||
setNodeValue({ name: node.name, key: 'position', value: position });
|
||||
}
|
||||
|
||||
function convertTemplateNodeToNodeUi(node: IWorkflowTemplateNode): INodeUi {
|
||||
const filteredCredentials = Object.keys(node.credentials ?? {}).reduce<INodeCredentials>(
|
||||
(credentials, curr) => {
|
||||
const credential = node?.credentials?.[curr];
|
||||
if (!credential || typeof credential === 'string') {
|
||||
return credentials;
|
||||
}
|
||||
|
||||
credentials[curr] = credential;
|
||||
|
||||
return credentials;
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
return {
|
||||
...node,
|
||||
credentials: filteredCredentials,
|
||||
};
|
||||
}
|
||||
|
||||
function getWorkflow(nodes: INodeUi[], connections: IConnections, copyData?: boolean): Workflow {
|
||||
const nodeTypes = getNodeTypes();
|
||||
let cachedWorkflowId: string | undefined = workflowId.value;
|
||||
|
||||
if (cachedWorkflowId && cachedWorkflowId === PLACEHOLDER_EMPTY_WORKFLOW_ID) {
|
||||
cachedWorkflowId = undefined;
|
||||
}
|
||||
@ -327,7 +350,6 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
|
||||
active: false,
|
||||
nodeTypes,
|
||||
settings: workflowSettings.value,
|
||||
// @ts-ignore
|
||||
pinData: pinnedWorkflowData.value,
|
||||
});
|
||||
|
||||
@ -1520,6 +1542,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
|
||||
getPinDataSize,
|
||||
getNodeTypes,
|
||||
getNodes,
|
||||
convertTemplateNodeToNodeUi,
|
||||
getWorkflow,
|
||||
getCurrentWorkflow,
|
||||
getWorkflowFromUrl,
|
||||
|
@ -25,6 +25,7 @@ import type {
|
||||
INodeUpdatePropertiesInformation,
|
||||
IPersonalizationLatestVersion,
|
||||
IWorkflowDb,
|
||||
IWorkflowTemplateNode,
|
||||
NodeFilterType,
|
||||
} from '@/Interface';
|
||||
import type { ComponentPublicInstance } from 'vue/dist/vue';
|
||||
@ -68,6 +69,7 @@ export interface ExternalHooks {
|
||||
addNodeButton: Array<ExternalHooksMethod<{ nodeTypeName: string }>>;
|
||||
onRunNode: Array<ExternalHooksMethod<ITelemetryTrackProperties>>;
|
||||
onRunWorkflow: Array<ExternalHooksMethod<ITelemetryTrackProperties>>;
|
||||
onOpenChat: Array<ExternalHooksMethod<ITelemetryTrackProperties>>;
|
||||
};
|
||||
main: {
|
||||
routeChange: Array<ExternalHooksMethod<{ to: RouteLocation; from: RouteLocation }>>;
|
||||
@ -255,7 +257,7 @@ export interface ExternalHooks {
|
||||
ExternalHooksMethod<{
|
||||
templateId: string;
|
||||
templateName: string;
|
||||
workflow: { nodes: INodeUi[]; connections: IConnections };
|
||||
workflow: { nodes: INodeUi[] | IWorkflowTemplateNode[]; connections: IConnections };
|
||||
}>
|
||||
>;
|
||||
};
|
||||
|
@ -3,7 +3,7 @@ import {
|
||||
mapLegacyEndpointsToCanvasConnectionPort,
|
||||
getUniqueNodeName,
|
||||
} from '@/utils/canvasUtilsV2';
|
||||
import type { IConnections, INodeTypeDescription } from 'n8n-workflow';
|
||||
import { NodeConnectionType, type IConnections, type INodeTypeDescription } from 'n8n-workflow';
|
||||
import type { CanvasConnection } from '@/types';
|
||||
import type { INodeUi } from '@/Interface';
|
||||
|
||||
@ -15,7 +15,7 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
it('should map legacy connections to canvas connections', () => {
|
||||
const legacyConnections: IConnections = {
|
||||
'Node A': {
|
||||
main: [[{ node: 'Node B', type: 'main', index: 0 }]],
|
||||
main: [[{ node: 'Node B', type: NodeConnectionType.Main, index: 0 }]],
|
||||
},
|
||||
};
|
||||
const nodes: INodeUi[] = [
|
||||
@ -53,11 +53,11 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
fromNodeName: 'Node A',
|
||||
source: {
|
||||
index: 0,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
target: {
|
||||
index: 0,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -67,7 +67,7 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
it('should return empty array when no matching nodes found', () => {
|
||||
const legacyConnections: IConnections = {
|
||||
'Node A': {
|
||||
main: [[{ node: 'Node B', type: 'main', index: 0 }]],
|
||||
main: [[{ node: 'Node B', type: NodeConnectionType.Main, index: 0 }]],
|
||||
},
|
||||
};
|
||||
const nodes: INodeUi[] = [];
|
||||
@ -113,8 +113,8 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
const legacyConnections: IConnections = {
|
||||
'Node A': {
|
||||
main: [
|
||||
[{ node: 'Node B', type: 'main', index: 0 }],
|
||||
[{ node: 'Node B', type: 'main', index: 1 }],
|
||||
[{ node: 'Node B', type: NodeConnectionType.Main, index: 0 }],
|
||||
[{ node: 'Node B', type: NodeConnectionType.Main, index: 1 }],
|
||||
],
|
||||
},
|
||||
};
|
||||
@ -153,11 +153,11 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
fromNodeName: 'Node A',
|
||||
source: {
|
||||
index: 0,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
target: {
|
||||
index: 0,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -171,11 +171,11 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
fromNodeName: 'Node A',
|
||||
source: {
|
||||
index: 1,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
target: {
|
||||
index: 1,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -186,8 +186,8 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
const legacyConnections: IConnections = {
|
||||
'Node A': {
|
||||
main: [
|
||||
[{ node: 'Node B', type: 'main', index: 0 }],
|
||||
[{ node: 'Node C', type: 'main', index: 0 }],
|
||||
[{ node: 'Node B', type: NodeConnectionType.Main, index: 0 }],
|
||||
[{ node: 'Node C', type: NodeConnectionType.Main, index: 0 }],
|
||||
],
|
||||
},
|
||||
};
|
||||
@ -234,11 +234,11 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
fromNodeName: 'Node A',
|
||||
source: {
|
||||
index: 0,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
target: {
|
||||
index: 0,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -252,11 +252,11 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
fromNodeName: 'Node A',
|
||||
source: {
|
||||
index: 1,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
target: {
|
||||
index: 0,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -266,11 +266,13 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
it('should map complex node setup with mixed inputs and outputs', () => {
|
||||
const legacyConnections: IConnections = {
|
||||
'Node A': {
|
||||
main: [[{ node: 'Node B', type: 'main', index: 0 }]],
|
||||
other: [[{ node: 'Node C', type: 'other', index: 1 }]],
|
||||
main: [[{ node: 'Node B', type: NodeConnectionType.Main, index: 0 }]],
|
||||
[NodeConnectionType.AiMemory]: [
|
||||
[{ node: 'Node C', type: NodeConnectionType.AiMemory, index: 1 }],
|
||||
],
|
||||
},
|
||||
'Node B': {
|
||||
main: [[{ node: 'Node C', type: 'main', index: 0 }]],
|
||||
main: [[{ node: 'Node C', type: NodeConnectionType.Main, index: 0 }]],
|
||||
},
|
||||
};
|
||||
const nodes: INodeUi[] = [
|
||||
@ -316,29 +318,29 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
fromNodeName: 'Node A',
|
||||
source: {
|
||||
index: 0,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
target: {
|
||||
index: 0,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '[1/other/0][3/other/1]',
|
||||
id: `[1/${NodeConnectionType.AiMemory}/0][3/${NodeConnectionType.AiMemory}/1]`,
|
||||
source: '1',
|
||||
target: '3',
|
||||
sourceHandle: 'outputs/other/0',
|
||||
targetHandle: 'inputs/other/1',
|
||||
sourceHandle: `outputs/${NodeConnectionType.AiMemory}/0`,
|
||||
targetHandle: `inputs/${NodeConnectionType.AiMemory}/1`,
|
||||
data: {
|
||||
fromNodeName: 'Node A',
|
||||
source: {
|
||||
index: 0,
|
||||
type: 'other',
|
||||
type: NodeConnectionType.AiMemory,
|
||||
},
|
||||
target: {
|
||||
index: 1,
|
||||
type: 'other',
|
||||
type: NodeConnectionType.AiMemory,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -352,11 +354,11 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
fromNodeName: 'Node B',
|
||||
source: {
|
||||
index: 0,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
target: {
|
||||
index: 0,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -367,8 +369,8 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
const legacyConnections: IConnections = {
|
||||
'Node A': {
|
||||
main: [
|
||||
[{ node: 'Nonexistent Node', type: 'main', index: 0 }],
|
||||
[{ node: 'Node B', type: 'main', index: 0 }],
|
||||
[{ node: 'Nonexistent Node', type: NodeConnectionType.Main, index: 0 }],
|
||||
[{ node: 'Node B', type: NodeConnectionType.Main, index: 0 }],
|
||||
],
|
||||
},
|
||||
};
|
||||
@ -407,11 +409,11 @@ describe('mapLegacyConnectionsToCanvasConnections', () => {
|
||||
fromNodeName: 'Node A',
|
||||
source: {
|
||||
index: 1,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
target: {
|
||||
index: 0,
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -435,66 +437,69 @@ describe('mapLegacyEndpointsToCanvasConnectionPort', () => {
|
||||
});
|
||||
|
||||
it('should map string endpoints correctly', () => {
|
||||
const endpoints: INodeTypeDescription['inputs'] = ['main', 'ai_tool'];
|
||||
const endpoints: INodeTypeDescription['inputs'] = [
|
||||
NodeConnectionType.Main,
|
||||
NodeConnectionType.AiTool,
|
||||
];
|
||||
const result = mapLegacyEndpointsToCanvasConnectionPort(endpoints);
|
||||
|
||||
expect(result).toEqual([
|
||||
{ type: 'main', index: 0, label: undefined },
|
||||
{ type: 'ai_tool', index: 0, label: undefined },
|
||||
{ type: NodeConnectionType.Main, index: 0, label: undefined },
|
||||
{ type: NodeConnectionType.AiTool, index: 0, label: undefined },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should map object endpoints correctly', () => {
|
||||
const endpoints: INodeTypeDescription['inputs'] = [
|
||||
{ type: 'main', displayName: 'Main Input' },
|
||||
{ type: 'ai_tool', displayName: 'AI Tool', required: true },
|
||||
{ type: NodeConnectionType.Main, displayName: 'Main Input' },
|
||||
{ type: NodeConnectionType.AiTool, displayName: 'AI Tool', required: true },
|
||||
];
|
||||
const result = mapLegacyEndpointsToCanvasConnectionPort(endpoints);
|
||||
|
||||
expect(result).toEqual([
|
||||
{ type: 'main', index: 0, label: 'Main Input' },
|
||||
{ type: 'ai_tool', index: 0, label: 'AI Tool', required: true },
|
||||
{ type: NodeConnectionType.Main, index: 0, label: 'Main Input' },
|
||||
{ type: NodeConnectionType.AiTool, index: 0, label: 'AI Tool', required: true },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should map mixed string and object endpoints correctly', () => {
|
||||
const endpoints: INodeTypeDescription['inputs'] = [
|
||||
'main',
|
||||
{ type: 'ai_tool', displayName: 'AI Tool' },
|
||||
'main',
|
||||
NodeConnectionType.Main,
|
||||
{ type: NodeConnectionType.AiTool, displayName: 'AI Tool' },
|
||||
NodeConnectionType.Main,
|
||||
];
|
||||
const result = mapLegacyEndpointsToCanvasConnectionPort(endpoints);
|
||||
|
||||
expect(result).toEqual([
|
||||
{ type: 'main', index: 0, label: undefined },
|
||||
{ type: 'ai_tool', index: 0, label: 'AI Tool' },
|
||||
{ type: 'main', index: 1, label: undefined },
|
||||
{ type: NodeConnectionType.Main, index: 0, label: undefined },
|
||||
{ type: NodeConnectionType.AiTool, index: 0, label: 'AI Tool' },
|
||||
{ type: NodeConnectionType.Main, index: 1, label: undefined },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle multiple same type object endpoints', () => {
|
||||
const endpoints: INodeTypeDescription['inputs'] = [
|
||||
{ type: 'main', displayName: 'Main Input' },
|
||||
{ type: 'main', displayName: 'Secondary Main Input' },
|
||||
{ type: NodeConnectionType.Main, displayName: 'Main Input' },
|
||||
{ type: NodeConnectionType.Main, displayName: 'Secondary Main Input' },
|
||||
];
|
||||
const result = mapLegacyEndpointsToCanvasConnectionPort(endpoints);
|
||||
|
||||
expect(result).toEqual([
|
||||
{ type: 'main', index: 0, label: 'Main Input' },
|
||||
{ type: 'main', index: 1, label: 'Secondary Main Input' },
|
||||
{ type: NodeConnectionType.Main, index: 0, label: 'Main Input' },
|
||||
{ type: NodeConnectionType.Main, index: 1, label: 'Secondary Main Input' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should map required and non-required endpoints correctly', () => {
|
||||
const endpoints: INodeTypeDescription['inputs'] = [
|
||||
{ type: 'main', displayName: 'Main Input', required: true },
|
||||
{ type: 'ai_tool', displayName: 'Optional Tool', required: false },
|
||||
{ type: NodeConnectionType.Main, displayName: 'Main Input', required: true },
|
||||
{ type: NodeConnectionType.AiTool, displayName: 'Optional Tool', required: false },
|
||||
];
|
||||
const result = mapLegacyEndpointsToCanvasConnectionPort(endpoints);
|
||||
|
||||
expect(result).toEqual([
|
||||
{ type: 'main', index: 0, label: 'Main Input', required: true },
|
||||
{ type: 'ai_tool', index: 0, label: 'Optional Tool' },
|
||||
{ type: NodeConnectionType.Main, index: 0, label: 'Main Input', required: true },
|
||||
{ type: NodeConnectionType.AiTool, index: 0, label: 'Optional Tool' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
@ -1,6 +1,7 @@
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { faker } from '@faker-js/faker/locale/en';
|
||||
import type { ITemplatesWorkflowFull, IWorkflowTemplateNode } from '@/Interface';
|
||||
import { NodeConnectionType } from 'n8n-workflow';
|
||||
|
||||
export const newWorkflowTemplateNode = ({
|
||||
type,
|
||||
@ -26,6 +27,7 @@ export const fullShopifyTelegramTwitterTemplate = {
|
||||
workflow: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'd65f8060-0196-430a-923c-57f838991cc1',
|
||||
name: 'Twitter',
|
||||
type: 'n8n-nodes-base.twitter',
|
||||
position: [720, -220],
|
||||
@ -39,6 +41,7 @@ export const fullShopifyTelegramTwitterTemplate = {
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
id: 'd65f8060-0196-430a-923c-57f838991dd3',
|
||||
name: 'Telegram',
|
||||
type: 'n8n-nodes-base.telegram',
|
||||
position: [720, -20],
|
||||
@ -53,6 +56,7 @@ export const fullShopifyTelegramTwitterTemplate = {
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
id: 'd65f8060-0196-430a-923c-57f838991dd2',
|
||||
name: 'product created',
|
||||
type: 'n8n-nodes-base.shopifyTrigger',
|
||||
position: [540, -110],
|
||||
@ -72,12 +76,12 @@ export const fullShopifyTelegramTwitterTemplate = {
|
||||
[
|
||||
{
|
||||
node: 'Twitter',
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
index: 0,
|
||||
},
|
||||
{
|
||||
node: 'Telegram',
|
||||
type: 'main',
|
||||
type: NodeConnectionType.Main,
|
||||
index: 0,
|
||||
},
|
||||
],
|
||||
@ -195,6 +199,7 @@ export const fullSaveEmailAttachmentsToNextCloudTemplate = {
|
||||
workflow: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'd65f8060-0196-430a-923c-57f8389911f3',
|
||||
name: 'IMAP Email',
|
||||
type: 'n8n-nodes-base.emailReadImap',
|
||||
position: [240, 420],
|
||||
@ -206,6 +211,7 @@ export const fullSaveEmailAttachmentsToNextCloudTemplate = {
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
id: 'd65f8060-0196-430a-923c-57f838991gg2',
|
||||
name: 'Nextcloud',
|
||||
type: 'n8n-nodes-base.nextCloud',
|
||||
position: [940, 420],
|
||||
@ -217,6 +223,7 @@ export const fullSaveEmailAttachmentsToNextCloudTemplate = {
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
id: 'd65f8060-0196-430a-923c-57f838991ddh',
|
||||
name: 'Map each attachment',
|
||||
type: 'n8n-nodes-base.function',
|
||||
position: [620, 420],
|
||||
@ -228,8 +235,12 @@ export const fullSaveEmailAttachmentsToNextCloudTemplate = {
|
||||
},
|
||||
],
|
||||
connections: {
|
||||
'IMAP Email': { main: [[{ node: 'Map each attachment', type: 'main', index: 0 }]] },
|
||||
'Map each attachment': { main: [[{ node: 'Nextcloud', type: 'main', index: 0 }]] },
|
||||
'IMAP Email': {
|
||||
main: [[{ node: 'Map each attachment', type: NodeConnectionType.Main, index: 0 }]],
|
||||
},
|
||||
'Map each attachment': {
|
||||
main: [[{ node: 'Nextcloud', type: NodeConnectionType.Main, index: 0 }]],
|
||||
},
|
||||
},
|
||||
},
|
||||
workflowInfo: {
|
||||
@ -303,6 +314,7 @@ export const fullCreateApiEndpointTemplate = {
|
||||
workflow: {
|
||||
nodes: [
|
||||
{
|
||||
id: 'd65f8060-0196-430a-923c-57f838991dd1',
|
||||
name: 'Webhook',
|
||||
type: 'n8n-nodes-base.webhook',
|
||||
position: [375, 115],
|
||||
@ -315,6 +327,7 @@ export const fullCreateApiEndpointTemplate = {
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
id: 'd65f8060-0196-430a-923c-57f838991dd9',
|
||||
name: 'Note1',
|
||||
type: 'n8n-nodes-base.stickyNote',
|
||||
position: [355, -25],
|
||||
@ -327,6 +340,7 @@ export const fullCreateApiEndpointTemplate = {
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
id: 'd65f8060-0196-430a-923c-57f838991dd5',
|
||||
name: 'Respond to Webhook',
|
||||
type: 'n8n-nodes-base.respondToWebhook',
|
||||
position: [815, 115],
|
||||
@ -339,6 +353,7 @@ export const fullCreateApiEndpointTemplate = {
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
id: 'd65f8060-0196-430a-923c-57f838991df1',
|
||||
name: 'Create URL string',
|
||||
type: 'n8n-nodes-base.set',
|
||||
position: [595, 115],
|
||||
@ -358,6 +373,7 @@ export const fullCreateApiEndpointTemplate = {
|
||||
typeVersion: 1,
|
||||
},
|
||||
{
|
||||
id: 'd65f8060-0196-430a-923c-57f838991dbb',
|
||||
name: 'Note3',
|
||||
type: 'n8n-nodes-base.stickyNote',
|
||||
position: [355, 275],
|
||||
@ -371,8 +387,10 @@ export const fullCreateApiEndpointTemplate = {
|
||||
},
|
||||
],
|
||||
connections: {
|
||||
Webhook: { main: [[{ node: 'Create URL string', type: 'main', index: 0 }]] },
|
||||
'Create URL string': { main: [[{ node: 'Respond to Webhook', type: 'main', index: 0 }]] },
|
||||
Webhook: { main: [[{ node: 'Create URL string', type: NodeConnectionType.Main, index: 0 }]] },
|
||||
'Create URL string': {
|
||||
main: [[{ node: 'Respond to Webhook', type: NodeConnectionType.Main, index: 0 }]],
|
||||
},
|
||||
},
|
||||
},
|
||||
lastUpdatedBy: 1,
|
||||
|
@ -6,6 +6,8 @@ import type {
|
||||
} from 'n8n-workflow';
|
||||
import { nodeConnectionTypes } from 'n8n-workflow';
|
||||
import type { ICredentialsResponse, NewCredentialsModal } from '@/Interface';
|
||||
import type { jsPlumbDOMElement } from '@jsplumb/browser-ui';
|
||||
import type { Connection } from '@jsplumb/core';
|
||||
|
||||
/*
|
||||
Type guards used in editor-ui project
|
||||
@ -46,10 +48,14 @@ export const isResourceMapperValue = (value: unknown): value is string | number
|
||||
return ['string', 'number', 'boolean'].includes(typeof value);
|
||||
};
|
||||
|
||||
export const isJSPlumbEndpointElement = (element: Node): element is HTMLElement => {
|
||||
export const isJSPlumbEndpointElement = (element: Node): element is jsPlumbDOMElement => {
|
||||
return 'jtk' in element && 'endpoint' in (element.jtk as object);
|
||||
};
|
||||
|
||||
export const isJSPlumbConnection = (connection: unknown): connection is Connection => {
|
||||
return connection !== null && typeof connection === 'object' && 'connector' in connection;
|
||||
};
|
||||
|
||||
export function isDateObject(date: unknown): date is Date {
|
||||
return (
|
||||
!!date && Object.prototype.toString.call(date) === '[object Date]' && !isNaN(date as number)
|
||||
|
@ -42,6 +42,7 @@ import { useExternalSecretsStore } from '@/stores/externalSecrets.ee.store';
|
||||
import { useRootStore } from '@/stores/n8nRoot.store';
|
||||
import { useCollaborationStore } from '@/stores/collaboration.store';
|
||||
import { getUniqueNodeName } from '@/utils/canvasUtilsV2';
|
||||
import { isValidNodeConnectionType } from '@/utils/typeGuards';
|
||||
|
||||
const NodeCreation = defineAsyncComponent(
|
||||
async () => await import('@/components/Node/NodeCreation.vue'),
|
||||
@ -217,13 +218,17 @@ function onCreateNodeConnection(connection: Connection) {
|
||||
const sourceNodeId = connection.source;
|
||||
const sourceNode = workflowsStore.getNodeById(sourceNodeId);
|
||||
const sourceNodeName = sourceNode?.name ?? '';
|
||||
const [, sourceType, sourceIndex] = (connection.sourceHandle ?? '').split('/');
|
||||
const [, sourceType, sourceIndex] = (connection.sourceHandle ?? '')
|
||||
.split('/')
|
||||
.filter(isValidNodeConnectionType);
|
||||
|
||||
// Input
|
||||
const targetNodeId = connection.target;
|
||||
const targetNode = workflowsStore.getNodeById(targetNodeId);
|
||||
const targetNodeName = targetNode?.name ?? '';
|
||||
const [, targetType, targetIndex] = (connection.targetHandle ?? '').split('/');
|
||||
const [, targetType, targetIndex] = (connection.targetHandle ?? '')
|
||||
.split('/')
|
||||
.filter(isValidNodeConnectionType);
|
||||
|
||||
if (sourceNode && targetNode && !checkIfNodeConnectionIsAllowed(sourceNode, targetNode)) {
|
||||
return;
|
||||
@ -248,7 +253,7 @@ function onCreateNodeConnection(connection: Connection) {
|
||||
}
|
||||
|
||||
// @TODO Figure out a way to improve this
|
||||
function checkIfNodeConnectionIsAllowed(sourceNode: INodeUi, targetNode: INodeUi): boolean {
|
||||
function checkIfNodeConnectionIsAllowed(_sourceNode: INodeUi, _targetNode: INodeUi): boolean {
|
||||
// const targetNodeType = nodeTypesStore.getNodeType(
|
||||
// targetNode.type,
|
||||
// targetNode.typeVersion,
|
||||
@ -341,7 +346,7 @@ async function onAddNodes(
|
||||
) {
|
||||
let currentPosition = position;
|
||||
for (const { type, name, position: nodePosition, isAutoAdd, openDetail } of nodes) {
|
||||
const node = await addNode(
|
||||
const _node = await addNode(
|
||||
{
|
||||
name,
|
||||
type,
|
||||
@ -407,7 +412,7 @@ type AddNodeOptions = {
|
||||
isAutoAdd?: boolean;
|
||||
};
|
||||
|
||||
async function addNode(node: AddNodeData, options: AddNodeOptions): Promise<INodeUi | undefined> {
|
||||
async function addNode(node: AddNodeData, _options: AddNodeOptions): Promise<INodeUi | undefined> {
|
||||
if (!checkIfEditingIsAllowed()) {
|
||||
return;
|
||||
}
|
||||
@ -663,11 +668,11 @@ async function createNodeWithDefaultCredentials(node: Partial<INodeUi>) {
|
||||
* @TODO Probably not needed and can be merged into addNode
|
||||
*/
|
||||
async function injectNode(
|
||||
nodeTypeName: string,
|
||||
options: AddNodeOptions = {},
|
||||
showDetail = true,
|
||||
trackHistory = false,
|
||||
isAutoAdd = false,
|
||||
_nodeTypeName: string,
|
||||
_options: AddNodeOptions = {},
|
||||
_showDetail = true,
|
||||
_trackHistory = false,
|
||||
_isAutoAdd = false,
|
||||
) {
|
||||
// const nodeTypeData: INodeTypeDescription | null =
|
||||
// this.nodeTypesStore.getNodeType(nodeTypeName);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -25,7 +25,7 @@ const SettingsView = defineComponent({
|
||||
components: {
|
||||
SettingsSidebar,
|
||||
},
|
||||
beforeRouteEnter(to, from, next) {
|
||||
beforeRouteEnter(_to, from, next) {
|
||||
next((vm) => {
|
||||
(vm as unknown as InstanceType<typeof SettingsView>).previousRoute = from;
|
||||
});
|
||||
|
@ -29,7 +29,7 @@ const openWorkflowTemplate = async (templateId: string) => {
|
||||
const workflow = await workflowsStore.createNewWorkflow({
|
||||
name,
|
||||
connections: template.workflow.connections,
|
||||
nodes: template.workflow.nodes,
|
||||
nodes: template.workflow.nodes.map(workflowsStore.convertTemplateNodeToNodeUi),
|
||||
pinData: template.workflow.pinData,
|
||||
settings: template.workflow.settings,
|
||||
meta: {
|
||||
|
@ -68,7 +68,7 @@ export interface IConnection {
|
||||
node: string;
|
||||
|
||||
// The type of the input on destination node (for example "main")
|
||||
type: string;
|
||||
type: NodeConnectionType;
|
||||
|
||||
// The output/input-index of destination node (if node has multiple inputs/outputs of the same type)
|
||||
index: number;
|
||||
|
@ -168,7 +168,8 @@ export class Workflow {
|
||||
if (!connections.hasOwnProperty(sourceNode)) {
|
||||
continue;
|
||||
}
|
||||
for (const type in connections[sourceNode]) {
|
||||
|
||||
for (const type of Object.keys(connections[sourceNode]) as NodeConnectionType[]) {
|
||||
if (!connections[sourceNode].hasOwnProperty(type)) {
|
||||
continue;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user