@uipath/maestro-builder-sdk 6.46.7 → 6.47.1
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 +71 -69
- package/dist/api-members.md +217 -210
- package/dist/case/business-event-shape.d.ts +27 -0
- package/dist/case/business-event-shape.js +45 -0
- package/dist/case/case-sdk.d.ts +64 -3
- package/dist/case/case-sdk.js +36 -5
- package/dist/case/check.js +31 -1
- package/dist/case/decompile.d.ts +7 -0
- package/dist/case/decompile.js +39 -1
- package/dist/case/preserve.js +1 -1
- package/dist/case/rule-options.d.ts +15 -0
- package/dist/case/rule-options.js +46 -0
- package/dist/case/serialize.js +62 -1
- package/package.json +2 -2
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The problems with a case's declared business events that `check` reports and
|
|
3
|
+
* `serialize` refuses: an empty name, a name declared twice (an `api-event`
|
|
4
|
+
* rule matches by name, so it could not tell the two apart), an option
|
|
5
|
+
* `.businessEvent()` does not read (it would be dropped from the caseplan), or a
|
|
6
|
+
* payload of a type the Designer does not write.
|
|
7
|
+
*
|
|
8
|
+
* A payload is an object or array schema (`type` absent, `'object'` or
|
|
9
|
+
* `'array'`): the two shapes the Case Designer and `uip maestro case sdd
|
|
10
|
+
* convert` write. Any other type, such as `{ type: 'string' }`, is refused.
|
|
11
|
+
* Nobody has confirmed that Studio Web's Business events panel loads one; once
|
|
12
|
+
* someone does, this can be loosened.
|
|
13
|
+
*/
|
|
14
|
+
import type { BuiltBusinessEvent } from './case-sdk.js';
|
|
15
|
+
/** One problem with a declared business event. */
|
|
16
|
+
export interface BusinessEventProblem {
|
|
17
|
+
code: 'BUSINESS_EVENT_NAME' | 'DUP_BUSINESS_EVENT' | 'BUSINESS_EVENT_UNKNOWN_OPTION' | 'BUSINESS_EVENT_PAYLOAD_TYPE';
|
|
18
|
+
message: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Report every problem with the case's business events, in declaration order.
|
|
22
|
+
* Options set to `undefined` count as absent.
|
|
23
|
+
*
|
|
24
|
+
* @param events - The events as `.businessEvent()` recorded them.
|
|
25
|
+
* @returns The problems, empty when every event is well formed.
|
|
26
|
+
*/
|
|
27
|
+
export declare function businessEventProblems(events: readonly BuiltBusinessEvent[]): BusinessEventProblem[];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** The keys a declared business event carries; any other is refused. */
|
|
2
|
+
const BUSINESS_EVENT_KEYS = ['name', 'description', 'payload'];
|
|
3
|
+
/**
|
|
4
|
+
* Report every problem with the case's business events, in declaration order.
|
|
5
|
+
* Options set to `undefined` count as absent.
|
|
6
|
+
*
|
|
7
|
+
* @param events - The events as `.businessEvent()` recorded them.
|
|
8
|
+
* @returns The problems, empty when every event is well formed.
|
|
9
|
+
*/
|
|
10
|
+
export function businessEventProblems(events) {
|
|
11
|
+
const problems = [];
|
|
12
|
+
const seen = new Set();
|
|
13
|
+
for (const event of events) {
|
|
14
|
+
if (typeof event.name !== 'string' || !event.name.trim())
|
|
15
|
+
problems.push({ code: 'BUSINESS_EVENT_NAME', message: 'A business event has an empty name — rules match events by name.' });
|
|
16
|
+
else if (seen.has(event.name))
|
|
17
|
+
problems.push({
|
|
18
|
+
code: 'DUP_BUSINESS_EVENT',
|
|
19
|
+
message: `Business event "${event.name}" is declared twice — rules match events by name, so each name must be unique.`,
|
|
20
|
+
});
|
|
21
|
+
else
|
|
22
|
+
seen.add(event.name);
|
|
23
|
+
for (const key of Object.keys(event))
|
|
24
|
+
if (!BUSINESS_EVENT_KEYS.includes(key) && event[key] !== undefined)
|
|
25
|
+
problems.push({
|
|
26
|
+
code: 'BUSINESS_EVENT_UNKNOWN_OPTION',
|
|
27
|
+
message: `Business event "${String(event.name)}" does not take option "${key}", so it would be dropped from the caseplan — ` +
|
|
28
|
+
`.businessEvent() takes "description" and "payload" (the payload's JSON Schema object; the caseplan's "schema" string is written from it).`,
|
|
29
|
+
});
|
|
30
|
+
const payload = event.payload;
|
|
31
|
+
if (payload !== undefined) {
|
|
32
|
+
const isRecord = typeof payload === 'object' && payload !== null && !Array.isArray(payload);
|
|
33
|
+
const type = isRecord ? payload.type : undefined;
|
|
34
|
+
if (!isRecord || (type !== undefined && type !== 'object' && type !== 'array'))
|
|
35
|
+
problems.push({
|
|
36
|
+
code: 'BUSINESS_EVENT_PAYLOAD_TYPE',
|
|
37
|
+
message: `Business event "${String(event.name)}" payload must be an object or array schema (no "type", type "object" or type "array"), ` +
|
|
38
|
+
`e.g. { type: 'object', properties: { amount: { type: 'number' } } }` +
|
|
39
|
+
(isRecord ? `; got type ${JSON.stringify(type)}.` : '; got a value that is not a JSON Schema object.') +
|
|
40
|
+
` Other types are refused until Studio Web's Business events panel is confirmed to load them.`,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return problems;
|
|
45
|
+
}
|
package/dist/case/case-sdk.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ export { preserveCaseJson } from './preserve.js';
|
|
|
24
24
|
export type { CaseJsonPreservation, PreservedCaseNode, PreservedTaskFields, PreservedVariableLane } from './preserve.js';
|
|
25
25
|
export { CaseBuildError } from './case-expr-check.js';
|
|
26
26
|
export type { CaseLocatedDiagnostic } from './case-expr-check.js';
|
|
27
|
-
export type CaseRuleType = 'case-entered' | 'required-tasks-completed' | 'required-stages-completed' | 'selected-stage-completed' | 'selected-stage-exited' | 'selected-tasks-completed' | 'current-stage-entered' | 'adhoc' | 'runs-sequentially' | 'user-selected-stage' | 'wait-for-connector' | 'sla-status-change';
|
|
27
|
+
export type CaseRuleType = 'case-entered' | 'required-tasks-completed' | 'required-stages-completed' | 'selected-stage-completed' | 'selected-stage-exited' | 'selected-tasks-completed' | 'current-stage-entered' | 'adhoc' | 'runs-sequentially' | 'user-selected-stage' | 'wait-for-connector' | 'sla-status-change' | 'api-event';
|
|
28
28
|
export interface RuleOpts {
|
|
29
29
|
/**
|
|
30
30
|
* Symbolic stage label (for `selected-stage-completed` / `selected-stage-exited`).
|
|
@@ -72,6 +72,14 @@ export interface RuleOpts {
|
|
|
72
72
|
* a **breach** rule (`slaId` alone). Resolved to `escalationId`.
|
|
73
73
|
*/
|
|
74
74
|
escalation?: string;
|
|
75
|
+
/**
|
|
76
|
+
* For an `api-event` rule — the **name of the business event** it listens for,
|
|
77
|
+
* e.g. `rule('api-event', { businessEvent: 'Invoice Approved' })`. Declare the
|
|
78
|
+
* event on the case with {@link CaseBuilder.businessEvent}. Emitted as the
|
|
79
|
+
* rule's `businessEventType`; the match is on the exact name, so an
|
|
80
|
+
* undeclared or misspelled name never fires.
|
|
81
|
+
*/
|
|
82
|
+
businessEvent?: string;
|
|
75
83
|
/** A `=js:` gate on case state (for `adhoc`, or as an extra guard on any rule). */
|
|
76
84
|
expression?: string;
|
|
77
85
|
}
|
|
@@ -372,9 +380,9 @@ export interface BuiltCaseExitCondition {
|
|
|
372
380
|
marksCaseComplete?: boolean;
|
|
373
381
|
rules: CaseRule[][];
|
|
374
382
|
}
|
|
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';
|
|
383
|
+
export type TaskKind = 'process' | 'agent' | 'rpa' | 'api-workflow' | 'case-management' | 'flow-process' | 'function' | 'business-rule' | 'external-agent' | 'external-workflow' | 'action' | 'connector' | 'wait-for-timer' | 'wait-for-connector';
|
|
376
384
|
/** Published-resource task kinds that the Case schema permits as unresolved skeletons. */
|
|
377
|
-
export type UnresolvedReferenceTaskKind = 'process' | 'agent' | 'rpa' | 'api-workflow' | 'case-management';
|
|
385
|
+
export type UnresolvedReferenceTaskKind = 'process' | 'agent' | 'rpa' | 'api-workflow' | 'case-management' | 'function';
|
|
378
386
|
/**
|
|
379
387
|
* A `wait-for-connector` subscription: suspend on an Integration Service event.
|
|
380
388
|
* Omit `connectorKey`/`operation` for a bare **placeholder** (for a connector not
|
|
@@ -680,6 +688,29 @@ export interface CaseLayout {
|
|
|
680
688
|
/** Trigger node layout keyed by its display name (`Trigger 1` for the default). */
|
|
681
689
|
triggers?: Record<string, CaseNodeLayout>;
|
|
682
690
|
}
|
|
691
|
+
/** Options for {@link CaseBuilder.businessEvent}. */
|
|
692
|
+
export interface BusinessEventOpts {
|
|
693
|
+
/** What the event means, shown in the Case Designer's Business events panel. */
|
|
694
|
+
description?: string;
|
|
695
|
+
/**
|
|
696
|
+
* The JSON Schema of the payload the sender attaches, e.g.
|
|
697
|
+
* `{ type: 'object', properties: { amount: { type: 'number' } } }`. Written to
|
|
698
|
+
* `schema` as a string, with `$schema` defaulted to draft 7, the way the Case
|
|
699
|
+
* Designer saves it. It must be an object or array schema (no `type`,
|
|
700
|
+
* `type: 'object'` or `type: 'array'`, the shapes the Designer writes): any
|
|
701
|
+
* other type is refused (BUSINESS_EVENT_PAYLOAD_TYPE) until someone confirms
|
|
702
|
+
* Studio Web's Business events panel loads it. Only the empty object schema (`{}`,
|
|
703
|
+
* `{ type: 'object' }` or `{ type: 'object', properties: {} }`) is left out;
|
|
704
|
+
* any other object payload, e.g. `{ type: 'object', required: ['a'] }`, is
|
|
705
|
+
* written.
|
|
706
|
+
*/
|
|
707
|
+
payload?: Record<string, unknown>;
|
|
708
|
+
}
|
|
709
|
+
/** A declared business event, as the serializer reads it. */
|
|
710
|
+
export interface BuiltBusinessEvent extends BusinessEventOpts {
|
|
711
|
+
/** The event name an `api-event` rule matches on, exactly. */
|
|
712
|
+
name: string;
|
|
713
|
+
}
|
|
683
714
|
export interface BuiltCase {
|
|
684
715
|
id: string;
|
|
685
716
|
name: string;
|
|
@@ -701,6 +732,8 @@ export interface BuiltCase {
|
|
|
701
732
|
stages: BuiltStage[];
|
|
702
733
|
caseExit: BuiltCaseExitCondition[];
|
|
703
734
|
slaRules: BuiltSla[];
|
|
735
|
+
/** Business events an `api-event` rule can listen for (`metadata.businessEvents`). */
|
|
736
|
+
businessEvents?: BuiltBusinessEvent[];
|
|
704
737
|
/** Empty = the default single manual trigger. */
|
|
705
738
|
triggers: BuiltTrigger[];
|
|
706
739
|
/** Optional designer layout, resolved to final node ids by the serializer. */
|
|
@@ -851,6 +884,22 @@ declare class TaskBuilder {
|
|
|
851
884
|
flowProcess(name: string, opts?: {
|
|
852
885
|
folder?: string;
|
|
853
886
|
}): this;
|
|
887
|
+
/**
|
|
888
|
+
* Reference a deployed **Coded Function** (a `uipath-functions` project).
|
|
889
|
+
*
|
|
890
|
+
* @remarks
|
|
891
|
+
* Emits a `function` task bound like the other published resources, with
|
|
892
|
+
* `resourceSubType: 'Function'`. A function is its own task type: do not
|
|
893
|
+
* model one as `.apiWorkflow()` or `.agent()`. A function with several
|
|
894
|
+
* deployments of one name is told apart by its folder.
|
|
895
|
+
*
|
|
896
|
+
* @param name - The deployed function's name.
|
|
897
|
+
* @param opts - `folder` — the Orchestrator folder it lives in, or `'solution_folder'` (the default when omitted) for one built in this solution. Compile binds the task through a name/folderPath pair of root bindings keyed `<folder>.<name>`.
|
|
898
|
+
* @returns This builder, so calls chain.
|
|
899
|
+
*/
|
|
900
|
+
function(name: string, opts?: {
|
|
901
|
+
folder?: string;
|
|
902
|
+
}): this;
|
|
854
903
|
/**
|
|
855
904
|
* Evaluate a deployed **business rule** (a DMN decision package) and read its
|
|
856
905
|
* decisions back.
|
|
@@ -1146,6 +1195,7 @@ declare class CaseBuilder {
|
|
|
1146
1195
|
private readonly _stages;
|
|
1147
1196
|
private readonly _caseExit;
|
|
1148
1197
|
private readonly _sla;
|
|
1198
|
+
private readonly _businessEvents;
|
|
1149
1199
|
private readonly _triggers;
|
|
1150
1200
|
constructor(_id: string);
|
|
1151
1201
|
/**
|
|
@@ -1367,6 +1417,17 @@ declare class CaseBuilder {
|
|
|
1367
1417
|
* @returns This builder, so calls chain.
|
|
1368
1418
|
*/
|
|
1369
1419
|
sla(opts: SlaOpts): this;
|
|
1420
|
+
/**
|
|
1421
|
+
* Declare a business event: a named message an outside system sends to a
|
|
1422
|
+
* running case. An `api-event` rule listens for it by name, e.g.
|
|
1423
|
+
* `.exitWhen(rule('api-event', { businessEvent: 'Invoice Approved' }), …)`.
|
|
1424
|
+
* Emitted to `metadata.businessEvents`.
|
|
1425
|
+
*
|
|
1426
|
+
* @param name - The event name rules match on, exactly.
|
|
1427
|
+
* @param opts - Its description and payload schema.
|
|
1428
|
+
* @returns This builder, so calls chain.
|
|
1429
|
+
*/
|
|
1430
|
+
businessEvent(name: string, opts?: BusinessEventOpts): this;
|
|
1370
1431
|
/**
|
|
1371
1432
|
* Finish the plan and return the description the serializer writes.
|
|
1372
1433
|
*
|
package/dist/case/case-sdk.js
CHANGED
|
@@ -264,6 +264,22 @@ class TaskBuilder {
|
|
|
264
264
|
flowProcess(name, opts) {
|
|
265
265
|
return this.reference('flow-process', name, opts);
|
|
266
266
|
}
|
|
267
|
+
/**
|
|
268
|
+
* Reference a deployed **Coded Function** (a `uipath-functions` project).
|
|
269
|
+
*
|
|
270
|
+
* @remarks
|
|
271
|
+
* Emits a `function` task bound like the other published resources, with
|
|
272
|
+
* `resourceSubType: 'Function'`. A function is its own task type: do not
|
|
273
|
+
* model one as `.apiWorkflow()` or `.agent()`. A function with several
|
|
274
|
+
* deployments of one name is told apart by its folder.
|
|
275
|
+
*
|
|
276
|
+
* @param name - The deployed function's name.
|
|
277
|
+
* @param opts - `folder` — the Orchestrator folder it lives in, or `'solution_folder'` (the default when omitted) for one built in this solution. Compile binds the task through a name/folderPath pair of root bindings keyed `<folder>.<name>`.
|
|
278
|
+
* @returns This builder, so calls chain.
|
|
279
|
+
*/
|
|
280
|
+
function(name, opts) {
|
|
281
|
+
return this.reference('function', name, opts);
|
|
282
|
+
}
|
|
267
283
|
/**
|
|
268
284
|
* Evaluate a deployed **business rule** (a DMN decision package) and read its
|
|
269
285
|
* decisions back.
|
|
@@ -488,12 +504,11 @@ class TaskBuilder {
|
|
|
488
504
|
_build() {
|
|
489
505
|
if (!this._kind)
|
|
490
506
|
throw new Error(`task "${this._name}" has no kind — call .process()/.agent()/.rpa()/.action()/.connector()/.waitForTimer()/… once.`);
|
|
491
|
-
// Task I/O binding is reference-mode only
|
|
492
|
-
//
|
|
493
|
-
|
|
494
|
-
const REF_IO = new Set(['process', 'agent', 'rpa', 'api-workflow', 'case-management', 'flow-process', 'business-rule']);
|
|
507
|
+
// Task I/O binding is reference-mode only (REF_IO). Connector tasks derive
|
|
508
|
+
// their own inputs/outputs from the operation schema.
|
|
509
|
+
const REF_IO = new Set(['process', 'agent', 'rpa', 'api-workflow', 'case-management', 'flow-process', 'function', 'business-rule']);
|
|
495
510
|
if ((this._inputs || this._outputs) && !REF_IO.has(this._kind))
|
|
496
|
-
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}".`);
|
|
511
|
+
throw new Error(`task "${this._name}": .inputs()/.outputs() are only supported on reference-mode tasks (process/agent/rpa/api-workflow/case-management/flow-process/function/business-rule), not "${this._kind}".`);
|
|
497
512
|
if (!this._ref && (this._inputs || this._outputs))
|
|
498
513
|
throw new Error(`task "${this._name}": an unresolved reference task cannot declare inputs or outputs.`);
|
|
499
514
|
return {
|
|
@@ -652,6 +667,7 @@ class CaseBuilder {
|
|
|
652
667
|
_stages = [];
|
|
653
668
|
_caseExit = [];
|
|
654
669
|
_sla = [];
|
|
670
|
+
_businessEvents = [];
|
|
655
671
|
_triggers = [];
|
|
656
672
|
constructor(_id) {
|
|
657
673
|
this._id = _id;
|
|
@@ -936,6 +952,20 @@ class CaseBuilder {
|
|
|
936
952
|
this._sla.push(buildSla(opts));
|
|
937
953
|
return this;
|
|
938
954
|
}
|
|
955
|
+
/**
|
|
956
|
+
* Declare a business event: a named message an outside system sends to a
|
|
957
|
+
* running case. An `api-event` rule listens for it by name, e.g.
|
|
958
|
+
* `.exitWhen(rule('api-event', { businessEvent: 'Invoice Approved' }), …)`.
|
|
959
|
+
* Emitted to `metadata.businessEvents`.
|
|
960
|
+
*
|
|
961
|
+
* @param name - The event name rules match on, exactly.
|
|
962
|
+
* @param opts - Its description and payload schema.
|
|
963
|
+
* @returns This builder, so calls chain.
|
|
964
|
+
*/
|
|
965
|
+
businessEvent(name, opts = {}) {
|
|
966
|
+
this._businessEvents.push({ name, ...opts });
|
|
967
|
+
return this;
|
|
968
|
+
}
|
|
939
969
|
/**
|
|
940
970
|
* Finish the plan and return the description the serializer writes.
|
|
941
971
|
*
|
|
@@ -958,6 +988,7 @@ class CaseBuilder {
|
|
|
958
988
|
stages: this._stages,
|
|
959
989
|
caseExit: this._caseExit,
|
|
960
990
|
slaRules: this._sla,
|
|
991
|
+
...(this._businessEvents.length ? { businessEvents: this._businessEvents } : {}),
|
|
961
992
|
triggers: this._triggers,
|
|
962
993
|
...(this._layout ? { layout: this._layout } : {}),
|
|
963
994
|
};
|
package/dist/case/check.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { businessEventProblems } from './business-event-shape.js';
|
|
2
|
+
import { ruleShapeProblems } from './rule-options.js';
|
|
1
3
|
import { types } from '../core/expr.js';
|
|
2
4
|
import { authoredCaseVersion, CASE_FORMAT_PROFILE, CASE_SCHEMA_VERSIONS } from './format-profile.js';
|
|
3
5
|
import { schemaVersionRefusal } from '../schema-version.js';
|
|
@@ -212,7 +214,35 @@ export function check(built, opts = {}) {
|
|
|
212
214
|
code: 'NO_CASE_COMPLETION',
|
|
213
215
|
message: 'Case has no completion rule — add .completeWhen(rule(\'required-stages-completed\')).',
|
|
214
216
|
});
|
|
217
|
+
// Business events: `metadata.businessEvents` names, matched exactly by an
|
|
218
|
+
// `api-event` rule's `businessEventType`. `uip maestro case validate` rejects a
|
|
219
|
+
// rule naming none (BUSINESS_EVENT_NOT_SELECTED) or an undeclared one
|
|
220
|
+
// (BUSINESS_EVENT_NOT_FOUND); both are reported here, at the rule.
|
|
221
|
+
const businessEvents = new Set((built.businessEvents ?? []).map((event) => event.name));
|
|
222
|
+
for (const problem of businessEventProblems(built.businessEvents ?? []))
|
|
223
|
+
diags.push({ level: 'error', code: problem.code, message: problem.message });
|
|
224
|
+
const refEvent = (r, where) => {
|
|
225
|
+
if (r.rule !== 'api-event')
|
|
226
|
+
return;
|
|
227
|
+
if (!r.businessEvent)
|
|
228
|
+
diags.push({
|
|
229
|
+
level: 'error',
|
|
230
|
+
code: 'API_EVENT_NO_BUSINESS_EVENT',
|
|
231
|
+
message: 'Rule "api-event" names no business event, so it listens for nothing — pass { businessEvent: \'<name>\' } with a name declared by .businessEvent(\'<name>\').',
|
|
232
|
+
where,
|
|
233
|
+
});
|
|
234
|
+
else if (!businessEvents.has(r.businessEvent))
|
|
235
|
+
diags.push({
|
|
236
|
+
level: 'error',
|
|
237
|
+
code: 'UNKNOWN_BUSINESS_EVENT',
|
|
238
|
+
message: `Rule "api-event" listens for business event "${r.businessEvent}", which the case does not declare — add .businessEvent(${JSON.stringify(r.businessEvent)}) to the case.`,
|
|
239
|
+
where,
|
|
240
|
+
});
|
|
241
|
+
};
|
|
215
242
|
const refPlacement = (r, slot, where) => {
|
|
243
|
+
for (const problem of ruleShapeProblems(r))
|
|
244
|
+
diags.push({ level: 'error', code: problem.code, message: problem.message, where });
|
|
245
|
+
refEvent(r, where);
|
|
216
246
|
const bad = ILLEGAL.find((x) => x.rule === r.rule && x.slot === slot);
|
|
217
247
|
if (bad)
|
|
218
248
|
diags.push({
|
|
@@ -432,7 +462,7 @@ function checkStage(stage, diags, refStage, refPlacement, bindings) {
|
|
|
432
462
|
// fails. So the signature below must include the expression and the refs, not
|
|
433
463
|
// just the rule name — keying on the rule type alone would reject legal
|
|
434
464
|
// value-tiered routing, which is the natural way to express "under $25k goes here".
|
|
435
|
-
const sig = (c) => JSON.stringify(c.rules.map((g) => g.map((r) => [r.rule, r.stage ?? '', (r.tasks ?? []).join(','), r.expression ?? ''])));
|
|
465
|
+
const sig = (c) => JSON.stringify(c.rules.map((g) => g.map((r) => [r.rule, r.stage ?? '', (r.tasks ?? []).join(','), r.expression ?? '', r.businessEvent ?? ''])));
|
|
436
466
|
const seenExit = new Map();
|
|
437
467
|
stage.exit.forEach((c, i) => {
|
|
438
468
|
const k = sig(c);
|
package/dist/case/decompile.d.ts
CHANGED
|
@@ -40,6 +40,7 @@ interface Rule {
|
|
|
40
40
|
uipath?: SubscriptionBag;
|
|
41
41
|
slaId?: string;
|
|
42
42
|
escalationId?: string;
|
|
43
|
+
businessEventType?: string;
|
|
43
44
|
}
|
|
44
45
|
interface Condition {
|
|
45
46
|
id?: string;
|
|
@@ -137,6 +138,12 @@ interface CasePlan {
|
|
|
137
138
|
caseAppEnabled?: boolean;
|
|
138
139
|
caseExitRules?: Condition[];
|
|
139
140
|
slaRules?: SlaWire[];
|
|
141
|
+
businessEvents?: Array<{
|
|
142
|
+
name?: unknown;
|
|
143
|
+
description?: unknown;
|
|
144
|
+
schema?: unknown;
|
|
145
|
+
[key: string]: unknown;
|
|
146
|
+
}>;
|
|
140
147
|
caseAppConfig?: {
|
|
141
148
|
caseSummary?: unknown;
|
|
142
149
|
sections?: Array<{
|
package/dist/case/decompile.js
CHANGED
|
@@ -13,6 +13,7 @@ const TASK_KIND_METHOD = {
|
|
|
13
13
|
'api-workflow': 'apiWorkflow',
|
|
14
14
|
'case-management': 'caseManagement',
|
|
15
15
|
'flow-process': 'flowProcess',
|
|
16
|
+
function: 'function',
|
|
16
17
|
};
|
|
17
18
|
function boolLit(value) {
|
|
18
19
|
return value ? 'true' : 'false';
|
|
@@ -261,11 +262,46 @@ function emitRule(rule, refs) {
|
|
|
261
262
|
entries.push(['escalation', tsLiteral(escalation)]);
|
|
262
263
|
}
|
|
263
264
|
}
|
|
265
|
+
if (rule.rule === 'api-event' && rule.businessEventType !== undefined)
|
|
266
|
+
entries.push(['businessEvent', tsLiteral(rule.businessEventType)]);
|
|
264
267
|
if (rule.conditionExpression !== undefined)
|
|
265
268
|
entries.push(['expression', tsLiteral(rule.conditionExpression)]);
|
|
266
269
|
const options = opts(entries);
|
|
267
270
|
return options ? `rule(${tsLiteral(rule.rule)}, ${options})` : `rule(${tsLiteral(rule.rule)})`;
|
|
268
271
|
}
|
|
272
|
+
/**
|
|
273
|
+
* `.businessEvent(name, { description, payload })` for one
|
|
274
|
+
* `metadata.businessEvents[]` entry. The Designer stores the payload schema as
|
|
275
|
+
* a JSON string; it comes back as the object the builder takes, `$schema`
|
|
276
|
+
* included, so the recompile writes the same string.
|
|
277
|
+
*/
|
|
278
|
+
function businessEventSource(event) {
|
|
279
|
+
if (!event || typeof event !== 'object' || typeof event.name !== 'string')
|
|
280
|
+
throw new Error('a business event has no name, which is not authorable.');
|
|
281
|
+
const unknown = Object.keys(event).filter((key) => !['name', 'description', 'schema'].includes(key));
|
|
282
|
+
if (unknown.length)
|
|
283
|
+
throw new Error(`business event "${event.name}" has unsupported field "${unknown[0]}", which is not authorable.`);
|
|
284
|
+
if (event.description !== undefined && typeof event.description !== 'string')
|
|
285
|
+
throw new Error(`business event "${event.name}" has a non-string description.`);
|
|
286
|
+
let payload;
|
|
287
|
+
if (event.schema !== undefined) {
|
|
288
|
+
if (typeof event.schema !== 'string')
|
|
289
|
+
throw new Error(`business event "${event.name}" has a non-string schema.`);
|
|
290
|
+
try {
|
|
291
|
+
payload = JSON.parse(event.schema);
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
throw new Error(`business event "${event.name}" has a schema that is not JSON.`);
|
|
295
|
+
}
|
|
296
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload))
|
|
297
|
+
throw new Error(`business event "${event.name}" has a schema that is not a JSON Schema object.`);
|
|
298
|
+
}
|
|
299
|
+
const options = opts([
|
|
300
|
+
['description', event.description !== undefined ? tsLiteral(event.description) : undefined],
|
|
301
|
+
['payload', payload !== undefined ? tsLiteral(payload) : undefined],
|
|
302
|
+
]);
|
|
303
|
+
return options ? `.businessEvent(${tsLiteral(event.name)}, ${options})` : `.businessEvent(${tsLiteral(event.name)})`;
|
|
304
|
+
}
|
|
269
305
|
function emitRuleArg(condition, refs) {
|
|
270
306
|
const groups = condition.rules ?? [];
|
|
271
307
|
const rendered = groups.map((group) => group.map((rule) => emitRule(rule, refs)));
|
|
@@ -1126,7 +1162,7 @@ export function collectPreservation(plan) {
|
|
|
1126
1162
|
preservation.topLevel = topLevel;
|
|
1127
1163
|
const metadataKnown = new Set([
|
|
1128
1164
|
'caseIdentifier', 'caseIdentifierType', 'caseAppEnabled', 'caseAppConfig', 'publishVersion', 'caseExitRules', 'slaRules',
|
|
1129
|
-
'allowAdhocOptionalStageTasks', 'waitForHumanSelectNextStageDFConnector',
|
|
1165
|
+
'allowAdhocOptionalStageTasks', 'waitForHumanSelectNextStageDFConnector', 'businessEvents',
|
|
1130
1166
|
]);
|
|
1131
1167
|
const metadata = Object.fromEntries(Object.entries(plan.metadata ?? {}).filter(([name]) => !metadataKnown.has(name)));
|
|
1132
1168
|
if (Object.keys(metadata).length)
|
|
@@ -1336,6 +1372,8 @@ export function decompileWithDiagnostics(plan, options = {}) {
|
|
|
1336
1372
|
emitVariables(out, depth, plan, triggerNameById);
|
|
1337
1373
|
triggers.forEach((_trigger, index) => out.line(depth, `.trigger(trigger${index + 1})`));
|
|
1338
1374
|
(metadata.slaRules ?? []).forEach((sla, index) => out.line(depth, `.sla(${slaSource(sla, index)})`));
|
|
1375
|
+
for (const event of metadata.businessEvents ?? [])
|
|
1376
|
+
out.line(depth, businessEventSource(event));
|
|
1339
1377
|
for (const node of plan.nodes ?? [])
|
|
1340
1378
|
if (isStage(node))
|
|
1341
1379
|
emitStage(out, depth, node, refs, options.library, selectNextStage);
|
package/dist/case/preserve.js
CHANGED
|
@@ -21,7 +21,7 @@ const TOP_LEVEL_KEYS = new Set([
|
|
|
21
21
|
const METADATA_KEYS = new Set([
|
|
22
22
|
'caseIdentifier', 'caseIdentifierType', 'caseAppEnabled', 'publishVersion', 'caseExitRules', 'slaRules',
|
|
23
23
|
'caseAppConfig', 'adhocCaseEndData',
|
|
24
|
-
'allowAdhocOptionalStageTasks', 'waitForHumanSelectNextStageDFConnector',
|
|
24
|
+
'allowAdhocOptionalStageTasks', 'waitForHumanSelectNextStageDFConnector', 'businessEvents',
|
|
25
25
|
]);
|
|
26
26
|
const VARIABLE_KEYS = new Set([
|
|
27
27
|
'id', 'name', 'type', 'default', 'body', '_jsonSchema', 'elementId', 'var',
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Options each rule kind reads; check and serialize refuse any other key. */
|
|
2
|
+
import type { CaseRule } from './case-sdk.js';
|
|
3
|
+
/** One problem with a rule's shape: an unknown kind, or an option its kind does not read. */
|
|
4
|
+
export interface RuleShapeProblem {
|
|
5
|
+
code: 'UNKNOWN_RULE' | 'RULE_UNKNOWN_OPTION';
|
|
6
|
+
message: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Report an unknown rule kind, or each option the rule's kind does not read.
|
|
10
|
+
* Options set to `undefined` count as absent.
|
|
11
|
+
*
|
|
12
|
+
* @param r - The rule as built (`rule(type, opts)` or a literal).
|
|
13
|
+
* @returns The problems, empty when the rule is well formed.
|
|
14
|
+
*/
|
|
15
|
+
export declare function ruleShapeProblems(r: CaseRule): RuleShapeProblem[];
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ownEntry } from '../core/own-entry.js';
|
|
2
|
+
/** `expression` is a gate any rule can carry. */
|
|
3
|
+
const COMMON = ['expression'];
|
|
4
|
+
const RULE_OPTIONS = {
|
|
5
|
+
'case-entered': [],
|
|
6
|
+
'required-tasks-completed': [],
|
|
7
|
+
'required-stages-completed': [],
|
|
8
|
+
'selected-stage-completed': ['stage'],
|
|
9
|
+
'selected-stage-exited': ['stage'],
|
|
10
|
+
'selected-tasks-completed': ['tasks'],
|
|
11
|
+
'current-stage-entered': [],
|
|
12
|
+
adhoc: [],
|
|
13
|
+
'runs-sequentially': [],
|
|
14
|
+
'user-selected-stage': [],
|
|
15
|
+
'wait-for-connector': ['connector'],
|
|
16
|
+
'sla-status-change': ['sla', 'escalation'],
|
|
17
|
+
'api-event': ['businessEvent'],
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Report an unknown rule kind, or each option the rule's kind does not read.
|
|
21
|
+
* Options set to `undefined` count as absent.
|
|
22
|
+
*
|
|
23
|
+
* @param r - The rule as built (`rule(type, opts)` or a literal).
|
|
24
|
+
* @returns The problems, empty when the rule is well formed.
|
|
25
|
+
*/
|
|
26
|
+
export function ruleShapeProblems(r) {
|
|
27
|
+
const kind = r.rule;
|
|
28
|
+
const own = ownEntry(RULE_OPTIONS, kind);
|
|
29
|
+
if (!own)
|
|
30
|
+
return [
|
|
31
|
+
{
|
|
32
|
+
code: 'UNKNOWN_RULE',
|
|
33
|
+
message: `Unknown rule "${kind}" — the rule kinds are ${Object.keys(RULE_OPTIONS).map((k) => `"${k}"`).join(', ')}.`,
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
const allowed = [...own, ...COMMON];
|
|
37
|
+
return Object.keys(r)
|
|
38
|
+
.filter((key) => key !== 'rule' && r[key] !== undefined)
|
|
39
|
+
.filter((key) => !allowed.includes(key))
|
|
40
|
+
.map((key) => ({
|
|
41
|
+
code: 'RULE_UNKNOWN_OPTION',
|
|
42
|
+
message: `Rule "${kind}" does not take option "${key}", so it would be dropped from the caseplan — ` +
|
|
43
|
+
`"${kind}" takes ${allowed.map((k) => `"${k}"`).join(', ')}.` +
|
|
44
|
+
(kind === 'api-event' ? ` Name the event with { businessEvent: '<name>' }.` : ''),
|
|
45
|
+
}));
|
|
46
|
+
}
|
package/dist/case/serialize.js
CHANGED
|
@@ -22,7 +22,9 @@ import { isResourceLabelRefusal, resolveResourceRef, resourceIdentity } from './
|
|
|
22
22
|
import { ownEntry } from '../core/own-entry.js';
|
|
23
23
|
import { resolveSchemaVersion } from '../schema-version.js';
|
|
24
24
|
import { caseJsonPreservation, caseJsonPreservationErrors, } from './preserve.js';
|
|
25
|
+
import { businessEventProblems } from './business-event-shape.js';
|
|
25
26
|
import { readEventFilter, eventFilterProblem, eventFilterTreeLeaf } from '../core/event-filters.js';
|
|
27
|
+
import { ruleShapeProblems } from './rule-options.js';
|
|
26
28
|
/**
|
|
27
29
|
* Condition `displayName` allocator.
|
|
28
30
|
*
|
|
@@ -90,6 +92,7 @@ export function serialize(built, opts = {}) {
|
|
|
90
92
|
slaIdByName: new Map(),
|
|
91
93
|
escalationId: new Map(),
|
|
92
94
|
escalationIdByName: new Map(),
|
|
95
|
+
businessEvents: new Set((built.businessEvents ?? []).map((event) => event.name)),
|
|
93
96
|
conditionName: conditionNamer(built),
|
|
94
97
|
};
|
|
95
98
|
// Preallocate SLA + escalation ids (before any rule is emitted) so a
|
|
@@ -221,6 +224,12 @@ export function serialize(built, opts = {}) {
|
|
|
221
224
|
if (built.slaRules.length) {
|
|
222
225
|
metadata.slaRules = built.slaRules.map((s, i, all) => emitSla(s, i, all.length, ids, resolver));
|
|
223
226
|
}
|
|
227
|
+
if (built.businessEvents?.length) {
|
|
228
|
+
const [problem] = businessEventProblems(built.businessEvents);
|
|
229
|
+
if (problem)
|
|
230
|
+
throw new Error(problem.message);
|
|
231
|
+
metadata.businessEvents = built.businessEvents.map(emitBusinessEvent);
|
|
232
|
+
}
|
|
224
233
|
// Variables: declared vars + the readable companions that task outputs bound
|
|
225
234
|
// (collected during stage emission above). A declared var wins on a name clash.
|
|
226
235
|
const variables = emitVariables(built.vars, ids, triggerNodeByRef);
|
|
@@ -905,7 +914,8 @@ function taskData(task, resolver, taskId, stageId, ids) {
|
|
|
905
914
|
// into the child (`data.inputs[]`) and reading results back (`data.outputs[]`)
|
|
906
915
|
// uses the identical io-binding wire as the other reference tasks (Gap F).
|
|
907
916
|
case 'case-management':
|
|
908
|
-
case 'flow-process':
|
|
917
|
+
case 'flow-process':
|
|
918
|
+
case 'function': {
|
|
909
919
|
// Reference-mode task, plus optional io-binding: input params and output
|
|
910
920
|
// extractions (`data.inputs[]` / `data.outputs[]`). Each row's `elementId`
|
|
911
921
|
// is "<stageId>-<taskId>"; an output also registers a root `inputOutputs`
|
|
@@ -1249,6 +1259,7 @@ const REFERENCE_BINDING = {
|
|
|
1249
1259
|
'api-workflow': { resource: 'process', subType: 'Api' },
|
|
1250
1260
|
'case-management': { resource: 'process', subType: 'CaseManagement' },
|
|
1251
1261
|
'flow-process': { resource: 'process' },
|
|
1262
|
+
function: { resource: 'process', subType: 'Function' },
|
|
1252
1263
|
};
|
|
1253
1264
|
/**
|
|
1254
1265
|
* Mint (once per resource + resourceKey + attribute) one root binding row of a
|
|
@@ -1745,6 +1756,9 @@ function emitRules(rules, seed, ownerId, ids, resolver, site) {
|
|
|
1745
1756
|
return rules.map((group, gi) => group.map((r, ri) => emitRule(r, `${seed}:${gi}:${ri}`, ownerId, ids, resolver, site)));
|
|
1746
1757
|
}
|
|
1747
1758
|
function emitRule(r, seed, ownerId, ids, resolver, site) {
|
|
1759
|
+
const [shape] = ruleShapeProblems(r);
|
|
1760
|
+
if (shape)
|
|
1761
|
+
throw new Error(`${shape.message.replace(/^Rule /, 'rule ')} (in ${site})`);
|
|
1748
1762
|
const ruleId = ids.ruleId(seed);
|
|
1749
1763
|
const out = { rule: r.rule, id: ruleId };
|
|
1750
1764
|
if (r.rule === 'selected-stage-completed' || r.rule === 'selected-stage-exited') {
|
|
@@ -1780,10 +1794,57 @@ function emitRule(r, seed, ownerId, ids, resolver, site) {
|
|
|
1780
1794
|
out.escalationId = escId;
|
|
1781
1795
|
}
|
|
1782
1796
|
}
|
|
1797
|
+
if (r.rule === 'api-event') {
|
|
1798
|
+
// The event is matched by its exact name at run time; a rule without one
|
|
1799
|
+
// listens for nothing, so both a missing and an undeclared name refuse.
|
|
1800
|
+
if (!r.businessEvent)
|
|
1801
|
+
throw new Error(`rule "api-event" in ${site} names no business event — write rule('api-event', { businessEvent: '<name>' }) with a name declared by .businessEvent('<name>').`);
|
|
1802
|
+
if (!resolver.businessEvents.has(r.businessEvent))
|
|
1803
|
+
throw new Error(`rule "api-event" in ${site} listens for business event "${r.businessEvent}", which the case does not declare — add .businessEvent(${JSON.stringify(r.businessEvent)}).`);
|
|
1804
|
+
out.businessEventType = r.businessEvent;
|
|
1805
|
+
}
|
|
1783
1806
|
if (r.expression)
|
|
1784
1807
|
out.conditionExpression = r.expression;
|
|
1785
1808
|
return out;
|
|
1786
1809
|
}
|
|
1810
|
+
const JSON_SCHEMA_DRAFT_7 = 'http://json-schema.org/draft-07/schema#';
|
|
1811
|
+
/**
|
|
1812
|
+
* True only for a payload that says nothing at all: `{}`, `{ type: 'object' }`
|
|
1813
|
+
* or `{ type: 'object', properties: {} }`. Any other key (`required`,
|
|
1814
|
+
* `$schema`, a non-object `type`, ...) is content the author wrote, so the
|
|
1815
|
+
* schema is written and survives a decompile/recompile round trip.
|
|
1816
|
+
*/
|
|
1817
|
+
function isEmptyObjectSchema(payload) {
|
|
1818
|
+
return Object.entries(payload).every(([key, value]) => {
|
|
1819
|
+
if (key === 'type')
|
|
1820
|
+
return value === 'object';
|
|
1821
|
+
if (key === 'properties')
|
|
1822
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value) && Object.keys(value).length === 0;
|
|
1823
|
+
return false;
|
|
1824
|
+
});
|
|
1825
|
+
}
|
|
1826
|
+
/**
|
|
1827
|
+
* One `metadata.businessEvents[]` entry, in the shape the Case Designer saves
|
|
1828
|
+
* (`{ name, description?, schema? }`). `schema` is the payload's JSON Schema
|
|
1829
|
+
* serialized to a string with `$schema` defaulted to draft 7. The payload is an
|
|
1830
|
+
* object or array schema (`businessEventProblems` refuses any other type before
|
|
1831
|
+
* this runs), and every one except the empty object schema
|
|
1832
|
+
* (`isEmptyObjectSchema`) is written. That keeps more than the Designer and `uip maestro case sdd
|
|
1833
|
+
* convert`, which write a schema only for `type: 'array'` or a non-empty
|
|
1834
|
+
* `properties`: `{ type: 'object', required: ['a'] }` is written here and left
|
|
1835
|
+
* out there, so a decompiled payload recompiles unchanged.
|
|
1836
|
+
*/
|
|
1837
|
+
function emitBusinessEvent(event) {
|
|
1838
|
+
const out = { name: event.name };
|
|
1839
|
+
if (event.description !== undefined)
|
|
1840
|
+
out.description = event.description;
|
|
1841
|
+
const payload = event.payload;
|
|
1842
|
+
if (payload !== undefined) {
|
|
1843
|
+
if (!isEmptyObjectSchema(payload))
|
|
1844
|
+
out.schema = JSON.stringify({ ...payload, $schema: payload.$schema ?? JSON_SCHEMA_DRAFT_7 });
|
|
1845
|
+
}
|
|
1846
|
+
return out;
|
|
1847
|
+
}
|
|
1787
1848
|
function emitEntry(c, seed, ownerId, label, ids, resolver) {
|
|
1788
1849
|
const out = {
|
|
1789
1850
|
id: ids.conditionId(seed),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uipath/maestro-builder-sdk",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.47.1",
|
|
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": "05cde3ec95db903ec81d2df24fd4e60bade77a97"
|
|
97
97
|
}
|