@octaviaflow/flow-rules 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +312 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +312 -0
- package/dist/rules/flattenNestedWorkflow.d.ts +25 -0
- package/dist/rules/flowShape.d.ts +122 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -47,12 +47,14 @@ __export(exports_src, {
|
|
|
47
47
|
ConditionConfig: () => ConditionConfig,
|
|
48
48
|
DATABASE_CONNECTOR_IDS: () => DATABASE_CONNECTOR_IDS,
|
|
49
49
|
DebugConfig: () => DebugConfig,
|
|
50
|
+
EdgeType: () => EdgeType,
|
|
50
51
|
EmailConfig: () => EmailConfig,
|
|
51
52
|
FieldType: () => FieldType,
|
|
52
53
|
ForEachConfig: () => ForEachConfig,
|
|
53
54
|
JoinType: () => JoinType,
|
|
54
55
|
LogicalOperator: () => LogicalOperator,
|
|
55
56
|
ManualTriggerConfig: () => ManualTriggerConfig,
|
|
57
|
+
NodeStatus: () => NodeStatus,
|
|
56
58
|
QueryMode: () => QueryMode,
|
|
57
59
|
QueryType: () => QueryType,
|
|
58
60
|
STORAGE_CONNECTOR_IDS: () => STORAGE_CONNECTOR_IDS,
|
|
@@ -65,6 +67,8 @@ __export(exports_src, {
|
|
|
65
67
|
collectArrayPaths: () => collectArrayPaths,
|
|
66
68
|
collectArrayPathsFromSample: () => collectArrayPathsFromSample,
|
|
67
69
|
emailDefinition: () => emailDefinition,
|
|
70
|
+
flattenNestedWorkflow: () => flattenNestedWorkflow,
|
|
71
|
+
generateConnectionsFromSteps: () => generateConnectionsFromSteps,
|
|
68
72
|
generateEdgeId: () => generateEdgeId,
|
|
69
73
|
generateStepId: () => generateStepId,
|
|
70
74
|
generateUUIDv4: () => generateUUIDv4,
|
|
@@ -81,7 +85,11 @@ __export(exports_src, {
|
|
|
81
85
|
migrateDelayConfig: () => migrateDelayConfig,
|
|
82
86
|
queryDefinition: () => queryDefinition,
|
|
83
87
|
storageConfigKeys: () => storageConfigKeys,
|
|
88
|
+
transformConnectionToEdge: () => transformConnectionToEdge,
|
|
89
|
+
transformFlowToWorkflow: () => transformFlowToWorkflow,
|
|
90
|
+
transformStepToNode: () => transformStepToNode,
|
|
84
91
|
validateEdge: () => validateEdge,
|
|
92
|
+
validateFlowData: () => validateFlowData,
|
|
85
93
|
validateNode: () => validateNode,
|
|
86
94
|
validateWorkflow: () => validateWorkflow
|
|
87
95
|
});
|
|
@@ -3781,3 +3789,307 @@ function storageConfigKeys() {
|
|
|
3781
3789
|
}
|
|
3782
3790
|
return [...keys];
|
|
3783
3791
|
}
|
|
3792
|
+
// src/catalog/workflow.ts
|
|
3793
|
+
var NodeStatus;
|
|
3794
|
+
((NodeStatus) => {
|
|
3795
|
+
NodeStatus["Idle"] = "idle";
|
|
3796
|
+
NodeStatus["Running"] = "running";
|
|
3797
|
+
NodeStatus["Success"] = "success";
|
|
3798
|
+
NodeStatus["Error"] = "error";
|
|
3799
|
+
NodeStatus["Warning"] = "warning";
|
|
3800
|
+
NodeStatus["Disabled"] = "disabled";
|
|
3801
|
+
})(NodeStatus ||= {});
|
|
3802
|
+
var EdgeType;
|
|
3803
|
+
((EdgeType) => {
|
|
3804
|
+
EdgeType["Default"] = "default";
|
|
3805
|
+
EdgeType["Conditional"] = "conditional";
|
|
3806
|
+
EdgeType["Loop"] = "loop";
|
|
3807
|
+
EdgeType["Error"] = "error";
|
|
3808
|
+
})(EdgeType ||= {});
|
|
3809
|
+
// src/rules/flattenNestedWorkflow.ts
|
|
3810
|
+
function workflowIsNested(workflow) {
|
|
3811
|
+
const stack = [...workflow.steps];
|
|
3812
|
+
while (stack.length > 0) {
|
|
3813
|
+
const s = stack.pop();
|
|
3814
|
+
if (s.branches && Object.keys(s.branches).length > 0)
|
|
3815
|
+
return true;
|
|
3816
|
+
}
|
|
3817
|
+
return false;
|
|
3818
|
+
}
|
|
3819
|
+
function flattenNestedWorkflow(workflow) {
|
|
3820
|
+
if (!workflowIsNested(workflow))
|
|
3821
|
+
return workflow;
|
|
3822
|
+
const flatSteps = [];
|
|
3823
|
+
const flatConns = [...workflow.connections];
|
|
3824
|
+
const groups = [];
|
|
3825
|
+
const walk = (steps, parentIdForSubflow) => {
|
|
3826
|
+
for (const step of steps) {
|
|
3827
|
+
const { branches, ...rest } = step;
|
|
3828
|
+
const flatStep = {
|
|
3829
|
+
...rest,
|
|
3830
|
+
...parentIdForSubflow ? { parentId: parentIdForSubflow } : {}
|
|
3831
|
+
};
|
|
3832
|
+
flatSteps.push(flatStep);
|
|
3833
|
+
if (!branches)
|
|
3834
|
+
continue;
|
|
3835
|
+
if (branches.body && step.type === "group") {
|
|
3836
|
+
const body = branches.body;
|
|
3837
|
+
groups.push({
|
|
3838
|
+
group: flatStep,
|
|
3839
|
+
bodyStepIds: body.steps.map((s) => s.id),
|
|
3840
|
+
bodyConnections: body.connections
|
|
3841
|
+
});
|
|
3842
|
+
for (const c of body.connections)
|
|
3843
|
+
flatConns.push(c);
|
|
3844
|
+
walk(body.steps, step.id);
|
|
3845
|
+
}
|
|
3846
|
+
for (const branchKey of Object.keys(branches)) {
|
|
3847
|
+
if (branchKey === "body" && step.type === "group")
|
|
3848
|
+
continue;
|
|
3849
|
+
const sub = branches[branchKey];
|
|
3850
|
+
if (!sub)
|
|
3851
|
+
continue;
|
|
3852
|
+
for (const c of sub.connections)
|
|
3853
|
+
flatConns.push(c);
|
|
3854
|
+
walk(sub.steps, parentIdForSubflow);
|
|
3855
|
+
}
|
|
3856
|
+
}
|
|
3857
|
+
};
|
|
3858
|
+
walk(workflow.steps, undefined);
|
|
3859
|
+
for (const g of groups) {
|
|
3860
|
+
if (g.bodyStepIds.length === 0)
|
|
3861
|
+
continue;
|
|
3862
|
+
const bodyIdSet = new Set(g.bodyStepIds);
|
|
3863
|
+
const inDeg = new Map;
|
|
3864
|
+
const outDeg = new Map;
|
|
3865
|
+
for (const id of g.bodyStepIds) {
|
|
3866
|
+
inDeg.set(id, 0);
|
|
3867
|
+
outDeg.set(id, 0);
|
|
3868
|
+
}
|
|
3869
|
+
for (const c of g.bodyConnections) {
|
|
3870
|
+
if (bodyIdSet.has(c.sourceStepId)) {
|
|
3871
|
+
outDeg.set(c.sourceStepId, (outDeg.get(c.sourceStepId) ?? 0) + 1);
|
|
3872
|
+
}
|
|
3873
|
+
if (bodyIdSet.has(c.targetStepId)) {
|
|
3874
|
+
inDeg.set(c.targetStepId, (inDeg.get(c.targetStepId) ?? 0) + 1);
|
|
3875
|
+
}
|
|
3876
|
+
}
|
|
3877
|
+
const roots = g.bodyStepIds.filter((id) => (inDeg.get(id) ?? 0) === 0);
|
|
3878
|
+
const leaves = g.bodyStepIds.filter((id) => (outDeg.get(id) ?? 0) === 0);
|
|
3879
|
+
for (const rootId of roots) {
|
|
3880
|
+
flatConns.push({
|
|
3881
|
+
id: `synth-inner-in-${g.group.id}-${rootId}`,
|
|
3882
|
+
sourceStepId: g.group.id,
|
|
3883
|
+
targetStepId: rootId,
|
|
3884
|
+
sourcePort: "_inner_in"
|
|
3885
|
+
});
|
|
3886
|
+
}
|
|
3887
|
+
for (const leafId of leaves) {
|
|
3888
|
+
flatConns.push({
|
|
3889
|
+
id: `synth-inner-out-${leafId}-${g.group.id}`,
|
|
3890
|
+
sourceStepId: leafId,
|
|
3891
|
+
targetStepId: g.group.id,
|
|
3892
|
+
targetPort: "_inner_out"
|
|
3893
|
+
});
|
|
3894
|
+
}
|
|
3895
|
+
}
|
|
3896
|
+
return { ...workflow, steps: flatSteps, connections: flatConns };
|
|
3897
|
+
}
|
|
3898
|
+
|
|
3899
|
+
// src/rules/flowShape.ts
|
|
3900
|
+
function transformStepToNode(step) {
|
|
3901
|
+
const stepType = step.type;
|
|
3902
|
+
const stepActionDefId = step.actionDefinitionId ?? "";
|
|
3903
|
+
const nodeType = stepType === "trigger" ? "trigger" : stepType === "group" || stepActionDefId === "group" ? "group" : "action";
|
|
3904
|
+
let nodeStatus = "idle" /* Idle */;
|
|
3905
|
+
switch (step.status?.toLowerCase()) {
|
|
3906
|
+
case "running":
|
|
3907
|
+
case "executing":
|
|
3908
|
+
nodeStatus = "running" /* Running */;
|
|
3909
|
+
break;
|
|
3910
|
+
case "completed":
|
|
3911
|
+
case "success":
|
|
3912
|
+
nodeStatus = "success" /* Success */;
|
|
3913
|
+
break;
|
|
3914
|
+
case "failed":
|
|
3915
|
+
case "error":
|
|
3916
|
+
nodeStatus = "error" /* Error */;
|
|
3917
|
+
break;
|
|
3918
|
+
case "pending":
|
|
3919
|
+
case "waiting":
|
|
3920
|
+
nodeStatus = "idle" /* Idle */;
|
|
3921
|
+
break;
|
|
3922
|
+
default:
|
|
3923
|
+
nodeStatus = "idle" /* Idle */;
|
|
3924
|
+
}
|
|
3925
|
+
const position = step.position || { x: step.position_x || 0, y: step.position_y || 0 };
|
|
3926
|
+
const posX = typeof position === "object" && position !== null && "x" in position ? position.x : step.position_x || 0;
|
|
3927
|
+
const posY = typeof position === "object" && position !== null && "y" in position ? position.y : step.position_y || 0;
|
|
3928
|
+
const config = step.config || {};
|
|
3929
|
+
const configData = config.data || config || {};
|
|
3930
|
+
const derivedFromName = step.name ? step.name.toLowerCase().replace(/\s+/g, "_") : undefined;
|
|
3931
|
+
const resolvedActionDefinitionId = step.actionDefinitionId || config.connector_id || step.connector_id || derivedFromName || step.type;
|
|
3932
|
+
const actionDef = getActionById(resolvedActionDefinitionId);
|
|
3933
|
+
const node = {
|
|
3934
|
+
id: step.id,
|
|
3935
|
+
type: nodeType,
|
|
3936
|
+
actionDefinitionId: resolvedActionDefinitionId,
|
|
3937
|
+
actionDefinition: actionDef,
|
|
3938
|
+
label: step.label || step.name,
|
|
3939
|
+
description: step.description || config.description || actionDef?.description || "",
|
|
3940
|
+
icon: actionDef?.icon ?? getIconForStepType(step.type),
|
|
3941
|
+
position: {
|
|
3942
|
+
x: posX,
|
|
3943
|
+
y: posY
|
|
3944
|
+
},
|
|
3945
|
+
...step.parentId ? { parentId: step.parentId } : {},
|
|
3946
|
+
...step.extent ? { extent: step.extent } : {},
|
|
3947
|
+
...step.disabled ? { disabled: true } : {},
|
|
3948
|
+
...step.dimensions && nodeType === "group" ? { dimensions: step.dimensions } : nodeType === "group" ? { dimensions: { width: 480, height: 240 } } : {},
|
|
3949
|
+
config: {
|
|
3950
|
+
...configData,
|
|
3951
|
+
timeout: step.timeout_seconds,
|
|
3952
|
+
retryPolicy: step.retry_policy,
|
|
3953
|
+
notes: step.notes,
|
|
3954
|
+
tags: step.tags,
|
|
3955
|
+
stepCount: step.step_count
|
|
3956
|
+
},
|
|
3957
|
+
ports: {
|
|
3958
|
+
input: nodeType === "action" || nodeType === "group" ? [
|
|
3959
|
+
{
|
|
3960
|
+
id: `${step.id}-input`,
|
|
3961
|
+
type: "input",
|
|
3962
|
+
position: "top",
|
|
3963
|
+
connected: false
|
|
3964
|
+
}
|
|
3965
|
+
] : [],
|
|
3966
|
+
output: (() => {
|
|
3967
|
+
if (resolvedActionDefinitionId === "condition") {
|
|
3968
|
+
return [
|
|
3969
|
+
{ id: `${step.id}_output_true`, type: "output", position: "bottom", label: "True", connected: false },
|
|
3970
|
+
{ id: `${step.id}_output_false`, type: "output", position: "bottom", label: "False", connected: false }
|
|
3971
|
+
];
|
|
3972
|
+
}
|
|
3973
|
+
if (resolvedActionDefinitionId === "for_each" || resolvedActionDefinitionId === "for-each") {
|
|
3974
|
+
return [
|
|
3975
|
+
{ id: `${step.id}_output_each`, type: "output", position: "bottom", label: "Each", connected: false },
|
|
3976
|
+
{ id: `${step.id}_output_done`, type: "output", position: "bottom", label: "Done", connected: false }
|
|
3977
|
+
];
|
|
3978
|
+
}
|
|
3979
|
+
if (resolvedActionDefinitionId === "group") {
|
|
3980
|
+
return [
|
|
3981
|
+
{ id: `${step.id}_output_0`, type: "output", position: "bottom", connected: false }
|
|
3982
|
+
];
|
|
3983
|
+
}
|
|
3984
|
+
return [
|
|
3985
|
+
{ id: `${step.id}-output`, type: "output", position: "bottom", connected: false }
|
|
3986
|
+
];
|
|
3987
|
+
})()
|
|
3988
|
+
},
|
|
3989
|
+
configStatus: step.configStatus || undefined,
|
|
3990
|
+
...step.outputSchema ? { outputSchema: step.outputSchema } : {},
|
|
3991
|
+
status: nodeStatus,
|
|
3992
|
+
selected: false,
|
|
3993
|
+
dragging: false,
|
|
3994
|
+
isValid: step.configStatus ? step.configStatus === "valid" : true,
|
|
3995
|
+
expanded: step.expanded,
|
|
3996
|
+
minimized: false,
|
|
3997
|
+
locked: false,
|
|
3998
|
+
createdAt: step.created_at || new Date().toISOString(),
|
|
3999
|
+
updatedAt: step.updated_at || new Date().toISOString()
|
|
4000
|
+
};
|
|
4001
|
+
return node;
|
|
4002
|
+
}
|
|
4003
|
+
function transformConnectionToEdge(connection) {
|
|
4004
|
+
const sourceStepId = connection.sourceStepId || connection.source_step_id;
|
|
4005
|
+
const targetStepId = connection.targetStepId || connection.target_step_id;
|
|
4006
|
+
const sourcePort = connection.sourcePort || connection.source_port || `${sourceStepId}-output`;
|
|
4007
|
+
const targetPort = connection.targetPort || connection.target_port || `${targetStepId}-input`;
|
|
4008
|
+
let edgeType = "default" /* Default */;
|
|
4009
|
+
if (connection.type === "success" || connection.type === "failure") {
|
|
4010
|
+
edgeType = "conditional" /* Conditional */;
|
|
4011
|
+
}
|
|
4012
|
+
return {
|
|
4013
|
+
id: connection.id,
|
|
4014
|
+
source: sourceStepId,
|
|
4015
|
+
target: targetStepId,
|
|
4016
|
+
sourcePort,
|
|
4017
|
+
targetPort,
|
|
4018
|
+
type: edgeType,
|
|
4019
|
+
label: connection.label,
|
|
4020
|
+
selected: false,
|
|
4021
|
+
animated: false,
|
|
4022
|
+
createdAt: new Date().toISOString()
|
|
4023
|
+
};
|
|
4024
|
+
}
|
|
4025
|
+
function generateConnectionsFromSteps(steps) {
|
|
4026
|
+
const connections = [];
|
|
4027
|
+
const sortedSteps = [...steps].sort((a, b) => a.step_count - b.step_count);
|
|
4028
|
+
for (let i = 0;i < sortedSteps.length - 1; i++) {
|
|
4029
|
+
const sourceStep = sortedSteps[i];
|
|
4030
|
+
const targetStep = sortedSteps[i + 1];
|
|
4031
|
+
connections.push({
|
|
4032
|
+
id: `${sourceStep.id}-${targetStep.id}`,
|
|
4033
|
+
source_step_id: sourceStep.id,
|
|
4034
|
+
target_step_id: targetStep.id,
|
|
4035
|
+
source_port: `${sourceStep.id}-output`,
|
|
4036
|
+
target_port: `${targetStep.id}-input`,
|
|
4037
|
+
type: "default"
|
|
4038
|
+
});
|
|
4039
|
+
}
|
|
4040
|
+
return connections;
|
|
4041
|
+
}
|
|
4042
|
+
function getIconForStepType(type) {
|
|
4043
|
+
const iconMap = {
|
|
4044
|
+
trigger: "PlayOutline",
|
|
4045
|
+
transform: "Transform",
|
|
4046
|
+
action: "Application",
|
|
4047
|
+
condition: "DecisionTree",
|
|
4048
|
+
loop: "Repeat"
|
|
4049
|
+
};
|
|
4050
|
+
return iconMap[type] || "Application";
|
|
4051
|
+
}
|
|
4052
|
+
function transformFlowToWorkflow(flow) {
|
|
4053
|
+
const flatWorkflow = flattenNestedWorkflow(flow.workflow);
|
|
4054
|
+
const nodes = flatWorkflow.steps.map((step) => transformStepToNode(step));
|
|
4055
|
+
let connections = flatWorkflow.connections;
|
|
4056
|
+
if (connections === undefined || connections === null) {
|
|
4057
|
+
connections = generateConnectionsFromSteps(flatWorkflow.steps);
|
|
4058
|
+
} else {
|
|
4059
|
+
connections = connections || [];
|
|
4060
|
+
}
|
|
4061
|
+
const edges = connections.map((conn, index) => {
|
|
4062
|
+
if (!conn.id) {
|
|
4063
|
+
conn.id = `edge-${conn.source_step_id}-${conn.target_step_id}-${index}`;
|
|
4064
|
+
}
|
|
4065
|
+
return transformConnectionToEdge(conn);
|
|
4066
|
+
});
|
|
4067
|
+
edges.forEach((edge) => {
|
|
4068
|
+
const sourceNode = nodes.find((n) => n.id === edge.source);
|
|
4069
|
+
const targetNode = nodes.find((n) => n.id === edge.target);
|
|
4070
|
+
if (sourceNode) {
|
|
4071
|
+
const port = sourceNode.ports.output.find((p) => p.id === edge.sourcePort);
|
|
4072
|
+
if (port)
|
|
4073
|
+
port.connected = true;
|
|
4074
|
+
}
|
|
4075
|
+
if (targetNode) {
|
|
4076
|
+
const port = targetNode.ports.input.find((p) => p.id === edge.targetPort);
|
|
4077
|
+
if (port)
|
|
4078
|
+
port.connected = true;
|
|
4079
|
+
}
|
|
4080
|
+
});
|
|
4081
|
+
return {
|
|
4082
|
+
id: flow.workflow.id,
|
|
4083
|
+
name: flow.workflow.name,
|
|
4084
|
+
description: flow.workflow.description,
|
|
4085
|
+
nodes,
|
|
4086
|
+
edges,
|
|
4087
|
+
workflowHistory: flow.workflowHistory || []
|
|
4088
|
+
};
|
|
4089
|
+
}
|
|
4090
|
+
function validateFlowData(flow) {
|
|
4091
|
+
if (!flow || typeof flow !== "object")
|
|
4092
|
+
return false;
|
|
4093
|
+
const f = flow;
|
|
4094
|
+
return typeof f.id === "string" && typeof f.name === "string" && f.workflow !== undefined && typeof f.workflow === "object" && f.workflow !== null && Array.isArray(f.workflow.steps);
|
|
4095
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -40,3 +40,6 @@ export { DATABASE_CONNECTOR_IDS, type FieldMapping, TRANSFORM_OPERATIONS, Transf
|
|
|
40
40
|
export { STORAGE_CONNECTOR_IDS, type StorageExtraField, type StoragePathField, getStorageProvider, storageConfigKeys, } from "./catalog/storage/storageCapabilities";
|
|
41
41
|
export { ManualTriggerConfig } from "./catalog/triggers/manual-trigger/index";
|
|
42
42
|
export { type ManualTriggerConfigType } from "./catalog/triggers/manual-trigger/config";
|
|
43
|
+
export * from "./catalog/workflow";
|
|
44
|
+
export { type FlowConnection, type FlowData, type FlowStep, generateConnectionsFromSteps, transformConnectionToEdge, transformFlowToWorkflow, transformStepToNode, validateFlowData, } from "./rules/flowShape";
|
|
45
|
+
export { flattenNestedWorkflow } from "./rules/flattenNestedWorkflow";
|
package/dist/index.js
CHANGED
|
@@ -3692,6 +3692,310 @@ function storageConfigKeys() {
|
|
|
3692
3692
|
}
|
|
3693
3693
|
return [...keys];
|
|
3694
3694
|
}
|
|
3695
|
+
// src/catalog/workflow.ts
|
|
3696
|
+
var NodeStatus;
|
|
3697
|
+
((NodeStatus) => {
|
|
3698
|
+
NodeStatus["Idle"] = "idle";
|
|
3699
|
+
NodeStatus["Running"] = "running";
|
|
3700
|
+
NodeStatus["Success"] = "success";
|
|
3701
|
+
NodeStatus["Error"] = "error";
|
|
3702
|
+
NodeStatus["Warning"] = "warning";
|
|
3703
|
+
NodeStatus["Disabled"] = "disabled";
|
|
3704
|
+
})(NodeStatus ||= {});
|
|
3705
|
+
var EdgeType;
|
|
3706
|
+
((EdgeType) => {
|
|
3707
|
+
EdgeType["Default"] = "default";
|
|
3708
|
+
EdgeType["Conditional"] = "conditional";
|
|
3709
|
+
EdgeType["Loop"] = "loop";
|
|
3710
|
+
EdgeType["Error"] = "error";
|
|
3711
|
+
})(EdgeType ||= {});
|
|
3712
|
+
// src/rules/flattenNestedWorkflow.ts
|
|
3713
|
+
function workflowIsNested(workflow) {
|
|
3714
|
+
const stack = [...workflow.steps];
|
|
3715
|
+
while (stack.length > 0) {
|
|
3716
|
+
const s = stack.pop();
|
|
3717
|
+
if (s.branches && Object.keys(s.branches).length > 0)
|
|
3718
|
+
return true;
|
|
3719
|
+
}
|
|
3720
|
+
return false;
|
|
3721
|
+
}
|
|
3722
|
+
function flattenNestedWorkflow(workflow) {
|
|
3723
|
+
if (!workflowIsNested(workflow))
|
|
3724
|
+
return workflow;
|
|
3725
|
+
const flatSteps = [];
|
|
3726
|
+
const flatConns = [...workflow.connections];
|
|
3727
|
+
const groups = [];
|
|
3728
|
+
const walk = (steps, parentIdForSubflow) => {
|
|
3729
|
+
for (const step of steps) {
|
|
3730
|
+
const { branches, ...rest } = step;
|
|
3731
|
+
const flatStep = {
|
|
3732
|
+
...rest,
|
|
3733
|
+
...parentIdForSubflow ? { parentId: parentIdForSubflow } : {}
|
|
3734
|
+
};
|
|
3735
|
+
flatSteps.push(flatStep);
|
|
3736
|
+
if (!branches)
|
|
3737
|
+
continue;
|
|
3738
|
+
if (branches.body && step.type === "group") {
|
|
3739
|
+
const body = branches.body;
|
|
3740
|
+
groups.push({
|
|
3741
|
+
group: flatStep,
|
|
3742
|
+
bodyStepIds: body.steps.map((s) => s.id),
|
|
3743
|
+
bodyConnections: body.connections
|
|
3744
|
+
});
|
|
3745
|
+
for (const c of body.connections)
|
|
3746
|
+
flatConns.push(c);
|
|
3747
|
+
walk(body.steps, step.id);
|
|
3748
|
+
}
|
|
3749
|
+
for (const branchKey of Object.keys(branches)) {
|
|
3750
|
+
if (branchKey === "body" && step.type === "group")
|
|
3751
|
+
continue;
|
|
3752
|
+
const sub = branches[branchKey];
|
|
3753
|
+
if (!sub)
|
|
3754
|
+
continue;
|
|
3755
|
+
for (const c of sub.connections)
|
|
3756
|
+
flatConns.push(c);
|
|
3757
|
+
walk(sub.steps, parentIdForSubflow);
|
|
3758
|
+
}
|
|
3759
|
+
}
|
|
3760
|
+
};
|
|
3761
|
+
walk(workflow.steps, undefined);
|
|
3762
|
+
for (const g of groups) {
|
|
3763
|
+
if (g.bodyStepIds.length === 0)
|
|
3764
|
+
continue;
|
|
3765
|
+
const bodyIdSet = new Set(g.bodyStepIds);
|
|
3766
|
+
const inDeg = new Map;
|
|
3767
|
+
const outDeg = new Map;
|
|
3768
|
+
for (const id of g.bodyStepIds) {
|
|
3769
|
+
inDeg.set(id, 0);
|
|
3770
|
+
outDeg.set(id, 0);
|
|
3771
|
+
}
|
|
3772
|
+
for (const c of g.bodyConnections) {
|
|
3773
|
+
if (bodyIdSet.has(c.sourceStepId)) {
|
|
3774
|
+
outDeg.set(c.sourceStepId, (outDeg.get(c.sourceStepId) ?? 0) + 1);
|
|
3775
|
+
}
|
|
3776
|
+
if (bodyIdSet.has(c.targetStepId)) {
|
|
3777
|
+
inDeg.set(c.targetStepId, (inDeg.get(c.targetStepId) ?? 0) + 1);
|
|
3778
|
+
}
|
|
3779
|
+
}
|
|
3780
|
+
const roots = g.bodyStepIds.filter((id) => (inDeg.get(id) ?? 0) === 0);
|
|
3781
|
+
const leaves = g.bodyStepIds.filter((id) => (outDeg.get(id) ?? 0) === 0);
|
|
3782
|
+
for (const rootId of roots) {
|
|
3783
|
+
flatConns.push({
|
|
3784
|
+
id: `synth-inner-in-${g.group.id}-${rootId}`,
|
|
3785
|
+
sourceStepId: g.group.id,
|
|
3786
|
+
targetStepId: rootId,
|
|
3787
|
+
sourcePort: "_inner_in"
|
|
3788
|
+
});
|
|
3789
|
+
}
|
|
3790
|
+
for (const leafId of leaves) {
|
|
3791
|
+
flatConns.push({
|
|
3792
|
+
id: `synth-inner-out-${leafId}-${g.group.id}`,
|
|
3793
|
+
sourceStepId: leafId,
|
|
3794
|
+
targetStepId: g.group.id,
|
|
3795
|
+
targetPort: "_inner_out"
|
|
3796
|
+
});
|
|
3797
|
+
}
|
|
3798
|
+
}
|
|
3799
|
+
return { ...workflow, steps: flatSteps, connections: flatConns };
|
|
3800
|
+
}
|
|
3801
|
+
|
|
3802
|
+
// src/rules/flowShape.ts
|
|
3803
|
+
function transformStepToNode(step) {
|
|
3804
|
+
const stepType = step.type;
|
|
3805
|
+
const stepActionDefId = step.actionDefinitionId ?? "";
|
|
3806
|
+
const nodeType = stepType === "trigger" ? "trigger" : stepType === "group" || stepActionDefId === "group" ? "group" : "action";
|
|
3807
|
+
let nodeStatus = "idle" /* Idle */;
|
|
3808
|
+
switch (step.status?.toLowerCase()) {
|
|
3809
|
+
case "running":
|
|
3810
|
+
case "executing":
|
|
3811
|
+
nodeStatus = "running" /* Running */;
|
|
3812
|
+
break;
|
|
3813
|
+
case "completed":
|
|
3814
|
+
case "success":
|
|
3815
|
+
nodeStatus = "success" /* Success */;
|
|
3816
|
+
break;
|
|
3817
|
+
case "failed":
|
|
3818
|
+
case "error":
|
|
3819
|
+
nodeStatus = "error" /* Error */;
|
|
3820
|
+
break;
|
|
3821
|
+
case "pending":
|
|
3822
|
+
case "waiting":
|
|
3823
|
+
nodeStatus = "idle" /* Idle */;
|
|
3824
|
+
break;
|
|
3825
|
+
default:
|
|
3826
|
+
nodeStatus = "idle" /* Idle */;
|
|
3827
|
+
}
|
|
3828
|
+
const position = step.position || { x: step.position_x || 0, y: step.position_y || 0 };
|
|
3829
|
+
const posX = typeof position === "object" && position !== null && "x" in position ? position.x : step.position_x || 0;
|
|
3830
|
+
const posY = typeof position === "object" && position !== null && "y" in position ? position.y : step.position_y || 0;
|
|
3831
|
+
const config = step.config || {};
|
|
3832
|
+
const configData = config.data || config || {};
|
|
3833
|
+
const derivedFromName = step.name ? step.name.toLowerCase().replace(/\s+/g, "_") : undefined;
|
|
3834
|
+
const resolvedActionDefinitionId = step.actionDefinitionId || config.connector_id || step.connector_id || derivedFromName || step.type;
|
|
3835
|
+
const actionDef = getActionById(resolvedActionDefinitionId);
|
|
3836
|
+
const node = {
|
|
3837
|
+
id: step.id,
|
|
3838
|
+
type: nodeType,
|
|
3839
|
+
actionDefinitionId: resolvedActionDefinitionId,
|
|
3840
|
+
actionDefinition: actionDef,
|
|
3841
|
+
label: step.label || step.name,
|
|
3842
|
+
description: step.description || config.description || actionDef?.description || "",
|
|
3843
|
+
icon: actionDef?.icon ?? getIconForStepType(step.type),
|
|
3844
|
+
position: {
|
|
3845
|
+
x: posX,
|
|
3846
|
+
y: posY
|
|
3847
|
+
},
|
|
3848
|
+
...step.parentId ? { parentId: step.parentId } : {},
|
|
3849
|
+
...step.extent ? { extent: step.extent } : {},
|
|
3850
|
+
...step.disabled ? { disabled: true } : {},
|
|
3851
|
+
...step.dimensions && nodeType === "group" ? { dimensions: step.dimensions } : nodeType === "group" ? { dimensions: { width: 480, height: 240 } } : {},
|
|
3852
|
+
config: {
|
|
3853
|
+
...configData,
|
|
3854
|
+
timeout: step.timeout_seconds,
|
|
3855
|
+
retryPolicy: step.retry_policy,
|
|
3856
|
+
notes: step.notes,
|
|
3857
|
+
tags: step.tags,
|
|
3858
|
+
stepCount: step.step_count
|
|
3859
|
+
},
|
|
3860
|
+
ports: {
|
|
3861
|
+
input: nodeType === "action" || nodeType === "group" ? [
|
|
3862
|
+
{
|
|
3863
|
+
id: `${step.id}-input`,
|
|
3864
|
+
type: "input",
|
|
3865
|
+
position: "top",
|
|
3866
|
+
connected: false
|
|
3867
|
+
}
|
|
3868
|
+
] : [],
|
|
3869
|
+
output: (() => {
|
|
3870
|
+
if (resolvedActionDefinitionId === "condition") {
|
|
3871
|
+
return [
|
|
3872
|
+
{ id: `${step.id}_output_true`, type: "output", position: "bottom", label: "True", connected: false },
|
|
3873
|
+
{ id: `${step.id}_output_false`, type: "output", position: "bottom", label: "False", connected: false }
|
|
3874
|
+
];
|
|
3875
|
+
}
|
|
3876
|
+
if (resolvedActionDefinitionId === "for_each" || resolvedActionDefinitionId === "for-each") {
|
|
3877
|
+
return [
|
|
3878
|
+
{ id: `${step.id}_output_each`, type: "output", position: "bottom", label: "Each", connected: false },
|
|
3879
|
+
{ id: `${step.id}_output_done`, type: "output", position: "bottom", label: "Done", connected: false }
|
|
3880
|
+
];
|
|
3881
|
+
}
|
|
3882
|
+
if (resolvedActionDefinitionId === "group") {
|
|
3883
|
+
return [
|
|
3884
|
+
{ id: `${step.id}_output_0`, type: "output", position: "bottom", connected: false }
|
|
3885
|
+
];
|
|
3886
|
+
}
|
|
3887
|
+
return [
|
|
3888
|
+
{ id: `${step.id}-output`, type: "output", position: "bottom", connected: false }
|
|
3889
|
+
];
|
|
3890
|
+
})()
|
|
3891
|
+
},
|
|
3892
|
+
configStatus: step.configStatus || undefined,
|
|
3893
|
+
...step.outputSchema ? { outputSchema: step.outputSchema } : {},
|
|
3894
|
+
status: nodeStatus,
|
|
3895
|
+
selected: false,
|
|
3896
|
+
dragging: false,
|
|
3897
|
+
isValid: step.configStatus ? step.configStatus === "valid" : true,
|
|
3898
|
+
expanded: step.expanded,
|
|
3899
|
+
minimized: false,
|
|
3900
|
+
locked: false,
|
|
3901
|
+
createdAt: step.created_at || new Date().toISOString(),
|
|
3902
|
+
updatedAt: step.updated_at || new Date().toISOString()
|
|
3903
|
+
};
|
|
3904
|
+
return node;
|
|
3905
|
+
}
|
|
3906
|
+
function transformConnectionToEdge(connection) {
|
|
3907
|
+
const sourceStepId = connection.sourceStepId || connection.source_step_id;
|
|
3908
|
+
const targetStepId = connection.targetStepId || connection.target_step_id;
|
|
3909
|
+
const sourcePort = connection.sourcePort || connection.source_port || `${sourceStepId}-output`;
|
|
3910
|
+
const targetPort = connection.targetPort || connection.target_port || `${targetStepId}-input`;
|
|
3911
|
+
let edgeType = "default" /* Default */;
|
|
3912
|
+
if (connection.type === "success" || connection.type === "failure") {
|
|
3913
|
+
edgeType = "conditional" /* Conditional */;
|
|
3914
|
+
}
|
|
3915
|
+
return {
|
|
3916
|
+
id: connection.id,
|
|
3917
|
+
source: sourceStepId,
|
|
3918
|
+
target: targetStepId,
|
|
3919
|
+
sourcePort,
|
|
3920
|
+
targetPort,
|
|
3921
|
+
type: edgeType,
|
|
3922
|
+
label: connection.label,
|
|
3923
|
+
selected: false,
|
|
3924
|
+
animated: false,
|
|
3925
|
+
createdAt: new Date().toISOString()
|
|
3926
|
+
};
|
|
3927
|
+
}
|
|
3928
|
+
function generateConnectionsFromSteps(steps) {
|
|
3929
|
+
const connections = [];
|
|
3930
|
+
const sortedSteps = [...steps].sort((a, b) => a.step_count - b.step_count);
|
|
3931
|
+
for (let i = 0;i < sortedSteps.length - 1; i++) {
|
|
3932
|
+
const sourceStep = sortedSteps[i];
|
|
3933
|
+
const targetStep = sortedSteps[i + 1];
|
|
3934
|
+
connections.push({
|
|
3935
|
+
id: `${sourceStep.id}-${targetStep.id}`,
|
|
3936
|
+
source_step_id: sourceStep.id,
|
|
3937
|
+
target_step_id: targetStep.id,
|
|
3938
|
+
source_port: `${sourceStep.id}-output`,
|
|
3939
|
+
target_port: `${targetStep.id}-input`,
|
|
3940
|
+
type: "default"
|
|
3941
|
+
});
|
|
3942
|
+
}
|
|
3943
|
+
return connections;
|
|
3944
|
+
}
|
|
3945
|
+
function getIconForStepType(type) {
|
|
3946
|
+
const iconMap = {
|
|
3947
|
+
trigger: "PlayOutline",
|
|
3948
|
+
transform: "Transform",
|
|
3949
|
+
action: "Application",
|
|
3950
|
+
condition: "DecisionTree",
|
|
3951
|
+
loop: "Repeat"
|
|
3952
|
+
};
|
|
3953
|
+
return iconMap[type] || "Application";
|
|
3954
|
+
}
|
|
3955
|
+
function transformFlowToWorkflow(flow) {
|
|
3956
|
+
const flatWorkflow = flattenNestedWorkflow(flow.workflow);
|
|
3957
|
+
const nodes = flatWorkflow.steps.map((step) => transformStepToNode(step));
|
|
3958
|
+
let connections = flatWorkflow.connections;
|
|
3959
|
+
if (connections === undefined || connections === null) {
|
|
3960
|
+
connections = generateConnectionsFromSteps(flatWorkflow.steps);
|
|
3961
|
+
} else {
|
|
3962
|
+
connections = connections || [];
|
|
3963
|
+
}
|
|
3964
|
+
const edges = connections.map((conn, index) => {
|
|
3965
|
+
if (!conn.id) {
|
|
3966
|
+
conn.id = `edge-${conn.source_step_id}-${conn.target_step_id}-${index}`;
|
|
3967
|
+
}
|
|
3968
|
+
return transformConnectionToEdge(conn);
|
|
3969
|
+
});
|
|
3970
|
+
edges.forEach((edge) => {
|
|
3971
|
+
const sourceNode = nodes.find((n) => n.id === edge.source);
|
|
3972
|
+
const targetNode = nodes.find((n) => n.id === edge.target);
|
|
3973
|
+
if (sourceNode) {
|
|
3974
|
+
const port = sourceNode.ports.output.find((p) => p.id === edge.sourcePort);
|
|
3975
|
+
if (port)
|
|
3976
|
+
port.connected = true;
|
|
3977
|
+
}
|
|
3978
|
+
if (targetNode) {
|
|
3979
|
+
const port = targetNode.ports.input.find((p) => p.id === edge.targetPort);
|
|
3980
|
+
if (port)
|
|
3981
|
+
port.connected = true;
|
|
3982
|
+
}
|
|
3983
|
+
});
|
|
3984
|
+
return {
|
|
3985
|
+
id: flow.workflow.id,
|
|
3986
|
+
name: flow.workflow.name,
|
|
3987
|
+
description: flow.workflow.description,
|
|
3988
|
+
nodes,
|
|
3989
|
+
edges,
|
|
3990
|
+
workflowHistory: flow.workflowHistory || []
|
|
3991
|
+
};
|
|
3992
|
+
}
|
|
3993
|
+
function validateFlowData(flow) {
|
|
3994
|
+
if (!flow || typeof flow !== "object")
|
|
3995
|
+
return false;
|
|
3996
|
+
const f = flow;
|
|
3997
|
+
return typeof f.id === "string" && typeof f.name === "string" && f.workflow !== undefined && typeof f.workflow === "object" && f.workflow !== null && Array.isArray(f.workflow.steps);
|
|
3998
|
+
}
|
|
3695
3999
|
export {
|
|
3696
4000
|
ActionCategory,
|
|
3697
4001
|
ActionStatus,
|
|
@@ -3701,12 +4005,14 @@ export {
|
|
|
3701
4005
|
ConditionConfig,
|
|
3702
4006
|
DATABASE_CONNECTOR_IDS,
|
|
3703
4007
|
DebugConfig,
|
|
4008
|
+
EdgeType,
|
|
3704
4009
|
EmailConfig,
|
|
3705
4010
|
FieldType,
|
|
3706
4011
|
ForEachConfig,
|
|
3707
4012
|
JoinType,
|
|
3708
4013
|
LogicalOperator,
|
|
3709
4014
|
ManualTriggerConfig,
|
|
4015
|
+
NodeStatus,
|
|
3710
4016
|
QueryMode,
|
|
3711
4017
|
QueryType,
|
|
3712
4018
|
STORAGE_CONNECTOR_IDS,
|
|
@@ -3719,6 +4025,8 @@ export {
|
|
|
3719
4025
|
collectArrayPaths,
|
|
3720
4026
|
collectArrayPathsFromSample,
|
|
3721
4027
|
emailDefinition,
|
|
4028
|
+
flattenNestedWorkflow,
|
|
4029
|
+
generateConnectionsFromSteps,
|
|
3722
4030
|
generateEdgeId,
|
|
3723
4031
|
generateStepId,
|
|
3724
4032
|
generateUUIDv4,
|
|
@@ -3735,7 +4043,11 @@ export {
|
|
|
3735
4043
|
migrateDelayConfig,
|
|
3736
4044
|
queryDefinition,
|
|
3737
4045
|
storageConfigKeys,
|
|
4046
|
+
transformConnectionToEdge,
|
|
4047
|
+
transformFlowToWorkflow,
|
|
4048
|
+
transformStepToNode,
|
|
3738
4049
|
validateEdge,
|
|
4050
|
+
validateFlowData,
|
|
3739
4051
|
validateNode,
|
|
3740
4052
|
validateWorkflow
|
|
3741
4053
|
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
type AnyConn = {
|
|
2
|
+
id?: string;
|
|
3
|
+
sourceStepId: string;
|
|
4
|
+
targetStepId: string;
|
|
5
|
+
sourcePort?: string;
|
|
6
|
+
targetPort?: string;
|
|
7
|
+
[k: string]: unknown;
|
|
8
|
+
};
|
|
9
|
+
type AnyStep = {
|
|
10
|
+
id: string;
|
|
11
|
+
type: string;
|
|
12
|
+
parentId?: string;
|
|
13
|
+
branches?: Record<string, {
|
|
14
|
+
steps: AnyStep[];
|
|
15
|
+
connections: AnyConn[];
|
|
16
|
+
}>;
|
|
17
|
+
[k: string]: unknown;
|
|
18
|
+
};
|
|
19
|
+
type AnyWorkflow = {
|
|
20
|
+
steps: AnyStep[];
|
|
21
|
+
connections: AnyConn[];
|
|
22
|
+
[k: string]: unknown;
|
|
23
|
+
};
|
|
24
|
+
export declare function flattenNestedWorkflow<W extends AnyWorkflow>(workflow: W): W;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The PERSISTED flow shape, and how it becomes the canvas shape.
|
|
3
|
+
*
|
|
4
|
+
* A flow is stored as `steps` + `connections`; the rules engine and anything
|
|
5
|
+
* that reasons about a flow work in `nodes` + `edges`. This is the ONE
|
|
6
|
+
* conversion between them.
|
|
7
|
+
*
|
|
8
|
+
* It lived in octaviaflow-ui because only the editor ever read a flow. The
|
|
9
|
+
* server has to now: `check_flow` runs the same rules against a STORED flow
|
|
10
|
+
* and cannot do that without converting it the same way. A second converter
|
|
11
|
+
* would make an agent's verdict quietly disagree with the editor's, which is
|
|
12
|
+
* the failure this package exists to prevent.
|
|
13
|
+
*
|
|
14
|
+
* Two UI-only pieces did not come: `loadFlowFromJSON` (an HTTP fetch) and
|
|
15
|
+
* `calculateFitViewport` (canvas geometry) stay in the editor.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Flow Loader Utilities
|
|
19
|
+
*
|
|
20
|
+
* Utilities for loading, transforming, and managing workflow flows
|
|
21
|
+
* from various data sources (JSON files, APIs, etc.)
|
|
22
|
+
*/
|
|
23
|
+
import { type WorkflowNode, type WorkflowEdge } from "../catalog/workflow";
|
|
24
|
+
/**
|
|
25
|
+
* Flow data structure from database/JSON
|
|
26
|
+
*/
|
|
27
|
+
export interface FlowData {
|
|
28
|
+
id: string;
|
|
29
|
+
name: string;
|
|
30
|
+
description: string;
|
|
31
|
+
status: string;
|
|
32
|
+
owner: {
|
|
33
|
+
user_id: string;
|
|
34
|
+
user_name: string;
|
|
35
|
+
user_email: string;
|
|
36
|
+
permissions: string[];
|
|
37
|
+
};
|
|
38
|
+
collaborators: Array<{
|
|
39
|
+
user_id: string;
|
|
40
|
+
user_name: string;
|
|
41
|
+
user_email: string;
|
|
42
|
+
permissions: string[];
|
|
43
|
+
added_at: string;
|
|
44
|
+
added_by: string;
|
|
45
|
+
}>;
|
|
46
|
+
workflow: {
|
|
47
|
+
id: string;
|
|
48
|
+
name: string;
|
|
49
|
+
description: string;
|
|
50
|
+
status: string;
|
|
51
|
+
steps: FlowStep[];
|
|
52
|
+
connections?: FlowConnection[];
|
|
53
|
+
};
|
|
54
|
+
created_at?: string;
|
|
55
|
+
updated_at?: string;
|
|
56
|
+
}
|
|
57
|
+
export interface FlowStep {
|
|
58
|
+
id: string;
|
|
59
|
+
name: string;
|
|
60
|
+
description: string;
|
|
61
|
+
type: 'trigger' | 'transform' | 'action' | 'condition' | 'loop';
|
|
62
|
+
config: {
|
|
63
|
+
connector_id?: string;
|
|
64
|
+
connection_id?: string;
|
|
65
|
+
data?: Record<string, unknown>;
|
|
66
|
+
};
|
|
67
|
+
step_count: number;
|
|
68
|
+
position_x: number;
|
|
69
|
+
position_y: number;
|
|
70
|
+
expanded: boolean;
|
|
71
|
+
timeout_seconds?: number;
|
|
72
|
+
retry_policy?: {
|
|
73
|
+
enabled: boolean;
|
|
74
|
+
max_retries: number;
|
|
75
|
+
retry_delay_seconds: number;
|
|
76
|
+
backoff_strategy: string;
|
|
77
|
+
};
|
|
78
|
+
status: string;
|
|
79
|
+
notes?: string;
|
|
80
|
+
tags?: string[];
|
|
81
|
+
created_at?: string;
|
|
82
|
+
created_by?: string;
|
|
83
|
+
updated_at?: string;
|
|
84
|
+
updated_by?: string;
|
|
85
|
+
}
|
|
86
|
+
export interface FlowConnection {
|
|
87
|
+
id: string;
|
|
88
|
+
source_step_id: string;
|
|
89
|
+
target_step_id: string;
|
|
90
|
+
source_port?: string;
|
|
91
|
+
target_port?: string;
|
|
92
|
+
condition?: string;
|
|
93
|
+
label?: string;
|
|
94
|
+
type?: 'default' | 'success' | 'failure';
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Transform flow step to WorkflowNode
|
|
98
|
+
*/
|
|
99
|
+
export declare function transformStepToNode(step: FlowStep): WorkflowNode;
|
|
100
|
+
/**
|
|
101
|
+
* Transform flow connection to WorkflowEdge
|
|
102
|
+
*/
|
|
103
|
+
export declare function transformConnectionToEdge(connection: FlowConnection): WorkflowEdge;
|
|
104
|
+
/**
|
|
105
|
+
* Auto-generate connections from step order if not provided
|
|
106
|
+
*/
|
|
107
|
+
export declare function generateConnectionsFromSteps(steps: FlowStep[]): FlowConnection[];
|
|
108
|
+
/**
|
|
109
|
+
* Transform complete flow data to workflow state
|
|
110
|
+
*/
|
|
111
|
+
export declare function transformFlowToWorkflow(flow: FlowData): {
|
|
112
|
+
id: string;
|
|
113
|
+
name: string;
|
|
114
|
+
description: string;
|
|
115
|
+
nodes: any;
|
|
116
|
+
edges: any;
|
|
117
|
+
workflowHistory: any;
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* Validate flow data structure
|
|
121
|
+
*/
|
|
122
|
+
export declare function validateFlowData(flow: unknown): flow is FlowData;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@octaviaflow/flow-rules",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "The flow action catalog and the rules engine behind Flow Doctor \u2014 one definition of what a step accepts and what makes a flow valid, shared by the editor and the server",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|