@w6w/ui 0.2.0 → 0.2.54

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.
Files changed (160) hide show
  1. package/CHANGELOG.md +146 -2
  2. package/README.md +28 -10
  3. package/dist/AppPicker.d.ts +58 -2
  4. package/dist/CanvasContextMenu.d.ts +19 -0
  5. package/dist/CanvasLayer.d.ts +38 -0
  6. package/dist/{CodeBlock-Dot3Pd7P.js → CodeBlock-C-oIIYfC.js} +1436 -555
  7. package/dist/CodeBlock-C-oIIYfC.js.map +1 -0
  8. package/dist/CodeBlock-CymlfyeA.cjs +15 -0
  9. package/dist/CodeBlock-CymlfyeA.cjs.map +1 -0
  10. package/dist/CodeEditor.d.ts +4 -1
  11. package/dist/ConditionEditor.d.ts +11 -0
  12. package/dist/EditConnectionModal.d.ts +6 -1
  13. package/dist/JsonEditor.d.ts +20 -0
  14. package/dist/Markdown.d.ts +25 -0
  15. package/dist/ParamCustomization.d.ts +5 -0
  16. package/dist/ParamsForm.d.ts +18 -4
  17. package/dist/RouterEditor.d.ts +5 -0
  18. package/dist/StepBuilderModal.d.ts +103 -15
  19. package/dist/StepNameEditor.d.ts +11 -0
  20. package/dist/StopEditor.d.ts +5 -0
  21. package/dist/TriggerFillForm.d.ts +14 -1
  22. package/dist/WebhookListenPanel.d.ts +28 -0
  23. package/dist/WorkflowConnections.d.ts +29 -0
  24. package/dist/WorkflowFlowEditor.d.ts +74 -7
  25. package/dist/app-pages.d.ts +2 -0
  26. package/dist/assets/script-completion.worker-DDJKkj5M.js +8290 -0
  27. package/dist/assets/script-completion.worker-DDJKkj5M.js.map +1 -0
  28. package/dist/canvas-context-menu.d.ts +27 -0
  29. package/dist/canvas-types.d.ts +36 -0
  30. package/dist/canvas-utils.d.ts +44 -0
  31. package/dist/code.cjs +1 -1
  32. package/dist/code.mjs +3 -4
  33. package/dist/code.mjs.map +1 -1
  34. package/dist/components/AnchoredFlyout.d.ts +7 -0
  35. package/dist/components/AuthFieldsForm.d.ts +11 -0
  36. package/dist/components/BudgetMeter.d.ts +15 -0
  37. package/dist/components/CallableList.d.ts +3 -2
  38. package/dist/components/ColumnsEditor.d.ts +27 -0
  39. package/dist/components/Combobox.d.ts +19 -1
  40. package/dist/components/ConnectionList.d.ts +2 -3
  41. package/dist/components/CredentialPicker.d.ts +35 -0
  42. package/dist/components/CsvPanel.d.ts +21 -0
  43. package/dist/components/DataEntryEditor.d.ts +38 -0
  44. package/dist/components/DataEntryList.d.ts +33 -0
  45. package/dist/components/DataStoreList.d.ts +33 -0
  46. package/dist/components/DataTable.d.ts +27 -0
  47. package/dist/components/DataTableGrid.d.ts +46 -0
  48. package/dist/components/EmptyState.d.ts +9 -0
  49. package/dist/components/EventbriteTriggerPicker.d.ts +7 -0
  50. package/dist/components/ExecutionDetail.d.ts +17 -1
  51. package/dist/components/ExecutionList.d.ts +19 -1
  52. package/dist/components/ExecutionLogPanel.d.ts +61 -3
  53. package/dist/components/ExpressionEditorModal.d.ts +33 -10
  54. package/dist/components/ExpressionOptions.d.ts +101 -15
  55. package/dist/components/HtmlPreview.d.ts +7 -0
  56. package/dist/components/JsonTree.d.ts +32 -0
  57. package/dist/components/ResolvedParams.d.ts +6 -1
  58. package/dist/components/ResultViewer.d.ts +46 -0
  59. package/dist/components/ScriptBindingsNotice.d.ts +31 -0
  60. package/dist/components/ScriptCodeField.d.ts +13 -0
  61. package/dist/components/StepStatusPill.d.ts +7 -0
  62. package/dist/components/expression-authoring.d.ts +13 -0
  63. package/dist/components/expression-dom.d.ts +17 -2
  64. package/dist/components/expression-template.d.ts +25 -3
  65. package/dist/components/icons.d.ts +1 -1
  66. package/dist/components/json-compose.d.ts +53 -0
  67. package/dist/components/result-format.d.ts +13 -0
  68. package/dist/condition-editor.d.ts +44 -0
  69. package/dist/data-stores/data-store-view.d.ts +371 -0
  70. package/dist/dynamic-fields.d.ts +60 -0
  71. package/dist/editor-theme.d.ts +24 -0
  72. package/dist/eventbrite-trigger-selection.d.ts +13 -0
  73. package/dist/flow-connect.d.ts +22 -16
  74. package/dist/flow-delete.d.ts +16 -0
  75. package/dist/flow-fanout.d.ts +2 -2
  76. package/dist/flow-history.d.ts +75 -0
  77. package/dist/flow-layout.d.ts +13 -0
  78. package/dist/flow-loop-colors.d.ts +39 -0
  79. package/dist/flow-loop.d.ts +98 -0
  80. package/dist/flow-node-flags.d.ts +14 -0
  81. package/dist/flow-problems.d.ts +69 -0
  82. package/dist/flow-state.d.ts +20 -0
  83. package/dist/flow-types.d.ts +155 -10
  84. package/dist/flow-utils.d.ts +33 -12
  85. package/dist/flow.cjs +4 -2
  86. package/dist/flow.cjs.map +1 -1
  87. package/dist/flow.d.ts +15 -2
  88. package/dist/flow.mjs +3737 -1350
  89. package/dist/flow.mjs.map +1 -1
  90. package/dist/index.cjs +2 -1
  91. package/dist/index.cjs.map +1 -1
  92. package/dist/index.d.ts +43 -5
  93. package/dist/index.mjs +2855 -1333
  94. package/dist/index.mjs.map +1 -1
  95. package/dist/node-samples-ChdyMBnJ.cjs +22 -0
  96. package/dist/node-samples-ChdyMBnJ.cjs.map +1 -0
  97. package/dist/node-samples-DkJL07EX.js +12302 -0
  98. package/dist/node-samples-DkJL07EX.js.map +1 -0
  99. package/dist/node-samples.d.ts +80 -0
  100. package/dist/note-markdown.d.ts +22 -0
  101. package/dist/output-shape.d.ts +30 -14
  102. package/dist/param-customization.d.ts +7 -0
  103. package/dist/provider.d.ts +174 -13
  104. package/dist/resolve-params.d.ts +50 -11
  105. package/dist/run-visuals.d.ts +18 -22
  106. package/dist/script-bindings.d.ts +63 -0
  107. package/dist/script-completion-service.d.ts +9 -0
  108. package/dist/script-completion.worker.d.ts +1 -0
  109. package/dist/script-completions.d.ts +2 -0
  110. package/dist/state-shape.d.ts +38 -0
  111. package/dist/step-preview-state.d.ts +155 -6
  112. package/dist/step-problems.d.ts +64 -0
  113. package/dist/test-inputs.d.ts +25 -0
  114. package/dist/trigger-registration.d.ts +28 -0
  115. package/dist/types.d.ts +179 -11
  116. package/dist/use-app-action-defs.d.ts +9 -1
  117. package/dist/use-dynamic-fields.d.ts +23 -0
  118. package/dist/use-test-input.d.ts +18 -0
  119. package/dist/workflow-connections.d.ts +30 -0
  120. package/package.json +18 -7
  121. package/src/code.css +60 -6
  122. package/src/styles/_budget-meter.scss +46 -0
  123. package/src/styles/_callable-list.scss +15 -6
  124. package/src/styles/_canvas.scss +353 -0
  125. package/src/styles/_code-editor.scss +35 -0
  126. package/src/styles/_code.scss +6 -5
  127. package/src/styles/_connections.scss +2 -14
  128. package/src/styles/_copyable.scss +24 -0
  129. package/src/styles/_data-stores.scss +138 -0
  130. package/src/styles/_data-table.scss +55 -0
  131. package/src/styles/_data-vars.scss +17 -6
  132. package/src/styles/_empty-state.scss +21 -0
  133. package/src/styles/_execution-history.scss +73 -0
  134. package/src/styles/_execution-log.scss +145 -1
  135. package/src/styles/_expression-modal.scss +106 -16
  136. package/src/styles/_fields.scss +11 -0
  137. package/src/styles/_flow.scss +171 -0
  138. package/src/styles/_fx.scss +26 -1
  139. package/src/styles/_incoming-state.scss +33 -0
  140. package/src/styles/_layout.scss +10 -0
  141. package/src/styles/_markdown.scss +20 -0
  142. package/src/styles/_modal.scss +12 -0
  143. package/src/styles/_multiselect.scss +35 -0
  144. package/src/styles/_sections.scss +36 -0
  145. package/src/styles/_step-builder.scss +145 -0
  146. package/src/styles/_tester.scss +57 -14
  147. package/src/styles/_tokens.scss +27 -1
  148. package/src/styles.css +1861 -115
  149. package/src/styles.scss +7 -0
  150. package/dist/CodeBlock-CZu4TFSi.cjs +0 -15
  151. package/dist/CodeBlock-CZu4TFSi.cjs.map +0 -1
  152. package/dist/CodeBlock-Dot3Pd7P.js.map +0 -1
  153. package/dist/ConfirmModal-ChoiGy2K.cjs +0 -11
  154. package/dist/ConfirmModal-ChoiGy2K.cjs.map +0 -1
  155. package/dist/ConfirmModal-Wx3i8F1n.js +0 -6359
  156. package/dist/ConfirmModal-Wx3i8F1n.js.map +0 -1
  157. package/dist/Copyable-1niV9gUa.js +0 -771
  158. package/dist/Copyable-1niV9gUa.js.map +0 -1
  159. package/dist/Copyable-CYZU1o4X.cjs +0 -2
  160. package/dist/Copyable-CYZU1o4X.cjs.map +0 -1
