@uipath/maestro-builder-sdk 6.30.0 → 6.30.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.
- package/dist/api-index.md +17 -17
- package/dist/api-members.md +76 -76
- package/dist/check.js +4 -2
- package/dist/decompile-cli.js +11 -3
- package/dist/flow-sdk.d.ts +3 -2
- package/dist/merge-cli.d.ts +7 -0
- package/dist/merge-cli.js +95 -9
- package/dist/merge-compile.d.ts +8 -1
- package/dist/merge-compile.js +3 -2
- package/dist/merge-roundtrip.d.ts +1 -1
- package/dist/merge-roundtrip.js +2 -1
- package/dist/merge-sidecars.d.ts +71 -0
- package/dist/merge-sidecars.js +214 -0
- package/dist/merge.d.ts +17 -2
- package/dist/merge.js +71 -9
- package/dist/serialize.js +21 -4
- package/package.json +2 -2
package/dist/merge.js
CHANGED
|
@@ -79,6 +79,7 @@ import { jsonClone } from './core/json-file.js';
|
|
|
79
79
|
import { DEFINITIONLESS_NODE_TYPES } from './core/definitionless-node-types.js';
|
|
80
80
|
import { MergeRefusedError, mergedFileFindings, roundTripFindings } from './merge-roundtrip.js';
|
|
81
81
|
import { placeAddedNodes } from './node-placement.js';
|
|
82
|
+
import { mergeAgentSidecars } from './merge-sidecars.js';
|
|
82
83
|
export { MergeRefusedError } from './merge-roundtrip.js';
|
|
83
84
|
function byId(nodes) {
|
|
84
85
|
return new Map((nodes ?? []).map((n) => [n.id, n]));
|
|
@@ -145,6 +146,34 @@ function layoutNodesOf(f) {
|
|
|
145
146
|
return nodes && typeof nodes === 'object' && !Array.isArray(nodes) ? nodes : undefined;
|
|
146
147
|
}
|
|
147
148
|
export function mergeFlow(original, edited, options = {}) {
|
|
149
|
+
const { result, findings } = mergeFlowFindings(original, edited, options);
|
|
150
|
+
reportOrRefuse(findings, options);
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* `mergeFlow`, plus the agent sidecar files the merged flow needs written beside
|
|
155
|
+
* it (#1207). An inline agent's prompts and settings live only in
|
|
156
|
+
* `<source>/agent.json`, so an edit to them changes no node; this carries the
|
|
157
|
+
* author's change to that file onto the original's copy. What it cannot carry
|
|
158
|
+
* becomes a finding: warned, and refused under `strict`, with the flow's own
|
|
159
|
+
* findings. See merge-sidecars.ts.
|
|
160
|
+
*/
|
|
161
|
+
export function mergeFlowWithSidecars(original, edited, sidecars, options = {}) {
|
|
162
|
+
const { result, findings } = mergeFlowFindings(original, edited, options);
|
|
163
|
+
const carried = mergeAgentSidecars(result, sidecars);
|
|
164
|
+
reportOrRefuse([...findings, ...carried.findings], options);
|
|
165
|
+
return { flow: result, sidecars: carried.writes };
|
|
166
|
+
}
|
|
167
|
+
/** Warn every finding; under `strict`, throw over the refusing ones (and still warn the rest). */
|
|
168
|
+
function reportOrRefuse(findings, options) {
|
|
169
|
+
const refusing = options.strict ? findings.filter((f) => f.refuses) : [];
|
|
170
|
+
for (const f of findings)
|
|
171
|
+
if (!refusing.includes(f))
|
|
172
|
+
options.warn?.(f.message);
|
|
173
|
+
if (refusing.length > 0)
|
|
174
|
+
throw new MergeRefusedError(refusing);
|
|
175
|
+
}
|
|
176
|
+
function mergeFlowFindings(original, edited, options) {
|
|
148
177
|
const result = jsonClone(original);
|
|
149
178
|
const origNodes = byId(original.nodes);
|
|
150
179
|
const baseNodes = byId(options.baseline?.nodes);
|
|
@@ -205,6 +234,7 @@ export function mergeFlow(original, edited, options = {}) {
|
|
|
205
234
|
// ── Bindings: union by what each row binds (original wins), so resources for
|
|
206
235
|
// newly-added nodes come from `edited` while the original's stay intact. ──
|
|
207
236
|
result.bindings = mergeBindings(original.bindings, edited.bindings);
|
|
237
|
+
result.bindings = withoutRowsKeyedOnlyByKeptNodes(result.bindings, original, edited, options.baseline);
|
|
208
238
|
// metadata / solutionId / projectId / layout / runtime were carried by the
|
|
209
239
|
// initial clone of `original` and are intentionally left untouched.
|
|
210
240
|
//
|
|
@@ -219,19 +249,12 @@ export function mergeFlow(original, edited, options = {}) {
|
|
|
219
249
|
result.version = edited.version;
|
|
220
250
|
}
|
|
221
251
|
// ── What the no-edit round trip changed, and what mixing sources broke. ──
|
|
222
|
-
//
|
|
223
|
-
// refused run shows them too.
|
|
252
|
+
// The caller warns them, or refuses under strict (`reportOrRefuse`).
|
|
224
253
|
const findings = [
|
|
225
254
|
...(options.baseline ? roundTripFindings(original, options.baseline, result) : []),
|
|
226
255
|
...mergedFileFindings(result, original),
|
|
227
256
|
];
|
|
228
|
-
|
|
229
|
-
for (const f of findings)
|
|
230
|
-
if (!refusing.includes(f))
|
|
231
|
-
options.warn?.(f.message);
|
|
232
|
-
if (refusing.length > 0)
|
|
233
|
-
throw new MergeRefusedError(refusing);
|
|
234
|
-
return result;
|
|
257
|
+
return { result, findings };
|
|
235
258
|
}
|
|
236
259
|
/**
|
|
237
260
|
* The root edges with their edited ids, except that an edge with no id, or with
|
|
@@ -351,3 +374,42 @@ function mergeBindings(orig, edited) {
|
|
|
351
374
|
}
|
|
352
375
|
return out;
|
|
353
376
|
}
|
|
377
|
+
/**
|
|
378
|
+
* Drop each added row that the no-edit recompile minted for a resource only
|
|
379
|
+
* nodes kept from the original spell. A row qualifies when the original lacks
|
|
380
|
+
* it, the baseline has it, and its `resourceKey` appears in at least one edited
|
|
381
|
+
* root node, every one of which the merge replaced with the original's: the kept
|
|
382
|
+
* node is the original's, and it reaches the resource without that row.
|
|
383
|
+
*
|
|
384
|
+
* Example: a classic-CLI IxP extract carries its model inline and has no
|
|
385
|
+
* binding rows. It decompiles to `ixpExtract(...)`, whose compile mints
|
|
386
|
+
* `ixpDeployment` `name` / `folderPath` rows keyed by the model id, and an
|
|
387
|
+
* untouched node would carry them into the merged file.
|
|
388
|
+
*
|
|
389
|
+
* A row whose key no edited node spells is kept, as are rows a subflow node or
|
|
390
|
+
* an edited node spells.
|
|
391
|
+
*/
|
|
392
|
+
function withoutRowsKeyedOnlyByKeptNodes(rows, original, edited, baseline) {
|
|
393
|
+
if (!baseline || !rows)
|
|
394
|
+
return rows;
|
|
395
|
+
const origNodes = byId(original.nodes);
|
|
396
|
+
const baseNodes = byId(baseline.nodes);
|
|
397
|
+
const nodes = [
|
|
398
|
+
...(edited.nodes ?? []).map((n) => ({ kept: origNodes.has(n.id) && nodeUnchanged(n, baseNodes.get(n.id)), text: JSON.stringify(n) })),
|
|
399
|
+
...Object.values(edited.subflows ?? {}).flatMap((sf) => (sf?.nodes ?? []).map((n) => ({ kept: false, text: JSON.stringify(n) }))),
|
|
400
|
+
];
|
|
401
|
+
if (!nodes.some((n) => n.kept))
|
|
402
|
+
return rows;
|
|
403
|
+
const fromOriginal = new Set((original.bindings ?? []).map(bindingIdentity));
|
|
404
|
+
const inBaseline = new Set((baseline.bindings ?? []).map(bindingIdentity));
|
|
405
|
+
return rows.filter((b) => {
|
|
406
|
+
const identity = bindingIdentity(b);
|
|
407
|
+
if (fromOriginal.has(identity) || !inBaseline.has(identity))
|
|
408
|
+
return true;
|
|
409
|
+
if (typeof b.resourceKey !== 'string' || b.resourceKey === '')
|
|
410
|
+
return true;
|
|
411
|
+
const spelled = JSON.stringify(b.resourceKey).slice(1, -1);
|
|
412
|
+
const spellers = nodes.filter((n) => n.text.includes(spelled));
|
|
413
|
+
return !(spellers.length > 0 && spellers.every((n) => n.kept));
|
|
414
|
+
});
|
|
415
|
+
}
|
package/dist/serialize.js
CHANGED
|
@@ -4371,6 +4371,8 @@ export function serialize(built, opts = {}) {
|
|
|
4371
4371
|
// anything else: the root trigger's id is fixed, so a step named `start` must
|
|
4372
4372
|
// be renamed rather than silently collide with it.
|
|
4373
4373
|
const takenIds = new Set([built.triggerId ?? 'start']);
|
|
4374
|
+
/** Per scope, each named `.return` End emitted so far and its content. */
|
|
4375
|
+
const namedEnds = new Map();
|
|
4374
4376
|
let edgeSeq = 0;
|
|
4375
4377
|
/** Claim a globally unique node id, honouring the scope's rename map. */
|
|
4376
4378
|
function uid(scope, base) {
|
|
@@ -6474,7 +6476,7 @@ export function serialize(built, opts = {}) {
|
|
|
6474
6476
|
// `name` is the End's identity — `layout` keys on it and a decompiled
|
|
6475
6477
|
// source must be able to ask for the id the original file had, or the
|
|
6476
6478
|
// round trip cannot reproduce its own layout block (ReturnOptions.name).
|
|
6477
|
-
const
|
|
6479
|
+
const name = step.options?.name;
|
|
6478
6480
|
const explicit = {};
|
|
6479
6481
|
const updates = [];
|
|
6480
6482
|
for (const [name, expr] of Object.entries(step.values)) {
|
|
@@ -6489,7 +6491,6 @@ export function serialize(built, opts = {}) {
|
|
|
6489
6491
|
if (!scope.outputType.has(name))
|
|
6490
6492
|
updates.push({ variableId: name, expression: src });
|
|
6491
6493
|
}
|
|
6492
|
-
emitEnd(scope, id, tails, { label: step.options?.label, parentId, requestedVersion: step.options?.version, explicit });
|
|
6493
6494
|
const extra = step.options?.updates ?? {};
|
|
6494
6495
|
for (const name of Object.keys(extra)) {
|
|
6495
6496
|
if (name in step.values) {
|
|
@@ -6497,8 +6498,24 @@ export function serialize(built, opts = {}) {
|
|
|
6497
6498
|
}
|
|
6498
6499
|
}
|
|
6499
6500
|
updates.push(...renderUpdates(extra, scope.rename));
|
|
6500
|
-
|
|
6501
|
-
|
|
6501
|
+
// Decompile writes one `.return` per path into an End that several paths
|
|
6502
|
+
// share, so a repeat of an earlier named End in this scope, with the same
|
|
6503
|
+
// content, is that End reached again rather than a second one.
|
|
6504
|
+
const sig = JSON.stringify([parentId ?? null, step.options?.label ?? null, step.options?.version ?? null, explicit, updates]);
|
|
6505
|
+
const prior = name === undefined ? undefined : namedEnds.get(scope)?.get(name);
|
|
6506
|
+
if (prior?.sig === sig) {
|
|
6507
|
+
for (const t of tails)
|
|
6508
|
+
addEdge(scope, t, prior.id);
|
|
6509
|
+
}
|
|
6510
|
+
else {
|
|
6511
|
+
const id = uid(scope, name ?? 'end');
|
|
6512
|
+
emitEnd(scope, id, tails, { label: step.options?.label, parentId, requestedVersion: step.options?.version, explicit });
|
|
6513
|
+
if (updates.length)
|
|
6514
|
+
scope.variableUpdates[id] = updates;
|
|
6515
|
+
if (name !== undefined && !prior) {
|
|
6516
|
+
(namedEnds.get(scope) ?? namedEnds.set(scope, new Map()).get(scope)).set(name, { id, sig });
|
|
6517
|
+
}
|
|
6518
|
+
}
|
|
6502
6519
|
tails = [];
|
|
6503
6520
|
}
|
|
6504
6521
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uipath/maestro-builder-sdk",
|
|
3
|
-
"version": "6.30.
|
|
3
|
+
"version": "6.30.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": "
|
|
95
|
+
"gitref": "dcf5c8c2813ef37768cf148cd82cc27791621715"
|
|
96
96
|
}
|