@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 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
- ## Application chart authoring (TSX, no React app)
5
+ ## Flow authoring (TSX, no React app)
6
6
 
7
- Chart-only projects declare flows as `.tsx` modules using `Flow`, `State`, and `Transition`. They do not install `react` or `@types/react`.
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
- Extend the chart preset (TypeScript requires `jsx` for `.tsx`; the preset supplies it):
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.charts.json",
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 { ApplicationChartDefinition } from '@ai-digital-factory/state-flow-chart';
3
- export { ApplicationHookContext, DEFAULT_TRANSITION_NAME, DefaultTransitionName, STATE_APPLICATION_HOOK_EXPORT_NAMES, TRANSITION_APPLICATION_HOOK_EXPORT_NAMES, TransitionHookContext } from '@ai-digital-factory/state-flow-chart';
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 chart identifier (e.g. `visitor-access`). Enforced by chart check. */
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 TransitionProps = {
32
- to: string;
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
- /** Declares an allowed transition; rendered by `Flow`, not directly. */
38
- declare function Transition(_props: TransitionProps): null;
39
- declare namespace Transition {
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 ApplicationChartRendererProps = {
44
- chart: ApplicationChartDefinition;
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
- declare function ApplicationChartRenderer({ chart, }: ApplicationChartRendererProps): ReactElement;
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 ChartPageHeaderProps = {
49
- title: string;
75
+ type FlowListProps = {
76
+ /** Lowercase kebab-case region name. */
77
+ name: string;
50
78
  description: string;
51
- /** Lowercase kebab-case chart id; picks a header icon when set. */
52
- chartId?: string;
79
+ /** The reusable child Flow every member executes, from the same flows package. */
80
+ flow: string;
53
81
  };
54
- declare function ChartPageHeader({ title, description, chartId, }: ChartPageHeaderProps): ReactElement;
55
-
56
- declare function ChartHeaderIcon({ chartId, }: {
57
- chartId?: string;
58
- }): ReactElement;
59
- declare function chartNavIcon(chartId: string): string;
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 ParsedTransition = {
62
- fromId: string;
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
- type ParsedState = {
70
- id: string;
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
- type ParsedChart = {
75
- states: ParsedState[];
76
- edges: ParsedTransition[];
77
- initialId: string;
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(chart: ParsedChart, title: string, instanceId: string): string;
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 extractApplicationChartDefinition(node: ReactNode): ApplicationChartDefinition;
103
- type ApplicationChartComponent = () => ReactNode;
104
- declare function extractApplicationChartDefinitionFromComponent(component: ApplicationChartComponent): ApplicationChartDefinition;
184
+ declare function extractFlowDefinition(node: ReactNode): FlowDefinition;
185
+ type FlowComponent = () => ReactNode;
186
+ declare function extractFlowDefinitionFromComponent(component: FlowComponent): FlowDefinition;
105
187
 
106
- export { type ApplicationChartComponent, ApplicationChartRenderer, type ApplicationChartRendererProps, ChartHeaderIcon, ChartPageHeader, type ChartPageHeaderProps, DiagramOverview, type DiagramOverviewProps, Flow, type FlowProps, State, StateFlowPlaceholder, type StateProps, Transition, type TransitionProps, chartNavIcon, diagramSvg, extractApplicationChartDefinition, extractApplicationChartDefinitionFromComponent, mergeScrollYIntoSearch, parseScrollYFromSearch, replaceBrowserUrlSearch };
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 };