@uipath/maestro-builder-sdk 6.15.4 → 6.15.6
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 +122 -121
- package/dist/api-members.md +410 -405
- package/dist/bpmn/bpmn-sdk.d.ts +170 -18
- package/dist/bpmn/bpmn-sdk.js +123 -5
- package/dist/bpmn/check-cli.js +4 -3
- 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/check.js +142 -6
- package/dist/cli-result.d.ts +14 -0
- package/dist/cli-result.js +18 -0
- 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/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
|
@@ -259,6 +259,57 @@ const SLOT_FOR_LOCATION = {
|
|
|
259
259
|
query: 'queryParameters',
|
|
260
260
|
path: 'pathParameters',
|
|
261
261
|
};
|
|
262
|
+
/**
|
|
263
|
+
* Fold a body keyed by the registry's dotted field ids into the nested request
|
|
264
|
+
* object those ids describe: `{"fields.project.key":"CE"}` → `{"fields":{"project":{"key":"CE"}}}`.
|
|
265
|
+
*
|
|
266
|
+
* `buildConnectorInputs` keeps the DOTTED keys on purpose — Flow's `bodyParameters` is a
|
|
267
|
+
* flat map and Flow's runtime (`convertDotNotationToNested`) nests it before the call.
|
|
268
|
+
* The Maestro BPMN runtime does not: `IntSvcSendTaskHandler` sends the `body` input as
|
|
269
|
+
* written, so a dotted key reaches the connector as a literal property name. Measured on
|
|
270
|
+
* flow-v2-run-12: the SDK's Jira `create-issue` body went out flat and Integration
|
|
271
|
+
* Service answered 400 `project: Specify a valid project ID or key`; the v1 arm, which
|
|
272
|
+
* writes the nested form by hand, created the ticket. A BPMN body therefore goes through
|
|
273
|
+
* this before it is serialised.
|
|
274
|
+
*
|
|
275
|
+
* A `[*]` segment names an array. On a leaf (`users[*]`) an array value is kept and a
|
|
276
|
+
* scalar becomes a one-element list, as the product's own conversion does; on an inner
|
|
277
|
+
* segment (`attachments[*].url`) each element of an array value becomes one object, and
|
|
278
|
+
* a scalar one. Later keys win over earlier ones at the same leaf; a nested object the
|
|
279
|
+
* author already wrote under a plain key is merged into, not replaced.
|
|
280
|
+
*/
|
|
281
|
+
export function nestConnectorBody(flat) {
|
|
282
|
+
const out = {};
|
|
283
|
+
for (const [name, value] of Object.entries(flat)) {
|
|
284
|
+
const parts = connectorPath(name);
|
|
285
|
+
place(out, parts, 0, value);
|
|
286
|
+
}
|
|
287
|
+
return out;
|
|
288
|
+
}
|
|
289
|
+
function place(target, parts, index, value) {
|
|
290
|
+
const part = parts[index];
|
|
291
|
+
const last = index === parts.length - 1;
|
|
292
|
+
if (last) {
|
|
293
|
+
target[part.key] = part.array && !Array.isArray(value) ? [value] : value;
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
if (part.array) {
|
|
297
|
+
const elements = Array.isArray(value) ? value : [value];
|
|
298
|
+
const existing = Array.isArray(target[part.key]) ? target[part.key] : [];
|
|
299
|
+
target[part.key] = elements.map((element, i) => {
|
|
300
|
+
const slot = isRecord(existing[i]) ? existing[i] : {};
|
|
301
|
+
place(slot, parts, index + 1, element);
|
|
302
|
+
return slot;
|
|
303
|
+
});
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
const next = isRecord(target[part.key]) ? target[part.key] : {};
|
|
307
|
+
target[part.key] = next;
|
|
308
|
+
place(next, parts, index + 1, value);
|
|
309
|
+
}
|
|
310
|
+
function isRecord(value) {
|
|
311
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
312
|
+
}
|
|
262
313
|
function connectorPath(name) {
|
|
263
314
|
return name.split('.').map((raw) => ({
|
|
264
315
|
key: raw.endsWith('[*]') ? raw.slice(0, -3) : raw,
|
|
@@ -9,12 +9,14 @@
|
|
|
9
9
|
* `.waitForEvent()` node carries the same facts under its own names, and
|
|
10
10
|
* `bpmn/check.ts` adapts one to the other.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
12
|
+
* BPMN carries `filters` too, since flow-v2-run-12: both families write payload
|
|
13
|
+
* conditions into the subscription the platform reads (Flow's `filterExpression`,
|
|
14
|
+
* BPMN's `body.filters.expression`), lowered by the same grammar, so the
|
|
15
|
+
* `EVENT_FILTER_*` family fires for both. The subscription rules are the rest:
|
|
16
|
+
* whether it resolves at all, whether a generic event named its object, and whether
|
|
17
|
+
* its scope names parameters the operation actually declares.
|
|
18
|
+
* `skill-bpmn-outlook-waitfor-email` in `flow-v2-run-8` failed on exactly that last
|
|
19
|
+
* question, and in run 12 on having nowhere to put a payload condition.
|
|
18
20
|
*/
|
|
19
21
|
import { type LookupResolutions } from './lookups.js';
|
|
20
22
|
import { type ByoaEventFacts, type ByoaLedger } from './byoa.js';
|
|
@@ -9,12 +9,14 @@
|
|
|
9
9
|
* `.waitForEvent()` node carries the same facts under its own names, and
|
|
10
10
|
* `bpmn/check.ts` adapts one to the other.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
12
|
+
* BPMN carries `filters` too, since flow-v2-run-12: both families write payload
|
|
13
|
+
* conditions into the subscription the platform reads (Flow's `filterExpression`,
|
|
14
|
+
* BPMN's `body.filters.expression`), lowered by the same grammar, so the
|
|
15
|
+
* `EVENT_FILTER_*` family fires for both. The subscription rules are the rest:
|
|
16
|
+
* whether it resolves at all, whether a generic event named its object, and whether
|
|
17
|
+
* its scope names parameters the operation actually declares.
|
|
18
|
+
* `skill-bpmn-outlook-waitfor-email` in `flow-v2-run-8` failed on exactly that last
|
|
19
|
+
* question, and in run 12 on having nowhere to put a payload condition.
|
|
18
20
|
*/
|
|
19
21
|
import { isLookupToken, resolvedValue, unresolvedLookupMessage, } from './lookups.js';
|
|
20
22
|
import { prepareCommand } from './cli-spelling.js';
|
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>;
|
package/dist/flow-variables.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { globalNameCollisionMessage, globalNameCollisions } from './core/global-names.js';
|
|
1
2
|
const CONTAINER_KEYS = new Set(['globals', 'nodes', 'variableUpdates']);
|
|
2
3
|
const DIRECTIONS = new Set(['in', 'out', 'inout']);
|
|
3
4
|
/** Read both supported Flow variable spellings into one declaration table. */
|
|
@@ -40,3 +41,86 @@ export function readFlowGlobals(variables, strict = false) {
|
|
|
40
41
|
}
|
|
41
42
|
return globals;
|
|
42
43
|
}
|
|
44
|
+
const sameJson = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
45
|
+
/**
|
|
46
|
+
* Turn a flow's `.input()` / `.output()` / `.var()` declarations into the
|
|
47
|
+
* globals the `.flow` file will carry — one per id, because the product's
|
|
48
|
+
* `flow validate` rejects a repeated id (`[variables.globals] Duplicate variable
|
|
49
|
+
* ID`), and the three methods share that one namespace.
|
|
50
|
+
*
|
|
51
|
+
* **An output and a var with the same name are one variable** (flow-builder-sdk#899).
|
|
52
|
+
* The natural loop accumulator is
|
|
53
|
+
* `.output({ product: types.number }).var('product', types.number, 1)`: declare
|
|
54
|
+
* the result, then seed it. Both declarations describe the same value, so they
|
|
55
|
+
* merge into ONE `direction: 'out'` global that carries the var's default.
|
|
56
|
+
*
|
|
57
|
+
* Why `out` and not `inout`, measured against `@uipath/flow-converter` 0.69 and
|
|
58
|
+
* `@uipath/maestro-sdk` (the code that turns a `.flow` into what runs):
|
|
59
|
+
* - only an `out` global is a flow OUTPUT at the root — `buildNodeOutputMappings`
|
|
60
|
+
* drops an End node's mapping for any id that is not `direction: 'out'`;
|
|
61
|
+
* - `inout` is published as an INPUT too — solution discovery puts it on the
|
|
62
|
+
* entry point's input schema — so `inout` would widen the caller's contract;
|
|
63
|
+
* - `{ updates }` on an `out` global is the same output mapping (`var: <id>`) as
|
|
64
|
+
* on an `inout` one, so a loop body can read and rewrite it mid-run;
|
|
65
|
+
* - the `default` on `<uipath:output>` is serialized (`collectVariablesFromNodes`)
|
|
66
|
+
* and a subflow seeds its `out` globals from `defaultValue ?? null`
|
|
67
|
+
* (`buildSeedTask`). The v1 CLI's `variable add --direction out
|
|
68
|
+
* --default-value` writes the same shape.
|
|
69
|
+
*
|
|
70
|
+
* Metadata: the output's `description` / `subType` / `schema` win, falling back
|
|
71
|
+
* to the var's; the default is whichever side declared one. A disagreement the
|
|
72
|
+
* merge would have to resolve by picking a side — a different `type`, two
|
|
73
|
+
* different defaults, two different `subType`s or `schema`s — is
|
|
74
|
+
* `OUTPUT_VAR_MISMATCH` instead, because silently choosing one changes what the
|
|
75
|
+
* flow computes. Every other repeated name (two vars, two outputs, an input
|
|
76
|
+
* reused as an output or var) stays `VARIABLE_NAME_COLLISION`
|
|
77
|
+
* (`core/global-names.ts`, flow-builder-sdk#890).
|
|
78
|
+
*/
|
|
79
|
+
export function planFlowGlobals(decl) {
|
|
80
|
+
const problems = [];
|
|
81
|
+
const varsByName = new Map(decl.vars.map((d) => [d.name, d]));
|
|
82
|
+
const mergeable = new Set();
|
|
83
|
+
for (const collision of globalNameCollisions(decl.inputs, decl.outputs, decl.vars)) {
|
|
84
|
+
const pair = collision.kinds.length === 2 && collision.kinds[0] === 'output' && collision.kinds[1] === 'var';
|
|
85
|
+
if (pair)
|
|
86
|
+
mergeable.add(collision.name);
|
|
87
|
+
else
|
|
88
|
+
problems.push({ code: 'VARIABLE_NAME_COLLISION', name: collision.name, message: globalNameCollisionMessage(collision) });
|
|
89
|
+
}
|
|
90
|
+
const merged = [];
|
|
91
|
+
const outputs = decl.outputs.map((o) => {
|
|
92
|
+
const vd = varsByName.get(o.name);
|
|
93
|
+
if (!vd || !mergeable.has(o.name))
|
|
94
|
+
return o;
|
|
95
|
+
const clash = [];
|
|
96
|
+
if (o.type !== vd.type)
|
|
97
|
+
clash.push(`the output is ${o.type} and the var is ${vd.type}`);
|
|
98
|
+
if (o.default !== undefined && vd.default !== undefined && !sameJson(o.default, vd.default))
|
|
99
|
+
clash.push(`the output's default is ${JSON.stringify(o.default)} and the var's is ${JSON.stringify(vd.default)}`);
|
|
100
|
+
if (o.subType !== undefined && vd.subType !== undefined && o.subType !== vd.subType)
|
|
101
|
+
clash.push(`the output's subType is ${JSON.stringify(o.subType)} and the var's is ${JSON.stringify(vd.subType)}`);
|
|
102
|
+
if (o.schema !== undefined && vd.schema !== undefined && !sameJson(o.schema, vd.schema))
|
|
103
|
+
clash.push(`the output and the var declare different schemas`);
|
|
104
|
+
if (clash.length) {
|
|
105
|
+
problems.push({
|
|
106
|
+
code: 'OUTPUT_VAR_MISMATCH', name: o.name,
|
|
107
|
+
message: `"${o.name}" is declared by both .output() and .var(), which the SDK merges into one output `
|
|
108
|
+
+ `variable, but ${clash.join('; ')}. Make the two declarations agree, or drop the .var().`,
|
|
109
|
+
suggestion: `An output is assignable with { updates } on its own; declare its starting value with `
|
|
110
|
+
+ `.output({ ${o.name}: { type: types.${o.type}, default: … } }).`,
|
|
111
|
+
});
|
|
112
|
+
return o;
|
|
113
|
+
}
|
|
114
|
+
merged.push(o.name);
|
|
115
|
+
return {
|
|
116
|
+
...o,
|
|
117
|
+
...(o.default === undefined && vd.default !== undefined ? { default: vd.default } : {}),
|
|
118
|
+
...(o.description === undefined && vd.description !== undefined ? { description: vd.description } : {}),
|
|
119
|
+
...(o.subType === undefined && vd.subType !== undefined ? { subType: vd.subType } : {}),
|
|
120
|
+
...(o.schema === undefined && vd.schema !== undefined ? { schema: vd.schema } : {}),
|
|
121
|
+
};
|
|
122
|
+
});
|
|
123
|
+
const mergedSet = new Set(merged);
|
|
124
|
+
const vars = decl.vars.filter((vd) => !mergedSet.has(vd.name));
|
|
125
|
+
return { outputs, vars, merged, problems };
|
|
126
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { reportResult } from '../cli-result.js';
|
|
1
|
+
import { printResult, reportResult } from '../cli-result.js';
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { CURRENT_LINK, PULL_COMMAND, currentEntrySha, libraryCacheRoot, resolveCachedLibrary, resolveCachedMarkdown, } from "./cache.js";
|
|
4
4
|
/**
|
|
@@ -37,7 +37,7 @@ export async function run(argv) {
|
|
|
37
37
|
console.error(`No connector library in the cache yet. Run \`${PULL_COMMAND}\`.`);
|
|
38
38
|
return 1;
|
|
39
39
|
}
|
|
40
|
-
|
|
40
|
+
printResult(value);
|
|
41
41
|
return 0;
|
|
42
42
|
};
|
|
43
43
|
if (argv.includes("--library-json"))
|
|
@@ -56,7 +56,7 @@ export async function run(argv) {
|
|
|
56
56
|
libraryMarkdown: libraryMarkdown ?? null,
|
|
57
57
|
};
|
|
58
58
|
reportResult(all);
|
|
59
|
-
|
|
59
|
+
printResult(JSON.stringify(all, null, 2));
|
|
60
60
|
// Not an error: a caller asking for the whole picture is entitled to be told
|
|
61
61
|
// the cache is empty, rather than getting a non-zero exit for a plain report.
|
|
62
62
|
return 0;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { reportResult } from '../cli-result.js';
|
|
1
|
+
import { printResult, reportResult } from '../cli-result.js';
|
|
2
2
|
import { readFile, stat } from "node:fs/promises";
|
|
3
3
|
import { PULL_COMMAND, resolveLibraryDir } from "./cache.js";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
@@ -155,7 +155,7 @@ export async function run(argv) {
|
|
|
155
155
|
limit,
|
|
156
156
|
});
|
|
157
157
|
reportResult(result);
|
|
158
|
-
|
|
158
|
+
printResult(JSON.stringify(result, null, 2));
|
|
159
159
|
return 0;
|
|
160
160
|
}
|
|
161
161
|
catch (error) {
|