@uipath/maestro-builder-sdk 6.55.0 → 6.56.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 +97 -80
- package/dist/api-members.md +372 -345
- package/dist/bpmn/bpmn-sdk.d.ts +110 -27
- package/dist/bpmn/bpmn-sdk.js +84 -6
- package/dist/bpmn/declared-vars.js +3 -2
- package/dist/bpmn/decompile.js +66 -0
- package/dist/bpmn/human-task-form.d.ts +170 -0
- package/dist/bpmn/human-task-form.js +321 -0
- package/dist/bpmn/serialize.js +8 -0
- package/dist/bpmn/typed-node.d.ts +6 -0
- package/dist/bpmn/typed-node.js +6 -0
- package/package.json +2 -2
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import { FORM_FIELD_TYPES, formFieldJsonSchema, formFieldOptionsProblem, formFieldOptionsWhy, formFieldSettingProblems, formFieldSettingsWire, formFieldTypeHint, liftFormFieldSettings, validFormFieldTypesText, wireFormFieldType, } from '../core/form-field-type.js';
|
|
2
|
+
import { outcomeIdOf } from '../core/hitl-routing.js';
|
|
3
|
+
import { schemaId as stableSchemaId } from '../core/stable-id.js';
|
|
4
|
+
/** The `uipath:type` version both the converter and Studio Web write for `Actions.HITL`. */
|
|
5
|
+
const QUICK_TYPE_VERSION = 'v2';
|
|
6
|
+
/** The response row the app variant derives from the registry; the quick form keeps it. */
|
|
7
|
+
const RESPONSE_ROW = 'Process response';
|
|
8
|
+
const DRAFT_07 = 'http://json-schema.org/draft-07/schema#';
|
|
9
|
+
/**
|
|
10
|
+
* A field id has to be a plain, unreserved identifier, for two reasons.
|
|
11
|
+
*
|
|
12
|
+
* An answer is read back as the bare expression `=<id>`, and `=review-note` is a
|
|
13
|
+
* subtraction. And the local evaluator makes every key of the response a PARAMETER of
|
|
14
|
+
* one `new Function(...keys, body)`, so a single key that cannot be a parameter name —
|
|
15
|
+
* `review-note`, `default`, `true` — throws for every output row of the task, the
|
|
16
|
+
* outcome mapping included, and `applyOutputs` writes `undefined` for all of them.
|
|
17
|
+
*/
|
|
18
|
+
const IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
|
|
19
|
+
/** Words a parameter cannot be named: ECMAScript's reserved words, strict-mode ones, and the literal-like globals. */
|
|
20
|
+
const RESERVED_WORDS = new Set([
|
|
21
|
+
'await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default', 'delete', 'do', 'else',
|
|
22
|
+
'enum', 'export', 'extends', 'false', 'finally', 'for', 'function', 'if', 'import', 'in', 'instanceof', 'new',
|
|
23
|
+
'null', 'return', 'super', 'switch', 'this', 'throw', 'true', 'try', 'typeof', 'var', 'void', 'while', 'with',
|
|
24
|
+
'yield', 'let', 'static', 'implements', 'interface', 'package', 'private', 'protected', 'public',
|
|
25
|
+
'arguments', 'eval', 'undefined', 'NaN', 'Infinity',
|
|
26
|
+
]);
|
|
27
|
+
/** The response's own key for the outcome picked — an answer cannot claim it. */
|
|
28
|
+
const OUTCOME_KEY = 'Action';
|
|
29
|
+
/** Whether a field is shown to the reviewer (its value is sent). */
|
|
30
|
+
function isShown(f) {
|
|
31
|
+
return f.direction === 'input' || f.direction === 'inOut';
|
|
32
|
+
}
|
|
33
|
+
/** Whether a field comes back as an answer. */
|
|
34
|
+
function isAnswer(f) {
|
|
35
|
+
return f.direction === 'output' || f.direction === 'inOut';
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* A shown value as the form schema's `binding`: the variable PATH the designer reads
|
|
39
|
+
* (`vars.amount`), when the expression is one; otherwise the expression as written.
|
|
40
|
+
*/
|
|
41
|
+
function bindingOf(value) {
|
|
42
|
+
const path = /^=(?:js:)?(vars\.[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)$/.exec(value);
|
|
43
|
+
return path ? path[1] : value;
|
|
44
|
+
}
|
|
45
|
+
/** An output row's type for a field: its JSON Schema type, with structures as `json`. */
|
|
46
|
+
function rowTypeOf(type) {
|
|
47
|
+
const json = formFieldJsonSchema(type)?.type ?? 'string';
|
|
48
|
+
return json === 'object' || json === 'array' ? 'json' : json;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Refuse a form the engine could not render, at the call that wrote it.
|
|
52
|
+
*
|
|
53
|
+
* @throws Error naming the task and what is wrong.
|
|
54
|
+
*/
|
|
55
|
+
function validate(id, input) {
|
|
56
|
+
const { form } = input;
|
|
57
|
+
const where = `humanTask "${id}"`;
|
|
58
|
+
if (!Array.isArray(form.fields))
|
|
59
|
+
throw new Error(`${where}: form.fields must be an array.`);
|
|
60
|
+
if (!Array.isArray(form.outcomes) || form.outcomes.length === 0) {
|
|
61
|
+
throw new Error(`${where}: the form needs at least one outcome, e.g. outcomes: ['Approve', 'Reject'].`);
|
|
62
|
+
}
|
|
63
|
+
const ids = new Set();
|
|
64
|
+
for (const f of form.fields) {
|
|
65
|
+
if (typeof f.id !== 'string' || f.id === '')
|
|
66
|
+
throw new Error(`${where}: every form field needs an id.`);
|
|
67
|
+
if (ids.has(f.id))
|
|
68
|
+
throw new Error(`${where}: form field id "${f.id}" is used twice.`);
|
|
69
|
+
ids.add(f.id);
|
|
70
|
+
if (f.id === OUTCOME_KEY) {
|
|
71
|
+
throw new Error(`${where}: form field "${OUTCOME_KEY}" collides with the response's own "${OUTCOME_KEY}" — the outcome picked. Rename it, e.g. id: '${OUTCOME_KEY}Value'.`);
|
|
72
|
+
}
|
|
73
|
+
if (!IDENTIFIER.test(f.id)) {
|
|
74
|
+
throw new Error(`${where}: form field "${f.id}" (${f.direction}) is not a name the runtime can read back as =${f.id}. ` +
|
|
75
|
+
`Use an identifier (letters, digits, _ or $), e.g. id: '${f.id.replace(/\W+(\w)?/g, (_m, c) => (c ?? '').toUpperCase())}'.`);
|
|
76
|
+
}
|
|
77
|
+
if (RESERVED_WORDS.has(f.id)) {
|
|
78
|
+
throw new Error(`${where}: form field "${f.id}" is a reserved word, which the runtime cannot bind as a name — it would lose every answer and the outcome. Rename it, e.g. id: '${f.id}Value'.`);
|
|
79
|
+
}
|
|
80
|
+
// The SDK's own spellings only. The product also resolves platform aliases such as
|
|
81
|
+
// `System.String`, but those are not `FormFieldType`s: accepting one here would let a
|
|
82
|
+
// decompile lift write source that does not type-check, so such an import stays verbatim.
|
|
83
|
+
if (!FORM_FIELD_TYPES.includes(String(f.type))) {
|
|
84
|
+
throw new Error(`${where}: form field "${f.id}" has type "${String(f.type)}". Valid: ${validFormFieldTypesText()}. ${formFieldTypeHint(f.type)}`.trimEnd());
|
|
85
|
+
}
|
|
86
|
+
// The checks Flow's form checker reports as errors: a picker the reviewer cannot
|
|
87
|
+
// pick from, and a setting the platform's schema refuses. (Options on a field that
|
|
88
|
+
// renders no picker is a warning there, and is not refused here.)
|
|
89
|
+
if (formFieldOptionsProblem(f) === 'missing') {
|
|
90
|
+
throw new Error(`${where}: form field "${f.id}" ${formFieldOptionsWhy('missing', f.type)}. Add options: [{ id: 'o1', value: 'a', label: 'A' }, …].`);
|
|
91
|
+
}
|
|
92
|
+
const settings = formFieldSettingProblems(f);
|
|
93
|
+
if (settings.length)
|
|
94
|
+
throw new Error(`${where}: form field "${f.id}" sets ${settings.join('; ')}.`);
|
|
95
|
+
if (isShown(f) && (typeof f.value !== 'string' || !f.value.startsWith('='))) {
|
|
96
|
+
throw new Error(`${where}: form field "${f.id}" (${f.direction}) needs a value — a =-expression such as '=vars.${f.id}' — or the form shows it empty.`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// One variable, one writer. Two answers landing in one variable, or an `outputs`
|
|
100
|
+
// mapping onto an answer's variable, both evaluate and the LAST row wins, so an answer
|
|
101
|
+
// is lost with no signal (`outputs: { decision: '=Action' }` beside a field named
|
|
102
|
+
// `decision` replaces the reviewer's text with the outcome name). The product's own
|
|
103
|
+
// converter never writes that shape (`applyHitlExtension` dedupes with `seenVars`).
|
|
104
|
+
const writers = new Map();
|
|
105
|
+
const claim = (variable, writer) => {
|
|
106
|
+
const first = writers.get(variable);
|
|
107
|
+
if (first !== undefined) {
|
|
108
|
+
throw new Error(`${where}: ${first} and ${writer} both write vars.${variable}, so one would overwrite the other. Give one a different variable.`);
|
|
109
|
+
}
|
|
110
|
+
writers.set(variable, writer);
|
|
111
|
+
};
|
|
112
|
+
claim(input.outputVar ?? `${id}_processResponse`, 'the task response (outputVar)');
|
|
113
|
+
for (const f of form.fields)
|
|
114
|
+
if (isAnswer(f))
|
|
115
|
+
claim(f.variable ?? f.id, `form field "${f.id}"`);
|
|
116
|
+
for (const v of Object.keys(input.outputs ?? {}))
|
|
117
|
+
claim(v, `outputs.${v}`);
|
|
118
|
+
const names = new Set();
|
|
119
|
+
const outcomeIds = new Map();
|
|
120
|
+
form.outcomes.forEach((o, i) => {
|
|
121
|
+
const name = typeof o === 'string' ? o : o?.name;
|
|
122
|
+
if (typeof name !== 'string' || name.trim() === '')
|
|
123
|
+
throw new Error(`${where}: every outcome needs a name.`);
|
|
124
|
+
if (names.has(name))
|
|
125
|
+
throw new Error(`${where}: outcome "${name}" is listed twice.`);
|
|
126
|
+
names.add(name);
|
|
127
|
+
// The schema's outcome ids must be unique too, and two names can reach one: an
|
|
128
|
+
// explicit `id` repeated, or `Needs review` and `Needs-review`, which both derive
|
|
129
|
+
// `needs-review`.
|
|
130
|
+
const outcomeId = outcomeIdOf(o, i);
|
|
131
|
+
const clash = outcomeIds.get(outcomeId);
|
|
132
|
+
if (clash !== undefined) {
|
|
133
|
+
throw new Error(`${where}: outcomes "${clash}" and "${name}" both have the id "${outcomeId}". Give one an explicit, different id: { name: '${name}', id: '…' }.`);
|
|
134
|
+
}
|
|
135
|
+
outcomeIds.set(outcomeId, name);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* The rows a quick human task is written as.
|
|
140
|
+
*
|
|
141
|
+
* @param id - The task's element id.
|
|
142
|
+
* @param processId - The process id, which the default `hitlSchemaId` is derived from.
|
|
143
|
+
* @param input - The form and the task options.
|
|
144
|
+
* @returns The context, payload and output rows, the input schema and the type version.
|
|
145
|
+
* @throws Error when the form is malformed — see {@link validate}.
|
|
146
|
+
*/
|
|
147
|
+
export function quickHumanTaskRows(id, processId, input) {
|
|
148
|
+
const { form } = input;
|
|
149
|
+
validate(id, input);
|
|
150
|
+
const schemaId = form.schemaId ?? stableSchemaId(processId, id);
|
|
151
|
+
const fields = form.fields.map((f) => {
|
|
152
|
+
const row = { id: f.id, label: f.label ?? f.id, type: wireFormFieldType(f.type), direction: f.direction };
|
|
153
|
+
if (isShown(f))
|
|
154
|
+
row.binding = bindingOf(f.value);
|
|
155
|
+
if (isAnswer(f)) {
|
|
156
|
+
row.variable = `vars.${f.variable ?? f.id}`;
|
|
157
|
+
if (typeof f.required === 'boolean')
|
|
158
|
+
row.required = f.required;
|
|
159
|
+
}
|
|
160
|
+
return { ...row, ...formFieldSettingsWire(f) };
|
|
161
|
+
});
|
|
162
|
+
const outcomes = form.outcomes.map((o, i) => ({
|
|
163
|
+
id: outcomeIdOf(o, i),
|
|
164
|
+
name: typeof o === 'string' ? o : o.name,
|
|
165
|
+
type: 'string',
|
|
166
|
+
isPrimary: i === 0,
|
|
167
|
+
action: 'Continue',
|
|
168
|
+
}));
|
|
169
|
+
const shown = form.fields.filter(isShown);
|
|
170
|
+
const bindings = Object.fromEntries(shown.map((f) => [f.id, bindingOf(f.value)]));
|
|
171
|
+
const contextRows = [
|
|
172
|
+
{ name: 'hitlType', type: 'string', value: 'quick' },
|
|
173
|
+
{ name: '__hitlFormSchema', type: 'json', body: { id: schemaId, fields, outcomes } },
|
|
174
|
+
{ name: 'hitlSchemaId', type: 'string', value: schemaId },
|
|
175
|
+
...(shown.length ? [{ name: '__hitlInputBindings', type: 'json', body: bindings }] : []),
|
|
176
|
+
{ name: 'taskTitle', type: 'string', value: input.title ?? input.name ?? id },
|
|
177
|
+
...(input.priority !== undefined ? [{ name: 'priority', type: 'string', value: input.priority }] : []),
|
|
178
|
+
...(input.labels !== undefined ? [{ name: 'labels', type: 'string', value: input.labels }] : []),
|
|
179
|
+
];
|
|
180
|
+
// What the form shows, as JSON Schema — the converter's `inputSchema`.
|
|
181
|
+
const properties = {};
|
|
182
|
+
const required = [];
|
|
183
|
+
for (const f of shown) {
|
|
184
|
+
properties[f.id] = { ...formFieldJsonSchema(f.type), title: f.label ?? f.id };
|
|
185
|
+
if (f.direction === 'inOut' && f.required === true)
|
|
186
|
+
required.push(f.id);
|
|
187
|
+
}
|
|
188
|
+
const inputSchema = { $schema: DRAFT_07, type: 'object', properties, ...(required.length ? { required } : {}) };
|
|
189
|
+
const inputRows = [
|
|
190
|
+
{ name: 'HitlTaskArguments', type: 'json', target: 'bodyField', body: Object.fromEntries(shown.map((f) => [f.id, f.value])) },
|
|
191
|
+
];
|
|
192
|
+
const fieldById = new Map(form.fields.map((f) => [f.id, f]));
|
|
193
|
+
const outputRows = [
|
|
194
|
+
{ name: RESPONSE_ROW, type: 'Actions.HITL', var: input.outputVar ?? `${id}_processResponse` },
|
|
195
|
+
...form.fields.filter(isAnswer).map((f) => {
|
|
196
|
+
const v = f.variable ?? f.id;
|
|
197
|
+
return { name: v, type: rowTypeOf(f.type), source: `=${f.id}`, var: v, custom: true };
|
|
198
|
+
}),
|
|
199
|
+
];
|
|
200
|
+
const mappedOutputRows = Object.entries(input.outputs ?? {}).map(([v, source]) => {
|
|
201
|
+
// The fallback type: an answer's field type for `=<fieldId>`, `string` otherwise
|
|
202
|
+
// (`=Action`, the outcome NAME, is a string). A declared variable overrides it.
|
|
203
|
+
const field = /^=([A-Za-z_$][\w$]*)$/.exec(source)?.[1];
|
|
204
|
+
const f = field === undefined ? undefined : fieldById.get(field);
|
|
205
|
+
return { name: v, type: f ? rowTypeOf(f.type) : 'string', source, var: v };
|
|
206
|
+
});
|
|
207
|
+
return { typeVersion: QUICK_TYPE_VERSION, contextRows, inputSchema, inputRows, outputRows, mappedOutputRows };
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Rows as comparable text: each row's own keys sorted (a row read from XML and a row
|
|
211
|
+
* built here need not list them in the same order), but every body as its exact TEXT.
|
|
212
|
+
* A body that parses to the same value with other whitespace or key order would compile
|
|
213
|
+
* back to different bytes, so it must not count as a match.
|
|
214
|
+
*/
|
|
215
|
+
function rowsText(rows) {
|
|
216
|
+
return JSON.stringify(rows.map((r) => {
|
|
217
|
+
const entries = Object.entries(r)
|
|
218
|
+
.filter(([, v]) => v !== undefined)
|
|
219
|
+
.map(([k, v]) => [k, k === 'body' || k === 'schema' ? textOf(v) : v]);
|
|
220
|
+
return Object.fromEntries(entries.sort(([a], [b]) => a.localeCompare(b)));
|
|
221
|
+
}));
|
|
222
|
+
}
|
|
223
|
+
/** A body as the text it is written as — a value is JSON-stringified, as the serializer does. */
|
|
224
|
+
function textOf(body) {
|
|
225
|
+
return typeof body === 'string' ? body : JSON.stringify(body);
|
|
226
|
+
}
|
|
227
|
+
/** Parse a json body read from XML, or `undefined` when it is not JSON. */
|
|
228
|
+
function bodyValue(body) {
|
|
229
|
+
if (typeof body !== 'string')
|
|
230
|
+
return body;
|
|
231
|
+
try {
|
|
232
|
+
return JSON.parse(body);
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
return undefined;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Read a quick task back as `.humanTask({ form })` options — or `undefined` when its
|
|
240
|
+
* rows are anything but what those options compile to.
|
|
241
|
+
*
|
|
242
|
+
* Best effort by construction, and safe because of the last step: the lifted options
|
|
243
|
+
* are run through {@link quickHumanTaskRows} and the result must equal the node's rows,
|
|
244
|
+
* order and all. A Studio Web task (sidecar schema, `_schemaFileId`, no response row)
|
|
245
|
+
* fails that and stays the verbatim `.activity()` it was.
|
|
246
|
+
*
|
|
247
|
+
* @param node - The node's rows, as decompile read them.
|
|
248
|
+
* @param processId - The id the decompiled `bpmn(…)` call will carry.
|
|
249
|
+
* @param authoredVarType - The wire type of each variable the decompiled source will
|
|
250
|
+
* DECLARE with `.var()` — what the serializer types a mapped `outputs` row from on the
|
|
251
|
+
* way back. A variable decompile folds away is not one: its row is re-typed from the
|
|
252
|
+
* fallback, so it has to already carry that type to match.
|
|
253
|
+
* @returns The options, or `undefined` to keep the node verbatim.
|
|
254
|
+
*/
|
|
255
|
+
export function liftQuickHumanTask(node, processId, authoredVarType = () => undefined) {
|
|
256
|
+
const ctx = new Map(node.contextRows.map((r) => [r.name, r]));
|
|
257
|
+
if (ctx.get('hitlType')?.value !== 'quick')
|
|
258
|
+
return undefined;
|
|
259
|
+
const schema = bodyValue(ctx.get('__hitlFormSchema')?.body);
|
|
260
|
+
if (!schema || !Array.isArray(schema.fields) || !Array.isArray(schema.outcomes))
|
|
261
|
+
return undefined;
|
|
262
|
+
const args = bodyValue(node.inputRows.find((r) => r.name === 'HitlTaskArguments')?.body);
|
|
263
|
+
let lifted;
|
|
264
|
+
try {
|
|
265
|
+
const fields = schema.fields.map((row) => {
|
|
266
|
+
const id = String(row.id);
|
|
267
|
+
const base = {
|
|
268
|
+
id,
|
|
269
|
+
...(row.label !== undefined && row.label !== id ? { label: String(row.label) } : {}),
|
|
270
|
+
type: row.type,
|
|
271
|
+
...liftFormFieldSettings(row),
|
|
272
|
+
};
|
|
273
|
+
const variable = typeof row.variable === 'string' ? row.variable.replace(/^vars\./, '') : undefined;
|
|
274
|
+
const answerVar = variable !== undefined && variable !== id ? { variable } : {};
|
|
275
|
+
const required = typeof row.required === 'boolean' ? { required: row.required } : {};
|
|
276
|
+
if (row.direction === 'input')
|
|
277
|
+
return { ...base, direction: 'input', value: String(args?.[id]) };
|
|
278
|
+
if (row.direction === 'inOut')
|
|
279
|
+
return { ...base, direction: 'inOut', value: String(args?.[id]), ...required, ...answerVar };
|
|
280
|
+
return { ...base, direction: 'output', ...required, ...answerVar };
|
|
281
|
+
});
|
|
282
|
+
const outcomes = schema.outcomes.map((o, i) => {
|
|
283
|
+
const name = String(o.name);
|
|
284
|
+
return outcomeIdOf(name, i) === o.id ? name : { name, id: String(o.id) };
|
|
285
|
+
});
|
|
286
|
+
const schemaId = String(schema.id);
|
|
287
|
+
const response = node.outputRows[0];
|
|
288
|
+
const answerVars = new Set(fields.filter((f) => f.direction !== 'input').map((f) => f.variable ?? f.id));
|
|
289
|
+
const extra = node.outputRows.slice(1).filter((r) => r.var !== undefined && !answerVars.has(r.var) && !r.custom);
|
|
290
|
+
lifted = {
|
|
291
|
+
form: { fields, outcomes, ...(schemaId !== stableSchemaId(processId, node.id) ? { schemaId } : {}) },
|
|
292
|
+
...(node.name !== undefined ? { name: node.name } : {}),
|
|
293
|
+
...(ctx.get('taskTitle')?.value !== (node.name ?? node.id) ? { title: ctx.get('taskTitle')?.value } : {}),
|
|
294
|
+
...(ctx.has('priority') ? { priority: ctx.get('priority').value } : {}),
|
|
295
|
+
...(ctx.has('labels') ? { labels: ctx.get('labels').value } : {}),
|
|
296
|
+
...(extra.length ? { outputs: Object.fromEntries(extra.map((r) => [r.var, r.source ?? ''])) } : {}),
|
|
297
|
+
...(response?.var !== undefined && response.var !== `${node.id}_processResponse` ? { outputVar: response.var } : {}),
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
return undefined;
|
|
302
|
+
}
|
|
303
|
+
let rebuilt;
|
|
304
|
+
try {
|
|
305
|
+
rebuilt = quickHumanTaskRows(node.id, processId, lifted);
|
|
306
|
+
}
|
|
307
|
+
catch {
|
|
308
|
+
return undefined;
|
|
309
|
+
}
|
|
310
|
+
const same = (node.typeVersion ?? 'v1') === rebuilt.typeVersion &&
|
|
311
|
+
rowsText(node.contextRows) === rowsText(rebuilt.contextRows) &&
|
|
312
|
+
node.inputSchema !== undefined &&
|
|
313
|
+
textOf(node.inputSchema) === textOf(rebuilt.inputSchema) &&
|
|
314
|
+
rowsText(node.inputRows) === rowsText(rebuilt.inputRows) &&
|
|
315
|
+
rowsText(node.outputRows) ===
|
|
316
|
+
rowsText([
|
|
317
|
+
...rebuilt.outputRows,
|
|
318
|
+
...rebuilt.mappedOutputRows.map((r) => ({ ...r, type: (r.var !== undefined ? authoredVarType(r.var) : undefined) ?? r.type })),
|
|
319
|
+
]);
|
|
320
|
+
return same ? lifted : undefined;
|
|
321
|
+
}
|
package/dist/bpmn/serialize.js
CHANGED
|
@@ -470,6 +470,7 @@ function createNode(ctx, n) {
|
|
|
470
470
|
skipCondition: n.skipCondition,
|
|
471
471
|
contextVerbatim: n.contextVerbatim,
|
|
472
472
|
outputRows: n.outputRows,
|
|
473
|
+
mappedOutputRows: n.mappedOutputRows,
|
|
473
474
|
}, (varId) => ctx.authoredVarType.get(varId)), n);
|
|
474
475
|
case 'subProcess':
|
|
475
476
|
return applyActivityFields(ctx, createSubProcess(ctx, n), n);
|
|
@@ -822,6 +823,13 @@ export function implicitNodeVars(nodes, declared) {
|
|
|
822
823
|
// is what keeps a node's own outputs inside the node: 83 of the 112 declarations
|
|
823
824
|
// in `claims-intake-hitl` are this shape, and every one of them had to be
|
|
824
825
|
// restated as a `.var()` because the derivation could not reach the row.
|
|
826
|
+
// A mapped row's own type is its fallback, so an undeclared variable is declared
|
|
827
|
+
// as the row is written; a declared one keeps its own (`claim` skips it). First, so
|
|
828
|
+
// `typedNodeDeclaredVars` below, which lists them too, cannot claim them untyped.
|
|
829
|
+
for (const row of n.mappedOutputRows ?? []) {
|
|
830
|
+
const d = rowDeclaration(row);
|
|
831
|
+
claim(row.var, n.id, d.type, d);
|
|
832
|
+
}
|
|
825
833
|
if (n.outputRows) {
|
|
826
834
|
for (const row of n.outputRows) {
|
|
827
835
|
const d = rowDeclaration(row);
|
|
@@ -376,6 +376,12 @@ export interface TypedNodeInput {
|
|
|
376
376
|
* for a caller that already knows the exact rows.
|
|
377
377
|
*/
|
|
378
378
|
outputRows?: TypedOutputRow[];
|
|
379
|
+
/**
|
|
380
|
+
* Rows written after everything else, each typed from the author-declared variable it
|
|
381
|
+
* writes (the `varType` callback), its own `type` otherwise. Unlike `outputs` these
|
|
382
|
+
* survive `outputRows`; `.humanTask({ form })` uses them for its `outputs`.
|
|
383
|
+
*/
|
|
384
|
+
mappedOutputRows?: TypedOutputRow[];
|
|
379
385
|
/** `=`-expression that skips the node when truthy (activity/event tags only). */
|
|
380
386
|
skipCondition?: string;
|
|
381
387
|
/**
|
package/dist/bpmn/typed-node.js
CHANGED
|
@@ -556,6 +556,7 @@ varType) {
|
|
|
556
556
|
// TypedNodeInput.outputRows — this is what lets a connector's response schema
|
|
557
557
|
// survive a round trip.
|
|
558
558
|
rows.push(...verbatimOutputRows(m, input.outputRows));
|
|
559
|
+
rows.push(...mappedRows(m, input.mappedOutputRows, varType));
|
|
559
560
|
if (rows.length)
|
|
560
561
|
payload.output = rows;
|
|
561
562
|
return finish(m, spec, id, input.name, payload);
|
|
@@ -588,10 +589,15 @@ varType) {
|
|
|
588
589
|
// which is what every row did before.
|
|
589
590
|
rows.push(m.create('uipath:Output', { name: varId, type: varType?.(varId) ?? 'string', source, var: varId }));
|
|
590
591
|
}
|
|
592
|
+
rows.push(...mappedRows(m, input.mappedOutputRows, varType));
|
|
591
593
|
if (rows.length)
|
|
592
594
|
payload.output = rows;
|
|
593
595
|
return finish(m, spec, id, input.name, payload);
|
|
594
596
|
}
|
|
597
|
+
/** `TypedNodeInput.mappedOutputRows`, each typed from its declared variable when there is one. */
|
|
598
|
+
function mappedRows(m, rows, varType) {
|
|
599
|
+
return verbatimOutputRows(m, (rows ?? []).map((r) => ({ ...r, type: (r.var !== undefined ? varType?.(r.var) : undefined) ?? r.type })));
|
|
600
|
+
}
|
|
595
601
|
/** `uipath:Output` rows exactly as the caller spelled them, deriving nothing. */
|
|
596
602
|
export function verbatimOutputRows(m, rows) {
|
|
597
603
|
return rows.map((row) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uipath/maestro-builder-sdk",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.56.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",
|
|
@@ -96,5 +96,5 @@
|
|
|
96
96
|
"@types/node": "^22.7.0",
|
|
97
97
|
"esbuild": "^0.28.1"
|
|
98
98
|
},
|
|
99
|
-
"gitref": "
|
|
99
|
+
"gitref": "31230281aaf4fa5e44d2844c08747ead2814e94d"
|
|
100
100
|
}
|