@@ -1,5 +1,6 @@
1
1
  import { Edge } from '@xyflow/react';
2
- import { PortsLookup } from './flow-types.ts';
2
+ import { LoopRefusalReason } from './flow-loop.ts';
3
+ import { CanvasLane, PortsLookup } from './flow-types.ts';
3
4
  import { StepNode } from './flow-utils.ts';
4
5
  /**
5
6
  * Which lane a React Flow edge belongs to (core `rfcs/workflow.md` · `Edge.when`
@@ -8,7 +9,7 @@ import { StepNode } from './flow-utils.ts';
8
9
  * `data` at all. The single accessor: every consumer calls this rather than
9
10
  * re-deriving the fallback, so "absent means success" is decided once.
10
11
  */
11
- export declare function edgeLane(e: Edge): "success" | "error";
12
+ export declare function edgeLane(e: Edge): CanvasLane;
12
13
  /**
13
14
  * A refused connection: the source's exit port is full **in this lane** and the cap
14
15
  * is finite. Drawing past a finite `out` cap is refused, never satisfied by evicting
@@ -18,14 +19,22 @@ export type OutCapRefusal = {
18
19
  refused: "out_cap";
19
20
  cap: number;
20
21
  };
22
+ /**
23
+ * A refused connection that would break the v1 linear-body rule of a For each
24
+ * (see {@link loopRefusal}). Raised before the out-cap census and before any
25
+ * inbound eviction, always against the INPUT edges.
26
+ */
27
+ export type LoopRefusal = {
28
+ refused: "loop";
29
+ reason: LoopRefusalReason;
30
+ };
21
31
  /**
22
32
  * The hard rules for an edge `source → target` — the ones no amount of
23
33
  * replacement can satisfy: a real, *distinct* pair (no self-loops), no duplicate
24
34
  * edge, the target accepts an entry port (blocks connecting *into* a trigger,
25
35
  * which declares `in: 0`), and the source has an exit port. Port **capacity** is
26
- * deliberately NOT checked here: a full single-slot port is freed by replacement
27
- * (see `applyConnect`), so dragging a new wire from an already-connected node
28
- * re-points it rather than being rejected. Used as the live `isValidConnection`.
36
+ * deliberately NOT checked here: an input accepts any number of edges, and the exit
37
+ * cap is refused in `applyConnect`. Used as the live `isValidConnection`.
29
38
  *
30
39
  * The duplicate-pair check is **lane-blind by design**, and that is a v1 *editor*
31
40
  * limit, not a model limit: the model permits a success edge and an error edge
@@ -41,13 +50,13 @@ export type OutCapRefusal = {
41
50
  * Letting the drop through and refusing in `applyConnect` is what lets the reason
42
51
  * paint — see {@link connectConflict}.
43
52
  */
