@uipath/maestro-builder-sdk 6.49.3 → 6.50.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.
package/dist/serialize.js CHANGED
@@ -18,8 +18,6 @@ import { FLOW_NODE_TYPES } from './flow-node-registry.js';
18
18
  import { stepExits } from './flow-step-exits.js';
19
19
  import { resolveVersionCarried } from './core/nodes/versioned.js';
20
20
  import { indexCarriedDefinitions } from './core/carried-definitions.js';
21
- import { ConnectorResolveError } from './core/connector-resolve-error.js';
22
- import { synthesizedDefinitionRefusal } from './core/synthesized-definition.js';
23
21
  import { connectionConnectors } from './bindings-v2.js';
24
22
  import { BindingRefusal, GUID_RE, bindingLabelRefusal, bindingMissingMessage, missingBindingLabels } from './core/binding-messages.js';
25
23
  import { ByoaRefusal, byoaUnverified } from './core/byoa.js';
@@ -30,11 +28,10 @@ import { placeholderPosition } from './node-placement.js';
30
28
  import { hasExits } from './core/step-ports.js';
31
29
  import { stopsAtEnd } from './flow-step-tree.js';
32
30
  import { assertNodeEnvelope } from './core/node-envelope.js';
33
- import { buildConfiguration } from './core/connector-configuration.js';
34
31
  import { fnv1aX4Uuid, stableId } from './core/stable-id.js';
35
32
  import { readEventFilter, eventFilterProblem, eventFilterJmes, eventFilterTreeLeaf, jmesString, jmesStringProblem } from './core/event-filters.js';
36
33
  import { FLOW_EXPR_DIALECT } from './core/connector-checks.js';
