@uipath/maestro-builder-sdk 6.41.0 → 6.41.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/core/step-ports.d.ts +1 -1
- package/dist/core/step-ports.js +1 -1
- package/dist/flow-step-exits.js +2 -2
- package/dist/merge-variables.d.ts +7 -0
- package/dist/merge-variables.js +28 -15
- package/dist/merge.d.ts +2 -1
- package/dist/merge.js +77 -7
- package/package.json +2 -2
|
@@ -55,7 +55,7 @@ type SpecLike = {
|
|
|
55
55
|
* The author-declared exits of `spec`, or a `NoExits` explaining why there are
|
|
56
56
|
* none to route.
|
|
57
57
|
*/
|
|
58
|
-
export declare function declaredExits(spec: SpecLike | undefined
|
|
58
|
+
export declare function declaredExits(spec: SpecLike | undefined): DeclaredExit[] | NoExits;
|
|
59
59
|
/** Narrowing helper — `declaredExits` returns one or the other. */
|
|
60
60
|
export declare function hasExits(r: DeclaredExit[] | NoExits): r is DeclaredExit[];
|
|
61
61
|
export {};
|
package/dist/core/step-ports.js
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* The author-declared exits of `spec`, or a `NoExits` explaining why there are
|
|
27
27
|
* none to route.
|
|
28
28
|
*/
|
|
29
|
-
export function declaredExits(spec
|
|
29
|
+
export function declaredExits(spec) {
|
|
30
30
|
const kind = spec && 'kind' in spec ? spec.kind : undefined;
|
|
31
31
|
return {
|
|
32
32
|
reason: `a ${kind ?? 'custom'} step declares no author-named exits`,
|
package/dist/flow-step-exits.js
CHANGED
|
@@ -23,7 +23,7 @@ export function stepExits(spec, pinnedVersion) {
|
|
|
23
23
|
return raw;
|
|
24
24
|
const entry = typeof spec?.kind === 'string' ? FLOW_NODE_TYPES.byKind(spec.kind, spec) : undefined;
|
|
25
25
|
if (!entry)
|
|
26
|
-
return declaredExits(spec
|
|
26
|
+
return declaredExits(spec);
|
|
27
27
|
let impl;
|
|
28
28
|
try {
|
|
29
29
|
impl = resolveVersion(entry.type, spec, pinnedVersion, 'exits');
|
|
@@ -31,5 +31,5 @@ export function stepExits(spec, pinnedVersion) {
|
|
|
31
31
|
catch {
|
|
32
32
|
impl = resolveVersion(entry.type, spec, undefined, 'exits');
|
|
33
33
|
}
|
|
34
|
-
return impl.exits?.(spec, pinnedVersion) ?? declaredExits(spec
|
|
34
|
+
return impl.exits?.(spec, pinnedVersion) ?? declaredExits(spec);
|
|
35
35
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
type Json = unknown;
|
|
2
|
+
type Obj = Record<string, Json>;
|
|
2
3
|
/** JSON with object keys sorted, so key order is not a difference. */
|
|
3
4
|
export declare function stableJson(v: Json): string;
|
|
4
5
|
/**
|
|
@@ -16,4 +17,10 @@ export declare function isDeltaSpelling(variables: Json): boolean;
|
|
|
16
17
|
export declare function mergeVariables(original: Json, edited: Json, baseline: {
|
|
17
18
|
variables?: Json;
|
|
18
19
|
} | undefined, goneNodeIds: Set<string>): Json;
|
|
20
|
+
/**
|
|
21
|
+
* `o` with the author's changes in `e` applied key by key under the delta rule,
|
|
22
|
+
* keys in the original's order; `inner` merges a value all three have and the
|
|
23
|
+
* author changed. Built from entries, so an own `__proto__` key stays a key.
|
|
24
|
+
*/
|
|
25
|
+
export declare function mergeObject(o: Obj, b: Obj, e: Obj, inner: (o: Json, b: Json, e: Json) => Json): Obj;
|
|
19
26
|
export {};
|
package/dist/merge-variables.js
CHANGED
|
@@ -75,7 +75,7 @@ export function mergeVariables(original, edited, baseline, goneNodeIds) {
|
|
|
75
75
|
return copy(edited);
|
|
76
76
|
const orig = isObj(original) ? original : {};
|
|
77
77
|
const baseObj = isObj(base) ? base : {};
|
|
78
|
-
const
|
|
78
|
+
const entries = [];
|
|
79
79
|
for (const key of keysInOrder(orig, edited)) {
|
|
80
80
|
const updates = key === 'variableUpdates';
|
|
81
81
|
const inner = updates
|
|
@@ -83,16 +83,16 @@ export function mergeVariables(original, edited, baseline, goneNodeIds) {
|
|
|
83
83
|
: (o, b, e) => mergeList(o, b, e, goneNodeIds);
|
|
84
84
|
// A section the original lacks but the round trip writes is merged as an
|
|
85
85
|
// empty one, so its minted entries stay out and only the author's land.
|
|
86
|
-
const absent = !(key
|
|
86
|
+
const absent = !has(orig, key) && has(baseObj, key) && has(edited, key) && !same(edited[key], baseObj[key]);
|
|
87
87
|
const value = absent
|
|
88
88
|
? nonEmpty(inner(updates ? {} : [], baseObj[key], edited[key]))
|
|
89
|
-
: mergeKey(orig
|
|
89
|
+
: mergeKey(own(orig, key), own(baseObj, key), own(edited, key), has(orig, key), has(baseObj, key), has(edited, key), inner);
|
|
90
90
|
if (value !== undefined)
|
|
91
|
-
|
|
91
|
+
entries.push([key, value]);
|
|
92
92
|
}
|
|
93
|
-
if (original === undefined &&
|
|
93
|
+
if (original === undefined && entries.length === 0)
|
|
94
94
|
return undefined;
|
|
95
|
-
return withoutGoneUpdates(
|
|
95
|
+
return withoutGoneUpdates(Object.fromEntries(entries), goneNodeIds);
|
|
96
96
|
}
|
|
97
97
|
/** `undefined` for an empty list or map, so an absent section stays absent. */
|
|
98
98
|
function nonEmpty(v) {
|
|
@@ -113,9 +113,29 @@ function withoutGoneUpdates(variables, goneNodeIds) {
|
|
|
113
113
|
return variables;
|
|
114
114
|
return { ...variables, variableUpdates: Object.fromEntries(Object.entries(updates).filter(([k]) => !goneNodeIds.has(k))) };
|
|
115
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* Own keys only, so a key named like an `Object.prototype` member
|
|
118
|
+
* (`constructor`, `toString`) is not taken as present everywhere.
|
|
119
|
+
*/
|
|
120
|
+
const has = (v, k) => Object.hasOwn(v, k);
|
|
121
|
+
const own = (v, k) => (has(v, k) ? v[k] : undefined);
|
|
116
122
|
/** Original's keys in its order, then the edited file's new ones. */
|
|
117
123
|
function keysInOrder(original, edited) {
|
|
118
|
-
return [...Object.keys(original), ...Object.keys(edited).filter((k) => !(k
|
|
124
|
+
return [...Object.keys(original), ...Object.keys(edited).filter((k) => !has(original, k))];
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* `o` with the author's changes in `e` applied key by key under the delta rule,
|
|
128
|
+
* keys in the original's order; `inner` merges a value all three have and the
|
|
129
|
+
* author changed. Built from entries, so an own `__proto__` key stays a key.
|
|
130
|
+
*/
|
|
131
|
+
export function mergeObject(o, b, e, inner) {
|
|
132
|
+
const entries = [];
|
|
133
|
+
for (const key of keysInOrder(o, e)) {
|
|
134
|
+
const v = mergeKey(own(o, key), own(b, key), own(e, key), has(o, key), has(b, key), has(e, key), inner);
|
|
135
|
+
if (v !== undefined)
|
|
136
|
+
entries.push([key, v]);
|
|
137
|
+
}
|
|
138
|
+
return Object.fromEntries(entries);
|
|
119
139
|
}
|
|
120
140
|
/**
|
|
121
141
|
* One key's value under the delta rule; `undefined` = leave the key out.
|
|
@@ -173,14 +193,7 @@ function insertAt(out, edited, i) {
|
|
|
173
193
|
* keys in the original's order.
|
|
174
194
|
*/
|
|
175
195
|
function mergeFields(o, b, e) {
|
|
176
|
-
|
|
177
|
-
const out = {};
|
|
178
|
-
for (const key of keysInOrder(o, e)) {
|
|
179
|
-
const v = mergeKey(o[key], base[key], e[key], key in o, key in base, key in e, (_o, _b, ev) => copy(ev));
|
|
180
|
-
if (v !== undefined)
|
|
181
|
-
out[key] = v;
|
|
182
|
-
}
|
|
183
|
-
return out;
|
|
196
|
+
return mergeObject(o, b ?? {}, e, (_o, _b, ev) => copy(ev));
|
|
184
197
|
}
|
|
185
198
|
function idList(v) {
|
|
186
199
|
return Array.isArray(v) && v.every((x) => isObj(x) && typeof x.id === 'string');
|
package/dist/merge.d.ts
CHANGED
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
* `edited` is byte-equal to its `baseline` counterpart, the user did NOT
|
|
20
20
|
* touch it, so the merge keeps the ORIGINAL node verbatim (its real type,
|
|
21
21
|
* inputs, layout — even for a placeholder). Only nodes the user actually
|
|
22
|
-
* changed (or added) are taken from `edited
|
|
22
|
+
* changed (or added) are taken from `edited`, and a changed node takes only
|
|
23
|
+
* the fields that differ from the baseline (see withEdits).
|
|
23
24
|
* - Without a `baseline`, the merge is two-way: it takes `edited`'s graph and
|
|
24
25
|
* re-attaches the original layout by node-id match. This is correct as long
|
|
25
26
|
* as decompile was faithful for every node (the common case).
|
package/dist/merge.js
CHANGED
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
* `edited` is byte-equal to its `baseline` counterpart, the user did NOT
|
|
20
20
|
* touch it, so the merge keeps the ORIGINAL node verbatim (its real type,
|
|
21
21
|
* inputs, layout — even for a placeholder). Only nodes the user actually
|
|
22
|
-
* changed (or added) are taken from `edited
|
|
22
|
+
* changed (or added) are taken from `edited`, and a changed node takes only
|
|
23
|
+
* the fields that differ from the baseline (see withEdits).
|
|
23
24
|
* - Without a `baseline`, the merge is two-way: it takes `edited`'s graph and
|
|
24
25
|
* re-attaches the original layout by node-id match. This is correct as long
|
|
25
26
|
* as decompile was faithful for every node (the common case).
|
|
@@ -85,12 +86,13 @@
|
|
|
85
86
|
*/
|
|
86
87
|
import { bindingIdentity } from './core/binding-identity.js';
|
|
87
88
|
import { jsonClone } from './core/json-clone.js';
|
|
89
|
+
import { isRecord } from './core/node-envelope.js';
|
|
88
90
|
import { DEFINITIONLESS_NODE_TYPES } from './core/definitionless-node-types.js';
|
|
89
91
|
import { MergeRefusedError, mergedFileFindings, roundTripFindings } from './merge-roundtrip.js';
|
|
90
92
|
import { placeAddedNodes } from './node-placement.js';
|
|
91
93
|
import { mergeAgentSidecars } from './merge-sidecars.js';
|
|
92
94
|
import { embeddedAgentFindings, isEmbeddedAgent, mergeEmbeddedAgentNode } from './merge-embedded-agent.js';
|
|
93
|
-
import { mergeVariables } from './merge-variables.js';
|
|
95
|
+
import { mergeObject, mergeVariables, stableJson } from './merge-variables.js';
|
|
94
96
|
export { MergeRefusedError } from './merge-roundtrip.js';
|
|
95
97
|
function byId(nodes) {
|
|
96
98
|
return new Map((nodes ?? []).map((n) => [n.id, n]));
|
|
@@ -163,6 +165,65 @@ function withOriginalLayout(edited, orig, originalLayout) {
|
|
|
163
165
|
}
|
|
164
166
|
return edited;
|
|
165
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* Give `node` the original's `display` with the author's display edits applied,
|
|
170
|
+
* key by key: a key whose edited value differs from the baseline's is the
|
|
171
|
+
* author's (a relabel); every other key stays as the original has it. Compile
|
|
172
|
+
* writes only a label and the type's default icon, so the edited display taken
|
|
173
|
+
* whole drops a canvas file's `subLabel`, `iconBackground` and custom icon, and
|
|
174
|
+
* the original's taken whole drops the relabel.
|
|
175
|
+
*/
|
|
176
|
+
function withDisplayEdits(node, orig, e, base) {
|
|
177
|
+
const o = isRecord(orig.display) ? orig.display : {};
|
|
178
|
+
const ed = isRecord(e.display) ? e.display : {};
|
|
179
|
+
const b = isRecord(base.display) ? base.display : {};
|
|
180
|
+
const display = {};
|
|
181
|
+
for (const k of new Set([...Object.keys(o), ...Object.keys(ed)])) {
|
|
182
|
+
const v = sortedJson(ed[k]) === sortedJson(b[k]) ? o[k] : ed[k];
|
|
183
|
+
if (v !== undefined)
|
|
184
|
+
display[k] = jsonClone(v);
|
|
185
|
+
}
|
|
186
|
+
const next = sortedJson(display) === sortedJson(orig.display ?? {}) ? orig.display : display;
|
|
187
|
+
if (JSON.stringify(next) === JSON.stringify(node.display))
|
|
188
|
+
return node;
|
|
189
|
+
// In place, so the node keeps its key order.
|
|
190
|
+
const out = { ...node, display: undefined };
|
|
191
|
+
if (next === undefined)
|
|
192
|
+
delete out.display;
|
|
193
|
+
else
|
|
194
|
+
out.display = jsonClone(next);
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* The original with the author's edits applied, by the delta rule of
|
|
199
|
+
* merge-variables.ts: where `e` equals the baseline `b` the original's value
|
|
200
|
+
* stays (absent stays absent), and inside objects all three have, the rule
|
|
201
|
+
* recurses key by key. Lists are taken whole, and so is a changed expression
|
|
202
|
+
* value, whose `type` says how to read its `expression`: decompile turns a
|
|
203
|
+
* legacy `{ type: 'literal', expression: '{{ $vars.x }}' }` agent input into a
|
|
204
|
+
* `jsExpression`, so an edited reference merged key by key would keep
|
|
205
|
+
* `literal` and hand the agent the text. Applied to an edited node, a
|
|
206
|
+
* one-input edit to a connector keeps the rest of its `detail` as written, and
|
|
207
|
+
* compile's `model`, derived `outputs` and other spellings stay out of it.
|
|
208
|
+
*/
|
|
209
|
+
function withEdits(o, b, e) {
|
|
210
|
+
if (stableJson(e) === stableJson(b))
|
|
211
|
+
return o === undefined ? o : jsonClone(o);
|
|
212
|
+
if (!isRecord(o) || !isRecord(b) || !isRecord(e) || isExpressionValue(o) || isExpressionValue(e)) {
|
|
213
|
+
return e === undefined ? e : jsonClone(e);
|
|
214
|
+
}
|
|
215
|
+
return mergeObject(o, b, e, withEdits);
|
|
216
|
+
}
|
|
217
|
+
const isExpressionValue = (v) => typeof v.type === 'string' && Object.hasOwn(v, 'expression');
|
|
218
|
+
/**
|
|
219
|
+
* Whether an edited node can merge field by field onto its original: all three
|
|
220
|
+
* have one type and one `typeVersion`. A node whose version the edit or the
|
|
221
|
+
* round trip moved is taken from `edited` whole, so it never mixes two
|
|
222
|
+
* versions' inputs.
|
|
223
|
+
*/
|
|
224
|
+
function mergesByField(orig, base, e) {
|
|
225
|
+
return orig.type === e.type && base.type === e.type && orig.typeVersion === e.typeVersion && base.typeVersion === e.typeVersion;
|
|
226
|
+
}
|
|
166
227
|
/**
|
|
167
228
|
* Give an edited node the original's `inputs.source` when only the round trip
|
|
168
229
|
* changed it (the baseline's equals the edited one's).
|
|
@@ -243,18 +304,23 @@ function mergeFlowFindings(original, edited, options) {
|
|
|
243
304
|
const editedIds = new Set();
|
|
244
305
|
for (const e of edited.nodes ?? []) {
|
|
245
306
|
const orig = origNodes.get(e.id);
|
|
307
|
+
const base = baseNodes.get(e.id);
|
|
246
308
|
if (isKept(e, origNodes, baseNodes, options.baseline) && orig) {
|
|
247
309
|
// Untouched by the user (equal to the faithful baseline) → keep the
|
|
248
|
-
// original node exactly, placeholder-safe.
|
|
249
|
-
finalNodes.push(jsonClone(orig));
|
|
310
|
+
// original node exactly, placeholder-safe, plus any relabel.
|
|
311
|
+
finalNodes.push(base ? withDisplayEdits(jsonClone(orig), orig, e, base) : jsonClone(orig));
|
|
250
312
|
keptFromOriginal.add(e.id);
|
|
251
313
|
}
|
|
252
314
|
else {
|
|
253
|
-
// Added or edited →
|
|
315
|
+
// Added or edited → the original plus the author's changes, field by
|
|
316
|
+
// field (withEdits), else the edited node; then re-attach original layout.
|
|
254
317
|
// An EMBEDDED agent stays embedded: the original plus the author's change
|
|
255
318
|
// to the node, never the edited shell whole (#1220, merge-embedded-agent.ts).
|
|
256
|
-
const
|
|
257
|
-
|
|
319
|
+
const taken = keepsEmbeddedAgent(orig, e) ? mergeEmbeddedAgentNode(orig, e, base)
|
|
320
|
+
: orig && base && mergesByField(orig, base, e) ? withEdits(orig, base, e)
|
|
321
|
+
: jsonClone(e);
|
|
322
|
+
const node = orig && base ? withDisplayEdits(taken, orig, e, base) : taken;
|
|
323
|
+
const placed = withOriginalSource(withOriginalLayout(node, orig, layoutNodesOf(original)), orig, base);
|
|
258
324
|
finalNodes.push(placed);
|
|
259
325
|
// Without a baseline every node lands here; count one as edited only
|
|
260
326
|
// when the merged node differs from the original.
|
|
@@ -289,6 +355,10 @@ function mergeFlowFindings(original, edited, options) {
|
|
|
289
355
|
...(original.nodes ?? []),
|
|
290
356
|
...Object.values(original.subflows ?? {}).flatMap((sf) => sf?.nodes ?? []),
|
|
291
357
|
].map((n) => n.id).filter((id) => !mergedNodeIds.has(id)));
|
|
358
|
+
const layoutNodes = layoutNodesOf(result);
|
|
359
|
+
if (layoutNodes)
|
|
360
|
+
for (const id of goneNodeIds)
|
|
361
|
+
delete layoutNodes[id];
|
|
292
362
|
const variables = mergeVariables(original.variables, edited.variables, options.baseline, goneNodeIds);
|
|
293
363
|
if (variables === undefined)
|
|
294
364
|
delete result.variables;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uipath/maestro-builder-sdk",
|
|
3
|
-
"version": "6.41.
|
|
3
|
+
"version": "6.41.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",
|
|
@@ -93,5 +93,5 @@
|
|
|
93
93
|
"@types/node": "^22.7.0",
|
|
94
94
|
"esbuild": "^0.28.1"
|
|
95
95
|
},
|
|
96
|
-
"gitref": "
|
|
96
|
+
"gitref": "cac59a549d2de4ef1ed882e7d21c7ed68598cd52"
|
|
97
97
|
}
|