@ai-digital-factory/state-flow 0.1.0 → 0.1.1
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/README.md +106 -4
- package/dist/index.d.ts +119 -37
- package/dist/index.js +457 -211
- package/dist/state-flow.css +70 -5
- package/package.json +4 -4
- /package/{tsconfig.charts.json → tsconfig.flows.json} +0 -0
package/README.md
CHANGED
|
@@ -2,15 +2,117 @@
|
|
|
2
2
|
|
|
3
3
|
React library for flow-style state (states, transitions, and related UI). See repository `CONTEXT.md` for domain vocabulary.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Flow authoring (TSX, no React app)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Flow-only projects declare flows as `.tsx` modules using `Flow`, `State`, `FinalState`, `Transition` and `To`, plus `ParallelState`, `Region` and `FlowList` for composition. They do not install `react` or `@types/react`.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
A Flow is a static graph (ADR 0087). Each Transition lists its allowed destinations as `<To>` children, each describing when that destination is selected:
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
import {
|
|
13
|
+
FinalState,
|
|
14
|
+
Flow,
|
|
15
|
+
State,
|
|
16
|
+
To,
|
|
17
|
+
Transition,
|
|
18
|
+
} from "@ai-digital-factory/state-flow";
|
|
19
|
+
|
|
20
|
+
export default function PlaceOrder() {
|
|
21
|
+
return (
|
|
22
|
+
<Flow name="place-order" description="One broker order." default="idle">
|
|
23
|
+
<State name="idle" description="Not yet submitted.">
|
|
24
|
+
<Transition name="submit-order" description="Send the order.">
|
|
25
|
+
<To state="placed" description="The broker accepted it." />
|
|
26
|
+
<To state="rejected" description="The broker rejected it." />
|
|
27
|
+
</Transition>
|
|
28
|
+
</State>
|
|
29
|
+
<State name="placed" description="Open at the broker.">
|
|
30
|
+
<Transition name="record-fill" description="The order filled.">
|
|
31
|
+
<To state="filled" description="Always." />
|
|
32
|
+
</Transition>
|
|
33
|
+
</State>
|
|
34
|
+
<FinalState name="rejected" description="Refused by the broker." />
|
|
35
|
+
<FinalState name="filled" description="Filled." />
|
|
36
|
+
</Flow>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
- One `<To>` makes a fixed destination; its `onTransition` handler is optional. Several make the Transition branch; its handler module must export `onTransition` returning one of the destination names.
|
|
42
|
+
- `<FinalState>` has no outgoing Transitions and no handlers. Reaching it completes the instance.
|
|
43
|
+
- Flow check and the `state-flow/flow-module` ESLint rule reject Transitions without a `<To>`, unknown or repeated destinations, empty To descriptions, Transitions on a FinalState, and graphs that are not static JSX (conditional children, `.map(...)`, computed props or elements built outside the Flow).
|
|
44
|
+
- The diagram draws a branching Transition as one named segment into a split, with one branch per `<To>` labelled by its description. Final states use a distinct colour and a `FINAL` tag.
|
|
45
|
+
|
|
46
|
+
### Composition: compound, parallel and FlowList (ADR 0095, 0096)
|
|
47
|
+
|
|
48
|
+
Larger lifecycles compose Flows declaratively. Nested States, regions and placed Flows are all part of the static graph; handlers never start child Flows.
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
<State name="signed-in" description="A user is signed in." default="dashboard">
|
|
52
|
+
<State name="dashboard" description="Overview.">
|
|
53
|
+
<Transition name="start-upload-batch" description="Upload files.">
|
|
54
|
+
<To state="uploading" description="Always." />
|
|
55
|
+
</Transition>
|
|
56
|
+
</State>
|
|
57
|
+
<State name="uploading" description="Each file uploads independently.">
|
|
58
|
+
<FlowList name="uploads" flow="file-upload" description="One per file." />
|
|
59
|
+
<Transition name="batch-finished" description="All done." trigger="done">
|
|
60
|
+
<To state="dashboard" description="Always." />
|
|
61
|
+
</Transition>
|
|
62
|
+
</State>
|
|
63
|
+
<ParallelState name="order-desk" description="Order and watchlist together.">
|
|
64
|
+
<Region name="order" description="One order." flow="place-order" />
|
|
65
|
+
<Region name="watchlist" description="Prices." default="watching">
|
|
66
|
+
<State name="watching" description="Live prices." />
|
|
67
|
+
</Region>
|
|
68
|
+
</ParallelState>
|
|
69
|
+
<Transition name="sign-out" description="Busy while work runs.">
|
|
70
|
+
<To state="signed-out" description="Always." />
|
|
71
|
+
</Transition>
|
|
72
|
+
<Transition
|
|
73
|
+
name="force-sign-out"
|
|
74
|
+
description="Interrupt work."
|
|
75
|
+
childWork="interrupt"
|
|
76
|
+
>
|
|
77
|
+
<To state="signed-out" description="Always." />
|
|
78
|
+
</Transition>
|
|
79
|
+
</State>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
- A `<State>` with nested States and a `default` is compound; `<State flow="x">` places one execution of Flow `x` from the same flows package. A `<ParallelState>` activates every `<Region>` and `<FlowList>` it contains.
|
|
83
|
+
- Transitions reach only siblings in their own region. Declare a Transition that leaves a containing State on that State; it is available from every nested configuration.
|
|
84
|
+
- `childWork="block"` (default) answers `busy` while descendant work is running or unresolved; `childWork="interrupt"` cancels it and proceeds. Neither undoes an external effect.
|
|
85
|
+
- `trigger="done"` fires once every region completes, with each region's FinalState (and each FlowList member's) as payload. Completing a child never completes the root.
|
|
86
|
+
- State and Transition names are unique across the whole flow file. The diagram tags composite States `COMPOUND` or `PARALLEL`, lists placed Flows and FlowList templates, and brackets nested States under their parent.
|
|
87
|
+
|
|
88
|
+
In the handlers package, a child Flow declares its input in `src/<flow-id>/input.ts` (`export const inputSchema = z.strictObject({...})`), read as `context.input`. A composite State's handler exports `children: { run, recovery }`, which receives the entering Transition's payload and returns child inputs and FlowList members (`{ uploads: [{ key, input }] }`). Generated `ChildInputs_<state>` and `Completion_<state>` types describe both.
|
|
89
|
+
|
|
90
|
+
### Migrating from the `to` prop
|
|
91
|
+
|
|
92
|
+
The `to` prop on `Transition` has been removed. Move the target into a `<To>` child and describe when it applies:
|
|
93
|
+
|
|
94
|
+
```diff
|
|
95
|
+
-<Transition to="present" name="unlock-succeeds" description="Correct credentials." />
|
|
96
|
+
+<Transition name="unlock-succeeds" description="Correct credentials.">
|
|
97
|
+
+ <To state="present" description="Always." />
|
|
98
|
+
+</Transition>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Recompile the flows package and its handlers package together: compiled flow artifacts move to schema version 2, generated `TransitionContext` types expose `destinations` instead of `toState`, and callback contexts expose `dependencies` instead of `config`. In each handlers package, rename `src/<flow-id>/config.ts` to `dependencies.ts` and its `Config` type to `Dependencies`; services import `Dependencies` instead of `FlowConfig` from the compiled module. Runtime types also lose their `Durable` prefix (ADR 0089), for example `TransitionCallback` instead of `DurableTransitionCallback`. Durable definition identities of flows without branching or final states are unchanged, so existing instances reopen.
|
|
102
|
+
|
|
103
|
+
The same release (ADRs 0089 and 0090) also renames and removes, without aliases:
|
|
104
|
+
|
|
105
|
+
- `@ai-digital-factory/state-flow-chart` is now `@ai-digital-factory/state-flow-definition`; update dependencies and imports.
|
|
106
|
+
- Presets: extend `@ai-digital-factory/state-flow/tsconfig.flows.json` and import `eslint.flows.config.js` (formerly `tsconfig.charts.json`, `eslint.charts.config.js`). The ESLint rule is `state-flow/flow-module`.
|
|
107
|
+
- Rule IDs in `state-flow.rules.json`: `state-chart.*` is `flow.*`, `application-hook.*` is `handler.*`, and `transition.name-unique-across-chart` is `transition.name-unique-across-flow`.
|
|
108
|
+
- Identifiers drop `Application` and use Flow/Handler, for example `FlowDefinition`, `defineFlow`, `HandlerContext`; data fields `chartName`/`chartId` are `flowName`/`flowId`, and compile output is under `dist/flows/`.
|
|
109
|
+
- Services: `await runtime.createFlow(compiled, dependencies)`, `await flow.ensureInstance({ instanceId })`, then `when(state).handle(outcomes).dispatch(input)`. The builder `.handleFailure(...).run()`, `runtime.flow`, `handleCreate`/`handleOpen`/`handleEnsure`, direct `when(state).dispatch` and `DispatchError`/`OperationError` are gone.
|
|
110
|
+
|
|
111
|
+
Extend the flow preset (TypeScript requires `jsx` for `.tsx`; the preset supplies it):
|
|
10
112
|
|
|
11
113
|
```json
|
|
12
114
|
{
|
|
13
|
-
"extends": "@ai-digital-factory/state-flow/tsconfig.
|
|
115
|
+
"extends": "@ai-digital-factory/state-flow/tsconfig.flows.json",
|
|
14
116
|
"include": ["src/**/*.tsx"]
|
|
15
117
|
}
|
|
16
118
|
```
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { ReactElement, ReactNode } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
export {
|
|
2
|
+
import { FlowDefinition, ParsedState, ParsedTransition, ParsedFlow } from '@ai-digital-factory/state-flow-definition';
|
|
3
|
+
export { DEFAULT_TRANSITION_NAME, DefaultTransitionName, HandlerContext, STATE_HANDLER_EXPORT_NAMES, TRANSITION_HANDLER_EXPORT_NAMES, TransitionHandlerContext } from '@ai-digital-factory/state-flow-definition';
|
|
4
4
|
|
|
5
5
|
declare function StateFlowPlaceholder(): ReactElement;
|
|
6
6
|
|
|
7
7
|
type FlowProps = {
|
|
8
|
-
/** Lowercase kebab-case
|
|
8
|
+
/** Lowercase kebab-case flow identifier (e.g. `visitor-access`). Enforced by flow check. */
|
|
9
9
|
name: string;
|
|
10
10
|
description: string;
|
|
11
11
|
default: string;
|
|
@@ -20,6 +20,17 @@ type StateProps = {
|
|
|
20
20
|
id?: string;
|
|
21
21
|
name: string;
|
|
22
22
|
description: string;
|
|
23
|
+
/**
|
|
24
|
+
* Compound State (ADR 0095, 0096): the nested child State entered with this State. Required
|
|
25
|
+
* when the State contains nested `<State>` / `<FinalState>` children.
|
|
26
|
+
*/
|
|
27
|
+
default?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Compound State whose child is one placement of a reusable Flow of the same flows package.
|
|
30
|
+
* Its completion fires this State's `trigger="done"` Transition.
|
|
31
|
+
*/
|
|
32
|
+
flow?: string;
|
|
33
|
+
/** Transitions, nested States, or one `<FlowList>`. */
|
|
23
34
|
children?: ReactNode;
|
|
24
35
|
};
|
|
25
36
|
/** Declares a flow state; rendered by `Flow`, not directly. */
|
|
@@ -28,54 +39,125 @@ declare namespace State {
|
|
|
28
39
|
var displayName: string;
|
|
29
40
|
}
|
|
30
41
|
|
|
31
|
-
type
|
|
32
|
-
|
|
42
|
+
type ParallelStateProps = {
|
|
43
|
+
id?: string;
|
|
33
44
|
name: string;
|
|
34
45
|
description: string;
|
|
46
|
+
/** `<Region>` and `<FlowList>` children, all active together, plus Transitions. */
|
|
35
47
|
children?: ReactNode;
|
|
36
48
|
};
|
|
37
|
-
/**
|
|
38
|
-
|
|
39
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Declares a parallel State (ADR 0095, 0096): entering it activates every declared region.
|
|
51
|
+
* It completes, firing its `trigger="done"` Transition, when every region completes.
|
|
52
|
+
*/
|
|
53
|
+
declare function ParallelState(_props: ParallelStateProps): null;
|
|
54
|
+
declare namespace ParallelState {
|
|
40
55
|
var displayName: string;
|
|
41
56
|
}
|
|
42
57
|
|
|
43
|
-
type
|
|
44
|
-
|
|
58
|
+
type RegionProps = {
|
|
59
|
+
/** Lowercase kebab-case; addressed as `state:region` from the parallel State. */
|
|
60
|
+
name: string;
|
|
61
|
+
description: string;
|
|
62
|
+
/** For nested States: the child entered with the region. */
|
|
63
|
+
default?: string;
|
|
64
|
+
/** Instead of nested States: one placement of a reusable Flow of the same flows package. */
|
|
65
|
+
flow?: string;
|
|
66
|
+
/** Nested `<State>` / `<FinalState>` children of an inline region. */
|
|
67
|
+
children?: ReactNode;
|
|
45
68
|
};
|
|
46
|
-
|
|
69
|
+
/** One region of a `ParallelState`; rendered by `Flow`, not directly. */
|
|
70
|
+
declare function Region(_props: RegionProps): null;
|
|
71
|
+
declare namespace Region {
|
|
72
|
+
var displayName: string;
|
|
73
|
+
}
|
|
47
74
|
|
|
48
|
-
type
|
|
49
|
-
|
|
75
|
+
type FlowListProps = {
|
|
76
|
+
/** Lowercase kebab-case region name. */
|
|
77
|
+
name: string;
|
|
50
78
|
description: string;
|
|
51
|
-
/**
|
|
52
|
-
|
|
79
|
+
/** The reusable child Flow every member executes, from the same flows package. */
|
|
80
|
+
flow: string;
|
|
53
81
|
};
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
declare function
|
|
82
|
+
/**
|
|
83
|
+
* Declares a FlowList (ADR 0095, 0096): one keyed execution of a reusable Flow per member. The
|
|
84
|
+
* containing State's `children` handler returns the members when the State is entered; the
|
|
85
|
+
* membership is fixed for that activation. Use inside a `<State>` or a `<ParallelState>`.
|
|
86
|
+
*/
|
|
87
|
+
declare function FlowList(_props: FlowListProps): null;
|
|
88
|
+
declare namespace FlowList {
|
|
89
|
+
var displayName: string;
|
|
90
|
+
}
|
|
60
91
|
|
|
61
|
-
type
|
|
62
|
-
|
|
63
|
-
fromName: string;
|
|
64
|
-
toId: string;
|
|
65
|
-
toName: string;
|
|
92
|
+
type FinalStateProps = {
|
|
93
|
+
id?: string;
|
|
66
94
|
name: string;
|
|
67
95
|
description: string;
|
|
68
96
|
};
|
|
69
|
-
|
|
70
|
-
|
|
97
|
+
/**
|
|
98
|
+
* Declares a terminal State: no outgoing Transitions and no callbacks. Reaching it completes the
|
|
99
|
+
* flow instance (ADR 0087). Rendered by `Flow`, not directly.
|
|
100
|
+
*/
|
|
101
|
+
declare function FinalState(_props: FinalStateProps): null;
|
|
102
|
+
declare namespace FinalState {
|
|
103
|
+
var displayName: string;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
type TransitionProps = {
|
|
71
107
|
name: string;
|
|
72
108
|
description: string;
|
|
109
|
+
/**
|
|
110
|
+
* When this Transition leaves a composite State while descendant work is running or
|
|
111
|
+
* unresolved (ADR 0095): `block` (default) answers `busy`; `interrupt` cancels that work
|
|
112
|
+
* cooperatively and proceeds without waiting for it.
|
|
113
|
+
*/
|
|
114
|
+
childWork?: "block" | "interrupt";
|
|
115
|
+
/**
|
|
116
|
+
* `done`: fired by the runtime when the State's children complete, with their completion
|
|
117
|
+
* result as payload. Allowed once per composite State. Omitted: dispatched by the application.
|
|
118
|
+
*/
|
|
119
|
+
trigger?: "dispatch" | "done";
|
|
120
|
+
/**
|
|
121
|
+
* One or more `<To state="..." description="..." />` children. A single destination is fixed;
|
|
122
|
+
* several make the Transition branch, and its `onTransition` selects one (ADR 0087).
|
|
123
|
+
*/
|
|
124
|
+
children?: ReactNode;
|
|
73
125
|
};
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
126
|
+
/** Declares an allowed transition; rendered by `Flow`, not directly. */
|
|
127
|
+
declare function Transition(_props: TransitionProps): null;
|
|
128
|
+
declare namespace Transition {
|
|
129
|
+
var displayName: string;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
type ToProps = {
|
|
133
|
+
/** Destination State name. */
|
|
134
|
+
state: string;
|
|
135
|
+
/** When this destination is selected, for example "Broker accepted the order." */
|
|
136
|
+
description: string;
|
|
78
137
|
};
|
|
138
|
+
/** Declares one allowed destination of its parent `Transition`; rendered by `Flow`, not directly. */
|
|
139
|
+
declare function To(_props: ToProps): null;
|
|
140
|
+
declare namespace To {
|
|
141
|
+
var displayName: string;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
type FlowRendererProps = {
|
|
145
|
+
flow: FlowDefinition;
|
|
146
|
+
};
|
|
147
|
+
declare function FlowRenderer({ flow }: FlowRendererProps): ReactElement;
|
|
148
|
+
|
|
149
|
+
type FlowPageHeaderProps = {
|
|
150
|
+
title: string;
|
|
151
|
+
description: string;
|
|
152
|
+
/** Lowercase kebab-case flow id; picks a header icon when set. */
|
|
153
|
+
flowId?: string;
|
|
154
|
+
};
|
|
155
|
+
declare function FlowPageHeader({ title, description, flowId, }: FlowPageHeaderProps): ReactElement;
|
|
156
|
+
|
|
157
|
+
declare function FlowHeaderIcon({ flowId }: {
|
|
158
|
+
flowId?: string;
|
|
159
|
+
}): ReactElement;
|
|
160
|
+
declare function flowNavIcon(flowId: string): string;
|
|
79
161
|
|
|
80
162
|
type DiagramOverviewProps = {
|
|
81
163
|
svgHtml: string;
|
|
@@ -93,14 +175,14 @@ type DiagramOverviewProps = {
|
|
|
93
175
|
declare function DiagramOverview({ svgHtml, states, edges, viewPersistenceKey, guardBlockedEdgeIndex, detailLayout, initialStateId, }: DiagramOverviewProps): ReactElement;
|
|
94
176
|
|
|
95
177
|
/** Forward edges run above the states; return edges run below them. */
|
|
96
|
-
declare function diagramSvg(
|
|
178
|
+
declare function diagramSvg(flow: ParsedFlow, title: string, instanceId: string): string;
|
|
97
179
|
|
|
98
180
|
declare function parseScrollYFromSearch(search: string): number | null;
|
|
99
181
|
declare function mergeScrollYIntoSearch(search: string, scrollY: number): string;
|
|
100
182
|
declare function replaceBrowserUrlSearch(search: string): void;
|
|
101
183
|
|
|
102
|
-
declare function
|
|
103
|
-
type
|
|
104
|
-
declare function
|
|
184
|
+
declare function extractFlowDefinition(node: ReactNode): FlowDefinition;
|
|
185
|
+
type FlowComponent = () => ReactNode;
|
|
186
|
+
declare function extractFlowDefinitionFromComponent(component: FlowComponent): FlowDefinition;
|
|
105
187
|
|
|
106
|
-
export { type
|
|
188
|
+
export { DiagramOverview, type DiagramOverviewProps, FinalState, type FinalStateProps, Flow, type FlowComponent, FlowHeaderIcon, FlowList, type FlowListProps, FlowPageHeader, type FlowPageHeaderProps, type FlowProps, FlowRenderer, type FlowRendererProps, ParallelState, type ParallelStateProps, Region, type RegionProps, State, StateFlowPlaceholder, type StateProps, To, type ToProps, Transition, type TransitionProps, diagramSvg, extractFlowDefinition, extractFlowDefinitionFromComponent, flowNavIcon, mergeScrollYIntoSearch, parseScrollYFromSearch, replaceBrowserUrlSearch };
|