Search code examples
node.jsjsonplantumlxstate

type error when trying to use xstate-plantuml to visualise the FSM diagram


I'm trying to create a script to automate the visualisation of a xstate FSM. As there is currently now way to make an http request to the xstate-visualiser. I'm using xstate-plantuml to make a diagram. Because it expects JSON as an input, I've used JSON.stringify() on my machine object and pass the code to the visualize.

import visualize from "xstate-plantuml";
import encoder from "plantuml-encoder";

import machine from "./machine.json";

const puml = visualize(machine);
const url = "http://www.plantuml.com/plantuml/svg/" + encoder.encode(puml);

const img = document.createElement("img");
img.src = url;

document.querySelector("#app").appendChild(img);
console.log(puml);

I keep getting a TypeError: value.replace is not a function error, I suspect it's an issue with my JSON structure, because it seems to work with the original example it came with. Unfortunately the error makes no mention of location besides a line in an npm package.

{
    "id": "runner",
    "initial": "setup",
    "strict": true,
    "states": {
        "setup": {
            "entry": [],
            "on": {
                "ERROR": {
                    "target": "error"
                },
                "TERMINATED": {
                    "target": "terminated"
                },
                "RUNNING": {
                    "target": "running"
                },
                "HANDLER_EXIT": {
                    "target": "handleExit"
                }
            },
            "id": "runTaskFSM",
            "initial": "pullPackage",
            "states": {
                "pullPackage": {
                    "entry": ["reportMachineStart", "pullPackage"],
                    "on": {
                        "MAKE_IO_DIR": {
                            "target": "mkIODir"
                        }
                    }
                },
                "mkIODir": {
                    "entry": ["mkIODir"],
                    "on": {
                        "WRITE_TASK_JSON": {
                            "target": "writeTaskDataJSON"
                        }
                    }
                },
                "writeTaskDataJSON": {
                    "entry": ["writeTaskDataJSON"],
                    "on": {
                        "DOWNLOAD_INPUTS": {
                            "target": "downloadInputs"
                        }
                    }
                },
                "downloadInputs": {
                    "entry": ["downloadInputs"],
                    "on": {
                        "LINK_INPUTS": {
                            "target": "linkInputs"
                        }
                    }
                },
                "linkInputs": {
                    "entry": ["linkInputs"],
                    "on": {
                        "DOWNLOAD_RESOURCES": {
                            "target": "downloadResources"
                        }
                    }
                },
                "downloadResources": {
                    "entry": ["downloadResources"],
                    "on": {
                        "TASKDIR_PERMISSIONS": {
                            "target": "chmodTaskDir"
                        }
                    }
                },
                "chmodTaskDir": {
                    "entry": ["chmodTaskDir"],
                    "on": {
                        "JOB_TIMEOUT": {
                            "target": "jobTimeout"
                        }
                    }
                },
                "jobTimeout": {
                    "entry": ["jobTimeout"],
                    "on": {
                        "INIT_HANDLER": {
                            "target": "initHandler"
                        }
                    }
                },
                "initHandler": {
                    "entry": ["initHandler"],
                    "on": {
                        "START_JOB": {
                            "target": "startJob"
                        }
                    }
                },
                "startJob": {
                    "entry": ["reportUserStart", "taskRunning", "startJob"],
                    "on": {
                        "TASK_RUNNING": {
                            "target": "taskRunning"
                        }
                    }
                },
                "taskRunning": {
                    "entry": [{
                        "type": "xstate.send",
                        "event": {
                            "type": "RUNNING"
                        },
                        "id": "RUNNING"
                    }]
                }
            }
        },
        "running": {
            "entry": [],
            "on": {
                "HANDLER_EXIT": {
                    "target": "handleExit"
                },
                "ERROR": {
                    "target": "error"
                },
                "TERMINATED": {
                    "target": "terminated"
                }
            }
        },
        "handleExit": {
            "entry": ["reportUserStop", "taskHandleExit"],
            "on": {
                "COMPLETE": {
                    "target": "complete"
                },
                "ERROR": {
                    "target": "error"
                }
            }
        },
        "complete": {
            "entry": ["taskComplete"],
            "on": {
                "DESTROY": {
                    "target": "destroy"
                }
            }
        },
        "error": {
            "entry": ["taskError"],
            "on": {
                "DESTROY": {
                    "target": "destroy"
                }
            }
        },
        "terminated": {
            "entry": ["reportUserStop", "terminate"],
            "on": {
                "DESTROY": {
                    "target": "destroy"
                }
            }
        },
        "destroy": {
            "entry": ["reportMachineStop", "destroyTask"],
            "type": "final"
        }
    }
}

Edit jolly-spence-tr8ff


Solution

  • It seems like the library is unable to handle the entry nodes in your json representation, for example, if I take your example, and remove all the entry attributes, then it works, here is what it looks like then:

    {
      "id": "runner",
      "initial": "setup",
      "states": {
        "setup": {
          "on": {
            "ERROR": {
              "target": "error"
            },
            "TERMINATED": {
              "target": "terminated"
            },
            "RUNNING": {
              "target": "running"
            },
            "HANDLER_EXIT": {
              "target": "handleExit"
            }
          },
          "id": "runTaskFSM",
          "initial": "pullPackage",
          "states": {
            "pullPackage": {
              "on": {
                "MAKE_IO_DIR": {
                  "target": "mkIODir"
                }
              }
            },
            "mkIODir": {
              "on": {
                "WRITE_TASK_JSON": {
                  "target": "writeTaskDataJSON"
                }
              }
            },
            "writeTaskDataJSON": {
              "on": {
                "DOWNLOAD_INPUTS": {
                  "target": "downloadInputs"
                }
              }
            },
            "downloadInputs": {
              "on": {
                "LINK_INPUTS": {
                  "target": "linkInputs"
                }
              }
            },
            "linkInputs": {
              "on": {
                "DOWNLOAD_RESOURCES": {
                  "target": "downloadResources"
                }
              }
            },
            "downloadResources": {
              "on": {
                "TASKDIR_PERMISSIONS": {
                  "target": "chmodTaskDir"
                }
              }
            },
            "chmodTaskDir": {
              "on": {
                "JOB_TIMEOUT": {
                  "target": "jobTimeout"
                }
              }
            },
            "jobTimeout": {
              "on": {
                "INIT_HANDLER": {
                  "target": "initHandler"
                }
              }
            },
            "initHandler": {
              "on": {
                "START_JOB": {
                  "target": "startJob"
                }
              }
            },
            "startJob": {
              "on": {
                "TASK_RUNNING": {
                  "target": "taskRunning"
                }
              }
            },
            "taskRunning": {}
          }
        },
        "running": {
          "on": {
            "HANDLER_EXIT": {
              "target": "handleExit"
            },
            "ERROR": {
              "target": "error"
            },
            "TERMINATED": {
              "target": "terminated"
            }
          }
        },
        "handleExit": {
          "on": {
            "COMPLETE": {
              "target": "complete"
            },
            "ERROR": {
              "target": "error"
            }
          }
        },
        "complete": {
          "on": {
            "DESTROY": {
              "target": "destroy"
            }
          }
        },
        "error": {
          "on": {
            "DESTROY": {
              "target": "destroy"
            }
          }
        },
        "terminated": {
          "on": {
            "DESTROY": {
              "target": "destroy"
            }
          }
        },
        "destroy": {
          "type": "final"
        }
      }
    }
    

    And the sandbox in working condition here:

    Edit wonderful-bas-s9cni

    Now the tricky part is determining if your use of the library is correct or if the library is lacking in implementation, the error occurs on this line in the library.