@uipath/maestro-builder-sdk 6.31.4 → 6.31.5

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/merge.d.ts CHANGED
@@ -28,7 +28,9 @@
28
28
  * preserved from the original; `subflows` and edges (with their ids) come from
29
29
  * `edited`; `definitions` and `bindings` are unioned
30
30
  * (original wins on conflict, since the original's are the tenant-resolved
31
- * authority). A binding row's identity is what it binds, not its row id. Node ids are stable by construction (decompile names every step by
31
+ * authority). A binding row's identity is what it binds, not its row id, and a
32
+ * row the no-edit recompile minted only for nodes merge kept from the original
33
+ * is not carried (see withoutRowsMintedForKeptNodes). Node ids are stable by construction (decompile names every step by
32
34
  * its node id), which is what lets every re-attach key on id.
33
35
  *
34
36
  * ## Definitions are keyed by `nodeType@version`, not by `nodeType`
package/dist/merge.js CHANGED
@@ -28,7 +28,9 @@
28
28
  * preserved from the original; `subflows` and edges (with their ids) come from
29
29
  * `edited`; `definitions` and `bindings` are unioned
30
30
  * (original wins on conflict, since the original's are the tenant-resolved
31
- * authority). A binding row's identity is what it binds, not its row id. Node ids are stable by construction (decompile names every step by
31
+ * authority). A binding row's identity is what it binds, not its row id, and a
32
+ * row the no-edit recompile minted only for nodes merge kept from the original
33
+ * is not carried (see withoutRowsMintedForKeptNodes). Node ids are stable by construction (decompile names every step by
32
34
  * its node id), which is what lets every re-attach key on id.
33
35
  *
34
36
  * ## Definitions are keyed by `nodeType@version`, not by `nodeType`
