nucleus-core-ts 0.10.124 → 0.10.126

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/dist/.build-ok CHANGED
@@ -1 +1 @@
1
- 0.10.124
1
+ 0.10.126
@@ -6,6 +6,7 @@ import { useEffect, useEffectEvent, useRef, useState } from 'react';
6
6
  import { cn } from '../../../utils/cn';
7
7
  import { newId } from '../../../utils/newId';
8
8
  import { DEFAULT_VERIFICATION_FLOW_LABELS, setActiveLabels } from '../labels';
9
+ import { assignStepOrders } from '../stepOrder';
9
10
  import { useVerificationFlowStore } from '../store';
10
11
  import { getVerificationFlowTheme } from '../theme';
11
12
  import { setActiveTheme } from '../theme/store';
@@ -177,43 +178,26 @@ function buildSavePayload(flowId, entityName, flowName, flowDescription, trigger
177
178
  label: String(e.label || ''),
178
179
  animated: true
179
180
  }));
180
- const stepNodeIds = new Set(nodes.filter((n)=>{
181
+ /*
182
+ * `step_order` comes from the DRAWING, through one shared pure function.
183
+ *
184
+ * This used to be an inline walk whose seed list and neighbour order
185
+ * followed whatever order the nodes and edges arrived in — heap order, for a
186
+ * flow loaded from the database. Re-saving a flow without touching it could
187
+ * therefore renumber its steps and hand the first decision to a different
188
+ * reviewer. `assignStepOrders` is deterministic and has a test that fails
189
+ * without it; `getFlow` returns ordered rows so both layers agree.
190
+ */ const stepNodeIds = new Set(nodes.filter((n)=>{
181
191
  const d = n.data;
182
192
  return (d.nodeType || n.type) === 'step';
183
193
  }).map((n)=>n.id));
184
- const adjacency = {};
185
- for (const e of flowEdges){
186
- if (!adjacency[e.source_node_id]) adjacency[e.source_node_id] = [];
187
- adjacency[e.source_node_id]?.push(e.target_node_id);
188
- }
189
- const findNextSteps = (fromId, visited = new Set())=>{
190
- const result = [];
191
- for (const t of adjacency[fromId] || []){
192
- if (visited.has(t)) continue;
193
- visited.add(t);
194
- if (stepNodeIds.has(t)) result.push(t);
195
- else result.push(...findNextSteps(t, visited));
196
- }
197
- return result;
198
- };
199
- const stepGraph = {};
200
- const incomingSteps = new Set();
201
- for (const sid of stepNodeIds){
202
- stepGraph[sid] = findNextSteps(sid);
203
- for (const next of stepGraph[sid])incomingSteps.add(next);
204
- }
205
- const stepOrderMap = {};
206
- const queue = [
207
- ...stepNodeIds
208
- ].filter((id)=>!incomingSteps.has(id));
209
- let order = 1;
210
- while(queue.length > 0){
211
- const current = queue.shift();
212
- if (!current) break;
213
- if (stepOrderMap[current] !== undefined) continue;
214
- stepOrderMap[current] = order++;
215
- for (const next of stepGraph[current] || [])queue.push(next);
216
- }
194
+ const stepOrderMap = assignStepOrders({
195
+ stepNodeIds,
196
+ edges: flowEdges.map((e)=>({
197
+ source: e.source_node_id,
198
+ target: e.target_node_id
199
+ }))
200
+ });
217
201
  const steps = nodes.map((n)=>{
218
202
  const d = n.data;
219
203
  const nodeType = String(d.nodeType || n.type);
@@ -819,14 +803,14 @@ export function VerificationFlowPage({ entityName, title, subtitle, flowListActi
819
803
  value: "update",
820
804
  children: say.triggerOnUpdate
821
805
  }),
822
- /*#__PURE__*/ _jsx("option", {
823
- value: "delete",
824
- children: say.triggerOnDelete
825
- }),
826
806
  /*#__PURE__*/ _jsx("option", {
827
807
  value: "manual",
828
808
  children: say.triggerOnManual
829
- })
809
+ }),
810
+ flowTriggerOn === 'delete' ? /*#__PURE__*/ _jsx("option", {
811
+ value: "delete",
812
+ children: say.triggerOnDelete
813
+ }) : null
830
814
  ]
831
815
  })
