@uipath/maestro-builder-sdk 6.15.5 → 6.16.0
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 +109 -108
- package/dist/api-members.md +416 -409
- package/dist/bpmn/bpmn-sdk.d.ts +170 -18
- package/dist/bpmn/bpmn-sdk.js +123 -5
- package/dist/bpmn/check.js +79 -6
- package/dist/bpmn/connector.js +5 -2
- package/dist/bpmn/data-service.d.ts +10 -2
- package/dist/bpmn/data-service.js +10 -1
- package/dist/bpmn/decompile.js +13 -2
- package/dist/bpmn/intsvc.d.ts +8 -0
- package/dist/bpmn/intsvc.js +43 -2
- package/dist/bpmn/serialize.d.ts +14 -0
- package/dist/bpmn/serialize.js +38 -9
- package/dist/bpmn/typed-node.js +76 -4
- package/dist/bpmn/uipath-moddle.v1.json +20 -0
- package/dist/check.js +29 -3
- package/dist/core/connector-inputs.d.ts +20 -0
- package/dist/core/connector-inputs.js +51 -0
- package/dist/core/event-checks.d.ts +8 -6
- package/dist/core/event-checks.js +8 -6
- package/dist/decompile.js +64 -8
- package/dist/flow-expr-check.js +6 -0
- package/dist/flow-sdk.d.ts +52 -0
- package/dist/flow-sdk.js +29 -0
- package/dist/flow-variables.d.ts +74 -0
- package/dist/flow-variables.js +101 -0
- package/dist/serialize.js +50 -4
- package/package.json +2 -2
package/dist/decompile.js
CHANGED
|
@@ -1739,7 +1739,7 @@ function typeExpr(type, imp) {
|
|
|
1739
1739
|
* (`{ type: types.x, description, subType, schema }`); a bare one keeps the
|
|
1740
1740
|
* compact `name: types.x` form.
|
|
1741
1741
|
*/
|
|
1742
|
-
function declExpr(g, withDefault, imp) {
|
|
1742
|
+
function declExpr(g, withDefault, imp, shared) {
|
|
1743
1743
|
const meta = [];
|
|
1744
1744
|
if (withDefault && g.defaultValue !== undefined)
|
|
1745
1745
|
meta.push(`default: ${literal(g.defaultValue)}`);
|
|
@@ -1749,16 +1749,58 @@ function declExpr(g, withDefault, imp) {
|
|
|
1749
1749
|
meta.push(`subType: ${str(g.subType)}`);
|
|
1750
1750
|
if (g.schema !== undefined)
|
|
1751
1751
|
meta.push(`schema: ${literal(g.schema)}`);
|
|
1752
|
+
if (shared !== undefined)
|
|
1753
|
+
meta.push(`shared: ${str(shared)}`);
|
|
1752
1754
|
if (meta.length === 0)
|
|
1753
1755
|
return typeExpr(g.type, imp);
|
|
1754
1756
|
return `{ type: ${typeExpr(g.type, imp)}, ${meta.join(', ')} }`;
|
|
1755
1757
|
}
|
|
1756
|
-
function
|
|
1758
|
+
function sharedCopies(flow, triggers) {
|
|
1757
1759
|
const globals = readFlowGlobals(flow.variables, runStrict);
|
|
1758
|
-
const
|
|
1760
|
+
const byInput = new Map();
|
|
1761
|
+
const unrecognized = [];
|
|
1762
|
+
const fed = new Map();
|
|
1763
|
+
for (const t of triggers) {
|
|
1764
|
+
const rows = flow.variables?.variableUpdates?.[t.id] ?? [];
|
|
1765
|
+
const seenOnRoot = new Set();
|
|
1766
|
+
for (const row of rows) {
|
|
1767
|
+
const src = unwrapSource(row.expression);
|
|
1768
|
+
const m = src?.startsWith('=js:') ? /^\$vars\.([A-Za-z0-9_]+)\.output\.([A-Za-z0-9_]+)$/.exec(src.slice(4).trim()) : null;
|
|
1769
|
+
const inp = m && m[1] === t.id
|
|
1770
|
+
? globals.find((g) => g.direction === 'in' && g.id === m[2] && g.triggerNodeId === t.id)
|
|
1771
|
+
: undefined;
|
|
1772
|
+
const target = globals.find((g) => g.id === row.variableId && g.direction !== 'in');
|
|
1773
|
+
const ok = inp !== undefined && target !== undefined && target.type === inp.type
|
|
1774
|
+
&& !byInput.has(`${t.id}\0${inp.id}`) && !seenOnRoot.has(row.variableId);
|
|
1775
|
+
if (!ok) {
|
|
1776
|
+
unrecognized.push({ trigger: t.id, variableId: row.variableId });
|
|
1777
|
+
continue;
|
|
1778
|
+
}
|
|
1779
|
+
seenOnRoot.add(row.variableId);
|
|
1780
|
+
byInput.set(`${t.id}\0${inp.id}`, row.variableId);
|
|
1781
|
+
fed.set(row.variableId, (fed.get(row.variableId) ?? 0) + 1);
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
const auto = new Set();
|
|
1785
|
+
for (const name of fed.keys()) {
|
|
1786
|
+
const g = globals.find((x) => x.id === name);
|
|
1787
|
+
if (g.direction === 'inout' && g.defaultValue === undefined && g.description === undefined
|
|
1788
|
+
&& g.subType === undefined && g.schema === undefined)
|
|
1789
|
+
auto.add(name);
|
|
1790
|
+
}
|
|
1791
|
+
return { byInput, auto, unrecognized };
|
|
1792
|
+
}
|
|
1793
|
+
function emitVariables(e, depth, flow, imp, epTriggers = new Set(), shared) {
|
|
1794
|
+
const globals = readFlowGlobals(flow.variables, runStrict);
|
|
1795
|
+
// `.input()` declares the DEFAULT root's inputs. An input bound to another
|
|
1796
|
+
// root comes back on that root's `.entryPoint(…, { inputs })` instead —
|
|
1797
|
+
// listing it here too declared it twice, which recompiles to
|
|
1798
|
+
// ENTRY_POINT_INPUT_COLLISION.
|
|
1799
|
+
const inputs = globals.filter((g) => g.direction === 'in' && !(g.triggerNodeId !== undefined && epTriggers.has(g.triggerNodeId)));
|
|
1759
1800
|
const outputs = globals.filter((g) => g.direction === 'out');
|
|
1760
|
-
const inouts = globals.filter((g) => g.direction === 'inout');
|
|
1761
|
-
const
|
|
1801
|
+
const inouts = globals.filter((g) => g.direction === 'inout' && !shared?.auto.has(g.id));
|
|
1802
|
+
const sharedOf = (g) => g.triggerNodeId !== undefined ? shared?.byInput.get(`${g.triggerNodeId}\0${g.id}`) : undefined;
|
|
1803
|
+
const shape = (list, withDefault) => `{ ${list.map((g) => `${key(g.name ?? g.id)}: ${declExpr(g, withDefault, imp, sharedOf(g))}`).join(', ')} }`;
|
|
1762
1804
|
if (inputs.length)
|
|
1763
1805
|
e.line(depth, `.input(${shape(inputs, true)})`);
|
|
1764
1806
|
if (outputs.length)
|
|
@@ -2652,9 +2694,22 @@ export function decompile(flow, options = {}) {
|
|
|
2652
2694
|
e.line(d, `// TODO: node ${str(n.id)} (${n.type}) is not reachable from any trigger and is NOT in this source; recompiling drops it. Connect it in the .flow or edit its JSON directly.`);
|
|
2653
2695
|
}
|
|
2654
2696
|
}
|
|
2655
|
-
|
|
2697
|
+
// `{ shared }` input copies on the roots (#924). Any other trigger-level
|
|
2698
|
+
// update has no builder spelling: say so instead of dropping it.
|
|
2699
|
+
const shared = sharedCopies(flow, triggerNodes);
|
|
2700
|
+
if (shared.unrecognized.length) {
|
|
2701
|
+
const listing = shared.unrecognized.map((u) => `${str(u.variableId)} on ${str(u.trigger)}`).join(', ');
|
|
2702
|
+
const message = `${shared.unrecognized.length} variable update(s) on a trigger node are not a { shared } input copy: ${listing}. `
|
|
2703
|
+
+ 'The builder writes a trigger-level update only for { type, shared: \'<var>\' } (the row `<var> ← $vars.<trigger>.output.<input>`), '
|
|
2704
|
+
+ 'so this source omits them and recompiling drops them.';
|
|
2705
|
+
if (runStrict)
|
|
2706
|
+
throw new Error(`decompile: ${message}`);
|
|
2707
|
+
warnOnce(message);
|
|
2708
|
+
e.line(d, `// TODO: ${message}`);
|
|
2709
|
+
}
|
|
2710
|
+
emitVariables(e, d, flow, imp, new Set(triggerNodes.filter((t) => t !== trigger).map((t) => t.id)), shared);
|
|
2656
2711
|
// Reconstruct the body: structured control flow recovered from the graph.
|
|
2657
|
-
const ctx = { graph, flow, imp, inputNames, o: options, emitted: new Set() };
|
|
2712
|
+
const ctx = { graph, flow, imp, inputNames, o: options, emitted: new Set(), shared };
|
|
2658
2713
|
const scope = scopeIds(flow.nodes ?? [], undefined);
|
|
2659
2714
|
const startId = trigger?.id ?? 'start';
|
|
2660
2715
|
const first = scopeSucc(graph, startId, scope)[0];
|
|
@@ -2746,7 +2801,8 @@ function entryPointSeg(t, scope, ipdom, ctx) {
|
|
|
2746
2801
|
.filter((g) => g.direction === 'in' && g.triggerNodeId === t.id);
|
|
2747
2802
|
const optParts = [];
|
|
2748
2803
|
if (scoped.length) {
|
|
2749
|
-
|
|
2804
|
+
const sharedOf = (g) => ctx.shared?.byInput.get(`${t.id}\0${g.id}`);
|
|
2805
|
+
optParts.push(`inputs: { ${scoped.map((g) => `${key(g.name ?? g.id)}: ${declExpr(g, true, ctx.imp, sharedOf(g))}`).join(', ')} }`);
|
|
2750
2806
|
}
|
|
2751
2807
|
const ver = verClause(t);
|
|
2752
2808
|
if (ver)
|
package/dist/flow-expr-check.js
CHANGED
|
@@ -58,6 +58,12 @@ function globalRoots(built) {
|
|
|
58
58
|
roots.add('start');
|
|
59
59
|
if (built.triggerId)
|
|
60
60
|
roots.add(built.triggerId);
|
|
61
|
+
// A variable an input feeds with `{ shared }` is declared by the SDK when the
|
|
62
|
+
// author did not declare it — readable as `v('<name>')` all the same (#924).
|
|
63
|
+
for (const inp of [...built.inputs, ...(built.entryPoints ?? []).flatMap((ep) => ep.inputs)]) {
|
|
64
|
+
if (inp.shared !== undefined)
|
|
65
|
+
roots.add(inp.shared);
|
|
66
|
+
}
|
|
61
67
|
for (const ep of built.entryPoints ?? []) {
|
|
62
68
|
roots.add(ep.id);
|
|
63
69
|
addStepNames(ep.steps, roots);
|
package/dist/flow-sdk.d.ts
CHANGED
|
@@ -54,6 +54,8 @@ export interface VarDecl {
|
|
|
54
54
|
subType?: string;
|
|
55
55
|
/** JSON schema for complex type validation (object/array shapes). */
|
|
56
56
|
schema?: Record<string, unknown>;
|
|
57
|
+
/** Inputs only: the flow variable this root's input is copied into when the root fires. See {@link VarSpec.shared}. */
|
|
58
|
+
shared?: string;
|
|
57
59
|
}
|
|
58
60
|
/**
|
|
59
61
|
* The rich spelling of a variable declaration: pass it instead of a bare
|
|
@@ -77,6 +79,39 @@ export interface VarSpec {
|
|
|
77
79
|
* `.output({ product: { type: types.number, default: 1 } })`.
|
|
78
80
|
*/
|
|
79
81
|
default?: unknown;
|
|
82
|
+
/**
|
|
83
|
+
* Inputs only: copy this input into the flow variable named here when its
|
|
84
|
+
* root fires, so a body shared by several roots reads one variable,
|
|
85
|
+
* `v('<shared>')`, whichever root started the run.
|
|
86
|
+
*
|
|
87
|
+
* @remarks
|
|
88
|
+
* For a flow with more than one root: the default `.input()` root plus
|
|
89
|
+
* `.entryPoint(...)` roots. Each root keeps its own input name. The format
|
|
90
|
+
* keys every global, and every entry point's caller-facing input, by one
|
|
91
|
+
* unique id, so two roots cannot both take `x`. `shared` names the variable
|
|
92
|
+
* they all feed.
|
|
93
|
+
*
|
|
94
|
+
* - The variable is declared for you unless a `.var()` or `.output()` of that
|
|
95
|
+
* name exists. Declare the `.var()` yourself to give it a default.
|
|
96
|
+
* - The copy is a `variableUpdates` row on the root's TRIGGER node,
|
|
97
|
+
* `<shared> ← $vars.<triggerId>.output.<input>`. So it is done before the
|
|
98
|
+
* root's own prefix steps run.
|
|
99
|
+
* - A root that feeds nothing leaves the variable at its default, or null
|
|
100
|
+
* if it has none.
|
|
101
|
+
* - Every input feeding one variable must have that variable's type, or
|
|
102
|
+
* `check` reports `SHARED_INPUT_TYPE_MISMATCH`.
|
|
103
|
+
* - Not accepted on a subflow child's input. A subflow has one root, its
|
|
104
|
+
* caller, so there is nothing to merge.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```ts
|
|
108
|
+
* flow('order')
|
|
109
|
+
* .input({ orderAmount: { type: types.number, shared: 'amount' } })
|
|
110
|
+
* .entryPoint('retry', manual(), { inputs: { retryAmount: { type: types.number, shared: 'amount' } } })
|
|
111
|
+
* .step('double', script({ code: 'return $vars.amount * 2;', returns: 'number' })) // v('amount') in an Expr slot
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
shared?: string;
|
|
80
115
|
}
|
|
81
116
|
/**
|
|
82
117
|
* The eight intervals the designer offers in its Frequency dropdown. Any other
|
|
@@ -1229,6 +1264,18 @@ declare class FlowBuilder extends StepList {
|
|
|
1229
1264
|
* .step('normalize', script({ code: 'return 1;' })) // shared body
|
|
1230
1265
|
* ```
|
|
1231
1266
|
*
|
|
1267
|
+
* **Sharing an input across roots.** Mark each root's input with the variable
|
|
1268
|
+
* it feeds, `{ type, shared: '<var>' }`. The SDK copies it on that root's
|
|
1269
|
+
* trigger node, and the shared body reads `v('<var>')` whichever root fired.
|
|
1270
|
+
* See {@link VarSpec.shared}.
|
|
1271
|
+
*
|
|
1272
|
+
* ```ts
|
|
1273
|
+
* flow('order')
|
|
1274
|
+
* .input({ orderAmount: { type: types.number, shared: 'amount' } })
|
|
1275
|
+
* .entryPoint('retry', manual(), { inputs: { retryAmount: { type: types.number, shared: 'amount' } } })
|
|
1276
|
+
* .step('double', script({ code: 'return $vars.amount * 2;', returns: 'number' })) // v('amount') in an Expr slot
|
|
1277
|
+
* ```
|
|
1278
|
+
*
|
|
1232
1279
|
* @param id - The root's trigger-node id — unique among entry points, steps,
|
|
1233
1280
|
* and the default trigger id. Expressions read it as `$vars.<id>.output.*`.
|
|
1234
1281
|
* @param trigger - What fires this root: `manual()`, `scheduled(...)`, or
|
|
@@ -1319,6 +1366,11 @@ declare class FlowBuilder extends StepList {
|
|
|
1319
1366
|
/**
|
|
1320
1367
|
* Declare the flow's inputs. Read them with `input('<name>')`.
|
|
1321
1368
|
*
|
|
1369
|
+
* @remarks
|
|
1370
|
+
* In a flow with several roots, give an input `{ type, shared: '<var>' }` to
|
|
1371
|
+
* copy it into a flow variable the shared body reads with `v('<var>')`. See
|
|
1372
|
+
* {@link VarSpec.shared}.
|
|
1373
|
+
*
|
|
1322
1374
|
* @param shape - Input names to type descriptors, e.g. `{ amount: types.number }`.
|
|
1323
1375
|
* @returns This builder, so calls chain.
|
|
1324
1376
|
* @see input
|
package/dist/flow-sdk.js
CHANGED
|
@@ -71,12 +71,24 @@ function varDeclOf(method, name, value) {
|
|
|
71
71
|
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
|
72
72
|
const spec = value;
|
|
73
73
|
assertType(method, name, spec.type);
|
|
74
|
+
if (spec.shared !== undefined) {
|
|
75
|
+
if (method !== 'input') {
|
|
76
|
+
throw new TypeError(`.${method}() declared ${JSON.stringify(name)} with { shared }, which only an input takes: it names `
|
|
77
|
+
+ `the flow variable a root's input is copied into. Declare the variable with .var() and feed it from `
|
|
78
|
+
+ `.input({ <name>: { type, shared: ${JSON.stringify(spec.shared)} } }).`);
|
|
79
|
+
}
|
|
80
|
+
if (typeof spec.shared !== 'string' || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(spec.shared)) {
|
|
81
|
+
throw new TypeError(`Input ${JSON.stringify(name)}: { shared } must be a variable name (letters, digits, _), got `
|
|
82
|
+
+ `${JSON.stringify(spec.shared)}.`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
74
85
|
return {
|
|
75
86
|
type: spec.type,
|
|
76
87
|
...(spec.default !== undefined ? { default: spec.default } : {}),
|
|
77
88
|
...(spec.description !== undefined ? { description: spec.description } : {}),
|
|
78
89
|
...(spec.subType !== undefined ? { subType: spec.subType } : {}),
|
|
79
90
|
...(spec.schema !== undefined ? { schema: spec.schema } : {}),
|
|
91
|
+
...(spec.shared !== undefined ? { shared: spec.shared } : {}),
|
|
80
92
|
};
|
|
81
93
|
}
|
|
82
94
|
assertType(method, name, value);
|
|
@@ -967,6 +979,18 @@ class FlowBuilder extends StepList {
|
|
|
967
979
|
* .step('normalize', script({ code: 'return 1;' })) // shared body
|
|
968
980
|
* ```
|
|
969
981
|
*
|
|
982
|
+
* **Sharing an input across roots.** Mark each root's input with the variable
|
|
983
|
+
* it feeds, `{ type, shared: '<var>' }`. The SDK copies it on that root's
|
|
984
|
+
* trigger node, and the shared body reads `v('<var>')` whichever root fired.
|
|
985
|
+
* See {@link VarSpec.shared}.
|
|
986
|
+
*
|
|
987
|
+
* ```ts
|
|
988
|
+
* flow('order')
|
|
989
|
+
* .input({ orderAmount: { type: types.number, shared: 'amount' } })
|
|
990
|
+
* .entryPoint('retry', manual(), { inputs: { retryAmount: { type: types.number, shared: 'amount' } } })
|
|
991
|
+
* .step('double', script({ code: 'return $vars.amount * 2;', returns: 'number' })) // v('amount') in an Expr slot
|
|
992
|
+
* ```
|
|
993
|
+
*
|
|
970
994
|
* @param id - The root's trigger-node id — unique among entry points, steps,
|
|
971
995
|
* and the default trigger id. Expressions read it as `$vars.<id>.output.*`.
|
|
972
996
|
* @param trigger - What fires this root: `manual()`, `scheduled(...)`, or
|
|
@@ -1081,6 +1105,11 @@ class FlowBuilder extends StepList {
|
|
|
1081
1105
|
/**
|
|
1082
1106
|
* Declare the flow's inputs. Read them with `input('<name>')`.
|
|
1083
1107
|
*
|
|
1108
|
+
* @remarks
|
|
1109
|
+
* In a flow with several roots, give an input `{ type, shared: '<var>' }` to
|
|
1110
|
+
* copy it into a flow variable the shared body reads with `v('<var>')`. See
|
|
1111
|
+
* {@link VarSpec.shared}.
|
|
1112
|
+
*
|
|
1084
1113
|
* @param shape - Input names to type descriptors, e.g. `{ amount: types.number }`.
|
|
1085
1114
|
* @returns This builder, so calls chain.
|
|
1086
1115
|
* @see input
|
package/dist/flow-variables.d.ts
CHANGED
|
@@ -77,3 +77,77 @@ export declare function planFlowGlobals<D extends DeclaredVariable>(decl: {
|
|
|
77
77
|
outputs: readonly D[];
|
|
78
78
|
vars: readonly D[];
|
|
79
79
|
}): FlowGlobalsPlan<D>;
|
|
80
|
+
/** One root's input that feeds a shared variable. */
|
|
81
|
+
export interface SharedInputFeed {
|
|
82
|
+
/** The root's trigger-node id at AUTHORING time: `start` for the default root, else the entry-point id. */
|
|
83
|
+
root: string;
|
|
84
|
+
/** The input's declared name on that root (its global id). */
|
|
85
|
+
input: string;
|
|
86
|
+
type: string;
|
|
87
|
+
}
|
|
88
|
+
/** One variable that inputs feed with `{ shared: '<name>' }`. */
|
|
89
|
+
export interface SharedInputTarget {
|
|
90
|
+
name: string;
|
|
91
|
+
type: string;
|
|
92
|
+
/** What declares it: an explicit `.var()` / `.output()`, or the SDK (`auto`). */
|
|
93
|
+
declaredAs: 'var' | 'output' | 'auto';
|
|
94
|
+
hasDefault: boolean;
|
|
95
|
+
feeds: SharedInputFeed[];
|
|
96
|
+
}
|
|
97
|
+
export interface SharedInputsProblem {
|
|
98
|
+
level: 'error' | 'warning';
|
|
99
|
+
code: 'SHARED_INPUT_TARGET_IS_INPUT' | 'SHARED_INPUT_TYPE_MISMATCH' | 'SHARED_INPUT_SAME_ROOT' | 'SHARED_INPUT_ROOT_MISSING';
|
|
100
|
+
name: string;
|
|
101
|
+
message: string;
|
|
102
|
+
suggestion?: string;
|
|
103
|
+
}
|
|
104
|
+
export interface SharedInputsPlan {
|
|
105
|
+
/** Every shared variable, in order of its first feeding input. */
|
|
106
|
+
targets: SharedInputTarget[];
|
|
107
|
+
/** The `.var()` declarations the SDK adds — one per `auto` target, emitted after the explicit vars. */
|
|
108
|
+
autoVars: {
|
|
109
|
+
name: string;
|
|
110
|
+
type: string;
|
|
111
|
+
}[];
|
|
112
|
+
problems: SharedInputsProblem[];
|
|
113
|
+
}
|
|
114
|
+
/** The `{ shared }` slice of an input declaration. */
|
|
115
|
+
interface SharedCapable extends DeclaredVariable {
|
|
116
|
+
shared?: string;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Plan the variables that root inputs feed with `{ shared: '<var>' }`.
|
|
120
|
+
*
|
|
121
|
+
* Shared by `check` and the serializer, so the two cannot disagree about which
|
|
122
|
+
* variable exists, what type it is, or which problems refuse the flow (the
|
|
123
|
+
* #899/#904 lesson: `check` said `Errors: 0`, validate refused the file).
|
|
124
|
+
*
|
|
125
|
+
* Why the inputs keep their own names: `flow validate` refuses a repeated id in
|
|
126
|
+
* `variables.globals` whatever its `triggerNodeId`, and each entry point's
|
|
127
|
+
* caller-facing input schema is keyed by that id. So every root's input is a
|
|
128
|
+
* distinct global, and the variable they feed is a distinct one too.
|
|
129
|
+
*/
|
|
130
|
+
export declare function planSharedInputs(decl: {
|
|
131
|
+
roots: readonly {
|
|
132
|
+
id: string;
|
|
133
|
+
inputs: readonly SharedCapable[];
|
|
134
|
+
}[];
|
|
135
|
+
outputs: readonly DeclaredVariable[];
|
|
136
|
+
vars: readonly DeclaredVariable[];
|
|
137
|
+
}): SharedInputsPlan;
|
|
138
|
+
/**
|
|
139
|
+
* A flow's roots for {@link planSharedInputs}: the default root first (under
|
|
140
|
+
* its EMITTED trigger id, `.triggerId()` applied), then each `.entryPoint()`.
|
|
141
|
+
*/
|
|
142
|
+
export declare function sharedInputRoots(built: {
|
|
143
|
+
inputs: readonly SharedCapable[];
|
|
144
|
+
entryPoints?: readonly {
|
|
145
|
+
id: string;
|
|
146
|
+
inputs: readonly SharedCapable[];
|
|
147
|
+
}[];
|
|
148
|
+
triggerId?: string;
|
|
149
|
+
}): {
|
|
150
|
+
id: string;
|
|
151
|
+
inputs: readonly SharedCapable[];
|
|
152
|
+
}[];
|
|
153
|
+
export {};
|
package/dist/flow-variables.js
CHANGED
|
@@ -124,3 +124,104 @@ export function planFlowGlobals(decl) {
|
|
|
124
124
|
const vars = decl.vars.filter((vd) => !mergedSet.has(vd.name));
|
|
125
125
|
return { outputs, vars, merged, problems };
|
|
126
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Plan the variables that root inputs feed with `{ shared: '<var>' }`.
|
|
129
|
+
*
|
|
130
|
+
* Shared by `check` and the serializer, so the two cannot disagree about which
|
|
131
|
+
* variable exists, what type it is, or which problems refuse the flow (the
|
|
132
|
+
* #899/#904 lesson: `check` said `Errors: 0`, validate refused the file).
|
|
133
|
+
*
|
|
134
|
+
* Why the inputs keep their own names: `flow validate` refuses a repeated id in
|
|
135
|
+
* `variables.globals` whatever its `triggerNodeId`, and each entry point's
|
|
136
|
+
* caller-facing input schema is keyed by that id. So every root's input is a
|
|
137
|
+
* distinct global, and the variable they feed is a distinct one too.
|
|
138
|
+
*/
|
|
139
|
+
export function planSharedInputs(decl) {
|
|
140
|
+
const problems = [];
|
|
141
|
+
const inputNames = new Set(decl.roots.flatMap((r) => r.inputs.map((i) => i.name)));
|
|
142
|
+
const byName = new Map();
|
|
143
|
+
for (const root of decl.roots) {
|
|
144
|
+
for (const inp of root.inputs) {
|
|
145
|
+
if (inp.shared === undefined)
|
|
146
|
+
continue;
|
|
147
|
+
const name = inp.shared;
|
|
148
|
+
let target = byName.get(name);
|
|
149
|
+
if (!target) {
|
|
150
|
+
const vd = decl.vars.find((x) => x.name === name);
|
|
151
|
+
const od = decl.outputs.find((x) => x.name === name);
|
|
152
|
+
const declared = od ?? vd;
|
|
153
|
+
target = {
|
|
154
|
+
name,
|
|
155
|
+
type: declared ? String(declared.type) : String(inp.type),
|
|
156
|
+
declaredAs: od ? 'output' : vd ? 'var' : 'auto',
|
|
157
|
+
hasDefault: (od?.default ?? vd?.default) !== undefined,
|
|
158
|
+
feeds: [],
|
|
159
|
+
};
|
|
160
|
+
byName.set(name, target);
|
|
161
|
+
}
|
|
162
|
+
target.feeds.push({ root: root.id, input: inp.name, type: String(inp.type) });
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
for (const t of byName.values()) {
|
|
166
|
+
if (inputNames.has(t.name)) {
|
|
167
|
+
problems.push({
|
|
168
|
+
level: 'error', code: 'SHARED_INPUT_TARGET_IS_INPUT', name: t.name,
|
|
169
|
+
message: `{ shared: ${JSON.stringify(t.name)} } names a flow INPUT. The shared variable is its own global, and `
|
|
170
|
+
+ `\`flow validate\` refuses two globals with one id, so it needs a name no input uses.`,
|
|
171
|
+
suggestion: `Rename the variable, e.g. { shared: '${t.name}Value' }, and read it with v('${t.name}Value').`,
|
|
172
|
+
});
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
const wrong = t.feeds.filter((f) => f.type !== t.type);
|
|
176
|
+
if (wrong.length) {
|
|
177
|
+
const source = t.declaredAs === 'auto'
|
|
178
|
+
? `the first input feeding it ("${t.feeds[0].input}" on root "${t.feeds[0].root}") is ${t.type}`
|
|
179
|
+
: `it is declared by .${t.declaredAs}() as ${t.type}`;
|
|
180
|
+
problems.push({
|
|
181
|
+
level: 'error', code: 'SHARED_INPUT_TYPE_MISMATCH', name: t.name,
|
|
182
|
+
message: `Shared variable "${t.name}": ${source}, but `
|
|
183
|
+
+ wrong.map((f) => `"${f.input}" on root "${f.root}" is ${f.type}`).join(', ')
|
|
184
|
+
+ `. Every input copied into one variable must have that variable's type.`,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
const perRoot = new Map();
|
|
188
|
+
for (const f of t.feeds)
|
|
189
|
+
(perRoot.get(f.root) ?? perRoot.set(f.root, []).get(f.root)).push(f.input);
|
|
190
|
+
for (const [root, inputs] of perRoot) {
|
|
191
|
+
if (inputs.length > 1) {
|
|
192
|
+
problems.push({
|
|
193
|
+
level: 'error', code: 'SHARED_INPUT_SAME_ROOT', name: t.name,
|
|
194
|
+
message: `Root "${root}" feeds shared variable "${t.name}" from ${inputs.length} inputs `
|
|
195
|
+
+ `(${inputs.map((i) => `"${i}"`).join(', ')}). A root's copy writes the variable once, so only one of `
|
|
196
|
+
+ `its inputs can feed it.`,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (decl.roots.length > 1 && !t.hasDefault) {
|
|
201
|
+
const missing = decl.roots.filter((r) => !perRoot.has(r.id)).map((r) => r.id);
|
|
202
|
+
if (missing.length) {
|
|
203
|
+
problems.push({
|
|
204
|
+
level: 'warning', code: 'SHARED_INPUT_ROOT_MISSING', name: t.name,
|
|
205
|
+
message: `Shared variable "${t.name}" is fed by ${[...perRoot.keys()].map((r) => `"${r}"`).join(', ')} but not by `
|
|
206
|
+
+ `${missing.map((r) => `"${r}"`).join(', ')}. A run started there reads it as null.`,
|
|
207
|
+
suggestion: `Feed it from that root too, or declare it with a default: .var('${t.name}', types.${t.type}, <default>).`,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const targets = [...byName.values()];
|
|
213
|
+
// A target that names an input is already refused above; leaving it out here
|
|
214
|
+
// keeps the global plan from reporting the same name a second time.
|
|
215
|
+
const autoVars = targets.filter((t) => t.declaredAs === 'auto' && !inputNames.has(t.name)).map((t) => ({ name: t.name, type: t.type }));
|
|
216
|
+
return { targets, autoVars, problems };
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* A flow's roots for {@link planSharedInputs}: the default root first (under
|
|
220
|
+
* its EMITTED trigger id, `.triggerId()` applied), then each `.entryPoint()`.
|
|
221
|
+
*/
|
|
222
|
+
export function sharedInputRoots(built) {
|
|
223
|
+
return [
|
|
224
|
+
{ id: built.triggerId ?? 'start', inputs: built.inputs },
|
|
225
|
+
...(built.entryPoints ?? []).map((ep) => ({ id: ep.id, inputs: ep.inputs })),
|
|
226
|
+
];
|
|
227
|
+
}
|
package/dist/serialize.js
CHANGED
|
@@ -45,7 +45,7 @@ import { declaredScriptShape, inferScriptShape } from './core/script-return.js';
|
|
|
45
45
|
import { migrateToSchemaVersion } from './migrate.js';
|
|
46
46
|
import { FLOW_SCHEMA_VERSIONS } from './format-profile.js';
|
|
47
47
|
import { resolveSchemaVersion } from './schema-version.js';
|
|
48
|
-
import { planFlowGlobals } from './flow-variables.js';
|
|
48
|
+
import { planFlowGlobals, planSharedInputs, sharedInputRoots } from './flow-variables.js';
|
|
49
49
|
import { prepareCommand } from './core/cli-spelling.js';
|
|
50
50
|
import { QUEUE_ITEM_RECORD_SCHEMA } from './core/queue-record.js';
|
|
51
51
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
@@ -3959,16 +3959,38 @@ function misplacedLookupMessage(token) {
|
|
|
3959
3959
|
* (`compile --no-check`, `emitFlow`, a direct `serialize()`) cannot write a
|
|
3960
3960
|
* file `flow validate` rejects for a duplicate variable id.
|
|
3961
3961
|
*/
|
|
3962
|
-
function emittableGlobals(built, where) {
|
|
3962
|
+
function emittableGlobals(built, where, kind = 'root') {
|
|
3963
3963
|
// Entry-point inputs are rows of the same globals[], so they share the
|
|
3964
3964
|
// namespace; `check` reports those as ENTRY_POINT_INPUT_COLLISION.
|
|
3965
3965
|
const inputs = [...built.inputs, ...(built.entryPoints ?? []).flatMap((ep) => ep.inputs)];
|
|
3966
|
-
|
|
3966
|
+
// A variable an input feeds with `{ shared }` (#924) is one more `.var()`
|
|
3967
|
+
// unless the author declared it, so it goes through the same collision plan.
|
|
3968
|
+
const shared = sharedInputsOf(built, where, kind);
|
|
3969
|
+
const plan = planFlowGlobals({ inputs, outputs: built.outputs, vars: [...built.vars, ...shared.autoVars] });
|
|
3967
3970
|
if (plan.problems.length) {
|
|
3968
3971
|
throw new Error(`${where}: ${plan.problems.map((p) => `[${p.code}] ${p.message}`).join(' ')}`);
|
|
3969
3972
|
}
|
|
3970
3973
|
return plan;
|
|
3971
3974
|
}
|
|
3975
|
+
/**
|
|
3976
|
+
* The flow's `{ shared }` input plan (#924), refused here as `check` refuses it
|
|
3977
|
+
* — so `compile --no-check` cannot write what `check` would have stopped.
|
|
3978
|
+
* Warnings (a root that feeds nothing) are `check`'s alone.
|
|
3979
|
+
*/
|
|
3980
|
+
function sharedInputsOf(built, where, kind) {
|
|
3981
|
+
if (kind === 'subflow') {
|
|
3982
|
+
const marked = built.inputs.filter((i) => i.shared !== undefined);
|
|
3983
|
+
if (marked.length) {
|
|
3984
|
+
throw new Error(`${where}: [SHARED_INPUT_IN_SUBFLOW] input "${marked[0].name}" has { shared: "${marked[0].shared}" }, but a subflow `
|
|
3985
|
+
+ `has one root — its caller — so there is nothing to merge. Read the input with input('${marked[0].name}').`);
|
|
3986
|
+
}
|
|
3987
|
+
}
|
|
3988
|
+
const plan = planSharedInputs({ roots: sharedInputRoots(built), outputs: built.outputs, vars: built.vars });
|
|
3989
|
+
const errors = plan.problems.filter((p) => p.level === 'error');
|
|
3990
|
+
if (errors.length)
|
|
3991
|
+
throw new Error(`${where}: ${errors.map((p) => `[${p.code}] ${p.message}`).join(' ')}`);
|
|
3992
|
+
return plan;
|
|
3993
|
+
}
|
|
3972
3994
|
function newScope(outputs, vars = []) {
|
|
3973
3995
|
return {
|
|
3974
3996
|
nodes: [],
|
|
@@ -4417,7 +4439,7 @@ export function serialize(built, opts = {}) {
|
|
|
4417
4439
|
* a null. It is emitted here, always, so it cannot be forgotten.
|
|
4418
4440
|
*/
|
|
4419
4441
|
function emitSubflowBody(child, nodeId) {
|
|
4420
|
-
const childGlobals = emittableGlobals(child, `subflow "${nodeId}"
|
|
4442
|
+
const childGlobals = emittableGlobals(child, `subflow "${nodeId}"`, 'subflow');
|
|
4421
4443
|
const scope = newScope(childGlobals.outputs, childGlobals.vars);
|
|
4422
4444
|
// Claim this copy's node ids UP FRONT, so the rename map is complete before
|
|
4423
4445
|
// the first expression renders. A child's step names are free the first
|
|
@@ -6544,6 +6566,30 @@ export function serialize(built, opts = {}) {
|
|
|
6544
6566
|
}
|
|
6545
6567
|
}
|
|
6546
6568
|
}
|
|
6569
|
+
// ── Shared root inputs (#924) ────────────────────────────────────────────
|
|
6570
|
+
// An input marked `{ shared: '<var>' }` is copied into that variable on its
|
|
6571
|
+
// root's TRIGGER node: a `variableUpdates[<triggerId>]` row, the same
|
|
6572
|
+
// construct `{ updates }` writes for a step. On the trigger, the copy is done
|
|
6573
|
+
// before the root's own prefix steps run. The platform engine applies a start
|
|
6574
|
+
// event's output rows in order, so the row sees the trigger's input mapping
|
|
6575
|
+
// (measured live, #924). The default root's row is written against `start`
|
|
6576
|
+
// and renamed with every other `input()` read, so `.triggerId()` follows.
|
|
6577
|
+
{
|
|
6578
|
+
const roots = [
|
|
6579
|
+
{ node: built.triggerId ?? 'start', read: 'start', inputs: built.inputs },
|
|
6580
|
+
...entryPoints.map((ep) => ({ node: ep.id, read: ep.id, inputs: ep.inputs })),
|
|
6581
|
+
];
|
|
6582
|
+
for (const r of roots) {
|
|
6583
|
+
const copies = {};
|
|
6584
|
+
for (const inp of r.inputs) {
|
|
6585
|
+
if (inp.shared !== undefined)
|
|
6586
|
+
copies[inp.shared] = new Expr(`$vars.${r.read}.output.${inp.name}`);
|
|
6587
|
+
}
|
|
6588
|
+
if (Object.keys(copies).length === 0)
|
|
6589
|
+
continue;
|
|
6590
|
+
root.variableUpdates[r.node] = [...(root.variableUpdates[r.node] ?? []), ...renderUpdates(copies, root.rename)];
|
|
6591
|
+
}
|
|
6592
|
+
}
|
|
6547
6593
|
const globals = [];
|
|
6548
6594
|
const triggerNodeId = built.triggerId ?? 'start';
|
|
6549
6595
|
for (const inp of built.inputs)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uipath/maestro-builder-sdk",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.16.0",
|
|
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": "ac943f282d466660e0366f2703dffdea618a7711"
|
|
96
96
|
}
|