@uipath/maestro-builder-sdk 6.31.8 → 6.32.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.
@@ -1,6 +1,6 @@
1
1
  import { Expr } from '../../../core/expr.js';
2
2
  import { nearestName } from '../../../core/edit-distance.js';
3
- import { FORM_FIELD_TYPES, formFieldTypeHint, formFieldTypeWhy, validFormFieldTypesText } from '../../../core/form-field-type.js';
3
+ import { FORM_FIELD_TYPES, formFieldOptionsProblem, formFieldOptionsWhy, formFieldTypeHint, formFieldTypeWhy, validFormFieldTypesText } from '../../../core/form-field-type.js';
4
4
  import { isAnswerField } from '../../../core/hitl-answers.js';
5
5
  import { hitlDerivesOutcomeHandles, outcomeIdOf } from '../../../core/hitl-routing.js';
6
6
  export const HITL_DIAGNOSTICS = {
@@ -23,6 +23,8 @@ export const HITL_DIAGNOSTICS = {
23
23
  HITL_DUP_FIELD: { severity: 'error', summary: 'a form field id declared twice' },
24
24
  HITL_FIELD_RESERVED_ID: { severity: 'error', summary: 'a form field id the result already uses' },
25
25
  HITL_FIELD_TYPE_UNKNOWN: { severity: 'error', summary: 'a form field type the platform does not recognize' },
26
+ HITL_FIELD_OPTIONS: { severity: 'error', summary: 'a picker with no choices, or choices that are not { id, value, label } strings' },
27
+ HITL_FIELD_OPTIONS_IGNORED: { severity: 'warning', summary: 'choices on a field type that does not render them' },
26
28
  HITL_FIELD_NO_VALUE: { severity: 'error', summary: 'a shown field with no value' },
27
29
  HITL_FIELD_VALUE_NOT_REF: { severity: 'error', summary: 'a shown field filled from a literal' },
28
30
  HITL_FIELD_VALUE_NOT_PATH: { severity: 'error', summary: 'a shown field filled from a computed expression' },
@@ -215,6 +217,10 @@ export function checkHitl(inputs, ctx) {
215
217
  at('HITL_FIELD_TYPE_UNKNOWN', `Human task "${step}" gives the form field "${id}" the type ${JSON.stringify(f?.type)}, `
216
218
  + `${formFieldTypeWhy(f?.type)}. Valid: ${validFormFieldTypesText()}.`, formFieldTypeHint(f?.type));
217
219
  }
220
+ const optionsProblem = formFieldOptionsProblem(f?.type, f.options);
221
+ if (optionsProblem !== undefined) {
222
+ at(optionsProblem === 'ignored' ? 'HITL_FIELD_OPTIONS_IGNORED' : 'HITL_FIELD_OPTIONS', `Human task "${step}"'s form field "${id}" ${formFieldOptionsWhy(optionsProblem, f?.type)}.`, `options: [{ id: 'opt-1', value: 'high', label: 'High' }, { id: 'opt-2', value: 'low', label: 'Low' }]`);
223
+ }
218
224
  if (isAnswerField(f))
219
225
  answers++; // an `output`, or an `inOut` whose reviewed value comes back
220
226
  if (f?.direction === 'input' || f?.direction === 'inOut') {
@@ -13,7 +13,7 @@
13
13
  * every edge out of an outcome-handle node to.
14
14
  */
15
15
  import { Expr, toExpr } from '../../../core/expr.js';
16
- import { formFieldJsonSchema, sourceFormFieldType } from '../../../core/form-field-type.js';
16
+ import { formFieldJsonSchema, formFieldOptionsWire, liftFormFieldOptions, sourceFormFieldType } from '../../../core/form-field-type.js';
17
17
  import { isAnswerField } from '../../../core/hitl-answers.js';
18
18
  import { OUTCOME_HANDLE_NODE_TYPES, hitlExitModel, outcomeIdOf, outcomeSlug } from '../../../core/hitl-routing.js';
19
19
  import { jsonClone } from '../../../core/json-clone.js';
@@ -86,6 +86,11 @@ function hitlInputs(inputs, nodeType, ctx) {
86
86
  if (f.required)
87
87
  base.required = true;
88
88
  }
89
+ // A picker's choices (a `singleSelect` dropdown, a `multiSelect`), as the
90
+ // canvas stores them on the row (#1235).
91
+ const options = formFieldOptionsWire(f.options);
92
+ if (options !== undefined)
93
+ base.options = options;
89
94
  return base;
90
95
  });
91
96
  const outcomes = (inputs?.outcomes ?? []).map((o, i) => {
@@ -326,6 +331,7 @@ function liftHitl(node, typeName, ctx) {
326
331
  const id = String(f.id ?? '');
327
332
  const direction = String(f.direction ?? 'output');
328
333
  const value = direction === 'input' || direction === 'inOut' ? bindingExpr(f.binding) : undefined;
334
+ const options = liftFormFieldOptions(f.options);
329
335
  return {
330
336
  id,
331
337
  // serialize defaults a row's label to its id.
@@ -336,6 +342,7 @@ function liftHitl(node, typeName, ctx) {
336
342
  direction,
337
343
  ...(value !== undefined ? { value } : {}),
338
344
  ...(f.required === true ? { required: true } : {}),
345
+ ...(options !== undefined ? { options } : {}),
339
346
  };
340
347
  });
341
348
  const outcomeRows = (Array.isArray(schema.outcomes) ? schema.outcomes : []);
@@ -498,6 +505,11 @@ function printHitl({ inputs }, ctx) {
498
505
  ['direction', ctx.string(f.direction)],
499
506
  ['value', f.value !== undefined ? ctx.value(f.value) : undefined],
500
507
  ['required', f.required ? 'true' : undefined],
508
+ ['options', f.options !== undefined ? `[${f.options.map((o) => obj([
509
+ ['id', ctx.string(o.id)],
510
+ ['value', ctx.string(o.value)],
511
+ ['label', ctx.string(o.label)],
512
+ ], ctx)).join(', ')}]` : undefined],
501
513
  ], ctx));
502
514
  const outcomes = h.outcomes.map((o) => (typeof o === 'string' ? ctx.string(o) : obj([
503
515
  ['name', ctx.string(o.name)],
package/dist/serialize.js CHANGED
@@ -20,7 +20,7 @@ import { FLOW_NODE_TYPES } from './flow-node-registry.js';
20
20
  import { stepExits } from './flow-step-exits.js';
21
21
  import { resolveVersionCarried } from './core/nodes/versioned.js';
22
22
  import { indexCarriedDefinitions } from './core/carried-definitions.js';
23
- import { formFieldJsonSchema } from './core/form-field-type.js';
23
+ import { formFieldJsonSchema, formFieldOptionsWire } from './core/form-field-type.js';
24
24
  import { ConnectorResolveError } from './core/connector-resolve-error.js';
25
25
  import { connectionConnectors } from './bindings-v2.js';
26
26
  import { BindingRefusal, GUID_RE, bindingLabelRefusal, bindingMissingMessage, missingBindingLabels } from './core/binding-messages.js';
@@ -3862,6 +3862,7 @@ function newScope(outputs, vars = [], backing = new Map(), backingReads = backin
3862
3862
  stepNodeId: new Map(),
3863
3863
  pendingEdges: [],
3864
3864
  openTails: false,
3865
+ endNodeId: new Map(),
3865
3866
  };
3866
3867
  }
3867
3868
  /**
@@ -3905,6 +3906,7 @@ function newScope(outputs, vars = [], backing = new Map(), backingReads = backin
3905
3906
  */
3906
3907
  /**
3907
3908
  * Wire every parked `.stepToRef('<step>')` edge, now that the whole scope exists.
3909
+ * The target is a step, or else a named End (`.return({}, { name })`, #1223).
3908
3910
  *
3909
3911
  * An unresolved target should be impossible — `check`'s REF_UNKNOWN_TARGET
3910
3912
  * runs first and names the steps that do exist — but a serializer that silently
@@ -3914,10 +3916,12 @@ function newScope(outputs, vars = [], backing = new Map(), backingReads = backin
3914
3916
  */
3915
3917
  function resolveRejoins(scope, addEdge) {
3916
3918
  for (const { from, target } of scope.pendingEdges) {
3917
- const id = scope.stepNodeId.get(target);
3919
+ const id = scope.stepNodeId.get(target) ?? scope.endNodeId.get(target);
3918
3920
  if (!id) {
3921
+ const ends = [...scope.endNodeId.keys()];
3919
3922
  throw new Error(`.stepToRef('${target}') names a step this flow does not have. Known steps: `
3920
- + `${[...scope.stepNodeId.keys()].join(', ') || '(none)'}.`);
3923
+ + `${[...scope.stepNodeId.keys()].join(', ') || '(none)'}`
3924
+ + `${ends.length ? `; named Ends: ${ends.join(', ')}` : ''}.`);
3921
3925
  }
3922
3926
  // Through the serializer's own `addEdge`, so the id comes from the one
3923
3927
  // file-wide counter. A per-scope count (`edge_ref_${scope.edges.length}`)
@@ -5533,12 +5537,17 @@ export function serialize(built, opts = {}) {
5533
5537
  const qnode = makeNode(qdef.nodeType, qid, esc.name, { parentId, def: qdef });
5534
5538
  qnode.display = { ...qnode.display, shape: 'circle' };
5535
5539
  delete qnode.model;
5536
- const rows = (esc.fields ?? []).map((f) => ({
5537
- id: f.id,
5538
- label: f.label ?? f.id,
5539
- type: f.type,
5540
- direction: f.direction,
5541
- }));
5540
+ const rows = (esc.fields ?? []).map((f) => {
5541
+ const options = formFieldOptionsWire(f.options);
5542
+ return {
5543
+ id: f.id,
5544
+ label: f.label ?? f.id,
5545
+ type: f.type,
5546
+ direction: f.direction,
5547
+ // A picker's choices, as the human task's rows carry them (#1235).
5548
+ ...(options !== undefined ? { options } : {}),
5549
+ };
5550
+ });
5542
5551
  const qsource = fnv1aX4Uuid(`${built.id}/${step.name}/escalation/${esc.name}`);
5543
5552
  qnode.inputs = {
5544
5553
  source: qsource,
@@ -6500,6 +6509,11 @@ export function serialize(built, opts = {}) {
6500
6509
  scope.variableUpdates[id] = updates;
6501
6510
  if (name !== undefined && !prior) {
6502
6511
  (namedEnds.get(scope) ?? namedEnds.set(scope, new Map()).get(scope)).set(name, { id, sig });
6512
+ // A NAMED End can be a `.stepToRef` target (#1223); an unnamed one
6513
+ // cannot, since its id (`end`, `end2`, …) depends on emission order.
6514
+ // First wins, like `stepUid`: two Ends sharing a name with different
6515
+ // content are `check`'s REF_AMBIGUOUS_TARGET when a ref names them.
6516
+ scope.endNodeId.set(name, id);
6503
6517
  }
6504
6518
  }
6505
6519
  tails = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uipath/maestro-builder-sdk",
3
- "version": "6.31.8",
3
+ "version": "6.32.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": "294a7da19cbb9eaad2ae8b97b35fdc07923d50ac"
96
+ "gitref": "83ae5bb02e8784f1a445c0377a09daae9ffc5b26"
97
97
  }