44
- export declare function canConnect(source: string | null | undefined, target: string | null | undefined, nodes: StepNode[], edges: Edge[], lookup?: PortsLookup): boolean;
53
+ export declare function canConnect(source: string | null | undefined, target: string | null | undefined, nodes: StepNode[], edges: Edge[], lookup?: PortsLookup, route?: string): boolean;
45
54
  /**
46
55
  * Build the next edge set for a new `source → target` connection. **Additive at the
47
56
  * source**: a source under its `out` cap (or unbounded) keeps every existing edge and
48
57
  * gains one; at a finite cap the connection is **refused** (see {@link connectRefusal}),
49
- * never satisfied by evicting. The target's entry port is still single-slot-replacing:
50
- * the oldest inbound edge(s) are dropped to make room. Returns `null` when the
58
+ * never satisfied by evicting. The target's entry port accepts any number of
59
+ * inbound edges, so nothing is ever evicted there. Returns `null` when the
51
60
  * connection is disallowed by {@link canConnect}, refused at the out cap — **or when the minted id would
52
61
  * duplicate a surviving edge's id**, the same refusal {@link setEdgeWhen} performs
53
62
  * through the same {@link collidingEdgeId} test.
@@ -68,13 +77,10 @@ export declare function canConnect(source: string | null | undefined, target: st
68
77
  * `send → (error) → fallback` shape authorable at all. No `ports.out` bump is
69
78
  * needed, so every persisted definition and palette entry stays valid.
70
79
  *
71
- * The target's entry capacity stays **lane-blind**, and that asymmetry is the
72
- * point: a converging tail step receiving one success and one error edge is
73
- * governed by `ports.in` alone. Partitioning the target rule by lane too — the
74
- * plausible "symmetry" cleanup — would let a `ports.in: 1` node silently accept
75
- * two inbound edges.
80
+ * The target's entry has no capacity: any number of edges, from any number of
81
+ * sources (several triggers may feed one node), converge on it.
76
82
  */