@@ -212,12 +214,17 @@ function mergeFlowFindings(original, edited, options) {
212
214
  // ── Nodes: the edited graph decides which nodes exist and in what order; the
213
215
  // original decides their look (and, for untouched nodes, their content). ──
214
216
  const finalNodes = [];
217
+ // The edited nodes the merge replaced with the original's: their bindings
218
+ // references are the original's now, not the recompile's
219
+ // (see withoutRowsMintedForKeptNodes).
220
+ const keptFromOriginal = new Set();
215
221
  for (const e of edited.nodes ?? []) {
216
222
  const orig = origNodes.get(e.id);
217
223
  if (isKept(e, origNodes, baseNodes, options.baseline) && orig) {
218
224
  // Untouched by the user (equal to the faithful baseline) → keep the
219
225
  // original node exactly, placeholder-safe.
220
226
  finalNodes.push(jsonClone(orig));
227
+ keptFromOriginal.add(e.id);
221
228
  }
222
229
  else {
223
230
  // Added or edited → take the edited node, re-attaching original layout.
@@ -266,9 +273,11 @@ function mergeFlowFindings(original, edited, options) {
266
273
  ];
267
274
  result.definitions = mergeDefinitions(original.definitions, edited.definitions, allNodes, originalNodes, options.warn);
268
275
  // ── Bindings: union by what each row binds (original wins), so resources for
269
- // newly-added nodes come from `edited` while the original's stay intact. ──
276
+ // newly-added nodes come from `edited` while the original's stay intact.
277
+ // A row the no-edit recompile minted only for nodes the merge kept from the
278
+ // original is not carried (#1133). ──
270
279
  result.bindings = mergeBindings(original.bindings, edited.bindings);
271
- result.bindings = withoutRowsKeyedOnlyByKeptNodes(result.bindings, original, edited, options.baseline);
280
+ result.bindings = withoutRowsMintedForKeptNodes(result.bindings, original, edited, options.baseline, keptFromOriginal);
272
281
  // metadata / solutionId / projectId / layout / runtime were carried by the
273
282
  // initial clone of `original` and are intentionally left untouched.
274
283
  //
@@ -379,12 +388,14 @@ function mergeDefinitions(orig, edited, nodes, originalNodes, warn) {
379
388
  * holds it, in which case it gets a fresh one — ids are unreferenced labels, but
380
389
  * two rows with one id are not a valid file.
381
390
  *
382
- * Rows no surviving node uses are NOT pruned. The file has no uniform
391
+ * Rows of the ORIGINAL are never pruned. The canvas file has no uniform
383
392
  * node-to-row reference to decide "unused" by: an in-solution Flow is named by
384
393
  * the node TYPE's suffix, a connection by an id in the node's inputs, a published
385
394
  * process by a folder-qualified key that no node field spells. A wrong prune
386
395
  * drops a row a kept node needs, and the run fails to resolve its resource; an
387
- * orphan row is accepted by `validate` and ignored at run time.
396
+ * orphan row is accepted by `validate` and ignored at run time. Added rows the
397
+ * round trip minted only for kept nodes are dropped afterwards, by
398
+ * {@link withoutRowsMintedForKeptNodes}.
388
399
  */
389
400
  function mergeBindings(orig, edited) {
390
401
  const out = (orig ?? []).map((b) => jsonClone(b));
@@ -409,41 +420,70 @@ function mergeBindings(orig, edited) {
409
420
  return out;
410
421
  }
411
422
  /**
412
- * Drop each added row that the no-edit recompile minted for a resource only
413
- * nodes kept from the original spell. A row qualifies when the original lacks
414
- * it, the baseline has it, and its `resourceKey` appears in at least one edited
415
- * root node, every one of which the merge replaced with the original's: the kept
416
- * node is the original's, and it reaches the resource without that row.
423
+ * Drop each added row that the no-edit recompile minted only for nodes the merge
424
+ * kept from the original (#1133). A row qualifies when the original lacks it, the
425
+ * baseline has it, and at least one edited node references it, every one of
426
+ * which is in `keptFromOriginal`: the kept node is the original's, which reaches
427
+ * its resource without that row.
417
428
  *
418
- * Example: a classic-CLI IxP extract carries its model inline and has no
419
- * binding rows. It decompiles to `ixpExtract(...)`, whose compile mints
420
- * `ixpDeployment` `name` / `folderPath` rows keyed by the model id, and an
421
- * untouched node would carry them into the merged file.
429
+ * Examples:
430
+ * - A classic-CLI IxP extract carries its model inline and has no rows. It
431
+ * decompiles to `ixpExtract(...)`, whose compile mints `ixpDeployment` rows
432
+ * keyed by the model id.
433
+ * - A classic-CLI `core.action.http.v2` with no connection decompiles to
434
+ * `http({ managed: true })`, whose compile mints `""`-keyed rows for
435
+ * `<bindings.uipath-uipath-http connection>` and `<bindings.FolderKey>`.
422
436
  *
423
- * A row whose key no edited node spells is kept, as are rows a subflow node or
424
- * an edited node spells.
437
+ * "References" is read off the EDITED file, which the SDK wrote. A node
438
+ * references a row when it spells the row's `resourceKey`, or when it spells the
439
+ * row's `<bindings.NAME>` placeholder (in the node, or in its type's definition)
440
+ * and points at that row's resource. Names are not unique: a Slack step on
441
+ * connection X and a connection-less http step both use `<bindings.FolderKey>`,
442
+ * each with its own `FolderKey` row. So a placeholder counts for a keyed row
443
+ * only when no other row has that name, and for a `""`-keyed row only when the
444
+ * node spells none of the other same-named rows' keys.
445
+ *
446
+ * Kept: a row no edited node references (one a sidecar file uses, say), a row
447
+ * a subflow node or an edited node references, and every row without a baseline.
425
448
  */
426
- function withoutRowsKeyedOnlyByKeptNodes(rows, original, edited, baseline) {
427
- if (!baseline || !rows)
449
+ function withoutRowsMintedForKeptNodes(rows, original, edited, baseline, keptFromOriginal) {
450
+ if (!baseline || !rows || keptFromOriginal.size === 0)
428
451
  return rows;
429
- const origNodes = byId(original.nodes);
430
- const baseNodes = byId(baseline.nodes);
452
+ const contextByType = new Map();
453
+ for (const d of (edited.definitions ?? [])) {
454
+ if (typeof d?.nodeType !== 'string' || d.model?.context === undefined)
455
+ continue;
456
+ contextByType.set(d.nodeType, (contextByType.get(d.nodeType) ?? '') + JSON.stringify(d.model.context));
457
+ }
458
+ const describe = (n, kept) => ({ kept, text: JSON.stringify(n) + (contextByType.get(n.type) ?? '') });
431
459
  const nodes = [
432
- ...(edited.nodes ?? []).map((n) => ({ kept: origNodes.has(n.id) && nodeUnchanged(n, baseNodes.get(n.id)), text: JSON.stringify(n) })),
433
- ...Object.values(edited.subflows ?? {}).flatMap((sf) => (sf?.nodes ?? []).map((n) => ({ kept: false, text: JSON.stringify(n) }))),
460
+ ...(edited.nodes ?? []).map((n) => describe(n, keptFromOriginal.has(n.id))),
461
+ ...Object.values(edited.subflows ?? {}).flatMap((sf) => (sf?.nodes ?? []).map((n) => describe(n, false))),
434
462
  ];
435
- if (!nodes.some((n) => n.kept))
436
- return rows;
463
+ const spelled = (s) => JSON.stringify(s).slice(1, -1);
464
+ const keyOf = (b) => (typeof b.resourceKey === 'string' ? b.resourceKey : '');
465
+ const all = (edited.bindings ?? []);
437
466
  const fromOriginal = new Set((original.bindings ?? []).map(bindingIdentity));
438
467
  const inBaseline = new Set((baseline.bindings ?? []).map(bindingIdentity));
439
468
  return rows.filter((b) => {
440
469
  const identity = bindingIdentity(b);
441
470
  if (fromOriginal.has(identity) || !inBaseline.has(identity))
442
471
  return true;
443
- if (typeof b.resourceKey !== 'string' || b.resourceKey === '')
444
- return true;
445
- const spelled = JSON.stringify(b.resourceKey).slice(1, -1);
446
- const spellers = nodes.filter((n) => n.text.includes(spelled));
447
- return !(spellers.length > 0 && spellers.every((n) => n.kept));
472
+ const key = keyOf(b);
473
+ const others = typeof b.name === 'string'
474
+ ? all.filter((o) => o.name === b.name && keyOf(o) !== key)
475
+ : [];
476
+ const placeholder = typeof b.name === 'string' ? spelled(`<bindings.${b.name}>`) : undefined;
477
+ const references = (text) => {
478
+ if (key !== '' && text.includes(spelled(key)))
479
+ return true;
480
+ if (placeholder === undefined || !text.includes(placeholder))
481
+ return false;
482
+ return key === ''
483
+ ? !others.some((o) => text.includes(spelled(keyOf(o))))
484
+ : others.length === 0;
485
+ };
486
+ const referencers = nodes.filter((n) => references(n.text));
487
+ return !(referencers.length > 0 && referencers.every((n) => n.kept));
448
488
  });
449
489
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uipath/maestro-builder-sdk",
3
- "version": "6.31.4",
3
+ "version": "6.31.5",
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",
@@ -93,5 +93,5 @@
93
93
  "@types/node": "^22.7.0",
94
94
  "esbuild": "^0.28.1"
95
95
  },
96
- "gitref": "db2cd4696c73eec6402fdb616e678841a5ac3b06"
96
+ "gitref": "19700f105da9eae840ec05cd3796c28a96f1a4ed"
97
97
  }