@uipath/maestro-builder-sdk 6.44.2 → 6.44.3

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/check.js CHANGED
@@ -22,6 +22,7 @@ import { FLOW_SCHEMA_VERSIONS } from './format-profile.js';
22
22
  import { compareSchemaVersions, schemaVersionRefusal } from './schema-version.js';
23
23
  import { planFlowGlobals, planSharedInputs, sharedInputRoots } from './flow-variables.js';
24
24
  import { planOutputBacking } from './flow-output-backing.js';
25
+ import { VARS_READ_RE } from './core/vars-read.js';
25
26
  import { terminateStepClashMessage, terminateTwiceMessage } from './flow-terminate-names.js';
26
27
  import { endContentKey } from './flow-end-content.js';
27
28
  import { FLOW_NODE_TYPES } from './flow-node-registry.js';
@@ -1056,7 +1057,7 @@ function checkFlow(built, ancestors, opts = {}, ledger = newResourceIdLedger())
1056
1057
  // subflow read, where the value lives one level deeper
1057
1058
  // (`$vars.<call>.output.<childOutput>`). Every other rule reads only the
1058
1059
  // first two segments, exactly as before.
1059
- for (const m of js.matchAll(/\$vars\.([A-Za-z0-9_]+)(?:\??\.([A-Za-z0-9_]+))?((?:\??\.[A-Za-z0-9_]+)*)/g)) {
1060
+ for (const m of js.matchAll(VARS_READ_RE)) {
1060
1061
  const [, name, field] = m;
1061
1062
  // `?.` reads the same path as `.`, and the rules below parse the tail as `.a.b`.
1062
1063
  const rest = m[3].replace(/\?\./g, '.');
@@ -0,0 +1,6 @@
1
+ /**
2
+ * A dotted `$vars` read: `$vars.<name>`, an optional `.<field>`, then the dotted
3
+ * `rest`, each dot optionally `?.`. It stops at the first bracket. Check and
4
+ * decompile both scan with it, so decompile declares the body fields check reads.
5
+ */
6
+ export declare const VARS_READ_RE: RegExp;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * A dotted `$vars` read: `$vars.<name>`, an optional `.<field>`, then the dotted
3
+ * `rest`, each dot optionally `?.`. It stops at the first bracket. Check and
4
+ * decompile both scan with it, so decompile declares the body fields check reads.
5
+ */
6
+ export const VARS_READ_RE = /\$vars\.([A-Za-z0-9_]+)(?:\??\.([A-Za-z0-9_]+))?((?:\??\.[A-Za-z0-9_]+)*)/g;
package/dist/decompile.js CHANGED
@@ -42,6 +42,7 @@ import { parseIxpProjectType } from './core/actions.js';
42
42
  import { readFlowGlobals } from './flow-variables.js';
43
43
  import { outputBackingGlobal, outputBackingName } from './flow-output-backing.js';
44
44
  import { mapJsCode } from './core/js-code-spans.js';
45
+ import { VARS_READ_RE } from './core/vars-read.js';
45
46
  import { missingBindingLabels } from './core/binding-messages.js';
46
47
  import { prepareCommand } from './core/cli-spelling.js';
47
48
  import { OUTCOME_HANDLE_NODE_TYPES } from './core/hitl-routing.js';
@@ -459,6 +460,30 @@ function emitValMap(m, imp, inputNames) {
459
460
  */
460
461
  const ARTIFACT_PORTS = new Set(['tool', 'context', 'escalation', 'memory']);
461
462
  // ─── Graph model ──────────────────────────────────────────────────────────────
463
+ /** An index right after a read: `[0]`, `[i]`, `?.["k"]`. */
464
+ const INDEX_NEXT_RE = /^\s*(?:\?\.)?\[/;
465
+ /** A read's tail when an index follows it, whatever the index: `.output.body.items[]`. */
466
+ const INDEX_MARK = '[]';
467
+ /** Every `$vars` read in `values`' strings, by id, with `?.` read as `.`. */
468
+ function scopeReads(values) {
469
+ const reads = new Map();
470
+ const visit = (v) => {
471
+ if (typeof v === 'string') {
472
+ for (const m of v.matchAll(VARS_READ_RE)) {
473
+ const [read, id, field, rest] = m;
474
+ const path = field === undefined ? '' : `.${field}${rest.replace(/\?\./g, '.')}`;
475
+ const indexed = INDEX_NEXT_RE.test(v.slice(m.index + read.length));
476
+ (reads.get(id) ?? reads.set(id, []).get(id)).push(indexed ? path + INDEX_MARK : path);
477
+ }
478
+ return;
479
+ }
480
+ if (v && typeof v === 'object')
481
+ for (const x of Object.values(v))
482
+ visit(x);
483
+ };
484
+ visit(values);
485
+ return reads;
486
+ }
462
487
  class Graph {
463
488
  byId = new Map();
464
489
  out = new Map();
@@ -474,8 +499,11 @@ class Graph {
474
499
  */
475
500
  accounted = new Set();
476
501
  edges;
477
- constructor(nodes, edges) {
502
+ variables;
503
+ reads;
504
+ constructor(nodes, edges, variables) {
478
505
  this.edges = edges;
506
+ this.variables = variables;
479
507
  for (const n of nodes)
480
508
  this.byId.set(n.id, n);
481
509
  for (const e of edges) {
@@ -483,6 +511,11 @@ class Graph {
483
511
  (this.inc.get(e.targetNodeId) ?? this.inc.set(e.targetNodeId, []).get(e.targetNodeId)).push(e);
484
512
  }
485
513
  }
514
+ /** The tails this scope's inputs, outputs and updates read off `$vars.<id>`, e.g. `.output.body.current`. */
515
+ readsOf(id) {
516
+ this.reads ??= scopeReads([...[...this.byId.values()].flatMap((n) => [n.inputs, n.outputs]), this.variables?.variableUpdates]);
517
+ return this.reads.get(id) ?? [];
518
+ }
486
519
  outEdges(id) {
487
520
  return this.out.get(id) ?? [];
488
521
  }
@@ -666,6 +699,7 @@ function liftRegistered(node, impl = registeredImpl(node), graph) {
666
699
  absorb: (id, compiledId) => { runAbsorbed.set(id, compiledId); },
667
700
  outPorts: () => (graph?.outEdges(node.id) ?? []).map((e) => e.sourcePort),
668
701
  convergedFanOut: () => graph?.convergedFanOut(node.id) === true,
702
+ reads: () => graph?.readsOf(node.id) ?? [],
669
703
  };
670
704
  const spec = impl.deserialize(node, ctx);
671
705
  return spec === undefined ? undefined : { impl, spec };
@@ -2556,7 +2590,7 @@ function readChildStartAsStart(child, startId) {
2556
2590
  function emitChildFlowIn(name, child, trigger, ctx) {
2557
2591
  const globals = readFlowGlobals(child.variables, runStrict);
2558
2592
  const childCtx = {
2559
- graph: new Graph(child.nodes ?? [], child.edges ?? []),
2593
+ graph: new Graph(child.nodes ?? [], child.edges ?? [], child.variables),
2560
2594
  flow: child,
2561
2595
  imp: ctx.imp,
2562
2596
  inputNames: new Set(globals.filter((g) => g.direction === 'in').map((g) => g.name ?? g.id)),
@@ -2721,7 +2755,7 @@ export function decompile(flow, options = {}) {
2721
2755
  }
2722
2756
  runCarried = carriedPairs(flow);
2723
2757
  const imp = new Imports();
2724
- const graph = new Graph(flow.nodes ?? [], flow.edges ?? []);
2758
+ const graph = new Graph(flow.nodes ?? [], flow.edges ?? [], flow.variables);
2725
2759
  const globals = readFlowGlobals(flow.variables, runStrict);
2726
2760
  const inputNames = new Set(globals.filter((g) => g.direction === 'in').map((g) => g.name ?? g.id));
2727
2761
  // Trigger-bound globals name the node they came from; fall back to the graph's
@@ -281,6 +281,8 @@ export interface FlowDeserializeContext {
281
281
  * the type's `exits()`, so it is `false` for any other node.
282
282
  */
283
283
  convergedFanOut(): boolean;
284
+ /** The tails the node's scope reads off `$vars.<nodeId>`, e.g. `.output.body.current`, `.output.body.items[]` for an indexed read. */
285
+ reads(): readonly string[];
284
286
  }
285
287
  /** What `print` may ask of the decompiler. Every method returns source text. */
286
288
  export interface FlowPrintContext {
@@ -245,6 +245,27 @@ function liftReturns(node) {
245
245
  .map(([k, v]) => [k, v.type]);
246
246
  return entries.length ? Object.fromEntries(entries) : undefined;
247
247
  }
248
+ /** A read of a body field: the field, then whatever the read goes on to. */
249
+ const BODY_READ_RE = /^\.output\.body\.([A-Za-z0-9_]+)(.*)$/;
250
+ /**
251
+ * The `returns` a node with no body schema needs for the body fields its flow
252
+ * already reads, so the source decompile prints compiles. A field read further
253
+ * in (`body.current.time`) is an `object`; a field read whole is a `string`, the
254
+ * type the check suggests. Undefined when nothing reads the body.
255
+ */
256
+ function returnsFromReads(reads) {
257
+ const fields = new Map();
258
+ for (const read of reads) {
259
+ const m = BODY_READ_RE.exec(read);
260
+ if (!m)
261
+ continue;
262
+ if (m[2] !== '')
263
+ fields.set(m[1], 'object');
264
+ else if (!fields.has(m[1]))
265
+ fields.set(m[1], 'string');
266
+ }
267
+ return fields.size ? Object.fromEntries(fields) : undefined;
268
+ }
248
269
  /**
249
270
  * Invert an HTTP node. The plain node keeps its request at the top level
250
271
  * (`url` / `method` / `headers` / `queryParams` / `body` / `contentType`); the
@@ -275,7 +296,7 @@ function liftHttp(node, ctx, managed) {
275
296
  throw new Error(`decompile: expected a number, got ${JSON.stringify(inputs.retryCount)}.`);
276
297
  }
277
298
  const branchRows = Array.isArray(inputs.branches) ? inputs.branches : [];
278
- const returns = liftReturns(node);
299
+ const returns = liftReturns(node) ?? returnsFromReads(ctx.reads());
279
300
  const spec = {
280
301
  url: value(req.url ?? ''),
281
302
  ...(text(req.method) !== undefined && req.method !== 'GET' ? { method: req.method } : {}),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uipath/maestro-builder-sdk",
3
- "version": "6.44.2",
3
+ "version": "6.44.3",
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": "7afed05cfe5a6856deb6fe7984a5cf2b31a2dd20"
96
+ "gitref": "89512b0912180900bd6b3c62191b7beb090af3cc"
97
97
  }