fulcro

tony.kay 2025-09-10T18:20:54.080239Z

I started a routing design doc for statecharts, to explore the issues aloud. Comments welcome. https://github.com/fulcrologic/statecharts/blob/ui-routing-2/src/main/com/fulcrologic/statecharts/integration/fulcro/routing-design.adoc

Eric Dvorsak 2025-09-10T18:54:12.161689Z

my main m,otivation for switching from dynamic routing to statechart routing would be indeed that it works even for routing states that are not part of the URL (or only part of the query params), so that it can even be used for wizards. in a wizard you might actually still want to intercept the back button, either to go to the previous step or to prevent doing so

tony.kay 2025-09-11T04:45:32.614489Z

that’s a great point. I was just about to start fleshing out the URL/browser integration bits. So, having a popstate event handler that just notifies the chart. My main concern with this is that I want the chart itself to be the single source of truth for the URL. If you’re in a state, then the URL reflects it and vice versa. Auto-healing the browser forced forward/back popstate event (which have pushed you out of sync) is my top priority. But obviously giving you some way to reflect your state in the URL that is synchronized is ideal so that you can easily implement these kinds of things.

Eric Dvorsak 2025-09-10T18:47:15.593189Z

If I want to use fulcro-state/mark-complete* in a state chart, I just assign the result with (ops/assign :fulcro/state (fulcro-state/mark-complete* (:fulcro/state data) ident) basically the question applied to any of fulcro utils that are expected to be used with swap! on the app-state within a mutation

tony.kay 2025-09-11T04:55:36.824559Z

You want apply-action

tony.kay 2025-09-11T04:56:19.553109Z

assign is for putting a value in a particular location. apply-action is basically swap! on the atom.

tony.kay 2025-09-11T04:57:15.298449Z

The naming comes from mutations…“`apply` what would normally be the action section of a mutation)

Eric Dvorsak 2025-09-11T06:17:35.125299Z

Yeah ofc I even used it and forgot about it. assign definitely felt wrong 🤦

Eric Dvorsak 2025-09-10T19:54:04.286649Z

Regarding statechart session-id for statecarts associated to components I wonder why the default isn't to use the ident of the component (in use-statechart and statechart routing): • with a constant session-id I get the feeling that if I have a long running thing that will send an event later, if the session is reopened (same component but different entity for instance) it may wrongly handle the event. • with a random-id I have a coordination problem: what if I want to combine invoke (from a parent for instance) and use-statechart? so that my statechart still exit on unmout but the parent is aware of what happens A default session-id based on ident would support both, and I can even have a routing state invoking my component statechart AND have use-statechart in the child component to manage the lifecycle of the child chart and provide the easy access to config and local-state (provided the :statechart vs ro/statechart split is fixed)

Eric Dvorsak 2025-09-11T07:08:48.470979Z

> In invocations the statechart system actually terminates the child statechart. I assume what I missed is > If the state of the (local) machine is exited before receiving the done event, then it cancels the invoked service > and ignores any events received by it after that point. so basically when you leave the istate it's calling sp/exit! and cleans the working memory, following the code the on-exit handlers are being called and that seems to be the only things? which means when a statechart is invoked one has to use on-exit elements rather than a final state for the cleanup? or shouldn't final state also be called?

Eric Dvorsak 2025-09-11T07:10:26.921139Z

a helper function the “send event to parent”where is this one? are you refering to doing something like:

(Send {:event      :child.left-y
                 :type       ::sc/chart
                 :targetexpr (fn [env data]
                               (e/parent-session-id env))})

tony.kay 2025-09-11T07:16:38.079559Z

Yes and no. good question….for a Send node, what you have is correct. But no, I’m referring to a wrapper in fulcro integration that derives the parent session id from this. so you could say (send-parent! this :event/foo {}) in a component onClick handler, say

tony.kay 2025-09-11T07:16:47.607049Z

but I’m having trouble finding it myself 😄

tony.kay 2025-09-11T07:19:55.271089Z

I remember it being tricky somehow…OH, I’m thinking of uir/send-to-self! helper for using within a component that has a co-located chart

tony.kay 2025-09-11T07:21:06.452909Z

but that’s part of the routing system. For convenience the routing system copies the invoked session’s ID into it’s own data model, so the helper can look up, for the given target, what the current session ID is for it.

tony.kay 2025-09-11T07:22:39.639479Z

within the chart it is trivial, because the parent session id is stored for you, and that env helper (`parent-session-id`) can always look it up.

tony.kay 2025-09-11T07:23:09.074209Z

I think I’m technically supposed to support a special target name for parent 😛

Eric Dvorsak 2025-09-11T07:23:17.305719Z

to be honest send-to-parent shouldn't be that useful right? since in most cases you would just listen to child events

tony.kay 2025-09-11T07:26:48.829889Z

Am I fogetting how it works?

tony.kay 2025-09-11T07:27:33.245389Z

I know that you can autoforward all parent events TO the child, and that child events go through an optional finalize node. But I don’t remember the child events being autoforwarded towards the parent.

