@uipath/maestro-builder-sdk 6.20.6 → 6.21.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.
@@ -0,0 +1,265 @@
1
+ import { Expr } from '../../../core/expr.js';
2
+ import { jsonClone } from '../../../core/json-file.js';
3
+ import { checkQueueItem } from './check.js';
4
+ import { QUEUE_ITEM_RECORD_SCHEMA } from './record.js';
5
+ const CREATE = 'core.action.queue.create';
6
+ const CREATE_AND_WAIT = 'core.action.queue.create-and-wait';
7
+ /**
8
+ * Build the definition for one queue-item node.
9
+ *
10
+ * Unlike `rpaWorkflowDef`, this one starts from the BUNDLED definition — the queue
11
+ * node types are generic (no tenant key in the type), so the platform's own copy
12
+ * can be shipped and is: `core-definitions.json` carries both at `@1.2`, verbatim
13
+ * from the catalogue the running `uip` consults (`@uipath/flow-core/manifest`'s
14
+ * `ootbNodes`). A test pins that the emitted definition is that copy plus exactly
15
+ * the three per-instance edits below, so the bundled snapshot stays diffable
16
+ * against upstream.
17
+ *
18
+ * VERSION 1.2, not the 1.0 the bundled sample carries. The platform is two
19
+ * migrations on, and both are semantic: 1.0 → 1.1 re-types `itemData` from
20
+ * `string` to `object`, and 1.1 → 1.2 makes the field validate as an object
21
+ * expression. `shared/sample-flows/queue-operations.flow` is v1.0-era (a JSON
22
+ * STRING in `itemData`) and dated 2026-05-20 — the shape the platform no longer
23
+ * writes. Reading it for the surface would have shipped the wrong field TYPE.
24
+ *
25
+ * The three per-instance edits:
26
+ *
27
+ * - `model.bindings.resourceKey` — the queue's own Orchestrator key. The shipped
28
+ * definition ships it BLANK, because only an instance knows which queue.
29
+ * - `model.context` — the `=bindings.<id>` references. The definition ships
30
+ * `<bindings.name>` placeholders; an instance names the actual binding ids,
31
+ * which is the second way `resourceBindings` can be resolved and the
32
+ * shape the bundled sample has.
33
+ * - `outputDefinition.output.schema` — the queue item's record for a plain
34
+ * create, or the author's `returns` for a create-and-wait. Without a schema a
35
+ * downstream read is unresolvable.
36
+ */
37
+ function queueItemDef(spec, nodeType, stepName, ids, base) {
38
+ // The template at the version being emitted: the registry resolved it exactly
39
+ // (an unregistered pin is NODE_VERSION_UNREGISTERED before this runs).
40
+ if (!base)
41
+ throw new Error(`no bundled definition for "${nodeType}"`);
42
+ const def = jsonClone(base);
43
+ def.model.bindings.resourceKey = spec.key;
44
+ def.model.context = [
45
+ { name: 'name', type: 'string', value: `=bindings.${ids.name}`, default: spec.queue },
46
+ { name: 'folderPath', type: 'string', value: `=bindings.${ids.folderPath}`, default: spec.folderPath },
47
+ { name: '_label', type: 'string', value: stepName },
48
+ ];
49
+ const schema = spec.wait
50
+ ? (spec.returns && Object.keys(spec.returns).length
51
+ ? {
52
+ type: 'object',
53
+ properties: Object.fromEntries(Object.entries(spec.returns).map(([k, t]) => [k, { type: t }])),
54
+ }
55
+ : undefined)
56
+ : jsonClone(QUEUE_ITEM_RECORD_SCHEMA);
57
+ if (schema)
58
+ def.outputDefinition.output.schema = schema;
59
+ return def;
60
+ }
61
+ /**
62
+ * The two bindings a queue reference carries — the same `name` + `folderPath` pair
63
+ * the process families use, keyed to the queue's own Orchestrator key rather than
64
+ * to a `<folderPath>.<name>` string. That is what the only queue instance the repo
65
+ * has (`sample-flows/queue-operations.flow`) uses, and it is the queue's stable
66
+ * identity: a queue can be renamed, its key cannot.
67
+ *
68
+ * ONE PAIR PER QUEUE, not per step. Bindings are addressed by
69
+ * `resource + resourceKey + propertyAttribute` — that is the key a bindings
70
+ * lookup map is built on — so a second pair for the same queue is not a second
71
+ * reference, it is a duplicate that shadows the first: both nodes resolve to
72
+ * whichever was inserted last, and the other pair sits in the file referenced by
73
+ * nothing. So two steps on the same queue SHARE these, and the ids come from the
74
+ * queue rather than from a step name.
75
+ *
76
+ * (The rpa family mints its pair from the STEP name, so two steps starting the same
77
+ * process emit the same duplicate-and-shadow shape. Nothing exercises it today —
78
+ * every rpa flow in the repo has one process step — and changing those ids would
79
+ * rewrite the rpa goldens, so it is recorded as a finding rather than fixed here.)
80
+ */
81
+ function queueItemBindings(spec, existing) {
82
+ const found = (attr) => [...existing.values()].find((b) => b.resource === 'queue' && b.resourceKey === spec.key && b.propertyAttribute === attr)?.id;
83
+ const already = { name: found('name'), folderPath: found('folderPath') };
84
+ if (already.name && already.folderPath) {
85
+ return { ids: { name: already.name, folderPath: already.folderPath }, entries: [] };
86
+ }
87
+ // A readable, stable id per queue. Two queues that share a NAME across folders
88
+ // would collide, so the id is suffixed until it is free.
89
+ const slug = spec.queue.replace(/[^A-Za-z0-9]/g, '') || 'Queue';
90
+ let base = `b${slug.charAt(0).toUpperCase()}${slug.slice(1)}`;
91
+ for (let n = 2; existing.has(`${base}Name`) || existing.has(`${base}FolderPath`); n++) {
92
+ base = `b${slug.charAt(0).toUpperCase()}${slug.slice(1)}${n}`;
93
+ }
94
+ const ids = { name: `${base}Name`, folderPath: `${base}FolderPath` };
95
+ return {
96
+ ids,
97
+ entries: [
98
+ {
99
+ id: ids.name,
100
+ name: 'name',
101
+ type: 'string',
102
+ resource: 'queue',
103
+ resourceKey: spec.key,
104
+ default: spec.queue,
105
+ propertyAttribute: 'name',
106
+ },
107
+ {
108
+ id: ids.folderPath,
109
+ name: 'folderPath',
110
+ type: 'string',
111
+ resource: 'queue',
112
+ resourceKey: spec.key,
113
+ default: spec.folderPath,
114
+ propertyAttribute: 'folderPath',
115
+ },
116
+ ],
117
+ };
118
+ }
119
+ /** A read of a queue step's output: `QUEUE_READ_*`, in the wording the rule had before the port. */
120
+ const read = (wait) => ({
121
+ codePrefix: 'QUEUE',
122
+ label: wait ? 'Queue wait' : 'Queue item',
123
+ whenUndeclared: `What the consumer writes into the processed item's result is that automation's business; `
124
+ + `declare the fields you read.`,
125
+ });
126
+ /**
127
+ * A plain enqueue publishes the queue ITEM's Orchestrator record (fixed, from
128
+ * `QUEUE_ITEM_RECORD_SCHEMA`); a `wait: true` publishes the processed result,
129
+ * whose fields only the author's `returns` states.
130
+ */
131
+ function queueOutput(inputs, wait) {
132
+ if (!wait) {
133
+ const fields = Object.fromEntries(Object.entries(QUEUE_ITEM_RECORD_SCHEMA.properties)
134
+ .map(([k, v]) => [k, v.type ?? 'any']));
135
+ return { kind: 'fields', fields, read: read(false) };
136
+ }
137
+ const returns = inputs?.returns ?? {};
138
+ return {
139
+ kind: 'fields',
140
+ fields: Object.keys(returns).length ? returns : undefined,
141
+ read: read(true),
142
+ };
143
+ }
144
+ /**
145
+ * The `returns` a create-and-wait was authored with, from the field map
146
+ * `queueItemDef` wrote into `outputDefinition.output.schema` and `makeNode`
147
+ * copied onto this node's own `outputs`. Read from the NODE, not the file's
148
+ * `definitions[]` entry: that holds one copy per `<type>@<version>` (the last
149
+ * step's), so two wait steps would share one schema. Undefined when the node
150
+ * carries no field map, which is what an omitted `returns` emits.
151
+ */
152
+ function liftReturns(node) {
153
+ const props = node
154
+ ?.outputs?.output?.schema?.properties;
155
+ if (!props || typeof props !== 'object')
156
+ return undefined;
157
+ const entries = Object.entries(props)
158
+ .filter(([, v]) => typeof v?.type === 'string')
159
+ .map(([k, v]) => [k, v.type]);
160
+ return entries.length ? Object.fromEntries(entries) : undefined;
161
+ }
162
+ function queueAt(typeName, wait) {
163
+ return {
164
+ kind: 'queueItem', typeName, version: '1.2', factory: 'queueItem', definition: 'per-tenant', errorPort: true,
165
+ rawNode: { factory: wait ? 'queueItem({ wait: true })' : 'queueItem()' },
166
+ serialize: ({ inputs: s }, ctx) => {
167
+ // The bindings first: two steps on the same queue share one pair, and the
168
+ // node's `model.context` has to name the ids that actually exist.
169
+ const { ids, entries } = queueItemBindings(s, ctx.bindings);
170
+ for (const b of entries)
171
+ ctx.bind(b);
172
+ const manifest = queueItemDef(s, typeName, ctx.step, ids, ctx.definition(typeName, ctx.version));
173
+ return {
174
+ definition: { source: 'snapshot', nodeType: typeName, version: ctx.version, manifest },
175
+ // The result is read as `$vars.<step>.output`, like every other family: the
176
+ // node's own output block maps the runtime `response` onto the variable
177
+ // `output` (`source: "=response", var: "output"`), and the product honours
178
+ // that mapping for both queue variants. The node KEEPS its copy of the model,
179
+ // as every core action node with a model does.
180
+ inputs: {
181
+ queue: { name: s.queue, folderPath: s.folderPath, key: s.key },
182
+ // `itemData`, not `item`: the surface's name is friendlier, the node's is
183
+ // the platform's. Rendered one level deep, as the item is FLAT.
184
+ itemData: Object.fromEntries(Object.entries(s.item ?? {}).map(([k, v]) => [k, ctx.render(v)])),
185
+ priority: s.priority ?? 'Normal',
186
+ reference: s.reference === undefined ? '' : ctx.render(s.reference),
187
+ deferDate: s.deferDate === undefined ? '' : ctx.render(s.deferDate),
188
+ dueDate: s.dueDate === undefined ? '' : ctx.render(s.dueDate),
189
+ },
190
+ };
191
+ },
192
+ deserialize: (node, ctx) => {
193
+ const inputs = node.inputs ?? {};
194
+ const q = (inputs.queue ?? {});
195
+ // A value as the author wrote it: an expression back to an Expr, a literal
196
+ // string as itself, any other JSON as is.
197
+ const value = (raw) => {
198
+ const text = ctx.sourceText(raw);
199
+ if (text === undefined)
200
+ return raw;
201
+ return text.startsWith('=js:') ? ctx.expr(text) : text;
202
+ };
203
+ // serialize writes '' for an omitted optional; that is not an authored value.
204
+ const optional = (raw) => {
205
+ const text = ctx.sourceText(raw);
206
+ return text === undefined || text === '' ? undefined : value(raw);
207
+ };
208
+ const priority = ctx.plainValue(inputs.priority);
209
+ // A wait's `returns` is the schema serialize wrote for THIS step (the
210
+ // template ships none), as the node's own `outputs` carries it.
211
+ const returns = wait ? liftReturns(node) : undefined;
212
+ return {
213
+ kind: 'queueItem',
214
+ inputs: {
215
+ queue: q.name, folderPath: q.folderPath, key: q.key,
216
+ item: Object.fromEntries(Object.entries((inputs.itemData ?? {}))
217
+ .map(([k, v]) => [k, value(v)])),
218
+ ...(typeof priority === 'string' ? { priority: priority } : {}),
219
+ ...(optional(inputs.reference) !== undefined ? { reference: optional(inputs.reference) } : {}),
220
+ ...(optional(inputs.deferDate) !== undefined ? { deferDate: optional(inputs.deferDate) } : {}),
221
+ ...(optional(inputs.dueDate) !== undefined ? { dueDate: optional(inputs.dueDate) } : {}),
222
+ ...(wait ? { wait: true } : {}),
223
+ ...(returns ? { returns } : {}),
224
+ },
225
+ };
226
+ },
227
+ print: ({ inputs }, ctx) => {
228
+ const rows = Object.entries(inputs.item ?? {});
229
+ return {
230
+ factory: 'queueItem',
231
+ options: [
232
+ ['queue', ctx.string(String(inputs.queue ?? ''))],
233
+ ['folderPath', ctx.string(String(inputs.folderPath ?? ''))],
234
+ ['key', ctx.string(String(inputs.key ?? ''))],
235
+ // An expression as the helper an author writes; any other value as the data
236
+ // the node stores, verbatim (a nested value is refused by check, but kept).
237
+ ['item', rows.length
238
+ ? `{ ${rows.map(([k, v]) => `${ctx.key(k)}: ${v instanceof Expr ? ctx.value(v) : ctx.json(v)}`).join(', ')} }`
239
+ : '{}'],
240
+ // `Normal` is the definition's default — only a departure was authored.
241
+ ['priority', inputs.priority !== undefined && inputs.priority !== 'Normal' ? ctx.string(inputs.priority) : undefined],
242
+ ['reference', inputs.reference === undefined ? undefined : ctx.value(inputs.reference)],
243
+ ['deferDate', inputs.deferDate === undefined ? undefined : ctx.value(inputs.deferDate)],
244
+ ['dueDate', inputs.dueDate === undefined ? undefined : ctx.value(inputs.dueDate)],
245
+ ['wait', wait ? 'true' : undefined],
246
+ ['returns', inputs.returns && Object.keys(inputs.returns).length
247
+ ? `{ ${Object.entries(inputs.returns).map(([k, t]) => `${ctx.key(k)}: ${ctx.string(String(t))}`).join(', ')} }`
248
+ : undefined],
249
+ ],
250
+ };
251
+ },
252
+ check: ({ inputs }, ctx) => checkQueueItem(inputs, ctx),
253
+ // check collected no expressions for a queue step before the port; unchanged by it.
254
+ expressions: () => [],
255
+ output: ({ inputs }) => queueOutput(inputs, wait),
256
+ };
257
+ }
258
+ function queueType(typeName, wait) {
259
+ return {
260
+ kind: 'queueItem', typeName, factory: 'queueItem', defaultVersion: '1.2',
261
+ versions: { '1.2': queueAt(typeName, wait) },
262
+ matches: ({ inputs }) => Boolean(inputs?.wait) === wait,
263
+ };
264
+ }
265
+ export const queueNodeTypes = [queueType(CREATE, false), queueType(CREATE_AND_WAIT, true)];
@@ -0,0 +1,92 @@
1
+ /**
2
+ * The Queue family's authoring types (`core.action.queue.create` and
3
+ * `core.action.queue.create-and-wait`), and its kind's entry in
4
+ * `FlowActionSpecs`. Moved from core/actions.ts in Phase 4 of
5
+ * docs/NODE_TYPE_ARCHITECTURE_DESIGN.md.
6
+ */
7
+ import type { ReturnFieldType } from '../../../core/actions.js';
8
+ import type { Expr } from '../../../core/expr.js';
9
+ /** The three priorities an Orchestrator queue item can carry. */
10
+ export type QueuePriority = 'Low' | 'Normal' | 'High';
11
+ export interface QueueItemInputs {
12
+ /** The queue's name in Orchestrator — e.g. `'SupplierInvoices'`. */
13
+ queue: string;
14
+ /** The Orchestrator folder holding the queue — e.g. `'Shared'`. */
15
+ folderPath: string;
16
+ /**
17
+ * The queue's own Orchestrator key (a GUID). It is the JOIN between this node
18
+ * and the flow's queue bindings — a node which does not reference its bindings
19
+ * cannot be dispatched — and it is what
20
+ * the designer's queue picker writes. The RUNTIME resolves the queue by
21
+ * `queue` + `folderPath`, so all three are needed and none is redundant.
22
+ *
23
+ * Find it with `uip or queues list --all-folders`.
24
+ */
25
+ key: string;
26
+ /**
27
+ * The work item's own data: a FLAT object of primitive values. It becomes the
28
+ * item's `SpecificContent`, and Orchestrator rejects a nested object or array
29
+ * with "Property values must be simple (like numbers or text)".
30
+ */
31
+ item: Record<string, unknown>;
32
+ /** Processing priority. Defaults to `'Normal'`, as the node type does. */
33
+ priority?: QueuePriority;
34
+ /**
35
+ * A reference string for the item. Optional in general — but a queue created
36
+ * with "enforce unique reference" REQUIRES one and rejects a repeat with
37
+ * `HTTP 409: Duplicate Reference`, so derive it from the flow's input rather
38
+ * than hard-coding it.
39
+ *
40
+ * Which is why it takes an {@link Expr} as well as a literal: the serializer
41
+ * renders it through the same path as any other value, so
42
+ * `reference: input('invoiceRef')` and
43
+ * ``reference: js`${input('invoiceRef')} + "-review"` `` both emit. Typing it
44
+ * as a bare `string` contradicted the advice directly above and stopped two
45
+ * of the SDK's own examples compiling.
46
+ */
47
+ reference?: string | Expr;
48
+ /** Earliest the item may be processed, ISO-8601 (e.g. `'2026-08-01T09:00:00Z'`). */
49
+ deferDate?: string;
50
+ /** Latest the item should be processed, ISO-8601. What an SLA is measured against. */
51
+ dueDate?: string;
52
+ /**
53
+ * `false` (the default) enqueues and moves on — `core.action.queue.create`.
54
+ * `true` PARKS the flow until whatever consumes the queue has processed the
55
+ * item, and publishes that result — `core.action.queue.create-and-wait`.
56
+ *
57
+ * The two are one construct with two data paths, so this is one field rather
58
+ * than two factories. Waiting is not free: nothing completes the item but a
59
+ * consumer (an RPA process bound to the queue), so a flow that waits on a queue
60
+ * nobody drains waits forever.
61
+ */
62
+ wait?: boolean;
63
+ /**
64
+ * Only for `wait: true`: the fields the PROCESSED item's result carries, and
65
+ * their types (e.g. `{ approved: 'boolean' }`). Required if anything reads the
66
+ * step's output.
67
+ *
68
+ * Why you have to say: what a consumer writes into a transaction's `Output` is
69
+ * that automation's business, not the queue's — no definition declares it and
70
+ * authoring happens offline. Without it `$vars.<step>.output.<field>` reads from
71
+ * an object with no declared fields, and nothing could tell a real field from
72
+ * a typo.
73
+ *
74
+ * `wait: false` needs none: what it publishes is the queue ITEM's own
75
+ * Orchestrator record (`Key`, `Status`, `Reference`, `SpecificContent`, …),
76
+ * which is the same for every queue, so the serializer declares it for you.
77
+ *
78
+ * @enforcedBy QUEUE_READ_UNKNOWN_FIELD A read must name a field of the record
79
+ * (`wait: false`) or of this `returns` (`wait: true`).
80
+ * @enforcedBy QUEUE_READ_WITHOUT_RETURNS Reading a field off a `wait: true` result
81
+ * requires declaring it here.
82
+ */
83
+ returns?: Record<string, ReturnFieldType>;
84
+ }
85
+ declare module '../../../core/actions.js' {
86
+ interface FlowActionSpecs {
87
+ /** The spec {@link queueItem} builds. */
88
+ queueItem: {
89
+ inputs: QueueItemInputs;
90
+ };
91
+ }
92
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -152,6 +152,13 @@ export declare function actionVersion(spec: {
152
152
  * it), so the error-port answer depends on the version.
153
153
  */
154
154
  export declare function bundledDefinition(nodeType: string, requestedVersion?: string): any | undefined;
155
+ /**
156
+ * The bundled definition a `.loop()` or `.doWhile()` container emits, at the
157
+ * version serialize writes (`.loop()` defaults to `@2.4`, `.doWhile()` to the
158
+ * pinned `core.logic.dowhile@1.0`). `check` asks it whether the container has an
159
+ * error port (#1010).
160
+ */
161
+ export declare function bundledContainerDefinition(kind: 'loop' | 'doWhile', requestedVersion?: string): any | undefined;
155
162
  /**
156
163
  * Resolve a contribution's {@link DefinitionReference} to a concrete manifest —
157
164
  * exactly (design §2.6/§4.2): the named version resolves or compilation fails,
package/dist/serialize.js CHANGED
@@ -58,7 +58,6 @@ import { planFlowGlobals, planSharedInputs, sharedInputRoots } from './flow-vari
58
58
  import { outputBackingGlobal, planOutputBacking } from './flow-output-backing.js';
59
59
  import { mapJsCode } from './core/js-code-spans.js';
60
60
  import { prepareCommand } from './core/cli-spelling.js';
61
- import { QUEUE_ITEM_RECORD_SCHEMA } from './core/queue-record.js';
62
61
  import { AGENTIC_PROCESS_FORMS } from './core/agentic-process-forms.js';
63
62
  import { resourceNodeSortOrder } from './core/process-resource-sort-order.js';
64
63
  import { types } from './core/expr.js';
@@ -121,8 +120,6 @@ const NODE_TYPE = {
121
120
  doWhile: 'core.logic.dowhile',
122
121
  subflow: 'core.subflow',
123
122
  terminate: 'core.logic.terminate',
124
- queueCreate: 'core.action.queue.create',
125
- queueCreateAndWait: 'core.action.queue.create-and-wait',
126
123
  // The two AI patterns. `summarize()` emits `deep-rag`: the canvas name is
127
124
  // "Summarize", the wire type is the backend's (`ECS.DeepRag`), and there is
128
125
  // no `uipath.pattern.summarize` node type at all.
@@ -2925,123 +2922,6 @@ function connectionResourceBindings(spec, existing) {
2925
2922
  });
2926
2923
  return { entries };
2927
2924
  }
2928
- /**
2929
- * Build the definition for one queue-item node.
2930
- *
2931
- * Unlike `rpaWorkflowDef`, this one starts from the BUNDLED definition — the queue
2932
- * node types are generic (no tenant key in the type), so the platform's own copy
2933
- * can be shipped and is: `core-definitions.json` carries both at `@1.2`, verbatim
2934
- * from the catalogue the running `uip` consults (`@uipath/flow-core/manifest`'s
2935
- * `ootbNodes`). A test pins that the emitted definition is that copy plus exactly
2936
- * the three per-instance edits below, so the bundled snapshot stays diffable
2937
- * against upstream.
2938
- *
2939
- * VERSION 1.2, not the 1.0 the bundled sample carries. The platform is two
2940
- * migrations on, and both are semantic: 1.0 → 1.1 re-types `itemData` from
2941
- * `string` to `object`, and 1.1 → 1.2 makes the field validate as an object
2942
- * expression. `shared/sample-flows/queue-operations.flow` is v1.0-era (a JSON
2943
- * STRING in `itemData`) and dated 2026-05-20 — the shape the platform no longer
2944
- * writes. Reading it for the surface would have shipped the wrong field TYPE.
2945
- *
2946
- * The three per-instance edits:
2947
- *
2948
- * - `model.bindings.resourceKey` — the queue's own Orchestrator key. The shipped
2949
- * definition ships it BLANK, because only an instance knows which queue.
2950
- * - `model.context` — the `=bindings.<id>` references. The definition ships
2951
- * `<bindings.name>` placeholders; an instance names the actual binding ids,
2952
- * which is the second way `resourceBindings` can be resolved and the
2953
- * shape the bundled sample has.
2954
- * - `outputDefinition.output.schema` — the queue item's record for a plain
2955
- * create, or the author's `returns` for a create-and-wait. Without a schema a
2956
- * downstream read is unresolvable.
2957
- */
2958
- function queueItemDef(spec, stepName, ids, version) {
2959
- const nodeType = spec.wait ? NODE_TYPE.queueCreateAndWait : NODE_TYPE.queueCreate;
2960
- // The template at the AUTHOR'S version, when the bundle has it. Starting from
2961
- // the default and handing it to makeNode with the pin refused every other
2962
- // bundled version — the bug #988 fixed for inline agents (#1128). An
2963
- // unbundled pin falls back to the default, and makeNode's exact-version
2964
- // refusal names what is bundled.
2965
- const base = (version !== undefined ? DEFS[`${nodeType}@${version}`] : undefined) ?? defByType.get(nodeType);
2966
- if (!base)
2967
- throw new Error(`no bundled definition for "${nodeType}"`);
2968
- const def = jsonClone(base);
2969
- def.model.bindings.resourceKey = spec.key;
2970
- def.model.context = [
2971
- { name: 'name', type: 'string', value: `=bindings.${ids.name}`, default: spec.queue },
2972
- { name: 'folderPath', type: 'string', value: `=bindings.${ids.folderPath}`, default: spec.folderPath },
2973
- { name: '_label', type: 'string', value: stepName },
2974
- ];
2975
- const schema = spec.wait
2976
- ? (spec.returns && Object.keys(spec.returns).length
2977
- ? {
2978
- type: 'object',
2979
- properties: Object.fromEntries(Object.entries(spec.returns).map(([k, t]) => [k, { type: t }])),
2980
- }
2981
- : undefined)
2982
- : jsonClone(QUEUE_ITEM_RECORD_SCHEMA);
2983
- if (schema)
2984
- def.outputDefinition.output.schema = schema;
2985
- return def;
2986
- }
2987
- /**
2988
- * The two bindings a queue reference carries — the same `name` + `folderPath` pair
2989
- * the process families use, keyed to the queue's own Orchestrator key rather than
2990
- * to a `<folderPath>.<name>` string. That is what the only queue instance the repo
2991
- * has (`sample-flows/queue-operations.flow`) uses, and it is the queue's stable
2992
- * identity: a queue can be renamed, its key cannot.
2993
- *
2994
- * ONE PAIR PER QUEUE, not per step. Bindings are addressed by
2995
- * `resource + resourceKey + propertyAttribute` — that is the key a bindings
2996
- * lookup map is built on — so a second pair for the same queue is not a second
2997
- * reference, it is a duplicate that shadows the first: both nodes resolve to
2998
- * whichever was inserted last, and the other pair sits in the file referenced by
2999
- * nothing. So two steps on the same queue SHARE these, and the ids come from the
3000
- * queue rather than from a step name.
3001
- *
3002
- * (The rpa family mints its pair from the STEP name, so two steps starting the same
3003
- * process emit the same duplicate-and-shadow shape. Nothing exercises it today —
3004
- * every rpa flow in the repo has one process step — and changing those ids would
3005
- * rewrite the rpa goldens, so it is recorded as a finding rather than fixed here.)
3006
- */
3007
- function queueItemBindings(spec, existing) {
3008
- const found = (attr) => [...existing.values()].find((b) => b.resource === 'queue' && b.resourceKey === spec.key && b.propertyAttribute === attr)?.id;
3009
- const already = { name: found('name'), folderPath: found('folderPath') };
3010
- if (already.name && already.folderPath) {
3011
- return { ids: { name: already.name, folderPath: already.folderPath }, entries: [] };
3012
- }
3013
- // A readable, stable id per queue. Two queues that share a NAME across folders
3014
- // would collide, so the id is suffixed until it is free.
3015
- const slug = spec.queue.replace(/[^A-Za-z0-9]/g, '') || 'Queue';
3016
- let base = `b${slug.charAt(0).toUpperCase()}${slug.slice(1)}`;
3017
- for (let n = 2; existing.has(`${base}Name`) || existing.has(`${base}FolderPath`); n++) {
3018
- base = `b${slug.charAt(0).toUpperCase()}${slug.slice(1)}${n}`;
3019
- }
3020
- const ids = { name: `${base}Name`, folderPath: `${base}FolderPath` };
3021
- return {
3022
- ids,
3023
- entries: [
3024
- {
3025
- id: ids.name,
3026
- name: 'name',
3027
- type: 'string',
3028
- resource: 'queue',
3029
- resourceKey: spec.key,
3030
- default: spec.queue,
3031
- propertyAttribute: 'name',
3032
- },
3033
- {
3034
- id: ids.folderPath,
3035
- name: 'folderPath',
3036
- type: 'string',
3037
- resource: 'queue',
3038
- resourceKey: spec.key,
3039
- default: spec.folderPath,
3040
- propertyAttribute: 'folderPath',
3041
- },
3042
- ],
3043
- };
3044
- }
3045
2925
  /**
3046
2926
  * Encode a scheduled trigger's single `every` interval into the three inputs the
3047
2927
  * platform's node carries.
@@ -3781,7 +3661,6 @@ export function bundledActionNodeType(spec) {
3781
3661
  switch (spec?.kind) {
3782
3662
  case 'http': return spec.inputs?.managed ? NODE_TYPE.httpV2 : NODE_TYPE.http;
3783
3663
  case 'hitl': return hitlNodeType(spec.inputs);
3784
- case 'queueItem': return spec.inputs?.wait ? NODE_TYPE.queueCreateAndWait : NODE_TYPE.queueCreate;
3785
3664
  case 'inlineAgent': return INLINE_AGENT_TYPE;
3786
3665
  case 'subflow':
3787
3666
  case 'summarize':
@@ -3890,6 +3769,19 @@ function hitlNeededVersion(inputs) {
3890
3769
  export function bundledDefinition(nodeType, requestedVersion) {
3891
3770
  return requestedVersion !== undefined ? DEFS[`${nodeType}@${requestedVersion}`] : defByType.get(nodeType);
3892
3771
  }
3772
+ /** The `core.logic.loop` version `.loop()` emits unless `{ version }` pins another. */
3773
+ const LOOP_DEFAULT_VERSION = '2.4';
3774
+ /**
3775
+ * The bundled definition a `.loop()` or `.doWhile()` container emits, at the
3776
+ * version serialize writes (`.loop()` defaults to `@2.4`, `.doWhile()` to the
3777
+ * pinned `core.logic.dowhile@1.0`). `check` asks it whether the container has an
3778
+ * error port (#1010).
3779
+ */
3780
+ export function bundledContainerDefinition(kind, requestedVersion) {
3781
+ return kind === 'loop'
3782
+ ? bundledDefinition(NODE_TYPE.loop, requestedVersion ?? LOOP_DEFAULT_VERSION)
3783
+ : bundledDefinition(NODE_TYPE.doWhile, requestedVersion);
3784
+ }
3893
3785
  /**
3894
3786
  * Resolve a contribution's {@link DefinitionReference} to a concrete manifest —
3895
3787
  * exactly (design §2.6/§4.2): the named version resolves or compilation fails,
@@ -5118,10 +5010,6 @@ export function serialize(built, opts = {}) {
5118
5010
  return 'default';
5119
5011
  if (type === NODE_TYPE.loop)
5120
5012
  return 'success';
5121
- // Both queue definitions declare exactly one source handle, `success` — and
5122
- // the shipped `sample-flows/queue-operations.flow` wires its edges to it.
5123
- if (type === NODE_TYPE.queueCreate || type === NODE_TYPE.queueCreateAndWait)
5124
- return 'success';
5125
5013
  // Every human-task definition declares one static source handle, `completed`.
5126
5014
  // It is the real exit only where the product draws it: action-app,
5127
5015
  // document-validation, and a task with no outcomes. A base or quick-form task
@@ -5243,6 +5131,7 @@ export function serialize(built, opts = {}) {
5243
5131
  mint: (seed) => stableId(`${built.id}:${id}:${seed}`),
5244
5132
  bind: (b) => { bound.push(b); },
5245
5133
  definition: (typeName, version) => bundledDefinition(typeName, version),
5134
+ bindings: resourceBindings,
5246
5135
  };
5247
5136
  const bound = [];
5248
5137
  const contribution = impl.serialize(spec, ctx);
@@ -6003,42 +5892,6 @@ export function serialize(built, opts = {}) {
6003
5892
  addEdge(scope, t, id);
6004
5893
  return [{ node: id, port: successPort(def.nodeType) }];
6005
5894
  }
6006
- if (spec.kind === 'queueItem') {
6007
- const s = spec.inputs;
6008
- // The bindings first: two steps on the same queue share one pair, and the
6009
- // node's `model.context` has to name the ids that actually exist.
6010
- const { ids, entries } = queueItemBindings(s, resourceBindings);
6011
- for (const b of entries)
6012
- resourceBindings.set(b.id, b);
6013
- const def = queueItemDef(s, step.name, ids, step.options?.version);
6014
- const id = stepUid(scope, step.name);
6015
- // The result is read as `$vars.<step>.output`, like every other family: the
6016
- // node's own output block maps the runtime `response` onto the variable
6017
- // `output` (`source: "=response", var: "output"`), and the product honours
6018
- // that mapping for both queue variants. (An alias to `.response` lived here
6019
- // from #273 until #891; a live debug run on 2026-10-01 measured
6020
- // `.output.Key` populated and `.response` undefined.)
6021
- const node = makeNode(def.nodeType, id, step.name, { parentId, requestedVersion: step.options?.version, def });
6022
- // The node KEEPS its copy of the model here, unlike the rpa families. Two
6023
- // reasons: it is what the one queue instance the repo has carries, and it is
6024
- // what `makeNode` does for every other core action node with a model (http,
6025
- // script, transform). The model resolves from either place since #94, and a
6026
- // bindings lookup reads node-first for the same reason.
6027
- node.inputs = {
6028
- queue: { name: s.queue, folderPath: s.folderPath, key: s.key },
6029
- // `itemData`, not `item`: the surface's name is friendlier, the node's is
6030
- // the platform's. The reference documents the mapping.
6031
- itemData: renderMap(s.item ?? {}, scope.rename),
6032
- priority: s.priority ?? 'Normal',
6033
- reference: s.reference === undefined ? '' : renderValue(s.reference, scope.rename),
6034
- deferDate: s.deferDate === undefined ? '' : renderValue(s.deferDate, scope.rename),
6035
- dueDate: s.dueDate === undefined ? '' : renderValue(s.dueDate, scope.rename),
6036
- };
6037
- scope.nodes.push(node);
6038
- for (const t of tails)
6039
- addEdge(scope, t, id);
6040
- return [{ node: id, port: successPort(def.nodeType) }];
6041
- }
6042
5895
  if (spec.kind === 'conversationalAgent') {
6043
5896
  // The text CONVERSATIONAL agent: the inline agent's cluster shape (a
6044
5897
  // minted `source` plus an `agent.json` sidecar) with the family's own
@@ -6831,7 +6684,7 @@ export function serialize(built, opts = {}) {
6831
6684
  // An explicit 1.0.0 plus a rich option is a contradiction the author must
6832
6685
  // resolve, not a silent drop.
6833
6686
  const rich = o.parallel !== undefined || o.completionCondition !== undefined || o.breakEnabled !== undefined || usesBreak;
6834
- const version = o.version ?? '2.4';
6687
+ const version = o.version ?? LOOP_DEFAULT_VERSION;
6835
6688
  if (rich && version === '1.0.0') {
6836
6689
  throw new Error(`loop "${step.name}": parallel/completionCondition/breakEnabled/break() need the loop's 2.4 `
6837
6690
  + `definition, but { version: '1.0.0' } was requested. Drop the version or request 2.4.`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uipath/maestro-builder-sdk",
3
- "version": "6.20.6",
3
+ "version": "6.21.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",
@@ -92,5 +92,5 @@
92
92
  "@types/node": "^22.7.0",
93
93
  "esbuild": "^0.28.1"
94
94
  },
95
- "gitref": "124c0cac0f49d9974da5619817458d4d8760a91f"
95
+ "gitref": "fb7b702cde7f69674dfc8ed9e4eb171472e8d8e7"
96
96
  }