@uipath/maestro-builder-sdk 6.38.0 → 6.39.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/bpmn/check.js +237 -0
- package/package.json +2 -2
package/dist/bpmn/check.js
CHANGED
|
@@ -67,6 +67,7 @@ export function check(built, opts = {}) {
|
|
|
67
67
|
checkVariableOrder(built, diags);
|
|
68
68
|
checkDocumentIds(built, diags);
|
|
69
69
|
checkFolderBindings(built, diags);
|
|
70
|
+
checkIntsvcActivities(built, diags);
|
|
70
71
|
checkEntryPointId(built, diags);
|
|
71
72
|
checkMethodParentheses(built, diags);
|
|
72
73
|
checkConnectorRows(built, diags);
|
|
@@ -380,6 +381,242 @@ function checkFolderBindings(built, diags) {
|
|
|
380
381
|
}
|
|
381
382
|
}
|
|
382
383
|
}
|
|
384
|
+
/** The `method` vocabulary of the Integration Service activity catalog (`MethodName`). */
|
|
385
|
+
const INTSVC_METHODS = new Set(['GET', 'GETBYID', 'POST', 'PUT', 'PATCH', 'DELETE']);
|
|
386
|
+
/** The catalog's identity fields on an `Intsvc.ActivityExecution` task, in the order the message names them. */
|
|
387
|
+
const INTSVC_IDENTITY = ['connectorKey', 'objectName', 'operation', 'method', 'path'];
|
|
388
|
+
/**
|
|
389
|
+
* A hand-filled `Intsvc.ActivityExecution` task: what the catalog, the refresh and
|
|
390
|
+
* the runtime require of its context that `uip maestro bpmn validate` does not report.
|
|
391
|
+
*
|
|
392
|
+
* `.connector()` and `.dataService()` write this task from the library; an author
|
|
393
|
+
* who reaches for `.activity(id, 'Intsvc.ActivityExecution', …)` — the route the skill
|
|
394
|
+
* names for a connector the library lacks — fills the identity rows by hand from
|
|
395
|
+
* `uip is activities list <key>`, and the 2026-10-16 eval run measured what goes wrong
|
|
396
|
+
* (141 SDK-authored files, 109 such tasks): `objectName: 'N/A'` and `method: 'N/A'`
|
|
397
|
+
* copied from the catalog's GENERIC rows (5 tasks — `validate` reported them Valid, and
|
|
398
|
+
* they dispatch to nothing), an empty `operation` (5), `<METHOD_FROM_CLI>`-style template
|
|
399
|
+
* placeholders left in place, and a `connection` that `uip maestro bpmn refresh` then
|
|
400
|
+
* refused with `connection must reference a root binding as "=bindings.<id>"`.
|
|
401
|
+
*
|
|
402
|
+
* What the registry's own field notes say the rows are: `operation` is the catalog
|
|
403
|
+
* activity's `Name`, `objectName` its `ObjectName`, `method` its `MethodName`, `path` the
|
|
404
|
+
* request path `is resources describe` returns, and `connection` "points to a root binding
|
|
405
|
+
* for a Connection resource" with `folderKey` a second binding for the same key. Real
|
|
406
|
+
* exports agree on the shape and differ on one spelling: `operation` is the catalog Name
|
|
407
|
+
* on the canvas (`UpdateEntityRecordV2`) and the CRUD verb in 94 of 108 hand-written v1
|
|
408
|
+
* tasks (`Create`, `Retrieve`), so that field is held to "an identifier", not a list.
|
|
409
|
+
* `method` IS a closed list: GET, GETBYID, POST, PUT, PATCH, DELETE, measured over every
|
|
410
|
+
* task in both arms and the corpus.
|
|
411
|
+
*
|
|
412
|
+
* Errors where the task cannot dispatch or the platform refuses it; warnings where it
|
|
413
|
+
* deploys but the SDK cannot tell it is right.
|
|
414
|
+
*/
|
|
415
|
+
function checkIntsvcActivities(built, diags) {
|
|
416
|
+
// The bindings the compiled process will carry: the ones declared with `.binding()`,
|
|
417
|
+
// and the ones `serialize` synthesizes for every `.connector()`, `.eventTrigger()` /
|
|
418
|
+
// `.waitForEvent()` and external task from the symbolic names they read (ids equal
|
|
419
|
+
// to the names, `connector.ts` `bindingRef`) — a hand-filled task may reuse those.
|
|
420
|
+
const declared = new Map(built.bindings.map((b) => [b.id, { resource: String(b.resource ?? ''), propertyAttribute: String(b.propertyAttribute ?? '') }]));
|
|
421
|
+
for (const n of allNodes(built.nodes)) {
|
|
422
|
+
if (n.kind !== 'connector' && n.kind !== 'connectorEvent' && n.kind !== 'externalTask')
|
|
423
|
+
continue;
|
|
424
|
+
if (n.connection && !declared.has(n.connection))
|
|
425
|
+
declared.set(n.connection, { resource: 'Connection', propertyAttribute: 'ConnectionId' });
|
|
426
|
+
if (n.folder && !declared.has(n.folder))
|
|
427
|
+
declared.set(n.folder, { resource: 'Connection', propertyAttribute: 'folderKey' });
|
|
428
|
+
}
|
|
429
|
+
const expr = (v) => typeof v === 'string' && v.startsWith('=');
|
|
430
|
+
const text = (v) => (typeof v === 'string' ? v : v === undefined || v === null ? undefined : String(v));
|
|
431
|
+
for (const n of allNodes(built.nodes)) {
|
|
432
|
+
if (n.kind !== 'typed' || n.type !== 'Intsvc.ActivityExecution')
|
|
433
|
+
continue;
|
|
434
|
+
// `contextRows`, when given, REPLACE `context` at emission (typed-node.ts), so only
|
|
435
|
+
// the rows are the task's context then; a record field beside them is not emitted.
|
|
436
|
+
const ctx = {};
|
|
437
|
+
if (n.contextRows)
|
|
438
|
+
for (const row of n.contextRows)
|
|
439
|
+
ctx[row.name] = row.value ?? row.body ?? '';
|
|
440
|
+
else
|
|
441
|
+
Object.assign(ctx, n.context);
|
|
442
|
+
const where = n.id;
|
|
443
|
+
const key = text(ctx['connectorKey']) ?? '<connector-key>';
|
|
444
|
+
// 1. A catalog placeholder: `N/A` from a generic row, or a template's `<…>`.
|
|
445
|
+
for (const field of INTSVC_IDENTITY) {
|
|
446
|
+
const v = text(ctx[field]);
|
|
447
|
+
if (v === undefined)
|
|
448
|
+
continue;
|
|
449
|
+
const placeholder = /^n\/a$/i.test(v.trim()) || /^(<|<)[^<>]*(>|>)$/.test(v.trim());
|
|
450
|
+
if (!placeholder)
|
|
451
|
+
continue;
|
|
452
|
+
diags.push({
|
|
453
|
+
level: 'error',
|
|
454
|
+
code: 'INTSVC_CATALOG_PLACEHOLDER',
|
|
455
|
+
message: `"${n.id}" (Intsvc.ActivityExecution) has ${field}="${v}", which dispatches to nothing. ` +
|
|
456
|
+
(/^n\/a$/i.test(v.trim())
|
|
457
|
+
? `"N/A" is what \`uip is activities list ${key}\` prints on a GENERIC record verb (Create/Retrieve/Update/Delete Record), ` +
|
|
458
|
+
`whose object is chosen at design time: set objectName to an object from \`uip is resources list ${key} --connection-id <id>\`, ` +
|
|
459
|
+
`method to that verb's HTTP method, and path from \`uip is resources describe ${key} <object> --operation <Verb> --connection-id <id>\`. ` +
|
|
460
|
+
`For a curated activity, objectName and method are the row's ObjectName and MethodName.`
|
|
461
|
+
: `Replace the placeholder with the catalog's value (\`uip is activities list ${key} --output json\`: Name → operation, ObjectName → objectName, MethodName → method; ` +
|
|
462
|
+
`\`uip is resources describe\` → path).`) +
|
|
463
|
+
` Or let the library write the task: \`uip maestro registry prepare ${key} --action <name>\` then \`.connector()\`.`,
|
|
464
|
+
where,
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
// 2. `method` is the catalog's closed vocabulary.
|
|
468
|
+
const method = text(ctx['method']);
|
|
469
|
+
// An empty one is EMPTY_REQUIRED_FIELD's (the registry marks it required), as for `operation`.
|
|
470
|
+
if (method !== undefined && method.trim() !== '' && !expr(method) && !/^n\/a$/i.test(method.trim()) && !/^(<|<)/.test(method.trim()) && !INTSVC_METHODS.has(method.trim())) {
|
|
471
|
+
diags.push({
|
|
472
|
+
level: 'error',
|
|
473
|
+
code: 'INTSVC_METHOD_UNKNOWN',
|
|
474
|
+
message: `"${n.id}" (Intsvc.ActivityExecution) has method="${method}"; the activity catalog's MethodName is one of ` +
|
|
475
|
+
`${[...INTSVC_METHODS].join(', ')}` +
|
|
476
|
+
(INTSVC_METHODS.has(method.trim().toUpperCase()) ? ` — write it in upper case: "${method.trim().toUpperCase()}".` : `. Read it from \`uip is activities list ${key} --output json\` (MethodName).`),
|
|
477
|
+
where,
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
// 3. `operation` is the catalog activity's Name (or the CRUD verb hand-written exports use): an identifier.
|
|
481
|
+
const operation = text(ctx['operation']);
|
|
482
|
+
if (operation !== undefined && operation.trim() !== '' && !expr(operation) && !/^n\/a$/i.test(operation.trim()) && !/^(<|<)/.test(operation.trim())
|
|
483
|
+
&& !/^[A-Za-z][A-Za-z0-9_]*$/.test(operation.trim())) {
|
|
484
|
+
diags.push({
|
|
485
|
+
level: 'error',
|
|
486
|
+
code: 'INTSVC_OPERATION_NOT_IDENTIFIER',
|
|
487
|
+
message: `"${n.id}" (Intsvc.ActivityExecution) has operation="${operation}"; the registry defines it as the activity catalog's Name ` +
|
|
488
|
+
`(\`uip is activities list ${key}\`: CreateTestSet, QueryEntityRecordsCurated) — an identifier, not a REST slug or a label.`,
|
|
489
|
+
where,
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
// 4. `objectName` spelled as a REST path: validates, dispatches to nothing. Only a
|
|
493
|
+
// `/` says so — `::` is in curated ObjectNames too (`v2::webSearch`, airdk).
|
|
494
|
+
const objectName = text(ctx['objectName']);
|
|
495
|
+
if (objectName !== undefined && !expr(objectName) && objectName.includes('/')) {
|
|
496
|
+
diags.push({
|
|
497
|
+
level: 'warning',
|
|
498
|
+
code: 'INTSVC_OBJECT_NAME_SLUG',
|
|
499
|
+
message: `"${n.id}" (Intsvc.ActivityExecution) has objectName="${objectName}", which reads as a REST path, not an object. The platform matches ` +
|
|
500
|
+
`on the catalog's ObjectName (\`uip is activities list ${key}\`: TestSet, UploadAttachment, v2::webSearch); a path validates and then dispatches to nothing.`,
|
|
501
|
+
where,
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
// 5. The path's placeholders each need a `target: 'path'` input row of that name.
|
|
505
|
+
const path = text(ctx['path']);
|
|
506
|
+
if (path !== undefined && !expr(path)) {
|
|
507
|
+
if (!path.startsWith('/')) {
|
|
508
|
+
diags.push({
|
|
509
|
+
level: 'warning',
|
|
510
|
+
code: 'INTSVC_PATH_NOT_ABSOLUTE',
|
|
511
|
+
message: `"${n.id}" (Intsvc.ActivityExecution) has path="${path}"; the request path \`is resources describe\` returns starts with "/" (e.g. "/v2/{ProjectId}/testsets").`,
|
|
512
|
+
where,
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
const params = [...path.matchAll(/\{([^{}]+)\}/g)].map((m) => m[1]);
|
|
516
|
+
if (params.length > 0) {
|
|
517
|
+
// Bound by a row of that name targeting the path, or by a path AGGREGATE —
|
|
518
|
+
// `inputs: { pathParameters: { ProjectId } }` emits one `target="pathParameters"`
|
|
519
|
+
// row holding every placeholder (an expression-valued aggregate binds them all).
|
|
520
|
+
const PATH_TARGETS = new Set(['path', 'pathvariable', 'pathparameters']);
|
|
521
|
+
const bound = new Set();
|
|
522
|
+
let aggregateExpression = false;
|
|
523
|
+
const aggregate = (value) => {
|
|
524
|
+
if (expr(value))
|
|
525
|
+
aggregateExpression = true;
|
|
526
|
+
else if (value && typeof value === 'object')
|
|
527
|
+
for (const k of Object.keys(value))
|
|
528
|
+
bound.add(k);
|
|
529
|
+
};
|
|
530
|
+
for (const r of n.inputRows ?? []) {
|
|
531
|
+
const target = (r.target ?? '').toLowerCase();
|
|
532
|
+
if (target === 'pathparameters' || target === 'pathvariable' || PATH_TARGETS.has(r.name.toLowerCase()))
|
|
533
|
+
aggregate(r.body ?? r.value);
|
|
534
|
+
if (PATH_TARGETS.has(target))
|
|
535
|
+
bound.add(r.name);
|
|
536
|
+
}
|
|
537
|
+
if (!n.inputRows)
|
|
538
|
+
for (const key of ['pathParameters', 'pathVariable'])
|
|
539
|
+
if (key in (n.inputs ?? {}))
|
|
540
|
+
aggregate(n.inputs[key]);
|
|
541
|
+
const unbound = aggregateExpression ? [] : params.filter((p) => !bound.has(p));
|
|
542
|
+
if (unbound.length > 0) {
|
|
543
|
+
diags.push({
|
|
544
|
+
level: 'warning',
|
|
545
|
+
code: 'INTSVC_PATH_PARAM_UNBOUND',
|
|
546
|
+
message: `"${n.id}" (Intsvc.ActivityExecution) path "${path}" has placeholder(s) ${unbound.map((p) => `{${p}}`).join(', ')} with no input row ` +
|
|
547
|
+
`of that name targeting the path. Write \`inputs: { pathParameters: { ${unbound[0]}: '…' } }\` (the other fields stay ` +
|
|
548
|
+
`beside it), or an \`inputRows\` row \`{ name: '${unbound[0]}', type: 'string', value: '…', target: 'path' }\` — a bare ` +
|
|
549
|
+
`\`inputs: { ${unbound[0]} }\` lands in the body and leaves the placeholder unfilled.`,
|
|
550
|
+
where,
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
// 6. The connection and folder rows: binding references to declared Connection bindings.
|
|
556
|
+
const bindingRow = (field, attribute) => {
|
|
557
|
+
const v = text(ctx[field]);
|
|
558
|
+
if (v === undefined || v.trim() === '')
|
|
559
|
+
return;
|
|
560
|
+
if (!v.startsWith('=bindings.')) {
|
|
561
|
+
// A literal gets the binding to declare with that value; any other
|
|
562
|
+
// expression (`=vars.connId`) is told what refresh needs instead — a
|
|
563
|
+
// connection is resolved from a root binding, never computed at run time.
|
|
564
|
+
const label = field === 'connection' ? 'conn' : 'folder';
|
|
565
|
+
const refusal = `\`uip maestro bpmn refresh\` refuses it with \`${field} must reference a root binding as "=bindings.<id>"\``;
|
|
566
|
+
diags.push({
|
|
567
|
+
level: 'error',
|
|
568
|
+
code: 'INTSVC_CONNECTION_NOT_BINDING',
|
|
569
|
+
message: expr(v)
|
|
570
|
+
? `"${n.id}" (Intsvc.ActivityExecution) has ${field}="${v}", an expression; ${refusal}. A ${field} is resolved from a root binding, ` +
|
|
571
|
+
`not computed: declare \`.binding('${label}', { resource: 'Connection', propertyAttribute: '${attribute}', value: '<${field === 'connection' ? 'connection id' : 'folder key'}>' })\` ` +
|
|
572
|
+
`and write ${field}: '=bindings.${label}'.`
|
|
573
|
+
: `"${n.id}" (Intsvc.ActivityExecution) has ${field}="${v}", a literal; ${refusal}. Declare ` +
|
|
574
|
+
`\`.binding('${label}', { resource: 'Connection', propertyAttribute: '${attribute}', value: '${v}' })\` ` +
|
|
575
|
+
`and write ${field}: '=bindings.${label}'.`,
|
|
576
|
+
where,
|
|
577
|
+
});
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
const id = v.slice('=bindings.'.length);
|
|
581
|
+
const b = declared.get(id);
|
|
582
|
+
if (!b) {
|
|
583
|
+
diags.push({
|
|
584
|
+
level: 'error',
|
|
585
|
+
code: 'INTSVC_BINDING_UNDECLARED',
|
|
586
|
+
message: `"${n.id}" (Intsvc.ActivityExecution) reads ${field}="${v}" but the process declares no binding "${id}" ` +
|
|
587
|
+
`(\`uip maestro bpmn validate\` reports MISSING_BINDING). Declare it at the root: ` +
|
|
588
|
+
`\`.binding('${id}', { resource: 'Connection', propertyAttribute: '${attribute}', value: '<${field === 'connection' ? 'connection id' : 'folder key'}>' })\`` +
|
|
589
|
+
(field === 'folderKey' ? ' — keyed by its connection; the builder pairs it with the connection this task reads.' : '.'),
|
|
590
|
+
where,
|
|
591
|
+
});
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
const attr = b.propertyAttribute;
|
|
595
|
+
const resource = b.resource;
|
|
596
|
+
if (attr.toLowerCase() !== attribute.toLowerCase() || resource.toLowerCase() !== 'connection') {
|
|
597
|
+
diags.push({
|
|
598
|
+
level: 'error',
|
|
599
|
+
code: 'INTSVC_BINDING_MISMATCH',
|
|
600
|
+
message: `"${n.id}" (Intsvc.ActivityExecution) reads ${field}="${v}", but binding "${id}" is resource "${resource}" / ` +
|
|
601
|
+
`propertyAttribute "${attr}"; the ${field} row needs resource "Connection" / propertyAttribute "${attribute}".`,
|
|
602
|
+
where,
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
bindingRow('connection', 'ConnectionId');
|
|
607
|
+
bindingRow('folderKey', 'folderKey');
|
|
608
|
+
if (text(ctx['connection']) === undefined) {
|
|
609
|
+
diags.push({
|
|
610
|
+
level: 'warning',
|
|
611
|
+
code: 'INTSVC_CONNECTION_MISSING',
|
|
612
|
+
message: `"${n.id}" (Intsvc.ActivityExecution) names no connection. The registry says to omit \`connection\` and \`folderKey\` only ` +
|
|
613
|
+
`when the activity uses none; a connector activity almost always does. Declare a Connection binding and read it as ` +
|
|
614
|
+
`connection: '=bindings.<id>' (\`uip is connections list ${key} --all-folders --output json\` lists the ids).`,
|
|
615
|
+
where,
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
383
620
|
/**
|
|
384
621
|
* Every `id` in the document must be unique — including across KINDS.
|
|
385
622
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uipath/maestro-builder-sdk",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.39.0",
|
|
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",
|
|
@@ -93,5 +93,5 @@
|
|
|
93
93
|
"@types/node": "^22.7.0",
|
|
94
94
|
"esbuild": "^0.28.1"
|
|
95
95
|
},
|
|
96
|
-
"gitref": "
|
|
96
|
+
"gitref": "df5d45c8b0eb17d5a82da37146e5b8130be03fab"
|
|
97
97
|
}
|