37
- import { buildConnectorInputs, connectorInputErrors as coreConnectorInputErrors, transportHttpMethod } from './core/connector-inputs.js';
34
+ import { connectorInputErrors as coreConnectorInputErrors } from './core/connector-inputs.js';
38
35
  /**
39
36
  * What `compile` would refuse these connector inputs with, or `undefined` when it
40
37
  * would accept them — Flow's binding of the shared validator.
@@ -118,48 +115,6 @@ const SUBFLOW_OUTPUT = {
118
115
  source: '=result.response',
119
116
  var: 'output',
120
117
  };
121
- /** The inline (autonomous) agent's node type — fixed, not per-instance. */
122
- /**
123
- * Declare a LIST operation's output as the ARRAY it is, when the ledger has
124
- * MEASURED that it is one ([#203](https://github.com/UiPath/flow-builder-sdk/issues/203)).
125
- *
126
- * Every connector definition declares `outputs.output.type: 'object'` — list
127
- * operations included, and **0 of 544 deployed connector nodes declare `array`
128
- * anywhere** (card CLEANUP's corpus scan). For most of the library that is the
129
- * honest thing to emit, because the SDK does not know better. For an operation in
130
- * the list-envelope ledger it does:
131
- *
132
- * · `envelope: null` (a measured BARE array) ⇒ **`type: 'array'`**;
133
- * · a measured WRAPPER ⇒ `object`, unchanged — the value IS an object then;
134
- * · UNDECLARED ⇒ untouched, which is #202's status quo.
135
- *
136
- * Why this matters beyond tidiness: `typeAgentInputSchemas` builds an agent's
137
- * pydantic input model from these declarations, and card ga7's scored run r2 died
138
- * at `AGENT_STARTUP.INPUT_VALIDATION_ERROR` because a Data Service list node said
139
- * `object` and the runtime sent 8 rows. #202 made that property TYPELESS, which
140
- * unblocked the agent without asserting anything; this says the true thing.
141
- *
142
- * ## And deliberately NO item schema
143
- *
144
- * #203 suggested emitting the item fields alongside. They are not emitted, and the
145
- * reason is a measurement rather than caution: a list operation's declared fields
146
- * are **neither a superset nor a subset** of the real response (card LT, three live
147
- * responses — ServiceNow `incident` declares 0 of the 103 fields it returns; Entra
148
- * declares 20, returns 15, misses 2; Jira declares 253, returns 116, misses 42).
149
- * Asserting them into an agent's pydantic model would reject real rows — the exact
150
- * fault class above, moved rather than fixed. The library lift stays a READ-checking
151
- * aid, where it fails open by design (lesson 26). The emitted bytes claim only what
152
- * is certain: this is an array.
153
- */
154
- function declareListOutputType(node, rc) {
155
- const declared = rc.listEnvelope;
156
- if (!declared || declared.envelope !== null)
157
- return;
158
- const out = node.outputs?.output;
159
- if (!out || typeof out !== 'object' || out.type !== 'object')
160
- return;
161
- out.type = 'array';
162
- }
163
118
  /**
164
119
  * Read a connector definition's connection requirements without collapsing
165
120
  * three distinct states:
@@ -315,8 +270,7 @@ function scheduleInputs(inputs, nodeId) {
315
270
  ...(isPreset ? {} : { timerValue: every }),
316
271
  };
317
272
  }
318
- /** A required connector input left empty — the one draft code with no error class of its own. */
319
- export const CONNECTOR_INPUT_MISSING = 'CONNECTOR_INPUT_MISSING';
273
+ export { CONNECTOR_INPUT_MISSING } from './core/connector-inputs.js';
320
274
  /**
321
275
  * `serialize`, plus the extra files the flow needs on disk beside it.
322
276
  *
@@ -2346,6 +2300,14 @@ export function serialize(built, opts = {}) {
2346
2300
  if (impl.errorEnvelope)
2347
2301
  scope.rename.errorEnvelopes.set(step.name, impl.errorEnvelope);
2348
2302
  const rendered = new WeakSet();
2303
+ // A rendered value's JSON copy, marked as the file's: placement writes it as
2304
+ // is instead of rendering it again. Both renderers below go through it.
2305
+ const fileCopy = (out) => {
2306
+ const copy = jsonClone(out);
2307
+ if (copy !== null && typeof copy === 'object')
2308
+ rendered.add(copy);
2309
+ return copy;
2310
+ };
2349
2311
  const ctx = {
2350
2312
  step: step.name,
2351
2313
  nodeId: id,
@@ -2357,13 +2319,7 @@ export function serialize(built, opts = {}) {
2357
2319
  // Only a `lit` unwraps to a new value. Its JSON copy is the file's: a
2358
2320
  // copy, so an object the author also passes outside the `lit` is not
2359
2321
  // mistaken for this one and still has its expressions rendered.
2360
- if (out !== value && out !== null && typeof out === 'object') {
2361
- const copy = JSON.parse(JSON.stringify(out));
2362
- if (copy !== null && typeof copy === 'object')
2363
- rendered.add(copy);
2364
- return copy;
2365
- }
2366
- return out;
2322
+ return out !== value && out !== null && typeof out === 'object' ? fileCopy(out) : out;
2367
2323
  },
2368
2324
  renameRefs: (js) => renameRefs(js, scope.rename),
2369
2325
  stepNodeId: (name) => scope.stepNodeId.get(name) ?? name,
@@ -2397,9 +2353,34 @@ export function serialize(built, opts = {}) {
2397
2353
  resourceNodeId: (explicit, seed, at) => resourceNodeId(scope, explicit, seed, at),
2398
2354
  emitted: (typeName, v) => emittedDefs.get(`${typeName}@${v}`),
2399
2355
  library: opts.library,
2356
+ // Rendered once: placement renders the contribution's inputs again, so the
2357
+ // result is marked as the file's, as `render` marks a `lit`'s copy. A copy,
2358
+ // so an author object the walk returned as is (a `lit`'s own) stays unmarked
2359
+ // where it is also used outside the `lit`.
2360
+ renderDeep: (value) => {
2361
+ const out = renderDeep(value, scope.rename, rendered);
2362
+ return out !== null && typeof out === 'object' ? fileCopy(out) : out;
2363
+ },
2364
+ drafting: opts.draft !== undefined,
2365
+ draft: (code, message) => opts.draft?.({ nodeId: id, code: code, message }),
2400
2366
  };
2401
2367
  const bound = [];
2402
2368
  const contribution = impl.serialize(spec, ctx);
2369
+ if (contribution.standIn !== undefined) {
2370
+ // A draft stand-in: the node as the draft has it, with no definition to
2371
+ // place it against (see FlowNodeContribution.standIn).
2372
+ if (!ctx.drafting)
2373
+ throw new Error(`internal SDK error: ${where}: node type ${impl.typeName} returned a draft stand-in outside a draft compile.`);
2374
+ const { type, typeVersion, inputs } = contribution.standIn;
2375
+ scope.nodes.push({
2376
+ id, type, typeVersion,
2377
+ ...(parentId ? { parentId } : {}), display: { label: step.name },
2378
+ inputs: { ...inputs },
2379
+ });
2380
+ for (const t of tails)
2381
+ addEdge(scope, t, id);
2382
+ return [{ node: id, port: contribution.port ?? 'output' }];
2383
+ }
2403
2384
  // The definition contract, both halves: a per-tenant type supplies its
2404
2385
  // definition, and a bundled type does not (its definition is the bundle's, and
2405
2386
  // makeNode records it, so the error-edge refusal and check ask the same one).
@@ -2414,7 +2395,9 @@ export function serialize(built, opts = {}) {
2414
2395
  // instance's edits (a queue step's own resourceKey, for instance). Only a type
2415
2396
  // that supplies none at a carried version takes the carried definition as is.
2416
2397
  if (contribution.definition !== undefined) {
2417
- def = resolveContributionDefinition(contribution.definition, version, where);
2398
+ // A type whose definition names its version is held to the author's pin by
2399
+ // makeNode, with the message every definition's mismatch gets.
2400
+ def = resolveContributionDefinition(contribution.definition, entry.type.versionedByDefinition ? undefined : version, where);
2418
2401
  }
2419
2402
  else if (impl.definition === 'per-tenant' && resolved.carried === undefined) {
2420
2403
  throw new Error(`internal SDK error: ${where}: per-tenant node type ${impl.typeName}@${version} returned no definition.`);
@@ -2436,14 +2419,14 @@ export function serialize(built, opts = {}) {
2436
2419
  // tenant definition (an inline agent at 1.1) may have no error handle.
2437
2420
  const port = resolved.carried !== undefined
2438
2421
  ? definitionHasErrorPort(def ?? resolved.carried)
2439
- : typeof impl.errorPort === 'function' ? impl.errorPort(spec) : impl.errorPort === true;
2422
+ : typeof impl.errorPort === 'function' ? impl.errorPort(spec, def) : impl.errorPort === true;
2440
2423
  // The declaration answers for check, which has no definition; here the
2441
2424
  // definition is in hand, and the two must agree (#1011).
2442
2425
  if (port && def !== undefined && !definitionHasErrorPort(def)) {
2443
2426
  throw new Error(`internal SDK error: ${where}: node type ${impl.typeName}@${version} declares an error port, `
2444
2427
  + 'but the definition it supplied has no error handle and no supportsErrorHandling.');
2445
2428
  }
2446
- declaredErrorPorts.set(id, { port, typeName: nodeType, version });
2429
+ declaredErrorPorts.set(id, { port, typeName: nodeType, version: def?.version ?? version });
2447
2430
  }
2448
2431
  // A forward port is a handle the definition declares, one of the type's
2449
2432
  // author-named exits, or one the product derives (`derivesPort`: a human
@@ -2462,7 +2445,11 @@ export function serialize(built, opts = {}) {
2462
2445
  if (resolved.carried !== undefined)
2463
2446
  bundledNodeDefs.set(id, def ?? resolved.carried);
2464
2447
  return placeStepNode({
2465
- where, stepName: step.name, id, typeName: nodeType, requestedVersion: version, def, contribution, bound, tails, scope, parentId,
2448
+ where, stepName: step.name, id, typeName: nodeType,
2449
+ // A type whose definition names its version is placed at that version,
2450
+ // held to the author's pin, as makeNode holds any definition to one.
2451
+ requestedVersion: entry.type.versionedByDefinition ? step.options?.version : version,
2452
+ def, contribution, bound, tails, scope, parentId,
2466
2453
  trackOutcomes: true,
2467
2454
  rendered,
2468
2455
  });
@@ -2501,129 +2488,6 @@ export function serialize(built, opts = {}) {
2501
2488
  subflows[id] = emitSubflowBody(spec.child, id);
2502
2489
  return [{ node: id, port: successPort(NODE_TYPE.subflow) }];
2503
2490
  }
2504
- if (spec.kind === 'connector') {
2505
- if (!opts.library)
2506
- throw new Error(`connector "${step.name}" needs a library (pass --library / set $FLOW_SDK_LIBRARY_JSON).`);
2507
- let rc;
2508
- try {
2509
- rc = opts.library.resolve(spec.key, spec.action, spec.version, spec.object);
2510
- }
2511
- catch (error) {
2512
- if (!opts.draft || !(error instanceof ConnectorResolveError) || error.code !== 'CONNECTOR_OBJECT_REQUIRED')
2513
- throw error;
2514
- // A generic operation saved before its object was picked has no
2515
- // definition to emit against. Draft tolerance emits a stable stand-in
2516
- // under the node's id; merge restores the original when it is untouched
2517
- // and refuses it otherwise.
2518
- const id = stepUid(scope, step.name);
2519
- opts.draft({ nodeId: id, code: error.code, message: error.message });
2520
- // It leaves on the port every connector action leaves on, so the
2521
- // no-edit round trip rewires nothing. Its inputs carry what the author
2522
- // wrote, so an edit to the node (an input, a connection) shows up as a
2523
- // change and merge refuses it instead of restoring the original.
2524
- const type = `uipath.connector.${spec.key}.${spec.action}`;
2525
- scope.nodes.push({
2526
- id, type, typeVersion: spec.version ?? '1.0.0',
2527
- ...(parentId ? { parentId } : {}), display: { label: step.name },
2528
- inputs: {
2529
- draft: true,
2530
- spec: renderDeep(spec.inputs ?? {}, scope.rename),
2531
- ...(spec.connection !== undefined ? { connection: spec.connection } : {}),
2532
- ...(spec.folder !== undefined ? { folder: spec.folder } : {}),
2533
- },
2534
- });
2535
- for (const t of tails)
2536
- addEdge(scope, t, id);
2537
- return [{ node: id, port: successPort(type) }];
2538
- }
2539
- // An entry `prepare` synthesized from Integration Service compiles in BPMN only.
2540
- const noDefinition = synthesizedDefinitionRefusal(rc);
2541
- if (noDefinition)
2542
- throw noDefinition;
2543
- const id = stepUid(scope, step.name);
2544
- const node = makeNode(rc.nodeType, id, step.name, { parentId, requestedVersion: step.options?.version, def: rc.v1def });
2545
- declareListOutputType(node, rc);
2546
- // A draft that names no connection gets no binding rows either: there
2547
- // is nothing to bind, and rows with an empty resourceKey would leak into
2548
- // the merged file through merge's bindings union.
2549
- let unbound = false;
2550
- try {
2551
- requireBindingPair(`connector "${step.name}"`, spec, { key: spec.key, action: spec.action });
2552
- }
2553
- catch (error) {
2554
- if (!opts.draft || !(error instanceof BindingRefusal))
2555
- throw error;
2556
- opts.draft({ nodeId: id, code: error.code, message: error.message });
2557
- unbound = true;
2558
- }
2559
- const conn = resolveBinding(opts, spec.connection, 'connection', `connector "${step.name}"`, { key: spec.key, action: spec.action });
2560
- const folder = resolveBinding(opts, spec.folder, 'folder', `connector "${step.name}"`, { key: spec.key, action: spec.action });
2561
- // The platform reaches the connection through the flow's `bindings[]`,
2562
- // never through `inputs.detail` — without this pair the run faults with
2563
- // `[102010]` (board G-19; see connectionResourceBindings).
2564
- const { entries: connBindings } = connectionResourceBindings({ def: rc.v1def, connectorKey: rc.connectorKey, connectionId: conn, folderKey: folder }, resourceBindings);
2565
- if (!unbound)
2566
- for (const b of connBindings)
2567
- resourceBindings.set(b.id, b);
2568
- const connectorInputs = buildConnectorInputs(rc, spec.inputs, {
2569
- ...(opts.draft
2570
- ? { onMissingRequired: (m) => opts.draft({ nodeId: id, code: CONNECTOR_INPUT_MISSING, message: `connector ${rc.nodeType}: ${m}` }) }
2571
- : {}),
2572
- dialect: FLOW_EXPR_DIALECT,
2573
- render: (value) => renderDeep(value, scope.rename),
2574
- resolutions: opts.library.lookupResolutions ?? {},
2575
- // Only for the missing-required hint; a test double that is not a full
2576
- // Library simply gets the plain message.
2577
- lookups: typeof opts.library.lookupSpecs === 'function'
2578
- ? opts.library.lookupSpecs(spec.key, spec.action, spec.version, spec.object)
2579
- : {},
2580
- });
2581
- const replayInputs = {
2582
- ...(connectorInputs.bodyParameters ?? {}),
2583
- ...(connectorInputs.pathParameters ?? {}),
2584
- ...(connectorInputs.queryParameters ?? {}),
2585
- };
2586
- const detail = {
2587
- connector: rc.connectorKey,
2588
- method: transportHttpMethod(rc.httpMethod),
2589
- endpoint: rc.endpoint,
2590
- // The op's object identity, as a SIBLING of the copy inside
2591
- // `configuration` — the designer emits both, and every connector node in
2592
- // a product-exported flow carries it (`curated_create_issue`,
2593
- // `Company__c`, `send_message_to_channel_v2` on the reference export).
2594
- // The event path already did; this action path had dropped it.
2595
- // Conditional because the library defaults a missing
2596
- // `operation.objectName` to '' — an op that names no object gets no key,
2597
- // not an empty one.
2598
- ...(rc.objectName ? { objectName: rc.objectName } : {}),
2599
- inputMetadata: {},
2600
- errorState: { hasError: false },
2601
- telemetryData: { connectorKey: rc.connectorKey, connectorName: rc.connectorName },
2602
- configuration: buildConfiguration(rc.entry, rc.version, replayInputs, id),
2603
- ...(rc.activityTypeId ? { uiPathActivityTypeId: rc.activityTypeId } : {}),
2604
- ...(conn ? { connectionId: conn, connectionResourceId: conn } : {}),
2605
- ...(folder ? { connectionFolderKey: folder } : {}),
2606
- };
2607
- // Validate activity inputs against the connector's field list and place
2608
- // each one in the slot its LOCATION names — body → detail.bodyParameters,
2609
- // query → detail.queryParameters, path → detail.pathParameters, multipart
2610
- // → detail.multipartParameters (where the runtime looks) — so
2611
- // a schema-valid-but-contract-wrong connector node fails at compile with
2612
- // an actionable message instead of silently at run time.
2613
- //
2614
- // `bodyParameters` is emitted unconditionally, as it always has been (3 of
2615
- // 1,283 deployed connector nodes carry an empty one, so it is a legal
2616
- // shape); the other three appear only when the op has inputs there, which
2617
- // is the platform's dominant convention — of those 1,283 nodes, 899 omit
2618
- // `pathParameters` entirely and NONE carries an empty one. That also keeps
2619
- // every body-only connector's emission byte-identical to before.
2620
- Object.assign(detail, connectorInputs);
2621
- node.inputs = { detail };
2622
- scope.nodes.push(node);
2623
- for (const t of tails)
2624
- addEdge(scope, t, id);
2625
- return [{ node: id, port: successPort(rc.nodeType) }];
2626
- }
2627
2491
  if (spec.kind === 'waitEvent') {
2628
2492
  if (!opts.library)
2629
2493
  throw new Error(`waitForEvent "${step.name}" needs a library (pass --library / set $FLOW_SDK_LIBRARY_JSON).`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uipath/maestro-builder-sdk",
3
- "version": "6.49.3",
3
+ "version": "6.50.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": "0187c4bea37cd0a8fd78aae6125b040583097f96"
95
+ "gitref": "3b0f8b3f6e84465828936a6242656c563947c74d"
96
96
  }