Files
dify/web/app/components/workflow/utils/plugin-install-check.ts
yyh f6d80b9fa7 fix(workflow): derive plugin install state in render
Remove useEffect-based sync of _pluginInstallLocked/_dimmed in workflow nodes to avoid render-update loops.\n\nMove plugin-missing checks to pure utilities and use them in checklist.\nOptimize node installation hooks by enabling only relevant queries and narrowing memo dependencies.
2026-03-09 17:18:09 +08:00

96 lines
3.4 KiB
TypeScript

import type { TriggerWithProvider } from '../block-selector/types'
import type { DataSourceNodeType } from '../nodes/data-source/types'
import type { ToolNodeType } from '../nodes/tool/types'
import type { PluginTriggerNodeType } from '../nodes/trigger-plugin/types'
import type { CommonNodeType, ToolWithProvider } from '../types'
import { CollectionType } from '@/app/components/tools/types'
import { canFindTool } from '@/utils'
import { BlockEnum } from '../types'
export const PLUGIN_DEPENDENT_TYPES: BlockEnum[] = [
BlockEnum.Tool,
BlockEnum.DataSource,
BlockEnum.TriggerPlugin,
]
export function isPluginDependentNode(type: string): boolean {
return PLUGIN_DEPENDENT_TYPES.includes(type as BlockEnum)
}
export function matchToolInCollection(
collection: ToolWithProvider[],
data: { plugin_id?: string, provider_id?: string, provider_name?: string },
): ToolWithProvider | undefined {
return collection.find(tool =>
(data.plugin_id && tool.plugin_id === data.plugin_id)
|| canFindTool(tool.id, data.provider_id)
|| tool.name === data.provider_name,
)
}
export function matchTriggerProvider(
providers: TriggerWithProvider[],
data: { provider_name?: string, provider_id?: string, plugin_id?: string },
): TriggerWithProvider | undefined {
return providers.find(provider =>
provider.name === data.provider_name
|| provider.id === data.provider_id
|| (data.plugin_id && provider.plugin_id === data.plugin_id),
)
}
export function matchDataSource(
list: ToolWithProvider[],
data: { plugin_unique_identifier?: string, plugin_id?: string, provider_name?: string },
): ToolWithProvider | undefined {
return list.find(item =>
(data.plugin_unique_identifier && item.plugin_unique_identifier === data.plugin_unique_identifier)
|| (data.plugin_id && item.plugin_id === data.plugin_id)
|| (data.provider_name && item.provider === data.provider_name),
)
}
export type PluginInstallCheckContext = {
builtInTools?: ToolWithProvider[]
customTools?: ToolWithProvider[]
workflowTools?: ToolWithProvider[]
mcpTools?: ToolWithProvider[]
triggerPlugins?: TriggerWithProvider[]
dataSourceList?: ToolWithProvider[]
}
export function isNodePluginMissing(
data: CommonNodeType,
context: PluginInstallCheckContext,
): boolean {
switch (data.type as BlockEnum) {
case BlockEnum.Tool: {
const toolData = data as ToolNodeType
const collectionMap: Partial<Record<CollectionType, ToolWithProvider[] | undefined>> = {
[CollectionType.builtIn]: context.builtInTools,
[CollectionType.custom]: context.customTools,
[CollectionType.workflow]: context.workflowTools,
[CollectionType.mcp]: context.mcpTools,
}
const collection = collectionMap[toolData.provider_type]
if (!collection)
return false
return !matchToolInCollection(collection, toolData) && Boolean(toolData.plugin_unique_identifier)
}
case BlockEnum.TriggerPlugin: {
const triggerData = data as PluginTriggerNodeType
if (!context.triggerPlugins)
return false
return !matchTriggerProvider(context.triggerPlugins, triggerData) && Boolean(triggerData.plugin_unique_identifier)
}
case BlockEnum.DataSource: {
const dataSourceData = data as DataSourceNodeType
if (!context.dataSourceList)
return false
return !matchDataSource(context.dataSourceList, dataSourceData) && Boolean(dataSourceData.plugin_unique_identifier)
}
default:
return false
}
}