77
- export declare function applyConnect(source: string | null | undefined, target: string | null | undefined, nodes: StepNode[], edges: Edge[], when?: "success" | "error", lookup?: PortsLookup): Edge[] | null;
83
+ export declare function applyConnect(source: string | null | undefined, target: string | null | undefined, nodes: StepNode[], edges: Edge[], when?: CanvasLane, lookup?: PortsLookup, route?: string): Edge[] | null;
78
84
  /**
79
85
  * The id of the existing edge that drawing `source → target` would collide with, or
80
86
  * `null` when the connection is safe (or refused for an ordinary reason — a
@@ -85,13 +91,13 @@ export declare function applyConnect(source: string | null | undefined, target:
85
91
  * selects that edge and renders the message in the edge-lane panel (never a browser
86
92
  * dialog; see `.ai/conventions.md`).
87
93
  */
88
- export declare function connectConflict(source: string | null | undefined, target: string | null | undefined, nodes: StepNode[], edges: Edge[], when?: "success" | "error", lookup?: PortsLookup): string | null;
94
+ export declare function connectConflict(source: string | null | undefined, target: string | null | undefined, nodes: StepNode[], edges: Edge[], when?: CanvasLane, lookup?: PortsLookup, route?: string): string | null;
89
95
  /**
90
96
  * The out-cap refusal for drawing `source → target`, or `null` when the source has
91
97
  * room (or is unbounded). The editor renders `cap` in an inline message on the same
92
98
  * channel as {@link connectConflict}; {@link applyConnect} returns `null` for it.
93
99
  */
94
- export declare function connectRefusal(source: string | null | undefined, target: string | null | undefined, nodes: StepNode[], edges: Edge[], when?: "success" | "error", lookup?: PortsLookup): OutCapRefusal | null;
100
+ export declare function connectRefusal(source: string | null | undefined, target: string | null | undefined, nodes: StepNode[], edges: Edge[], when?: CanvasLane, lookup?: PortsLookup, route?: string): OutCapRefusal | LoopRefusal | null;
95
101
  /**
96
102
  * Re-lane one edge. Updates its `data.when`, its visuals and its id, and frees
97
103
  * nothing: when the destination lane is at a finite out cap the switch is refused
@@ -0,0 +1,16 @@
1
+ import { Edge } from '@xyflow/react';
2
+ import { FlowCanvas } from './canvas-types.ts';
3
+ import { StepNode } from './flow-utils.ts';
4
+ export interface FlowDeleteSelection {
5
+ nodeIds: string[];
6
+ edgeIds: string[];
7
+ }
8
+ /** Capture the full selection, independently of the first item shown in the side panel. */
9
+ export declare function selectedFlowElements(nodes: StepNode[], edges: Edge[]): FlowDeleteSelection;
10
+ /** Apply the entire confirmed selection before the editor emits one change/history entry. */
11
+ export declare function deleteFlowSelection(selection: FlowDeleteSelection, nodes: StepNode[], edges: Edge[], canvas: FlowCanvas): {
12
+ nodes: StepNode[];
13
+ edges: Edge[];
14
+ canvas: FlowCanvas;
15
+ };
16
+ export declare function flowDeleteMessage(selection: FlowDeleteSelection, nodes: StepNode[], edges: Edge[]): string;
@@ -2,7 +2,7 @@ import { Edge } from '@xyflow/react';
2
2
  import { FlowStep } from './flow-types.ts';
3
3
  export type FanOutMode = "sequential" | "parallel";
4
4
  /**
5
- * `count` is the largest number of outgoing edges the step has in any ONE lane
5
+ * `count` is the largest number of outgoing edges the node has in any ONE lane
6
6
  * (success / error); fan-out only means something at `count >= 2`.
7
7
  */
