@uipath/maestro-builder-sdk 6.16.10 → 6.16.12

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.
@@ -279,10 +279,21 @@ export function solutionConnectionIds(cwd, connectorKey) {
279
279
  * module's rule intact — ambiguity is never resolved by guessing.
280
280
  */
281
281
  export async function candidateConnections(client, connectorKey, name) {
282
+ // Both listings are always needed here, so they run at once: each is a `uip`
283
+ // process and a tenant round trip (~1 s each, measured in the n8n-vs-flow
284
+ // adhoc run of 2026-10-06), and neither depends on the other. Started in
285
+ // own-folder-then-tenant order, merged in that order, and a failure is
286
+ // reported in that order too — the result is what the sequential loop gave.
287
+ const settled = await Promise.allSettled([false, true].map((allFolders) => listConnections(client, { allFolders })));
288
+ const listings = settled.map((outcome) => {
289
+ if (outcome.status === "rejected")
290
+ throw outcome.reason;
291
+ return outcome.value;
292
+ });
282
293
  const ordered = [];
283
294
  const seen = new Set();
284
- for (const allFolders of [false, true]) {
285
- for (const row of enabledConnections(await listConnections(client, { allFolders }), connectorKey, name)) {
295
+ for (const listing of listings) {
296
+ for (const row of enabledConnections(listing, connectorKey, name)) {
286
297
  const identifier = pyStrAny(pyGet(row, "Id", ""));
287
298
  if (identifier && seen.has(identifier))
288
299
  continue;
@@ -88,6 +88,17 @@ export declare function serializeWithSidecars(built: BuiltFlow, opts?: Serialize
88
88
  }>;
89
89
  };
90
90
  export declare function serialize(built: BuiltFlow, opts?: SerializeOpts): any;
91
+ /**
92
+ * Node ids and edge ids are each one namespace over the WHOLE file: the root
93
+ * and every `subflows` entry. Pack flattens subflows into one BPMN document, so
94
+ * two scopes sharing an id make it drop one element or crash in layout
95
+ * (`Cannot read properties of undefined (reading 'get')`), while `check` and
96
+ * `uip maestro flow validate` both pass. #938 shipped that way for `.stepToRef`
97
+ * edges; this check stops the next id scheme that forgets the file is shared.
98
+ *
99
+ * @throws Error naming each duplicated id and the scopes that carry it.
100
+ */
101
+ export declare function assertFileWideIdsUnique(flow: any): void;
91
102
  /**
92
103
  * Every `<bindings.NAME>` placeholder a node's definition carries in its
93
104
  * `model.context` must resolve to a `bindings[]` entry for THAT node.
package/dist/serialize.js CHANGED
@@ -3633,14 +3633,15 @@ const STICKY_NOTE_TYPE_VERSION = '1.0.0';
3633
3633
  * — a note colliding with a step would be silently dropped by the id-keyed
3634
3634
  * layout and merge paths downstream.
3635
3635
  */
3636
- function emitStickyNotes(built, scope) {
3636
+ function emitStickyNotes(built, scope, claimNoteId) {
3637
3637
  const notes = built.stickyNotes;
3638
3638
  if (!notes?.length)
3639
3639
  return;
3640
3640
  const taken = new Set(scope.nodes.map((n) => n.id));
3641
3641
  const seen = new Set();
3642
3642
  notes.forEach((note, i) => {
3643
- const id = note.id ?? `stickyNote_${i + 1}`;
3643
+ const authorId = note.id ?? `stickyNote_${i + 1}`;
3644
+ const id = authorId;
3644
3645
  if (taken.has(id)) {
3645
3646
  throw new Error(`.stickyNotes(): id "${id}" is already a node in this flow. A sticky note is a real node, `
3646
3647
  + `so its id has to be unique — name it something else.`);
@@ -3657,12 +3658,18 @@ function emitStickyNotes(built, scope) {
3657
3658
  if ((note.width === undefined) !== (note.height === undefined)) {
3658
3659
  throw new Error(`.stickyNotes(): note "${id}" needs both width and height, or neither.`);
3659
3660
  }
3661
+ // Node ids are one namespace over the whole file (#938): pack merges every
3662
+ // subflow into one BPMN document. The author's id is kept unless another
3663
+ // scope already holds it — a child with notes, called from two steps — and
3664
+ // `.layout()` finds a renamed note through `noteNodeId`.
3665
+ const emittedId = claimNoteId(authorId);
3666
+ (scope.noteNodeId ??= new Map()).set(authorId, emittedId);
3660
3667
  // The envelope Studio Web writes for a note (flow-v1 canvas: the add path
3661
3668
  // sets `typeVersion: '1.0.0'` and no display, and `nodeToInstance` persists
3662
3669
  // `display` as `{}`). Both fields are required on every node by
3663
3670
  // `flow validate`, manifest or not (#869).
3664
3671
  scope.nodes.push({
3665
- id,
3672
+ id: emittedId,
3666
3673
  type: 'stickyNote',
3667
3674
  typeVersion: STICKY_NOTE_TYPE_VERSION,
3668
3675
  ui: {
@@ -3685,7 +3692,9 @@ function applyAuthorLayout(layout, scope, where) {
3685
3692
  if (!layout)
3686
3693
  return undefined;
3687
3694
  const resolveId = (key) => {
3688
- const id = scope.stepNodeId.get(key) ?? (scope.nodes.some((n) => n.id === key) ? key : undefined);
3695
+ const id = scope.stepNodeId.get(key)
3696
+ ?? scope.noteNodeId?.get(key)
3697
+ ?? (scope.nodes.some((n) => n.id === key) ? key : undefined);
3689
3698
  if (!id) {
3690
3699
  throw new Error(`${where}.layout: "${key}" names no step or node in this flow. Layout keys are the flow's own step names (plus the trigger id and "end").`);
3691
3700
  }
@@ -4366,20 +4375,18 @@ function newScope(outputs, vars = []) {
4366
4375
  * exact class of failure `.onError` used to have (an error path that vanishes in
4367
4376
  * conversion with every rung green). So it throws instead.
4368
4377
  */
4369
- function resolveRejoins(scope) {
4378
+ function resolveRejoins(scope, addEdge) {
4370
4379
  for (const { from, target } of scope.pendingEdges) {
4371
4380
  const id = scope.stepNodeId.get(target);
4372
4381
  if (!id) {
4373
4382
  throw new Error(`.stepToRef('${target}') names a step this flow does not have. Known steps: `
4374
4383
  + `${[...scope.stepNodeId.keys()].join(', ') || '(none)'}.`);
4375
4384
  }
4376
- scope.edges.push({
4377
- id: `edge_ref_${scope.edges.length}`,
4378
- sourceNodeId: from.node,
4379
- sourcePort: from.port,
4380
- targetNodeId: id,
4381
- targetPort: 'input',
4382
- });
4385
+ // Through the serializer's own `addEdge`, so the id comes from the one
4386
+ // file-wide counter. A per-scope count (`edge_ref_${scope.edges.length}`)
4387
+ // gave two subflow copies of one child the same edge id; pack then dropped
4388
+ // one copy's edge or crashed in layout (flow-builder-sdk#938).
4389
+ addEdge(scope, from, id);
4383
4390
  }
4384
4391
  scope.pendingEdges.length = 0;
4385
4392
  }
@@ -4653,6 +4660,18 @@ export function serialize(built, opts = {}) {
4653
4660
  takenIds.add(id);
4654
4661
  return id;
4655
4662
  }
4663
+ /**
4664
+ * A sticky note's id: the author's id as written when nothing in the file
4665
+ * holds it yet, else `claimId`'s de-duplicated form. Not sanitized first, so
4666
+ * an explicit note id the file does not collide on is emitted unchanged.
4667
+ */
4668
+ function claimNoteId(base) {
4669
+ if (!takenIds.has(base)) {
4670
+ takenIds.add(base);
4671
+ return base;
4672
+ }
4673
+ return claimId(base);
4674
+ }
4656
4675
  /**
4657
4676
  * `uid` for a node that IS an author's step, recording which id that step got.
4658
4677
  *
@@ -4672,8 +4691,9 @@ export function serialize(built, opts = {}) {
4672
4691
  * Base and quick-form human tasks emitted so far, by node id. `addEdge` holds
4673
4692
  * every edge out of one to the product's handle rule (`hitlExitViolation`), so
4674
4693
  * `completed` on a task with outcomes fails at the edge that would write it;
4675
- * `assertHitlExitInvariant` repeats the check over each finished scope for the
4676
- * edges that do not come through here (parked `.stepToRef` rejoins).
4694
+ * `assertHitlExitInvariant` repeats the check over each finished scope, as a
4695
+ * guard for any future edge that is pushed without coming through here (parked
4696
+ * `.stepToRef` rejoins now do come through here, #938).
4677
4697
  */
4678
4698
  const hitlNodes = new Map();
4679
4699
  /**
@@ -4796,10 +4816,10 @@ export function serialize(built, opts = {}) {
4796
4816
  globals.push({ id: o.name, name: o.name, direction: 'out', type: o.type, ...varMeta(o) });
4797
4817
  for (const vd of childGlobals.vars)
4798
4818
  globals.push({ id: vd.name, name: vd.name, direction: 'inout', type: vd.type, ...varMeta(vd) });
4799
- resolveRejoins(scope);
4819
+ resolveRejoins(scope, addEdge);
4800
4820
  assertHitlExitInvariant(scope.nodes, scope.edges);
4801
4821
  typeAgentInputSchemas(scope, globals);
4802
- emitStickyNotes(child, scope);
4822
+ emitStickyNotes(child, scope, claimNoteId);
4803
4823
  const layoutEdges = applyAuthorLayout(child.layout, scope, `subflow "${nodeId}"`);
4804
4824
  assertNodeEnvelope(scope.nodes, `subflow "${nodeId}"`);
4805
4825
  return {
@@ -6964,10 +6984,10 @@ export function serialize(built, opts = {}) {
6964
6984
  globals.push({ id: inp.name, name: inp.name, direction: 'in', type: inp.type, triggerNodeId: ep.id, ...varMeta(inp) });
6965
6985
  }
6966
6986
  }
6967
- resolveRejoins(root);
6987
+ resolveRejoins(root, addEdge);
6968
6988
  assertHitlExitInvariant(root.nodes, root.edges);
6969
6989
  typeAgentInputSchemas(root, globals);
6970
- emitStickyNotes(built, root);
6990
+ emitStickyNotes(built, root, claimNoteId);
6971
6991
  const rootLayoutEdges = applyAuthorLayout(built.layout, root, `flow "${built.id}"`);
6972
6992
  assertNodeEnvelope(root.nodes, `flow "${built.id}"`);
6973
6993
  // The FILE-FORMAT version this document declares: the author's
@@ -7002,9 +7022,44 @@ export function serialize(built, opts = {}) {
7002
7022
  // so every family and every emission order is covered by one rule, not by
7003
7023
  // each call site remembering it (flow-builder-sdk#865/#866).
7004
7024
  assertBindingPlaceholdersResolve(flow);
7025
+ assertFileWideIdsUnique(flow);
7005
7026
  connectionConnectors(flow);
7006
7027
  return flow;
7007
7028
  }
7029
+ /**
7030
+ * Node ids and edge ids are each one namespace over the WHOLE file: the root
7031
+ * and every `subflows` entry. Pack flattens subflows into one BPMN document, so
7032
+ * two scopes sharing an id make it drop one element or crash in layout
7033
+ * (`Cannot read properties of undefined (reading 'get')`), while `check` and
7034
+ * `uip maestro flow validate` both pass. #938 shipped that way for `.stepToRef`
7035
+ * edges; this check stops the next id scheme that forgets the file is shared.
7036
+ *
7037
+ * @throws Error naming each duplicated id and the scopes that carry it.
7038
+ */
7039
+ export function assertFileWideIdsUnique(flow) {
7040
+ const scopes = [['root', flow]];
7041
+ for (const [name, sub] of Object.entries(flow?.subflows ?? {}))
7042
+ scopes.push([`subflows.${name}`, sub]);
7043
+ const dupes = [];
7044
+ for (const kind of ['nodes', 'edges']) {
7045
+ const seen = new Map();
7046
+ for (const [where, scope] of scopes) {
7047
+ for (const el of Array.isArray(scope?.[kind]) ? scope[kind] : []) {
7048
+ const id = el?.id;
7049
+ if (typeof id !== 'string')
7050
+ continue;
7051
+ const first = seen.get(id);
7052
+ if (first !== undefined)
7053
+ dupes.push(`${kind.slice(0, -1)} id "${id}" in ${first} and ${where}`);
7054
+ else
7055
+ seen.set(id, where);
7056
+ }
7057
+ }
7058
+ }
7059
+ if (dupes.length) {
7060
+ throw new Error(`serialize: ids must be unique across the root and all subflows; duplicated: ${dupes.join('; ')}.`);
7061
+ }
7062
+ }
7008
7063
  /**
7009
7064
  * Every `<bindings.NAME>` placeholder a node's definition carries in its
7010
7065
  * `model.context` must resolve to a `bindings[]` entry for THAT node.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uipath/maestro-builder-sdk",
3
- "version": "6.16.10",
3
+ "version": "6.16.12",
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": "2e666ec2dd9e7baa4716a2a0d21ee86484b0de89"
95
+ "gitref": "a17e4a3af0cefe7881625c5888b282c4ea4a0546"
96
96
  }