@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 +2 -2
- package/dist/check.js +1 -0
- package/dist/core/script-return.d.ts +14 -4
- package/dist/decompile.js +108 -20
- package/dist/flow-node-type.d.ts +9 -1
- package/dist/nodes/script/flow/check.js +21 -6
- package/dist/nodes/script/flow/node-type.js +17 -8
- package/package.json +2 -2
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:
|
|
223
|
-
| `ScriptReturnType` | type | `dist/core/script-return.d.ts:31-
|
|
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
|
-
/**
|
|
32
|
-
|
|
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
|
|
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 =
|
|
2389
|
-
const elseSegs =
|
|
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
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
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
|
-
|
|
2582
|
-
|
|
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 =
|
|
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',
|
|
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
|
|
3024
|
-
const ipdom =
|
|
3025
|
-
|
|
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
|
-
|
|
3148
|
-
|
|
3149
|
-
const
|
|
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
|
|
3240
|
-
|
|
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})`;
|
package/dist/flow-node-type.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
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
|
-
/**
|
|
6
|
-
|
|
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
|
-
|
|
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 ${
|
|
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' || !
|
|
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 ${
|
|
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
|
|
27
|
-
//
|
|
28
|
-
|
|
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
|
|
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:
|
|
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
|
|
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.
|
|
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": "
|
|
95
|
+
"gitref": "b82dedf06e578750e6cb85ed825d25cec1745b09"
|
|
96
96
|
}
|