8
8
  export declare function fanOutState(stepId: string, edges: readonly Edge[]): {
@@ -11,5 +11,5 @@ export declare function fanOutState(stepId: string, edges: readonly Edge[]): {
11
11
  };
12
12
  /** Sequential is the default, so it is expressed by the key's ABSENCE. */
13
13
  export declare function setFanOut(step: FlowStep, mode: FanOutMode): FlowStep;
14
- /** Card-badge wording: how many next steps run, and in which mode. */
14
+ /** Card-badge wording: how many next nodes run, and in which mode. */
15
15
  export declare function fanOutLabel(count: number, mode: FanOutMode): string;
@@ -0,0 +1,75 @@
1
+ import { Edge } from '@xyflow/react';
2
+ import { FlowCanvas } from './canvas-types.ts';
3
+ import { FlowStep, FlowWorkflow } from './flow-types.ts';
4
+ import { StepNode } from './flow-utils.ts';
5
+ /** Only authored graph data. Tests, samples, run paint, selection and viewport never enter history. */
6
+ export interface FlowEditSnapshot {
7
+ nodes: FlowStep[];
8
+ edges: NonNullable<FlowWorkflow["edges"]>;
9
+ connections: NonNullable<FlowWorkflow["connections"]>;
10
+ canvas: FlowCanvas;
11
+ /** Keep the live layout even when saving positions is switched off. */
12
+ positions: Record<string, {
13
+ x: number;
14
+ y: number;
15
+ }>;
16
+ }
17
+ export declare function captureFlowEdit(base: FlowWorkflow, nodes: StepNode[], edges: Edge[], connections: FlowEditSnapshot["connections"], canvas: FlowCanvas): FlowEditSnapshot;
18
+ export declare function restoreFlowEdit(base: FlowWorkflow, snapshot: FlowEditSnapshot): {
19
+ nodes: {
20
+ position: {
21
+ x: number;
22
+ y: number;
23
+ };
24
+ id: string;
25
+ data: import('./flow-utils.ts').StepNodeData;
26
+ sourcePosition?: import('@xyflow/react').Position;
27
+ targetPosition?: import('@xyflow/react').Position;
28
+ hidden?: boolean;
29
+ selected?: boolean;
30
+ dragging?: boolean;
31
+ draggable?: boolean;
32
+ selectable?: boolean;
33
+ connectable?: boolean;
34
+ deletable?: boolean;
35
+ dragHandle?: string;
36
+ width?: number;
37
+ height?: number;
38
+ initialWidth?: number;
39
+ initialHeight?: number;
40
+ parentId?: string;
41
+ zIndex?: number;
42
+ extent?: "parent" | import('@xyflow/react').CoordinateExtent | null;
43
+ expandParent?: boolean;
44
+ ariaLabel?: string;
45
+ origin?: import('@xyflow/react').NodeOrigin;
46
+ handles?: import('@xyflow/react').NodeHandle[];
47
+ measured?: {
48
+ width?: number;
49
+ height?: number;
50
+ };
51
+ type?: string | undefined;
52
+ style?: import('react').CSSProperties;
53
+ className?: string;
54
+ resizing?: boolean;
55
+ focusable?: boolean;
56
+ ariaRole?: import('react').AriaRole;
57
+ domAttributes?: Omit<import('react').HTMLAttributes<HTMLDivElement>, "id" | "style" | "className" | "draggable" | "role" | "aria-label" | "defaultValue" | "dangerouslySetInnerHTML" | keyof import('react').DOMAttributes<HTMLDivElement>>;
58
+ }[];
59
+ edges: Edge[];
60
+ };
61
+ /** Bounded, editor-session history. One text-field focus session is one undoable edit. */
62
+ export declare class FlowEditHistory {
63
+ private past;
64
+ private future;
65
+ private present;
66
+ private group;
67
+ private readonly limit;
68
+ constructor(initial: FlowEditSnapshot, limit?: number);
69
+ reset(initial: FlowEditSnapshot): void;
70
+ record(snapshot: FlowEditSnapshot, group?: object): void;
71
+ undo(): FlowEditSnapshot | undefined;
72
+ redo(): FlowEditSnapshot | undefined;
73
+ }
74
+ /** Leave native text undo and CodeMirror's own history alone. */
75
+ export declare function isFlowTextTarget(target: EventTarget | null): boolean;
@@ -0,0 +1,13 @@
1
+ import { Edge } from '@xyflow/react';
2
+ import { FlowCanvas } from './canvas-types.ts';
3
+ import { StepNode } from './flow-utils.ts';
4
+ /**
5
+ * Arrange paths and their section frames together. Membership is authored data:
6
+ * fit each frame to its members, never infer new membership from moved geometry.
7
+ * A section spanning non-members is moved with its members below the obstruction.
8
+ * Notes and empty sections retain their authored geometry.
9
+ */
10
+ export declare function relayoutFlow(nodes: StepNode[], edges: Edge[], canvas: FlowCanvas): {
11
+ nodes: StepNode[];
12
+ canvas: FlowCanvas;
13
+ };
@@ -0,0 +1,39 @@
1
+ import { Edge } from '@xyflow/react';
2
+ import { CSSProperties } from 'react';
3
+ import { StepNode } from './flow-utils.ts';
4
+ /** How many `--w6w-loop-N` tokens `_tokens.scss` defines. */
5
+ export declare const LOOP_TONES = 6;
6
+ /**
7
+ * Tone index (1-based) per For each id. Ids are visited in sorted order so the result
8
+ * does not depend on node order; each takes its hashed tone, or the next free one.
9
+ */
10
+ export declare function assignLoopTones(foreachIds: Iterable<string>): Map<string, number>;
11
+ /** The CSS colour for a tone index. */
12
+ export declare function loopToneVar(tone: number): string;
13
+ /** The loops enclosing `id`, outermost first, following each For each's own `loopOf`. */
14
+ export declare function enclosingLoops(id: string, loopOfById: ReadonlyMap<string, string | undefined>): string[];
15
+ /**
16
+ * Paint each-lane edges with the colour of the loop they belong to: the For each itself
17
+ * for its Each item link, else the loop the edge's source step runs in. Sets the
18
+ * `--w6w-loop-color` custom property the `.w6w-edge-each` rule reads, so run-state
19
+ * classes still compose over it. Same array when nothing needs painting.
20
+ */
21
+ export declare function paintLoopEdges(edges: Edge[], tones: ReadonlyMap<string, number>, loopOfById: ReadonlyMap<string, string | undefined>): Edge[];
22
+ /**
23
+ * The colour custom property a card's root carries: a For each's own tone, read by its
24
+ * Each item port. A loop-body step gets none — it looks exactly like a step outside a
25
+ * loop; its dots are what mark it.
26
+ */
27
+ export declare function loopCardStyle(id: string, tones: ReadonlyMap<string, number>): CSSProperties | undefined;
28
+ /** What the canvas paints loops from: each For each's tone, and each step's own loop. */
29
+ export type LoopPaint = {
30
+ tones: ReadonlyMap<string, number>;
31
+ loopOf: ReadonlyMap<string, string | undefined>;
32
+ };
33
+ /**
34
+ * A string that changes only when a loop's membership does — the memo key for
35
+ * {@link loopPaint}, so dragging a node does not hand every card a new context value.
36
+ */
37
+ export declare function loopPaintKey(nodes: readonly StepNode[]): string;
38
+ /** Rebuild the {@link LoopPaint} a {@link loopPaintKey} describes. */
39
+ export declare function loopPaint(key: string): LoopPaint;
@@ -0,0 +1,98 @@
1
+ import { Edge } from '@xyflow/react';
2
+ import { FlowEdge, FlowStep, FlowWorkflow } from './flow-types.ts';
3
+ import { StepNode } from './flow-utils.ts';
4
+ type UsesLike = {
5
+ uses?: {
6
+ app?: string;
7
+ action?: string;
8
+ };
9
+ };
10
+ /** A step is a foreach iff it is `@w6w/control` · `foreach` (P1). */
11
+ export declare function isForeachStep(step: UsesLike | undefined): boolean;
12
+ /**
13
+ * The step ids a control step owns — engine `controlOwnedIds`, arm for arm
14
+ * (foreach `body`, parallel `branches`, if `then`/`else`; strings at any array depth).
15
+ */
16
+ export declare function controlOwnedIds(step: FlowStep): string[];
17
+ /** Every step id owned by some control step (engine `ownedByControl`). */
18
+ export declare function ownedStepIds(steps: readonly FlowStep[]): Set<string>;
19
+ /** A canvas each-lane edge in the ONE shape load, bridge and `applyConnect` share (P3). */
20
+ export declare function eachLaneEdge(from: string, to: string, fromIsForeach: boolean, route?: string): Edge;
21
+ /**
22
+ * The body drawn from `foreachId`: every step reachable from its Each-item port
23
+ * over each-lane edges, as a topological order (Kahn over the body's own each-lane
24
+ * edges, ties broken by discovery order) so the Each-item target is first and every
25
+ * edge goes from an earlier id to a later one. A plain chain comes out exactly as
26
+ * the chain. Never includes `foreachId` or a repeat — so it terminates on a cycle
27
+ * (steps stuck in one are appended in discovery order). `claimed` (internal) stops
28
+ * the walk BEFORE a step an earlier foreach already owns.
29
+ */
30
+ export declare function loopBody(foreachId: string, nodes: readonly StepNode[], edges: readonly Edge[], claimed?: ReadonlySet<string>): string[];
31
+ /** The each-lane edges with both ends inside `body` (the For each → body[0] link is not one). */
32
+ export declare function bodyInternalEdges(body: readonly string[], edges: readonly Edge[]): Edge[];
33
+ /**
34
+ * A body is plain iff it is a chain the ordered `with.body` list already says: every
35
+ * body node has at most one outgoing each-lane edge and none carries a route. Plain
36
+ * bodies serialize as the list alone; anything else also emits its body edges.
37
+ */
38
+ export declare function isPlainBody(body: readonly string[], edges: readonly Edge[]): boolean;
39
+ /** The definition edges a canvas's non-plain loop bodies need (empty for plain chains). */
40
+ export declare function loopBodyDefinitionEdges(nodes: readonly StepNode[], edges: readonly Edge[]): FlowEdge[];
41
+ /** Every foreach's body, handled in `nodes` order so emitted bodies are disjoint (P6). */
42
+ export declare function loopBodies(nodes: readonly StepNode[], edges: readonly Edge[]): Map<string, string[]>;
43
+ /** `bodyStepId → foreachId` over the whole canvas. */
44
+ export declare function loopMembership(nodes: readonly StepNode[], edges: readonly Edge[]): Record<string, string>;
45
+ /**
46
+ * The edge set after deleting step `id` (D-A4). A body step with exactly one inbound
47
+ * and one outbound each-lane edge is bridged (`prev → next`, each lane, keeping the
48
+ * inbound edge's route when `prev` is an If/Router, `out-each` iff `prev` is the
49
+ * foreach); any other body step just loses its incident edges; deleting the foreach
50
+ * also removes the each-lane edges of its whole body (the body steps stay, as
51
+ * ordinary unconnected nodes). A non-body step is the plain incident-edge filter.
52
+ */
53
+ export declare function bridgeDeletedBodyStep(id: string, nodes: readonly StepNode[], edges: readonly Edge[]): Edge[];
54
+ /** `with.body` is the derived view of the each lane — drop it from a foreach (P1); other steps untouched. */
55
+ export declare function stripBody<T extends UsesLike & {
56
+ with?: Record<string, unknown>;
57
+ }>(step: T): T;
58
+ /** Why a connection would break the v1 linear-body rule (exhaustive; see {@link loopRefusal}). */
59
+ export type LoopRefusalReason = "error_lane_in_body" | "outside_inbound" | "loop_cycle" | "already_in_loop" | "control_in_body" | "body_branch" | "body_exit";
60
+ /**
61
+ * The loop rule for drawing `source → target` in `lane` (already promoted: a success
62
+ * connect from a body node arrives as `"each"`). First match wins. Pure; run on the
63
+ * INPUT edges, never a post-eviction set.
64
+ */
65
+ export declare function loopRefusal(source: string, target: string, lane: "success" | "error" | "each", nodes: StepNode[], edges: Edge[]): LoopRefusalReason | null;
66
+ export declare function loopRefusalMessage(reason: LoopRefusalReason): string;
67
+ /**
68
+ * Set or clear `data.loopOf` from {@link loopMembership}. Same array reference when
69
+ * nothing changed, and the same node object for every unchanged node.
70
+ */
71
+ export declare function stampLoopOf(nodes: StepNode[], edges: Edge[]): StepNode[];
72
+ /** The delete-confirm text. Byte-identical to the plain message unless a For each owns a body. */
73
+ export declare function deleteStepMessage(id: string, nodes: StepNode[], edges: Edge[]): string;
74
+ export type LoopInvalidReason = "body_not_literal" | "body_unknown_step" | "body_self" | "body_duplicate" | "body_claimed_twice" | "body_contains_control" | "body_step_has_edges" | "legacy_not_linear";
75
+ export type LoopInvalid = {
76
+ reason: LoopInvalidReason;
77
+ stepId?: string;
78
+ };
79
+ export type LoopResult = {
80
+ body: string[];
81
+ source: "body" | "legacy";
82
+ edges?: FlowEdge[];
83
+ } | {
84
+ invalid: LoopInvalid;
85
+ };
86
+ /** The edges the engine would run: declared, else the implicit chain over main-graph (non-owned) steps. */
87
+ export declare function effectiveEdges(wf: FlowWorkflow): FlowEdge[];
88
+ /** A saved For each → `body[0]` link ({@link FOREACH_EACH_ROUTE}); the canvas draws it from `with.body`. */
89
+ export declare const isEachLink: (e: FlowEdge) => boolean;
90
+ /**
91
+ * What each foreach's loop is, in a stored definition — the ONE place that knows
92
+ * the legacy-chain rule (D-A3). A foreach carrying a `body` key is judged on it;
93
+ * one without is a legacy loop iff its success-lane successors form a valid linear
94
+ * chain (every node a non-control step owned by no control, whose only inbound edge
95
+ * is from its predecessor and whose only outbound edge is a success edge onward).
96
+ */
97
+ export declare function definitionLoops(wf: FlowWorkflow): Record<string, LoopResult>;
98
+ export {};
@@ -0,0 +1,14 @@
1
+ import { FlowStep } from './flow-types.ts';
2
+ export declare const PAUSED_LABEL = "Paused \u2014 test runs stop before this node";
3
+ export declare const DISABLED_LABEL = "Disabled \u2014 skipped in every run";
4
+ export declare function isPausedStep(step: FlowStep): boolean;
5
+ export declare function isDisabledStep(step: FlowStep): boolean;
6
+ /** `step` with `ui.paused` set (on) or removed (off); an emptied `ui` is dropped. */
7
+ export declare function withPaused(step: FlowStep, on: boolean): FlowStep;
8
+ /** `step` with `disabled: true` (on) or without the key (off). */
9
+ export declare function withDisabled(step: FlowStep, on: boolean): FlowStep;
10
+ /**
11
+ * The `stopAt` of a run started from `startId`'s ▶: every paused node except the start itself
12
+ * (pressing ▶ on a paused node runs it — the pause stops runs that ARRIVE at it).
13
+ */
14
+ export declare function pausedStopAt(steps: readonly FlowStep[], startId: string): string[];
@@ -0,0 +1,69 @@
1
+ import { Edge } from '@xyflow/react';
2
+ import { LoopRefusalReason } from './flow-loop.ts';
3
+ import { CanvasLane, FlowStep, FlowWorkflowInput } from './flow-types.ts';
4
+ import { StepNode } from './flow-utils.ts';
5
+ import { StepProblem, StepProblems } from './step-problems.ts';
6
+ /**
7
+ * THE matcher: is `ref` a reference to foreach `foreachId`'s output, and what
8
+ * does it become inside the loop. `R.F.output` → `foreach.item`, `R.F.output.<rest>`
9
+ * → `foreach.item.<rest>`, `R.F.output[<rest>` → `foreach.item[<rest>`. Segment
10
+ * boundary only: `…outputs` and `for_each_10` never match.
11
+ */
12
+ export declare function loopOutputRef(ref: string, foreachId: string): string | null;
13
+ /**
14
+ * The loop problems of a stored definition, keyed by step id. Never throws and
15
+ * never mutates: any input `workflowToFlow` would reject gives `{}` (it runs in a
16
+ * host memo with no error boundary).
17
+ */
18
+ export declare function findLoopProblems(input: FlowWorkflowInput): StepProblems;
19
+ /** One editor-side parallel-write warning — the engine's `StateWriteWarning`, this code only. */
20
+ export interface StateParallelWrite {
21
+ code: "state_parallel_write";
22
+ nodes: string[];
23
+ path: string[];
24
+ }
25
+ /**
26
+ * The editor mirror of the engine's plan-time parallel-write warning (R16, plan
27
+ * §2.3; proven equal on `core/packages/expr/tests/fixtures/state-write-conflicts.json`).
28
+ * Two writers are concurrent iff they sit in different branches of one
29
+ * `@w6w/control` `parallel`; else never when they share an anchor (outermost
30
+ * owning control, else the step) or one anchor reaches the other; else iff one
31
+ * `fanOut: "parallel"` step has two distinct outgoing edges in the SAME lane
32
+ * whose targets reach the two anchors. Each overlapping pair of static write
33
+ * paths gives one warning on the shorter path; deduped, sorted by the engine's
34
+ * order (`code`, then `nodes`, then `path`). Never throws (→ `[]`), never mutates.
35
+ */
36
+ export declare function stateWriteConflicts(input: FlowWorkflowInput): StateParallelWrite[];
37
+ /**
38
+ * `stateWriteConflicts` as canvas problems: each warning lands on both of its
39
+ * steps, naming the other one (`with`). Never throws (→ `{}`), never mutates.
40
+ */
41
+ export declare function findStateWriteProblems(input: FlowWorkflowInput): StepProblems;
42
+ /**
43
+ * Apply a problem's fix to `step`, returning a new step. Re-scans the step it is
44
+ * given (never trusts the problem's `refs` snapshot); the SAME reference comes
45
+ * back for a problem with no fix or nothing left to fix. Never mutates.
46
+ */
47
+ export declare function applyStepProblemFix(step: FlowStep, problem: StepProblem): FlowStep;
48
+ /**
49
+ * The lane of a drag, read from the handle the pointer went down on. xyflow's
50
+ * `connectionState.fromHandle` resolves an id-less handle (the Done port) to the
51
+ * node's FIRST handle (`out-each`), so it cannot tell Done from Each item;
52
+ * `onConnectStart` reports the real `handleId` (null for Done). Falls back to
53
+ * `fromHandle.id` when no matching start was recorded.
54
+ */
55
+ export declare function dragLane(start: {
56
+ nodeId: string | null;
57
+ handleId: string | null;
58
+ handleType: string | null;
59
+ } | null, fromNodeId: string, fromHandle: {
60
+ id?: string | null;
61
+ type?: string;
62
+ } | null | undefined): CanvasLane;
63
+ /**
64
+ * The loop reason a source-handle drag dropped ON an existing node is refused for
65
+ * (`null` when the loop rule has nothing to say). `onConnectEnd` shows it as the
66
+ * node-anchored notice instead of opening the step builder. The lane is promoted the
67
+ * way `planConnect` does: a success drag from a body node extends the body.
68
+ */
69
+ export declare function connectDropRefusal(source: string, target: string, lane: "success" | "error" | "each", nodes: StepNode[], edges: Edge[]): LoopRefusalReason | null;
@@ -0,0 +1,20 @@
1
+ import { StateWriteConfig } from '@w6w/expr';
2
+ import { FlowStep } from './flow-types.ts';
3
+ export type StateMode = StateWriteConfig["mode"];
4
+ /** The five modes, in the order the control lists them. */
5
+ export declare const STATE_MODES: readonly StateMode[];
6
+ export declare const STATE_MODE_LABELS: Record<StateMode, string>;
7
+ type StepLike = Pick<FlowStep, "id" | "uses" | "state">;
8
+ /** What the step writes, the default applied (R14: Set data merges at root, every other step is Off). */
9
+ export declare function stateWriteFor(step: StepLike): StateWriteConfig;
10
+ /**
11
+ * The step with `config` as its state write. The default is the key's ABSENCE
12
+ * (Off on an ordinary step, Merge at root on Set data), so writing the default
13
+ * removes `state`. Never mutates; every other key passes through.
14
+ */
15
+ export declare function setStateWrite<S extends StepLike>(step: S, config: StateWriteConfig): S;
16
+ /** The config written when the author picks `mode`: named after the step so it is valid at once. */
17
+ export declare function seedStateWrite(step: Pick<FlowStep, "id">, mode: StateMode): StateWriteConfig;
18
+ /** The control shows for app steps and triggers; every control-flow step (If, For each, Parallel, Race) hides it. */
19
+ export declare function stateWriteVisible(step: Pick<FlowStep, "uses">): boolean;
20
+ export {};