@uipath/maestro-builder-sdk 6.17.0 → 6.17.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/api-index.md +12 -12
- package/dist/api-members.md +27 -27
- package/dist/check-cli.js +2 -30
- package/dist/check.js +139 -58
- package/dist/cli-library.d.ts +18 -4
- package/dist/cli-library.js +24 -6
- package/dist/compile-cli.js +4 -33
- package/dist/core/actions.d.ts +1 -1
- package/dist/core/cli-library.d.ts +16 -7
- package/dist/core/cli-library.js +53 -19
- package/dist/core/connector-checks.d.ts +18 -2
- package/dist/core/connector-checks.js +15 -5
- package/dist/core/error-port.d.ts +25 -0
- package/dist/core/error-port.js +36 -0
- package/dist/core/expr.d.ts +11 -5
- package/dist/core/expr.js +9 -3
- package/dist/core/library.d.ts +19 -0
- package/dist/core/library.js +22 -6
- package/dist/flow-sdk.d.ts +8 -3
- package/dist/flow-sdk.js +9 -4
- package/dist/serialize.js +72 -58
- package/package.json +2 -2
|
@@ -321,18 +321,28 @@ export function checkConnectorSchema(where, spec, opts, dialect, report) {
|
|
|
321
321
|
}
|
|
322
322
|
}
|
|
323
323
|
/**
|
|
324
|
-
* The field contract and the binding names `compile` enforces,
|
|
324
|
+
* The operation, its field contract and the binding names `compile` enforces,
|
|
325
|
+
* reported here first.
|
|
325
326
|
*
|
|
326
|
-
*
|
|
327
|
+
* Each rule runs only with the facts in hand (a library for the operation and its fields, a
|
|
327
328
|
* bindings.json for the names); without them `check` stays a pure function of the
|
|
328
329
|
* built model and says nothing, as before.
|
|
329
330
|
*/
|
|
330
331
|
export function checkConnectorContract(where, spec, opts, report,
|
|
331
332
|
/** The activity's own id, for {@link connectionConnectorConflicts}; omit to record nothing. */
|
|
332
333
|
id) {
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
|
|
334
|
+
const target = { key: spec.key, action: spec.action, version: spec.version, object: spec.object };
|
|
335
|
+
// The operation itself first: an operation compile cannot resolve has no field
|
|
336
|
+
// contract to check, and `connectorInputs` answers "unknown" for it.
|
|
337
|
+
const unresolved = opts.connectorResolveError?.(target);
|
|
338
|
+
if (unresolved) {
|
|
339
|
+
report({ level: 'error', code: unresolved.code, message: `${where}: ${unresolved.message}` });
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
const inputErr = opts.connectorInputs?.(target, spec.inputs ?? {});
|
|
343
|
+
if (inputErr) {
|
|
344
|
+
report({ level: 'error', code: 'CONNECTOR_INPUT', message: `${where}: ${inputErr}` });
|
|
345
|
+
}
|
|
336
346
|
}
|
|
337
347
|
checkBindingLabels(where, spec, opts, (code, message) => report({ level: 'error', code, message }), { key: spec.key, action: spec.action }, id === undefined ? undefined : { id, connector: spec.key });
|
|
338
348
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Does a node definition have an `error` port? One answer for `check` and
|
|
3
|
+
* `serialize` (flow-builder-sdk#943).
|
|
4
|
+
*
|
|
5
|
+
* The product's rule, read from @uipath/flow-core and @uipath/flow-tool:
|
|
6
|
+
*
|
|
7
|
+
* - the manifest client injects an `error` source handle into every served
|
|
8
|
+
* definition with `supportsErrorHandling: true` that does not declare one
|
|
9
|
+
* (`withErrorHandles` / `nodeSupportsErrorHandling`);
|
|
10
|
+
* - `uip maestro flow validate` does the same when it checks edge handles
|
|
11
|
+
* (`validateEdgeHandlesAgainstCurrentManifests`: `supportsErrorHandling ===
|
|
12
|
+
* true` adds `error` to the node's source handles).
|
|
13
|
+
*
|
|
14
|
+
* So the flag alone grants the port. For instance, the manifest
|
|
15
|
+
* `registry get --local` returns for a sibling Flow or RPA project
|
|
16
|
+
* (`buildNodeManifest`) has `supportsErrorHandling: true` and only `input` +
|
|
17
|
+
* `output` handles, and an `error` edge from it is valid. A definition that
|
|
18
|
+
* declares an `error` source handle without the flag counts too.
|
|
19
|
+
*/
|
|
20
|
+
export declare function definitionHasErrorPort(def: any): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* The ONERROR_NO_ERROR_PORT sentence for a definition-backed node, naming its
|
|
23
|
+
* node type. Shared with serialize, which throws it for the same node.
|
|
24
|
+
*/
|
|
25
|
+
export declare function errorPortRefusal(stepName: string, def: any): string;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Does a node definition have an `error` port? One answer for `check` and
|
|
3
|
+
* `serialize` (flow-builder-sdk#943).
|
|
4
|
+
*
|
|
5
|
+
* The product's rule, read from @uipath/flow-core and @uipath/flow-tool:
|
|
6
|
+
*
|
|
7
|
+
* - the manifest client injects an `error` source handle into every served
|
|
8
|
+
* definition with `supportsErrorHandling: true` that does not declare one
|
|
9
|
+
* (`withErrorHandles` / `nodeSupportsErrorHandling`);
|
|
10
|
+
* - `uip maestro flow validate` does the same when it checks edge handles
|
|
11
|
+
* (`validateEdgeHandlesAgainstCurrentManifests`: `supportsErrorHandling ===
|
|
12
|
+
* true` adds `error` to the node's source handles).
|
|
13
|
+
*
|
|
14
|
+
* So the flag alone grants the port. For instance, the manifest
|
|
15
|
+
* `registry get --local` returns for a sibling Flow or RPA project
|
|
16
|
+
* (`buildNodeManifest`) has `supportsErrorHandling: true` and only `input` +
|
|
17
|
+
* `output` handles, and an `error` edge from it is valid. A definition that
|
|
18
|
+
* declares an `error` source handle without the flag counts too.
|
|
19
|
+
*/
|
|
20
|
+
export function definitionHasErrorPort(def) {
|
|
21
|
+
if (def?.supportsErrorHandling === true)
|
|
22
|
+
return true;
|
|
23
|
+
const groups = Array.isArray(def?.handleConfiguration) ? def.handleConfiguration : [];
|
|
24
|
+
return groups.some((g) => (Array.isArray(g?.handles) ? g.handles : [])
|
|
25
|
+
.some((h) => h?.id === 'error' && h?.type === 'source'));
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The ONERROR_NO_ERROR_PORT sentence for a definition-backed node, naming its
|
|
29
|
+
* node type. Shared with serialize, which throws it for the same node.
|
|
30
|
+
*/
|
|
31
|
+
export function errorPortRefusal(stepName, def) {
|
|
32
|
+
return `Step "${stepName}" is a ${def?.nodeType}@${def?.version} node, and its definition has no error port: `
|
|
33
|
+
+ `it does not set supportsErrorHandling: true and declares no "error" source handle, so the edge would leave a `
|
|
34
|
+
+ `handle the node does not have. Handle the failure downstream instead, or pass the definition exactly as `
|
|
35
|
+
+ `\`uip maestro flow registry get\` returns it.`;
|
|
36
|
+
}
|
package/dist/core/expr.d.ts
CHANGED
|
@@ -145,8 +145,8 @@ export declare function ran(step: string): Expr;
|
|
|
145
145
|
* The first five are what every error-capable node's definition declares
|
|
146
146
|
* (`outputDefinition.error.schema`, `required`, `additionalProperties: false`).
|
|
147
147
|
* `response` and `element` are NOT declared and are present at run time anyway —
|
|
148
|
-
* measured on every family probed (2026-09-10), `element` carrying the
|
|
149
|
-
* step that failed. They are typed here because a field that exists should be
|
|
148
|
+
* measured on every family probed (2026-09-10), `element` carrying the label of
|
|
149
|
+
* the step that failed. They are typed here because a field that exists should be
|
|
150
150
|
* readable; the declared five stay first because those are the contract.
|
|
151
151
|
*/
|
|
152
152
|
export type ErrorEnvelopeField = 'code' | 'message' | 'detail' | 'category' | 'status' | 'response' | 'element';
|
|
@@ -166,15 +166,21 @@ export type ErrorEnvelopeField = 'code' | 'message' | 'detail' | 'category' | 's
|
|
|
166
166
|
* | `category` | string | `'User'` |
|
|
167
167
|
* | `status` | number | `404` |
|
|
168
168
|
* | `response` | unknown | undeclared; `null` in every probe |
|
|
169
|
-
* | `element` | string | undeclared; the
|
|
169
|
+
* | `element` | string | undeclared; the failing step's label |
|
|
170
170
|
*
|
|
171
171
|
* The last two resolve at run time — measured, a container handler reading
|
|
172
|
-
* `element` came back with the failing body step's
|
|
173
|
-
* the declared schema, so `uip maestro flow validate` reports
|
|
172
|
+
* `element` came back with the failing body step's label — but they are absent
|
|
173
|
+
* from the declared schema, so `uip maestro flow validate` reports
|
|
174
174
|
* `EXPRESSION_DIAGNOSTIC: Property 'element' does not exist on type
|
|
175
175
|
* '<Step>_Error'` for them. Valid, and noisy: prefer the declared five unless you
|
|
176
176
|
* specifically need one of these.
|
|
177
177
|
*
|
|
178
|
+
* **`element` is the step's canvas label (`display.label`), not its node id.** The
|
|
179
|
+
* label defaults to the step name, so it matches the name (not always the node
|
|
180
|
+
* id) only while no `label` is set: `.step('failA', …, { label: 'Fail A' })`
|
|
181
|
+
* failing gives `element: 'Fail A'`. Route on `code` / `message`, never on
|
|
182
|
+
* `element`.
|
|
183
|
+
*
|
|
178
184
|
* **Which VARIABLE holds it is per node family, and this helper owns that.**
|
|
179
185
|
* Measured on the product (2026-09-10), one debug run per family:
|
|
180
186
|
*
|
package/dist/core/expr.js
CHANGED
|
@@ -179,15 +179,21 @@ export function ran(step) {
|
|
|
179
179
|
* | `category` | string | `'User'` |
|
|
180
180
|
* | `status` | number | `404` |
|
|
181
181
|
* | `response` | unknown | undeclared; `null` in every probe |
|
|
182
|
-
* | `element` | string | undeclared; the
|
|
182
|
+
* | `element` | string | undeclared; the failing step's label |
|
|
183
183
|
*
|
|
184
184
|
* The last two resolve at run time — measured, a container handler reading
|
|
185
|
-
* `element` came back with the failing body step's
|
|
186
|
-
* the declared schema, so `uip maestro flow validate` reports
|
|
185
|
+
* `element` came back with the failing body step's label — but they are absent
|
|
186
|
+
* from the declared schema, so `uip maestro flow validate` reports
|
|
187
187
|
* `EXPRESSION_DIAGNOSTIC: Property 'element' does not exist on type
|
|
188
188
|
* '<Step>_Error'` for them. Valid, and noisy: prefer the declared five unless you
|
|
189
189
|
* specifically need one of these.
|
|
190
190
|
*
|
|
191
|
+
* **`element` is the step's canvas label (`display.label`), not its node id.** The
|
|
192
|
+
* label defaults to the step name, so it matches the name (not always the node
|
|
193
|
+
* id) only while no `label` is set: `.step('failA', …, { label: 'Fail A' })`
|
|
194
|
+
* failing gives `element: 'Fail A'`. Route on `code` / `message`, never on
|
|
195
|
+
* `element`.
|
|
196
|
+
*
|
|
191
197
|
* **Which VARIABLE holds it is per node family, and this helper owns that.**
|
|
192
198
|
* Measured on the product (2026-09-10), one debug run per family:
|
|
193
199
|
*
|
package/dist/core/library.d.ts
CHANGED
|
@@ -218,6 +218,25 @@ export interface LibraryOpts {
|
|
|
218
218
|
*/
|
|
219
219
|
overlayDir?: string;
|
|
220
220
|
}
|
|
221
|
+
/** Why {@link Library.resolve} refused a connector action. */
|
|
222
|
+
export type ConnectorResolveCode =
|
|
223
|
+
/** The library carries no such operation (or no such connector, or not at that version). */
|
|
224
|
+
'CONNECTOR_NOT_IN_LIBRARY'
|
|
225
|
+
/** A GENERIC operation (one entry per object) named without an `object`. */
|
|
226
|
+
| 'CONNECTOR_OBJECT_REQUIRED'
|
|
227
|
+
/** An `object` on a single, non-generic operation. */
|
|
228
|
+
| 'CONNECTOR_OBJECT_NOT_GENERIC';
|
|
229
|
+
/**
|
|
230
|
+
* The refusal {@link Library.resolve} throws, with a `code` for each reason.
|
|
231
|
+
*
|
|
232
|
+
* Typed so a caller can tell a refusal from a bug without matching on message
|
|
233
|
+
* text: `check` reports it under this code with this message, and a compile
|
|
234
|
+
* that throws it carries the same code into its `--json` diagnostics (#948).
|
|
235
|
+
*/
|
|
236
|
+
export declare class ConnectorResolveError extends Error {
|
|
237
|
+
readonly code: ConnectorResolveCode;
|
|
238
|
+
constructor(code: ConnectorResolveCode, message: string);
|
|
239
|
+
}
|
|
221
240
|
export declare class Library {
|
|
222
241
|
private layers;
|
|
223
242
|
/** The LIST-ENVELOPE LEDGER, overlay first so a prepared entry can add a row. */
|
package/dist/core/library.js
CHANGED
|
@@ -9,10 +9,26 @@
|
|
|
9
9
|
import { readFileSync, existsSync } from 'node:fs';
|
|
10
10
|
import { join } from 'node:path';
|
|
11
11
|
import { prepareCommand } from './cli-spelling.js';
|
|
12
|
+
import { PULL_COMMAND } from './library-cache.js';
|
|
12
13
|
import { editDistance } from './edit-distance.js';
|
|
13
14
|
import { loadListEnvelopes, lookupListEnvelope } from './list-envelope.js';
|
|
14
15
|
import { lookupSpecOf } from './lookups.js';
|
|
15
16
|
import { loadByoaLedger, loadInvariantCollections, loadLookupResolutions } from './lookup-store.js';
|
|
17
|
+
/**
|
|
18
|
+
* The refusal {@link Library.resolve} throws, with a `code` for each reason.
|
|
19
|
+
*
|
|
20
|
+
* Typed so a caller can tell a refusal from a bug without matching on message
|
|
21
|
+
* text: `check` reports it under this code with this message, and a compile
|
|
22
|
+
* that throws it carries the same code into its `--json` diagnostics (#948).
|
|
23
|
+
*/
|
|
24
|
+
export class ConnectorResolveError extends Error {
|
|
25
|
+
code;
|
|
26
|
+
constructor(code, message) {
|
|
27
|
+
super(message);
|
|
28
|
+
this.name = 'ConnectorResolveError';
|
|
29
|
+
this.code = code;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
16
32
|
export class Library {
|
|
17
33
|
layers = [];
|
|
18
34
|
/** The LIST-ENVELOPE LEDGER, overlay first so a prepared entry can add a row. */
|
|
@@ -582,7 +598,7 @@ export class Library {
|
|
|
582
598
|
+ `the library index (\`index.json\`), and remember the two namespaces: `
|
|
583
599
|
+ `\`…connector.trigger.…\` starts a flow, \`…connector.event.…\` waits mid-flow.`
|
|
584
600
|
: '';
|
|
585
|
-
throw new
|
|
601
|
+
throw new ConnectorResolveError('CONNECTOR_NOT_IN_LIBRARY', `connector not in library: ${nodeType}${version ? '@' + version : ''}.${hint}`
|
|
586
602
|
+ this.describeMiss(nodeType, version, objectName));
|
|
587
603
|
}
|
|
588
604
|
/**
|
|
@@ -622,7 +638,7 @@ export class Library {
|
|
|
622
638
|
+ where
|
|
623
639
|
+ ' Note the operation slug is the nodeType segment, NOT the tenant\'s `objectName`'
|
|
624
640
|
+ ' — `uip maestro case spec` reports the latter, and the two can differ.'
|
|
625
|
-
+
|
|
641
|
+
+ ` If the operation is newer than the library, refresh it with \`${PULL_COMMAND}\`.`;
|
|
626
642
|
}
|
|
627
643
|
/** Every direct operation id under `prefix` (no further dot), across layers, sorted and de-duplicated. */
|
|
628
644
|
opsUnder(prefix) {
|
|
@@ -646,7 +662,7 @@ export class Library {
|
|
|
646
662
|
return ` No connector "${key}" in the library (${keys.length} indexed).`
|
|
647
663
|
+ (near && near !== key ? ` Closest: ${near}.` : '')
|
|
648
664
|
+ where
|
|
649
|
-
+
|
|
665
|
+
+ ` If the connector is newer than the library, refresh it with \`${PULL_COMMAND}\`.`;
|
|
650
666
|
}
|
|
651
667
|
/**
|
|
652
668
|
* The event half of {@link describeMiss}: which of the connector's EVENTS the
|
|
@@ -666,7 +682,7 @@ export class Library {
|
|
|
666
682
|
const waits = this.opsUnder(`uipath.connector.event.${key}.`);
|
|
667
683
|
// A start trigger resolves from either form; a mid-flow wait needs the `.event.` form itself.
|
|
668
684
|
const usable = kind === 'trigger' ? [...new Set([...starts, ...waits])].sort() : waits;
|
|
669
|
-
const refresh =
|
|
685
|
+
const refresh = ` If the event is newer than the library, refresh it with \`${PULL_COMMAND}\`.`;
|
|
670
686
|
if (usable.length) {
|
|
671
687
|
if (usable.includes(asked)) {
|
|
672
688
|
// Only a version can make a carried event miss here: every other refusal
|
|
@@ -755,7 +771,7 @@ export class Library {
|
|
|
755
771
|
+ `dispatch. If it is a typo, compile will not catch it.\n`);
|
|
756
772
|
return this.build(layer.dir, { ...only, objectName }, nodeType, { objectNameUnverified: true });
|
|
757
773
|
}
|
|
758
|
-
throw new
|
|
774
|
+
throw new ConnectorResolveError('CONNECTOR_OBJECT_NOT_GENERIC', `connector ${nodeType} does not have an object "${objectName}": it is a single, `
|
|
759
775
|
+ `non-generic operation addressing ${only.objectName ? `"${only.objectName}"` : 'no object'}. `
|
|
760
776
|
+ `Drop the \`object\` option (it is only for generic operations, whose node type covers many objects).`);
|
|
761
777
|
}
|
|
@@ -769,7 +785,7 @@ export class Library {
|
|
|
769
785
|
.filter((o) => typeof o === 'string' && o.length > 0)
|
|
770
786
|
.sort();
|
|
771
787
|
if (objectName === undefined) {
|
|
772
|
-
throw new
|
|
788
|
+
throw new ConnectorResolveError('CONNECTOR_OBJECT_REQUIRED', `connector ${nodeType} is a GENERIC operation covering ${matches.length} objects — `
|
|
773
789
|
+ `say which one with the \`object\` option, e.g. `
|
|
774
790
|
+ `connector('${nodeType.split('.').slice(2, 3)[0]}', '${nodeType.split('.').slice(3).join('.')}', `
|
|
775
791
|
+ `{ … }, { object: '${objects[0] ?? '<object>'}' }). ${describeObjects(objects)}`);
|
package/dist/flow-sdk.d.ts
CHANGED
|
@@ -824,7 +824,7 @@ declare class StepList {
|
|
|
824
824
|
* even a legal call: `out(step, path?)` takes two arguments.
|
|
825
825
|
*
|
|
826
826
|
* **Three ways the handler can end**: `.return(...)` answers the flow,
|
|
827
|
-
* `.terminate(...)` stops the run, and `h.
|
|
827
|
+
* `.terminate(...)` stops the run, and `h.stepToRef('<step>')` compensates and
|
|
828
828
|
* hands control back to a step the success path also reaches — the shape most
|
|
829
829
|
* deployed flows use. A handler that just runs out of steps still gets its own
|
|
830
830
|
* End: rejoining is something the author SAYS, never something wiring infers.
|
|
@@ -838,8 +838,9 @@ declare class StepList {
|
|
|
838
838
|
*
|
|
839
839
|
* **A `.loop()` container takes one too.** `core.logic.loop` declares the
|
|
840
840
|
* handle, and a body step's failure routes to it: the container's envelope
|
|
841
|
-
* carries the body's message in `detail` and the failing body step's
|
|
842
|
-
* `
|
|
841
|
+
* carries the body's message in `detail` and the failing body step's label
|
|
842
|
+
* (`display.label`, which defaults to the step name) in `element`, and the
|
|
843
|
+
* instance completes rather than faulting. `.doWhile()` does
|
|
843
844
|
* NOT — it declares an error variable and no error handle — so that one is
|
|
844
845
|
* refused by name, and its failures belong to the body step that can fail.
|
|
845
846
|
*
|
|
@@ -880,6 +881,10 @@ declare class StepList {
|
|
|
880
881
|
* The body is its own path. It ends by answering (`.return`), stopping the run
|
|
881
882
|
* (`.terminate`), or handing control back with `.stepToRef(...)`; a body that
|
|
882
883
|
* simply runs out of steps gets its own End, exactly as an error handler does.
|
|
884
|
+
* It does not rejoin the main path. That End maps each flow output from its
|
|
885
|
+
* variable, as the End a flow falls off the bottom into does, so set the
|
|
886
|
+
* outputs on the way with `{ updates }` or end the body with `.return({…})`;
|
|
887
|
+
* `check` warns NO_RETURN when an output is left unset on that path.
|
|
883
888
|
*
|
|
884
889
|
* The main path is untouched — an edge out of a NON-default port is a side exit,
|
|
885
890
|
* so whatever you chain after this call still belongs to the step's normal
|
package/dist/flow-sdk.js
CHANGED
|
@@ -402,7 +402,7 @@ class StepList {
|
|
|
402
402
|
* even a legal call: `out(step, path?)` takes two arguments.
|
|
403
403
|
*
|
|
404
404
|
* **Three ways the handler can end**: `.return(...)` answers the flow,
|
|
405
|
-
* `.terminate(...)` stops the run, and `h.
|
|
405
|
+
* `.terminate(...)` stops the run, and `h.stepToRef('<step>')` compensates and
|
|
406
406
|
* hands control back to a step the success path also reaches — the shape most
|
|
407
407
|
* deployed flows use. A handler that just runs out of steps still gets its own
|
|
408
408
|
* End: rejoining is something the author SAYS, never something wiring infers.
|
|
@@ -416,8 +416,9 @@ class StepList {
|
|
|
416
416
|
*
|
|
417
417
|
* **A `.loop()` container takes one too.** `core.logic.loop` declares the
|
|
418
418
|
* handle, and a body step's failure routes to it: the container's envelope
|
|
419
|
-
* carries the body's message in `detail` and the failing body step's
|
|
420
|
-
* `
|
|
419
|
+
* carries the body's message in `detail` and the failing body step's label
|
|
420
|
+
* (`display.label`, which defaults to the step name) in `element`, and the
|
|
421
|
+
* instance completes rather than faulting. `.doWhile()` does
|
|
421
422
|
* NOT — it declares an error variable and no error handle — so that one is
|
|
422
423
|
* refused by name, and its failures belong to the body step that can fail.
|
|
423
424
|
*
|
|
@@ -464,7 +465,7 @@ class StepList {
|
|
|
464
465
|
// `@2.4`, gated on `errorHandlingEnabled` like every other error handle), and
|
|
465
466
|
// the platform routes it: a body step throwing inside a measured run left the
|
|
466
467
|
// instance Completed with the container's envelope published — `detail` the
|
|
467
|
-
// body's own message, `element` the
|
|
468
|
+
// body's own message, `element` the label of the body step that failed.
|
|
468
469
|
//
|
|
469
470
|
// `.doWhile()` is NOT: `core.logic.dowhile@1.0` declares an error VARIABLE
|
|
470
471
|
// and no error HANDLE, so an edge would leave a handle the node does not
|
|
@@ -494,6 +495,10 @@ class StepList {
|
|
|
494
495
|
* The body is its own path. It ends by answering (`.return`), stopping the run
|
|
495
496
|
* (`.terminate`), or handing control back with `.stepToRef(...)`; a body that
|
|
496
497
|
* simply runs out of steps gets its own End, exactly as an error handler does.
|
|
498
|
+
* It does not rejoin the main path. That End maps each flow output from its
|
|
499
|
+
* variable, as the End a flow falls off the bottom into does, so set the
|
|
500
|
+
* outputs on the way with `{ updates }` or end the body with `.return({…})`;
|
|
501
|
+
* `check` warns NO_RETURN when an output is left unset on that path.
|
|
497
502
|
*
|
|
498
503
|
* The main path is untouched — an edge out of a NON-default port is a side exit,
|
|
499
504
|
* so whatever you chain after this call still belongs to the step's normal
|
package/dist/serialize.js
CHANGED
|
@@ -15,6 +15,7 @@ import { FlowAction, FlowTrigger } from './core/node-classes.js';
|
|
|
15
15
|
import { connectorRawNodeRefusal } from './core/connector-raw-node.js';
|
|
16
16
|
import { dataFabricUnknownInputs } from './core/typed-raw-node.js';
|
|
17
17
|
import { planContributionBindings } from './core/contribution-bindings.js';
|
|
18
|
+
import { definitionHasErrorPort, errorPortRefusal } from './core/error-port.js';
|
|
18
19
|
import { isAnswerField } from './core/hitl-answers.js';
|
|
19
20
|
import { connectionConnectors } from './bindings-v2.js';
|
|
20
21
|
import { BindingRefusal, GUID_RE, bindingMissingMessage, bindingSelfNameMessage, bindingUndeclaredMessage, missingBindingLabels, undeclaredBinding } from './core/binding-messages.js';
|
|
@@ -4375,9 +4376,9 @@ function newScope(outputs, vars = []) {
|
|
|
4375
4376
|
* note at `uipath.ts:118-123`, MST-9544).
|
|
4376
4377
|
*/
|
|
4377
4378
|
/**
|
|
4378
|
-
* Wire every parked `.
|
|
4379
|
+
* Wire every parked `.stepToRef('<step>')` edge, now that the whole scope exists.
|
|
4379
4380
|
*
|
|
4380
|
-
* An unresolved target should be impossible — `check`'s
|
|
4381
|
+
* An unresolved target should be impossible — `check`'s REF_UNKNOWN_TARGET
|
|
4381
4382
|
* runs first and names the steps that do exist — but a serializer that silently
|
|
4382
4383
|
* dropped the edge would emit a flow whose handler goes nowhere, which is the
|
|
4383
4384
|
* exact class of failure `.onError` used to have (an error path that vanishes in
|
|
@@ -4577,10 +4578,11 @@ function isTypeDesc(type) {
|
|
|
4577
4578
|
*
|
|
4578
4579
|
* Emitting the full set is not cosmetic. The product's output-mapping rule warns
|
|
4579
4580
|
* `MISSING_OUTPUT_MAPPING` once per (End node × unmapped output variable), and
|
|
4580
|
-
*
|
|
4581
|
-
*
|
|
4582
|
-
* `.return(…)`
|
|
4583
|
-
*
|
|
4581
|
+
* an End with no map answers the run with nothing. So every End site goes
|
|
4582
|
+
* through `emitEnd`, which calls this: the implicit End a flow falls off the
|
|
4583
|
+
* bottom into, a `.return(…)`'s End, the End a `.stepToList` / `.onError` body
|
|
4584
|
+
* gets when it runs out of steps, and the End for a `.stepSwitch` exit with no
|
|
4585
|
+
* arm (or a human task's `action: 'End'` outcome).
|
|
4584
4586
|
*/
|
|
4585
4587
|
function endOutputs(scope, explicit = {}) {
|
|
4586
4588
|
const outs = {};
|
|
@@ -4636,8 +4638,8 @@ function renameRefs(text, rename) {
|
|
|
4636
4638
|
/** Every step name a flow declares in its own scope (not its children's). */
|
|
4637
4639
|
function stepNamesOf(steps, out = []) {
|
|
4638
4640
|
for (const s of steps) {
|
|
4639
|
-
// `return`
|
|
4640
|
-
//
|
|
4641
|
+
// `return` carries a synthetic `name` but creates no node named after it (an
|
|
4642
|
+
// End id is claimed as `end`); `stepToRef` has no `name` at all (it is one edge).
|
|
4641
4643
|
if ('name' in s && s.kind !== 'return')
|
|
4642
4644
|
out.push(s.name);
|
|
4643
4645
|
if (s.kind === 'branch') {
|
|
@@ -4679,6 +4681,18 @@ export function serialize(built, opts = {}) {
|
|
|
4679
4681
|
* A bindings entry the node does not reference is not a bound resource.
|
|
4680
4682
|
*/
|
|
4681
4683
|
const resourceBindings = new Map();
|
|
4684
|
+
/**
|
|
4685
|
+
* The resolved definition of every contribution-lowered node, by node id, so an
|
|
4686
|
+
* `error` edge from one can be checked against its definition (#943). Typed
|
|
4687
|
+
* families are not recorded: their definitions are the SDK's own.
|
|
4688
|
+
*/
|
|
4689
|
+
const contributionDefs = new Map();
|
|
4690
|
+
/** Refuse an `error` edge from a contribution-lowered node whose definition has no error port. */
|
|
4691
|
+
function refuseErrorEdgeWithoutPort(nodeId, stepName) {
|
|
4692
|
+
const def = contributionDefs.get(nodeId);
|
|
4693
|
+
if (def !== undefined && !definitionHasErrorPort(def))
|
|
4694
|
+
throw new Error(errorPortRefusal(stepName, def));
|
|
4695
|
+
}
|
|
4682
4696
|
/**
|
|
4683
4697
|
* Hoist a contribution's own bindings, then close every `<bindings.NAME>` its
|
|
4684
4698
|
* definition references and it did not supply, from the definition's own
|
|
@@ -4785,7 +4799,7 @@ export function serialize(built, opts = {}) {
|
|
|
4785
4799
|
/**
|
|
4786
4800
|
* `uid` for a node that IS an author's step, recording which id that step got.
|
|
4787
4801
|
*
|
|
4788
|
-
* `.
|
|
4802
|
+
* `.stepToRef('<step>')` names a step, and the id it was emitted under is not
|
|
4789
4803
|
* derivable from the name: `uid` sanitizes, de-duplicates against every id in
|
|
4790
4804
|
* the file, and hands a subflow COPY a prefixed one from the rename map. So
|
|
4791
4805
|
* the mapping is recorded where it is decided rather than reconstructed later
|
|
@@ -4868,6 +4882,32 @@ export function serialize(built, opts = {}) {
|
|
|
4868
4882
|
node.parentId = o.parentId;
|
|
4869
4883
|
return node;
|
|
4870
4884
|
}
|
|
4885
|
+
/**
|
|
4886
|
+
* Emit an End node, map the flow's outputs on it, and wire `tails` into it.
|
|
4887
|
+
*
|
|
4888
|
+
* Every End goes through here: the `.return()` End, the End a root or
|
|
4889
|
+
* subflow path falls off into, a `.stepToList` / `.onError` body that runs
|
|
4890
|
+
* out of steps, a `.stepSwitch` exit with no arm, and a fan-out human task's
|
|
4891
|
+
* `action: 'End'` outcome. An End finishes the run, and the run's outputs are
|
|
4892
|
+
* whatever its `outputs` map says, so an End built without that map answers
|
|
4893
|
+
* with nothing at all, even on a path that set every output variable
|
|
4894
|
+
* (flow-builder-sdk#742). Building the node and its mappings in one place
|
|
4895
|
+
* makes that impossible to forget.
|
|
4896
|
+
*
|
|
4897
|
+
* `explicit` holds the sources a `.return({…})` named; every other output
|
|
4898
|
+
* maps to its own variable (`endOutputs`).
|
|
4899
|
+
*/
|
|
4900
|
+
function emitEnd(scope, id, tails, o = {}) {
|
|
4901
|
+
const node = makeNode(NODE_TYPE.end, id, o.label ?? 'End', { parentId: o.parentId, requestedVersion: o.requestedVersion });
|
|
4902
|
+
node.inputs = {};
|
|
4903
|
+
const outs = endOutputs(scope, o.explicit);
|
|
4904
|
+
if (Object.keys(outs).length)
|
|
4905
|
+
node.outputs = outs;
|
|
4906
|
+
scope.nodes.push(node);
|
|
4907
|
+
for (const t of tails)
|
|
4908
|
+
addEdge(scope, t, id);
|
|
4909
|
+
return node;
|
|
4910
|
+
}
|
|
4871
4911
|
/**
|
|
4872
4912
|
* Emit a child flow into its own scope and register it under the calling
|
|
4873
4913
|
* node's id — that key is how the format names a subflow body, and how
|
|
@@ -4908,19 +4948,10 @@ export function serialize(built, opts = {}) {
|
|
|
4908
4948
|
start.inputs = {};
|
|
4909
4949
|
scope.nodes.push(start);
|
|
4910
4950
|
const tails = emit(child.steps, [{ node: startId, port: 'output' }], scope);
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
end
|
|
4915
|
-
// Same as the root's implicit End — mapped against the SUBFLOW's own
|
|
4916
|
-
// outputs, which is the scope the product validates a subflow body in.
|
|
4917
|
-
const outs = endOutputs(scope);
|
|
4918
|
-
if (Object.keys(outs).length)
|
|
4919
|
-
end.outputs = outs;
|
|
4920
|
-
scope.nodes.push(end);
|
|
4921
|
-
for (const t of tails)
|
|
4922
|
-
addEdge(scope, t, endId);
|
|
4923
|
-
}
|
|
4951
|
+
// Same as the root's implicit End — mapped against the SUBFLOW's own
|
|
4952
|
+
// outputs, which is the scope the product validates a subflow body in.
|
|
4953
|
+
if (tails.length)
|
|
4954
|
+
emitEnd(scope, uid(scope, 'end'), tails);
|
|
4924
4955
|
const globals = [];
|
|
4925
4956
|
for (const inp of child.inputs)
|
|
4926
4957
|
globals.push({ id: inp.name, name: inp.name, direction: 'in', type: inp.type, triggerNodeId: startId, ...varMeta(inp) });
|
|
@@ -5120,6 +5151,7 @@ export function serialize(built, opts = {}) {
|
|
|
5120
5151
|
node.outputs = { ...(node.outputs ?? {}), ...subNodeId(clone(contribution.outputs), id) };
|
|
5121
5152
|
}
|
|
5122
5153
|
hoistContributionBindings(def, contribution.bindings, where);
|
|
5154
|
+
contributionDefs.set(id, def);
|
|
5123
5155
|
scope.nodes.push(node);
|
|
5124
5156
|
for (const t of tails)
|
|
5125
5157
|
addEdge(scope, t, id);
|
|
@@ -6457,7 +6489,7 @@ export function serialize(built, opts = {}) {
|
|
|
6457
6489
|
* The handler is its OWN path, and it still is: its leftover tails get their
|
|
6458
6490
|
* own End rather than rejoining the chain, because `.onError` must not
|
|
6459
6491
|
* SILENTLY change what runs after the step. What changed is that an author can
|
|
6460
|
-
* now ASK — `h.
|
|
6492
|
+
* now ASK — `h.stepToRef('<step>')` emits an edge into a step the success path
|
|
6461
6493
|
* also reaches, and returns no tail, so nothing is capped. Never inferred:
|
|
6462
6494
|
* a handler that just runs out of steps ends exactly as it always did.
|
|
6463
6495
|
*
|
|
@@ -6477,11 +6509,7 @@ export function serialize(built, opts = {}) {
|
|
|
6477
6509
|
* outcome.
|
|
6478
6510
|
*/
|
|
6479
6511
|
function endExit(scope, nodeId, port, stepName, parentId) {
|
|
6480
|
-
|
|
6481
|
-
const end = makeNode(NODE_TYPE.end, endId, 'End', parentId ? { parentId } : {});
|
|
6482
|
-
end.inputs = {};
|
|
6483
|
-
scope.nodes.push(end);
|
|
6484
|
-
addEdge(scope, { node: nodeId, port }, endId);
|
|
6512
|
+
emitEnd(scope, uid(scope, `${stepName}${port.charAt(0).toUpperCase()}${port.slice(1)}End`), [{ node: nodeId, port }], { parentId });
|
|
6485
6513
|
}
|
|
6486
6514
|
/**
|
|
6487
6515
|
* Refuse a side exit on a human task's port that the step's tails already
|
|
@@ -6522,16 +6550,17 @@ export function serialize(built, opts = {}) {
|
|
|
6522
6550
|
throw new Error(`.stepToList('${step.port}', …) after step "${source}": emitted node "${nodeId}" not found in scope`);
|
|
6523
6551
|
// Only the error port carries this: without it the platform routes but Studio
|
|
6524
6552
|
// Web HIDES the handle, so no human can see the handler.
|
|
6525
|
-
if (step.port === 'error')
|
|
6553
|
+
if (step.port === 'error') {
|
|
6554
|
+
refuseErrorEdgeWithoutPort(nodeId, source);
|
|
6526
6555
|
node.inputs = { ...(node.inputs ?? {}), errorHandlingEnabled: true };
|
|
6556
|
+
}
|
|
6527
6557
|
const handlerTails = emit(handler, [{ node: nodeId, port: step.port }], scope, parentId, breakTarget);
|
|
6558
|
+
// A body that runs out of steps ends the run on an End of its own (no
|
|
6559
|
+
// merge-back is inferred). That End maps the outputs like any other, so a
|
|
6560
|
+
// body that set them with `{ updates }` still answers (#742).
|
|
6528
6561
|
if (handlerTails.length) {
|
|
6529
6562
|
const endId = uid(scope, `${source}${step.port === 'error' ? 'Error' : step.port.charAt(0).toUpperCase() + step.port.slice(1)}End`);
|
|
6530
|
-
|
|
6531
|
-
end.inputs = {};
|
|
6532
|
-
scope.nodes.push(end);
|
|
6533
|
-
for (const t of handlerTails)
|
|
6534
|
-
addEdge(scope, t, endId);
|
|
6563
|
+
emitEnd(scope, endId, handlerTails, { parentId });
|
|
6535
6564
|
}
|
|
6536
6565
|
return tails;
|
|
6537
6566
|
}
|
|
@@ -6653,8 +6682,8 @@ export function serialize(built, opts = {}) {
|
|
|
6653
6682
|
routed.add(exit.port);
|
|
6654
6683
|
// Arms CONVERGE, exactly as `.switch()`'s do: every arm that does not
|
|
6655
6684
|
// end in `.return()` contributes its tail, so the next step fans in
|
|
6656
|
-
// from all of them. (`.stepToList` instead gives
|
|
6657
|
-
// End
|
|
6685
|
+
// from all of them. (`.stepToList` instead gives an arm that runs out
|
|
6686
|
+
// of steps an End of its own; `emitEnd` maps the outputs on it,
|
|
6658
6687
|
// flow-builder-sdk#742.)
|
|
6659
6688
|
const caseTails = emit(c.body, [{ node: nodeId, port: exit.port }], scope, parentId, breakTarget);
|
|
6660
6689
|
if (caseTails.length === 0 && stopsAtEnd(c.body))
|
|
@@ -6924,6 +6953,7 @@ export function serialize(built, opts = {}) {
|
|
|
6924
6953
|
throw new Error(`.stepToRef('${step.port}', '${step.target}') has no step to leave from.`);
|
|
6925
6954
|
refuseRewire('.stepToRef', step.port, lastAction ?? '?', tails);
|
|
6926
6955
|
if (step.port === 'error') {
|
|
6956
|
+
refuseErrorEdgeWithoutPort(from.node, lastAction ?? from.node);
|
|
6927
6957
|
const node = scope.nodes.find((n) => n.id === from.node);
|
|
6928
6958
|
if (node)
|
|
6929
6959
|
node.inputs = { ...(node.inputs ?? {}), errorHandlingEnabled: true };
|
|
@@ -6936,8 +6966,6 @@ export function serialize(built, opts = {}) {
|
|
|
6936
6966
|
// source must be able to ask for the id the original file had, or the
|
|
6937
6967
|
// round trip cannot reproduce its own layout block (ReturnOptions.name).
|
|
6938
6968
|
const id = uid(scope, step.options?.name ?? 'end');
|
|
6939
|
-
const node = makeNode(NODE_TYPE.end, id, step.options?.label ?? 'End', { parentId, requestedVersion: step.options?.version });
|
|
6940
|
-
node.inputs = {};
|
|
6941
6969
|
const explicit = {};
|
|
6942
6970
|
const updates = [];
|
|
6943
6971
|
for (const [name, expr] of Object.entries(step.values)) {
|
|
@@ -6948,12 +6976,7 @@ export function serialize(built, opts = {}) {
|
|
|
6948
6976
|
// variable; a variableUpdate for one would be a no-op assignment.
|
|
6949
6977
|
updates.push({ variableId: name, expression: src });
|
|
6950
6978
|
}
|
|
6951
|
-
|
|
6952
|
-
if (Object.keys(outs).length)
|
|
6953
|
-
node.outputs = outs;
|
|
6954
|
-
scope.nodes.push(node);
|
|
6955
|
-
for (const t of tails)
|
|
6956
|
-
addEdge(scope, t, id);
|
|
6979
|
+
emitEnd(scope, id, tails, { label: step.options?.label, parentId, requestedVersion: step.options?.version, explicit });
|
|
6957
6980
|
const extra = step.options?.updates ?? {};
|
|
6958
6981
|
for (const name of Object.keys(extra)) {
|
|
6959
6982
|
if (name in step.values) {
|
|
@@ -6969,20 +6992,11 @@ export function serialize(built, opts = {}) {
|
|
|
6969
6992
|
return tails;
|
|
6970
6993
|
}
|
|
6971
6994
|
const finalTails = emit(built.steps, [{ node: built.triggerId ?? 'start', port: 'output' }], root);
|
|
6972
|
-
|
|
6973
|
-
|
|
6974
|
-
|
|
6975
|
-
|
|
6976
|
-
|
|
6977
|
-
// flow, so it maps the flow's outputs — each to itself, since no `.return`
|
|
6978
|
-
// named a value here.
|
|
6979
|
-
const outs = endOutputs(root);
|
|
6980
|
-
if (Object.keys(outs).length)
|
|
6981
|
-
node.outputs = outs;
|
|
6982
|
-
root.nodes.push(node);
|
|
6983
|
-
for (const t of finalTails)
|
|
6984
|
-
addEdge(root, t, id);
|
|
6985
|
-
}
|
|
6995
|
+
// The implicit End a flow falls off the bottom into still terminates the
|
|
6996
|
+
// flow, so it maps the flow's outputs — each to itself, since no `.return`
|
|
6997
|
+
// named a value here.
|
|
6998
|
+
if (finalTails.length)
|
|
6999
|
+
emitEnd(root, uid(root, 'end'), finalTails);
|
|
6986
7000
|
// ── Additional entry points (design §5.1) ────────────────────────────────
|
|
6987
7001
|
// Emitted AFTER the shared body so their prefixes can join it by name. Once a
|
|
6988
7002
|
// flow has more than one root, every root carries the platform's entry-point
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uipath/maestro-builder-sdk",
|
|
3
|
-
"version": "6.17.
|
|
3
|
+
"version": "6.17.2",
|
|
4
4
|
"description": "Build UiPath Flow, Case, and BPMN artifacts by writing TypeScript.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://docs.uipath.com/maestro",
|
|
@@ -92,5 +92,5 @@
|
|
|
92
92
|
"@types/node": "^22.7.0",
|
|
93
93
|
"esbuild": "^0.28.1"
|
|
94
94
|
},
|
|
95
|
-
"gitref": "
|
|
95
|
+
"gitref": "629eba0e7332894902dec4cd1ef5c7072c02adf6"
|
|
96
96
|
}
|