@uipath/maestro-builder-sdk 6.14.2 → 6.14.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.
@@ -0,0 +1,34 @@
1
+ /**
2
+ * The node envelope: the fields `uip maestro flow validate` requires on EVERY
3
+ * entry of a `.flow` file's `nodes[]` (root and every `subflows.<id>.nodes[]`),
4
+ * whatever its type.
5
+ *
6
+ * Mirrors the platform's file-format node schema (`@uipath/flow-schema`
7
+ * `fileFormatNodeSchema`, bundled into the CLI's flow tool):
8
+ *
9
+ * ```ts
10
+ * id: z.string().min(1)
11
+ * type: z.string().min(1)
12
+ * typeVersion: z.string().regex(/^\d+\.\d+(\.\d+)?$/)
13
+ * display: z.object({ … }) // required, may be {}
14
+ * ui: { position: { x: number, y: number }, size?: { width, height } } // optional in the file
15
+ * inputs?: Record<string, unknown>
16
+ * outputs?: Record<string, unknown>
17
+ * parentId?: z.string().min(1)
18
+ * ```
19
+ *
20
+ * `makeNode` builds this shape for every definition-backed node, but a node
21
+ * built any other way (sticky notes were, issue #869) had nothing holding it
22
+ * to the contract: `check` passed, `compile` wrote the file, and only
23
+ * `flow validate` refused it. `serialize` runs this over every finished scope,
24
+ * so a missing field is an SDK emit bug caught before a file is written — on
25
+ * every path, `compile --no-check` and direct `serialize()` included.
26
+ */
27
+ /** Every way `node` breaks the envelope, as `<field>: <problem>` strings. Empty when it conforms. */
28
+ export declare function nodeEnvelopeProblems(node: unknown): string[];
29
+ /**
30
+ * Throw if any node in one scope breaks the envelope. `where` names the scope
31
+ * (`flow "x"`, `subflow "call"`) so the message points at the file section
32
+ * `flow validate` would have reported.
33
+ */
34
+ export declare function assertNodeEnvelope(nodes: readonly unknown[], where: string): void;
@@ -0,0 +1,83 @@
1
+ /**
2
+ * The node envelope: the fields `uip maestro flow validate` requires on EVERY
3
+ * entry of a `.flow` file's `nodes[]` (root and every `subflows.<id>.nodes[]`),
4
+ * whatever its type.
5
+ *
6
+ * Mirrors the platform's file-format node schema (`@uipath/flow-schema`
7
+ * `fileFormatNodeSchema`, bundled into the CLI's flow tool):
8
+ *
9
+ * ```ts
10
+ * id: z.string().min(1)
11
+ * type: z.string().min(1)
12
+ * typeVersion: z.string().regex(/^\d+\.\d+(\.\d+)?$/)
13
+ * display: z.object({ … }) // required, may be {}
14
+ * ui: { position: { x: number, y: number }, size?: { width, height } } // optional in the file
15
+ * inputs?: Record<string, unknown>
16
+ * outputs?: Record<string, unknown>
17
+ * parentId?: z.string().min(1)
18
+ * ```
19
+ *
20
+ * `makeNode` builds this shape for every definition-backed node, but a node
21
+ * built any other way (sticky notes were, issue #869) had nothing holding it
22
+ * to the contract: `check` passed, `compile` wrote the file, and only
23
+ * `flow validate` refused it. `serialize` runs this over every finished scope,
24
+ * so a missing field is an SDK emit bug caught before a file is written — on
25
+ * every path, `compile --no-check` and direct `serialize()` included.
26
+ */
27
+ const TYPE_VERSION_RE = /^\d+\.\d+(\.\d+)?$/;
28
+ const isRecord = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
29
+ const isNonEmptyString = (v) => typeof v === 'string' && v.length > 0;
30
+ /** Every way `node` breaks the envelope, as `<field>: <problem>` strings. Empty when it conforms. */
31
+ export function nodeEnvelopeProblems(node) {
32
+ if (!isRecord(node))
33
+ return ['node: expected an object'];
34
+ const problems = [];
35
+ if (!isNonEmptyString(node.id))
36
+ problems.push('id: expected a non-empty string');
37
+ if (!isNonEmptyString(node.type))
38
+ problems.push('type: expected a non-empty string');
39
+ if (typeof node.typeVersion !== 'string' || !TYPE_VERSION_RE.test(node.typeVersion)) {
40
+ problems.push(`typeVersion: expected "x.y" or "x.y.z", got ${JSON.stringify(node.typeVersion)}`);
41
+ }
42
+ if (!isRecord(node.display))
43
+ problems.push('display: expected an object (may be empty)');
44
+ if (node.ui !== undefined) {
45
+ const pos = isRecord(node.ui) ? node.ui.position : undefined;
46
+ if (!isRecord(pos) || typeof pos.x !== 'number' || typeof pos.y !== 'number') {
47
+ problems.push('ui.position: expected { x: number, y: number }');
48
+ }
49
+ const size = isRecord(node.ui) ? node.ui.size : undefined;
50
+ if (size !== undefined && (!isRecord(size) || typeof size.width !== 'number' || typeof size.height !== 'number')) {
51
+ problems.push('ui.size: expected { width: number, height: number }');
52
+ }
53
+ }
54
+ if (node.inputs !== undefined && !isRecord(node.inputs))
55
+ problems.push('inputs: expected an object');
56
+ if (node.outputs !== undefined && !isRecord(node.outputs))
57
+ problems.push('outputs: expected an object');
58
+ if (node.parentId !== undefined && !isNonEmptyString(node.parentId)) {
59
+ problems.push('parentId: expected a non-empty string');
60
+ }
61
+ return problems;
62
+ }
63
+ /**
64
+ * Throw if any node in one scope breaks the envelope. `where` names the scope
65
+ * (`flow "x"`, `subflow "call"`) so the message points at the file section
66
+ * `flow validate` would have reported.
67
+ */
68
+ export function assertNodeEnvelope(nodes, where) {
69
+ const bad = [];
70
+ nodes.forEach((n, i) => {
71
+ const problems = nodeEnvelopeProblems(n);
72
+ if (!problems.length)
73
+ return;
74
+ const id = isRecord(n) && typeof n.id === 'string' ? n.id : `#${i}`;
75
+ const type = isRecord(n) && typeof n.type === 'string' ? n.type : '?';
76
+ bad.push(` node "${id}" (${type}): ${problems.join('; ')}`);
77
+ });
78
+ if (!bad.length)
79
+ return;
80
+ throw new Error(`internal SDK error: ${where} would emit ${bad.length} node(s) that \`uip maestro flow validate\` `
81
+ + `rejects — every node needs id, type, typeVersion and display:\n${bad.join('\n')}\n`
82
+ + `This is a serializer bug, not an authoring mistake; please report it.`);
83
+ }
package/dist/serialize.js CHANGED
@@ -19,6 +19,7 @@ import { BindingRefusal, GUID_RE, bindingSelfNameMessage, bindingUndeclaredMessa
19
19
  import { ByoaRefusal, byoaUnverified } from './core/byoa.js';
20
20
  import { OUTCOME_HANDLE_NODE_TYPES, assertHitlExitInvariant, hitlExitModel, hitlExitViolation, outcomeSlug } from './core/hitl-routing.js';
21
21
  import { declaredExits, hasExits, stopsAtEnd } from './core/step-ports.js';
22
+ import { assertNodeEnvelope } from './core/node-envelope.js';
22
23
  import { buildConfiguration, transportHttpMethod } from './config.js';
23
24
  import { stableId } from './core/stable-id.js';
24
25
  import { readEventFilter, eventFilterProblem, eventFilterJmes, eventFilterTreeLeaf } from './event-filters.js';
@@ -3063,6 +3064,8 @@ const ERROR_ENVELOPE_VAR = {
3063
3064
  function errorEnvelopeVar(nodeType) {
3064
3065
  return ERROR_ENVELOPE_VAR[nodeType];
3065
3066
  }
3067
+ /** The `typeVersion` Studio Web stamps on a sticky note. The type has no manifest to take it from. */
3068
+ const STICKY_NOTE_TYPE_VERSION = '1.0.0';
3066
3069
  /**
3067
3070
  * Append the flow's `.stickyNotes()` as real `stickyNote` nodes.
3068
3071
  *
@@ -3100,14 +3103,20 @@ function emitStickyNotes(built, scope) {
3100
3103
  if ((note.width === undefined) !== (note.height === undefined)) {
3101
3104
  throw new Error(`.stickyNotes(): note "${id}" needs both width and height, or neither.`);
3102
3105
  }
3106
+ // The envelope Studio Web writes for a note (flow-v1 canvas: the add path
3107
+ // sets `typeVersion: '1.0.0'` and no display, and `nodeToInstance` persists
3108
+ // `display` as `{}`). Both fields are required on every node by
3109
+ // `flow validate`, manifest or not (#869).
3103
3110
  scope.nodes.push({
3104
3111
  id,
3105
3112
  type: 'stickyNote',
3106
- inputs: { color: note.color ?? 'yellow', content: note.content },
3113
+ typeVersion: STICKY_NOTE_TYPE_VERSION,
3107
3114
  ui: {
3108
3115
  position: { x: note.x, y: note.y },
3109
3116
  ...(note.width !== undefined ? { size: { width: note.width, height: note.height } } : {}),
3110
3117
  },
3118
+ display: {},
3119
+ inputs: { color: note.color ?? 'yellow', content: note.content },
3111
3120
  });
3112
3121
  });
3113
3122
  }
@@ -4184,6 +4193,7 @@ export function serialize(built, opts = {}) {
4184
4193
  typeAgentInputSchemas(scope, globals);
4185
4194
  emitStickyNotes(child, scope);
4186
4195
  const layoutEdges = applyAuthorLayout(child.layout, scope, `subflow "${nodeId}"`);
4196
+ assertNodeEnvelope(scope.nodes, `subflow "${nodeId}"`);
4187
4197
  return {
4188
4198
  nodes: scope.nodes,
4189
4199
  edges: scope.edges,
@@ -6241,6 +6251,7 @@ export function serialize(built, opts = {}) {
6241
6251
  typeAgentInputSchemas(root, globals);
6242
6252
  emitStickyNotes(built, root);
6243
6253
  const rootLayoutEdges = applyAuthorLayout(built.layout, root, `flow "${built.id}"`);
6254
+ assertNodeEnvelope(root.nodes, `flow "${built.id}"`);
6244
6255
  // The FILE-FORMAT version this document declares: the author's
6245
6256
  // `.schemaVersion()` when they set one, else the profile's write floor. It is
6246
6257
  // resolved HERE rather than in `check()` alone because the serializer must not
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uipath/maestro-builder-sdk",
3
- "version": "6.14.2",
3
+ "version": "6.14.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",
@@ -92,5 +92,5 @@
92
92
  "@types/node": "^22.7.0",
93
93
  "esbuild": "^0.28.1"
94
94
  },
95
- "gitref": "cb7a821ea17b6f3022c7b90e2567e91a5495576a"
95
+ "gitref": "52d87c5c383324464fd4c672ac4e9ec40795d994"
96
96
  }