832
816
  ]
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Which step comes first, second, third — derived from the DRAWING alone.
3
+ *
4
+ * The editor does not store a step's position; it re-derives it from the graph
5
+ * on every save. That derivation used to depend on the order the nodes and
6
+ * edges happened to arrive in, and for a flow loaded from the database that is
7
+ * heap order, which changes after updates. So opening a saved flow and pressing
8
+ * save without touching anything could renumber the steps — a different
9
+ * reviewer asked first, on a chain whose entire purpose is who is asked when.
10
+ *
11
+ * Extracted as a pure function so that property can be tested without a
12
+ * renderer: the same graph must always produce the same numbering, whatever
13
+ * order its nodes and edges are handed over in.
14
+ *
15
+ * Steps are numbered breadth-first from the steps nothing points at. A
16
+ * non-step node (a verifier, a notification) is walked THROUGH, so a step
17
+ * wired to the next one via an intermediate node still counts as adjacent.
18
+ * Ties — several roots, several next steps — are broken by node id, which is
19
+ * arbitrary but stable, and that is the whole point.
20
+ */
21
+ export type StepOrderGraph = {
22
+ /** Every node, with the ids of the step nodes among them. */
23
+ stepNodeIds: Iterable<string>;
24
+ /** Directed edges, source → target. */
25
+ edges: Array<{
26
+ source: string;
27
+ target: string;
28
+ }>;
29
+ };
30
+ export declare function assignStepOrders(graph: StepOrderGraph): Record<string, number>;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Which step comes first, second, third — derived from the DRAWING alone.
3
+ *
4
+ * The editor does not store a step's position; it re-derives it from the graph
5
+ * on every save. That derivation used to depend on the order the nodes and
6
+ * edges happened to arrive in, and for a flow loaded from the database that is
7
+ * heap order, which changes after updates. So opening a saved flow and pressing
8
+ * save without touching anything could renumber the steps — a different
9
+ * reviewer asked first, on a chain whose entire purpose is who is asked when.
10
+ *
11
+ * Extracted as a pure function so that property can be tested without a
12
+ * renderer: the same graph must always produce the same numbering, whatever
13
+ * order its nodes and edges are handed over in.
14
+ *
15
+ * Steps are numbered breadth-first from the steps nothing points at. A
16
+ * non-step node (a verifier, a notification) is walked THROUGH, so a step
17
+ * wired to the next one via an intermediate node still counts as adjacent.
18
+ * Ties — several roots, several next steps — are broken by node id, which is
19
+ * arbitrary but stable, and that is the whole point.
20
+ */ export function assignStepOrders(graph) {
21
+ const stepIds = new Set(graph.stepNodeIds);
22
+ const adjacency = {};
23
+ for (const e of graph.edges){
24
+ if (!adjacency[e.source]) adjacency[e.source] = [];
25
+ adjacency[e.source]?.push(e.target);
26
+ }
27
+ const nextSteps = (fromId, visited = new Set())=>{
28
+ const result = [];
29
+ for (const t of [
30
+ ...adjacency[fromId] || []
31
+ ].sort()){
32
+ if (visited.has(t)) continue;
33
+ visited.add(t);
34
+ if (stepIds.has(t)) result.push(t);
35
+ else result.push(...nextSteps(t, visited));
36
+ }
37
+ return result;
38
+ };
39
+ const stepGraph = {};
40
+ const hasIncoming = new Set();
41
+ // Sorted so `stepGraph`'s own construction does not depend on set insertion
42
+ // order either.
43
+ for (const sid of [
44
+ ...stepIds
45
+ ].sort()){
46
+ stepGraph[sid] = nextSteps(sid);
47
+ for (const n of stepGraph[sid])hasIncoming.add(n);
48
+ }
49
+ const order = {};
50
+ const queue = [
51
+ ...stepIds
52
+ ].filter((id)=>!hasIncoming.has(id)).sort();
53
+ let next = 1;
54
+ while(queue.length > 0){
55
+ const current = queue.shift();
56
+ if (!current) break;
57
+ if (order[current] !== undefined) continue;
58
+ order[current] = next++;
59
+ for (const n of stepGraph[current] || [])queue.push(n);
60
+ }
61
+ return order;
62
+ }