@uipath/maestro-builder-sdk 6.20.0 → 6.20.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 CHANGED
@@ -219,8 +219,8 @@ could hand you spans for a version you do not have.
219
219
  | `SchedulePreset` | type | `dist/flow-sdk.d.ts:131` | Supporting types | |
220
220
  | `script` | function | `dist/nodes/script/flow/factories.d.ts:7-14` | Actions | Declare an inline script action. |
221
221
  | `ScriptInputs` | interface | `dist/nodes/script/flow/spec.d.ts:7-44` | Option shapes | 2 field(s) |
222
- | `ScriptReturns` | type | `dist/core/script-return.d.ts:33-38` | Supporting types | What an author may write for `script({ returns })`: the type outright, or — for an object return — the FIELDS, which is… |
223
- | `ScriptReturnType` | type | `dist/core/script-return.d.ts:31-32` | Supporting types | The vocabulary a script's output can be declared as. |
222
+ | `ScriptReturns` | type | `dist/core/script-return.d.ts:42-48` | Supporting types | What an author may write for `script({ returns })`: the type outright, or — for an object return — the FIELDS, which is… |
223
+ | `ScriptReturnType` | type | `dist/core/script-return.d.ts:31-41` | Supporting types | The vocabulary a script's output can be declared as. |
224
224
  | `sendMessage` | function | `dist/core/actions.d.ts:2438-2456` | Actions | Post an assistant message into a live conversation (`uipath.conversational.send-message`). |
225
225
  | `SendMessageInputs` | interface | `dist/core/actions.d.ts:492-511` | Option shapes | 4 field(s) |
226
226
  | `ShownField` | interface | `dist/core/actions.d.ts:198-213` | Supporting types | A value the reviewer READS: shown on the form, filled in from the flow. |
