mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
fix(editor): Fix type errors for various utils files (no-changelog) (#9480)
This commit is contained in:
@@ -130,6 +130,14 @@ export type EndpointStyle = {
|
|||||||
hoverMessage?: string;
|
hoverMessage?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type EndpointMeta = {
|
||||||
|
__meta?: {
|
||||||
|
index: number;
|
||||||
|
totalEndpoints: number;
|
||||||
|
endpointLabelLength: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export interface IUpdateInformation<
|
export interface IUpdateInformation<
|
||||||
T extends NodeParameterValueType =
|
T extends NodeParameterValueType =
|
||||||
| string
|
| string
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { uuid } from '@jsplumb/util';
|
|||||||
import { defaultMockNodeTypes } from '@/__tests__/defaults';
|
import { defaultMockNodeTypes } from '@/__tests__/defaults';
|
||||||
import type { INodeUi, ITag, IUsedCredential, IWorkflowDb, WorkflowMetadata } from '@/Interface';
|
import type { INodeUi, ITag, IUsedCredential, IWorkflowDb, WorkflowMetadata } from '@/Interface';
|
||||||
import type { ProjectSharingData } from '@/features/projects/projects.types';
|
import type { ProjectSharingData } from '@/features/projects/projects.types';
|
||||||
|
import type { RouteLocationNormalized } from 'vue-router';
|
||||||
|
|
||||||
export function createTestNodeTypes(data: INodeTypeData = {}): INodeTypes {
|
export function createTestNodeTypes(data: INodeTypeData = {}): INodeTypes {
|
||||||
const getResolvedKey = (key: string) => {
|
const getResolvedKey = (key: string) => {
|
||||||
@@ -103,3 +104,27 @@ export function createTestNode(
|
|||||||
...node,
|
...node,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createTestRouteLocation({
|
||||||
|
path = '',
|
||||||
|
params = {},
|
||||||
|
fullPath = path,
|
||||||
|
hash = '',
|
||||||
|
matched = [],
|
||||||
|
redirectedFrom = undefined,
|
||||||
|
name = path,
|
||||||
|
meta = {},
|
||||||
|
query = {},
|
||||||
|
}: Partial<RouteLocationNormalized> = {}): RouteLocationNormalized {
|
||||||
|
return {
|
||||||
|
path,
|
||||||
|
params,
|
||||||
|
fullPath,
|
||||||
|
hash,
|
||||||
|
matched,
|
||||||
|
redirectedFrom,
|
||||||
|
name,
|
||||||
|
meta,
|
||||||
|
query,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export type Resolvable = {
|
|||||||
resolved: unknown;
|
resolved: unknown;
|
||||||
state: ResolvableState;
|
state: ResolvableState;
|
||||||
error: Error | null;
|
error: Error | null;
|
||||||
|
fullError?: Error;
|
||||||
} & Range;
|
} & Range;
|
||||||
|
|
||||||
export type Resolved = Resolvable;
|
export type Resolved = Resolvable;
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
import type { RouteLocationNormalized } from 'vue-router';
|
|
||||||
import {
|
import {
|
||||||
inferProjectIdFromRoute,
|
inferProjectIdFromRoute,
|
||||||
inferResourceTypeFromRoute,
|
inferResourceTypeFromRoute,
|
||||||
inferResourceIdFromRoute,
|
inferResourceIdFromRoute,
|
||||||
} from '../rbacUtils';
|
} from '../rbacUtils';
|
||||||
|
import { createTestRouteLocation } from '@/__tests__/mocks';
|
||||||
|
|
||||||
describe('rbacUtils', () => {
|
describe('rbacUtils', () => {
|
||||||
describe('inferProjectIdFromRoute()', () => {
|
describe('inferProjectIdFromRoute()', () => {
|
||||||
it('should infer project ID from route correctly', () => {
|
it('should infer project ID from route correctly', () => {
|
||||||
const route = { path: '/dashboard/projects/123/settings' } as RouteLocationNormalized;
|
const route = createTestRouteLocation({ path: '/dashboard/projects/123/settings' });
|
||||||
const projectId = inferProjectIdFromRoute(route);
|
const projectId = inferProjectIdFromRoute(route);
|
||||||
expect(projectId).toBe('123');
|
expect(projectId).toBe('123');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return undefined for project ID if not found', () => {
|
it('should return undefined for project ID if not found', () => {
|
||||||
const route = { path: '/dashboard/settings' } as RouteLocationNormalized;
|
const route = createTestRouteLocation({ path: '/dashboard/settings' });
|
||||||
const projectId = inferProjectIdFromRoute(route);
|
const projectId = inferProjectIdFromRoute(route);
|
||||||
expect(projectId).toBeUndefined();
|
expect(projectId).toBeUndefined();
|
||||||
});
|
});
|
||||||
@@ -29,15 +29,15 @@ describe('rbacUtils', () => {
|
|||||||
['/variables', 'variable'],
|
['/variables', 'variable'],
|
||||||
['/users', 'user'],
|
['/users', 'user'],
|
||||||
['/source-control', 'sourceControl'],
|
['/source-control', 'sourceControl'],
|
||||||
['/external-secrets', 'externalSecretsStore'],
|
['/external-secrets', 'externalSecret'],
|
||||||
])('should infer resource type from %s correctly to %s', (path, type) => {
|
])('should infer resource type from %s correctly to %s', (path, type) => {
|
||||||
const route = { path } as RouteLocationNormalized;
|
const route = createTestRouteLocation({ path });
|
||||||
const resourceType = inferResourceTypeFromRoute(route);
|
const resourceType = inferResourceTypeFromRoute(route);
|
||||||
expect(resourceType).toBe(type);
|
expect(resourceType).toBe(type);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return undefined for resource type if not found', () => {
|
it('should return undefined for resource type if not found', () => {
|
||||||
const route = { path: '/dashboard/settings' } as RouteLocationNormalized;
|
const route = createTestRouteLocation({ path: '/dashboard/settings' });
|
||||||
const resourceType = inferResourceTypeFromRoute(route);
|
const resourceType = inferResourceTypeFromRoute(route);
|
||||||
expect(resourceType).toBeUndefined();
|
expect(resourceType).toBeUndefined();
|
||||||
});
|
});
|
||||||
@@ -45,19 +45,19 @@ describe('rbacUtils', () => {
|
|||||||
|
|
||||||
describe('inferResourceIdFromRoute()', () => {
|
describe('inferResourceIdFromRoute()', () => {
|
||||||
it('should infer resource ID from params.id', () => {
|
it('should infer resource ID from params.id', () => {
|
||||||
const route = { params: { id: 'abc123' } } as RouteLocationNormalized;
|
const route = createTestRouteLocation({ params: { id: 'abc123' } });
|
||||||
const resourceId = inferResourceIdFromRoute(route);
|
const resourceId = inferResourceIdFromRoute(route);
|
||||||
expect(resourceId).toBe('abc123');
|
expect(resourceId).toBe('abc123');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should infer resource ID from params.name if id is not present', () => {
|
it('should infer resource ID from params.name if id is not present', () => {
|
||||||
const route = { params: { name: 'my-resource' } } as RouteLocationNormalized;
|
const route = createTestRouteLocation({ params: { name: 'my-resource' } });
|
||||||
const resourceId = inferResourceIdFromRoute(route);
|
const resourceId = inferResourceIdFromRoute(route);
|
||||||
expect(resourceId).toBe('my-resource');
|
expect(resourceId).toBe('my-resource');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return undefined for resource ID if neither id nor name is present', () => {
|
it('should return undefined for resource ID if neither id nor name is present', () => {
|
||||||
const route = { params: {} } as RouteLocationNormalized;
|
const route = createTestRouteLocation({ params: {} });
|
||||||
const resourceId = inferResourceIdFromRoute(route);
|
const resourceId = inferResourceIdFromRoute(route);
|
||||||
expect(resourceId).toBeUndefined();
|
expect(resourceId).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
import type { SourceControlStatus } from '@/Interface';
|
|
||||||
import { beforeEach } from 'vitest';
|
|
||||||
import { aggregateSourceControlFiles } from '@/utils/sourceControlUtils';
|
|
||||||
|
|
||||||
describe('sourceControlUtils', () => {
|
|
||||||
describe('aggregateSourceControlFiles()', () => {
|
|
||||||
let status: SourceControlStatus;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
status = {
|
|
||||||
ahead: 0,
|
|
||||||
behind: 0,
|
|
||||||
conflicted: [],
|
|
||||||
created: [],
|
|
||||||
current: 'main',
|
|
||||||
deleted: [],
|
|
||||||
detached: false,
|
|
||||||
files: [],
|
|
||||||
modified: [],
|
|
||||||
not_added: [],
|
|
||||||
renamed: [],
|
|
||||||
staged: [],
|
|
||||||
tracking: null,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be empty array if no files', () => {
|
|
||||||
expect(aggregateSourceControlFiles(status)).toEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should contain list of conflicted, created, deleted, modified, and renamed files', () => {
|
|
||||||
status.files = [
|
|
||||||
{ path: 'conflicted.json', index: 'A', working_dir: '' },
|
|
||||||
{ path: 'created.json', index: 'A', working_dir: '' },
|
|
||||||
{ path: 'deleted.json', index: 'A', working_dir: '' },
|
|
||||||
{ path: 'modified.json', index: 'A', working_dir: '' },
|
|
||||||
{ path: 'renamed.json', index: 'A', working_dir: '' },
|
|
||||||
];
|
|
||||||
|
|
||||||
status.conflicted.push('conflicted.json');
|
|
||||||
status.created.push('created.json');
|
|
||||||
status.deleted.push('deleted.json');
|
|
||||||
status.modified.push('modified.json');
|
|
||||||
status.renamed.push('renamed.json');
|
|
||||||
status.staged = status.files.map((file) => file.path);
|
|
||||||
|
|
||||||
expect(aggregateSourceControlFiles(status)).toEqual([
|
|
||||||
{ path: 'conflicted.json', status: 'conflicted', staged: true },
|
|
||||||
{ path: 'created.json', status: 'created', staged: true },
|
|
||||||
{ path: 'deleted.json', status: 'deleted', staged: true },
|
|
||||||
{ path: 'modified.json', status: 'modified', staged: true },
|
|
||||||
{ path: 'renamed.json', status: 'renamed', staged: true },
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -3,7 +3,7 @@ import type { IZoomConfig } from '@/Interface';
|
|||||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||||
import type { ConnectionDetachedParams } from '@jsplumb/core';
|
import type { ConnectionDetachedParams } from '@jsplumb/core';
|
||||||
import type { IConnection } from 'n8n-workflow';
|
import type { IConnection } from 'n8n-workflow';
|
||||||
import type { Route } from 'vue-router';
|
import type { RouteLocation } from 'vue-router';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Constants and utility functions mainly used by canvas store
|
Constants and utility functions mainly used by canvas store
|
||||||
@@ -52,7 +52,7 @@ export const scaleReset = (config: IZoomConfig): IZoomConfig => {
|
|||||||
return applyScale(1 / config.scale)(config);
|
return applyScale(1 / config.scale)(config);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getNodeViewTab = (route: Route): string | null => {
|
export const getNodeViewTab = (route: RouteLocation): string | null => {
|
||||||
if (route.meta?.nodeView) {
|
if (route.meta?.nodeView) {
|
||||||
return MAIN_HEADER_TABS.WORKFLOW;
|
return MAIN_HEADER_TABS.WORKFLOW;
|
||||||
} else if (
|
} else if (
|
||||||
|
|||||||
@@ -143,8 +143,8 @@ export const getMainAuthField = (nodeType: INodeTypeDescription | null): INodePr
|
|||||||
credentialDependencies.find(
|
credentialDependencies.find(
|
||||||
(prop) =>
|
(prop) =>
|
||||||
prop.name === MAIN_AUTH_FIELD_NAME &&
|
prop.name === MAIN_AUTH_FIELD_NAME &&
|
||||||
!prop.options?.find((option) => option.value === 'none'),
|
!prop.options?.find((option) => 'value' in option && option.value === 'none'),
|
||||||
) || null;
|
) ?? null;
|
||||||
// If there is a field name `authentication`, use it
|
// If there is a field name `authentication`, use it
|
||||||
// Otherwise, try to find alternative main auth field
|
// Otherwise, try to find alternative main auth field
|
||||||
const mainAuthFiled =
|
const mainAuthFiled =
|
||||||
@@ -166,7 +166,7 @@ const findAlternativeAuthField = (
|
|||||||
if (cred.displayOptions?.show) {
|
if (cred.displayOptions?.show) {
|
||||||
for (const fieldName in cred.displayOptions.show) {
|
for (const fieldName in cred.displayOptions.show) {
|
||||||
dependentAuthFieldValues[fieldName] = (dependentAuthFieldValues[fieldName] || []).concat(
|
dependentAuthFieldValues[fieldName] = (dependentAuthFieldValues[fieldName] || []).concat(
|
||||||
(cred.displayOptions.show[fieldName] || []).map((val) => (val ? val.toString() : '')),
|
(cred.displayOptions.show[fieldName] ?? []).map((val) => (val ? val.toString() : '')),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,7 +174,11 @@ const findAlternativeAuthField = (
|
|||||||
const alternativeAuthField = fields.find((field) => {
|
const alternativeAuthField = fields.find((field) => {
|
||||||
let required = true;
|
let required = true;
|
||||||
field.options?.forEach((option) => {
|
field.options?.forEach((option) => {
|
||||||
if (!dependentAuthFieldValues[field.name].includes(option.value)) {
|
if (
|
||||||
|
'value' in option &&
|
||||||
|
typeof option.value === 'string' &&
|
||||||
|
!dependentAuthFieldValues[field.name].includes(option.value)
|
||||||
|
) {
|
||||||
required = false;
|
required = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -206,21 +210,24 @@ export const getNodeAuthOptions = (
|
|||||||
if (field.options) {
|
if (field.options) {
|
||||||
options = options.concat(
|
options = options.concat(
|
||||||
field.options.map((option) => {
|
field.options.map((option) => {
|
||||||
|
const optionValue = 'value' in option ? `${option.value}` : '';
|
||||||
|
|
||||||
// Check if credential type associated with this auth option has overwritten properties
|
// Check if credential type associated with this auth option has overwritten properties
|
||||||
let hasOverrides = false;
|
let hasOverrides = false;
|
||||||
const cred = getNodeCredentialForSelectedAuthType(nodeType, option.value);
|
const cred = getNodeCredentialForSelectedAuthType(nodeType, optionValue);
|
||||||
if (cred) {
|
if (cred) {
|
||||||
hasOverrides =
|
hasOverrides =
|
||||||
useCredentialsStore().getCredentialTypeByName(cred.name)?.__overwrittenProperties !==
|
useCredentialsStore().getCredentialTypeByName(cred.name)?.__overwrittenProperties !==
|
||||||
undefined;
|
undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name:
|
name:
|
||||||
// Add recommended suffix if credentials have overrides and option is not already recommended
|
// Add recommended suffix if credentials have overrides and option is not already recommended
|
||||||
hasOverrides && !option.name.endsWith(recommendedSuffix)
|
hasOverrides && !option.name.endsWith(recommendedSuffix)
|
||||||
? `${option.name} ${recommendedSuffix}`
|
? `${option.name} ${recommendedSuffix}`
|
||||||
: option.name,
|
: option.name,
|
||||||
value: option.value,
|
value: optionValue,
|
||||||
// Also add in the display options so we can hide/show the option if necessary
|
// Also add in the display options so we can hide/show the option if necessary
|
||||||
displayOptions: field.displayOptions,
|
displayOptions: field.displayOptions,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { isNumber } from '@/utils/typeGuards';
|
import { isNumber, isValidNodeConnectionType } from '@/utils/typeGuards';
|
||||||
import { NODE_OUTPUT_DEFAULT_KEY, STICKY_NODE_TYPE } from '@/constants';
|
import { NODE_OUTPUT_DEFAULT_KEY, STICKY_NODE_TYPE } from '@/constants';
|
||||||
import type { EndpointStyle, IBounds, INodeUi, XYPosition } from '@/Interface';
|
import type { EndpointMeta, EndpointStyle, IBounds, INodeUi, XYPosition } from '@/Interface';
|
||||||
import type { ArrayAnchorSpec, ConnectorSpec, OverlaySpec, PaintStyle } from '@jsplumb/common';
|
import type { ArrayAnchorSpec, ConnectorSpec, OverlaySpec, PaintStyle } from '@jsplumb/common';
|
||||||
import type { Endpoint, Connection } from '@jsplumb/core';
|
import type { Connection, Endpoint, SelectOptions } from '@jsplumb/core';
|
||||||
import { N8nConnector } from '@/plugins/connectors/N8nCustomConnector';
|
import { N8nConnector } from '@/plugins/connectors/N8nCustomConnector';
|
||||||
import type {
|
import type {
|
||||||
ConnectionTypes,
|
ConnectionTypes,
|
||||||
IConnection,
|
IConnection,
|
||||||
ITaskData,
|
|
||||||
INodeExecutionData,
|
INodeExecutionData,
|
||||||
NodeInputConnections,
|
|
||||||
INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
|
ITaskData,
|
||||||
|
NodeInputConnections,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { NodeConnectionType } from 'n8n-workflow';
|
import { NodeConnectionType } from 'n8n-workflow';
|
||||||
import type { BrowserJsPlumbInstance } from '@jsplumb/browser-ui';
|
import type { BrowserJsPlumbInstance } from '@jsplumb/browser-ui';
|
||||||
@@ -72,13 +72,15 @@ export const CONNECTOR_FLOWCHART_TYPE: ConnectorSpec = {
|
|||||||
alwaysRespectStubs: false,
|
alwaysRespectStubs: false,
|
||||||
loopbackVerticalLength: NODE_SIZE + GRID_SIZE, // height of vertical segment when looping
|
loopbackVerticalLength: NODE_SIZE + GRID_SIZE, // height of vertical segment when looping
|
||||||
loopbackMinimum: LOOPBACK_MINIMUM, // minimum length before flowchart loops around
|
loopbackMinimum: LOOPBACK_MINIMUM, // minimum length before flowchart loops around
|
||||||
getEndpointOffset(endpoint: Endpoint) {
|
getEndpointOffset(endpoint: Endpoint & EndpointMeta) {
|
||||||
const indexOffset = 10; // stub offset between different endpoints of same node
|
const indexOffset = 10; // stub offset between different endpoints of same node
|
||||||
const index = endpoint?.__meta ? endpoint.__meta.index : 0;
|
const index = endpoint?.__meta ? endpoint.__meta.index : 0;
|
||||||
const totalEndpoints = endpoint?.__meta ? endpoint.__meta.totalEndpoints : 0;
|
const totalEndpoints = endpoint?.__meta ? endpoint.__meta.totalEndpoints : 0;
|
||||||
|
|
||||||
const outputOverlay = getOverlay(endpoint, OVERLAY_OUTPUT_NAME_LABEL);
|
const outputOverlay = getOverlay(endpoint, OVERLAY_OUTPUT_NAME_LABEL);
|
||||||
const labelOffset = outputOverlay?.label && outputOverlay.label.length > 1 ? 10 : 0;
|
const outputOverlayLabel =
|
||||||
|
outputOverlay && 'label' in outputOverlay ? `${outputOverlay?.label}` : '';
|
||||||
|
const labelOffset = outputOverlayLabel.length > 1 ? 10 : 0;
|
||||||
const outputsOffset = totalEndpoints > 3 ? 24 : 0; // avoid intersecting plus
|
const outputsOffset = totalEndpoints > 3 ? 24 : 0; // avoid intersecting plus
|
||||||
|
|
||||||
return index * indexOffset + labelOffset + outputsOffset;
|
return index * indexOffset + labelOffset + outputsOffset;
|
||||||
@@ -111,6 +113,10 @@ export const CONNECTOR_PAINT_STYLE_DATA: PaintStyle = {
|
|||||||
stroke: 'var(--color-foreground-dark)',
|
stroke: 'var(--color-foreground-dark)',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function isCanvasAugmentedType<T>(overlay: T): overlay is T & { canvas: HTMLElement } {
|
||||||
|
return typeof overlay === 'object' && overlay !== null && 'canvas' in overlay && !!overlay.canvas;
|
||||||
|
}
|
||||||
|
|
||||||
export const getConnectorColor = (type: ConnectionTypes, category?: string): string => {
|
export const getConnectorColor = (type: ConnectionTypes, category?: string): string => {
|
||||||
if (category === 'error') {
|
if (category === 'error') {
|
||||||
return '--color-node-error-output-text-color';
|
return '--color-node-error-output-text-color';
|
||||||
@@ -228,15 +234,18 @@ export const getAnchorPosition = (
|
|||||||
return returnPositions;
|
return returnPositions;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getScope = (type?: string) => {
|
export const getScope = (type?: NodeConnectionType): NodeConnectionType | undefined => {
|
||||||
if (!type || type === NodeConnectionType.Main) {
|
if (!type || type === NodeConnectionType.Main) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return type;
|
return type;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getEndpointScope = (endpointType: ConnectionTypes): string | undefined => {
|
export const getEndpointScope = (
|
||||||
if (Object.values(NodeConnectionType).includes(endpointType)) {
|
endpointType: NodeConnectionType | string,
|
||||||
|
): NodeConnectionType | undefined => {
|
||||||
|
if (isValidNodeConnectionType(endpointType)) {
|
||||||
return getScope(endpointType);
|
return getScope(endpointType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,7 +285,7 @@ export const getInputNameOverlay = (
|
|||||||
id: OVERLAY_INPUT_NAME_LABEL,
|
id: OVERLAY_INPUT_NAME_LABEL,
|
||||||
visible: true,
|
visible: true,
|
||||||
location: [-1, -1],
|
location: [-1, -1],
|
||||||
create: (component: Endpoint) => {
|
create: (_: Endpoint) => {
|
||||||
const label = document.createElement('div');
|
const label = document.createElement('div');
|
||||||
label.innerHTML = labelText;
|
label.innerHTML = labelText;
|
||||||
if (required) {
|
if (required) {
|
||||||
@@ -303,20 +312,20 @@ export const getOutputEndpointStyle = (
|
|||||||
|
|
||||||
export const getOutputNameOverlay = (
|
export const getOutputNameOverlay = (
|
||||||
labelText: string,
|
labelText: string,
|
||||||
outputName: ConnectionTypes,
|
outputName: NodeConnectionType,
|
||||||
category?: string,
|
category?: string,
|
||||||
): OverlaySpec => ({
|
): OverlaySpec => ({
|
||||||
type: 'Custom',
|
type: 'Custom',
|
||||||
options: {
|
options: {
|
||||||
id: OVERLAY_OUTPUT_NAME_LABEL,
|
id: OVERLAY_OUTPUT_NAME_LABEL,
|
||||||
visible: true,
|
visible: true,
|
||||||
create: (ep: Endpoint) => {
|
create: (ep: Endpoint & EndpointMeta) => {
|
||||||
const label = document.createElement('div');
|
const label = document.createElement('div');
|
||||||
label.innerHTML = labelText;
|
label.innerHTML = labelText;
|
||||||
label.classList.add('node-output-endpoint-label');
|
label.classList.add('node-output-endpoint-label');
|
||||||
|
|
||||||
if (ep?.__meta?.endpointLabelLength) {
|
if (ep?.__meta?.endpointLabelLength) {
|
||||||
label.setAttribute('data-endpoint-label-length', ep?.__meta?.endpointLabelLength);
|
label.setAttribute('data-endpoint-label-length', `${ep?.__meta?.endpointLabelLength}`);
|
||||||
}
|
}
|
||||||
label.classList.add(`node-connection-type-${getScope(outputName) ?? 'main'}`);
|
label.classList.add(`node-connection-type-${getScope(outputName) ?? 'main'}`);
|
||||||
if (outputName !== NodeConnectionType.Main) {
|
if (outputName !== NodeConnectionType.Main) {
|
||||||
@@ -438,8 +447,11 @@ export const showOrHideMidpointArrow = (connection: Connection) => {
|
|||||||
if (arrow) {
|
if (arrow) {
|
||||||
arrow.setVisible(isArrowVisible);
|
arrow.setVisible(isArrowVisible);
|
||||||
arrow.setLocation(hasItemsLabel ? 0.6 : 0.5);
|
arrow.setLocation(hasItemsLabel ? 0.6 : 0.5);
|
||||||
|
|
||||||
|
if (isCanvasAugmentedType(arrow)) {
|
||||||
connection.instance.repaint(arrow.canvas);
|
connection.instance.repaint(arrow.canvas);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getConnectorLengths = (connection: Connection): [number, number] => {
|
export const getConnectorLengths = (connection: Connection): [number, number] => {
|
||||||
@@ -481,7 +493,9 @@ export const showOrHideItemsLabel = (connection: Connection) => {
|
|||||||
const isHidden = diffX < MIN_X_TO_SHOW_OUTPUT_LABEL && diffY < MIN_Y_TO_SHOW_OUTPUT_LABEL;
|
const isHidden = diffX < MIN_X_TO_SHOW_OUTPUT_LABEL && diffY < MIN_Y_TO_SHOW_OUTPUT_LABEL;
|
||||||
|
|
||||||
overlay.setVisible(!isHidden);
|
overlay.setVisible(!isHidden);
|
||||||
const innerElement = overlay.canvas?.querySelector('span');
|
const innerElement = isCanvasAugmentedType(overlay)
|
||||||
|
? overlay.canvas.querySelector('span')
|
||||||
|
: undefined;
|
||||||
if (innerElement) {
|
if (innerElement) {
|
||||||
if (diffY === 0 || isLoopingBackwards(connection)) {
|
if (diffY === 0 || isLoopingBackwards(connection)) {
|
||||||
innerElement.classList.add('floating');
|
innerElement.classList.add('floating');
|
||||||
@@ -812,8 +826,11 @@ export const addClassesToOverlays = ({
|
|||||||
overlayIds.forEach((overlayId) => {
|
overlayIds.forEach((overlayId) => {
|
||||||
const overlay = getOverlay(connection, overlayId);
|
const overlay = getOverlay(connection, overlayId);
|
||||||
|
|
||||||
overlay?.canvas?.classList.add(...classNames);
|
if (overlay && isCanvasAugmentedType(overlay)) {
|
||||||
if (includeConnector) {
|
overlay.canvas?.classList.add(...classNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includeConnector && isCanvasAugmentedType(connection.connector)) {
|
||||||
connection.connector.canvas?.classList.add(...classNames);
|
connection.connector.canvas?.classList.add(...classNames);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -995,18 +1012,18 @@ export const addConnectionActionsOverlay = (
|
|||||||
|
|
||||||
export const getOutputEndpointUUID = (
|
export const getOutputEndpointUUID = (
|
||||||
nodeId: string,
|
nodeId: string,
|
||||||
connectionType: ConnectionTypes,
|
connectionType: NodeConnectionType,
|
||||||
outputIndex: number,
|
outputIndex: number,
|
||||||
) => {
|
) => {
|
||||||
return `${nodeId}${OUTPUT_UUID_KEY}${getScope(connectionType) || ''}${outputIndex}`;
|
return `${nodeId}${OUTPUT_UUID_KEY}${getScope(connectionType) ?? ''}${outputIndex}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getInputEndpointUUID = (
|
export const getInputEndpointUUID = (
|
||||||
nodeId: string,
|
nodeId: string,
|
||||||
connectionType: ConnectionTypes,
|
connectionType: NodeConnectionType,
|
||||||
inputIndex: number,
|
inputIndex: number,
|
||||||
) => {
|
) => {
|
||||||
return `${nodeId}${INPUT_UUID_KEY}${getScope(connectionType) || ''}${inputIndex}`;
|
return `${nodeId}${INPUT_UUID_KEY}${getScope(connectionType) ?? ''}${inputIndex}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getFixedNodesList = <T extends { position: XYPosition }>(workflowNodes: T[]): T[] => {
|
export const getFixedNodesList = <T extends { position: XYPosition }>(workflowNodes: T[]): T[] => {
|
||||||
@@ -1089,10 +1106,10 @@ export const getJSPlumbEndpoints = (
|
|||||||
node: INodeUi | null,
|
node: INodeUi | null,
|
||||||
instance: BrowserJsPlumbInstance,
|
instance: BrowserJsPlumbInstance,
|
||||||
): Endpoint[] => {
|
): Endpoint[] => {
|
||||||
const nodeEl = instance.getManagedElement(node?.id);
|
if (!node) return [];
|
||||||
|
|
||||||
const endpoints = instance?.getEndpoints(nodeEl);
|
const nodeEl = instance.getManagedElement(node?.id);
|
||||||
return endpoints;
|
return instance?.getEndpoints(nodeEl);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getPlusEndpoint = (
|
export const getPlusEndpoint = (
|
||||||
@@ -1102,8 +1119,7 @@ export const getPlusEndpoint = (
|
|||||||
): Endpoint | undefined => {
|
): Endpoint | undefined => {
|
||||||
const endpoints = getJSPlumbEndpoints(node, instance);
|
const endpoints = getJSPlumbEndpoints(node, instance);
|
||||||
return endpoints.find(
|
return endpoints.find(
|
||||||
(endpoint: Endpoint) =>
|
(endpoint: Endpoint & EndpointMeta) =>
|
||||||
// @ts-ignore
|
|
||||||
endpoint.endpoint.type === 'N8nPlus' && endpoint?.__meta?.index === outputIndex,
|
endpoint.endpoint.type === 'N8nPlus' && endpoint?.__meta?.index === outputIndex,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -1113,7 +1129,7 @@ export const getJSPlumbConnection = (
|
|||||||
sourceOutputIndex: number,
|
sourceOutputIndex: number,
|
||||||
targetNode: INodeUi | null,
|
targetNode: INodeUi | null,
|
||||||
targetInputIndex: number,
|
targetInputIndex: number,
|
||||||
connectionType: ConnectionTypes,
|
connectionType: NodeConnectionType,
|
||||||
sourceNodeType: INodeTypeDescription | null,
|
sourceNodeType: INodeTypeDescription | null,
|
||||||
instance: BrowserJsPlumbInstance,
|
instance: BrowserJsPlumbInstance,
|
||||||
): Connection | undefined => {
|
): Connection | undefined => {
|
||||||
@@ -1127,17 +1143,24 @@ export const getJSPlumbConnection = (
|
|||||||
const sourceEndpoint = getOutputEndpointUUID(sourceId, connectionType, sourceOutputIndex);
|
const sourceEndpoint = getOutputEndpointUUID(sourceId, connectionType, sourceOutputIndex);
|
||||||
const targetEndpoint = getInputEndpointUUID(targetId, connectionType, targetInputIndex);
|
const targetEndpoint = getInputEndpointUUID(targetId, connectionType, targetInputIndex);
|
||||||
|
|
||||||
const sourceNodeOutput = sourceNodeType?.outputs?.[sourceOutputIndex] || NodeConnectionType.Main;
|
const sourceNodeOutput = sourceNodeType?.outputs?.[sourceOutputIndex] ?? NodeConnectionType.Main;
|
||||||
const sourceNodeOutputName =
|
const sourceNodeOutputName =
|
||||||
typeof sourceNodeOutput === 'string' ? sourceNodeOutput : sourceNodeOutput.name;
|
typeof sourceNodeOutput === 'string'
|
||||||
|
? sourceNodeOutput
|
||||||
|
: 'name' in sourceNodeOutput
|
||||||
|
? `${sourceNodeOutput.name}`
|
||||||
|
: '';
|
||||||
const scope = getEndpointScope(sourceNodeOutputName);
|
const scope = getEndpointScope(sourceNodeOutputName);
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
const connections = instance?.getConnections({
|
const connections = instance?.getConnections({
|
||||||
scope,
|
scope,
|
||||||
source: sourceId,
|
source: sourceId,
|
||||||
target: targetId,
|
target: targetId,
|
||||||
}) as Connection[];
|
} as SelectOptions<Element>);
|
||||||
|
|
||||||
|
if (!Array.isArray(connections)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return connections.find((connection: Connection) => {
|
return connections.find((connection: Connection) => {
|
||||||
const uuids = connection.getUuids();
|
const uuids = connection.getUuids();
|
||||||
|
|||||||
@@ -11,20 +11,24 @@ export function inferProjectIdFromRoute(to: RouteLocationNormalized): string {
|
|||||||
|
|
||||||
export function inferResourceTypeFromRoute(to: RouteLocationNormalized): Resource | undefined {
|
export function inferResourceTypeFromRoute(to: RouteLocationNormalized): Resource | undefined {
|
||||||
const routeParts = to.path.split('/');
|
const routeParts = to.path.split('/');
|
||||||
const routeMap = {
|
const routeMap: Record<string, string> = {
|
||||||
workflow: 'workflows',
|
workflow: 'workflows',
|
||||||
credential: 'credentials',
|
credential: 'credentials',
|
||||||
user: 'users',
|
user: 'users',
|
||||||
variable: 'variables',
|
variable: 'variables',
|
||||||
sourceControl: 'source-control',
|
sourceControl: 'source-control',
|
||||||
externalSecretsStore: 'external-secrets',
|
externalSecret: 'external-secrets',
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const resource of Object.keys(routeMap) as Array<keyof typeof routeMap>) {
|
const isResource = (key: string): key is Resource => routeParts.includes(routeMap[key]);
|
||||||
if (routeParts.includes(routeMap[resource])) {
|
|
||||||
|
for (const resource of Object.keys(routeMap)) {
|
||||||
|
if (isResource(resource)) {
|
||||||
return resource;
|
return resource;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function inferResourceIdFromRoute(to: RouteLocationNormalized): string | undefined {
|
export function inferResourceIdFromRoute(to: RouteLocationNormalized): string | undefined {
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
import type { SourceControlAggregatedFile, SourceControlStatus } from '@/Interface';
|
|
||||||
|
|
||||||
export function aggregateSourceControlFiles(sourceControlStatus: SourceControlStatus) {
|
|
||||||
return sourceControlStatus.files.reduce<SourceControlAggregatedFile[]>((acc, file) => {
|
|
||||||
const staged = sourceControlStatus.staged.includes(file.path);
|
|
||||||
|
|
||||||
let status = '';
|
|
||||||
(
|
|
||||||
['conflicted', 'created', 'deleted', 'modified', 'renamed'] as Array<
|
|
||||||
keyof SourceControlStatus
|
|
||||||
>
|
|
||||||
).forEach((key) => {
|
|
||||||
const filesForStatus = sourceControlStatus[key] as string[];
|
|
||||||
if (filesForStatus.includes(file.path)) {
|
|
||||||
status = key;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
acc.push({
|
|
||||||
path: file.path,
|
|
||||||
status,
|
|
||||||
staged,
|
|
||||||
});
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
}
|
|
||||||
@@ -26,12 +26,14 @@ export function createExpressionTelemetryPayload(
|
|||||||
handlebar_error_count: erroringResolvables.length,
|
handlebar_error_count: erroringResolvables.length,
|
||||||
short_errors: erroringResolvables.map((r) => r.resolved ?? null),
|
short_errors: erroringResolvables.map((r) => r.resolved ?? null),
|
||||||
full_errors: erroringResolvables.map((erroringResolvable) => {
|
full_errors: erroringResolvables.map((erroringResolvable) => {
|
||||||
if (!erroringResolvable.fullError) return null;
|
if (erroringResolvable.fullError) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...exposeErrorProperties(erroringResolvable.fullError),
|
...exposeErrorProperties(erroringResolvable.fullError),
|
||||||
stack: erroringResolvable.fullError.stack,
|
stack: erroringResolvable.fullError.stack,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -295,17 +295,14 @@ export const fullSaveEmailAttachmentsToNextCloudTemplate = {
|
|||||||
export const fullCreateApiEndpointTemplate = {
|
export const fullCreateApiEndpointTemplate = {
|
||||||
id: 1750,
|
id: 1750,
|
||||||
name: 'Creating an API endpoint',
|
name: 'Creating an API endpoint',
|
||||||
views: 13265,
|
|
||||||
recentViews: 9899,
|
recentViews: 9899,
|
||||||
totalViews: 13265,
|
totalViews: 13265,
|
||||||
createdAt: '2022-07-06T14:45:19.659Z',
|
createdAt: '2022-07-06T14:45:19.659Z',
|
||||||
description:
|
description:
|
||||||
'**Task:**\nCreate a simple API endpoint using the Webhook and Respond to Webhook nodes\n\n**Why:**\nYou can prototype or replace a backend process with a single workflow\n\n**Main use cases:**\nReplace backend logic with a workflow',
|
'**Task:**\nCreate a simple API endpoint using the Webhook and Respond to Webhook nodes\n\n**Why:**\nYou can prototype or replace a backend process with a single workflow\n\n**Main use cases:**\nReplace backend logic with a workflow',
|
||||||
workflow: {
|
workflow: {
|
||||||
meta: { instanceId: '8c8c5237b8e37b006a7adce87f4369350c58e41f3ca9de16196d3197f69eabcd' },
|
|
||||||
nodes: [
|
nodes: [
|
||||||
{
|
{
|
||||||
id: 'f80aceed-b676-42aa-bf25-f7a44408b1bc',
|
|
||||||
name: 'Webhook',
|
name: 'Webhook',
|
||||||
type: 'n8n-nodes-base.webhook',
|
type: 'n8n-nodes-base.webhook',
|
||||||
position: [375, 115],
|
position: [375, 115],
|
||||||
@@ -318,7 +315,6 @@ export const fullCreateApiEndpointTemplate = {
|
|||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '3b9ec913-0bbe-4906-bf8e-da352b556655',
|
|
||||||
name: 'Note1',
|
name: 'Note1',
|
||||||
type: 'n8n-nodes-base.stickyNote',
|
type: 'n8n-nodes-base.stickyNote',
|
||||||
position: [355, -25],
|
position: [355, -25],
|
||||||
@@ -331,7 +327,6 @@ export const fullCreateApiEndpointTemplate = {
|
|||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '9c36dae5-0700-450c-9739-e9f3eff31bfe',
|
|
||||||
name: 'Respond to Webhook',
|
name: 'Respond to Webhook',
|
||||||
type: 'n8n-nodes-base.respondToWebhook',
|
type: 'n8n-nodes-base.respondToWebhook',
|
||||||
position: [815, 115],
|
position: [815, 115],
|
||||||
@@ -344,7 +339,6 @@ export const fullCreateApiEndpointTemplate = {
|
|||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '5a228fcb-78b9-4a28-95d2-d7c9fdf1d4ea',
|
|
||||||
name: 'Create URL string',
|
name: 'Create URL string',
|
||||||
type: 'n8n-nodes-base.set',
|
type: 'n8n-nodes-base.set',
|
||||||
position: [595, 115],
|
position: [595, 115],
|
||||||
@@ -364,7 +358,6 @@ export const fullCreateApiEndpointTemplate = {
|
|||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'e7971820-45a8-4dc8-ba4c-b3220d65307a',
|
|
||||||
name: 'Note3',
|
name: 'Note3',
|
||||||
type: 'n8n-nodes-base.stickyNote',
|
type: 'n8n-nodes-base.stickyNote',
|
||||||
position: [355, 275],
|
position: [355, 275],
|
||||||
@@ -383,7 +376,10 @@ export const fullCreateApiEndpointTemplate = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
lastUpdatedBy: 1,
|
lastUpdatedBy: 1,
|
||||||
workflowInfo: null,
|
workflowInfo: {
|
||||||
|
nodeCount: 2,
|
||||||
|
nodeTypes: {},
|
||||||
|
},
|
||||||
user: { username: 'jon-n8n' },
|
user: { username: 'jon-n8n' },
|
||||||
nodes: [
|
nodes: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { INodeParameterResourceLocator } from 'n8n-workflow';
|
import type { INodeParameterResourceLocator, NodeConnectionType } from 'n8n-workflow';
|
||||||
|
import { nodeConnectionTypes } from 'n8n-workflow';
|
||||||
import type { ICredentialsResponse, NewCredentialsModal } from '@/Interface';
|
import type { ICredentialsResponse, NewCredentialsModal } from '@/Interface';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -49,3 +50,9 @@ export function isDateObject(date: unknown): date is Date {
|
|||||||
!!date && Object.prototype.toString.call(date) === '[object Date]' && !isNaN(date as number)
|
!!date && Object.prototype.toString.call(date) === '[object Date]' && !isNaN(date as number)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isValidNodeConnectionType(
|
||||||
|
connectionType: string,
|
||||||
|
): connectionType is NodeConnectionType {
|
||||||
|
return nodeConnectionTypes.includes(connectionType as NodeConnectionType);
|
||||||
|
}
|
||||||
|
|||||||
@@ -77,11 +77,8 @@ export function shorten(s: string, limit: number, keep: number) {
|
|||||||
export const convertPath = (path: string): string => {
|
export const convertPath = (path: string): string => {
|
||||||
// TODO: That can for sure be done fancier but for now it works
|
// TODO: That can for sure be done fancier but for now it works
|
||||||
const placeholder = '*___~#^#~___*';
|
const placeholder = '*___~#^#~___*';
|
||||||
let inBrackets = path.match(/\[(.*?)]/g);
|
let inBrackets: string[] = path.match(/\[(.*?)]/g) ?? [];
|
||||||
|
|
||||||
if (inBrackets === null) {
|
|
||||||
inBrackets = [];
|
|
||||||
} else {
|
|
||||||
inBrackets = inBrackets
|
inBrackets = inBrackets
|
||||||
.map((item) => item.slice(1, -1))
|
.map((item) => item.slice(1, -1))
|
||||||
.map((item) => {
|
.map((item) => {
|
||||||
@@ -90,7 +87,7 @@ export const convertPath = (path: string): string => {
|
|||||||
}
|
}
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
}
|
|
||||||
const withoutBrackets = path.replace(/\[(.*?)]/g, placeholder);
|
const withoutBrackets = path.replace(/\[(.*?)]/g, placeholder);
|
||||||
const pathParts = withoutBrackets.split('.');
|
const pathParts = withoutBrackets.split('.');
|
||||||
const allParts = [] as string[];
|
const allParts = [] as string[];
|
||||||
@@ -98,7 +95,7 @@ export const convertPath = (path: string): string => {
|
|||||||
let index = part.indexOf(placeholder);
|
let index = part.indexOf(placeholder);
|
||||||
while (index !== -1) {
|
while (index !== -1) {
|
||||||
if (index === 0) {
|
if (index === 0) {
|
||||||
allParts.push(inBrackets!.shift() as string);
|
allParts.push(inBrackets.shift() ?? '');
|
||||||
part = part.substr(placeholder.length);
|
part = part.substr(placeholder.length);
|
||||||
} else {
|
} else {
|
||||||
allParts.push(part.substr(0, index));
|
allParts.push(part.substr(0, index));
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ function getPersonalizationSurveyV2OrLater(
|
|||||||
|
|
||||||
const companySize = answers[COMPANY_SIZE_KEY];
|
const companySize = answers[COMPANY_SIZE_KEY];
|
||||||
const companyType = answers[COMPANY_TYPE_KEY];
|
const companyType = answers[COMPANY_TYPE_KEY];
|
||||||
const automationGoal = answers[AUTOMATION_GOAL_KEY];
|
const automationGoal = AUTOMATION_GOAL_KEY in answers ? answers[AUTOMATION_GOAL_KEY] : undefined;
|
||||||
|
|
||||||
let codingSkill = null;
|
let codingSkill = null;
|
||||||
if (CODING_SKILL_KEY in answers && answers[CODING_SKILL_KEY]) {
|
if (CODING_SKILL_KEY in answers && answers[CODING_SKILL_KEY]) {
|
||||||
|
|||||||
@@ -1704,6 +1704,21 @@ export const enum NodeConnectionType {
|
|||||||
Main = 'main',
|
Main = 'main',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const nodeConnectionTypes: NodeConnectionType[] = [
|
||||||
|
NodeConnectionType.AiAgent,
|
||||||
|
NodeConnectionType.AiChain,
|
||||||
|
NodeConnectionType.AiDocument,
|
||||||
|
NodeConnectionType.AiEmbedding,
|
||||||
|
NodeConnectionType.AiLanguageModel,
|
||||||
|
NodeConnectionType.AiMemory,
|
||||||
|
NodeConnectionType.AiOutputParser,
|
||||||
|
NodeConnectionType.AiRetriever,
|
||||||
|
NodeConnectionType.AiTextSplitter,
|
||||||
|
NodeConnectionType.AiTool,
|
||||||
|
NodeConnectionType.AiVectorStore,
|
||||||
|
NodeConnectionType.Main,
|
||||||
|
];
|
||||||
|
|
||||||
export interface INodeInputFilter {
|
export interface INodeInputFilter {
|
||||||
// TODO: Later add more filter options like categories, subcatogries,
|
// TODO: Later add more filter options like categories, subcatogries,
|
||||||
// regex, allow to exclude certain nodes, ... ?
|
// regex, allow to exclude certain nodes, ... ?
|
||||||
|
|||||||
Reference in New Issue
Block a user