@uipath/maestro-builder-sdk 6.33.2 → 6.34.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.
@@ -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.
@@ -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,
@@ -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',
@@ -364,7 +364,12 @@ function syncBindingsV2(caseplan, outPath) {
364
364
  resource: group.resource,
365
365
  key: group.key,
366
366
  value: group.value,
367
- metadata: group.subType ? { subType: group.subType } : {},
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);
@@ -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 = `unsupported task kind "${kind}" for task "${task.displayName ?? task.id ?? '?'}".`;
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 31
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.1016.2',
33
- formatVersion: '30.0.0',
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 31
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
@@ -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
- (taskIdentity.outputs ?? []).forEach((row, index) => {
495
- const target = task?.data?.outputs?.[index];
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
  ];
@@ -106,11 +106,16 @@ export function schemaVersionRefusal(policy, requested) {
106
106
  if (isWritableSchemaVersion(policy, requested))
107
107
  return undefined;
108
108
  const writable = writableSchemaVersions(policy);
109
+ const newestWritable = writable.at(-1);
109
110
  const direction = policy.latestKnown !== undefined && schemaVersionAtLeast(requested, policy.latestKnown)
110
111
  ? compareSchemaVersions(requested, policy.latestKnown) > 0
111
112
  ? `The platform's newest known ${family} schema version is ${policy.latestKnown}, so ${requested} is newer than anything this SDK has read.`
112
113
  : `${requested} is the platform's current ${family} schema version, but this SDK build has not been taught to write it.`
113
- : `${requested} is older than the ${writable.length === 1 ? 'one version' : 'versions'} this build writes.`;
114
+ : newestWritable !== undefined && compareSchemaVersions(requested, newestWritable) > 0
115
+ ? `${requested} is a platform ${family} schema version newer than this build writes`
116
+ + (policy.latestKnown !== undefined ? ` (the platform's current is ${policy.latestKnown})` : '')
117
+ + '; this SDK build has not been taught to write it.'
118
+ : `${requested} is older than the ${writable.length === 1 ? 'one version' : 'versions'} this build writes.`;
114
119
  return (`${method}("${requested}"): this SDK build writes ${family} schema ${writable.join(' / ')}. ${direction} `
115
120
  + `A document's version claims that the WHOLE document matches that schema, so declaring one this build cannot emit `
116
121
  + `would write a false claim rather than a different document. ${policy.migration}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uipath/maestro-builder-sdk",
3
- "version": "6.33.2",
3
+ "version": "6.34.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": "04124f15f4e70827cb18bbd0f52c0bf676ceedd1"
96
+ "gitref": "aa9dc9a7c6f6adc8edbf4c20d0681307073bb53b"
97
97
  }