@uipath/maestro-builder-sdk 6.36.0 → 6.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api-index.md +28 -27
- package/dist/api-members.md +94 -93
- package/dist/bpmn/bpmn-sdk.d.ts +38 -9
- package/dist/bpmn/bpmn-sdk.js +104 -28
- package/dist/bpmn/check.js +64 -0
- package/dist/bpmn/connector.js +3 -0
- package/dist/bpmn/declared-vars.js +10 -3
- package/dist/bpmn/decompile.js +38 -10
- package/dist/bpmn/intsvc.d.ts +9 -10
- package/dist/bpmn/intsvc.js +31 -18
- package/dist/bpmn/registry-types.json +30 -24
- package/dist/bpmn/resource-key-pattern.d.ts +38 -0
- package/dist/bpmn/resource-key-pattern.js +47 -0
- package/dist/bpmn/serialize.js +22 -24
- package/dist/bpmn/typed-node.d.ts +17 -0
- package/dist/bpmn/typed-node.js +19 -0
- package/dist/cli-library.js +3 -1
- package/dist/core/cli-library.d.ts +8 -2
- package/dist/core/cli-library.js +6 -4
- package/dist/core/connector-resolve-error.d.ts +7 -1
- package/dist/core/event-checks.js +2 -2
- package/dist/core/lookups.d.ts +22 -0
- package/dist/core/lookups.js +34 -1
- package/dist/core/synthesized-definition.d.ts +15 -0
- package/dist/core/synthesized-definition.js +22 -0
- package/dist/registry/library-build/context.d.ts +16 -0
- package/dist/registry/library-build/context.js +32 -5
- package/dist/registry/prepare/actions.d.ts +15 -0
- package/dist/registry/prepare/actions.js +12 -1
- package/dist/registry/prepare/integration-service.d.ts +105 -0
- package/dist/registry/prepare/integration-service.js +218 -0
- package/dist/registry/prepare/messages.d.ts +41 -1
- package/dist/registry/prepare/messages.js +82 -1
- package/dist/registry/prepare/pipeline.js +73 -15
- package/dist/registry/uip/commands.d.ts +8 -0
- package/dist/registry/uip/commands.js +12 -0
- package/dist/serialize.js +11 -0
- package/package.json +2 -2
package/dist/bpmn/bpmn-sdk.d.ts
CHANGED
|
@@ -2454,6 +2454,17 @@ declare abstract class ScopeBuilder {
|
|
|
2454
2454
|
* is left alone.
|
|
2455
2455
|
*/
|
|
2456
2456
|
private bindRequiredContext;
|
|
2457
|
+
/**
|
|
2458
|
+
* A binding field's resource key from its `resourceKeyPattern`, with the declared
|
|
2459
|
+
* bindings the key was read through — or `undefined` when the pattern does not
|
|
2460
|
+
* resolve from this context (see `resolveResourceKeyPattern`), in which case the
|
|
2461
|
+
* caller keys the binding by the value, as it always has.
|
|
2462
|
+
*
|
|
2463
|
+
* @throws when the pattern resolves except for a `=bindings.<b>` reference that no
|
|
2464
|
+
* `.binding()` before this node declares with a value: that key would otherwise be
|
|
2465
|
+
* built, so the author is told what to declare rather than given a wrong pair.
|
|
2466
|
+
*/
|
|
2467
|
+
private resourceKeyFromPattern;
|
|
2457
2468
|
/** @internal Shared body of the Orchestrator process-invocation methods. */
|
|
2458
2469
|
private orchestratorNode;
|
|
2459
2470
|
/**
|
|
@@ -2530,12 +2541,23 @@ declare abstract class ScopeBuilder {
|
|
|
2530
2541
|
*
|
|
2531
2542
|
* @remarks
|
|
2532
2543
|
* **Not a DMN decision table**, despite the element and the label. The registry spec
|
|
2533
|
-
* is an Orchestrator process invocation — `
|
|
2534
|
-
*
|
|
2535
|
-
*
|
|
2536
|
-
* package
|
|
2537
|
-
*
|
|
2538
|
-
*
|
|
2544
|
+
* is an Orchestrator process invocation — `name`/`folderPath` plus `JobArguments` —
|
|
2545
|
+
* structurally a {@link ScopeBuilder.startProcess}. `process` names a package whose
|
|
2546
|
+
* Orchestrator process type is `BusinessRules`; whatever decision logic exists lives
|
|
2547
|
+
* INSIDE that package, not in the `.bpmn`. So this is the job-start family's sixth
|
|
2548
|
+
* member, not a rules subsystem.
|
|
2549
|
+
*
|
|
2550
|
+
* Where it departs from the family, the registry says so and this follows it:
|
|
2551
|
+
*
|
|
2552
|
+
* - the task is `version="v3"` — below v3 the engine cannot run the `.uirule`
|
|
2553
|
+
* package a BusinessRules project deploys — so a rule is always addressed by
|
|
2554
|
+
* name, and `releaseKey` / `folderKey` are refused;
|
|
2555
|
+
* - `name` and `folderPath` are each bound to a `BusinessRule` resource
|
|
2556
|
+
* (`propertyAttribute` `name` / `folderPath`), both keyed `<folder>.<name>`
|
|
2557
|
+
* (`<name>` alone when the folder is empty), declared on your behalf as
|
|
2558
|
+
* `<id>_name` / `<id>_folderPath`; an unbound `_label` carries the rule's name;
|
|
2559
|
+
* - two output rows: `output` (`=result`, in `outputVar`) and `Error` (`=Error`,
|
|
2560
|
+
* in `<id>_Error`), both `jsonSchema`.
|
|
2539
2561
|
*
|
|
2540
2562
|
* Distinct from the Case SDK's `rule()`, which declares DNF lifecycle conditions
|
|
2541
2563
|
* (`case-entered`, `required-tasks-completed`, …) deciding when a stage enters or
|
|
@@ -2555,7 +2577,7 @@ declare abstract class ScopeBuilder {
|
|
|
2555
2577
|
* process: 'DiscountTier',
|
|
2556
2578
|
* folder: 'Finance',
|
|
2557
2579
|
* input: { amount: 4200 },
|
|
2558
|
-
* outputs: { tier: '=
|
|
2580
|
+
* outputs: { tier: '=result.DiscountTier.tier' },
|
|
2559
2581
|
* })
|
|
2560
2582
|
* .endEvent('done')
|
|
2561
2583
|
* .sequenceFlow('start', 'tier')
|
|
@@ -2564,9 +2586,16 @@ declare abstract class ScopeBuilder {
|
|
|
2564
2586
|
* ```
|
|
2565
2587
|
*
|
|
2566
2588
|
* @param id - The element id, unique in the process and used by `sequenceFlow`.
|
|
2589
|
+
*
|
|
2590
|
+
* **The result is keyed by decision name.** The platform's `output` row is the whole
|
|
2591
|
+
* result (`source="=result"`) and a decision's column reads as
|
|
2592
|
+
* `=result.<Decision>.<column>` — `vars.<id>_businessRuleResponse.<Decision>.<column>`
|
|
2593
|
+
* downstream — never `=result.<column>` (canvas spec `outputNotes`; `uip rules describe
|
|
2594
|
+
* <name>` lists the decisions and columns, and only a decision-keyed rule, one whose
|
|
2595
|
+
* `OutputArguments` carry `x-uipath-decision-keyed`, fits this row).
|
|
2567
2596
|
* @param opts - Which rule package, folder, and input arguments. `outputVar` defaults
|
|
2568
|
-
* to `<id>_businessRuleResponse`
|
|
2569
|
-
*
|
|
2597
|
+
* to `<id>_businessRuleResponse`. `releaseKey` and `folderKey` are refused: a v3 rule
|
|
2598
|
+
* is addressed by name.
|
|
2570
2599
|
* @param body - Boundary events attached to this activity, declared on the {@link ActivityBuilder} it receives.
|
|
2571
2600
|
* @returns This builder, so calls chain.
|
|
2572
2601
|
*/
|
package/dist/bpmn/bpmn-sdk.js
CHANGED
|
@@ -3,6 +3,7 @@ import { checkBpmnExpressions, BpmnBuildError } from './bpmn-expr-check.js';
|
|
|
3
3
|
import { BPMN_SCHEMA_VERSIONS } from './format-profile.js';
|
|
4
4
|
import { schemaVersionRefusal } from '../schema-version.js';
|
|
5
5
|
import { bindingRequiredFields, REGISTRY_GAPS, registryType } from './typed-node.js';
|
|
6
|
+
import { resolveResourceKeyPattern } from './resource-key-pattern.js';
|
|
6
7
|
import { DATA_SERVICE_ACTIVITY_TYPE, DATA_SERVICE_CONNECTOR_KEY, dataServiceWire } from './data-service.js';
|
|
7
8
|
import { ownEntry } from '../core/own-entry.js';
|
|
8
9
|
export { BpmnBuildError } from './bpmn-expr-check.js';
|
|
@@ -1213,22 +1214,75 @@ class ScopeBuilder {
|
|
|
1213
1214
|
if (typeof value !== 'string' || value.startsWith('='))
|
|
1214
1215
|
continue;
|
|
1215
1216
|
const bindingId = `${id}_${field.name}`;
|
|
1217
|
+
// The key a pattern builds from the context values — a `=bindings.<b>` value as
|
|
1218
|
+
// the DECLARED value of `b`, since a key is never evaluated as an expression.
|
|
1219
|
+
// Resolved only for the fields the pattern names, and refused only when the
|
|
1220
|
+
// pattern would otherwise resolve: a pattern that names nothing the context has
|
|
1221
|
+
// (`${metadata.key}`, the live registry's pattern on a job start, whose context
|
|
1222
|
+
// has no `key`) leaves the key to the value, as before. This snapshot carries no
|
|
1223
|
+
// pattern for the job starts at all; `resource-key-pattern.ts` is tested on both.
|
|
1224
|
+
const keyed = this.resourceKeyFromPattern(id, type, field, context);
|
|
1216
1225
|
this._bindingRegistry.declare({
|
|
1217
1226
|
id: bindingId,
|
|
1218
1227
|
name: field.displayName ?? field.name,
|
|
1219
1228
|
resource: field.bindingInfo?.resource ?? 'process',
|
|
1220
1229
|
propertyAttribute: field.bindingInfo?.propertyAttribute ?? field.name,
|
|
1221
|
-
// A fact the registry omits and the validator enforces — see REGISTRY_GAPS.
|
|
1222
1230
|
resourceSubType: ownEntry(REGISTRY_GAPS, type)?.resourceSubType,
|
|
1223
1231
|
default: value,
|
|
1224
|
-
resourceKey: value,
|
|
1232
|
+
resourceKey: keyed?.key ?? value,
|
|
1225
1233
|
});
|
|
1234
|
+
// A declared binding the key was read from shares it when the author left its own
|
|
1235
|
+
// key unset, so the pair names one resource — only when the pattern resolved. One
|
|
1236
|
+
// that already carries a DIFFERENT key (a folder binding two rules share, or an
|
|
1237
|
+
// author's own key) would make this node's pair name two resources: refused, with
|
|
1238
|
+
// the binding to declare instead.
|
|
1239
|
+
if (keyed) {
|
|
1240
|
+
for (const decl of keyed.referenced) {
|
|
1241
|
+
if (decl.resourceKey === undefined)
|
|
1242
|
+
decl.resourceKey = keyed.key;
|
|
1243
|
+
else if (decl.resourceKey !== keyed.key) {
|
|
1244
|
+
const resource = field.bindingInfo?.resource ?? 'process';
|
|
1245
|
+
throw new Error(`${type} "${id}": its ${resource} key "${keyed.key}" is read through =bindings.${decl.id}, whose resource key is already ` +
|
|
1246
|
+
`"${decl.resourceKey}" (another node's pair, or the binding's own), so the pair would name two resources. ` +
|
|
1247
|
+
`A ${resource} binding names one: declare one per node, e.g. ` +
|
|
1248
|
+
`\`.binding('${decl.id}_${id}', { resource: '${decl.resource}', propertyAttribute: '${decl.propertyAttribute}', value: '${decl.default}' })\`.`);
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1226
1252
|
out[field.name] = `=bindings.${bindingId}`;
|
|
1227
1253
|
}
|
|
1228
1254
|
return out;
|
|
1229
1255
|
}
|
|
1256
|
+
/**
|
|
1257
|
+
* A binding field's resource key from its `resourceKeyPattern`, with the declared
|
|
1258
|
+
* bindings the key was read through — or `undefined` when the pattern does not
|
|
1259
|
+
* resolve from this context (see `resolveResourceKeyPattern`), in which case the
|
|
1260
|
+
* caller keys the binding by the value, as it always has.
|
|
1261
|
+
*
|
|
1262
|
+
* @throws when the pattern resolves except for a `=bindings.<b>` reference that no
|
|
1263
|
+
* `.binding()` before this node declares with a value: that key would otherwise be
|
|
1264
|
+
* built, so the author is told what to declare rather than given a wrong pair.
|
|
1265
|
+
*/
|
|
1266
|
+
resourceKeyFromPattern(id, type, field, context) {
|
|
1267
|
+
const pattern = field.bindingInfo?.resourceKeyPattern;
|
|
1268
|
+
const byId = (bindingId) => this._bindingRegistry.bindings.find((b) => b.id === bindingId);
|
|
1269
|
+
const resolved = resolveResourceKeyPattern(pattern, context, byId);
|
|
1270
|
+
if (resolved === undefined)
|
|
1271
|
+
return undefined;
|
|
1272
|
+
if ('blocked' in resolved) {
|
|
1273
|
+
const { field: blockedField, ref } = resolved.blocked;
|
|
1274
|
+
const refId = ref.slice('=bindings.'.length);
|
|
1275
|
+
const resource = field.bindingInfo?.resource ?? 'process';
|
|
1276
|
+
throw new Error(`${type} "${id}": ${blockedField} reads ${ref}, which no .binding() declares with a value before this node, ` +
|
|
1277
|
+
`so the ${resource} key \`${pattern}\` it is paired under cannot be built. Declare ` +
|
|
1278
|
+
`\`.binding('${refId}', { resource: '${resource}', propertyAttribute: '${blockedField}', value: '…' })\` first, or pass the value itself.`);
|
|
1279
|
+
}
|
|
1280
|
+
return { key: resolved.key, referenced: resolved.referenced.map((bid) => byId(bid)).filter((d) => d !== undefined) };
|
|
1281
|
+
}
|
|
1230
1282
|
/** @internal Shared body of the Orchestrator process-invocation methods. */
|
|
1231
|
-
orchestratorNode(id, type, opts, body
|
|
1283
|
+
orchestratorNode(id, type, opts, body,
|
|
1284
|
+
/** A member's own departures from the family: the business rule's v3 stamp and `_label`. */
|
|
1285
|
+
own = {}) {
|
|
1232
1286
|
// The `uipath:type` version selects the engine's addressing branch — see
|
|
1233
1287
|
// OrchestratorOpts.process. By name → v2 (`name` + `folderPath`); by key → v1
|
|
1234
1288
|
// (`releaseKey` + `folderKey`). A literal release key becomes a `process`/`Key`
|
|
@@ -1254,12 +1308,13 @@ class ScopeBuilder {
|
|
|
1254
1308
|
id,
|
|
1255
1309
|
name: opts.name,
|
|
1256
1310
|
type,
|
|
1257
|
-
typeVersion: byKey ? 'v1' : 'v2',
|
|
1311
|
+
typeVersion: own.typeVersion ?? (byKey ? 'v1' : 'v2'),
|
|
1258
1312
|
context: this.bindRequiredContext(id, type, {
|
|
1259
1313
|
name: opts.process,
|
|
1260
1314
|
...(opts.folder !== undefined ? { folderPath: opts.folder } : {}),
|
|
1261
1315
|
...(releaseKey !== undefined ? { releaseKey } : {}),
|
|
1262
1316
|
...(byKey && opts.folderKey !== undefined ? { folderKey: opts.folderKey } : {}),
|
|
1317
|
+
...(own.context ?? {}),
|
|
1263
1318
|
}),
|
|
1264
1319
|
inputs: opts.input ?? {},
|
|
1265
1320
|
outputs: opts.outputs,
|
|
@@ -1355,12 +1410,23 @@ class ScopeBuilder {
|
|
|
1355
1410
|
*
|
|
1356
1411
|
* @remarks
|
|
1357
1412
|
* **Not a DMN decision table**, despite the element and the label. The registry spec
|
|
1358
|
-
* is an Orchestrator process invocation — `
|
|
1359
|
-
*
|
|
1360
|
-
*
|
|
1361
|
-
* package
|
|
1362
|
-
*
|
|
1363
|
-
*
|
|
1413
|
+
* is an Orchestrator process invocation — `name`/`folderPath` plus `JobArguments` —
|
|
1414
|
+
* structurally a {@link ScopeBuilder.startProcess}. `process` names a package whose
|
|
1415
|
+
* Orchestrator process type is `BusinessRules`; whatever decision logic exists lives
|
|
1416
|
+
* INSIDE that package, not in the `.bpmn`. So this is the job-start family's sixth
|
|
1417
|
+
* member, not a rules subsystem.
|
|
1418
|
+
*
|
|
1419
|
+
* Where it departs from the family, the registry says so and this follows it:
|
|
1420
|
+
*
|
|
1421
|
+
* - the task is `version="v3"` — below v3 the engine cannot run the `.uirule`
|
|
1422
|
+
* package a BusinessRules project deploys — so a rule is always addressed by
|
|
1423
|
+
* name, and `releaseKey` / `folderKey` are refused;
|
|
1424
|
+
* - `name` and `folderPath` are each bound to a `BusinessRule` resource
|
|
1425
|
+
* (`propertyAttribute` `name` / `folderPath`), both keyed `<folder>.<name>`
|
|
1426
|
+
* (`<name>` alone when the folder is empty), declared on your behalf as
|
|
1427
|
+
* `<id>_name` / `<id>_folderPath`; an unbound `_label` carries the rule's name;
|
|
1428
|
+
* - two output rows: `output` (`=result`, in `outputVar`) and `Error` (`=Error`,
|
|
1429
|
+
* in `<id>_Error`), both `jsonSchema`.
|
|
1364
1430
|
*
|
|
1365
1431
|
* Distinct from the Case SDK's `rule()`, which declares DNF lifecycle conditions
|
|
1366
1432
|
* (`case-entered`, `required-tasks-completed`, …) deciding when a stage enters or
|
|
@@ -1380,7 +1446,7 @@ class ScopeBuilder {
|
|
|
1380
1446
|
* process: 'DiscountTier',
|
|
1381
1447
|
* folder: 'Finance',
|
|
1382
1448
|
* input: { amount: 4200 },
|
|
1383
|
-
* outputs: { tier: '=
|
|
1449
|
+
* outputs: { tier: '=result.DiscountTier.tier' },
|
|
1384
1450
|
* })
|
|
1385
1451
|
* .endEvent('done')
|
|
1386
1452
|
* .sequenceFlow('start', 'tier')
|
|
@@ -1389,28 +1455,38 @@ class ScopeBuilder {
|
|
|
1389
1455
|
* ```
|
|
1390
1456
|
*
|
|
1391
1457
|
* @param id - The element id, unique in the process and used by `sequenceFlow`.
|
|
1458
|
+
*
|
|
1459
|
+
* **The result is keyed by decision name.** The platform's `output` row is the whole
|
|
1460
|
+
* result (`source="=result"`) and a decision's column reads as
|
|
1461
|
+
* `=result.<Decision>.<column>` — `vars.<id>_businessRuleResponse.<Decision>.<column>`
|
|
1462
|
+
* downstream — never `=result.<column>` (canvas spec `outputNotes`; `uip rules describe
|
|
1463
|
+
* <name>` lists the decisions and columns, and only a decision-keyed rule, one whose
|
|
1464
|
+
* `OutputArguments` carry `x-uipath-decision-keyed`, fits this row).
|
|
1392
1465
|
* @param opts - Which rule package, folder, and input arguments. `outputVar` defaults
|
|
1393
|
-
* to `<id>_businessRuleResponse`
|
|
1394
|
-
*
|
|
1466
|
+
* to `<id>_businessRuleResponse`. `releaseKey` and `folderKey` are refused: a v3 rule
|
|
1467
|
+
* is addressed by name.
|
|
1395
1468
|
* @param body - Boundary events attached to this activity, declared on the {@link ActivityBuilder} it receives.
|
|
1396
1469
|
* @returns This builder, so calls chain.
|
|
1397
1470
|
*/
|
|
1398
1471
|
businessRule(id, opts, body) {
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
//
|
|
1404
|
-
//
|
|
1405
|
-
//
|
|
1406
|
-
// against
|
|
1407
|
-
//
|
|
1408
|
-
|
|
1409
|
-
//
|
|
1410
|
-
//
|
|
1411
|
-
//
|
|
1412
|
-
//
|
|
1413
|
-
|
|
1472
|
+
if (opts.releaseKey !== undefined || opts.folderKey !== undefined) {
|
|
1473
|
+
throw new Error(`businessRule('${id}'): a business rule is addressed by name and folder (version v3 binds the ` +
|
|
1474
|
+
'BusinessRule resource by `name` + `folderPath`); drop `releaseKey` / `folderKey`.');
|
|
1475
|
+
}
|
|
1476
|
+
// The default output variable is pinned rather than derived. Derived, it would come
|
|
1477
|
+
// from the registry's `OutputName`, which was once the designer LABEL, `Execute
|
|
1478
|
+
// business rule [Preview]` — a `[Preview]` marker in a name authors write expressions
|
|
1479
|
+
// against, and one that changed when the marker dropped. Pinning keeps every
|
|
1480
|
+
// `vars.<id>_businessRuleResponse` reference valid across that kind of spec edit.
|
|
1481
|
+
return this.orchestratorNode(id, 'Orchestrator.BusinessRules',
|
|
1482
|
+
// `folderPath` is a REQUIRED binding of the type: a rule in the running job's own
|
|
1483
|
+
// folder is bound with an empty path, so the pair is keyed by the name alone
|
|
1484
|
+
// (`uip maestro bpmn validate` accepts it); leaving the field out instead drew
|
|
1485
|
+
// BINDING_REQUIRED and EMPTY_REQUIRED_FIELD from `check`.
|
|
1486
|
+
{ ...opts, folder: opts.folder ?? '', outputVar: opts.outputVar ?? `${id}_businessRuleResponse` }, body,
|
|
1487
|
+
// `_label` is the one unbound context field: the rule's name, which the canvas
|
|
1488
|
+
// shows while `name` itself is a binding reference.
|
|
1489
|
+
{ typeVersion: 'v3', context: { _label: opts.process } });
|
|
1414
1490
|
}
|
|
1415
1491
|
/**
|
|
1416
1492
|
* Add an item to an Orchestrator **queue** (`Orchestrator.CreateQueueItem`, or
|
package/dist/bpmn/check.js
CHANGED
|
@@ -69,6 +69,7 @@ export function check(built, opts = {}) {
|
|
|
69
69
|
checkFolderBindings(built, diags);
|
|
70
70
|
checkEntryPointId(built, diags);
|
|
71
71
|
checkMethodParentheses(built, diags);
|
|
72
|
+
checkConnectorRows(built, diags);
|
|
72
73
|
checkConnectors(built, opts, diags);
|
|
73
74
|
checkConnectorEvents(built, opts, diags);
|
|
74
75
|
return diags;
|
|
@@ -219,6 +220,69 @@ function checkMethodParentheses(built, diags) {
|
|
|
219
220
|
};
|
|
220
221
|
walk(built.nodes, built.flows);
|
|
221
222
|
}
|
|
223
|
+
/** The Integration Service activity types, whose payload rows the platform reads only by `target`. */
|
|
224
|
+
const CONNECTOR_ACTIVITY_TYPES = new Set(['Intsvc.ActivityExecution', 'Intsvc.AsyncExecution']);
|
|
225
|
+
/**
|
|
226
|
+
* The targets the platform's reader knows (`UiPathExtensionsProvider`): anything else —
|
|
227
|
+
* an empty string included — falls through its switch to `Body`, and the row becomes the
|
|
228
|
+
* whole request body. `pathParameters` / `queryParameters` are the aggregate forms.
|
|
229
|
+
*/
|
|
230
|
+
const PLATFORM_ROW_TARGETS = new Set(['body', 'bodyField', 'header', 'path', 'query', 'file', 'pathParameters', 'queryParameters']);
|
|
231
|
+
/**
|
|
232
|
+
* A connector activity's payload rows, as the platform reads them.
|
|
233
|
+
*
|
|
234
|
+
* Integration Service reads a connector's payload rows by target only, and its reader
|
|
235
|
+
* maps a missing, empty or unknown target to `Body` — so such a row does not vanish,
|
|
236
|
+
* it REPLACES the request body. The 2026-10-16 eval run's agent wrote
|
|
237
|
+
* `inputRows: [{ name: 'script', type: 'string', value }]` for a JDBC query: it
|
|
238
|
+
* validated, and faulted live with `Value for required parameter 'script' not found`,
|
|
239
|
+
* while the v1 arm's `<uipath:input name="body" target="body">{"script":"…"}</uipath:input>`
|
|
240
|
+
* ran. `inputs: { script }` folds a request field into the body row, so the remedy is
|
|
241
|
+
* the plain record for a body field, or the row's own target when it is a real slot.
|
|
242
|
+
* And because an `inputRows` list REPLACES `inputs` at emission (typed-node.ts), a
|
|
243
|
+
* node that sets both silently drops every `inputs` field — the same live fault.
|
|
244
|
+
*/
|
|
245
|
+
function checkConnectorRows(built, diags) {
|
|
246
|
+
for (const n of allNodes(built.nodes)) {
|
|
247
|
+
if (n.kind !== 'typed' || !CONNECTOR_ACTIVITY_TYPES.has(n.type))
|
|
248
|
+
continue;
|
|
249
|
+
if (n.inputRows && Object.keys(n.inputs ?? {}).length > 0) {
|
|
250
|
+
diags.push({
|
|
251
|
+
level: 'error',
|
|
252
|
+
code: 'CONNECTOR_INPUTS_SHADOWED',
|
|
253
|
+
where: n.id,
|
|
254
|
+
message: `Connector activity "${n.id}" sets both \`inputs\` (${Object.keys(n.inputs).map((k) => `"${k}"`).join(', ')}) and \`inputRows\`; ` +
|
|
255
|
+
`an \`inputRows\` list replaces \`inputs\` entirely at emission, so those fields are never sent. Spell them as rows too ` +
|
|
256
|
+
`(a body field as \`{ name: 'body', type: 'json', target: 'body', body: { … } }\`), or drop \`inputRows\` and write every field in \`inputs\`.`,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
for (const row of n.inputRows ?? []) {
|
|
260
|
+
const target = row.target ?? '';
|
|
261
|
+
if (target === '') {
|
|
262
|
+
diags.push({
|
|
263
|
+
level: 'error',
|
|
264
|
+
code: 'CONNECTOR_INPUT_ROW_NO_TARGET',
|
|
265
|
+
where: n.id,
|
|
266
|
+
message: `Connector activity "${n.id}" input row "${row.name}" has no target. Integration Service reads a connector's ` +
|
|
267
|
+
`payload rows by target, and its reader maps a missing or empty one to the request body — so this row validates and ` +
|
|
268
|
+
`then becomes the WHOLE body, replacing the body row. Give the row its target — \`target: 'body'\` folds it into ` +
|
|
269
|
+
`the request body, \`path\`/\`query\`/\`file\` are the other slots — or move EVERY row to \`inputs: { ${row.name}: … }\` ` +
|
|
270
|
+
`and drop \`inputRows\`: an \`inputRows\` list, when present, replaces \`inputs\` entirely.`,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
else if (!PLATFORM_ROW_TARGETS.has(target)) {
|
|
274
|
+
diags.push({
|
|
275
|
+
level: 'error',
|
|
276
|
+
code: 'CONNECTOR_INPUT_ROW_TARGET_UNKNOWN',
|
|
277
|
+
where: n.id,
|
|
278
|
+
message: `Connector activity "${n.id}" input row "${row.name}" has target "${target}", which the platform's reader does not know; ` +
|
|
279
|
+
`it falls through to the request body, so this row would replace the body. Use one of ` +
|
|
280
|
+
`${[...PLATFORM_ROW_TARGETS].map((t) => `'${t}'`).join(', ')}.`,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
222
286
|
const GUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
223
287
|
/**
|
|
224
288
|
* `.metadata({ entryPointId })` must be a GUID.
|
package/dist/bpmn/connector.js
CHANGED
|
@@ -26,6 +26,9 @@ export function createConnectorTask(ctx, n) {
|
|
|
26
26
|
// authored for rather than an inferred one.
|
|
27
27
|
ctxInput(m, 'operation', 'string', op.name ?? op.objectName ?? n.action),
|
|
28
28
|
ctxInput(m, 'connectorKey', 'string', rc.connectorKey),
|
|
29
|
+
// The registry template's own value, and the one the CLI's V1 packaging path keys
|
|
30
|
+
// on; V2 (`Data.V2`) is tracked in https://github.com/UiPath/builder-sdk/issues/1298, blocked on UiPath/cli#5209.
|
|
31
|
+
ctxInput(m, 'activityConfigurationVersion', 'string', 'v1'),
|
|
29
32
|
];
|
|
30
33
|
const connRef = bindingRef(ctx, n.connection, 'Connection', 'ConnectionId', `${rc.connectorKey} connection`);
|
|
31
34
|
if (connRef) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { typedNodeOutputVar } from './typed-node.js';
|
|
1
|
+
import { typedNodeAdditionalOutputVars, typedNodeOutputVar } from './typed-node.js';
|
|
2
2
|
/**
|
|
3
3
|
* The variables a connector task's output rows land in.
|
|
4
4
|
*
|
|
@@ -38,7 +38,7 @@ export function typedNodeDeclaredVars(n) {
|
|
|
38
38
|
if (n.outputRows)
|
|
39
39
|
return n.outputRows.map((r) => r.var).filter((v) => v !== undefined);
|
|
40
40
|
const v = typedNodeOutputVar(n.id, n.type, n.outputVar, n.spec);
|
|
41
|
-
return [...(v ? [v] : []), ...Object.keys(n.outputs ?? {})];
|
|
41
|
+
return [...(v ? [v] : []), ...typedNodeAdditionalOutputVars(n.id, n.type, n.spec), ...Object.keys(n.outputs ?? {})];
|
|
42
42
|
}
|
|
43
43
|
/**
|
|
44
44
|
* Every variable name a node DECLARES by writing to it — the platform counts a
|
|
@@ -58,8 +58,15 @@ export function nodeDeclaredVars(n) {
|
|
|
58
58
|
}
|
|
59
59
|
if (n.kind === 'typed')
|
|
60
60
|
return typedNodeDeclaredVars(n);
|
|
61
|
-
|
|
61
|
+
// Spelled-out rows REPLACE the derived ones, so they are the whole answer — the
|
|
62
|
+
// same rule as `typedNodeDeclaredVars`. Reading only `outputs` here left a script
|
|
63
|
+
// task authored with `outputRows` (the form decompile writes) declaring nothing,
|
|
64
|
+
// so every `vars.<row.var>` downstream was EXPR_UNDECLARED_REF.
|
|
65
|
+
if (n.kind === 'scriptTask') {
|
|
66
|
+
if (n.outputRows)
|
|
67
|
+
return n.outputRows.map((r) => r.var).filter((v) => v !== undefined);
|
|
62
68
|
return Object.keys(n.outputs);
|
|
69
|
+
}
|
|
63
70
|
if (n.kind === 'task')
|
|
64
71
|
return Object.keys(n.set);
|
|
65
72
|
return [];
|
package/dist/bpmn/decompile.js
CHANGED
|
@@ -419,18 +419,32 @@ function outputsOf(payload, _id, type) {
|
|
|
419
419
|
const rows = payload?.output ?? [];
|
|
420
420
|
const extras = {};
|
|
421
421
|
let outputVar;
|
|
422
|
-
const
|
|
422
|
+
const spec = type ? registryType(type) : undefined;
|
|
423
|
+
const specName = spec?.output?.name;
|
|
424
|
+
// A row the registry declares beside the main one (a business rule's `Error`) is
|
|
425
|
+
// re-emitted by the builder, so lifting it as an extra would write it twice on the
|
|
426
|
+
// way back. Matched by NAME (and source): an export that binds it to a variable
|
|
427
|
+
// other than the derived `<id>_<name>` is spelled out verbatim instead
|
|
428
|
+
// (`outputRowsNeedSpellingOut`), which carries that variable through exactly.
|
|
429
|
+
const declaredBeside = new Map((spec?.additionalOutputs ?? []).map((o) => [o.name, o.source ?? undefined]));
|
|
423
430
|
for (const row of rows) {
|
|
424
431
|
if (specName !== undefined && row.name === specName) {
|
|
425
432
|
// Only worth emitting when it differs from the default the builder derives.
|
|
426
433
|
outputVar = row.var;
|
|
427
434
|
continue;
|
|
428
435
|
}
|
|
436
|
+
if (declaredBeside.has(row.name) && (declaredBeside.get(row.name) === undefined || declaredBeside.get(row.name) === row.source))
|
|
437
|
+
continue;
|
|
429
438
|
if (row.source !== undefined)
|
|
430
439
|
extras[row.var ?? row.name] = row.source;
|
|
431
440
|
}
|
|
432
441
|
return { outputVar, extras };
|
|
433
442
|
}
|
|
443
|
+
/** The variable the builder derives for each row the registry declares beside the main one. */
|
|
444
|
+
function derivedExtraVars(id, type) {
|
|
445
|
+
const spec = type ? registryType(type) : undefined;
|
|
446
|
+
return new Map((spec?.additionalOutputs ?? []).map((o) => [o.name, `${id}_${o.name}`]));
|
|
447
|
+
}
|
|
434
448
|
/**
|
|
435
449
|
* Whether these rows can be reproduced by the compact `outputVar` + `outputs` pair,
|
|
436
450
|
* or need spelling out.
|
|
@@ -445,10 +459,16 @@ function outputsOf(payload, _id, type) {
|
|
|
445
459
|
* form stays in use wherever it is exact, so the 30 other extension types decompile
|
|
446
460
|
* to exactly the source they did before, and only a row that genuinely needs the
|
|
447
461
|
* long form gets it.
|
|
462
|
+
*
|
|
463
|
+
* A row the registry declares beside the main one (`derivedBeside`: a business rule's
|
|
464
|
+
* `Error`) that an export binds to a variable other than the derived `<id>_<name>`
|
|
465
|
+
* needs it too: the compact form cannot rename that row, and lifting it as an extra
|
|
466
|
+
* would have the builder add its own beside it.
|
|
448
467
|
*/
|
|
449
|
-
function outputRowsNeedSpellingOut(payload) {
|
|
468
|
+
function outputRowsNeedSpellingOut(payload, derivedBeside = new Map()) {
|
|
450
469
|
const rows = payload?.output ?? [];
|
|
451
|
-
return rows.some((row) => row.
|
|
470
|
+
return rows.some((row) => (derivedBeside.has(row.name) && row.var !== undefined && row.var !== derivedBeside.get(row.name)) ||
|
|
471
|
+
row.body !== undefined ||
|
|
452
472
|
row.custom !== undefined ||
|
|
453
473
|
row.description !== undefined ||
|
|
454
474
|
row.target !== undefined ||
|
|
@@ -522,7 +542,7 @@ function emitTypedNode(chain, el, payload, type) {
|
|
|
522
542
|
const verbatimContext = contextRowsNeedSpellingOut(payload, type) ? emitContextRows(payload) : undefined;
|
|
523
543
|
const { outputVar, extras } = outputsOf(payload, id, type);
|
|
524
544
|
const defaultVar = spec.output ? `${id}_${identifierize(spec.output.name)}` : undefined;
|
|
525
|
-
const verbatimRows = outputRowsNeedSpellingOut(payload) ? emitOutputRows(payload) : undefined;
|
|
545
|
+
const verbatimRows = outputRowsNeedSpellingOut(payload, derivedExtraVars(id, type)) ? emitOutputRows(payload) : undefined;
|
|
526
546
|
chain.add(`.activity(${tsText(id)}, ${tsText(type)}${optionalArg(opts([
|
|
527
547
|
['name', el.name !== undefined ? tsText(el.name) : undefined],
|
|
528
548
|
// Only when it differs from the registry's own template, so an
|
|
@@ -2012,15 +2032,23 @@ rehome) {
|
|
|
2012
2032
|
// a bodied declaration through: ten `error<n>` declarations owned by boundary
|
|
2013
2033
|
// events in `claims-triage` were skipped as derived and then nobody derived them
|
|
2014
2034
|
// — they vanished from the recompiled process.
|
|
2015
|
-
|
|
2035
|
+
// A script task's default payload type is `BPMN.Variables` — the same type a
|
|
2036
|
+
// variable-assignment task carries, which derives nothing. The script task with
|
|
2037
|
+
// spelled-out rows is admitted by its element, not its type: those rows are the
|
|
2038
|
+
// declaration on the way back in (`implicitNodeVars`' script arm), so they are
|
|
2039
|
+
// skipped here too, or the source redeclares every one of them in `.vars()`.
|
|
2040
|
+
const spelledOutScriptRows = localName(el.$type) === 'scriptTask' && type === 'BPMN.Variables' && outputRowsNeedSpellingOut(payload);
|
|
2041
|
+
if (payload && type && (spelledOutScriptRows || (type !== 'BPMN.Variables' && typeDescribesElement(localName(el.$type), type)))) {
|
|
2016
2042
|
for (const row of payload.output ?? []) {
|
|
2017
2043
|
if (!row.var)
|
|
2018
2044
|
continue;
|
|
2019
|
-
// A script task's rows
|
|
2020
|
-
// `implicitNodeVars` derives
|
|
2021
|
-
//
|
|
2022
|
-
//
|
|
2023
|
-
|
|
2045
|
+
// A script task's rows come back as `outputs` when that form reproduces
|
|
2046
|
+
// them, and `implicitNodeVars` then derives their declarations from the
|
|
2047
|
+
// keys: id as the name, that arm's own wire type. Rows that need spelling
|
|
2048
|
+
// out (`emitScriptTask` writes `outputRows` exactly when this holds) are
|
|
2049
|
+
// declared through `rowDeclaration` on the way back in, like every other
|
|
2050
|
+
// typed node's, so they are matched the same way here.
|
|
2051
|
+
candidates.set(row.var, type === 'BPMN.ScriptTask' && !outputRowsNeedSpellingOut(payload)
|
|
2024
2052
|
? { owner: el.id, wire: 'string' }
|
|
2025
2053
|
: { owner: el.id, ...rowDeclaration({ name: row.name, type: row.type, schema: row.body }) });
|
|
2026
2054
|
}
|
package/dist/bpmn/intsvc.d.ts
CHANGED
|
@@ -35,11 +35,11 @@ type ExternalNode = Extract<BpmnNode, {
|
|
|
35
35
|
/**
|
|
36
36
|
* The extension type a Phase 3d node lowers to, WITHOUT consulting the library.
|
|
37
37
|
*
|
|
38
|
-
* The
|
|
39
|
-
* `
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
38
|
+
* The method the author called decides the placement — `.eventTrigger()` a start
|
|
39
|
+
* event, `.waitForEvent()` a receive task — so the type is known from the node alone,
|
|
40
|
+
* and the serializer can declare the node's output variable before it has resolved
|
|
41
|
+
* anything. The emitter uses this same value; the library only has to say the
|
|
42
|
+
* operation is a connector event.
|
|
43
43
|
*/
|
|
44
44
|
export declare function intsvcServiceType(n: EventNode | ExternalNode): string;
|
|
45
45
|
/**
|
|
@@ -55,11 +55,10 @@ export declare function intsvcOutputVar(n: EventNode | ExternalNode): string | u
|
|
|
55
55
|
/**
|
|
56
56
|
* A connector event node — `Intsvc.EventTrigger` or `Intsvc.WaitForEvent`.
|
|
57
57
|
*
|
|
58
|
-
* The extension type comes from the
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* receive task — a mismatch both offline validators accept.
|
|
58
|
+
* The extension type comes from the method the author called; the library only has
|
|
59
|
+
* to say the operation is a connector event (`runtime.serviceType` is one of the
|
|
60
|
+
* event types). A prepared descriptor carries the START form's runtime whichever
|
|
61
|
+
* method reaches it, so the placement, not the descriptor, decides the type.
|
|
63
62
|
*
|
|
64
63
|
* @param ctx - Serializer context: moddle, library, bindings, binding sink.
|
|
65
64
|
* @param n - The authored node.
|
package/dist/bpmn/intsvc.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createTypedNode, typedNodeOutputVar } from './typed-node.js';
|
|
2
2
|
import { connectorBindingRef } from './connector.js';
|
|
3
3
|
import { eventFilterJmes, eventFilterProblem, jmesString, jmesStringProblem, readEventFilter } from '../core/event-filters.js';
|
|
4
|
+
import { resolveEventWhere } from '../core/lookups.js';
|
|
4
5
|
/**
|
|
5
6
|
* Which extension type an external invocation lowers to.
|
|
6
7
|
*
|
|
@@ -19,11 +20,11 @@ const EVENT_SERVICE_TYPE = { trigger: 'Intsvc.EventTrigger', event: 'Intsvc.Wait
|
|
|
19
20
|
/**
|
|
20
21
|
* The extension type a Phase 3d node lowers to, WITHOUT consulting the library.
|
|
21
22
|
*
|
|
22
|
-
* The
|
|
23
|
-
* `
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
23
|
+
* The method the author called decides the placement — `.eventTrigger()` a start
|
|
24
|
+
* event, `.waitForEvent()` a receive task — so the type is known from the node alone,
|
|
25
|
+
* and the serializer can declare the node's output variable before it has resolved
|
|
26
|
+
* anything. The emitter uses this same value; the library only has to say the
|
|
27
|
+
* operation is a connector event.
|
|
27
28
|
*/
|
|
28
29
|
export function intsvcServiceType(n) {
|
|
29
30
|
return n.kind === 'connectorEvent'
|
|
@@ -45,11 +46,10 @@ export function intsvcOutputVar(n) {
|
|
|
45
46
|
/**
|
|
46
47
|
* A connector event node — `Intsvc.EventTrigger` or `Intsvc.WaitForEvent`.
|
|
47
48
|
*
|
|
48
|
-
* The extension type comes from the
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* receive task — a mismatch both offline validators accept.
|
|
49
|
+
* The extension type comes from the method the author called; the library only has
|
|
50
|
+
* to say the operation is a connector event (`runtime.serviceType` is one of the
|
|
51
|
+
* event types). A prepared descriptor carries the START form's runtime whichever
|
|
52
|
+
* method reaches it, so the placement, not the descriptor, decides the type.
|
|
53
53
|
*
|
|
54
54
|
* @param ctx - Serializer context: moddle, library, bindings, binding sink.
|
|
55
55
|
* @param n - The authored node.
|
|
@@ -67,14 +67,20 @@ export function createConnectorEvent(ctx, n) {
|
|
|
67
67
|
throw new Error(`connector event ${n.key}/${n.eventName} has no runtime.serviceType in the library; ` +
|
|
68
68
|
`it cannot be authored as a BPMN node.`);
|
|
69
69
|
}
|
|
70
|
-
// The
|
|
71
|
-
//
|
|
72
|
-
//
|
|
70
|
+
// The PLACEMENT decides the extension type — `.eventTrigger()` starts the
|
|
71
|
+
// process (`Intsvc.EventTrigger`), `.waitForEvent()` pauses it (`Intsvc.WaitForEvent`)
|
|
72
|
+
// — and the serializer has already declared this node's output variable from it
|
|
73
|
+
// (`intsvcServiceType`). The library's `runtime.serviceType` only has to agree
|
|
74
|
+
// that the operation IS a connector event: the platform ships one operation for
|
|
75
|
+
// both placements, the baked library carries the wait form under `.event.`, and
|
|
76
|
+
// `prepare` writes the same entry with the START form's runtime block (measured
|
|
77
|
+
// on `uipath-microsoft-outlook365/email-received`, 2026-10-16) — so reading the
|
|
78
|
+
// placement off that field refused every prepared `.waitForEvent()` with "use
|
|
79
|
+
// .eventTrigger() instead", while the unprepared same node compiled.
|
|
73
80
|
const expected = intsvcServiceType(n);
|
|
74
|
-
if (type !==
|
|
81
|
+
if (type !== 'Intsvc.EventTrigger' && type !== 'Intsvc.WaitForEvent') {
|
|
75
82
|
throw new Error(`connector event "${n.id}" was authored as a ${n.subscribe} (${expected}), but the library ` +
|
|
76
|
-
`describes ${n.key}/${n.eventName} as ${type}
|
|
77
|
-
`${type === 'Intsvc.EventTrigger' ? '.eventTrigger()' : '.waitForEvent()'} instead.`);
|
|
83
|
+
`describes ${n.key}/${n.eventName} as ${type}, which is not a connector event.`);
|
|
78
84
|
}
|
|
79
85
|
const conn = connectorBindingRef(ctx, n.connection, 'Connection', 'ConnectionId', `${rc.connectorKey} connection`);
|
|
80
86
|
const folder = connectorBindingRef(ctx, n.folder, 'Connection', 'folderKey', 'FolderKey', conn?.resourceKey);
|
|
@@ -108,10 +114,17 @@ export function createConnectorEvent(ctx, n) {
|
|
|
108
114
|
// The expression is the scope's equality clauses AND-joined with the authored
|
|
109
115
|
// `filters`, each lowered by the designer's own grammar (`core/event-filters.ts`) — the
|
|
110
116
|
// same lowering the Flow serializer does for `onEvent`/`waitForEvent`.
|
|
111
|
-
|
|
117
|
+
// A `lookup()` token in the scope is replaced by the id `prepare --resolve`
|
|
118
|
+
// recorded, as Flow's and Case's serializers do; an unrecorded one is refused
|
|
119
|
+
// with the command that records it. Stringified, a token was `[object Object]`.
|
|
120
|
+
const scope = resolveEventWhere(n.filter, ctx.library.lookupResolutions, `connector event "${n.id}"`);
|
|
121
|
+
inputs.body = eventFilterBody(n.id, scope, n.filters);
|
|
112
122
|
if (n.parameters !== undefined)
|
|
113
123
|
inputs.parameters = n.parameters;
|
|
114
|
-
return createTypedNode(ctx.m, n.id,
|
|
124
|
+
return createTypedNode(ctx.m, n.id,
|
|
125
|
+
// The placement's type, so a prepared `.event.` entry carrying the start form's
|
|
126
|
+
// runtime cannot turn a wait into a start event (see the check above).
|
|
127
|
+
expected, {
|
|
115
128
|
name: n.name ?? rc.entry.display?.label ?? n.eventName,
|
|
116
129
|
inputs,
|
|
117
130
|
outputVar: n.outputVar,
|