Replies: 1 comment
-
|
Hi @ymolists, You read the SDK correctly. The good news is you can do it with public API only. useStore is exported as a global
import { registerComponentDecorator } from '@workflowbuilder/sdk';
import { ConnectionValidationProvider } from './connection-validation-provider';
export function plugin(): void {
registerComponentDecorator('OptionalHooks', {
content: ConnectionValidationProvider,
});
}
import { useEffect } from 'react';
import { useStore } from '@workflowbuilder/sdk';
const ALLOWED_TARGETS: Record<string, ReadonlyArray<string>> = {
trigger: ['action', 'delay', 'conditional', 'decision', 'ai-agent'],
action: ['action', 'delay', 'conditional', 'decision', 'ai-agent', 'notification'],
// notification is terminal
};
function isAllowed(src?: string, tgt?: string): boolean {
if (!src || !tgt) return false;
if (tgt === 'trigger') return false;
return ALLOWED_TARGETS[src]?.includes(tgt) ?? false;
}
export function ConnectionValidationProvider() {
useEffect(() => {
const original = useStore.getState().onConnect;
useStore.setState({
onConnect: (connection) => {
const { nodes } = useStore.getState();
const src = nodes.find((n) => n.id === connection.source)?.data.type;
const tgt = nodes.find((n) => n.id === connection.target)?.data.type;
if (!isAllowed(src, tgt)) {
console.warn(`[connection-validation] blocked ${src} -> ${tgt}`);
return;
}
original(connection);
},
});
return () => {
useStore.setState({ onConnect: original });
};
}, []);
return null;
}Then add the plugin to the plugins array on <WorkflowBuilder.Root
plugins={[connectionValidationPlugin, /* ... */]}
// ...
/>No SDK changes, no flicker (the invalid edge never enters the store, so there is nothing to clean up after the fact). The one real limitation is that this fires on mouse-up, not during the drag, so xyflow does not light the handle red while you are dragging an invalid connection. That visual cue is exactly what That said, you are right that a proper extension point would be better. Both directions you suggested make sense. We will take care of this on our side soon. Exposing a proper public API for connection validation is on our list now, and your writeup gave us a clear picture of how a consumer wants to use it, which is genuinely helpful. Many thanks again for the detailed suggestions and the time you put into digging through the code! One small caveat on the timing. We are not yet ready to accept external PRs, the contribution process is still being finalized on our side. Once that is in place we will open it up and announce it here. In the meantime, the workaround above should unblock you, and we will follow up when the API lands. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Hi folks,
First of all, really impressive work on Workflow Builder. I have been exploring the codebase and trying to understand the extension points and plugin model in more depth.
I am currently looking for a way to enforce graph validation rules such as preventing certain node types from connecting to other node types. At first glance, I could not find a documented or supported mechanism for denying a connection before the edge is created.
After digging through the codebase, with the help from an agent (i have limited ts i am more backend) here is what I found.
What I observed
In
packages/sdk/src/store/slices/diagram-slice.tsthe store-levelonConnecthandler appears to unconditionally append the edge:From what I can tell,
packages/sdk/src/features/diagram/diagram.tsxwires this directly into<ReactFlow onConnect={…} />.I also noticed that:
isValidConnectionfrom React Flow / xyflow does not seem to be exposed or passed throughonConnectitself is not wrapped withwithOptionalFunctionPluginsgetPaletteData,getTemplates,trackFutureChange,getControlsDotsItems, etc.) via:packages/sdk/src/features/plugins-core/adapters/adapter-functions.tsBecause of this, it seems there is currently no public SDK-level mechanism that allows plugins or decorators to veto or validate a connection before the edge is committed.
Possible workarounds I found
1. Reactive cleanup (works but visually awkward)
A plugin could subscribe to Zustand state changes, inspect newly-added edges, and immediately remove invalid ones.
Something along the lines of:
This technically works, but the edge briefly flashes on screen before being removed.
2. Middleware interception
I also noticed
withInterceptingMiddlewarein:That seems capable of swallowing or intercepting state updates before they land.
This looks cleaner than reactive cleanup, but it also feels like relying on internal implementation details rather than a stable public extension API.
3. SDK-level validation hook (what I think would be ideal)
It feels like the framework could benefit from a first-class connection validation mechanism.
A couple approaches that seem relatively small in scope:
onConnectwithwithOptionalFunctionPluginsso plugins can do something like:
isValidConnectioninto theDiagramcomponent and plugin surface so consumers can implement validation directly using the underlying React Flow APIFrom my reading, this feels like a relatively small SDK enhancement (probably ~10–20 LOC plus a changeset), but it would unlock an important capability for workflow validation and domain constraints.
My question
Would love to hear how the team envisions this kind of validation being implemented.
Beta Was this translation helpful? Give feedback.
All reactions