@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
|
@@ -43,9 +43,18 @@ export function dataServiceWire(entity, operation) {
|
|
|
43
43
|
...optionalRow('queryExpression', 'query', operation.where),
|
|
44
44
|
scalarRow('start', 'query', operation.start ?? 0),
|
|
45
45
|
scalarRow('limit', 'query', operation.limit ?? 100),
|
|
46
|
-
...optionalRow('sortBy', 'query', operation.sortBy),
|
|
47
46
|
...optionalRow('isAscending', 'query', operation.ascending),
|
|
48
47
|
...optionalRow('expansionLevel', 'query', operation.expansionLevel),
|
|
48
|
+
// The sort FIELD rides in the body as `_sortFieldName` — the name the curated
|
|
49
|
+
// operation's `RequestFields` declare (`uip is resources describe
|
|
50
|
+
// uipath-uipath-dataservice QueryEntityRecordsCurated --operation Create`), and
|
|
51
|
+
// the only place the connector reads it: as a query parameter it is ignored and
|
|
52
|
+
// the query comes back in default order. This used to write `sortBy` as a query
|
|
53
|
+
// row; on flow-v2-run-12 the Query node so built validated and failed
|
|
54
|
+
// `no Query node sorted by priority DESC` (datafabric-e2e-contract-intake-pipeline
|
|
55
|
+
// 1.00 → 0.45) while the v1 arm wrote `{"_sortFieldName":"priority"}` by hand.
|
|
56
|
+
// `isAscending` stays a query parameter, which is where the connector reads it.
|
|
57
|
+
...(operation.sortBy !== undefined ? [bodyRow({ _sortFieldName: operation.sortBy })] : []),
|
|
49
58
|
],
|
|
50
59
|
};
|
|
51
60
|
case 'delete':
|
package/dist/bpmn/decompile.js
CHANGED
|
@@ -56,6 +56,7 @@ import BpmnModdle from 'bpmn-moddle';
|
|
|
56
56
|
import { createRequire } from 'node:module';
|
|
57
57
|
import { registryType, rowDeclaration } from './typed-node.js';
|
|
58
58
|
import { errorSchema, errorSchemaWithResponse } from './bpmn-sdk.js';
|
|
59
|
+
import { SCRIPT_ARGS_ENVELOPE, defaultScriptInputSchema } from './serialize.js';
|
|
59
60
|
import { BPMN_SCHEMA_VERSIONS } from './format-profile.js';
|
|
60
61
|
import { preserveSchemaVersion } from '../schema-version.js';
|
|
61
62
|
const require = createRequire(import.meta.url);
|
|
@@ -654,7 +655,17 @@ function scriptVersionOf(el) {
|
|
|
654
655
|
/** A `uipath:mapping` script task. */
|
|
655
656
|
function emitScriptTask(chain, el, payload, type) {
|
|
656
657
|
const args = (payload?.input ?? []).find((r) => r.name === 'args');
|
|
657
|
-
|
|
658
|
+
// The serializer writes the product's envelope — `vars`/`metadata`, and the default
|
|
659
|
+
// inputSchema declaring them — on every script task, so the source need not repeat
|
|
660
|
+
// them: an `args` that is exactly the envelope decompiles to no `inputs` at all, and
|
|
661
|
+
// only what the designer added (`"iterator[0]":"=iterator[0]"`) comes through. A
|
|
662
|
+
// schema that differs from the default (the designer's carries `$schema`) is kept.
|
|
663
|
+
const rawInputs = args ? safeJson(args.body) : undefined;
|
|
664
|
+
const inputs = rawInputs
|
|
665
|
+
? Object.fromEntries(Object.entries(rawInputs).filter(([k, v]) => !(k in SCRIPT_ARGS_ENVELOPE) || v !== SCRIPT_ARGS_ENVELOPE[k]))
|
|
666
|
+
: undefined;
|
|
667
|
+
const schemaBody = payload?.context?.inputSchema?.body;
|
|
668
|
+
const schemaIsDefault = schemaBody !== undefined && rawInputs !== undefined && JSON.stringify(safeJson(schemaBody)) === JSON.stringify(defaultScriptInputSchema(rawInputs));
|
|
658
669
|
const outputs = {};
|
|
659
670
|
for (const row of payload?.output ?? [])
|
|
660
671
|
outputs[row.var ?? row.name] = row.source;
|
|
@@ -680,7 +691,7 @@ function emitScriptTask(chain, el, payload, type) {
|
|
|
680
691
|
'type',
|
|
681
692
|
type !== undefined && type !== 'BPMN.Variables' && type !== 'BPMN.ScriptTask' ? str(type) : undefined,
|
|
682
693
|
],
|
|
683
|
-
['inputSchema',
|
|
694
|
+
['inputSchema', schemaBody !== undefined && !schemaIsDefault ? jsonLiteral(schemaBody) : undefined],
|
|
684
695
|
['inputs', record(inputs)],
|
|
685
696
|
['outputs', verbatim ? undefined : record(outputs)],
|
|
686
697
|
['outputRows', verbatim],
|
package/dist/bpmn/intsvc.d.ts
CHANGED
|
@@ -66,6 +66,14 @@ export declare function intsvcOutputVar(n: EventNode | ExternalNode): string | u
|
|
|
66
66
|
* @returns The moddle element.
|
|
67
67
|
*/
|
|
68
68
|
export declare function createConnectorEvent(ctx: ConnectorCtx, n: EventNode): El;
|
|
69
|
+
/**
|
|
70
|
+
* The `body` of a connector event node: the subscription's filter in the one place the
|
|
71
|
+
* platform reads it. `scope` (the `filter` option — the operation's event parameters, by
|
|
72
|
+
* equality) becomes both the `queryParams` and the leading `(k == 'v')` clauses;
|
|
73
|
+
* `filters` (payload conditions) follow, each in parentheses, all AND-joined. No scope and
|
|
74
|
+
* no filters is `{}`, which is what the designer writes for an unfiltered subscription.
|
|
75
|
+
*/
|
|
76
|
+
export declare function eventFilterBody(nodeId: string, scope: Record<string, unknown> | undefined, filters: readonly unknown[] | undefined): Record<string, unknown>;
|
|
69
77
|
/**
|
|
70
78
|
* An external agent / workflow invocation.
|
|
71
79
|
*
|
package/dist/bpmn/intsvc.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createTypedNode, typedNodeOutputVar } from './typed-node.js';
|
|
2
2
|
import { connectorBindingRef } from './connector.js';
|
|
3
|
+
import { eventFilterJmes, eventFilterProblem, readEventFilter } from '../core/event-filters.js';
|
|
3
4
|
/**
|
|
4
5
|
* Which extension type an external invocation lowers to.
|
|
5
6
|
*
|
|
@@ -96,8 +97,18 @@ export function createConnectorEvent(ctx, n) {
|
|
|
96
97
|
// and watches nothing. A curated entry carries its own and the author passes none.
|
|
97
98
|
objectName: n.object ?? op.objectName ?? '',
|
|
98
99
|
};
|
|
99
|
-
|
|
100
|
-
|
|
100
|
+
// The subscription's filter goes in the `body` row, where the platform reads it:
|
|
101
|
+
// `PO.BpmnEngine` `BpmnInputExtensions.GetEventFilters` takes `body.filters.expression`
|
|
102
|
+
// (and `queryParams` registers the scope with the connector). This emitter used to write
|
|
103
|
+
// `<uipath:input name="filter" target="filter">{…}</uipath:input>`, an input no handler
|
|
104
|
+
// reads: on flow-v2-run-12 the wait so scoped validated and matched every mail in the
|
|
105
|
+
// mailbox (outlook-waitfor-email 1.00 → 0.43), while the v1 arm and every Studio Web
|
|
106
|
+
// export write `{"filters":{"expression":"(parentFolderId == '…') && (contains(subject, '…'))"},"queryParams":{"parentFolderId":"…"}}`.
|
|
107
|
+
//
|
|
108
|
+
// The expression is the scope's equality clauses AND-joined with the authored
|
|
109
|
+
// `filters`, each lowered by the designer's own grammar (`core/event-filters.ts`) — the
|
|
110
|
+
// same lowering the Flow serializer does for `onEvent`/`waitForEvent`.
|
|
111
|
+
inputs.body = eventFilterBody(n.id, n.filter, n.filters);
|
|
101
112
|
if (n.parameters !== undefined)
|
|
102
113
|
inputs.parameters = n.parameters;
|
|
103
114
|
return createTypedNode(ctx.m, n.id, type, {
|
|
@@ -108,6 +119,36 @@ export function createConnectorEvent(ctx, n) {
|
|
|
108
119
|
skipCondition: n.skipCondition,
|
|
109
120
|
}, (varId) => ctx.varTypeOf?.(varId));
|
|
110
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* The `body` of a connector event node: the subscription's filter in the one place the
|
|
124
|
+
* platform reads it. `scope` (the `filter` option — the operation's event parameters, by
|
|
125
|
+
* equality) becomes both the `queryParams` and the leading `(k == 'v')` clauses;
|
|
126
|
+
* `filters` (payload conditions) follow, each in parentheses, all AND-joined. No scope and
|
|
127
|
+
* no filters is `{}`, which is what the designer writes for an unfiltered subscription.
|
|
128
|
+
*/
|
|
129
|
+
export function eventFilterBody(nodeId, scope, filters) {
|
|
130
|
+
const scopeEntries = Object.entries(scope ?? {}).filter(([, v]) => v !== undefined && v !== null && String(v) !== '');
|
|
131
|
+
const scopeClauses = scopeEntries.map(([k, v]) => `(${k} == '${String(v).replace(/'/g, "\\'")}')`);
|
|
132
|
+
const conditions = (filters ?? []).map((leaf, i) => {
|
|
133
|
+
const { read } = readEventFilter(leaf);
|
|
134
|
+
if (!read) {
|
|
135
|
+
throw new Error(`connector event "${nodeId}": filters[${i}] must carry a field and exactly ONE operator ` +
|
|
136
|
+
`(contains, startsWith, endsWith, equals, notEquals, lessThan, lessThanOrEqual, greaterThan, ` +
|
|
137
|
+
`greaterThanOrEqual) — got ${JSON.stringify(leaf)}.`);
|
|
138
|
+
}
|
|
139
|
+
const problem = eventFilterProblem(read);
|
|
140
|
+
if (problem)
|
|
141
|
+
throw new Error(`connector event "${nodeId}": filters[${i}] on \`${read.field}\`: ${problem}`);
|
|
142
|
+
return `(${eventFilterJmes(read)})`;
|
|
143
|
+
});
|
|
144
|
+
const expression = [...scopeClauses, ...conditions].join(' && ');
|
|
145
|
+
const body = {};
|
|
146
|
+
if (expression !== '')
|
|
147
|
+
body.filters = { expression };
|
|
148
|
+
if (scopeEntries.length > 0)
|
|
149
|
+
body.queryParams = Object.fromEntries(scopeEntries);
|
|
150
|
+
return body;
|
|
151
|
+
}
|
|
111
152
|
/**
|
|
112
153
|
* An external agent / workflow invocation.
|
|
113
154
|
*
|
package/dist/bpmn/serialize.d.ts
CHANGED
|
@@ -26,6 +26,20 @@ export declare function serialize(built: BuiltBpmn, opts?: BpmnSerializeOpts): P
|
|
|
26
26
|
*/
|
|
27
27
|
export declare function finalizeBpmnXml(xml: string): string;
|
|
28
28
|
export declare function processId(id: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* The `args` keys every Studio Web script task carries, in the order the designer writes
|
|
31
|
+
* them. The platform runtime injects these — and only these — into the script's scope.
|
|
32
|
+
*/
|
|
33
|
+
export declare const SCRIPT_ARGS_ENVELOPE: {
|
|
34
|
+
readonly vars: "=vars";
|
|
35
|
+
readonly metadata: "=metadata";
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* The `uipath:inputSchema` the product writes for a script task's `args`: `vars` and
|
|
39
|
+
* `metadata` as objects, plus an untyped property per extra key. Exported so the
|
|
40
|
+
* decompiler can recognise it and leave it out of the source.
|
|
41
|
+
*/
|
|
42
|
+
export declare function defaultScriptInputSchema(args: Record<string, unknown>): Record<string, unknown>;
|
|
29
43
|
/**
|
|
30
44
|
* The variable declarations a scope's output-bearing nodes imply.
|
|
31
45
|
*
|
package/dist/bpmn/serialize.js
CHANGED
|
@@ -444,21 +444,50 @@ function buildErrorMappingExt(ctx, rows) {
|
|
|
444
444
|
}),
|
|
445
445
|
});
|
|
446
446
|
}
|
|
447
|
+
/**
|
|
448
|
+
* The `args` keys every Studio Web script task carries, in the order the designer writes
|
|
449
|
+
* them. The platform runtime injects these — and only these — into the script's scope.
|
|
450
|
+
*/
|
|
451
|
+
export const SCRIPT_ARGS_ENVELOPE = { vars: '=vars', metadata: '=metadata' };
|
|
452
|
+
/**
|
|
453
|
+
* The `uipath:inputSchema` the product writes for a script task's `args`: `vars` and
|
|
454
|
+
* `metadata` as objects, plus an untyped property per extra key. Exported so the
|
|
455
|
+
* decompiler can recognise it and leave it out of the source.
|
|
456
|
+
*/
|
|
457
|
+
export function defaultScriptInputSchema(args) {
|
|
458
|
+
const properties = {};
|
|
459
|
+
for (const k of Object.keys(args))
|
|
460
|
+
properties[k] = k in SCRIPT_ARGS_ENVELOPE ? { type: 'object' } : {};
|
|
461
|
+
return { type: 'object', properties };
|
|
462
|
+
}
|
|
447
463
|
function createScriptTask(ctx, n) {
|
|
448
464
|
const { m } = ctx;
|
|
465
|
+
// The `args` row is the product's fixed envelope — `vars` and `metadata`, always, in
|
|
466
|
+
// that order — with the author's `inputs` after it. The platform runtime injects the
|
|
467
|
+
// ENVELOPE into the script's scope and nothing else: in flow-v2-run-12 every script
|
|
468
|
+
// that read an `inputs` key as a bare identifier faulted on the cloud with
|
|
469
|
+
// `<key> is not defined` (3 of 3 cloud-executed script tasks; `check`, `compile` and
|
|
470
|
+
// `uip maestro bpmn validate` had all passed), while every Studio Web export in
|
|
471
|
+
// `research/bpmn-roundtrip` and every v1-authored process in the same run carries
|
|
472
|
+
// exactly this envelope and reads `vars.<id>`. The author's keys are kept so an import
|
|
473
|
+
// round-trips (`iterator[0]` is how the designer scopes a loop body) and so a key the
|
|
474
|
+
// author knows the runtime honours can still be passed; a bare read of one is what
|
|
475
|
+
// `check` reports as SCRIPT_BARE_INPUT_REF. See docs/BPMN_EVAL_RUN12_FINDINGS.md §4 P0-1.
|
|
476
|
+
const args = { ...SCRIPT_ARGS_ENVELOPE, ...n.inputs };
|
|
477
|
+
const inputSchema = n.inputSchema ?? defaultScriptInputSchema(args);
|
|
449
478
|
const mapping = m.create('uipath:Mapping', {
|
|
450
479
|
version: 'v1',
|
|
451
480
|
type: m.create('uipath:Type', { value: n.type, version: 'v1' }),
|
|
452
|
-
// The context holds only the schema here — a script task has no context ROWS.
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
}),
|
|
481
|
+
// The context holds only the schema here — a script task has no context ROWS. Real
|
|
482
|
+
// script tasks all carry the schema; the product refuses to run without the envelope
|
|
483
|
+
// it declares, so an absent one is filled in rather than left out.
|
|
484
|
+
context: m.create('uipath:Context', {
|
|
485
|
+
inputSchema: m.create('uipath:InputSchema', {
|
|
486
|
+
type: 'jsonSchema',
|
|
487
|
+
body: typeof inputSchema === 'string' ? inputSchema : JSON.stringify(inputSchema),
|
|
460
488
|
}),
|
|
461
|
-
|
|
489
|
+
}),
|
|
490
|
+
input: [m.create('uipath:Input', { name: 'args', type: 'json', target: 'bodyField', body: JSON.stringify(args) })],
|
|
462
491
|
// Spelled-out rows win over derived ones: `outputs` names each row after the
|
|
463
492
|
// variable it writes, and the product names it after what the script returns
|
|
464
493
|
// while binding a differently-named variable. See ScriptTaskOpts.outputRows.
|
package/dist/bpmn/typed-node.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
import { resolveFromRegistry } from './registry-fallback.js';
|
|
3
|
+
import { nestConnectorBody } from '../core/connector-inputs.js';
|
|
3
4
|
import { types } from '../core/expr.js';
|
|
4
5
|
const require = createRequire(import.meta.url);
|
|
5
6
|
const REGISTRY = require('./registry-types.json');
|
|
@@ -64,7 +65,20 @@ export function isSnapshotType(type) {
|
|
|
64
65
|
* constant for the one type known to need it rather than derived.
|
|
65
66
|
*/
|
|
66
67
|
export const REGISTRY_GAPS = {
|
|
67
|
-
'
|
|
68
|
+
// `folderKey` — the job-start family's v1 addressing field. The registry declares
|
|
69
|
+
// `folderId`, but the engine's v1 branch reads `FolderKey` (`ExecuteApiWorkflowArgs`:
|
|
70
|
+
// `input.GetValueFromContext<string>(nameof(OrchestratorFolderContext.FolderKey))`), and
|
|
71
|
+
// the v1 skill verified live that `releaseKey` without it faults with `Could not get
|
|
72
|
+
// value for key:FolderKey from context in input`. Emitted only for a node addressed by
|
|
73
|
+
// `releaseKey` — see `OrchestratorOpts.folderKey`.
|
|
74
|
+
'Orchestrator.StartAgentJob': { resourceSubType: 'Agent', extraContext: ['folderKey'] },
|
|
75
|
+
'Orchestrator.StartJob': { extraContext: ['folderKey'] },
|
|
76
|
+
'Orchestrator.ExecuteApiWorkflowAsync': { extraContext: ['folderKey'] },
|
|
77
|
+
'Orchestrator.StartAgenticProcess': { extraContext: ['folderKey'] },
|
|
78
|
+
'Orchestrator.StartAgenticProcessAsync': { extraContext: ['folderKey'] },
|
|
79
|
+
'Orchestrator.StartCaseMgmtProcess': { extraContext: ['folderKey'] },
|
|
80
|
+
'Orchestrator.StartCaseMgmtProcessAsync': { extraContext: ['folderKey'] },
|
|
81
|
+
'Orchestrator.BusinessRules': { extraContext: ['folderKey'] },
|
|
68
82
|
// `notEnforcedInputs` — required InputFields the platform does NOT actually enforce.
|
|
69
83
|
//
|
|
70
84
|
// MEASURED, on a real trigger that carries `connection`, `folderKey`, `connectorKey`,
|
|
@@ -226,9 +240,19 @@ function contextRows(m, type, spec, supplied, verbatim = false) {
|
|
|
226
240
|
return rows;
|
|
227
241
|
}
|
|
228
242
|
for (const field of spec.context) {
|
|
229
|
-
|
|
243
|
+
let value = supplied[field.name] ?? field.default;
|
|
230
244
|
if (value === undefined || value === null)
|
|
231
245
|
continue;
|
|
246
|
+
// A json field whose REGISTRY default is the text `null` is unrunnable as declared:
|
|
247
|
+
// the platform parses the row to a JSON null and `IntSvcUnifiedHttpRequestArgs`
|
|
248
|
+
// throws `Body cannot be null` on it (incident 102018 — flow-v2-run-12
|
|
249
|
+
// slack-http-fallback, a GET with no body, faulted on exactly this after check,
|
|
250
|
+
// compile and validate had passed). Omitting the row is the same null. So a
|
|
251
|
+
// default of `null` on a json field is written as `{}`, the empty object the handler
|
|
252
|
+
// accepts; an author who wrote `null` themselves still gets it verbatim, and an
|
|
253
|
+
// import's `"null"` text round-trips untouched (see decompile's json-row rules).
|
|
254
|
+
if (field.type === 'json' && !(field.name in supplied) && field.default === 'null')
|
|
255
|
+
value = {};
|
|
232
256
|
emitted.add(field.name);
|
|
233
257
|
// A `json` field goes in the CDATA BODY, not a `value` attribute. The product
|
|
234
258
|
// validator parses that field as JSON and reads the attribute WITHOUT XML
|
|
@@ -266,6 +290,28 @@ function contextRows(m, type, spec, supplied, verbatim = false) {
|
|
|
266
290
|
}
|
|
267
291
|
return rows;
|
|
268
292
|
}
|
|
293
|
+
/**
|
|
294
|
+
* The `target` values a payload row can carry — `BpmnInputTarget` in PO.BpmnEngine
|
|
295
|
+
* (`InputResolutionActivities` routes `Body`/`BodyField`/`Header`/`PathVariable`/`Query`/
|
|
296
|
+
* `PathParameters`/`QueryParameters`/`File`), plus the connector emitter's `multipart`.
|
|
297
|
+
* An undeclared `separateInputs` key that is one of these addresses a slot; any other
|
|
298
|
+
* key is a field — see `inputRows`.
|
|
299
|
+
*/
|
|
300
|
+
const PAYLOAD_TARGETS = new Set([
|
|
301
|
+
'body',
|
|
302
|
+
'bodyField',
|
|
303
|
+
'header',
|
|
304
|
+
'headers',
|
|
305
|
+
'pathVariable',
|
|
306
|
+
'pathParameters',
|
|
307
|
+
'query',
|
|
308
|
+
'queryParameters',
|
|
309
|
+
'file',
|
|
310
|
+
'multipart',
|
|
311
|
+
]);
|
|
312
|
+
function isPlainRecord(value) {
|
|
313
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
314
|
+
}
|
|
269
315
|
/**
|
|
270
316
|
* The payload rows, per the type's `inputPattern`.
|
|
271
317
|
*
|
|
@@ -308,11 +354,37 @@ function inputRows(m, spec, inputs) {
|
|
|
308
354
|
// `recordId` as `"1111-…"`, quotes included, where every real export writes
|
|
309
355
|
// `type="string" value="1111-…"`. A structured value is still a json body; a
|
|
310
356
|
// scalar is written as the `value` attribute, the way a declared scalar is.
|
|
357
|
+
//
|
|
358
|
+
// An undeclared key is a TARGET, then — `body`, `pathParameters`, `queryParameters`
|
|
359
|
+
// — and only a target. A key that is none of the platform's targets is a request
|
|
360
|
+
// FIELD the author wrote where a slot belongs (`inputs: { script: '…' }` on
|
|
361
|
+
// `Intsvc.ActivityExecution`), and keyed as its own target it reaches a row the
|
|
362
|
+
// platform never reads: on flow-v2-run-12 the JDBC `execute-query-synchronously` node
|
|
363
|
+
// so written went out as `<uipath:input name="script" … target="script"/>` and every
|
|
364
|
+
// one of seven cloud runs faulted `Integration Services bad request`, while the v1 arm's
|
|
365
|
+
// `<uipath:input name="body" target="body">{"script":"…"}</uipath:input>` completed
|
|
366
|
+
// first time. Such keys fold into the type's own payload row (`inputName`/`inputTarget`,
|
|
367
|
+
// `body` for the Integration Service family), nested by their dotted ids, exactly as
|
|
368
|
+
// `.connector()` writes a request. A type with no payload row keeps keying them.
|
|
311
369
|
const declared = new Map(spec.inputs.map((f) => [f.name, f]));
|
|
312
|
-
|
|
370
|
+
const fieldKeys = spec.inputName !== null && spec.inputTarget !== null
|
|
371
|
+
? keys.filter((k) => !declared.has(k) && !PAYLOAD_TARGETS.has(k) && k !== spec.inputName)
|
|
372
|
+
: [];
|
|
373
|
+
const folded = new Set(fieldKeys);
|
|
374
|
+
const rows = keys
|
|
375
|
+
.filter((k) => !folded.has(k))
|
|
376
|
+
.map((k) => {
|
|
313
377
|
const field = declared.get(k);
|
|
314
|
-
|
|
378
|
+
let value = inputs[k];
|
|
379
|
+
if (k === spec.inputName && fieldKeys.length > 0 && isPlainRecord(value)) {
|
|
380
|
+
value = { ...nestConnectorBody(Object.fromEntries(fieldKeys.map((f) => [f, inputs[f]]))), ...value };
|
|
381
|
+
}
|
|
382
|
+
return payloadInput(m, k, field?.target ?? k, value, field?.type ?? inferredInputType(value));
|
|
315
383
|
});
|
|
384
|
+
if (fieldKeys.length > 0 && !keys.includes(spec.inputName)) {
|
|
385
|
+
rows.push(payloadInput(m, spec.inputName, spec.inputTarget, nestConnectorBody(Object.fromEntries(fieldKeys.map((f) => [f, inputs[f]]))), 'json'));
|
|
386
|
+
}
|
|
387
|
+
return rows;
|
|
316
388
|
}
|
|
317
389
|
if (!spec.inputName)
|
|
318
390
|
return [];
|
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
|
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.
|
|
@@ -78,3 +78,23 @@ interface ConnectorSlots {
|
|
|
78
78
|
};
|
|
79
79
|
};
|
|
80
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Fold a body keyed by the registry's dotted field ids into the nested request
|
|
83
|
+
* object those ids describe: `{"fields.project.key":"CE"}` → `{"fields":{"project":{"key":"CE"}}}`.
|
|
84
|
+
*
|
|
85
|
+
* `buildConnectorInputs` keeps the DOTTED keys on purpose — Flow's `bodyParameters` is a
|
|
86
|
+
* flat map and Flow's runtime (`convertDotNotationToNested`) nests it before the call.
|
|
87
|
+
* The Maestro BPMN runtime does not: `IntSvcSendTaskHandler` sends the `body` input as
|
|
88
|
+
* written, so a dotted key reaches the connector as a literal property name. Measured on
|
|
89
|
+
* flow-v2-run-12: the SDK's Jira `create-issue` body went out flat and Integration
|
|
90
|
+
* Service answered 400 `project: Specify a valid project ID or key`; the v1 arm, which
|
|
91
|
+
* writes the nested form by hand, created the ticket. A BPMN body therefore goes through
|
|
92
|
+
* this before it is serialised.
|
|
93
|
+
*
|
|
94
|
+
* A `[*]` segment names an array. On a leaf (`users[*]`) an array value is kept and a
|
|
95
|
+
* scalar becomes a one-element list, as the product's own conversion does; on an inner
|
|
96
|
+
* segment (`attachments[*].url`) each element of an array value becomes one object, and
|
|
97
|
+
* a scalar one. Later keys win over earlier ones at the same leaf; a nested object the
|
|
98
|
+
* author already wrote under a plain key is merged into, not replaced.
|
|
99
|
+
*/
|
|
100
|
+
export declare function nestConnectorBody(flat: Record<string, unknown>): Record<string, unknown>;
|