package/dist/check.js CHANGED
@@ -4814,6 +4814,7 @@ function checkRegisteredStep(entry, s, exprs, errorSource, opts, diags, typeOf,
4814
4814
  const ctx = {
4815
4815
  step: s.name,
4816
4816
  version: impl.version,
4817
+ definition: () => bundledDefinition(impl.typeName, impl.version),
4817
4818
  report: (code, message, suggestion) => diags.push(catalogued(entry, s.name, code, message, suggestion)),
4818
4819
  declaredRef: (js) => {
4819
4820
  const name = declaredRefName(js, triggerId);
@@ -28,14 +28,24 @@
28
28
  * into a designer error — the exact defect this file exists to remove, pointed
29
29
  * the other way.
30
30
  */
31
- /** The vocabulary a script's output can be declared as. */
32
- export type ScriptReturnType = 'string' | 'number' | 'boolean' | 'object' | 'array';
31
+ /**
32
+ * The vocabulary a script's output can be declared as.
33
+ *
34
+ * `'any'` is the platform's own declaration on `core.action.script@1.1` (the
35
+ * 1.0.0 definition says `'object'`), so it is valid only as the top-level
36
+ * `returns` of a 1.1 step; `check` refuses it on 1.0.0 and as a field type.
37
+ * Declaring it keeps the output open even when the body plainly returns one
38
+ * type; it is also how decompile writes back a 1.1 node whose output a designer
39
+ * left at that default.
40
+ */
41
+ export type ScriptReturnType = 'string' | 'number' | 'boolean' | 'object' | 'array' | 'any';
33
42
  /**
34
43
  * What an author may write for `script({ returns })`: the type outright, or —
35
44
  * for an object return — the FIELDS, which is the spelling every other action's
36
- * `returns` uses (`http`, `rpaWorkflow`, `agent`).
45
+ * `returns` uses (`http`, `rpaWorkflow`, `agent`). A field's type is never
46
+ * `'any'`.
37
47
  */
38
- export type ScriptReturns = ScriptReturnType | Record<string, ScriptReturnType>;
48
+ export type ScriptReturns = ScriptReturnType | Record<string, Exclude<ScriptReturnType, 'any'>>;
39
49
  /** A resolved output declaration: a type, and for objects the fields we know. */
40
50
  export interface ScriptOutputShape {
41
51
  type: ScriptReturnType;
package/dist/decompile.js CHANGED
@@ -563,7 +563,9 @@ function emitRegisteredSpec(node, imp, inputNames) {
563
563
  sourceText: unwrapSource,
564
564
  plainValue: unwrapPlain,
565
565
  stepName: (nodeId) => nodeId,
566
- definition: () => runDefs.get(`${node.type}@${version}`) ?? runDefs.get(node.type),
566
+ // The file's carried definition first; the bundled one at the node's version
567
+ // when the file carries none (a hand-trimmed or SDK-written file).
568
+ definition: () => runDefs.get(`${node.type}@${version}`) ?? BUNDLED_DEFINITIONS[`${node.type}@${version}`] ?? runDefs.get(node.type),
567
569
  warn: warnOnce,
568
570
  };
569
571
  const spec = impl.deserialize(node, ctx);
@@ -2227,6 +2229,83 @@ function sweepDroppedEdges(graph, skip) {
2227
2229
  }
2228
2230
  return lines;
2229
2231
  }
2232
+ /** Every out-edge of `id` on port `port`, in edge order. */
2233
+ function portEdges(graph, id, port) {
2234
+ return graph.outEdges(id).filter((e) => e.sourcePort === port);
2235
+ }
2236
+ /** `id`'s successor edges whose target is inside `scope` (the edges `scopeSucc` follows). */
2237
+ function scopeSuccEdges(graph, id, scope) {
2238
+ return graph.successorEdges(id).filter((e) => e.targetPort !== 'loopBack' && scope.has(e.targetNodeId));
2239
+ }
2240
+ /**
2241
+ * The nearest node that post-dominates EVERY one of `targets` — where the paths
2242
+ * that start at them all meet again — or `undefined` when they only meet at the
2243
+ * scope exit. Walks each target's `ipdom` chain (the target itself first).
2244
+ */
2245
+ function commonPostDominator(targets, ipdom) {
2246
+ const chain = (t) => {
2247
+ const out = [];
2248
+ for (let n = t; n !== undefined && n !== EXIT && !out.includes(n); n = ipdom.get(n))
2249
+ out.push(n);
2250
+ return out;
2251
+ };
2252
+ const [head, ...rest] = targets.map(chain);
2253
+ const others = rest.map((c) => new Set(c));
2254
+ return head?.find((n) => others.every((c) => c.has(n)));
2255
+ }
2256
+ /**
2257
+ * The region that starts with `edges`, the edges ONE source port (or a
2258
+ * trigger) has.
2259
+ *
2260
+ * One edge is an ordinary region (`edgeRegion`). Two or more are a fork, which
2261
+ * is what serialize emits for a `.parallel(...)` that is the first thing in a
2262
+ * flow, a subflow, an entry point's prefix or a branch / switch arm: the trigger
2263
+ * (or the arm's port) gets one edge per parallel arm, and the arms meet at the
2264
+ * parallel's merge node. When the targets meet at a merge node the fork
2265
+ * decompiles to that `.parallel(...)`, then the region continues from the
2266
+ * merge's output (flow-builder-sdk#1155).
2267
+ *
2268
+ * Every edge in `edges` is accounted (edge accounting, #1125): the fold
2269
+ * recreates each one. A fork whose arms never meet at a merge node has no
2270
+ * builder spelling. Like the same shape mid-flow (`region`), it throws under
2271
+ * `bestEffort: false`, and otherwise keeps the first arm behind a
2272
+ * `TODO: unstructured fan-out` marker, which is what reports the other edges.
2273
+ */
2274
+ function forkRegion(edges, stop, scope, ipdom, ctx, refInto) {
2275
+ if (edges.length <= 1)
2276
+ return edgeRegion(edges[0], stop, scope, ipdom, ctx, refInto);
2277
+ const targets = edges.map((e) => e.targetNodeId);
2278
+ const join = commonPostDominator(targets, ipdom);
2279
+ const joinNode = join !== undefined ? ctx.graph.byId.get(join) : undefined;
2280
+ if (join === undefined || join === stop || joinNode?.type !== T.merge) {
2281
+ if (ctx.o.bestEffort === false)
2282
+ throw new Error(`decompile: unstructured fan-out (${edges.length} edges).`);
2283
+ for (const e of edges)
2284
+ ctx.graph.accounted.add(e);
2285
+ return [
2286
+ `/* TODO: unstructured fan-out (${commentText(edges.length)} edges) */`,
2287
+ ...region(targets[0], stop, scope, ipdom, ctx, refInto),
2288
+ ];
2289
+ }
2290
+ for (const e of edges)
2291
+ ctx.graph.accounted.add(e);
2292
+ const fork = parallelSeg(join, joinNode, targets, scope, ipdom, ctx, refInto);
2293
+ if (fork.next.brk || fork.next.cont)
2294
+ return [fork.seg, fork.next.brk ? '.break()' : '.continue()'];
2295
+ return [fork.seg, ...region(fork.next.cursor, stop, scope, ipdom, ctx, refInto)];
2296
+ }
2297
+ /**
2298
+ * The `.parallel(join, [arms])` segment for a fork whose `targets` (one per
2299
+ * edge; the caller accounts those edges) meet at the merge node `join`. Each
2300
+ * arm is the region up to `join`. `next` is where the walk goes after the
2301
+ * merge: its `output` edge, accounted by `continueOn` (`brk` / `cont` when
2302
+ * that edge leaves through the enclosing loop's `break` / spelled `continue`).
2303
+ */
2304
+ function parallelSeg(join, joinNode, targets, scope, ipdom, ctx, refInto) {
2305
+ const arms = targets.map((t, i) => armCb(`a${i}`, region(t, join, scope, ipdom, ctx, refInto)));
2306
+ const seg = `.parallel(${tsLiteral(join)}, [\n${arms.map((a) => reindent(a, ' ')).join(',\n')},\n]${verArg(joinNode)})`;
2307
+ return { seg, next: continueOn(joinNode, 'output', ctx, refInto) };
2308
+ }
2230
2309
  /** Re-indent every non-empty line of a (possibly multi-line) segment by `pad`. */
2231
2310
  function reindent(seg, pad) {
2232
2311
  return seg.split('\n').map((l) => (l === '' ? l : pad + l)).join('\n');
@@ -2385,8 +2464,8 @@ function region(cursor, stop, scope, ipdom, ctx, refInto) {
2385
2464
  if (node.type === T.decision) {
2386
2465
  const r = ipdom.get(node.id) ?? stop;
2387
2466
  const cond = emitBareExpr(String((node.inputs ?? {}).expression ?? ''), ctx.imp, ctx.inputNames);
2388
- const thenSegs = edgeRegion(portEdge(ctx.graph, node.id, 'true'), r, scope, ipdom, ctx, refInto);
2389
- const elseSegs = edgeRegion(portEdge(ctx.graph, node.id, 'false'), r, scope, ipdom, ctx, refInto);
2467
+ const thenSegs = forkRegion(portEdges(ctx.graph, node.id, 'true'), r, scope, ipdom, ctx, refInto);
2468
+ const elseSegs = forkRegion(portEdges(ctx.graph, node.id, 'false'), r, scope, ipdom, ctx, refInto);
2390
2469
  const labels = armLabels(node);
2391
2470
  if (labels.trueLabel)
2392
2471
  thenSegs.unshift(`.label(${tsLiteral(labels.trueLabel)})`);
@@ -2574,16 +2653,16 @@ function region(cursor, stop, scope, ipdom, ctx, refInto) {
2574
2653
  }
2575
2654
  // Parallel: the fork's ipdom is the merge join; each successor is one arm.
2576
2655
  const join = ipdom.get(node.id);
2577
- if (join && join !== EXIT && ctx.graph.byId.get(join)?.type === T.merge) {
2578
- const arms = succ.map((t, i) => armCb(`a${i}`, region(follow(ctx.graph, node.id, t), join, scope, ipdom, ctx, refInto)));
2579
- segs.push(`.parallel(${tsLiteral(join)}, [\n${arms.map((a) => reindent(a, ' ')).join(',\n')},\n]${verArg(ctx.graph.byId.get(join))})`);
2656
+ const joinNode = join !== undefined ? ctx.graph.byId.get(join) : undefined;
2657
+ if (join && join !== EXIT && joinNode?.type === T.merge) {
2658
+ const fork = parallelSeg(join, joinNode, succ.map((t) => follow(ctx.graph, node.id, t)), scope, ipdom, ctx, refInto);
2659
+ segs.push(fork.seg);
2580
2660
  flagDropped(node, ctx, segs);
2581
- const next = continueOn(ctx.graph.byId.get(join), 'output', ctx, refInto);
2582
- if (next.brk || next.cont) {
2583
- segs.push(next.brk ? '.break()' : '.continue()');
2661
+ if (fork.next.brk || fork.next.cont) {
2662
+ segs.push(fork.next.brk ? '.break()' : '.continue()');
2584
2663
  return segs;
2585
2664
  }
2586
- cursor = next.cursor;
2665
+ cursor = fork.next.cursor;
2587
2666
  continue;
2588
2667
  }
2589
2668
  // Unstructured fan-out we can't fold — best-effort marker. Its successor
@@ -2803,7 +2882,7 @@ function emitSwitch(node, ipdom, scope, ctx, stop, refInto) {
2803
2882
  }
2804
2883
  }
2805
2884
  }
2806
- const armSegs = edgeRegion(portEdge(ctx.graph, node.id, `case-${c.id}`), r, scope, ipdom, ctx, refInto);
2885
+ const armSegs = forkRegion(portEdges(ctx.graph, node.id, `case-${c.id}`), r, scope, ipdom, ctx, refInto);
2807
2886
  const defaultLabel = `Case ${valueLiteral}`;
2808
2887
  const labelPart = c.label && c.label !== defaultLabel ? `, label: ${tsLiteral(c.label)}` : '';
2809
2888
  // The file's case id comes back when it is not the positional one serialize
@@ -2816,7 +2895,7 @@ function emitSwitch(node, ipdom, scope, ctx, stop, refInto) {
2816
2895
  const on = emitBareExpr(disc ?? '', ctx.imp, ctx.inputNames);
2817
2896
  const hasDefault = (node.inputs ?? {}).hasDefault === true || portEdge(ctx.graph, node.id, 'default') !== undefined;
2818
2897
  const defArm = hasDefault
2819
- ? `, ${armCb('d', edgeRegion(portEdge(ctx.graph, node.id, 'default'), r, scope, ipdom, ctx, refInto))}`
2898
+ ? `, ${armCb('d', forkRegion(portEdges(ctx.graph, node.id, 'default'), r, scope, ipdom, ctx, refInto))}`
2820
2899
  : '';
2821
2900
  const caseList = `[\n${caseSrcs.map((c) => reindent(c, ' ')).join(',\n')},\n]`;
2822
2901
  // The builder's `.switch(on, [{value}])` models `on === value` only. A flow whose
@@ -3020,9 +3099,11 @@ function emitChildFlow(name, child, ctx) {
3020
3099
  // Body: the child's trigger is its own manual Start; walk from its successor.
3021
3100
  const trigger = (child.nodes ?? []).find((n) => isTrigger(n.type) && !(child.edges ?? []).some((e) => e.targetNodeId === n.id));
3022
3101
  const scope = scopeIds(child.nodes ?? [], undefined);
3023
- const entry = trigger ? follow(childCtx.graph, trigger.id, scopeSucc(childCtx.graph, trigger.id, scope)[0]) : undefined;
3024
- const ipdom = entry ? postDominators(entry, scope, childCtx.graph) : new Map();
3025
- const bodySegs = entry ? region(entry, EXIT, scope, ipdom, childCtx) : [];
3102
+ const entries = trigger ? scopeSuccEdges(childCtx.graph, trigger.id, scope) : [];
3103
+ const ipdom = entries.length
3104
+ ? postDominators(entries.length > 1 ? trigger.id : entries[0].targetNodeId, scope, childCtx.graph)
3105
+ : new Map();
3106
+ const bodySegs = entries.length ? forkRegion(entries, EXIT, scope, ipdom, childCtx) : [];
3026
3107
  parts.push(...bodySegs);
3027
3108
  parts.push(...sweepDroppedEdges(childCtx.graph, new Set()));
3028
3109
  // A subflow carries its own scoped layout, resolved against its own node ids.
@@ -3144,9 +3225,13 @@ export function decompile(flow, options = {}) {
3144
3225
  const ctx = { graph, flow, imp, inputNames, o: options, emitted: new Set(), shared };
3145
3226
  const scope = scopeIds(flow.nodes ?? [], undefined);
3146
3227
  const startId = trigger?.id ?? 'start';
3147
- const first = follow(graph, startId, scopeSucc(graph, startId, scope)[0]);
3148
- const ipdom = first !== undefined ? postDominators(first, scope, graph) : new Map();
3149
- const bodySegs = first !== undefined ? region(first, EXIT, scope, ipdom, ctx) : [];
3228
+ // Walked from the trigger's successors together: a `.parallel` placed first
3229
+ // gives the trigger one edge per arm (#1155).
3230
+ const firsts = scopeSuccEdges(graph, startId, scope);
3231
+ const ipdom = firsts.length
3232
+ ? postDominators(firsts.length > 1 ? startId : firsts[0].targetNodeId, scope, graph)
3233
+ : new Map();
3234
+ const bodySegs = firsts.length ? forkRegion(firsts, EXIT, scope, ipdom, ctx) : [];
3150
3235
  // Additional roots decompile to .entryPoint(...): scoped inputs from the
3151
3236
  // globals bound to them, and a prefix region that hands off into the shared
3152
3237
  // body — the shared nodes are in ctx.emitted by now, so the walk naturally
@@ -3236,8 +3321,11 @@ function entryPointSeg(t, scope, ipdom, ctx) {
3236
3321
  optParts.push(`inputs: { ${scoped.map((g) => `${key(g.name ?? g.id)}: ${declExpr(g, true, ctx.imp, sharedOf(g))}`).join(', ')} }`);
3237
3322
  }
3238
3323
  optParts.push(...triggerOptParts(t));
3239
- const first = follow(ctx.graph, t.id, scopeSucc(ctx.graph, t.id, scope)[0]);
3240
- const prefixSegs = first !== undefined ? region(first, EXIT, scope, ipdom, ctx) : [];
3324
+ const firsts = scopeSuccEdges(ctx.graph, t.id, scope);
3325
+ // A prefix that forks needs post-dominators over ITS nodes, which the root's
3326
+ // map (computed from the default trigger) does not cover.
3327
+ const epIpdom = firsts.length > 1 ? postDominators(t.id, scope, ctx.graph) : ipdom;
3328
+ const prefixSegs = firsts.length ? forkRegion(firsts, EXIT, scope, epIpdom, ctx) : [];
3241
3329
  const optArgText = optParts.length ? `, { ${optParts.join(', ')} }` : (prefixSegs.length ? ', undefined' : '');
3242
3330
  const prefix = prefixSegs.length ? `, ${armCb('b', prefixSegs)}` : '';
3243
3331
  return `.entryPoint(${tsLiteral(t.id)}, ${factory}${optArgText}${prefix})`;
@@ -92,7 +92,10 @@ export interface FlowDeserializeContext {
92
92
  plainValue(raw: unknown): unknown;
93
93
  /** The step name a node id was emitted for. */
94
94
  stepName(nodeId: string): string;
95
- /** The file's own `definitions[]` entry for this node, if it carries one. */
95
+ /**
96
+ * The node's definition: the file's own `definitions[]` entry for it, else the
97
+ * SDK's bundled definition at the node's version; undefined when neither exists.
98
+ */
96
99
  definition(): unknown;
97
100
  /** Report something the lift could not keep. Decompile prints it once. */
98
101
  warn(message: string): void;
@@ -119,6 +122,11 @@ export interface FlowCheckContext {
119
122
  readonly step: string;
120
123
  /** The version the node will be emitted at. */
121
124
  readonly version: string;
125
+ /**
126
+ * The SDK's bundled definition of this step's node type at `version` (the one
127
+ * serialize emits from), or undefined when the SDK does not ship it.
128
+ */
129
+ definition(): unknown;
122
130
  /** Report a diagnostic on this step. Its severity is the family catalog's for `code`. */
123
131
  report(code: string, message: string, suggestion?: string): void;
124
132
  /**
@@ -2,8 +2,22 @@ export const SCRIPT_DIAGNOSTICS = {
2
2
  SCRIPT_RETURNS_UNKNOWN_TYPE: { severity: 'error', summary: '`returns` names a type the wire format does not know' },
3
3
  SCRIPT_EMPTY_RETURNS: { severity: 'warning', summary: '`returns: {}` names no fields' },
4
4
  };
5
- /** The type vocabulary a script's `returns` may name — what the wire format accepts. */
6
- const SCRIPT_RETURN_TYPES = new Set(['string', 'number', 'boolean', 'object', 'array']);
5
+ /**
6
+ * The types a script's `returns` may name for a FIELD, and at the top level on
7
+ * every version. `any` is not one: no definition declares a field as `any`.
8
+ */
9
+ const SCRIPT_FIELD_TYPES = new Set(['string', 'number', 'boolean', 'object', 'array']);
10
+ /**
11
+ * The types a top-level `returns` may name at this step's version: the field
12
+ * types, plus the output type the node's own definition declares at that version
13
+ * (`any` on `core.action.script@1.1`, `object` on 1.0.0).
14
+ */
15
+ function topLevelTypes(ctx) {
16
+ const declared = ctx.definition()
17
+ ?.outputDefinition?.output?.type;
18
+ return typeof declared === 'string' ? new Set([...SCRIPT_FIELD_TYPES, declared]) : SCRIPT_FIELD_TYPES;
19
+ }
20
+ const quoted = (types) => [...types].map((t) => `"${t}"`).join(', ');
7
21
  /**
8
22
  * A script step's `returns` — the author overriding what the body looks like.
9
23
  *
@@ -19,9 +33,10 @@ export function checkScript(inputs, ctx) {
19
33
  if (returns === undefined)
20
34
  return;
21
35
  if (typeof returns === 'string') {
22
- if (!SCRIPT_RETURN_TYPES.has(returns)) {
36
+ const allowed = topLevelTypes(ctx);
37
+ if (!allowed.has(returns)) {
23
38
  ctx.report('SCRIPT_RETURNS_UNKNOWN_TYPE', `Script step "${step}" declares \`returns: ${JSON.stringify(returns)}\`, which is not a type the `
24
- + `wire format knows. Use one of ${[...SCRIPT_RETURN_TYPES].map((t) => `"${t}"`).join(', ')}, or a `
39
+ + `wire format knows at ${ctx.version}. Use one of ${quoted(allowed)}, or a `
25
40
  + `field map like \`{ total: "number" }\` to name an object's fields.`);
26
41
  }
27
42
  return;
@@ -41,9 +56,9 @@ export function checkScript(inputs, ctx) {
41
56
  return;
42
57
  }
43
58
  for (const [name, type] of Object.entries(fields)) {
44
- if (typeof type !== 'string' || !SCRIPT_RETURN_TYPES.has(type)) {
59
+ if (typeof type !== 'string' || !SCRIPT_FIELD_TYPES.has(type)) {
45
60
  ctx.report('SCRIPT_RETURNS_UNKNOWN_TYPE', `Script step "${step}" declares field "${name}" as ${JSON.stringify(type)}, which is not a type the `
46
- + `wire format knows. Use one of ${[...SCRIPT_RETURN_TYPES].map((t) => `"${t}"`).join(', ')}.`);
61
+ + `wire format knows for a field. Use one of ${quoted(SCRIPT_FIELD_TYPES)}.`);
47
62
  }
48
63
  }
49
64
  }
@@ -23,9 +23,13 @@ import { checkScript } from './check.js';
23
23
  */
24
24
  function scriptOutputs(base, returns, code) {
25
25
  const shape = returns !== undefined ? declaredScriptShape(returns) : inferScriptShape(code);
26
- // Nothing knowable, or knowably the open object the definition already
27
- // declares — leave the node exactly as it was.
28
- if (!shape || (shape.type === 'object' && !shape.properties))
26
+ // Nothing knowable — leave the node exactly as it was. An INFERRED bare object
27
+ // is nothing knowable either: the inference never enumerates fields, so it
28
+ // only says "some object", and the definition's own declaration stands. A
29
+ // DECLARED type always lands, even `object`: the definition's default is
30
+ // version-dependent (`object` on script 1.0.0, `any` on 1.1), so "the same as
31
+ // the default" is not a property of the word `object`.
32
+ if (!shape || (returns === undefined && shape.type === 'object' && !shape.properties))
29
33
  return base;
30
34
  if (!base || typeof base !== 'object')
31
35
  return base;
@@ -46,7 +50,7 @@ function scriptOutputs(base, returns, code) {
46
50
  * the same way — a property map the body does not produce is one the author gave
47
51
  * as `returns: {…}`.
48
52
  */
49
- function liftReturns(node, code) {
53
+ function liftReturns(node, code, definitionType) {
50
54
  const out = node.outputs?.output;
51
55
  const declared = typeof out?.type === 'string' ? out.type : undefined;
52
56
  if (!declared)
@@ -54,11 +58,14 @@ function liftReturns(node, code) {
54
58
  const props = out?.properties;
55
59
  // What re-compiling this body with NO `returns` would declare. Mirror
56
60
  // `scriptOutputs` exactly: an inference of a bare `object` (or none at all)
57
- // leaves the definition's own `{type:'object'}` standing, untouched.
61
+ // leaves the definition's own output type standing, untouched. That type is
62
+ // the node's definition AT ITS VERSION (`definitionType`): `object` on script
63
+ // 1.0.0 but `any` on 1.1, so a 1.1 node's untouched `any` is the default, not
64
+ // an author override.
58
65
  const inferred = inferScriptShape(code);
59
66
  const effective = inferred && !(inferred.type === 'object' && !inferred.properties)
60
67
  ? inferred
61
- : { type: 'object', properties: undefined };
68
+ : { type: definitionType, properties: undefined };
62
69
  const same = effective.type === declared
63
70
  && JSON.stringify(sortKeys(effective.properties ?? {})) === JSON.stringify(sortKeys(props ?? {}));
64
71
  if (same)
@@ -95,14 +102,16 @@ function scriptAt(version) {
95
102
  ...(typed !== base ? { outputs: typed } : {}),
96
103
  };
97
104
  },
98
- deserialize: (node) => {
105
+ deserialize: (node, ctx) => {
99
106
  const raw = node.inputs?.script;
100
107
  const code = typeof raw === 'string' ? raw : '';
101
108
  // `returns` is recovered only when the node's declared output is something
102
109
  // re-compiling the body would NOT produce on its own: serialize infers from
103
110
  // the body first, so a script whose type is already plain in its code
104
111
  // round-trips without it.
105
- const returns = liftReturns(node, code);
112
+ const defType = ctx.definition()
113
+ ?.outputDefinition?.output?.type;
114
+ const returns = liftReturns(node, code, typeof defType === 'string' ? defType : 'object');
106
115
  return { kind: 'script', inputs: { code, ...(returns !== undefined ? { returns } : {}) } };
107
116
  },
108
117
  print: ({ inputs }, ctx) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uipath/maestro-builder-sdk",
3
- "version": "6.20.0",
3
+ "version": "6.20.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": "30b9615751de65281973caeec9d688ad105fc925"
95
+ "gitref": "b82dedf06e578750e6cb85ed825d25cec1745b09"
96
96
  }