@uipath/maestro-builder-sdk 6.29.1 → 6.29.2

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.
@@ -35,4 +35,17 @@ export declare function expandSourceHandles(def: Def, inputs: unknown): string[]
35
35
  * whose handles cannot be known: such a node has no fan-out to route.
36
36
  */
37
37
  export declare function forwardFanOut(def: Def, inputs: unknown): string[] | undefined;
38
+ /**
39
+ * The port a node's main path leaves on — the edge a plain `.step()` writes
40
+ * and a following step continues from: the first concrete forward source
41
+ * handle (`isDefaultForType` wins), else `output`. `undefined` when every
42
+ * forward source handle is templated (`case-{item.id}`), so no main path leaves
43
+ * the node.
44
+ *
45
+ * `serialize` writes the main-path edge on this port, and `decompile` reads the
46
+ * main path back off it, so the two must agree: a `core.logic.agentic-switch`
47
+ * (`case-{item.id}` + `default`) continues on `default`, and each other wired
48
+ * exit is a `.stepToList('case-<id>', …)` (#1214).
49
+ */
50
+ export declare function contributionPort(def: Def): string | undefined;
38
51
  export {};
@@ -72,3 +72,24 @@ export function forwardFanOut(def, inputs) {
72
72
  const handles = expandSourceHandles(def, inputs)?.filter((h) => !NOT_FORWARD.has(h));
73
73
  return handles !== undefined && handles.length > 1 ? handles : undefined;
74
74
  }
75
+ /**
76
+ * The port a node's main path leaves on — the edge a plain `.step()` writes
77
+ * and a following step continues from: the first concrete forward source
78
+ * handle (`isDefaultForType` wins), else `output`. `undefined` when every
79
+ * forward source handle is templated (`case-{item.id}`), so no main path leaves
80
+ * the node.
81
+ *
82
+ * `serialize` writes the main-path edge on this port, and `decompile` reads the
83
+ * main path back off it, so the two must agree: a `core.logic.agentic-switch`
84
+ * (`case-{item.id}` + `default`) continues on `default`, and each other wired
85
+ * exit is a `.stepToList('case-<id>', …)` (#1214).
86
+ */
87
+ export function contributionPort(def) {
88
+ const groups = Array.isArray(def?.handleConfiguration) ? def.handleConfiguration : [];
89
+ const handles = groups.flatMap((g) => (g?.handles ?? []));
90
+ const sources = handles.filter((h) => h?.type === 'source' && h?.id !== 'error');
91
+ const concrete = sources.filter((h) => typeof h?.id === 'string' && !h.id.includes('{'));
92
+ if (concrete.length === 0 && sources.length > 0)
93
+ return undefined;
94
+ return concrete.find((h) => h?.isDefaultForType)?.id ?? concrete[0]?.id ?? 'output';
95
+ }
package/dist/decompile.js CHANGED
@@ -46,7 +46,7 @@ import { mapJsCode } from './core/js-code-spans.js';
46
46
  import { missingBindingLabels } from './core/binding-messages.js';
47
47
  import { prepareCommand } from './core/cli-spelling.js';
48
48
  import { OUTCOME_HANDLE_NODE_TYPES } from './core/hitl-routing.js';
49
- import { forwardFanOut } from './core/definition-handles.js';
49
+ import { contributionPort, forwardFanOut } from './core/definition-handles.js';
50
50
  import { STICKY_NOTE_TYPE } from './core/definitionless-node-types.js';
51
51
  import { eventFilterFromTreeLeaf } from './core/event-filters.js';
52
52
  import { sdkFormFieldType } from './core/form-field-type.js';
@@ -532,11 +532,34 @@ class Graph {
532
532
  successorEdges(id) {
533
533
  const primary = this.primaryOutcomePort(id);
534
534
  const forked = this.fullyWiredExits(id);
535
+ const side = forked ? undefined : this.rawSidePorts(id);
535
536
  return this.outEdges(id).filter((e) => e.sourcePort !== 'error'
536
537
  && !ARTIFACT_PORTS.has(e.sourcePort)
537
538
  && (forked?.has(e.sourcePort)
538
539
  || (!e.sourcePort.startsWith('branch-')
539
- && (!e.sourcePort.startsWith('outcome-') || e.sourcePort === primary))));
540
+ && (!e.sourcePort.startsWith('outcome-') || e.sourcePort === primary)
541
+ && !side?.has(e.sourcePort))));
542
+ }
543
+ /**
544
+ * A multi-exit `rawNode`'s SIDE exits when not every exit is wired: each
545
+ * declared exit except the one its main path leaves on (`contributionPort`,
546
+ * the port serialize writes a plain `.step()`'s edge on — `default` for a
547
+ * `core.logic.agentic-switch`). Each is recovered as a
548
+ * `.stepToList('<port>', …)` after the `.step`, as `branch-*` and non-primary
549
+ * `outcome-*` exits are. `undefined` for every other node, and for a raw node
550
+ * whose exits are all wired (that one is a `.stepSwitch`).
551
+ *
552
+ * Without this, a partially wired agentic switch had two successors and no
553
+ * merge join, so the walk emitted an unstructured-fan-out TODO and dropped
554
+ * every arm (#1214).
555
+ */
556
+ rawSidePorts(id) {
557
+ const node = this.byId.get(id);
558
+ const exits = node ? rawNodeExits(node) : undefined;
559
+ if (!node || exits === undefined || this.fullyWiredExits(id) !== undefined)
560
+ return undefined;
561
+ const main = contributionPort(rawNodeDefinition(node));
562
+ return new Set(exits.map((e) => e.port).filter((p) => p !== main));
540
563
  }