tony.kay 2025-09-11T07:30:57.683409Z

I may not have implemented it exactly right. The standard says “The SCXML Processor MUST support the use of SCXML Event/IO processor (https://www.w3.org/TR/scxml/#SCXMLEventProcessor) to communicate between the invoking and the invoked sessions.” I assumed this to mean that you can put events on the queue, since that is my implementation of the “I/O processor”. Let me look at the internals…

tony.kay 2025-09-11T07:31:54.988209Z

tony.kay 2025-09-11T07:32:07.849789Z

tony.kay 2025-09-11T07:33:31.269679Z

so it relies on the event’s invokeid to determine that the event came from an invocation

tony.kay 2025-09-11T07:35:37.110519Z

Ah, and the implementation of send internally deduces that it is an invocation if the target is the parent session ID….so actually it should work correctly

tony.kay 2025-09-11T07:36:01.106899Z

Whew…thought I’d tested that mess 😄

tony.kay 2025-09-11T07:36:23.170359Z

So, child events are NOT forwarded to the parent. The child MAY send events to the parent. IF it does (which implies using an invocation) THEN the parent is allowed to have a finalize node that can record (into the parent’s data model) information from the event. This is essentially a “reusable invocation” adapter…right, you have some pre-written chart that can be invoked, but the way it communicates doesn’t fit well with your chart, so you “adapt” it.

Eric Dvorsak 2025-09-11T07:58:23.265679Z

intereresting so I guess I didn't get it the first time. so if you do nothing the parent will only get the :done event IF the child is done, but otherwise you have to be explicit in what you send to the parent. in that case then it makes sense to have a send-to-parent util yeah and if I understand correctly a Send element could achieve that but if you want to be able to do some amount of scripting a util you'd call from a script-fn would make more sense

👆 1
Eric Dvorsak 2025-09-11T08:10:40.935199Z

ok I got mixed up as you said (send-to-parent! this...) would be for within a component

tony.kay 2025-09-11T08:14:57.101689Z

yeah, I meant “send to self”…as in I’m a UI component…send to my statechart

tony.kay 2025-09-11T08:15:29.405009Z

but that’s a routing specific feature because the routing system tracks the component in parallel with the child chart

tony.kay 2025-09-11T08:16:02.716279Z

sending to the parent from an invocataion is always unambiguous…use parent-session-id

Eric Dvorsak 2025-09-11T09:42:03.200419Z

ok based on all of these discussions I created this issue: https://github.com/fulcrologic/statecharts/issues/20

tony.kay 2025-09-11T09:44:25.508979Z

well, I’m in the middle of messing with a lot of this, but at least that will help me track that one particular thing

tony.kay 2025-09-11T04:55:02.208659Z

Imagine a form. The ident for a new thing has a tempid. The ident for the saved version does not. Now you have a moving target. I’m not sure exactly what you’re advocating for that the standard doesn’t support. use-statechart let’s you set the session id. In routing invocations, the session ID pairing (child knows the parent and parent knows the child). The routing logic runs before the ident in question might be available (e.g. tempid). Furthermore, the fact that a particular istate in the chart is a state with an invocation, means that the particular invocation really can have just a stable ID that is well-known throughout the code, but represents the “current” instance of the invocation. Are you suggesting that you want to share a running instance of a chart between invoke and use-statechart? Or are you just saying you want to be able to share the chart definition? I’m not quite following your point. You would not use-statechart on a session that is running in an invocation I don’t think, because the chart handling the invocation could terminate that chart,…you’d have a coupling where a coupling should not exist.

Eric Dvorsak 2025-09-11T06:31:39.279329Z

Maybe I should explain the why first: I want the parent statechart to receive the child's statechart events. For instance let's say I have a component with a file upload component. The file upload component would be generic and have a statechart to manage file uploads. The parent will be interested in some of the child events and the child will use it's own statechart config and local-state. so I guess what's not obvious to me is how to access the config and local state of the child statecharts when it's invoked with a random session id from the parent? Also unless I'm missing something the parent would have to explicitly unmount/exit the child statecharts if use-statechart is not used

tony.kay 2025-09-11T06:57:38.857199Z

For invocations there is a standard, and a helper function the “send event to parent”. For use-statechart you would have to explicitly pass in the concept of “parent” session in as a parameter to put in the data model so the child would know who it was (which is basically how the standard does it for invocations)…if you ape the pattern, then the helper would even work in that situation for consistency. Look for parent-session-id in the invocation case…

tony.kay 2025-09-11T06:59:09.772189Z

The useEffect on the use-statechart does send an :event/unmount to the child, but yes it is up to you to make the child actually terminate. In invocations the statechart system actually terminates the child statechart.

tony.kay 2025-09-11T06:59:51.185519Z

I guess we could add some kind of option to make these two act in similar ways, as you suggest, so that the same chart can be used as an invocation or with the react hook. I agree that makes sense. I don’t see a way to make them behave completely identically (e.g. the termination of an invocation has a very specific implementation that kind of requires a parent chart to make sense), but we could probably figure out how to force auto-termination with a step of cleanup.