@uipath/maestro-builder-sdk 6.31.0 → 6.31.2

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.
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import { Expr } from '../../../core/expr.js';
10
10
  import { jsonClone } from '../../../core/json-clone.js';
11
+ import { isRecord } from '../../../core/node-envelope.js';
11
12
  import { wireExprBody } from '../../../core/wire-expr.js';
12
13
  import { checkHttpRead, checkHttpStep, plainHttpOnErrorMessage } from './check.js';
13
14
  const PLAIN = 'core.action.http';
@@ -264,7 +265,7 @@ function liftHttp(node, ctx, managed) {
264
265
  return raw;
265
266
  return text.startsWith('=js:') ? ctx.expr(text) : text;
266
267
  };
267
- const map = (m) => m && typeof m === 'object' && Object.keys(m).length
268
+ const map = (m) => isRecord(m) && Object.keys(m).length
268
269
  ? Object.fromEntries(Object.entries(m).map(([k, v]) => [k, value(v)]))
269
270
  : undefined;
270
271
  const text = (v) => (typeof v === 'string' ? v : undefined);
@@ -7,6 +7,19 @@ import type { FlowCheckContext } from '../../../flow-node-type.js';
7
7
  import type { QueueItemInputs } from './spec.js';
8
8
  /** Every code the family reports, with the severity it reports it at. */
9
9
  export declare const QUEUE_DIAGNOSTICS: DiagnosticCatalog;
10
+ /**
11
+ * Whether a definition stores `inputs.itemData` as a JSON STRING — the 1.0 era.
12
+ * 1.1 and later declare an `object`. Read off the definition being emitted or
13
+ * lifted, not off a version number, so a carried definition of any era answers
14
+ * for itself.
15
+ */
16
+ export declare function itemDataIsString(def: unknown): boolean;
17
+ /**
18
+ * Why `item.<field>` cannot be an expression on a definition that stores the item
19
+ * as one JSON string. Shared by `check` (QUEUE_ITEM_EXPR_UNSUPPORTED) and the
20
+ * serializer's refusal, so the two say the same thing.
21
+ */
22
+ export declare function queueItemExprMessage(step: string, typeName: string, version: string, field: string): string;
10
23
  /**
11
24
  * Check a `queueItem(...)` call.
12
25
  *
@@ -31,5 +44,7 @@ export declare const QUEUE_DIAGNOSTICS: DiagnosticCatalog;
31
44
  * - `returns` on a step that does not wait (there is nothing per-tenant to
32
45
  * declare), and a `wait: true` step whose result nothing can read.
33
46
  * - a non-ISO `dueDate` / `deferDate`.
47
+ * - an expression in `item` when the definition stores the item as one JSON
48
+ * string (1.0): the serializer refuses it, so `check` reports it first.
34
49
  */
