@uipath/maestro-builder-sdk 6.15.3 → 6.15.5
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/README.md +3 -1
- package/dist/api-index.md +48 -48
- package/dist/api-members.md +81 -81
- package/dist/bpmn/check-cli.js +4 -3
- package/dist/check.js +181 -18
- package/dist/cli-result.d.ts +14 -0
- package/dist/cli-result.js +18 -0
- package/dist/core/expr.d.ts +8 -2
- package/dist/core/expr.js +8 -2
- package/dist/core/global-names.d.ts +5 -7
- package/dist/core/global-names.js +5 -14
- package/dist/decompile.js +2 -2
- package/dist/flow-sdk.d.ts +27 -4
- package/dist/flow-sdk.js +24 -7
- package/dist/flow-variables.d.ts +66 -0
- package/dist/flow-variables.js +84 -0
- package/dist/registry/path-cli.js +3 -3
- package/dist/registry/search-cli.js +2 -2
- package/dist/serialize.js +27 -17
- package/package.json +2 -2
package/dist/check.js
CHANGED
|
@@ -12,11 +12,11 @@ import { checkEventResolution, checkEventSubscription, eventParamsFor, } from '.
|
|
|
12
12
|
import { FLOW_EXPR_DIALECT, checkBindingLabels, checkBindingPresence, checkConnectorContract as coreCheckConnectorContract, checkConnectorLookups as coreCheckConnectorLookups, checkConnectorSchema as coreCheckConnectorSchema, collectConnectionUses, connectionConnectorConflicts, } from './core/connector-checks.js';
|
|
13
13
|
import { prepareCommand } from './core/cli-spelling.js';
|
|
14
14
|
import { isAnswerField } from './core/hitl-answers.js';
|
|
15
|
-
import { globalNameCollisionMessage, globalNameCollisions } from './core/global-names.js';
|
|
16
15
|
import { hitlDerivesOutcomeHandles, hitlExitModel, hitlRoutesPerOutcome } from './core/hitl-routing.js';
|
|
17
16
|
import { childStepLists, declaredExits, hasExits, stopsAtEnd } from './core/step-ports.js';
|
|
18
17
|
import { FLOW_SCHEMA_VERSIONS } from './format-profile.js';
|
|
19
18
|
import { compareSchemaVersions, schemaVersionRefusal } from './schema-version.js';
|
|
19
|
+
import { planFlowGlobals } from './flow-variables.js';
|
|
20
20
|
export function check(built, opts = {}) {
|
|
21
21
|
let diags = [];
|
|
22
22
|
const uses = collectConnectionUses(() => { diags = checkFlow(built, [], opts); });
|
|
@@ -36,12 +36,8 @@ function checkFlow(built, ancestors, opts = {}) {
|
|
|
36
36
|
if (!built.id)
|
|
37
37
|
diags.push({ level: 'error', code: 'NO_ID', message: 'flow() requires an id.' });
|
|
38
38
|
checkSchemaVersion(built, ancestors, diags);
|
|
39
|
+
checkGlobalNames(built, diags);
|
|
39
40
|
checkNodeUpdates(built, diags);
|
|
40
|
-
// Inputs, outputs and vars are one namespace in the .flow (variables.globals[]);
|
|
41
|
-
// `serialize` throws the same sentence (flow-builder-sdk#890).
|
|
42
|
-
for (const collision of globalNameCollisions(built.inputs ?? [], built.outputs ?? [], built.vars ?? [])) {
|
|
43
|
-
diags.push({ level: 'error', code: 'VARIABLE_NAME_COLLISION', message: globalNameCollisionMessage(collision) });
|
|
44
|
-
}
|
|
45
41
|
const maybeCustomTrigger = built.trigger;
|
|
46
42
|
if (maybeCustomTrigger !== undefined && !('kind' in maybeCustomTrigger) && maybeCustomTrigger instanceof FlowNode) {
|
|
47
43
|
// A custom FlowTrigger subclass reports through its own diagnostics() hook;
|
|
@@ -945,7 +941,12 @@ function checkFlow(built, ancestors, opts = {}) {
|
|
|
945
941
|
}
|
|
946
942
|
}
|
|
947
943
|
// Check $vars.<name>[.<field>] references resolve to a declared name or step.
|
|
944
|
+
const inputNames = new Set(built.inputs.map((x) => x.name));
|
|
948
945
|
for (const { js, step, errorSource } of exprs) {
|
|
946
|
+
// Comments and string-literal text blanked out, offsets preserved: the
|
|
947
|
+
// input-read rule below fires on a VALID name, so it has to tell a read
|
|
948
|
+
// from a mention, the same way `uip maestro flow validate` does.
|
|
949
|
+
const code = codeOnly(js);
|
|
949
950
|
// `rest` is the remaining dotted tail after `<name>.<field>` — needed for a
|
|
950
951
|
// subflow read, where the value lives one level deeper
|
|
951
952
|
// (`$vars.<call>.output.<childOutput>`). Every other rule reads only the
|
|
@@ -963,6 +964,36 @@ function checkFlow(built, ancestors, opts = {}) {
|
|
|
963
964
|
});
|
|
964
965
|
continue;
|
|
965
966
|
}
|
|
967
|
+
// `$vars.<input>` — a flow INPUT read by its bare name.
|
|
968
|
+
//
|
|
969
|
+
// Inputs, outputs and vars share one namespace in `variables.globals[]`,
|
|
970
|
+
// but they are not read the same way. A var or output is read as
|
|
971
|
+
// `$vars.<name>`. An input is not: serialize gives EVERY input global a
|
|
972
|
+
// `triggerNodeId` (the root trigger, an entry point, or a subflow child's
|
|
973
|
+
// own Start), and the runtime surfaces such an input only under that
|
|
974
|
+
// trigger's output — `$vars.<trigger>.output.<name>`, which is exactly what
|
|
975
|
+
// `input('<name>')` lowers to. The bare read resolves only by accident (a
|
|
976
|
+
// manual trigger also emits a process-level input of the same name) and is
|
|
977
|
+
// null under any other trigger. `uip maestro flow validate` refuses it
|
|
978
|
+
// (UV-16316, UiPath/cli#4236); this rule used to treat every declared name
|
|
979
|
+
// as readable bare, so check and compile passed what validate refused
|
|
980
|
+
// (five shipped examples, found with flow-builder-sdk#904).
|
|
981
|
+
//
|
|
982
|
+
// A step with the same name wins, as it does in validate, and so does a
|
|
983
|
+
// mention in a comment or a string literal (template `${…}` is code).
|
|
984
|
+
if (inputNames.has(name) && !stepNames.has(name) && code.startsWith('$vars', m.index ?? 0)) {
|
|
985
|
+
diags.push({
|
|
986
|
+
level: 'error',
|
|
987
|
+
code: 'INPUT_READ_WITHOUT_TRIGGER',
|
|
988
|
+
message: `"$vars.${name}" in "${step}" reads flow input "${name}" by its bare name. A flow input is `
|
|
989
|
+
+ `published under the trigger's output, so the runtime value is "$vars.${triggerId}.output.${name}" `
|
|
990
|
+
+ `— the bare read is null under any trigger but a manual one, and \`uip maestro flow validate\` `
|
|
991
|
+
+ `refuses it. In an expression, write input('${name}'); \`v()\` is for a .var() or an output.`,
|
|
992
|
+
step,
|
|
993
|
+
suggestion: `$vars.${triggerId}.output.${name}`,
|
|
994
|
+
});
|
|
995
|
+
continue;
|
|
996
|
+
}
|
|
966
997
|
// A BARE `$vars.<step>` — the step itself, not its output.
|
|
967
998
|
//
|
|
968
999
|
// **The mechanism is now MEASURED, not inferred.** This comment used to say
|
|
@@ -1612,6 +1643,20 @@ const INTERVAL_WORDS = [
|
|
|
1612
1643
|
* Without a library (`uip maestro flow check` run with no `--library`) the fields
|
|
1613
1644
|
* are unknown, so the warning keeps the generic wording.
|
|
1614
1645
|
*/
|
|
1646
|
+
/**
|
|
1647
|
+
* Every declared name must become exactly one global, because `flow validate`
|
|
1648
|
+
* rejects a repeated id (`[variables.globals] Duplicate variable ID`;
|
|
1649
|
+
* VARIABLE_NAME_COLLISION, flow-builder-sdk#890). The planning — including the one collision that is NOT an error, a same-named
|
|
1650
|
+
* `.output()` + `.var()`, which merges into one `out` global carrying the var's
|
|
1651
|
+
* default — lives in `planFlowGlobals`, shared with the serializer, so `check`
|
|
1652
|
+
* and the emitted file cannot disagree about it (flow-builder-sdk#899: `check`
|
|
1653
|
+
* said `Errors: 0`, validate refused the file).
|
|
1654
|
+
*/
|
|
1655
|
+
function checkGlobalNames(built, diags) {
|
|
1656
|
+
for (const p of planFlowGlobals({ inputs: built.inputs ?? [], outputs: built.outputs ?? [], vars: built.vars ?? [] }).problems) {
|
|
1657
|
+
diags.push({ level: 'error', code: p.code, message: p.message, ...(p.suggestion ? { suggestion: p.suggestion } : {}) });
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1615
1660
|
/**
|
|
1616
1661
|
* `{ updates }` writes a declared flow variable when the node completes. The
|
|
1617
1662
|
* assignable targets are `.var()` variables and flow outputs — assigning to an
|
|
@@ -1761,6 +1806,97 @@ function declaredRefName(js, triggerId) {
|
|
|
1761
1806
|
const viaTrigger = new RegExp(`^\\$vars\\.${triggerId}\\.output\\.([A-Za-z0-9_]+)$`).exec(js.trim());
|
|
1762
1807
|
return viaTrigger?.[1];
|
|
1763
1808
|
}
|
|
1809
|
+
/**
|
|
1810
|
+
* The JavaScript text with `//` and `/* *\/` comments and the text of string and
|
|
1811
|
+
* template literals replaced by spaces — same length, so a match offset in the
|
|
1812
|
+
* original indexes the same character here. A template's `${…}` bodies are code
|
|
1813
|
+
* and are kept (nested templates and strings inside them are blanked in turn).
|
|
1814
|
+
*
|
|
1815
|
+
* This is the read-versus-mention line `uip maestro flow validate` draws in its
|
|
1816
|
+
* `$vars` extraction (`collectVarsRefs`, `inProse`): a rule that fires on a VALID
|
|
1817
|
+
* name must not fire on `"pass $vars.x here"` or a commented-out line. Regex
|
|
1818
|
+
* literals are not recognised; a `$vars` reference inside one is vanishingly rare.
|
|
1819
|
+
*/
|
|
1820
|
+
function codeOnly(src) {
|
|
1821
|
+
const out = src.split('');
|
|
1822
|
+
const n = src.length;
|
|
1823
|
+
let i = 0;
|
|
1824
|
+
const blank = (from, to) => {
|
|
1825
|
+
for (let k = from; k < to && k < n; k++)
|
|
1826
|
+
if (out[k] !== '\n')
|
|
1827
|
+
out[k] = ' ';
|
|
1828
|
+
};
|
|
1829
|
+
/** Scan code from `i`; with `stopAtBrace`, return at the `}` closing a `${`. */
|
|
1830
|
+
const code = (stopAtBrace) => {
|
|
1831
|
+
let depth = 0;
|
|
1832
|
+
while (i < n) {
|
|
1833
|
+
const c = src[i];
|
|
1834
|
+
if (c === '/' && src[i + 1] === '/') {
|
|
1835
|
+
const end = src.indexOf('\n', i);
|
|
1836
|
+
const stop = end === -1 ? n : end;
|
|
1837
|
+
blank(i, stop);
|
|
1838
|
+
i = stop;
|
|
1839
|
+
continue;
|
|
1840
|
+
}
|
|
1841
|
+
if (c === '/' && src[i + 1] === '*') {
|
|
1842
|
+
const end = src.indexOf('*/', i + 2);
|
|
1843
|
+
const stop = end === -1 ? n : end + 2;
|
|
1844
|
+
blank(i, stop);
|
|
1845
|
+
i = stop;
|
|
1846
|
+
continue;
|
|
1847
|
+
}
|
|
1848
|
+
if (c === '"' || c === "'") {
|
|
1849
|
+
const start = i++;
|
|
1850
|
+
while (i < n && src[i] !== c && src[i] !== '\n')
|
|
1851
|
+
i += src[i] === '\\' ? 2 : 1;
|
|
1852
|
+
blank(start + 1, i);
|
|
1853
|
+
i++;
|
|
1854
|
+
continue;
|
|
1855
|
+
}
|
|
1856
|
+
if (c === '`') {
|
|
1857
|
+
template();
|
|
1858
|
+
continue;
|
|
1859
|
+
}
|
|
1860
|
+
if (stopAtBrace) {
|
|
1861
|
+
if (c === '{')
|
|
1862
|
+
depth++;
|
|
1863
|
+
else if (c === '}') {
|
|
1864
|
+
if (depth === 0)
|
|
1865
|
+
return;
|
|
1866
|
+
depth--;
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
i++;
|
|
1870
|
+
}
|
|
1871
|
+
};
|
|
1872
|
+
const template = () => {
|
|
1873
|
+
let start = ++i;
|
|
1874
|
+
while (i < n) {
|
|
1875
|
+
const c = src[i];
|
|
1876
|
+
if (c === '\\') {
|
|
1877
|
+
i += 2;
|
|
1878
|
+
continue;
|
|
1879
|
+
}
|
|
1880
|
+
if (c === '`') {
|
|
1881
|
+
blank(start, i);
|
|
1882
|
+
i++;
|
|
1883
|
+
return;
|
|
1884
|
+
}
|
|
1885
|
+
if (c === '$' && src[i + 1] === '{') {
|
|
1886
|
+
blank(start, i);
|
|
1887
|
+
i += 2;
|
|
1888
|
+
code(true);
|
|
1889
|
+
i++; // the `}` closing this `${`
|
|
1890
|
+
start = i;
|
|
1891
|
+
continue;
|
|
1892
|
+
}
|
|
1893
|
+
i++;
|
|
1894
|
+
}
|
|
1895
|
+
blank(start, n);
|
|
1896
|
+
};
|
|
1897
|
+
code(false);
|
|
1898
|
+
return out.join('');
|
|
1899
|
+
}
|
|
1764
1900
|
const GUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1765
1901
|
/**
|
|
1766
1902
|
* The four published-resource factories once each named the release key
|
|
@@ -3940,19 +4076,46 @@ function checkHttp(step, inputs, diags) {
|
|
|
3940
4076
|
// Moving to the managed node is not a rename. A plain step keeps a non-2xx on
|
|
3941
4077
|
// the SUCCESS path (`out(step,'statusCode')`); the managed one fails on it, so
|
|
3942
4078
|
// a status branch becomes an error handler reading the envelope's `status`.
|
|
3943
|
-
//
|
|
3944
|
-
//
|
|
4079
|
+
//
|
|
4080
|
+
// It is not a free upgrade either, and for a connection-less URL it is usually
|
|
4081
|
+
// the wrong move (flow-builder-sdk#898). With no `connection` the managed node
|
|
4082
|
+
// still goes through Integration Service: serialize emits the `uipath-uipath-http`
|
|
4083
|
+
// connector on `ImplicitConnection`, so the request leaves from IS's SHARED
|
|
4084
|
+
// egress. Measured on slack-weather-pipeline (3/3 runs): agents took the old
|
|
4085
|
+
// `managed: true` hint, and Open-Meteo answered that egress 429 "Daily API
|
|
4086
|
+
// request limit exceeded" while direct calls from the same box got 200. The
|
|
4087
|
+
// plain node goes direct. So the hint only steers toward `managed: true` when
|
|
4088
|
+
// the step was given connection settings; the two other real reasons to move
|
|
4089
|
+
// (a binding, an `.onError()`) already fire as errors with that suggestion
|
|
4090
|
+
// (HTTP_BINDING_REQUIRES_MANAGED, HTTP_ONERROR_V1).
|
|
3945
4091
|
if (inputs && !inputs.managed) {
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
4092
|
+
const asked = inputs;
|
|
4093
|
+
const wantsConnection = asked.connection !== undefined || asked.folder !== undefined
|
|
4094
|
+
|| asked.targetConnector !== undefined;
|
|
4095
|
+
diags.push(wantsConnection
|
|
4096
|
+
? {
|
|
4097
|
+
level: 'warning',
|
|
4098
|
+
code: 'HTTP_V1_RETIRED',
|
|
4099
|
+
message: `Step "${step}" emits core.action.http, which the tenant registry no longer serves (only `
|
|
4100
|
+
+ `core.action.http.v2). It sets Integration Service connection settings, which only the managed node `
|
|
4101
|
+
+ `can use. Moving to managed: true changes behaviour rather than just the node type: a non-2xx stops `
|
|
4102
|
+
+ `arriving on the success path with statusCode and fails the step instead, so a status branch becomes `
|
|
4103
|
+
+ `an .onError() handler reading err('${step}', 'status').`,
|
|
4104
|
+
step,
|
|
4105
|
+
suggestion: `http({ url: …, managed: true, connection, folder })`,
|
|
4106
|
+
}
|
|
4107
|
+
: {
|
|
4108
|
+
level: 'warning',
|
|
4109
|
+
code: 'HTTP_V1_RETIRED',
|
|
4110
|
+
message: `Step "${step}" emits core.action.http, which the tenant registry no longer serves (only `
|
|
4111
|
+
+ `core.action.http.v2). It still compiles, validates and runs, and it calls the URL directly. For a URL `
|
|
4112
|
+
+ `that needs no Integration Service connection, keep managed: false. managed: true is not a drop-in `
|
|
4113
|
+
+ `replacement: even without a connection it sends the request through Integration Service's shared `
|
|
4114
|
+
+ `egress (rate limits there are shared, and public APIs can answer 429 where a direct call succeeds), `
|
|
4115
|
+
+ `and a non-2xx fails the step instead of arriving with statusCode. Switch only if the call needs an `
|
|
4116
|
+
+ `Integration Service connection or an .onError() handler.`,
|
|
4117
|
+
step,
|
|
4118
|
+
});
|
|
3956
4119
|
}
|
|
3957
4120
|
const connection = inputs?.connection;
|
|
3958
4121
|
const folder = inputs?.folder;
|
package/dist/cli-result.d.ts
CHANGED
|
@@ -10,6 +10,20 @@
|
|
|
10
10
|
* @param fields - JSON-serializable values, e.g. `{ path, nodes, edges }`.
|
|
11
11
|
*/
|
|
12
12
|
export declare function reportResult(fields: Record<string, unknown>): void;
|
|
13
|
+
/**
|
|
14
|
+
* Print text that IS the verb's result (a pretty-printed object, a captured
|
|
15
|
+
* path), as opposed to a status line such as `compile: wrote X`.
|
|
16
|
+
*
|
|
17
|
+
* Under `--json` this prints nothing: the result is already the JSON object on
|
|
18
|
+
* stdout, and the umbrella routes a tool's stdout to stderr meanwhile, so a
|
|
19
|
+
* plain `console.log` of the result would land on stderr as a second copy.
|
|
20
|
+
* A caller that merges the streams (`2>&1`, an agent's shell tool) then gets
|
|
21
|
+
* two documents and `json.load` fails with `Extra data` (#901). Status lines
|
|
22
|
+
* keep using `console.log`; they are what stderr is for under `--json`.
|
|
23
|
+
*
|
|
24
|
+
* @param text - the result as text; a trailing newline is added.
|
|
25
|
+
*/
|
|
26
|
+
export declare function printResult(text: string): void;
|
|
13
27
|
/**
|
|
14
28
|
* Report a check's diagnostics as `{ errors, warnings, diagnostics }`, the one
|
|
15
29
|
* shape every family's check (and compile's check pass) uses.
|
package/dist/cli-result.js
CHANGED
|
@@ -18,6 +18,24 @@ function slot() {
|
|
|
18
18
|
export function reportResult(fields) {
|
|
19
19
|
Object.assign(slot().fields, fields);
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Print text that IS the verb's result (a pretty-printed object, a captured
|
|
23
|
+
* path), as opposed to a status line such as `compile: wrote X`.
|
|
24
|
+
*
|
|
25
|
+
* Under `--json` this prints nothing: the result is already the JSON object on
|
|
26
|
+
* stdout, and the umbrella routes a tool's stdout to stderr meanwhile, so a
|
|
27
|
+
* plain `console.log` of the result would land on stderr as a second copy.
|
|
28
|
+
* A caller that merges the streams (`2>&1`, an agent's shell tool) then gets
|
|
29
|
+
* two documents and `json.load` fails with `Extra data` (#901). Status lines
|
|
30
|
+
* keep using `console.log`; they are what stderr is for under `--json`.
|
|
31
|
+
*
|
|
32
|
+
* @param text - the result as text; a trailing newline is added.
|
|
33
|
+
*/
|
|
34
|
+
export function printResult(text) {
|
|
35
|
+
if (slot().json)
|
|
36
|
+
return;
|
|
37
|
+
process.stdout.write(`${text}\n`);
|
|
38
|
+
}
|
|
21
39
|
/**
|
|
22
40
|
* Report a check's diagnostics as `{ errors, warnings, diagnostics }`, the one
|
|
23
41
|
* shape every family's check (and compile's check pass) uses.
|
package/dist/core/expr.d.ts
CHANGED
|
@@ -27,9 +27,15 @@ export declare class Expr {
|
|
|
27
27
|
*/
|
|
28
28
|
export declare function lit(value: unknown): Expr;
|
|
29
29
|
/**
|
|
30
|
-
* Reference a flow
|
|
30
|
+
* Reference a flow variable or output by name → `$vars.<name>`.
|
|
31
31
|
*
|
|
32
|
-
* @
|
|
32
|
+
* @remarks
|
|
33
|
+
* Not for a flow INPUT: an input is published under the trigger's output, so read
|
|
34
|
+
* it with {@link input} (`$vars.start.output.<name>`). A bare `$vars.<input>` is
|
|
35
|
+
* null under any trigger but a manual one, and `check` and `uip maestro flow
|
|
36
|
+
* validate` both refuse it.
|
|
37
|
+
*
|
|
38
|
+
* @param name - The variable or output as declared with `.var(...)` / `.output(...)`.
|
|
33
39
|
* @returns An {@link Expr} reading that variable at run time.
|
|
34
40
|
*/
|
|
35
41
|
export declare function v(name: string): Expr;
|
package/dist/core/expr.js
CHANGED
|
@@ -33,9 +33,15 @@ export function lit(value) {
|
|
|
33
33
|
return new Expr(JSON.stringify(value), true, value);
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
|
-
* Reference a flow
|
|
36
|
+
* Reference a flow variable or output by name → `$vars.<name>`.
|
|
37
37
|
*
|
|
38
|
-
* @
|
|
38
|
+
* @remarks
|
|
39
|
+
* Not for a flow INPUT: an input is published under the trigger's output, so read
|
|
40
|
+
* it with {@link input} (`$vars.start.output.<name>`). A bare `$vars.<input>` is
|
|
41
|
+
* null under any trigger but a manual one, and `check` and `uip maestro flow
|
|
42
|
+
* validate` both refuse it.
|
|
43
|
+
*
|
|
44
|
+
* @param name - The variable or output as declared with `.var(...)` / `.output(...)`.
|
|
39
45
|
* @returns An {@link Expr} reading that variable at run time.
|
|
40
46
|
*/
|
|
41
47
|
export function v(name) {
|
|
@@ -13,13 +13,11 @@
|
|
|
13
13
|
* `Duplicate variable ID: "x" (appears 2 times)`). Before flow-builder-sdk#890,
|
|
14
14
|
* `check` and `compile` were silent and wrote both rows.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
* the
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* validates — a `.var()` is an `inout` global, which the product counts as an
|
|
22
|
-
* output too — but the flow then has no declared `out` for it.
|
|
16
|
+
* One pair is NOT a collision: `.output({ x }) + .var('x')` with the same type is
|
|
17
|
+
* the loop-accumulator shape ("declare the result, then seed it"), and
|
|
18
|
+
* `planFlowGlobals` (`flow-variables.ts`) merges it into ONE `out` global that
|
|
19
|
+
* carries the var's default (flow-builder-sdk#899). Every other repeat of a name
|
|
20
|
+
* is reported with the sentence below.
|
|
23
21
|
*/
|
|
24
22
|
export type GlobalKind = 'input' | 'output' | 'var';
|
|
25
23
|
export interface GlobalNameCollision {
|
|
@@ -13,13 +13,11 @@
|
|
|
13
13
|
* `Duplicate variable ID: "x" (appears 2 times)`). Before flow-builder-sdk#890,
|
|
14
14
|
* `check` and `compile` were silent and wrote both rows.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
* the
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* validates — a `.var()` is an `inout` global, which the product counts as an
|
|
22
|
-
* output too — but the flow then has no declared `out` for it.
|
|
16
|
+
* One pair is NOT a collision: `.output({ x }) + .var('x')` with the same type is
|
|
17
|
+
* the loop-accumulator shape ("declare the result, then seed it"), and
|
|
18
|
+
* `planFlowGlobals` (`flow-variables.ts`) merges it into ONE `out` global that
|
|
19
|
+
* carries the var's default (flow-builder-sdk#899). Every other repeat of a name
|
|
20
|
+
* is reported with the sentence below.
|
|
23
21
|
*/
|
|
24
22
|
/** The names declared more than once across `.input()`, `.output()` and `.var()`. */
|
|
25
23
|
export function globalNameCollisions(inputs, outputs, vars) {
|
|
@@ -49,12 +47,5 @@ export function globalNameCollisionMessage({ name, kinds }) {
|
|
|
49
47
|
const head = `"${name}" is declared ${kinds.length} times (${declared}). The .flow keeps inputs, outputs and `
|
|
50
48
|
+ `variables in one list, variables.globals[], so this compiles to ${kinds.length} globals with the id "${name}" `
|
|
51
49
|
+ `and \`uip maestro flow validate\` refuses it: Duplicate variable ID: "${name}".`;
|
|
52
|
-
const distinct = new Set(kinds);
|
|
53
|
-
if (distinct.size === 2 && distinct.has('output') && distinct.has('var')) {
|
|
54
|
-
return `${head} Keep the output and give the variable its own name: `
|
|
55
|
-
+ `.var('${name}Value', …) with { updates: { ${name}Value: … } }, then .return({ ${name}: v('${name}Value') }). `
|
|
56
|
-
+ `(Dropping "${name}" from .output() also validates — a .var() compiles to an \`inout\` global, which the `
|
|
57
|
-
+ `product counts as an output — but then the flow declares no \`out\` variable for it.)`;
|
|
58
|
-
}
|
|
59
50
|
return `${head} Rename one of them.`;
|
|
60
51
|
}
|
package/dist/decompile.js
CHANGED
|
@@ -1762,7 +1762,7 @@ function emitVariables(e, depth, flow, imp) {
|
|
|
1762
1762
|
if (inputs.length)
|
|
1763
1763
|
e.line(depth, `.input(${shape(inputs, true)})`);
|
|
1764
1764
|
if (outputs.length)
|
|
1765
|
-
e.line(depth, `.output(${shape(outputs,
|
|
1765
|
+
e.line(depth, `.output(${shape(outputs, true)})`);
|
|
1766
1766
|
for (const g of inouts) {
|
|
1767
1767
|
const rich = declExpr(g, false, imp);
|
|
1768
1768
|
const def = g.defaultValue !== undefined ? `, ${literal(g.defaultValue)}` : '';
|
|
@@ -2538,7 +2538,7 @@ function emitChildFlow(name, child, ctx) {
|
|
|
2538
2538
|
};
|
|
2539
2539
|
const parts = [`${ctx.imp.use('flow')}(${str(name)})`];
|
|
2540
2540
|
// Variables.
|
|
2541
|
-
const shape = (dir) => `{ ${globals.filter((g) => g.direction === dir).map((g) => `${key(g.name ?? g.id)}: ${declExpr(g, dir
|
|
2541
|
+
const shape = (dir) => `{ ${globals.filter((g) => g.direction === dir).map((g) => `${key(g.name ?? g.id)}: ${declExpr(g, dir !== 'inout', ctx.imp)}`).join(', ')} }`;
|
|
2542
2542
|
if (globals.some((g) => g.direction === 'in'))
|
|
2543
2543
|
parts.push(`.input(${shape('in')})`);
|
|
2544
2544
|
if (globals.some((g) => g.direction === 'out'))
|
package/dist/flow-sdk.d.ts
CHANGED
|
@@ -71,7 +71,11 @@ export interface VarSpec {
|
|
|
71
71
|
subType?: string;
|
|
72
72
|
/** JSON schema for complex type validation (object/array shapes). */
|
|
73
73
|
schema?: Record<string, unknown>;
|
|
74
|
-
/**
|
|
74
|
+
/**
|
|
75
|
+
* Default value — the variable's value before anything assigns it. On an
|
|
76
|
+
* output it is the starting value a `{ updates }` accumulator reads, e.g.
|
|
77
|
+
* `.output({ product: { type: types.number, default: 1 } })`.
|
|
78
|
+
*/
|
|
75
79
|
default?: unknown;
|
|
76
80
|
}
|
|
77
81
|
/**
|
|
@@ -377,7 +381,7 @@ export interface ReturnOptions extends NodeOptions {
|
|
|
377
381
|
* `done` become `done` and `done2`.
|
|
378
382
|
*
|
|
379
383
|
* ```ts
|
|
380
|
-
* .branch('checkTemperature', js`$
|
|
384
|
+
* .branch('checkTemperature', js`${input('tempF')} > 60`,
|
|
381
385
|
* (t) => t.return({}, { name: 'endNiceDay', label: 'Nice Day' }),
|
|
382
386
|
* (e) => e.return({}, { name: 'endBringJacket', label: 'Bring a Jacket' }))
|
|
383
387
|
* ```
|
|
@@ -741,7 +745,7 @@ declare class StepList {
|
|
|
741
745
|
* ```ts
|
|
742
746
|
* export default flow('draft-review')
|
|
743
747
|
* .output({ summary: types.string })
|
|
744
|
-
* //
|
|
748
|
+
* // A working variable the arms assign; the return maps it onto the output.
|
|
745
749
|
* .var('result', types.string)
|
|
746
750
|
* .stepSwitch('review', hitl({ fields: [], outcomes: ['Publish', 'Revise'] }), [
|
|
747
751
|
* { value: 'Publish', body: (b) => b.step('publish', script({ code: 'return "published";', returns: 'string' }), { updates: { result: out('publish') } }) },
|
|
@@ -1323,6 +1327,25 @@ declare class FlowBuilder extends StepList {
|
|
|
1323
1327
|
/**
|
|
1324
1328
|
* Declare the flow's outputs — what `.return(...)` binds values to.
|
|
1325
1329
|
*
|
|
1330
|
+
* @remarks
|
|
1331
|
+
* An output is also a flow variable: `v('<name>')` reads it and a step's
|
|
1332
|
+
* `{ updates }` assigns it, so an accumulator needs no separate `.var()`. Give
|
|
1333
|
+
* it a starting value with the rich spelling, `{ type, default }`. A `.var()`
|
|
1334
|
+
* of the same name and type is merged into the output rather than emitted as a
|
|
1335
|
+
* second variable (its default becomes the output's); a different type is a
|
|
1336
|
+
* `check` error, `OUTPUT_VAR_MISMATCH`.
|
|
1337
|
+
*
|
|
1338
|
+
* @example
|
|
1339
|
+
* **A loop accumulator**
|
|
1340
|
+
* ```ts
|
|
1341
|
+
* flow('multiply')
|
|
1342
|
+
* .output({ product: { type: types.number, default: 1 } })
|
|
1343
|
+
* .loop('each', lit([2, 3, 4]), (b) => b
|
|
1344
|
+
* .step('mul', script({ code: 'return $vars.product * $vars.each.currentItem;', returns: 'number' }),
|
|
1345
|
+
* { updates: { product: out('mul') } }))
|
|
1346
|
+
* .return({ product: v('product') })
|
|
1347
|
+
* ```
|
|
1348
|
+
*
|
|
1326
1349
|
* @param shape - Output names to type descriptors, e.g. `{ total: types.number }`.
|
|
1327
1350
|
* @returns This builder, so calls chain.
|
|
1328
1351
|
*/
|
|
@@ -1384,7 +1407,7 @@ export declare function flow(id: string): FlowBuilder;
|
|
|
1384
1407
|
* const reverse = flow('reverse-text')
|
|
1385
1408
|
* .input({ text: types.string })
|
|
1386
1409
|
* .output({ reversed: types.string })
|
|
1387
|
-
* .step('rev', script({ code: 'return $vars.text.split("").reverse().join("");' }))
|
|
1410
|
+
* .step('rev', script({ code: 'return $vars.start.output.text.split("").reverse().join("");' }))
|
|
1388
1411
|
* .return({ reversed: out('rev') });
|
|
1389
1412
|
*
|
|
1390
1413
|
* export default flow('reverse-outer')
|
package/dist/flow-sdk.js
CHANGED
|
@@ -63,16 +63,14 @@ function assertShape(method, shape) {
|
|
|
63
63
|
}
|
|
64
64
|
/**
|
|
65
65
|
* Normalize a `TypeDesc | VarSpec` declaration value into VarDecl fields.
|
|
66
|
-
*
|
|
66
|
+
* Every direction takes a `default`: the format's `workflowVariableSchema` allows
|
|
67
|
+
* `defaultValue` on `in`, `out` and `inout` alike, and the v1 CLI's
|
|
68
|
+
* `variable add --direction out --default-value` writes one on an output.
|
|
67
69
|
*/
|
|
68
70
|
function varDeclOf(method, name, value) {
|
|
69
71
|
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
|
70
72
|
const spec = value;
|
|
71
73
|
assertType(method, name, spec.type);
|
|
72
|
-
if (method === 'output' && spec.default !== undefined) {
|
|
73
|
-
throw new TypeError(`.output() declared ${JSON.stringify(name)} with a default, but the .flow format only persists `
|
|
74
|
-
+ `defaults on inputs and vars. Bind the value with .return() instead.`);
|
|
75
|
-
}
|
|
76
74
|
return {
|
|
77
75
|
type: spec.type,
|
|
78
76
|
...(spec.default !== undefined ? { default: spec.default } : {}),
|
|
@@ -332,7 +330,7 @@ class StepList {
|
|
|
332
330
|
* ```ts
|
|
333
331
|
* export default flow('draft-review')
|
|
334
332
|
* .output({ summary: types.string })
|
|
335
|
-
* //
|
|
333
|
+
* // A working variable the arms assign; the return maps it onto the output.
|
|
336
334
|
* .var('result', types.string)
|
|
337
335
|
* .stepSwitch('review', hitl({ fields: [], outcomes: ['Publish', 'Revise'] }), [
|
|
338
336
|
* { value: 'Publish', body: (b) => b.step('publish', script({ code: 'return "published";', returns: 'string' }), { updates: { result: out('publish') } }) },
|
|
@@ -1096,6 +1094,25 @@ class FlowBuilder extends StepList {
|
|
|
1096
1094
|
/**
|
|
1097
1095
|
* Declare the flow's outputs — what `.return(...)` binds values to.
|
|
1098
1096
|
*
|
|
1097
|
+
* @remarks
|
|
1098
|
+
* An output is also a flow variable: `v('<name>')` reads it and a step's
|
|
1099
|
+
* `{ updates }` assigns it, so an accumulator needs no separate `.var()`. Give
|
|
1100
|
+
* it a starting value with the rich spelling, `{ type, default }`. A `.var()`
|
|
1101
|
+
* of the same name and type is merged into the output rather than emitted as a
|
|
1102
|
+
* second variable (its default becomes the output's); a different type is a
|
|
1103
|
+
* `check` error, `OUTPUT_VAR_MISMATCH`.
|
|
1104
|
+
*
|
|
1105
|
+
* @example
|
|
1106
|
+
* **A loop accumulator**
|
|
1107
|
+
* ```ts
|
|
1108
|
+
* flow('multiply')
|
|
1109
|
+
* .output({ product: { type: types.number, default: 1 } })
|
|
1110
|
+
* .loop('each', lit([2, 3, 4]), (b) => b
|
|
1111
|
+
* .step('mul', script({ code: 'return $vars.product * $vars.each.currentItem;', returns: 'number' }),
|
|
1112
|
+
* { updates: { product: out('mul') } }))
|
|
1113
|
+
* .return({ product: v('product') })
|
|
1114
|
+
* ```
|
|
1115
|
+
*
|
|
1099
1116
|
* @param shape - Output names to type descriptors, e.g. `{ total: types.number }`.
|
|
1100
1117
|
* @returns This builder, so calls chain.
|
|
1101
1118
|
*/
|
|
@@ -1196,7 +1213,7 @@ export function flow(id) {
|
|
|
1196
1213
|
* const reverse = flow('reverse-text')
|
|
1197
1214
|
* .input({ text: types.string })
|
|
1198
1215
|
* .output({ reversed: types.string })
|
|
1199
|
-
* .step('rev', script({ code: 'return $vars.text.split("").reverse().join("");' }))
|
|
1216
|
+
* .step('rev', script({ code: 'return $vars.start.output.text.split("").reverse().join("");' }))
|
|
1200
1217
|
* .return({ reversed: out('rev') });
|
|
1201
1218
|
*
|
|
1202
1219
|
* export default flow('reverse-outer')
|
package/dist/flow-variables.d.ts
CHANGED
|
@@ -11,3 +11,69 @@ export interface FlowGlobalDeclaration {
|
|
|
11
11
|
}
|
|
12
12
|
/** Read both supported Flow variable spellings into one declaration table. */
|
|
13
13
|
export declare function readFlowGlobals(variables: unknown, strict?: boolean): FlowGlobalDeclaration[];
|
|
14
|
+
/** The declaration fields `planFlowGlobals` reads (structurally a `VarDecl`). */
|
|
15
|
+
export interface DeclaredVariable {
|
|
16
|
+
name: string;
|
|
17
|
+
type: string;
|
|
18
|
+
default?: unknown;
|
|
19
|
+
description?: string;
|
|
20
|
+
subType?: string;
|
|
21
|
+
schema?: Record<string, unknown>;
|
|
22
|
+
}
|
|
23
|
+
/** One reason the declared names cannot become one global each. */
|
|
24
|
+
export interface FlowGlobalsProblem {
|
|
25
|
+
code: 'VARIABLE_NAME_COLLISION' | 'OUTPUT_VAR_MISMATCH';
|
|
26
|
+
name: string;
|
|
27
|
+
message: string;
|
|
28
|
+
suggestion?: string;
|
|
29
|
+
}
|
|
30
|
+
/** What `variables.globals` will hold, before directions and trigger ids are stamped. */
|
|
31
|
+
export interface FlowGlobalsPlan<D extends DeclaredVariable = DeclaredVariable> {
|
|
32
|
+
/** Every `.output()`, in order. One with a same-named `.var()` carries that var's default. */
|
|
33
|
+
outputs: D[];
|
|
34
|
+
/** The `.var()` declarations NOT folded into an output, in order. */
|
|
35
|
+
vars: D[];
|
|
36
|
+
/** Names declared by both `.output()` and `.var()` and merged into one `out` global. */
|
|
37
|
+
merged: string[];
|
|
38
|
+
/** Collisions that would emit two globals with one id. Empty when the plan is emittable. */
|
|
39
|
+
problems: FlowGlobalsProblem[];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Turn a flow's `.input()` / `.output()` / `.var()` declarations into the
|
|
43
|
+
* globals the `.flow` file will carry — one per id, because the product's
|
|
44
|
+
* `flow validate` rejects a repeated id (`[variables.globals] Duplicate variable
|
|
45
|
+
* ID`), and the three methods share that one namespace.
|
|
46
|
+
*
|
|
47
|
+
* **An output and a var with the same name are one variable** (flow-builder-sdk#899).
|
|
48
|
+
* The natural loop accumulator is
|
|
49
|
+
* `.output({ product: types.number }).var('product', types.number, 1)`: declare
|
|
50
|
+
* the result, then seed it. Both declarations describe the same value, so they
|
|
51
|
+
* merge into ONE `direction: 'out'` global that carries the var's default.
|
|
52
|
+
*
|
|
53
|
+
* Why `out` and not `inout`, measured against `@uipath/flow-converter` 0.69 and
|
|
54
|
+
* `@uipath/maestro-sdk` (the code that turns a `.flow` into what runs):
|
|
55
|
+
* - only an `out` global is a flow OUTPUT at the root — `buildNodeOutputMappings`
|
|
56
|
+
* drops an End node's mapping for any id that is not `direction: 'out'`;
|
|
57
|
+
* - `inout` is published as an INPUT too — solution discovery puts it on the
|
|
58
|
+
* entry point's input schema — so `inout` would widen the caller's contract;
|
|
59
|
+
* - `{ updates }` on an `out` global is the same output mapping (`var: <id>`) as
|
|
60
|
+
* on an `inout` one, so a loop body can read and rewrite it mid-run;
|
|
61
|
+
* - the `default` on `<uipath:output>` is serialized (`collectVariablesFromNodes`)
|
|
62
|
+
* and a subflow seeds its `out` globals from `defaultValue ?? null`
|
|
63
|
+
* (`buildSeedTask`). The v1 CLI's `variable add --direction out
|
|
64
|
+
* --default-value` writes the same shape.
|
|
65
|
+
*
|
|
66
|
+
* Metadata: the output's `description` / `subType` / `schema` win, falling back
|
|
67
|
+
* to the var's; the default is whichever side declared one. A disagreement the
|
|
68
|
+
* merge would have to resolve by picking a side — a different `type`, two
|
|
69
|
+
* different defaults, two different `subType`s or `schema`s — is
|
|
70
|
+
* `OUTPUT_VAR_MISMATCH` instead, because silently choosing one changes what the
|
|
71
|
+
* flow computes. Every other repeated name (two vars, two outputs, an input
|
|
72
|
+
* reused as an output or var) stays `VARIABLE_NAME_COLLISION`
|
|
73
|
+
* (`core/global-names.ts`, flow-builder-sdk#890).
|
|
74
|
+
*/
|
|
75
|
+
export declare function planFlowGlobals<D extends DeclaredVariable>(decl: {
|
|
76
|
+
inputs: readonly D[];
|
|
77
|
+
outputs: readonly D[];
|
|
78
|
+
vars: readonly D[];
|
|
79
|
+
}): FlowGlobalsPlan<D>;
|