@uipath/maestro-builder-sdk 6.66.6 → 6.66.8
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/bindings-v2.d.ts +15 -1
- package/dist/bindings-v2.js +29 -6
- package/dist/bpmn/check.js +16 -113
- package/dist/bpmn/serialize.d.ts +41 -0
- package/dist/bpmn/serialize.js +103 -1
- package/dist/bpmn/typed-node.d.ts +12 -0
- package/dist/bpmn/typed-node.js +24 -0
- package/dist/case/check.js +5 -40
- package/dist/case/serialize.js +6 -0
- package/dist/case/task-shape.d.ts +31 -0
- package/dist/case/task-shape.js +48 -0
- package/dist/check.js +7 -43
- package/dist/core/agent-sidecar.d.ts +13 -3
- package/dist/core/agent-sidecar.js +10 -0
- package/dist/core/compile-refusal.d.ts +25 -0
- package/dist/core/compile-refusal.js +19 -0
- package/dist/core/process-resource.d.ts +9 -0
- package/dist/core/process-resource.js +4 -0
- package/dist/core/refusals.d.ts +47 -44
- package/dist/core/refusals.js +47 -44
- package/dist/nodes/agent/flow/check.js +14 -42
- package/dist/nodes/agent/flow/inline-agent.js +62 -1
- package/dist/nodes/agent/flow/spec.d.ts +1 -1
- package/dist/nodes/connector/flow/node-type.js +5 -3
- package/dist/nodes/data-fabric/flow/check.js +2 -4
- package/dist/nodes/data-fabric/flow/shared.d.ts +9 -4
- package/dist/nodes/data-fabric/flow/shared.js +14 -4
- package/dist/nodes/document/flow/ixp.js +20 -9
- package/dist/nodes/hitl/flow/check.js +29 -110
- package/dist/nodes/hitl/flow/node-type.js +170 -3
- package/dist/nodes/process/flow/check.js +15 -80
- package/dist/nodes/process/flow/node-type.js +4 -0
- package/dist/nodes/process/flow/refusals.d.ts +8 -0
- package/dist/nodes/process/flow/refusals.js +92 -0
- package/dist/nodes/queue/flow/check.d.ts +25 -0
- package/dist/nodes/queue/flow/check.js +48 -18
- package/dist/nodes/queue/flow/node-type.js +13 -1
- package/dist/nodes/transform/flow/check.js +31 -43
- package/dist/nodes/transform/flow/node-type.js +35 -9
- package/dist/serialize.js +172 -20
- package/package.json +2 -2
package/dist/bindings-v2.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ConnectionUse } from './core/compile-refusal.js';
|
|
1
2
|
/** One declared solution resource. */
|
|
2
3
|
export interface BindingsV2Resource {
|
|
3
4
|
resource: string;
|
|
@@ -26,9 +27,22 @@ export interface BindingsV2Document {
|
|
|
26
27
|
* connector — are refused with `CONNECTION_CONNECTOR_CONFLICT`: at most one of
|
|
27
28
|
* them can run, and picking the first silently mislabels the solution resource.
|
|
28
29
|
*
|
|
30
|
+
* A collect-mode stand-in (`check` with no library) has no `detail`; `standIns`
|
|
31
|
+
* gives the connection it was bound to, by node id, so it is judged here like
|
|
32
|
+
* any node and `check` reports the conflict without a library.
|
|
33
|
+
*
|
|
34
|
+
* The bound-connection filter is not applied to a stand-in because a stand-in
|
|
35
|
+
* has no binding row to find. That does not make `check` stricter for the
|
|
36
|
+
* common case: with a library, a connector node writes its own ConnectionId row
|
|
37
|
+
* keyed by its connection id, a literal id with no bindings.json entry included
|
|
38
|
+
* (`connectionResourceBindings`), so the same id is in `bound` and compile
|
|
39
|
+
* refuses the same conflict. The one input where the two differ is a node whose
|
|
40
|
+
* definition declares no connection binding at all: compile skips it, the
|
|
41
|
+
* stand-in cannot know that without the definition.
|
|
42
|
+
*
|
|
29
43
|
* @throws BindingRefusal when two nodes on one connection name different connectors.
|
|
30
44
|
*/
|
|
31
|
-
export declare function connectionConnectors(flow: unknown): Map<string, string>;
|
|
45
|
+
export declare function connectionConnectors(flow: unknown, standIns?: ReadonlyMap<string, ConnectionUse>): Map<string, string>;
|
|
32
46
|
/**
|
|
33
47
|
* The `connection` resources a compiled flow declares — one per distinct
|
|
34
48
|
* connection, in node order, attributed to the FIRST node that uses it.
|
package/dist/bindings-v2.js
CHANGED
|
@@ -118,9 +118,22 @@ function allNodes(flow) {
|
|
|
118
118
|
* connector — are refused with `CONNECTION_CONNECTOR_CONFLICT`: at most one of
|
|
119
119
|
* them can run, and picking the first silently mislabels the solution resource.
|
|
120
120
|
*
|
|
121
|
+
* A collect-mode stand-in (`check` with no library) has no `detail`; `standIns`
|
|
122
|
+
* gives the connection it was bound to, by node id, so it is judged here like
|
|
123
|
+
* any node and `check` reports the conflict without a library.
|
|
124
|
+
*
|
|
125
|
+
* The bound-connection filter is not applied to a stand-in because a stand-in
|
|
126
|
+
* has no binding row to find. That does not make `check` stricter for the
|
|
127
|
+
* common case: with a library, a connector node writes its own ConnectionId row
|
|
128
|
+
* keyed by its connection id, a literal id with no bindings.json entry included
|
|
129
|
+
* (`connectionResourceBindings`), so the same id is in `bound` and compile
|
|
130
|
+
* refuses the same conflict. The one input where the two differ is a node whose
|
|
131
|
+
* definition declares no connection binding at all: compile skips it, the
|
|
132
|
+
* stand-in cannot know that without the definition.
|
|
133
|
+
*
|
|
121
134
|
* @throws BindingRefusal when two nodes on one connection name different connectors.
|
|
122
135
|
*/
|
|
123
|
-
export function connectionConnectors(flow) {
|
|
136
|
+
export function connectionConnectors(flow, standIns = new Map()) {
|
|
124
137
|
const out = new Map();
|
|
125
138
|
if (!isRecord(flow))
|
|
126
139
|
return out;
|
|
@@ -133,11 +146,21 @@ export function connectionConnectors(flow) {
|
|
|
133
146
|
const users = new Map();
|
|
134
147
|
for (const node of allNodes(flow)) {
|
|
135
148
|
const detail = nodeDetail(node);
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (
|
|
149
|
+
const standIn = typeof node.id === 'string' && isRecord(node.inputs) && node.inputs.standIn === true
|
|
150
|
+
? standIns.get(node.id) : undefined;
|
|
151
|
+
let key;
|
|
152
|
+
let connector;
|
|
153
|
+
if (standIn) {
|
|
154
|
+
key = standIn.connection;
|
|
155
|
+
connector = standIn.connector;
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
key = detail && nodeConnectionKey(detail);
|
|
159
|
+
if (!detail || !key || !bound.has(key))
|
|
160
|
+
continue;
|
|
161
|
+
connector = nodeConnectionConnector(detail);
|
|
162
|
+
}
|
|
163
|
+
if (!key || !connector)
|
|
141
164
|
continue;
|
|
142
165
|
const byConnector = users.get(key) ?? new Map();
|
|
143
166
|
users.set(key, byConnector);
|
package/dist/bpmn/check.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { allNodes, folderCompanionKeys, implicitNodeVars, processId } from './serialize.js';
|
|
1
|
+
import { allNodes, documentIdProblems, folderCompanionKeys, implicitNodeVars, processId } from './serialize.js';
|
|
3
2
|
import { KNOWN_METHOD_SET } from './known-methods.js';
|
|
4
3
|
import { EQUALS_EXPR_DIALECT, checkConnectorContract, checkConnectorLookups, checkConnectorSchema, checkBindingLabels, } from '../core/connector-checks.js';
|
|
5
4
|
import { checkEventResolution, checkEventSubscription, eventParamsFor, } from '../core/event-checks.js';
|
|
6
|
-
import { bindingRequiredFields, CASE_RUNTIME_INTERNAL_TYPES, missingRequiredContext, missingRequiredInputs, REGISTRY_GAPS, registryType, } from './typed-node.js';
|
|
5
|
+
import { bindingRequiredFields, CASE_RUNTIME_INTERNAL_TYPES, CONNECTOR_ACTIVITY_TYPES, connectorInputsShadowed, missingRequiredContext, missingRequiredInputs, REGISTRY_GAPS, registryType, } from './typed-node.js';
|
|
7
6
|
import { ownEntry } from '../core/own-entry.js';
|
|
8
7
|
import { isManagedHttpV2, managedHttpAbsoluteUrl, managedHttpMissing } from './managed-http.js';
|
|
9
8
|
/**
|
|
@@ -60,13 +59,13 @@ const GATEWAY_KINDS = new Set(['exclusiveGateway', 'parallelGateway', 'inclusive
|
|
|
60
59
|
const ACTIVITY_KINDS = new Set(['task', 'scriptTask', 'subProcess', 'connector', 'typed', 'connectorEvent', 'externalTask']);
|
|
61
60
|
export function check(built, opts = {}) {
|
|
62
61
|
const diags = [];
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
// The rule lives in serialize.ts `documentIdProblems`; this check does not serialize.
|
|
63
|
+
for (const p of documentIdProblems(built)) {
|
|
64
|
+
diags.push({ level: 'error', code: p.code, message: p.message, ...(p.where ? { where: p.where } : {}) });
|
|
65
|
+
}
|
|
66
|
+
checkScope(built.nodes, built.flows, 'process', diags, true);
|
|
67
67
|
checkScopedVars(built, diags);
|
|
68
68
|
checkVariableOrder(built, diags);
|
|
69
|
-
checkDocumentIds(built, diags);
|
|
70
69
|
checkFolderBindings(built, diags);
|
|
71
70
|
checkIntsvcActivities(built, diags);
|
|
72
71
|
checkEntryPointId(built, diags);
|
|
@@ -237,8 +236,6 @@ function checkMethodParentheses(built, diags) {
|
|
|
237
236
|
};
|
|
238
237
|
walk(built.nodes, built.flows);
|
|
239
238
|
}
|
|
240
|
-
/** The Integration Service activity types, whose payload rows the platform reads only by `target`. */
|
|
241
|
-
const CONNECTOR_ACTIVITY_TYPES = new Set(['Intsvc.ActivityExecution', 'Intsvc.AsyncExecution']);
|
|
242
239
|
/**
|
|
243
240
|
* The targets the platform's reader knows (`UiPathExtensionsProvider`): anything else —
|
|
244
241
|
* an empty string included — falls through its switch to `Body`, and the row becomes the
|
|
@@ -256,23 +253,18 @@ const PLATFORM_ROW_TARGETS = new Set(['body', 'bodyField', 'header', 'path', 'qu
|
|
|
256
253
|
* while the v1 arm's `<uipath:input name="body" target="body">{"script":"…"}</uipath:input>`
|
|
257
254
|
* ran. `inputs: { script }` folds a request field into the body row, so the remedy is
|
|
258
255
|
* the plain record for a body field, or the row's own target when it is a real slot.
|
|
259
|
-
* And because an `inputRows` list REPLACES `inputs` at emission
|
|
260
|
-
*
|
|
256
|
+
* And because an `inputRows` list REPLACES `inputs` at emission, a node that sets both
|
|
257
|
+
* would drop every `inputs` field — the same live fault — so the serializer refuses it
|
|
258
|
+
* (typed-node.ts `connectorInputsShadowed`).
|
|
261
259
|
*/
|
|
262
260
|
function checkConnectorRows(built, diags) {
|
|
263
261
|
for (const n of allNodes(built.nodes)) {
|
|
264
262
|
if (n.kind !== 'typed' || !CONNECTOR_ACTIVITY_TYPES.has(n.type))
|
|
265
263
|
continue;
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
where: n.id,
|
|
271
|
-
message: `Connector activity "${n.id}" sets both \`inputs\` (${Object.keys(n.inputs).map((k) => `"${k}"`).join(', ')}) and \`inputRows\`; ` +
|
|
272
|
-
`an \`inputRows\` list replaces \`inputs\` entirely at emission, so those fields are never sent. Spell them as rows too ` +
|
|
273
|
-
`(a body field as \`{ name: 'body', type: 'json', target: 'body', body: { … } }\`), or drop \`inputRows\` and write every field in \`inputs\`.`,
|
|
274
|
-
});
|
|
275
|
-
}
|
|
264
|
+
// The rule lives in typed-node.ts `connectorInputsShadowed`.
|
|
265
|
+
const shadowed = connectorInputsShadowed(n.id, n.type, n.inputs, n.inputRows);
|
|
266
|
+
if (shadowed)
|
|
267
|
+
diags.push({ level: 'error', code: 'CONNECTOR_INPUTS_SHADOWED', where: n.id, message: shadowed });
|
|
276
268
|
for (const row of n.inputRows ?? []) {
|
|
277
269
|
const target = row.target ?? '';
|
|
278
270
|
if (target === '') {
|
|
@@ -628,85 +620,6 @@ function checkIntsvcActivities(built, diags) {
|
|
|
628
620
|
}
|
|
629
621
|
}
|
|
630
622
|
}
|
|
631
|
-
/**
|
|
632
|
-
* Every `id` in the document must be unique — including across KINDS.
|
|
633
|
-
*
|
|
634
|
-
* Not a style rule. `bpmn-moddle` keeps one document-global id registry, so a
|
|
635
|
-
* second use of an id makes its whole element **unparsable**, and it drops it:
|
|
636
|
-
*
|
|
637
|
-
* unparsable content <bpmn:task> detected … nested error: duplicate ID <after>
|
|
638
|
-
*
|
|
639
|
-
* `.var('after', …)` next to `.task('after', …)` therefore emits a `.bpmn` whose
|
|
640
|
-
* task simply is not there. Both validators still pass it — the canvas one
|
|
641
|
-
* downgrades the now-dangling flows to warnings and `uip maestro bpmn validate`
|
|
642
|
-
* reports `Valid` — and the runtime then dies with `Unknown element 'after'`, or
|
|
643
|
-
* worse, silently does no work if nothing else routes through the hole. So this
|
|
644
|
-
* is the only gate that can catch it, and it has to be an error.
|
|
645
|
-
*
|
|
646
|
-
* The ids in play are every element and flow, every variable and binding, the
|
|
647
|
-
* synthesized `Message_n`/`Error_n` declarations, and the process itself — and the
|
|
648
|
-
* declarations serialize DERIVES from output rows (a connector's `<id>_response`, a
|
|
649
|
-
* script task's `outputs`), which are variables in the document too. Those were the one
|
|
650
|
-
* kind this check could not see: a script output named after the gateway that routes
|
|
651
|
-
* on it (`outputs: { confidence }` beside `.choose('confidence', …)`) passed here and
|
|
652
|
-
* every SDK gate, and `uip maestro bpmn validate` answered with three
|
|
653
|
-
* CROSSING_SUBPROCESS_BOUNDARY errors on the gateway's flows — the validator had
|
|
654
|
-
* resolved the id to the variable. Measured on the nested_review port.
|
|
655
|
-
*/
|
|
656
|
-
function checkDocumentIds(built, diags) {
|
|
657
|
-
const owners = new Map();
|
|
658
|
-
const claim = (id, kind) => {
|
|
659
|
-
const existing = owners.get(id);
|
|
660
|
-
if (existing === undefined) {
|
|
661
|
-
owners.set(id, kind);
|
|
662
|
-
return;
|
|
663
|
-
}
|
|
664
|
-
diags.push({
|
|
665
|
-
level: 'error',
|
|
666
|
-
code: 'DUPLICATE_DOCUMENT_ID',
|
|
667
|
-
message: `id "${id}" is used by both ${existing} and ${kind}. Every id in a .bpmn must be unique — ` +
|
|
668
|
-
`the XML parser drops the second element that reuses one, so it disappears from the process.`,
|
|
669
|
-
where: id,
|
|
670
|
-
});
|
|
671
|
-
};
|
|
672
|
-
claim(built.id, 'the process');
|
|
673
|
-
const owner = (nodes, id) => nodes.find((n) => nodeDeclaredVars(n).includes(id))?.id ?? '?';
|
|
674
|
-
// A sub-process row's variable counts as declared when serialize's createSubProcess
|
|
675
|
-
// sees it so: the root's declarations, implied ones included, and every sub-process's own.
|
|
676
|
-
const rootImplied = implicitNodeVars(built.nodes, built.variables);
|
|
677
|
-
const declared = [
|
|
678
|
-
...built.variables,
|
|
679
|
-
...rootImplied,
|
|
680
|
-
...allNodes(built.nodes).flatMap((n) => (n.kind === 'subProcess' ? n.variables : [])),
|
|
681
|
-
];
|
|
682
|
-
const walk = (nodes, flows) => {
|
|
683
|
-
for (const n of nodes) {
|
|
684
|
-
claim(n.id, `element "${n.id}"`);
|
|
685
|
-
if (n.kind === 'subProcess') {
|
|
686
|
-
for (const v of n.variables)
|
|
687
|
-
claim(v.id, `variable "${v.id}"`);
|
|
688
|
-
for (const v of implicitNodeVars(n.nodes, [...n.variables, ...declared])) {
|
|
689
|
-
claim(v.id, `the variable serialize declares for the output row of "${owner(n.nodes, v.id)}"`);
|
|
690
|
-
}
|
|
691
|
-
walk(n.nodes, n.flows);
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
for (const f of flows)
|
|
695
|
-
claim(f.id, `flow "${f.id}"`);
|
|
696
|
-
};
|
|
697
|
-
walk(built.nodes, built.flows);
|
|
698
|
-
for (const v of built.variables)
|
|
699
|
-
claim(v.id, `variable "${v.id}"`);
|
|
700
|
-
for (const v of rootImplied) {
|
|
701
|
-
claim(v.id, `the variable serialize declares for the output row of "${owner(built.nodes, v.id)}"`);
|
|
702
|
-
}
|
|
703
|
-
for (const b of built.bindings)
|
|
704
|
-
claim(b.id, `binding "${b.id}"`);
|
|
705
|
-
for (const mm of built.messages)
|
|
706
|
-
claim(mm.id, `message "${mm.name}"`);
|
|
707
|
-
for (const e of built.errors)
|
|
708
|
-
claim(e.id, `error "${e.name}"`);
|
|
709
|
-
}
|
|
710
623
|
/**
|
|
711
624
|
* A pinned declaration order must name the declarations it is ordering.
|
|
712
625
|
*
|
|
@@ -1021,7 +934,7 @@ function checkActivityFields(n, diags) {
|
|
|
1021
934
|
});
|
|
1022
935
|
}
|
|
1023
936
|
}
|
|
1024
|
-
function checkScope(nodes, flows, where, diags,
|
|
937
|
+
function checkScope(nodes, flows, where, diags, isTop,
|
|
1025
938
|
/**
|
|
1026
939
|
* Whether the CONTAINER of these nodes is an event sub-process. The process root
|
|
1027
940
|
* never is, so it defaults false — see ERROR_START_OUTSIDE_EVENT_SUBPROCESS below.
|
|
@@ -1030,11 +943,6 @@ inEventSubProcess = false) {
|
|
|
1030
943
|
const nodeIds = new Set();
|
|
1031
944
|
for (const n of nodes)
|
|
1032
945
|
nodeIds.add(n.id);
|
|
1033
|
-
// Duplicate ids (globally across the document).
|
|
1034
|
-
for (const n of nodes)
|
|
1035
|
-
dupId(n.id, seenIds, diags, where);
|
|
1036
|
-
for (const f of flows)
|
|
1037
|
-
dupId(f.id, seenIds, diags, where);
|
|
1038
946
|
// At least one start event per scope that has any nodes.
|
|
1039
947
|
//
|
|
1040
948
|
// "Start event" is the ELEMENT, not the builder kind. A registry-backed node can
|
|
@@ -1472,7 +1380,7 @@ inEventSubProcess = false) {
|
|
|
1472
1380
|
}
|
|
1473
1381
|
// Recurse into sub-process bodies.
|
|
1474
1382
|
if (n.kind === 'subProcess')
|
|
1475
|
-
checkScope(n.nodes, n.flows, `subProcess "${n.id}"`, diags,
|
|
1383
|
+
checkScope(n.nodes, n.flows, `subProcess "${n.id}"`, diags, false, n.triggeredByEvent === true);
|
|
1476
1384
|
}
|
|
1477
1385
|
}
|
|
1478
1386
|
/**
|
|
@@ -1500,8 +1408,3 @@ function isStartElement(n) {
|
|
|
1500
1408
|
return false;
|
|
1501
1409
|
}
|
|
1502
1410
|
}
|
|
1503
|
-
function dupId(id, seen, diags, where) {
|
|
1504
|
-
if (seen.has(id))
|
|
1505
|
-
diags.push({ level: 'error', code: 'DUP_ID', message: `Duplicate element id "${id}" in ${where}.`, where: id });
|
|
1506
|
-
seen.add(id);
|
|
1507
|
-
}
|
package/dist/bpmn/serialize.d.ts
CHANGED
|
@@ -24,6 +24,47 @@ export declare function serialize(built: BuiltBpmn, opts?: BpmnSerializeOpts): P
|
|
|
24
24
|
* of the trade. Revisit if the graders learn to treat absence as interrupting.
|
|
25
25
|
*/
|
|
26
26
|
export declare function finalizeBpmnXml(xml: string): string;
|
|
27
|
+
/** One id problem in a process: the code, its message, and the id it is about. */
|
|
28
|
+
export interface DocumentIdProblem {
|
|
29
|
+
code: 'BPMN_NO_ID' | 'DUP_ID' | 'DUPLICATE_DOCUMENT_ID';
|
|
30
|
+
message: string;
|
|
31
|
+
where: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Every id the `.bpmn` would get wrong, in document order. `serialize` refuses the
|
|
35
|
+
* first; `check` reports them all. One rule for both, because BPMN `check` does not
|
|
36
|
+
* run the serializer.
|
|
37
|
+
*
|
|
38
|
+
* - `BPMN_NO_ID`: an empty process id. `processId` would write the invented
|
|
39
|
+
* `Process_1`, a process the source does not name (decompile then writes
|
|
40
|
+
* `bpmn("Process_1")`). Flow's and Case's `NO_ID` stay `lint`: their files keep
|
|
41
|
+
* the empty id, or never carry it.
|
|
42
|
+
* - `DUP_ID`: two elements or sequence flows with one id.
|
|
43
|
+
* - `DUPLICATE_DOCUMENT_ID`: any other id used twice: an element and a variable,
|
|
44
|
+
* two variables, a binding, a message, an error, the process.
|
|
45
|
+
*
|
|
46
|
+
* Not style rules. `bpmn-moddle` keeps one document-global id registry, so a
|
|
47
|
+
* second use of an id makes its whole element **unparsable**, and it drops it:
|
|
48
|
+
*
|
|
49
|
+
* unparsable content <bpmn:task> detected … nested error: duplicate ID <after>
|
|
50
|
+
*
|
|
51
|
+
* `.var('after', …)` next to `.task('after', …)` therefore emits a `.bpmn` whose
|
|
52
|
+
* task simply is not there. Both validators still pass it — the canvas one
|
|
53
|
+
* downgrades the now-dangling flows to warnings and `uip maestro bpmn validate`
|
|
54
|
+
* reports `Valid` — and the runtime then dies with `Unknown element 'after'`, or
|
|
55
|
+
* worse, silently does no work if nothing else routes through the hole.
|
|
56
|
+
*
|
|
57
|
+
* The ids in play are every element and flow, every variable and binding, the
|
|
58
|
+
* synthesized `Message_n`/`Error_n` declarations, and the process itself (its id
|
|
59
|
+
* as written, `processId`) — and the declarations this file DERIVES from output
|
|
60
|
+
* rows (a connector's `<id>_response`, a script task's `outputs`), which are
|
|
61
|
+
* variables in the document too. A script output named after the gateway that
|
|
62
|
+
* routes on it (`outputs: { confidence }` beside `.choose('confidence', …)`) once
|
|
63
|
+
* passed every SDK gate, and `uip maestro bpmn validate` answered with three
|
|
64
|
+
* CROSSING_SUBPROCESS_BOUNDARY errors on the gateway's flows — the validator had
|
|
65
|
+
* resolved the id to the variable. Measured on the nested_review port.
|
|
66
|
+
*/
|
|
67
|
+
export declare function documentIdProblems(built: BuiltBpmn): DocumentIdProblem[];
|
|
27
68
|
export declare function processId(id: string): string;
|
|
28
69
|
/**
|
|
29
70
|
* The `args` keys every Studio Web script task carries, in the order the designer writes
|
package/dist/bpmn/serialize.js
CHANGED
|
@@ -18,7 +18,7 @@ import { createRequire } from 'node:module';
|
|
|
18
18
|
import { createConnectorTask } from './connector.js';
|
|
19
19
|
import { createConnectorEvent, createExternalTask, intsvcOutputVar } from './intsvc.js';
|
|
20
20
|
import { createExtensionPayload, createTypedNode, rowDeclaration, typedNodeOutputVar, verbatimOutputRows } from './typed-node.js';
|
|
21
|
-
import { connectorOutputVars, typedNodeDeclaredVars } from './declared-vars.js';
|
|
21
|
+
import { connectorOutputVars, nodeDeclaredVars, typedNodeDeclaredVars } from './declared-vars.js';
|
|
22
22
|
import { defaultEntryPointId } from './entry-point-id.js';
|
|
23
23
|
import { parseXmlRanges } from './xml-ranges.js';
|
|
24
24
|
import { ownEntry } from '../core/own-entry.js';
|
|
@@ -53,6 +53,11 @@ function mapType(t) {
|
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
export async function serialize(built, opts = {}) {
|
|
56
|
+
// compile writes what this returns, and since #1497 nothing runs `check` first
|
|
57
|
+
// on every path, so a document this would write wrong is refused here.
|
|
58
|
+
const [problem] = documentIdProblems(built);
|
|
59
|
+
if (problem)
|
|
60
|
+
throw new CompileRefusal(problem.code, problem.message);
|
|
56
61
|
const m = new BpmnModdle({ uipath: uipathDescriptor });
|
|
57
62
|
const messageById = new Map();
|
|
58
63
|
const messages = built.messages.map((mm) => {
|
|
@@ -308,6 +313,103 @@ function cdata(name, text) {
|
|
|
308
313
|
}
|
|
309
314
|
return `<![CDATA[${escaped}]]>`;
|
|
310
315
|
}
|
|
316
|
+
/**
|
|
317
|
+
* Every id the `.bpmn` would get wrong, in document order. `serialize` refuses the
|
|
318
|
+
* first; `check` reports them all. One rule for both, because BPMN `check` does not
|
|
319
|
+
* run the serializer.
|
|
320
|
+
*
|
|
321
|
+
* - `BPMN_NO_ID`: an empty process id. `processId` would write the invented
|
|
322
|
+
* `Process_1`, a process the source does not name (decompile then writes
|
|
323
|
+
* `bpmn("Process_1")`). Flow's and Case's `NO_ID` stay `lint`: their files keep
|
|
324
|
+
* the empty id, or never carry it.
|
|
325
|
+
* - `DUP_ID`: two elements or sequence flows with one id.
|
|
326
|
+
* - `DUPLICATE_DOCUMENT_ID`: any other id used twice: an element and a variable,
|
|
327
|
+
* two variables, a binding, a message, an error, the process.
|
|
328
|
+
*
|
|
329
|
+
* Not style rules. `bpmn-moddle` keeps one document-global id registry, so a
|
|
330
|
+
* second use of an id makes its whole element **unparsable**, and it drops it:
|
|
331
|
+
*
|
|
332
|
+
* unparsable content <bpmn:task> detected … nested error: duplicate ID <after>
|
|
333
|
+
*
|
|
334
|
+
* `.var('after', …)` next to `.task('after', …)` therefore emits a `.bpmn` whose
|
|
335
|
+
* task simply is not there. Both validators still pass it — the canvas one
|
|
336
|
+
* downgrades the now-dangling flows to warnings and `uip maestro bpmn validate`
|
|
337
|
+
* reports `Valid` — and the runtime then dies with `Unknown element 'after'`, or
|
|
338
|
+
* worse, silently does no work if nothing else routes through the hole.
|
|
339
|
+
*
|
|
340
|
+
* The ids in play are every element and flow, every variable and binding, the
|
|
341
|
+
* synthesized `Message_n`/`Error_n` declarations, and the process itself (its id
|
|
342
|
+
* as written, `processId`) — and the declarations this file DERIVES from output
|
|
343
|
+
* rows (a connector's `<id>_response`, a script task's `outputs`), which are
|
|
344
|
+
* variables in the document too. A script output named after the gateway that
|
|
345
|
+
* routes on it (`outputs: { confidence }` beside `.choose('confidence', …)`) once
|
|
346
|
+
* passed every SDK gate, and `uip maestro bpmn validate` answered with three
|
|
347
|
+
* CROSSING_SUBPROCESS_BOUNDARY errors on the gateway's flows — the validator had
|
|
348
|
+
* resolved the id to the variable. Measured on the nested_review port.
|
|
349
|
+
*/
|
|
350
|
+
export function documentIdProblems(built) {
|
|
351
|
+
const problems = [];
|
|
352
|
+
if (!built.id)
|
|
353
|
+
problems.push({ code: 'BPMN_NO_ID', message: 'bpmn() requires an id.', where: '' });
|
|
354
|
+
/** id → who holds it, and whether that holder is an element or a flow. */
|
|
355
|
+
const owners = new Map();
|
|
356
|
+
const claim = (id, kind, element = undefined) => {
|
|
357
|
+
const existing = owners.get(id);
|
|
358
|
+
if (existing === undefined) {
|
|
359
|
+
owners.set(id, { kind, element: element !== undefined });
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
if (element && existing.element) {
|
|
363
|
+
problems.push({ code: 'DUP_ID', message: `Duplicate element id "${id}" in ${element.scope}.`, where: id });
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
problems.push({
|
|
367
|
+
code: 'DUPLICATE_DOCUMENT_ID',
|
|
368
|
+
message: `id "${id}" is used by both ${existing.kind} and ${kind}. Every id in a .bpmn must be unique — ` +
|
|
369
|
+
`the XML parser drops the second element that reuses one, so it disappears from the process.`,
|
|
370
|
+
where: id,
|
|
371
|
+
});
|
|
372
|
+
};
|
|
373
|
+
if (built.id)
|
|
374
|
+
claim(processId(built.id), 'the process');
|
|
375
|
+
const owner = (nodes, id) => nodes.find((n) => nodeDeclaredVars(n).includes(id))?.id ?? '?';
|
|
376
|
+
// A sub-process row's variable counts as declared when createSubProcess sees it
|
|
377
|
+
// so: the root's declarations, implied ones included, and every sub-process's own.
|
|
378
|
+
const rootImplied = implicitNodeVars(built.nodes, built.variables);
|
|
379
|
+
const declared = [
|
|
380
|
+
...built.variables,
|
|
381
|
+
...rootImplied,
|
|
382
|
+
...allNodes(built.nodes).flatMap((n) => (n.kind === 'subProcess' ? n.variables : [])),
|
|
383
|
+
];
|
|
384
|
+
const walk = (nodes, flows, scope) => {
|
|
385
|
+
for (const n of nodes) {
|
|
386
|
+
claim(n.id, `element "${n.id}"`, { scope });
|
|
387
|
+
if (n.kind === 'subProcess') {
|
|
388
|
+
for (const v of n.variables)
|
|
389
|
+
claim(v.id, `variable "${v.id}"`);
|
|
390
|
+
for (const v of implicitNodeVars(n.nodes, [...n.variables, ...declared])) {
|
|
391
|
+
claim(v.id, `the variable serialize declares for the output row of "${owner(n.nodes, v.id)}"`);
|
|
392
|
+
}
|
|
393
|
+
walk(n.nodes, n.flows, `subProcess "${n.id}"`);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
for (const f of flows)
|
|
397
|
+
claim(f.id, `flow "${f.id}"`, { scope });
|
|
398
|
+
};
|
|
399
|
+
walk(built.nodes, built.flows, 'process');
|
|
400
|
+
for (const v of built.variables)
|
|
401
|
+
claim(v.id, `variable "${v.id}"`);
|
|
402
|
+
for (const v of rootImplied) {
|
|
403
|
+
claim(v.id, `the variable serialize declares for the output row of "${owner(built.nodes, v.id)}"`);
|
|
404
|
+
}
|
|
405
|
+
for (const b of built.bindings)
|
|
406
|
+
claim(b.id, `binding "${b.id}"`);
|
|
407
|
+
for (const mm of built.messages)
|
|
408
|
+
claim(mm.id, `message "${mm.name}"`);
|
|
409
|
+
for (const e of built.errors)
|
|
410
|
+
claim(e.id, `error "${e.name}"`);
|
|
411
|
+
return problems;
|
|
412
|
+
}
|
|
311
413
|
export function processId(id) {
|
|
312
414
|
return /^[A-Za-z_][\w.-]*$/.test(id) ? id : 'Process_1';
|
|
313
415
|
}
|
|
@@ -328,6 +328,18 @@ export interface TypedOutputRow {
|
|
|
328
328
|
*/
|
|
329
329
|
target?: string;
|
|
330
330
|
}
|
|
331
|
+
/** The Integration Service activity types, whose payload rows the platform reads only by `target`. */
|
|
332
|
+
export declare const CONNECTOR_ACTIVITY_TYPES: ReadonlySet<string>;
|
|
333
|
+
/**
|
|
334
|
+
* CONNECTOR_INPUTS_SHADOWED's message when a connector activity sets both `inputs`
|
|
335
|
+
* and `inputRows`, else `undefined`.
|
|
336
|
+
*
|
|
337
|
+
* An `inputRows` list replaces `inputs` at emission (`createTypedNode`), so writing
|
|
338
|
+
* the node would silently drop every `inputs` field, and the live call then faults
|
|
339
|
+
* on the missing parameter. `createTypedNode` refuses it; `check` reports the same
|
|
340
|
+
* condition from this function, because BPMN `check` does not serialize.
|
|
341
|
+
*/
|
|
342
|
+
export declare function connectorInputsShadowed(id: string, type: string, inputs: Record<string, unknown> | undefined, rows: readonly unknown[] | undefined): string | undefined;
|
|
331
343
|
/** What an author supplies for a typed node, on top of its id. */
|
|
332
344
|
export interface TypedNodeInput {
|
|
333
345
|
/** Display name the designer shows. */
|
package/dist/bpmn/typed-node.js
CHANGED
|
@@ -193,6 +193,27 @@ export function rowDeclaration(row) {
|
|
|
193
193
|
const wire = type === 'object' || type === 'array' || type === 'any' ? 'json' : type;
|
|
194
194
|
return { type, wire, name: row.name, schema: row.varSchema };
|
|
195
195
|
}
|
|
196
|
+
/** The Integration Service activity types, whose payload rows the platform reads only by `target`. */
|
|
197
|
+
export const CONNECTOR_ACTIVITY_TYPES = new Set(['Intsvc.ActivityExecution', 'Intsvc.AsyncExecution']);
|
|
198
|
+
/**
|
|
199
|
+
* CONNECTOR_INPUTS_SHADOWED's message when a connector activity sets both `inputs`
|
|
200
|
+
* and `inputRows`, else `undefined`.
|
|
201
|
+
*
|
|
202
|
+
* An `inputRows` list replaces `inputs` at emission (`createTypedNode`), so writing
|
|
203
|
+
* the node would silently drop every `inputs` field, and the live call then faults
|
|
204
|
+
* on the missing parameter. `createTypedNode` refuses it; `check` reports the same
|
|
205
|
+
* condition from this function, because BPMN `check` does not serialize.
|
|
206
|
+
*/
|
|
207
|
+
export function connectorInputsShadowed(id, type, inputs, rows) {
|
|
208
|
+
if (!CONNECTOR_ACTIVITY_TYPES.has(type) || !rows)
|
|
209
|
+
return undefined;
|
|
210
|
+
const keys = Object.keys(inputs ?? {});
|
|
211
|
+
if (keys.length === 0)
|
|
212
|
+
return undefined;
|
|
213
|
+
return (`Connector activity "${id}" sets both \`inputs\` (${keys.map((k) => `"${k}"`).join(', ')}) and \`inputRows\`; ` +
|
|
214
|
+
`an \`inputRows\` list replaces \`inputs\` entirely at emission, so those fields are never sent. Spell them as rows too ` +
|
|
215
|
+
`(a body field as \`{ name: 'body', type: 'json', target: 'body', body: { … } }\`), or drop \`inputRows\` and write every field in \`inputs\`.`);
|
|
216
|
+
}
|
|
196
217
|
/**
|
|
197
218
|
* An output name reduced to something an expression can actually read.
|
|
198
219
|
*
|
|
@@ -548,6 +569,9 @@ varType) {
|
|
|
548
569
|
// Spelled out by the caller: emit exactly these rows, deriving nothing — see
|
|
549
570
|
// TypedNodeInput.inputRows. This is what lets a connector's per-parameter rows
|
|
550
571
|
// (`name="start" type="string" value="0" target="query"`) survive a round trip.
|
|
572
|
+
const shadowed = connectorInputsShadowed(id, type, input.inputs, input.inputRows);
|
|
573
|
+
if (shadowed)
|
|
574
|
+
throw new CompileRefusal('CONNECTOR_INPUTS_SHADOWED', shadowed);
|
|
551
575
|
const ins = input.inputRows ? verbatimInputRows(m, input.inputRows) : inputRows(m, spec, input.inputs ?? {});
|
|
552
576
|
if (ins.length)
|
|
553
577
|
payload.input = ins;
|
package/dist/case/check.js
CHANGED
|
@@ -8,18 +8,8 @@ import { EQUALS_EXPR_DIALECT, checkBindingLabels, checkConnectorContract, checkC
|
|
|
8
8
|
import { checkEventResolution } from '../core/event-checks.js';
|
|
9
9
|
import { EXTERNAL_TASK_DESCRIPTOR_CONTRACT, EXTERNAL_TASK_HYBRID_UNSUPPORTED, externalHybridRefusal, isExternalExecutionMode } from './external.js';
|
|
10
10
|
import { isResourceLabelRefusal, resolveResourceRef } from './resource-identity.js';
|
|
11
|
+
import { taskShapeProblems } from './task-shape.js';
|
|
11
12
|
const TYPE_NAMES = new Set(Object.values(types));
|
|
12
|
-
/**
|
|
13
|
-
* Every key an `ActionField` takes. `compile` writes only these onto the form
|
|
14
|
-
* row, so any other key would be dropped without a word (builder-sdk#1422): an
|
|
15
|
-
* author who adds the Designer's row keys (`id`, `var`, `source`) sees `check`
|
|
16
|
-
* and `compile` pass and the row come out unchanged. The `satisfies` keeps this
|
|
17
|
-
* list and the interface in step: a key added to one and not the other fails to
|
|
18
|
-
* type-check.
|
|
19
|
-
*/
|
|
20
|
-
const ACTION_FIELD_KEYS = new Set(Object.keys({
|
|
21
|
-
name: true, type: true, subType: true, displayName: true, value: true, required: true, options: true,
|
|
22
|
-
}));
|
|
23
13
|
// audit-case-coverage: rule-slot table start
|
|
24
14
|
// This runtime table must equal the committed matrix's rejected cells. The audit
|
|
25
15
|
// parses these object literals; the product-pin matrix currently rejects none.
|
|
@@ -509,17 +499,10 @@ function checkStage(stage, diags, refStage, refPlacement, bindings) {
|
|
|
509
499
|
where: stage.label,
|
|
510
500
|
});
|
|
511
501
|
for (const t of tasks) {
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
diags.push({
|
|
517
|
-
level: 'error',
|
|
518
|
-
code: 'TIMER_VALUE_REQUIRED',
|
|
519
|
-
message: `Timer task "${t.name}" needs exactly one non-empty duration, date, or cycle. For a validating stand-in, use .waitForTimer({ duration: 'PT1M' }).`,
|
|
520
|
-
where: t.name,
|
|
521
|
-
});
|
|
522
|
-
}
|
|
502
|
+
// A timer with no or several values, an action field key compile does not
|
|
503
|
+
// write: serialize refuses the same shapes with the same text (task-shape.ts).
|
|
504
|
+
for (const problem of taskShapeProblems(t))
|
|
505
|
+
diags.push({ level: 'error', code: problem.code, message: problem.message, where: t.name });
|
|
523
506
|
const invalidBinding = [...(t.inputs ?? []), ...(t.outputs ?? [])]
|
|
524
507
|
.find((binding) => !TYPE_NAMES.has(binding.type));
|
|
525
508
|
if (invalidBinding)
|
|
@@ -681,24 +664,6 @@ function checkStage(stage, diags, refStage, refPlacement, bindings) {
|
|
|
681
664
|
message: `Action field "${emptyRequiredField.name}" in task "${t.name}" is required but has no value. Set its value or remove required: true; required does not mean reviewer-must-fill.`,
|
|
682
665
|
where: t.name,
|
|
683
666
|
});
|
|
684
|
-
for (const [side, fields] of [['input', t.action?.inputs], ['output', t.action?.outputs]])
|
|
685
|
-
for (const field of fields ?? []) {
|
|
686
|
-
const unknown = Object.keys(field)
|
|
687
|
-
.filter((key) => !ACTION_FIELD_KEYS.has(key) && field[key] !== undefined);
|
|
688
|
-
if (!unknown.length)
|
|
689
|
-
continue;
|
|
690
|
-
diags.push({
|
|
691
|
-
level: 'error',
|
|
692
|
-
code: 'ACTION_FIELD_UNKNOWN_KEY',
|
|
693
|
-
message: `Action ${side} field "${String(field.name)}" in task "${t.name}" has ${unknown.length === 1 ? 'a key' : 'keys'} ` +
|
|
694
|
-
`${unknown.map((key) => `"${key}"`).join(', ')} that an action field does not take, so compile would drop ${unknown.length === 1 ? 'it' : 'them'}. ` +
|
|
695
|
-
`An action field takes ${[...ACTION_FIELD_KEYS].join(', ')}. ` +
|
|
696
|
-
(side === 'output'
|
|
697
|
-
? `To choose the case variable an output writes, set value: '<variable>'; compile writes the row's id, var, source and target from it.`
|
|
698
|
-
: `An input's value is the literal or =vars.<name> expression the assignee sees.`),
|
|
699
|
-
where: t.name,
|
|
700
|
-
});
|
|
701
|
-
}
|
|
702
667
|
if (t.entry.length === 0)
|
|
703
668
|
diags.push({
|
|
704
669
|
level: 'error',
|
package/dist/case/serialize.js
CHANGED
|
@@ -31,6 +31,7 @@ import { readEventFilterLeaf, eventFilterTreeLeaf } from '../core/event-filters.
|
|
|
31
31
|
import { responseJsonSchema } from '../core/connector-response-schema.js';
|
|
32
32
|
import { ruleShapeProblems } from './rule-options.js';
|
|
33
33
|
import { CompileRefusal } from '../core/compile-refusal.js';
|
|
34
|
+
import { taskShapeProblems } from './task-shape.js';
|
|
34
35
|
/** The ids a serialize pass hashed from element ids, by the document it returned. */
|
|
35
36
|
const ELEMENT_SEEDED_IDS = new WeakMap();
|
|
36
37
|
function elementSeededId(resolver, elementId, suffix) {
|
|
@@ -1148,6 +1149,11 @@ function emitTask(task, stageLabel, taskId, ids, resolver) {
|
|
|
1148
1149
|
return node;
|
|
1149
1150
|
}
|
|
1150
1151
|
function taskData(task, resolver, taskId, stageId, ids) {
|
|
1152
|
+
// A timer with no or several values, or an action field key the row has no
|
|
1153
|
+
// place for, cannot be written as authored: refuse it, as `check` does (#1518).
|
|
1154
|
+
const [shape] = taskShapeProblems(task);
|
|
1155
|
+
if (shape)
|
|
1156
|
+
throw new CompileRefusal(shape.code, shape.message);
|
|
1151
1157
|
switch (task.kind) {
|
|
1152
1158
|
case 'process':
|
|
1153
1159
|
case 'agent':
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The task shapes Case `check` reports and `serialize` refuses, because the
|
|
3
|
+
* caseplan cannot say what the source says (#1518):
|
|
4
|
+
*
|
|
5
|
+
* - `TIMER_VALUE_REQUIRED`: a wait-for-timer task sets exactly one of
|
|
6
|
+
* duration, date or cycle, and that one is non-blank. With two, serialize writes the first and drops
|
|
7
|
+
* the rest (`{ duration: 'PT1M', date: '2027-…' }` came out as
|
|
8
|
+
* `timeDuration: 'PT1M'` alone); with none, it writes `data: {}`, which
|
|
9
|
+
* product validation refuses.
|
|
10
|
+
* - `ACTION_FIELD_UNKNOWN_KEY`: an action form field takes only the
|
|
11
|
+
* `ActionField` keys; serialize writes only those, so any other key
|
|
12
|
+
* (`default: 5`, the Designer's `id`/`var`/`source`) was dropped without a
|
|
13
|
+
* word (#1422).
|
|
14
|
+
*
|
|
15
|
+
* Case `check` does not run the serializer, so both call these functions: one
|
|
16
|
+
* rule, one place, the same code and message from each.
|
|
17
|
+
*/
|
|
18
|
+
import type { BuiltTask } from './case-sdk.js';
|
|
19
|
+
/** One problem with a task's shape. */
|
|
20
|
+
export interface TaskShapeProblem {
|
|
21
|
+
code: 'TIMER_VALUE_REQUIRED' | 'ACTION_FIELD_UNKNOWN_KEY';
|
|
22
|
+
message: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Every problem with `task`'s shape, in a fixed order: the timer first, then
|
|
26
|
+
* each action input field, then each output field.
|
|
27
|
+
*
|
|
28
|
+
* @param task - The task as the builder recorded it.
|
|
29
|
+
* @returns The problems, empty when the task is well formed.
|
|
30
|
+
*/
|
|
31
|
+
export declare function taskShapeProblems(task: BuiltTask): TaskShapeProblem[];
|