@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.
- package/dist/core/node-envelope.d.ts +34 -0
- package/dist/core/node-envelope.js +83 -0
- package/dist/serialize.js +12 -1
- package/package.json +2 -2
|
@@ -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
|
-
|
|
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.
|
|
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": "
|
|
95
|
+
"gitref": "52d87c5c383324464fd4c672ac4e9ec40795d994"
|
|
96
96
|
}
|