@uipath/maestro-builder-sdk 6.33.2 → 6.35.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 +103 -97
- package/dist/api-members.md +403 -383
- package/dist/case/case-sdk.d.ts +52 -1
- package/dist/case/case-sdk.js +37 -2
- package/dist/case/check.js +30 -0
- package/dist/case/compile-cli.js +6 -1
- package/dist/case/decompile.js +50 -3
- package/dist/case/format-profile.d.ts +1 -1
- package/dist/case/format-profile.js +7 -3
- package/dist/case/serialize.js +93 -9
- package/dist/check.js +6 -2
- package/dist/core/actions.d.ts +103 -18
- package/dist/core/form-field-type.d.ts +77 -29
- package/dist/core/form-field-type.js +264 -46
- package/dist/decompile.js +41 -12
- package/dist/flow-node-type.d.ts +6 -0
- package/dist/nodes/hitl/flow/check.js +8 -4
- package/dist/nodes/hitl/flow/node-type.js +20 -20
- package/dist/schema-version.js +6 -1
- package/dist/serialize.js +7 -4
- package/package.json +2 -2
package/dist/case/case-sdk.d.ts
CHANGED
|
@@ -372,7 +372,7 @@ export interface BuiltCaseExitCondition {
|
|
|
372
372
|
marksCaseComplete?: boolean;
|
|
373
373
|
rules: CaseRule[][];
|
|
374
374
|
}
|
|
375
|
-
export type TaskKind = 'process' | 'agent' | 'rpa' | 'api-workflow' | 'case-management' | 'flow-process' | 'external-agent' | 'external-workflow' | 'action' | 'connector' | 'wait-for-timer' | 'wait-for-connector';
|
|
375
|
+
export type TaskKind = 'process' | 'agent' | 'rpa' | 'api-workflow' | 'case-management' | 'flow-process' | 'business-rule' | 'external-agent' | 'external-workflow' | 'action' | 'connector' | 'wait-for-timer' | 'wait-for-connector';
|
|
376
376
|
/** Published-resource task kinds that the Case schema permits as unresolved skeletons. */
|
|
377
377
|
export type UnresolvedReferenceTaskKind = 'process' | 'agent' | 'rpa' | 'api-workflow' | 'case-management';
|
|
378
378
|
/**
|
|
@@ -428,6 +428,25 @@ export interface TaskRef {
|
|
|
428
428
|
name: string;
|
|
429
429
|
folderPath: string;
|
|
430
430
|
}
|
|
431
|
+
/**
|
|
432
|
+
* Options for {@link TaskBuilder.businessRule}.
|
|
433
|
+
*/
|
|
434
|
+
export interface BusinessRuleOptions {
|
|
435
|
+
/** The Orchestrator folder the rule is deployed in, e.g. `Shared`. */
|
|
436
|
+
folder: string;
|
|
437
|
+
/**
|
|
438
|
+
* The rule's whole result schema, as `uip maestro case tasks describe --type
|
|
439
|
+
* business-rule` returns it on the `output` row (`_jsonSchema`): one object
|
|
440
|
+
* keyed by decision name, each holding that decision's columns. Optional; it
|
|
441
|
+
* is copied onto the `output` row so the designer can show the columns.
|
|
442
|
+
*/
|
|
443
|
+
resultSchema?: unknown;
|
|
444
|
+
}
|
|
445
|
+
/** Normalized business-rule configuration stored on a built task. */
|
|
446
|
+
export interface BusinessRuleSpecData {
|
|
447
|
+
/** The rule's decision-keyed result schema, when the author supplied it. */
|
|
448
|
+
resultSchema?: unknown;
|
|
449
|
+
}
|
|
431
450
|
/**
|
|
432
451
|
* Action-task recipient type. `2` = a single user by email — the only value the
|
|
433
452
|
* platform corpus exercises for case action tasks; `0`/`1`/`3` are reserved for
|
|
@@ -520,6 +539,8 @@ export interface BuiltTask {
|
|
|
520
539
|
name: string;
|
|
521
540
|
kind: TaskKind;
|
|
522
541
|
ref?: TaskRef;
|
|
542
|
+
/** business-rule task: the rule's decision-keyed result schema, when known. */
|
|
543
|
+
businessRule?: BusinessRuleSpecData;
|
|
523
544
|
action?: ActionSpecData;
|
|
524
545
|
connector?: ConnectorSpecData;
|
|
525
546
|
/** Descriptor-resolved external agent/workflow configuration. */
|
|
@@ -718,6 +739,7 @@ declare class TaskBuilder {
|
|
|
718
739
|
private readonly _name;
|
|
719
740
|
private _kind?;
|
|
720
741
|
private _ref?;
|
|
742
|
+
private _businessRule?;
|
|
721
743
|
private _action?;
|
|
722
744
|
private _connector?;
|
|
723
745
|
private _external?;
|
|
@@ -808,6 +830,35 @@ declare class TaskBuilder {
|
|
|
808
830
|
flowProcess(name: string, opts?: {
|
|
809
831
|
folder?: string;
|
|
810
832
|
}): this;
|
|
833
|
+
/**
|
|
834
|
+
* Evaluate a deployed **business rule** (a DMN decision package) and read its
|
|
835
|
+
* decisions back.
|
|
836
|
+
*
|
|
837
|
+
* @remarks
|
|
838
|
+
* Emits a `business-rule` task at `version: 'v3'` (below v3 the engine cannot
|
|
839
|
+
* run the `.uirule` package a BusinessRules project deploys). The rule is named
|
|
840
|
+
* through a `name` + `folderPath` pair of root bindings with
|
|
841
|
+
* `resource: 'BusinessRule'` and `resourceKey: '<folder>.<name>'`, the shape
|
|
842
|
+
* Studio Web and `uip maestro case bindings sync` write.
|
|
843
|
+
*
|
|
844
|
+
* The task always carries the two outputs every business-rule task has: `output`
|
|
845
|
+
* (`source: '=result'`, the whole decision-keyed result) and `Error`. To store
|
|
846
|
+
* one decision column in a case variable, add an extract with `.outputs()`
|
|
847
|
+
* whose source names the decision and column directly — `=<Decision>.<column>`,
|
|
848
|
+
* never `=result.…` or `=output.…`:
|
|
849
|
+
*
|
|
850
|
+
* ```ts
|
|
851
|
+
* .task('Score Credit Risk', t => t
|
|
852
|
+
* .businessRule('CreditRisk', { folder: 'Shared' })
|
|
853
|
+
* .inputs({ creditScore: { value: '=vars.creditScore', type: 'number' } })
|
|
854
|
+
* .outputs({ riskBand: { source: '=RiskDecision.riskBand', type: 'string' } }))
|
|
855
|
+
* ```
|
|
856
|
+
*
|
|
857
|
+
* @param name - The deployed rule's name.
|
|
858
|
+
* @param opts - `folder` (required) and the optional `resultSchema`.
|
|
859
|
+
* @returns This builder, so calls chain.
|
|
860
|
+
*/
|
|
861
|
+
businessRule(name: string, opts: BusinessRuleOptions): this;
|
|
811
862
|
private external;
|
|
812
863
|
/**
|
|
813
864
|
* Invoke an external agent through its generated Integration Service descriptor.
|
package/dist/case/case-sdk.js
CHANGED
|
@@ -160,6 +160,7 @@ class TaskBuilder {
|
|
|
160
160
|
_name;
|
|
161
161
|
_kind;
|
|
162
162
|
_ref;
|
|
163
|
+
_businessRule;
|
|
163
164
|
_action;
|
|
164
165
|
_connector;
|
|
165
166
|
_external;
|
|
@@ -263,6 +264,39 @@ class TaskBuilder {
|
|
|
263
264
|
flowProcess(name, opts) {
|
|
264
265
|
return this.reference('flow-process', name, opts);
|
|
265
266
|
}
|
|
267
|
+
/**
|
|
268
|
+
* Evaluate a deployed **business rule** (a DMN decision package) and read its
|
|
269
|
+
* decisions back.
|
|
270
|
+
*
|
|
271
|
+
* @remarks
|
|
272
|
+
* Emits a `business-rule` task at `version: 'v3'` (below v3 the engine cannot
|
|
273
|
+
* run the `.uirule` package a BusinessRules project deploys). The rule is named
|
|
274
|
+
* through a `name` + `folderPath` pair of root bindings with
|
|
275
|
+
* `resource: 'BusinessRule'` and `resourceKey: '<folder>.<name>'`, the shape
|
|
276
|
+
* Studio Web and `uip maestro case bindings sync` write.
|
|
277
|
+
*
|
|
278
|
+
* The task always carries the two outputs every business-rule task has: `output`
|
|
279
|
+
* (`source: '=result'`, the whole decision-keyed result) and `Error`. To store
|
|
280
|
+
* one decision column in a case variable, add an extract with `.outputs()`
|
|
281
|
+
* whose source names the decision and column directly — `=<Decision>.<column>`,
|
|
282
|
+
* never `=result.…` or `=output.…`:
|
|
283
|
+
*
|
|
284
|
+
* ```ts
|
|
285
|
+
* .task('Score Credit Risk', t => t
|
|
286
|
+
* .businessRule('CreditRisk', { folder: 'Shared' })
|
|
287
|
+
* .inputs({ creditScore: { value: '=vars.creditScore', type: 'number' } })
|
|
288
|
+
* .outputs({ riskBand: { source: '=RiskDecision.riskBand', type: 'string' } }))
|
|
289
|
+
* ```
|
|
290
|
+
*
|
|
291
|
+
* @param name - The deployed rule's name.
|
|
292
|
+
* @param opts - `folder` (required) and the optional `resultSchema`.
|
|
293
|
+
* @returns This builder, so calls chain.
|
|
294
|
+
*/
|
|
295
|
+
businessRule(name, opts) {
|
|
296
|
+
this.reference('business-rule', name, { folder: opts.folder });
|
|
297
|
+
this._businessRule = opts.resultSchema === undefined ? {} : { resultSchema: opts.resultSchema };
|
|
298
|
+
return this;
|
|
299
|
+
}
|
|
266
300
|
external(kind, descriptor, opts) {
|
|
267
301
|
this.setKind(kind);
|
|
268
302
|
this._external = {
|
|
@@ -448,15 +482,16 @@ class TaskBuilder {
|
|
|
448
482
|
// Task I/O binding is reference-mode only — process/agent/rpa/api-workflow,
|
|
449
483
|
// case-management, and flow-process. Connector tasks derive their own
|
|
450
484
|
// inputs/outputs from the operation schema.
|
|
451
|
-
const REF_IO = new Set(['process', 'agent', 'rpa', 'api-workflow', 'case-management', 'flow-process']);
|
|
485
|
+
const REF_IO = new Set(['process', 'agent', 'rpa', 'api-workflow', 'case-management', 'flow-process', 'business-rule']);
|
|
452
486
|
if ((this._inputs || this._outputs) && !REF_IO.has(this._kind))
|
|
453
|
-
throw new Error(`task "${this._name}": .inputs()/.outputs() are only supported on reference-mode tasks (process/agent/rpa/api-workflow/case-management/flow-process), not "${this._kind}".`);
|
|
487
|
+
throw new Error(`task "${this._name}": .inputs()/.outputs() are only supported on reference-mode tasks (process/agent/rpa/api-workflow/case-management/flow-process/business-rule), not "${this._kind}".`);
|
|
454
488
|
if (!this._ref && (this._inputs || this._outputs))
|
|
455
489
|
throw new Error(`task "${this._name}": an unresolved reference task cannot declare inputs or outputs.`);
|
|
456
490
|
return {
|
|
457
491
|
name: this._name,
|
|
458
492
|
kind: this._kind,
|
|
459
493
|
ref: this._ref,
|
|
494
|
+
businessRule: this._businessRule,
|
|
460
495
|
action: this._action,
|
|
461
496
|
connector: this._connector,
|
|
462
497
|
external: this._external,
|
package/dist/case/check.js
CHANGED
|
@@ -483,6 +483,36 @@ function checkStage(stage, diags, refStage, refPlacement) {
|
|
|
483
483
|
message: `Task "${t.name}" binding "${'var' in invalidBinding ? invalidBinding.var : invalidBinding.name}" has unsupported type ${JSON.stringify(invalidBinding.type)}. Use a type name such as "string" or "jsonSchema"; jsonSchema(...) is only for declaring a structured case variable.`,
|
|
484
484
|
where: t.name,
|
|
485
485
|
});
|
|
486
|
+
if (t.kind === 'business-rule') {
|
|
487
|
+
if (!t.ref || !t.ref.name.trim() || !t.ref.folderPath.trim())
|
|
488
|
+
diags.push({
|
|
489
|
+
level: 'error',
|
|
490
|
+
code: 'BUSINESS_RULE_EMPTY_DATA',
|
|
491
|
+
message: `Business-rule task "${t.name}" must name its deployed rule and the Orchestrator folder it is deployed in: .businessRule('<rule>', { folder: '<folder>' }).`,
|
|
492
|
+
where: t.name,
|
|
493
|
+
});
|
|
494
|
+
for (const o of t.outputs ?? []) {
|
|
495
|
+
const wrapped = /^=\s*(result|output)\./.exec(o.source);
|
|
496
|
+
if (wrapped)
|
|
497
|
+
diags.push({
|
|
498
|
+
level: 'error',
|
|
499
|
+
code: 'BUSINESS_RULE_OUTPUT_SOURCE',
|
|
500
|
+
message: `Business-rule task "${t.name}" output "${o.var}" reads "${o.source}". An extract reads a field of the rule's result directly — write "=${o.source.replace(/^=\s*(result|output)\./, '')}" (\`=<Decision>.<column>\`). The whole result is already the task's own \`output\` row (\`=result\`); \`=${wrapped[1]}.…\` names a field the result does not have.`,
|
|
501
|
+
where: t.name,
|
|
502
|
+
});
|
|
503
|
+
// An extract row is NAMED after its source's last segment (`emitTaskOutput`),
|
|
504
|
+
// so `=Decision.output` writes a second row named `output` beside the
|
|
505
|
+
// task's own. The case variable it stores into collides with nothing.
|
|
506
|
+
const rowName = o.source.replace(/^=/, '').split('.').pop();
|
|
507
|
+
if (rowName === 'output' || rowName === 'Error')
|
|
508
|
+
diags.push({
|
|
509
|
+
level: 'error',
|
|
510
|
+
code: 'BUSINESS_RULE_OUTPUT_NAME',
|
|
511
|
+
message: `Business-rule task "${t.name}" output "${o.var}" reads "${o.source}", which writes an output row named "${rowName}" — the name of the task's own ${rowName === 'output' ? '`output` row (the whole result, `=result`)' : '`Error` row (`=Error`)'}. Two rows with one name are ambiguous to the designer; rename the decision column.`,
|
|
512
|
+
where: t.name,
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
}
|
|
486
516
|
if (t.kind === 'flow-process' && (!t.ref || !t.ref.name.trim()))
|
|
487
517
|
diags.push({
|
|
488
518
|
level: 'error',
|
package/dist/case/compile-cli.js
CHANGED
|
@@ -364,7 +364,12 @@ function syncBindingsV2(caseplan, outPath) {
|
|
|
364
364
|
resource: group.resource,
|
|
365
365
|
key: group.key,
|
|
366
366
|
value: group.value,
|
|
367
|
-
|
|
367
|
+
// A BusinessRule entry carries the bindings version deploy needs to
|
|
368
|
+
// override its folder — the metadata `uip maestro case bindings sync`
|
|
369
|
+
// writes for it (no subType).
|
|
370
|
+
metadata: group.subType
|
|
371
|
+
? { subType: group.subType }
|
|
372
|
+
: group.resource === 'BusinessRule' ? { BindingsVersion: '2.2' } : {},
|
|
368
373
|
});
|
|
369
374
|
}
|
|
370
375
|
resources.push(...connectionResources);
|
package/dist/case/decompile.js
CHANGED
|
@@ -579,6 +579,8 @@ function emitTaskKind(task, refs, library) {
|
|
|
579
579
|
const folder = folderPath ? `, ${opts([['folder', tsLiteral(folderPath)]])}` : '';
|
|
580
580
|
return `.${method}(${tsLiteral(name ?? '')}${folder})`;
|
|
581
581
|
}
|
|
582
|
+
if (isAuthorableBusinessRule(task))
|
|
583
|
+
return businessRuleCall(task, refs);
|
|
582
584
|
if (kind === 'action') {
|
|
583
585
|
let recipient;
|
|
584
586
|
if (data.recipient) {
|
|
@@ -616,7 +618,12 @@ function emitTaskKind(task, refs, library) {
|
|
|
616
618
|
const spec = waitConnectorSpec(data, `wait-for-connector task "${task.displayName ?? task.id ?? '?'}"`, refs);
|
|
617
619
|
return spec === undefined ? '.waitForConnector()' : `.waitForConnector(${spec})`;
|
|
618
620
|
}
|
|
619
|
-
const message =
|
|
621
|
+
const message = kind === 'business-rule'
|
|
622
|
+
// The converter reads a missing version as v2, whose result shape differs
|
|
623
|
+
// from v3's decision-keyed one; recompiling it as v3 would change what its
|
|
624
|
+
// extracts read. Keep the task as data instead.
|
|
625
|
+
? `business-rule task "${task.displayName ?? task.id ?? '?'}" has ${data.version === undefined ? 'no version (the converter reads it as v2)' : `version "${String(data.version)}"`}; only v3 business-rule tasks are authorable.`
|
|
626
|
+
: `unsupported task kind "${kind}" for task "${task.displayName ?? task.id ?? '?'}".`;
|
|
620
627
|
diagnose(refs, {
|
|
621
628
|
code: 'UNSUPPORTED_TASK_KIND',
|
|
622
629
|
message,
|
|
@@ -624,6 +631,29 @@ function emitTaskKind(task, refs, library) {
|
|
|
624
631
|
});
|
|
625
632
|
return `.__UNRESOLVED_TASK_KIND__(${tsLiteral(kind)}, ${tsLiteral(data)})`;
|
|
626
633
|
}
|
|
634
|
+
/** A business-rule task `.businessRule()` can rebuild: v3 only. */
|
|
635
|
+
function isAuthorableBusinessRule(task) {
|
|
636
|
+
return task.type === 'business-rule' && task.data?.version === 'v3';
|
|
637
|
+
}
|
|
638
|
+
/** The two outputs every business-rule task carries, which `.businessRule()` re-emits itself. */
|
|
639
|
+
function isBusinessRuleOwnOutput(row) {
|
|
640
|
+
return (row.name === 'output' && row.source === '=result') || (row.name === 'Error' && row.source === '=Error');
|
|
641
|
+
}
|
|
642
|
+
function businessRuleCall(task, refs) {
|
|
643
|
+
const data = task.data ?? {};
|
|
644
|
+
const owner = `business-rule task "${task.displayName ?? task.id ?? '?'}"`;
|
|
645
|
+
const name = bindingDefault(data.name, refs);
|
|
646
|
+
const folderPath = bindingDefault(data.folderPath, refs);
|
|
647
|
+
if (typeof name !== 'string' || !name || typeof folderPath !== 'string')
|
|
648
|
+
throw new Error(`${owner} must name its rule and folder (data.name / data.folderPath).`);
|
|
649
|
+
const outputRow = (Array.isArray(data.outputs) ? data.outputs : [])
|
|
650
|
+
.find((row) => row.name === 'output' && row.source === '=result');
|
|
651
|
+
const resultSchema = outputRow?._jsonSchema;
|
|
652
|
+
return `.businessRule(${tsLiteral(name)}, ${opts([
|
|
653
|
+
['folder', tsLiteral(folderPath)],
|
|
654
|
+
['resultSchema', resultSchema !== undefined ? tsLiteral(resultSchema) : undefined],
|
|
655
|
+
])})`;
|
|
656
|
+
}
|
|
627
657
|
function ioArg(rows, kind) {
|
|
628
658
|
const valueField = kind === 'inputs' ? 'value' : 'source';
|
|
629
659
|
const entries = rows.map((row) => {
|
|
@@ -647,6 +677,14 @@ function emitTask(out, depth, task, lane, refs, library) {
|
|
|
647
677
|
if (Array.isArray(data.outputs) && data.outputs.length)
|
|
648
678
|
out.line(inner, `.outputs(${ioArg(data.outputs, 'outputs')})`);
|
|
649
679
|
}
|
|
680
|
+
if (isAuthorableBusinessRule(task)) {
|
|
681
|
+
const data = task.data ?? {};
|
|
682
|
+
const extracts = (Array.isArray(data.outputs) ? data.outputs : []).filter((row) => !isBusinessRuleOwnOutput(row));
|
|
683
|
+
if (Array.isArray(data.inputs) && data.inputs.length)
|
|
684
|
+
out.line(inner, `.inputs(${ioArg(data.inputs, 'inputs')})`);
|
|
685
|
+
if (extracts.length)
|
|
686
|
+
out.line(inner, `.outputs(${ioArg(extracts, 'outputs')})`);
|
|
687
|
+
}
|
|
650
688
|
if (task.isRequired !== undefined)
|
|
651
689
|
out.line(inner, task.isRequired ? '.required()' : '.required(false)');
|
|
652
690
|
if (task.shouldRunOnlyOnce !== undefined)
|
|
@@ -749,11 +787,20 @@ function producedVariableNames(plan) {
|
|
|
749
787
|
if (!isStage(node))
|
|
750
788
|
continue;
|
|
751
789
|
for (const lane of node.data?.tasks ?? [])
|
|
752
|
-
for (const task of lane)
|
|
753
|
-
if (ownEntry(TASK_KIND_METHOD, task.type))
|
|
790
|
+
for (const task of lane) {
|
|
791
|
+
if (ownEntry(TASK_KIND_METHOD, task.type)) {
|
|
754
792
|
for (const row of task.data?.outputs ?? [])
|
|
755
793
|
if (row.var)
|
|
756
794
|
produced.add(row.var);
|
|
795
|
+
}
|
|
796
|
+
else if (isAuthorableBusinessRule(task)) {
|
|
797
|
+
// A business-rule extract registers its case variable on recompile, as
|
|
798
|
+
// a reference-task output does; the task's own rows do not.
|
|
799
|
+
for (const row of task.data?.outputs ?? [])
|
|
800
|
+
if (row.var && !isBusinessRuleOwnOutput(row))
|
|
801
|
+
produced.add(row.var);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
757
804
|
}
|
|
758
805
|
return produced;
|
|
759
806
|
}
|
|
@@ -40,7 +40,7 @@ export declare const CASE_FORMAT_PROFILE: Readonly<CaseFormatProfile>;
|
|
|
40
40
|
* Which Case JSON schema versions `.schemaVersion()` may declare.
|
|
41
41
|
*
|
|
42
42
|
* ONE, and that is the honest answer rather than a placeholder. Case is a
|
|
43
|
-
* closed, whole-document schema: `@uipath/case-schema` ships a chain of
|
|
43
|
+
* closed, whole-document schema: `@uipath/case-schema` ships a chain of
|
|
44
44
|
* grammars (`case-schema-census.json`'s `versionChain`), each with its own
|
|
45
45
|
* forward migration, and a version is a claim that the entire document matches
|
|
46
46
|
* that grammar. This SDK emits the V30 grammar natively — the V30 rule schemas
|
|
@@ -28,16 +28,20 @@ export const CASE_FORMAT_PROFILE = {
|
|
|
28
28
|
{ featureFlag: 'canvas-useExplicitEntryExitConditions', fromVersion: '12.0.0' },
|
|
29
29
|
{ featureFlag: 'canvas-caseUnifiedSchema', fromVersion: '20.0.0' },
|
|
30
30
|
],
|
|
31
|
+
// The validation pin (the `@uipath/case-schema` devDependency in
|
|
32
|
+
// typescript/package.json; the census records it as `generatedFrom.version`)
|
|
33
|
+
// and the newest Case JSON version that package reads. The writer stays at V30 (`writeVersion`): V31+ is read
|
|
34
|
+
// and migrated by the platform, not emitted here.
|
|
31
35
|
validationCeiling: {
|
|
32
|
-
caseSchemaVersion: '0.
|
|
33
|
-
formatVersion: '
|
|
36
|
+
caseSchemaVersion: '0.1173.0',
|
|
37
|
+
formatVersion: '32.0.4',
|
|
34
38
|
},
|
|
35
39
|
};
|
|
36
40
|
/**
|
|
37
41
|
* Which Case JSON schema versions `.schemaVersion()` may declare.
|
|
38
42
|
*
|
|
39
43
|
* ONE, and that is the honest answer rather than a placeholder. Case is a
|
|
40
|
-
* closed, whole-document schema: `@uipath/case-schema` ships a chain of
|
|
44
|
+
* closed, whole-document schema: `@uipath/case-schema` ships a chain of
|
|
41
45
|
* grammars (`case-schema-census.json`'s `versionChain`), each with its own
|
|
42
46
|
* forward migration, and a version is a claim that the entire document matches
|
|
43
47
|
* that grammar. This SDK emits the V30 grammar natively — the V30 rule schemas
|
package/dist/case/serialize.js
CHANGED
|
@@ -491,8 +491,18 @@ function applyPreservedIdentities(result, preservation) {
|
|
|
491
491
|
take(task, taskIdentity.id);
|
|
492
492
|
takeConditions(task?.entryConditions, taskIdentity.entryConditions);
|
|
493
493
|
(taskIdentity.inputs ?? []).forEach((row, index) => take(task?.data?.inputs?.[index], row.id));
|
|
494
|
-
|
|
495
|
-
|
|
494
|
+
const outputRows = task?.data?.outputs ?? [];
|
|
495
|
+
const wantedRows = taskIdentity.outputs ?? [];
|
|
496
|
+
// Match by name when unique on both sides, else by position.
|
|
497
|
+
const unique = (names, name) => names.filter((n) => n === name).length === 1;
|
|
498
|
+
const currentNames = outputRows.map((r) => r?.name);
|
|
499
|
+
const wantedNames = wantedRows.map((r) => r.name);
|
|
500
|
+
const named = new Set(wantedRows.filter((r) => unique(wantedNames, r.name) && unique(currentNames, r.name)).map((r) => r.name));
|
|
501
|
+
wantedRows.forEach((row, index) => {
|
|
502
|
+
const target = named.has(row.name)
|
|
503
|
+
? outputRows.find((r) => r?.name === row.name)
|
|
504
|
+
// A positional match never takes a row a name match owns.
|
|
505
|
+
: named.has(outputRows[index]?.name) ? undefined : outputRows[index];
|
|
496
506
|
const currentId = target?.id;
|
|
497
507
|
take(target, row.id);
|
|
498
508
|
if (typeof currentId === 'string' && typeof row.id === 'string' && target?.value === currentId) {
|
|
@@ -839,6 +849,8 @@ function taskData(task, resolver, taskId, stageId, ids) {
|
|
|
839
849
|
data.outputs = task.outputs.map((o, i) => emitTaskOutput(o, elementId, ids, `${elementId}:out:${i}:${o.var}`, resolver));
|
|
840
850
|
return data;
|
|
841
851
|
}
|
|
852
|
+
case 'business-rule':
|
|
853
|
+
return businessRuleTaskData(task, resolver, `${stageId}-${taskId}`, ids);
|
|
842
854
|
case 'action': {
|
|
843
855
|
const a = task.action ?? {};
|
|
844
856
|
const data = {};
|
|
@@ -1094,6 +1106,76 @@ function emitTaskOutput(o, elementId, ids, seed, resolver) {
|
|
|
1094
1106
|
elementId,
|
|
1095
1107
|
};
|
|
1096
1108
|
}
|
|
1109
|
+
// ─── Business-rule task ───────────────────────────────────────────────────────
|
|
1110
|
+
/** The version a business-rule task carries: below v3 the engine cannot run the
|
|
1111
|
+
* `.uirule` package a BusinessRules project deploys (the case converter falls
|
|
1112
|
+
* back to v2 when the field is missing). Same constant as the CLI's
|
|
1113
|
+
* `BUSINESS_RULE_TASK_VERSION`. */
|
|
1114
|
+
const BUSINESS_RULE_TASK_VERSION = 'v3';
|
|
1115
|
+
/** The binding `resource` a business rule is bound by — the CLI's and Studio
|
|
1116
|
+
* Web's spelling. `businessRule` (the registry cache category / BPMN binding
|
|
1117
|
+
* pattern) is a different namespace, and a binding that uses it is not a
|
|
1118
|
+
* BusinessRule resource to the bindings_v2 reader. */
|
|
1119
|
+
const BUSINESS_RULE_RESOURCE = 'BusinessRule';
|
|
1120
|
+
/** Mint (once per resourceKey + attribute) one root `BusinessRule` binding. */
|
|
1121
|
+
function businessRuleBinding(resolver, ids, resourceKey, propertyAttribute, value) {
|
|
1122
|
+
for (const entry of resolver.bindingSink.values()) {
|
|
1123
|
+
if (entry.resource === BUSINESS_RULE_RESOURCE && entry.resourceKey === resourceKey
|
|
1124
|
+
&& entry.propertyAttribute === propertyAttribute)
|
|
1125
|
+
return entry.id;
|
|
1126
|
+
}
|
|
1127
|
+
const id = ids.make('b', 8, `binding:${BUSINESS_RULE_RESOURCE}:${resourceKey}:${propertyAttribute}`);
|
|
1128
|
+
resolver.bindingSink.set(id, {
|
|
1129
|
+
id,
|
|
1130
|
+
name: propertyAttribute,
|
|
1131
|
+
type: 'string',
|
|
1132
|
+
resource: BUSINESS_RULE_RESOURCE,
|
|
1133
|
+
resourceKey,
|
|
1134
|
+
default: value,
|
|
1135
|
+
propertyAttribute,
|
|
1136
|
+
});
|
|
1137
|
+
return id;
|
|
1138
|
+
}
|
|
1139
|
+
/**
|
|
1140
|
+
* The `data` of a `business-rule` task, as Studio Web writes it and as the
|
|
1141
|
+
* CLI's `tasks describe --type business-rule` + splice produce it:
|
|
1142
|
+
*
|
|
1143
|
+
* - `name` / `folderPath` are `=bindings.<id>` references to a `BusinessRule`
|
|
1144
|
+
* binding pair keyed `<folder>.<name>` (the runtime resolves the rule through
|
|
1145
|
+
* them; `validate --strict` refuses literals);
|
|
1146
|
+
* - `version: 'v3'`;
|
|
1147
|
+
* - `outputs` always starts with `output` (`source: '=result'`, the whole
|
|
1148
|
+
* decision-keyed result) and `Error` (`source: '=Error'`), followed by the
|
|
1149
|
+
* author's extract rows, whose sources read a field of the result
|
|
1150
|
+
* (`=<Decision>.<column>`).
|
|
1151
|
+
*/
|
|
1152
|
+
function businessRuleTaskData(task, resolver, elementId, ids) {
|
|
1153
|
+
const ref = task.ref;
|
|
1154
|
+
const resourceKey = `${ref.folderPath}.${ref.name}`;
|
|
1155
|
+
const nameId = businessRuleBinding(resolver, ids, resourceKey, 'name', ref.name);
|
|
1156
|
+
const folderId = businessRuleBinding(resolver, ids, resourceKey, 'folderPath', ref.folderPath);
|
|
1157
|
+
const ownRow = (name, source, seed, extra = {}) => {
|
|
1158
|
+
const slot = ids.make('v', 8, `${elementId}:${seed}`);
|
|
1159
|
+
return { name, type: 'jsonSchema', id: slot, var: slot, value: slot, source, target: `=${slot}`, elementId, ...extra };
|
|
1160
|
+
};
|
|
1161
|
+
const resultSchema = task.businessRule?.resultSchema;
|
|
1162
|
+
const outputs = [
|
|
1163
|
+
ownRow('output', '=result', 'br:output', resultSchema === undefined ? {} : { _jsonSchema: resultSchema }),
|
|
1164
|
+
// The CLI's `buildErrorOutput()` row after `addVarIdsToInputsOutputs`:
|
|
1165
|
+
// `displayName`, and the error schema as both `_jsonSchema` and `body`.
|
|
1166
|
+
ownRow('Error', '=Error', 'br:error', {
|
|
1167
|
+
displayName: 'Error', _jsonSchema: structuredClone(ERROR_OUTPUT_SCHEMA), body: structuredClone(ERROR_OUTPUT_SCHEMA),
|
|
1168
|
+
}),
|
|
1169
|
+
...(task.outputs ?? []).map((o, i) => emitTaskOutput(o, elementId, ids, `${elementId}:out:${i}:${o.var}`, resolver)),
|
|
1170
|
+
];
|
|
1171
|
+
return {
|
|
1172
|
+
name: `=bindings.${nameId}`,
|
|
1173
|
+
folderPath: `=bindings.${folderId}`,
|
|
1174
|
+
version: BUSINESS_RULE_TASK_VERSION,
|
|
1175
|
+
inputs: (task.inputs ?? []).map((inp, i) => emitTaskInput(inp, elementId, ids, `${elementId}:in:${i}:${inp.name}`)),
|
|
1176
|
+
outputs,
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1097
1179
|
// ─── Connector task (execute-connector-activity) ──────────────────────────────
|
|
1098
1180
|
/**
|
|
1099
1181
|
* Build the `execute-connector-activity` `data` payload for an IS connector task,
|
|
@@ -1353,6 +1435,14 @@ function connectorOutputVar(resolver, base) {
|
|
|
1353
1435
|
resolver.connectorOutputCounts.set(base, occurrence);
|
|
1354
1436
|
return occurrence === 1 ? base : `${base}${occurrence}`;
|
|
1355
1437
|
}
|
|
1438
|
+
/** The `Error` output's schema: the designer's (and the CLI's) `ERROR_OUTPUT_SCHEMA`. */
|
|
1439
|
+
const ERROR_OUTPUT_SCHEMA = {
|
|
1440
|
+
type: 'object',
|
|
1441
|
+
properties: {
|
|
1442
|
+
code: { type: 'string' }, message: { type: 'string' }, detail: { type: 'string' },
|
|
1443
|
+
category: { type: 'string' }, status: { type: 'number' }, element: { type: 'string' },
|
|
1444
|
+
},
|
|
1445
|
+
};
|
|
1356
1446
|
function connectorOutputs(rc, elementId, resolver) {
|
|
1357
1447
|
const responseSchema = rc.entry.outputSchema
|
|
1358
1448
|
? schemaFromFields(rc.entry.outputSchema.fields ?? [])
|
|
@@ -1367,13 +1457,7 @@ function connectorOutputs(rc, elementId, resolver) {
|
|
|
1367
1457
|
},
|
|
1368
1458
|
{
|
|
1369
1459
|
name: 'Error', displayName: 'Error', type: 'jsonSchema', source: '=Error',
|
|
1370
|
-
body:
|
|
1371
|
-
type: 'object',
|
|
1372
|
-
properties: {
|
|
1373
|
-
code: { type: 'string' }, message: { type: 'string' }, detail: { type: 'string' },
|
|
1374
|
-
category: { type: 'string' }, status: { type: 'number' }, element: { type: 'string' },
|
|
1375
|
-
},
|
|
1376
|
-
},
|
|
1460
|
+
body: structuredClone(ERROR_OUTPUT_SCHEMA),
|
|
1377
1461
|
var: errorVar, id: errorVar, value: errorVar, target: `=${errorVar}`, elementId,
|
|
1378
1462
|
},
|
|
1379
1463
|
];
|
package/dist/check.js
CHANGED
|
@@ -10,7 +10,7 @@ import { FlowNode } from './core/node-classes.js';
|
|
|
10
10
|
import { planContributionBindings } from './core/contribution-bindings.js';
|
|
11
11
|
import { definitionHasErrorPort, errorPortRefusal } from './core/error-port.js';
|
|
12
12
|
import { actionVersion, bundledActionNodeType, bundledContainerDefinition, bundledDefinition, perCallDefinition, resolveContributionDefinition } from './serialize.js';
|
|
13
|
-
import { FORM_FIELD_TYPES, formFieldOptionsProblem, formFieldOptionsWhy, formFieldTypeHint, formFieldTypeWhy, validFormFieldTypesText } from './core/form-field-type.js';
|
|
13
|
+
import { FORM_FIELD_TYPES, formFieldOptionsProblem, formFieldOptionsWhy, formFieldSettingProblems, formFieldTypeHint, formFieldTypeWhy, validFormFieldTypesText, } from './core/form-field-type.js';
|
|
14
14
|
import { AGENTIC_PROCESS_FORMS } from './core/agentic-process-forms.js';
|
|
15
15
|
import { checkEventResolution, checkEventSubscription, eventParamsFor, } from './core/event-checks.js';
|
|
16
16
|
import { checkCookedEscapes } from './flow-expr-check.js';
|
|
@@ -3573,7 +3573,7 @@ function checkInlineAgent(step, inputs, diags, opts = {}, ledger = newResourceId
|
|
|
3573
3573
|
+ `${JSON.stringify(f?.type)}, ${formFieldTypeWhy(f?.type, `which the form does not recognize — it would `
|
|
3574
3574
|
+ `be published as a plain string`)}. Valid: ${validFormFieldTypesText()}.`, formFieldTypeHint(f?.type));
|
|
3575
3575
|
}
|
|
3576
|
-
const optionsProblem = formFieldOptionsProblem(f
|
|
3576
|
+
const optionsProblem = formFieldOptionsProblem(f);
|
|
3577
3577
|
if (optionsProblem !== undefined) {
|
|
3578
3578
|
const message = `Inline agent step "${step}"'s quick-form escalation field "${String(f?.id ?? '?')}" `
|
|
3579
3579
|
+ `${formFieldOptionsWhy(optionsProblem, f?.type)}.`;
|
|
@@ -3583,6 +3583,10 @@ function checkInlineAgent(step, inputs, diags, opts = {}, ledger = newResourceId
|
|
|
3583
3583
|
at('INLINE_AGENT_ESCALATION_FIELD_OPTIONS', message, `options: [{ id: 'opt-1', value: 'high', label: 'High' }, { id: 'opt-2', value: 'low', label: 'Low' }]`);
|
|
3584
3584
|
}
|
|
3585
3585
|
}
|
|
3586
|
+
for (const problem of formFieldSettingProblems(f)) {
|
|
3587
|
+
at('INLINE_AGENT_ESCALATION_FIELD_SETTING_INVALID', `Inline agent step "${step}"'s quick-form escalation gives the field "${String(f?.id ?? '?')}" `
|
|
3588
|
+
+ `${problem}. The platform refuses the form.`);
|
|
3589
|
+
}
|
|
3586
3590
|
}
|
|
3587
3591
|
continue;
|
|
3588
3592
|
}
|
package/dist/core/actions.d.ts
CHANGED
|
@@ -102,19 +102,114 @@ export interface FormFieldOption {
|
|
|
102
102
|
/** What the reviewer sees, e.g. `'High'`. */
|
|
103
103
|
label: string;
|
|
104
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* How a form field renders (`@uipath/hitl-schema-types` `controlType`). It is a
|
|
107
|
+
* separate vocabulary from the data `type`: a single choice is `type: 'string'`
|
|
108
|
+
* with `controlType: 'dropdown'`. When it is absent, the form uses the data
|
|
109
|
+
* type's default control (or the one a choice spelling such as `singleSelect`
|
|
110
|
+
* implies).
|
|
111
|
+
*/
|
|
112
|
+
export type FormFieldControlType = 'text' | 'textarea' | 'numberInput' | 'datePicker' | 'dateAndTimePicker' | 'checkbox' | 'dropdown' | 'radioGroup' | 'multiSelect' | 'checkboxGroup' | 'fileUpload';
|
|
113
|
+
/**
|
|
114
|
+
* One condition of a form-field rule (`@uipath/hitl-schema-types`
|
|
115
|
+
* `fieldConditionSchema`): `when` names the field it reads, and the other keys
|
|
116
|
+
* say what that field's value must be.
|
|
117
|
+
*/
|
|
118
|
+
export interface FormFieldCondition {
|
|
119
|
+
/** The id of the field the condition reads, e.g. `'priority'`. */
|
|
120
|
+
when: string;
|
|
121
|
+
/** The value must equal this. */
|
|
122
|
+
is?: unknown;
|
|
123
|
+
/** The value must not equal this. */
|
|
124
|
+
isNot?: unknown;
|
|
125
|
+
/** The value must be one of these. */
|
|
126
|
+
in?: unknown[];
|
|
127
|
+
/** The value must be none of these. */
|
|
128
|
+
notIn?: unknown[];
|
|
129
|
+
/** The value must match this regular expression. */
|
|
130
|
+
matches?: string;
|
|
131
|
+
/** A custom condition expression, kept as written. */
|
|
132
|
+
custom?: string;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* A form-field rule (`@uipath/hitl-schema-types` `fieldRuleSchema`): when its
|
|
136
|
+
* conditions hold, its `effects` apply to the field, for example
|
|
137
|
+
* `{ visible: true }` or `{ required: true }`.
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* hitl({
|
|
141
|
+
* fields: [
|
|
142
|
+
* { id: 'priority', type: 'singleSelect', direction: 'output', options: [{ id: 'o1', value: 'high', label: 'High' }] },
|
|
143
|
+
* {
|
|
144
|
+
* id: 'reason', type: 'string', direction: 'output',
|
|
145
|
+
* // Required only when the priority is high.
|
|
146
|
+
* rules: [{ id: 'rule-1', conditions: [{ when: 'priority', is: 'high' }], effects: { required: true } }],
|
|
147
|
+
* },
|
|
148
|
+
* ],
|
|
149
|
+
* outcomes: ['Done'],
|
|
150
|
+
* })
|
|
151
|
+
*/
|
|
152
|
+
export interface FormFieldRule {
|
|
153
|
+
/** The rule's own stable id, e.g. `'rule-1'`. */
|
|
154
|
+
id: string;
|
|
155
|
+
/** What must hold for the effects to apply. */
|
|
156
|
+
conditions: FormFieldCondition[];
|
|
157
|
+
/** How the conditions combine. The platform's default is `'AND'`. */
|
|
158
|
+
operator?: 'AND' | 'OR';
|
|
159
|
+
/** What applies while the conditions hold, keyed by effect (`visible`, `required`, `disabled`). */
|
|
160
|
+
effects: Record<string, unknown>;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* The form-field keys that say how a field looks and behaves, with the
|
|
164
|
+
* platform's own names and shapes (`@uipath/hitl-schema-types` `fieldSchemaFor`).
|
|
165
|
+
* Every one is optional and written to the form row as given, so a field the
|
|
166
|
+
* canvas saved keeps them through decompile, an edit and compile.
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* hitl({
|
|
170
|
+
* fields: [{
|
|
171
|
+
* id: 'priority', label: 'Priority', type: 'string', direction: 'output', required: true,
|
|
172
|
+
* controlType: 'dropdown',
|
|
173
|
+
* options: [{ id: 'o1', value: 'high', label: 'High' }, { id: 'o2', value: 'low', label: 'Low' }],
|
|
174
|
+
* defaultValue: 'high', colSpan: 6, requiredMessage: 'Pick one',
|
|
175
|
+
* description: 'How urgent the invoice is.',
|
|
176
|
+
* }],
|
|
177
|
+
* outcomes: ['Done'],
|
|
178
|
+
* })
|
|
179
|
+
*/
|
|
180
|
+
export interface FormFieldSettings {
|
|
181
|
+
/** Conditions that show, require or disable the field. */
|
|
182
|
+
rules?: FormFieldRule[];
|
|
183
|
+
/** The message the form shows when a required field is left empty. */
|
|
184
|
+
requiredMessage?: string;
|
|
185
|
+
/** The field's width on the form's 12-column grid: an integer from 1 to 12. Absent means the full width. */
|
|
186
|
+
colSpan?: number;
|
|
187
|
+
/**
|
|
188
|
+
* How the field renders, e.g. `'dropdown'` or `'textarea'`. Absent means the
|
|
189
|
+
* data type's default control.
|
|
190
|
+
*/
|
|
191
|
+
controlType?: FormFieldControlType;
|
|
192
|
+
/**
|
|
193
|
+
* The choices of a picker field — a `dropdown` / `singleSelect` / `enum`, or a
|
|
194
|
+
* `multiSelect`. Written to the form verbatim, in order.
|
|
195
|
+
*/
|
|
196
|
+
options?: FormFieldOption[];
|
|
197
|
+
/**
|
|
198
|
+
* The value the form starts with: one `options` value for a single-choice
|
|
199
|
+
* field, a list of them for a multi-select, or the starting text.
|
|
200
|
+
*/
|
|
201
|
+
defaultValue?: string | string[];
|
|
202
|
+
/** Guidance for an agent that pre-fills the field. */
|
|
203
|
+
description?: string;
|
|
204
|
+
}
|
|
105
205
|
/** A value the reviewer READS: shown on the form, filled in from the flow. */
|
|
106
|
-
export interface ShownField {
|
|
206
|
+
export interface ShownField extends FormFieldSettings {
|
|
107
207
|
/** Field id — the key the answer object uses. Must be unique on the form. */
|
|
108
208
|
id: string;
|
|
109
209
|
/** Label the reviewer sees. Defaults to `id`. */
|
|
110
210
|
label?: string;
|
|
111
211
|
type: FormFieldType;
|
|
112
212
|
direction: 'input';
|
|
113
|
-
/**
|
|
114
|
-
* The choices of a picker field — a `singleSelect` / `enum` dropdown or a
|
|
115
|
-
* `multiSelect`. Written to the form verbatim, in order.
|
|
116
|
-
*/
|
|
117
|
-
options?: FormFieldOption[];
|
|
118
213
|
/**
|
|
119
214
|
* What to show — a reference, e.g. `input('amount')` or
|
|
120
215
|
* `out('fetchInvoice', 'body.total')`. Required: a shown field with nothing
|
|
@@ -124,18 +219,13 @@ export interface ShownField {
|
|
|
124
219
|
value: Expr;
|
|
125
220
|
}
|
|
126
221
|
/** A value the reviewer FILLS IN: read downstream with `out('<step>', '<id>')`. */
|
|
127
|
-
export interface AskedField {
|
|
222
|
+
export interface AskedField extends FormFieldSettings {
|
|
128
223
|
/** Field id — the key the answer object uses. Must be unique on the form. */
|
|
129
224
|
id: string;
|
|
130
225
|
/** Label the reviewer sees. Defaults to `id`. */
|
|
131
226
|
label?: string;
|
|
132
227
|
type: FormFieldType;
|
|
133
228
|
direction: 'output';
|
|
134
|
-
/**
|
|
135
|
-
* The choices of a picker field — a `singleSelect` / `enum` dropdown or a
|
|
136
|
-
* `multiSelect`. Written to the form verbatim, in order.
|
|
137
|
-
*/
|
|
138
|
-
options?: FormFieldOption[];
|
|
139
229
|
/** Reviewer must fill it in before the task can be completed. */
|
|
140
230
|
required?: boolean;
|
|
141
231
|
}
|
|
@@ -145,18 +235,13 @@ export interface AskedField {
|
|
|
145
235
|
* answer. Read it downstream with `out('<step>', '<id>')` — the write target is
|
|
146
236
|
* the field's own id, deliberately decoupled from where the pre-fill came from.
|
|
147
237
|
*/
|
|
148
|
-
export interface InOutField {
|
|
238
|
+
export interface InOutField extends FormFieldSettings {
|
|
149
239
|
/** Field id — the key the answer object uses. Must be unique on the form. */
|
|
150
240
|
id: string;
|
|
151
241
|
/** Label the reviewer sees. Defaults to `id`. */
|
|
152
242
|
label?: string;
|
|
153
243
|
type: FormFieldType;
|
|
154
244
|
direction: 'inOut';
|
|
155
|
-
/**
|
|
156
|
-
* The choices of a picker field — a `singleSelect` / `enum` dropdown or a
|
|
157
|
-
* `multiSelect`. Written to the form verbatim, in order.
|
|
158
|
-
*/
|
|
159
|
-
options?: FormFieldOption[];
|
|
160
245
|
/** What to pre-fill — a reference, e.g. `out('fetchInvoice', 'body.total')`. */
|
|
161
246
|
value: Expr;
|
|
162
247
|
/** Reviewer must confirm/fill it before the task can be completed. */
|