541
564
  }
542
565
  // ─── Node → step-spec source ──────────────────────────────────────────────────
@@ -2397,6 +2420,17 @@ function region(cursor, stop, scope, ipdom, ctx, refInto) {
2397
2420
  segs.push(`.stepToList(${tsLiteral(oe.sourcePort)}, ${armCb('p', outSegs)})`);
2398
2421
  }
2399
2422
  }
2423
+ // A partially wired multi-exit raw node (an agentic switch with one choice
2424
+ // unwired): every wired exit but the main path's is its own port list, and
2425
+ // an unwired one stays unwired (#1214).
2426
+ const rawSide = ctx.graph.rawSidePorts(node.id);
2427
+ for (const re of rawSide ? ctx.graph.outEdges(node.id).filter((e) => rawSide.has(e.sourcePort)) : []) {
2428
+ const mainReach = forwardReach(succ, scope, ctx.graph);
2429
+ const sideSegs = edgeRegion(re, EXIT, scope, ipdom, ctx, mainReach);
2430
+ if (sideSegs.length === 0)
2431
+ sideSegs.push('.return({})');
2432
+ segs.push(`.stepToList(${tsLiteral(re.sourcePort)}, ${armCb('p', sideSegs)})`);
2433
+ }
2400
2434
  // A body edge into the enclosing container's break handle: the path exits
2401
2435
  // the loop here. Emit `.break()` and end the region — recompiling recreates
2402
2436
  // exactly this edge and adds no continue back-edge for the path.
@@ -2565,13 +2599,20 @@ function declaredExitsOfNode(node) {
2565
2599
  */
2566
2600
  function rawNodeExits(node) {
2567
2601
  const type = String(node.type);
2602
+ // A registered type (an HTTP call, say) answers from its own `exits`.
2603
+ if (registeredImpl(node) !== undefined)
2604
+ return undefined;
2568
2605
  if (Object.values(T).includes(type) || isTrigger(type))
2569
2606
  return undefined;
2570
2607
  if (['uipath.connector.', 'uipath.core.', 'uipath.ixp.', 'uipath.agent.', 'uipath.conversational.']
2571
2608
  .some((p) => type.startsWith(p)))
2572
2609
  return undefined;
2573
- const def = runDefs.get(`${type}@${node.typeVersion}`) ?? runDefs.get(type);
2574
- return forwardFanOut(def, node.inputs)?.map((port) => ({ value: port, port }));
2610
+ return forwardFanOut(rawNodeDefinition(node), node.inputs)?.map((port) => ({ value: port, port }));
2611
+ }
2612
+ /** The definition the file carries for `node`'s type (its version's, else the type's). */
2613
+ function rawNodeDefinition(node) {
2614
+ const type = String(node.type);
2615
+ return runDefs.get(`${type}@${node.typeVersion}`) ?? runDefs.get(type);
2575
2616
  }
2576
2617
  /**
2577
2618
  * Split a switch case expression into `<discriminant> === <literal>`, the only
package/dist/serialize.js CHANGED
@@ -57,6 +57,7 @@ import { resolveSchemaVersion } from './schema-version.js';
57
57
  import { planFlowGlobals, planSharedInputs, sharedInputRoots } from './flow-variables.js';
58
58
  import { outputBackingGlobal, planOutputBacking } from './flow-output-backing.js';
59
59
  import { mapJsCode } from './core/js-code-spans.js';
60
+ import { contributionPort } from './core/definition-handles.js';
60
61
  import { prepareCommand } from './core/cli-spelling.js';
61
62
  import { AGENTIC_PROCESS_FORMS } from './core/agentic-process-forms.js';
62
63
  import { resourceNodeSortOrder } from './core/process-resource-sort-order.js';
@@ -3531,18 +3532,6 @@ function declaresForwardPort(def, port) {
3531
3532
  return port.length > head.length + tail.length && port.startsWith(head) && port.endsWith(tail);
3532
3533
  });
3533
3534
  }
3534
- /**
3535
- * Forward port: the first concrete source handle (`isDefaultForType` wins), else `output`;
3536
- * `undefined` when every forward source handle is templated (`case-{item.id}`).
3537
- */
3538
- function contributionPort(def) {
3539
- const handles = (def?.handleConfiguration ?? []).flatMap((g) => g?.handles ?? []);
3540
- const sources = handles.filter((h) => h?.type === 'source' && h?.id !== 'error');
3541
- const concrete = sources.filter((h) => typeof h?.id === 'string' && !h.id.includes('{'));
3542
- if (concrete.length === 0 && sources.length > 0)
3543
- return undefined;
3544
- return concrete.find((h) => h?.isDefaultForType)?.id ?? concrete[0]?.id ?? 'output';
3545
- }
3546
3535
  /**
3547
3536
  * Render a switch case value as a JS literal for the case expression.
3548
3537
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uipath/maestro-builder-sdk",
3
- "version": "6.29.1",
3
+ "version": "6.29.2",
4
4
  "description": "Build UiPath Flow, Case, and BPMN artifacts by writing TypeScript.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://docs.uipath.com/maestro",
@@ -92,5 +92,5 @@
92
92
  "@types/node": "^22.7.0",
93
93
  "esbuild": "^0.28.1"
94
94
  },
95
- "gitref": "5dee9e0ebdef1bca6c586f4802285a0f429ae432"
95
+ "gitref": "b0d822e147a21ef1051b36c474b303839e980b13"
96
96
  }