35
- export declare function checkQueueItem(inputs: QueueItemInputs | undefined, ctx: FlowCheckContext): void;
50
+ export declare function checkQueueItem(inputs: QueueItemInputs | undefined, ctx: FlowCheckContext, typeName: string): void;
@@ -11,6 +11,7 @@ export const QUEUE_DIAGNOSTICS = {
11
11
  QUEUE_NO_ITEM: { severity: 'warning', summary: 'the item carries no data' },
12
12
  QUEUE_ITEM_NOT_OBJECT: { severity: 'error', summary: 'the item is not an object' },
13
13
  QUEUE_ITEM_FIELD_NOT_PRIMITIVE: { severity: 'error', summary: 'an item field is not a primitive' },
14
+ QUEUE_ITEM_EXPR_UNSUPPORTED: { severity: 'error', summary: 'an expression in an item stored as one JSON string' },
14
15
  QUEUE_BAD_PRIORITY: { severity: 'error', summary: 'a priority the node type does not offer' },
15
16
  QUEUE_BAD_DATE: { severity: 'error', summary: 'a date Orchestrator cannot read' },
16
17
  QUEUE_RETURNS_WITHOUT_WAIT: { severity: 'warning', summary: 'returns without wait: true' },
@@ -18,6 +19,27 @@ export const QUEUE_DIAGNOSTICS = {
18
19
  QUEUE_READ_WITHOUT_RETURNS: { severity: 'error', summary: 'a field read off a wait result with no returns' },
19
20
  QUEUE_READ_UNKNOWN_FIELD: { severity: 'error', summary: 'a read of a field the step does not publish' },
20
21
  };
22
+ /**
23
+ * Whether a definition stores `inputs.itemData` as a JSON STRING — the 1.0 era.
24
+ * 1.1 and later declare an `object`. Read off the definition being emitted or
25
+ * lifted, not off a version number, so a carried definition of any era answers
26
+ * for itself.
27
+ */
28
+ export function itemDataIsString(def) {
29
+ const props = def
30
+ ?.inputDefinition?.properties;
31
+ return props?.itemData?.type === 'string';
32
+ }
33
+ /**
34
+ * Why `item.<field>` cannot be an expression on a definition that stores the item
35
+ * as one JSON string. Shared by `check` (QUEUE_ITEM_EXPR_UNSUPPORTED) and the
36
+ * serializer's refusal, so the two say the same thing.
37
+ */
38
+ export function queueItemExprMessage(step, typeName, version, field) {
39
+ return `Queue step "${step}": ${typeName}@${version} stores the item as one JSON string, so \`item.${field}\` `
40
+ + `cannot be an expression there. Omit the { version } to emit the SDK's default version, which `
41
+ + `stores the item as an object of values and expressions.`;
42
+ }
21
43
  /**
22
44
  * Check a `queueItem(...)` call.
23
45
  *
@@ -42,8 +64,10 @@ export const QUEUE_DIAGNOSTICS = {
42
64
  * - `returns` on a step that does not wait (there is nothing per-tenant to
43
65
  * declare), and a `wait: true` step whose result nothing can read.
44
66
  * - a non-ISO `dueDate` / `deferDate`.
67
+ * - an expression in `item` when the definition stores the item as one JSON
68
+ * string (1.0): the serializer refuses it, so `check` reports it first.
45
69
  */
46
- export function checkQueueItem(inputs, ctx) {
70
+ export function checkQueueItem(inputs, ctx, typeName) {
47
71
  const step = ctx.step;
48
72
  // Severity is the catalog's (QUEUE_DIAGNOSTICS): `at` and `warn` name the intent.
49
73
  const at = (code, message, suggestion) => ctx.report(code, message, suggestion);
@@ -83,7 +107,12 @@ export function checkQueueItem(inputs, ctx) {
83
107
  + `item's \`SpecificContent\`.`, `item: { InvoiceId: 'INV-1', Amount: 120 }`);
84
108
  }
85
109
  else {
110
+ const itemIsString = itemDataIsString(ctx.definition());
86
111
  for (const [k, v] of Object.entries(item)) {
112
+ if (v instanceof Expr && itemIsString) {
113
+ at('QUEUE_ITEM_EXPR_UNSUPPORTED', queueItemExprMessage(step, typeName, ctx.version, k));
114
+ continue;
115
+ }
87
116
  // An Expr is a reference the runtime resolves — its VALUE may be anything,
88
117
  // and nothing local can know, so it is not flagged here.
89
118
  if (v instanceof Expr)
@@ -1,6 +1,7 @@
1
1
  import { Expr } from '../../../core/expr.js';
2
2
  import { jsonClone } from '../../../core/json-clone.js';
3
- import { checkQueueItem } from './check.js';
3
+ import { isRecord } from '../../../core/node-envelope.js';
4
+ import { checkQueueItem, itemDataIsString, queueItemExprMessage } from './check.js';
4
5
  import { QUEUE_ITEM_RECORD_SCHEMA } from './record.js';
5
6
  const CREATE = 'core.action.queue.create';
6
7
  const CREATE_AND_WAIT = 'core.action.queue.create-and-wait';
@@ -159,6 +160,87 @@ function liftReturns(node) {
159
160
  .map(([k, v]) => [k, v.type]);
160
161
  return entries.length ? Object.fromEntries(entries) : undefined;
161
162
  }
163
+ /**
164
+ * The surface's `item` as a 1.0 node stores it: one JSON string, `''` for no
165
+ * data (the 1.0 definition's own default). A JSON string holds DATA, so an
166
+ * expression in a field has no spelling there; it is refused rather than
167
+ * written as text the runtime would never evaluate.
168
+ */
169
+ function itemDataString(item, step, typeName, version) {
170
+ const rows = Object.entries(item);
171
+ if (rows.length === 0)
172
+ return '';
173
+ for (const [k, v] of rows) {
174
+ if (v instanceof Expr)
175
+ throw new Error(queueItemExprMessage(step, typeName, version, k));
176
+ }
177
+ return JSON.stringify(item);
178
+ }
179
+ /** Whether `v` is, or holds at any depth, an integer JSON.parse could not represent exactly. */
180
+ function hasUnsafeInteger(v) {
181
+ if (typeof v === 'number')
182
+ return Number.isInteger(v) && !Number.isSafeInteger(v);
183
+ if (Array.isArray(v))
184
+ return v.some(hasUnsafeInteger);
185
+ return isRecord(v) && Object.values(v).some(hasUnsafeInteger);
186
+ }
187
+ /**
188
+ * `inputs.itemData` back as the surface's `item`, in either wire shape:
189
+ *
190
+ * - 1.1 and later: an object of field → value, each value as the author wrote it
191
+ * (`=js:` text back to an Expr);
192
+ * - 1.0: ONE JSON STRING (`"{\"invoiceId\":\"INV-1001\"}"`), which is parsed back
193
+ * to the object it encodes; `''` is the 1.0 default, no data.
194
+ *
195
+ * `undefined`, with a warning, for a value `item` cannot hold without changing
196
+ * what the node does — a whole-item expression, a string that is not a JSON
197
+ * object, an integer beyond 2^53 (parsing rounds it), or a JSON field whose
198
+ * text starts with `=` (literal data at 1.0; it would become an expression once
199
+ * written as an object). The lift then
200
+ * declines the node and decompile keeps it as a `rawNode`, verbatim.
201
+ *
202
+ * Reading the shape off the value, never assuming it, is the point: treating
203
+ * the 1.0 string as an object spread it into one field per character
204
+ * (flow-builder-sdk#1164).
205
+ */
206
+ function liftItem(raw, ctx, value) {
207
+ const decline = (why) => {
208
+ ctx.warn(`node ${JSON.stringify(ctx.nodeId)}: its queue item data (inputs.itemData) is ${why}, which \`queueItem({ item })\` `
209
+ + `cannot hold without changing it, so the source keeps the node as a rawNode, verbatim.`);
210
+ return undefined;
211
+ };
212
+ if (raw === undefined || raw === null)
213
+ return {};
214
+ if (typeof raw === 'string') {
215
+ if (raw === '')
216
+ return {};
217
+ if (raw.startsWith('='))
218
+ return decline('an expression for the whole item');
219
+ let parsed;
220
+ try {
221
+ parsed = JSON.parse(raw);
222
+ }
223
+ catch {
224
+ return decline('a string that is not JSON');
225
+ }
226
+ if (!isRecord(parsed))
227
+ return decline('a JSON string that does not encode an object');
228
+ const expressionLike = Object.keys(parsed).find((k) => typeof parsed[k] === 'string' && parsed[k].startsWith('='));
229
+ if (expressionLike !== undefined)
230
+ return decline(`a JSON string whose field "${expressionLike}" starts with "="`);
231
+ // JSON.parse rounds an integer beyond 2^53 (`12345678901234567890` reads back
232
+ // as `12345678901234567000`), and the compile would write the rounded value.
233
+ const rounded = Object.keys(parsed).find((k) => hasUnsafeInteger(parsed[k]));
234
+ if (rounded !== undefined)
235
+ return decline(`a JSON string whose field "${rounded}" holds an integer too large to read back exactly`);
236
+ return parsed;
237
+ }
238
+ if (ctx.sourceText(raw) !== undefined)
239
+ return decline('an expression for the whole item');
240
+ if (!isRecord(raw))
241
+ return decline(Array.isArray(raw) ? 'an array' : `a ${typeof raw}`);
242
+ return Object.fromEntries(Object.entries(raw).map(([k, v]) => [k, value(v)]));
243
+ }
162
244
  function queueAt(typeName, wait) {
163
245
  return {
164
246
  kind: 'queueItem', typeName, version: '1.2', factory: 'queueItem', definition: 'per-tenant', errorPort: true,
@@ -169,7 +251,8 @@ function queueAt(typeName, wait) {
169
251
  const { ids, entries } = queueItemBindings(s, ctx.bindings);
170
252
  for (const b of entries)
171
253
  ctx.bind(b);
172
- const manifest = queueItemDef(s, typeName, ctx.step, ids, ctx.definition(typeName, ctx.version));
254
+ const base = ctx.definition(typeName, ctx.version);
255
+ const manifest = queueItemDef(s, typeName, ctx.step, ids, base);
173
256
  return {
174
257
  definition: { source: 'snapshot', nodeType: typeName, version: ctx.version, manifest },
175
258
  // The result is read as `$vars.<step>.output`, like every other family: the
@@ -180,8 +263,11 @@ function queueAt(typeName, wait) {
180
263
  inputs: {
181
264
  queue: { name: s.queue, folderPath: s.folderPath, key: s.key },
182
265
  // `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)])),
266
+ // the platform's. Rendered one level deep, as the item is FLAT — or, for a
267
+ // definition of the 1.0 era, encoded as the one JSON string it declares.
268
+ itemData: itemDataIsString(base)
269
+ ? itemDataString(s.item ?? {}, ctx.step, typeName, ctx.version)
270
+ : Object.fromEntries(Object.entries(s.item ?? {}).map(([k, v]) => [k, ctx.render(v)])),
185
271
  priority: s.priority ?? 'Normal',
186
272
  reference: s.reference === undefined ? '' : ctx.render(s.reference),
187
273
  deferDate: s.deferDate === undefined ? '' : ctx.render(s.deferDate),
@@ -209,12 +295,14 @@ function queueAt(typeName, wait) {
209
295
  // A wait's `returns` is the schema serialize wrote for THIS step (the
210
296
  // template ships none), as the node's own `outputs` carries it.
211
297
  const returns = wait ? liftReturns(node) : undefined;
298
+ const item = liftItem(inputs.itemData, ctx, value);
299
+ if (item === undefined)
300
+ return undefined;
212
301
  return {
213
302
  kind: 'queueItem',
214
303
  inputs: {
215
304
  queue: q.name, folderPath: q.folderPath, key: q.key,
216
- item: Object.fromEntries(Object.entries((inputs.itemData ?? {}))
217
- .map(([k, v]) => [k, value(v)])),
305
+ item,
218
306
  ...(typeof priority === 'string' ? { priority: priority } : {}),
219
307
  ...(optional(inputs.reference) !== undefined ? { reference: optional(inputs.reference) } : {}),
220
308
  ...(optional(inputs.deferDate) !== undefined ? { deferDate: optional(inputs.deferDate) } : {}),
@@ -249,7 +337,7 @@ function queueAt(typeName, wait) {
249
337
  ],
250
338
  };
251
339
  },
252
- check: ({ inputs }, ctx) => checkQueueItem(inputs, ctx),
340
+ check: ({ inputs }, ctx) => checkQueueItem(inputs, ctx, typeName),
253
341
  // check collected no expressions for a queue step before the port; unchanged by it.
254
342
  expressions: () => [],
255
343
  output: ({ inputs }) => queueOutput(inputs, wait),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uipath/maestro-builder-sdk",
3
- "version": "6.31.0",
3
+ "version": "6.31.2",
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",
@@ -62,6 +62,7 @@
62
62
  ],
63
63
  "files": [
64
64
  "dist",
65
+ "!dist/api.model.json",
65
66
  "lib/library-json.samples",
66
67
  "README.md"
67
68
  ],
@@ -92,5 +93,5 @@
92
93
  "@types/node": "^22.7.0",
93
94
  "esbuild": "^0.28.1"
94
95
  },
95
- "gitref": "21719d7a1fd422a6886d9021c02cf2f124c4f627"
96
+ "gitref": "37b4b349e7ce452d74154e65bdae4dff9a1aeb93"
96
97
  }