@uipath/maestro-builder-sdk 6.36.0 → 6.37.0
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/api-index.md +28 -27
- package/dist/api-members.md +94 -93
- package/dist/bpmn/bpmn-sdk.d.ts +38 -9
- package/dist/bpmn/bpmn-sdk.js +104 -28
- package/dist/bpmn/check.js +64 -0
- package/dist/bpmn/connector.js +3 -0
- package/dist/bpmn/declared-vars.js +10 -3
- package/dist/bpmn/decompile.js +38 -10
- package/dist/bpmn/intsvc.d.ts +9 -10
- package/dist/bpmn/intsvc.js +31 -18
- package/dist/bpmn/registry-types.json +30 -24
- package/dist/bpmn/resource-key-pattern.d.ts +38 -0
- package/dist/bpmn/resource-key-pattern.js +47 -0
- package/dist/bpmn/serialize.js +22 -24
- package/dist/bpmn/typed-node.d.ts +17 -0
- package/dist/bpmn/typed-node.js +19 -0
- package/dist/cli-library.js +3 -1
- package/dist/core/cli-library.d.ts +8 -2
- package/dist/core/cli-library.js +6 -4
- package/dist/core/connector-resolve-error.d.ts +7 -1
- package/dist/core/event-checks.js +2 -2
- package/dist/core/lookups.d.ts +22 -0
- package/dist/core/lookups.js +34 -1
- package/dist/core/synthesized-definition.d.ts +15 -0
- package/dist/core/synthesized-definition.js +22 -0
- package/dist/registry/library-build/context.d.ts +16 -0
- package/dist/registry/library-build/context.js +32 -5
- package/dist/registry/prepare/actions.d.ts +15 -0
- package/dist/registry/prepare/actions.js +12 -1
- package/dist/registry/prepare/integration-service.d.ts +105 -0
- package/dist/registry/prepare/integration-service.js +218 -0
- package/dist/registry/prepare/messages.d.ts +41 -1
- package/dist/registry/prepare/messages.js +82 -1
- package/dist/registry/prepare/pipeline.js +73 -15
- package/dist/registry/uip/commands.d.ts +8 -0
- package/dist/registry/uip/commands.js +12 -0
- package/dist/serialize.js +11 -0
- package/package.json +2 -2
|
@@ -7,7 +7,10 @@
|
|
|
7
7
|
],
|
|
8
8
|
"provenance": {
|
|
9
9
|
"typeCount": 30,
|
|
10
|
-
"uipVersion": "1.203.0-preview.180"
|
|
10
|
+
"uipVersion": "1.203.0-preview.180",
|
|
11
|
+
"refreshed": {
|
|
12
|
+
"Orchestrator.BusinessRules": "1.205.0-preview.256"
|
|
13
|
+
}
|
|
11
14
|
},
|
|
12
15
|
"types": {
|
|
13
16
|
"A2A.AgentExecution": {
|
|
@@ -1277,40 +1280,35 @@
|
|
|
1277
1280
|
"inputTarget": "bodyField",
|
|
1278
1281
|
"context": [
|
|
1279
1282
|
{
|
|
1280
|
-
"name": "
|
|
1281
|
-
"displayName": "
|
|
1283
|
+
"name": "name",
|
|
1284
|
+
"displayName": "Business rule",
|
|
1282
1285
|
"type": "string",
|
|
1283
|
-
"required":
|
|
1286
|
+
"required": true,
|
|
1284
1287
|
"hidden": true,
|
|
1285
|
-
"binding":
|
|
1288
|
+
"binding": true,
|
|
1286
1289
|
"default": null,
|
|
1287
1290
|
"bindingInfo": {
|
|
1288
|
-
"resource": "
|
|
1289
|
-
"propertyAttribute": "
|
|
1291
|
+
"resource": "BusinessRule",
|
|
1292
|
+
"propertyAttribute": "name",
|
|
1293
|
+
"resourceKeyPattern": "${metadata.folderPath}.${metadata.name}"
|
|
1290
1294
|
}
|
|
1291
1295
|
},
|
|
1292
|
-
{
|
|
1293
|
-
"name": "folderId",
|
|
1294
|
-
"displayName": null,
|
|
1295
|
-
"type": "string",
|
|
1296
|
-
"required": false,
|
|
1297
|
-
"hidden": true,
|
|
1298
|
-
"binding": false,
|
|
1299
|
-
"default": null,
|
|
1300
|
-
"bindingInfo": null
|
|
1301
|
-
},
|
|
1302
1296
|
{
|
|
1303
1297
|
"name": "folderPath",
|
|
1304
1298
|
"displayName": null,
|
|
1305
1299
|
"type": "string",
|
|
1306
|
-
"required":
|
|
1300
|
+
"required": true,
|
|
1307
1301
|
"hidden": true,
|
|
1308
|
-
"binding":
|
|
1302
|
+
"binding": true,
|
|
1309
1303
|
"default": null,
|
|
1310
|
-
"bindingInfo":
|
|
1304
|
+
"bindingInfo": {
|
|
1305
|
+
"resource": "BusinessRule",
|
|
1306
|
+
"propertyAttribute": "folderPath",
|
|
1307
|
+
"resourceKeyPattern": "${metadata.folderPath}.${metadata.name}"
|
|
1308
|
+
}
|
|
1311
1309
|
},
|
|
1312
1310
|
{
|
|
1313
|
-
"name": "
|
|
1311
|
+
"name": "_label",
|
|
1314
1312
|
"displayName": null,
|
|
1315
1313
|
"type": "string",
|
|
1316
1314
|
"required": false,
|
|
@@ -1322,11 +1320,19 @@
|
|
|
1322
1320
|
],
|
|
1323
1321
|
"inputs": [],
|
|
1324
1322
|
"output": {
|
|
1325
|
-
"name": "
|
|
1326
|
-
"type": "
|
|
1327
|
-
"source":
|
|
1323
|
+
"name": "output",
|
|
1324
|
+
"type": "jsonSchema",
|
|
1325
|
+
"source": "=result",
|
|
1328
1326
|
"relative": true
|
|
1329
1327
|
},
|
|
1328
|
+
"additionalOutputs": [
|
|
1329
|
+
{
|
|
1330
|
+
"name": "Error",
|
|
1331
|
+
"type": "jsonSchema",
|
|
1332
|
+
"source": "=Error",
|
|
1333
|
+
"relative": true
|
|
1334
|
+
}
|
|
1335
|
+
],
|
|
1330
1336
|
"eventDefinition": null,
|
|
1331
1337
|
"requiresDiscovery": true,
|
|
1332
1338
|
"bindingPattern": "businessRule"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A binding field's resource key from the registry's `resourceKeyPattern`
|
|
3
|
+
* (`${metadata.folderPath}.${metadata.name}` on a business rule), resolved from the
|
|
4
|
+
* node's context values. Pure: the builder supplies the declared bindings a
|
|
5
|
+
* `=bindings.<b>` reference is read through.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* What a pattern resolves to from one node's context: the key and the ids of the
|
|
9
|
+
* declared bindings it was read through; `blocked` when it would resolve except for a
|
|
10
|
+
* `=bindings.<b>` reference no declared binding supplies a value for (the author is
|
|
11
|
+
* told what to declare rather than given a wrong pair); `undefined` when the pattern
|
|
12
|
+
* does not apply to this context at all, so the caller keys the binding by the value,
|
|
13
|
+
* as it always has.
|
|
14
|
+
*/
|
|
15
|
+
export type ResourceKeyResolution = {
|
|
16
|
+
key: string;
|
|
17
|
+
referenced: string[];
|
|
18
|
+
} | {
|
|
19
|
+
blocked: {
|
|
20
|
+
field: string;
|
|
21
|
+
ref: string;
|
|
22
|
+
};
|
|
23
|
+
} | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Resolve `pattern` from `context`. A placeholder that is not `metadata.<field>`, a
|
|
26
|
+
* context field that is absent or not a string, or an `=` expression that is not a
|
|
27
|
+
* `=bindings.<b>` reference each make the pattern inapplicable (`undefined`) — so
|
|
28
|
+
* `${metadata.key}` on a job start, whose context has no `key`, leaves the key to the
|
|
29
|
+
* value. A `=bindings.<b>` value contributes the DECLARED value of `b`, never the
|
|
30
|
+
* expression text: a key is never evaluated.
|
|
31
|
+
*
|
|
32
|
+
* @param pattern - The field's `resourceKeyPattern`, or `undefined` for none.
|
|
33
|
+
* @param context - The node's context values as the author gave them.
|
|
34
|
+
* @param lookup - The declared binding for an id, with its `default` value, or `undefined`.
|
|
35
|
+
*/
|
|
36
|
+
export declare function resolveResourceKeyPattern(pattern: string | undefined, context: Record<string, unknown>, lookup: (bindingId: string) => {
|
|
37
|
+
default?: unknown;
|
|
38
|
+
} | undefined): ResourceKeyResolution;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A binding field's resource key from the registry's `resourceKeyPattern`
|
|
3
|
+
* (`${metadata.folderPath}.${metadata.name}` on a business rule), resolved from the
|
|
4
|
+
* node's context values. Pure: the builder supplies the declared bindings a
|
|
5
|
+
* `=bindings.<b>` reference is read through.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Resolve `pattern` from `context`. A placeholder that is not `metadata.<field>`, a
|
|
9
|
+
* context field that is absent or not a string, or an `=` expression that is not a
|
|
10
|
+
* `=bindings.<b>` reference each make the pattern inapplicable (`undefined`) — so
|
|
11
|
+
* `${metadata.key}` on a job start, whose context has no `key`, leaves the key to the
|
|
12
|
+
* value. A `=bindings.<b>` value contributes the DECLARED value of `b`, never the
|
|
13
|
+
* expression text: a key is never evaluated.
|
|
14
|
+
*
|
|
15
|
+
* @param pattern - The field's `resourceKeyPattern`, or `undefined` for none.
|
|
16
|
+
* @param context - The node's context values as the author gave them.
|
|
17
|
+
* @param lookup - The declared binding for an id, with its `default` value, or `undefined`.
|
|
18
|
+
*/
|
|
19
|
+
export function resolveResourceKeyPattern(pattern, context, lookup) {
|
|
20
|
+
if (pattern === undefined)
|
|
21
|
+
return undefined;
|
|
22
|
+
const placeholders = [...pattern.matchAll(/\$\{([\w.]+)\}/g)].map((m) => m[1]);
|
|
23
|
+
const values = new Map();
|
|
24
|
+
const referenced = [];
|
|
25
|
+
for (const name of placeholders) {
|
|
26
|
+
if (!name.startsWith('metadata.'))
|
|
27
|
+
return undefined;
|
|
28
|
+
const contextField = name.slice('metadata.'.length);
|
|
29
|
+
const v = context[contextField];
|
|
30
|
+
if (typeof v !== 'string')
|
|
31
|
+
return undefined;
|
|
32
|
+
if (!v.startsWith('=')) {
|
|
33
|
+
values.set(name, v);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (!v.startsWith('=bindings.'))
|
|
37
|
+
return undefined;
|
|
38
|
+
const refId = v.slice('=bindings.'.length);
|
|
39
|
+
const decl = lookup(refId);
|
|
40
|
+
if (decl === undefined || typeof decl.default !== 'string')
|
|
41
|
+
return { blocked: { field: contextField, ref: v } };
|
|
42
|
+
values.set(name, decl.default);
|
|
43
|
+
referenced.push(refId);
|
|
44
|
+
}
|
|
45
|
+
const key = pattern.replace(/\$\{([\w.]+)\}/g, (_m, name) => values.get(name) ?? '');
|
|
46
|
+
return { key: key.startsWith('.') ? key.slice(1) : key, referenced };
|
|
47
|
+
}
|
package/dist/bpmn/serialize.js
CHANGED
|
@@ -154,14 +154,7 @@ export async function serialize(built, opts = {}) {
|
|
|
154
154
|
version: meta.caseManagement.version,
|
|
155
155
|
value: meta.caseManagement.value,
|
|
156
156
|
}));
|
|
157
|
-
//
|
|
158
|
-
// Derived from the GRAPH, not from whether connector wiring happened to produce
|
|
159
|
-
// a binding: a connector with no connection/folder produced no binding and so
|
|
160
|
-
// used to lose the marker, and a DECOMPILED connector — which carries explicit
|
|
161
|
-
// `.binding()` calls rather than derived ones — lost it too.
|
|
162
|
-
if (allNodes(built.nodes).some(isIntsvcConnectorNode)) {
|
|
163
|
-
processExt.push(m.create('uipath:IntsvcActivityConfig', { version: 'v2' }));
|
|
164
|
-
}
|
|
157
|
+
// No process `intsvcActivityConfig`: the CLI packs Connection rows only for V1 nodes (https://github.com/UiPath/builder-sdk/issues/1298 tracks V2).
|
|
165
158
|
if (processExt.length)
|
|
166
159
|
processProps.extensionElements = m.create('bpmn:ExtensionElements', { values: processExt });
|
|
167
160
|
processProps.flowElements = flowElements;
|
|
@@ -711,13 +704,27 @@ export function implicitNodeVars(nodes, declared) {
|
|
|
711
704
|
claim(typedNodeOutputVar(n.id, n.trigger.type, n.trigger.outputVar), n.id);
|
|
712
705
|
}
|
|
713
706
|
else if (n.kind === 'scriptTask') {
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
707
|
+
if (n.outputRows) {
|
|
708
|
+
// Spelled-out rows state their declaration, as a typed node's do — the same
|
|
709
|
+
// `rowDeclaration` call, so `nodeDeclaredVars`, this, and decompile's skip set
|
|
710
|
+
// agree. Deriving from `outputs` alone here left a script task authored with
|
|
711
|
+
// `outputRows` declaring nothing on the wire: the checker (once it counted the
|
|
712
|
+
// rows) passed, and `uip maestro bpmn validate` reported MISSING_ROOT_VARIABLE
|
|
713
|
+
// on the task and VARIABLE_DOES_NOT_EXIST on every read of its variables.
|
|
714
|
+
for (const row of n.outputRows) {
|
|
715
|
+
const d = rowDeclaration(row);
|
|
716
|
+
claim(row.var, n.id, d.type, d);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
else {
|
|
720
|
+
// `object` (emitted as `json`), not `string`. Under the v3 contract a script
|
|
721
|
+
// returns `{ response: <whatever it returned> }`, so an output nobody declared
|
|
722
|
+
// holds arbitrary JSON — declaring a number or an object as `string` is the
|
|
723
|
+
// mis-declaration this fallback exists to avoid. An output the AUTHOR declared
|
|
724
|
+
// keeps its own type: `claim` skips an id already taken.
|
|
725
|
+
for (const varId of Object.keys(n.outputs))
|
|
726
|
+
claim(varId, n.id);
|
|
727
|
+
}
|
|
721
728
|
}
|
|
722
729
|
}
|
|
723
730
|
return out;
|
|
@@ -950,15 +957,6 @@ export function publicDeclarationScope(nodes, processId) {
|
|
|
950
957
|
function cap(s) {
|
|
951
958
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
952
959
|
}
|
|
953
|
-
/**
|
|
954
|
-
* Whether a node is an Integration Service connector activity — the `.connector()`
|
|
955
|
-
* method, or the same extension types reached through `.activity()`.
|
|
956
|
-
*/
|
|
957
|
-
function isIntsvcConnectorNode(n) {
|
|
958
|
-
if (n.kind === 'connector')
|
|
959
|
-
return true;
|
|
960
|
-
return n.kind === 'typed' && (n.type === 'Intsvc.ActivityExecution' || n.type === 'Intsvc.AsyncExecution');
|
|
961
|
-
}
|
|
962
960
|
/** Flatten a scope's nodes (recursing sub-processes) for global scans. */
|
|
963
961
|
export function allNodes(nodes) {
|
|
964
962
|
const out = [];
|
|
@@ -36,6 +36,13 @@ export interface RegistryContextField {
|
|
|
36
36
|
bindingInfo: {
|
|
37
37
|
resource: string | null;
|
|
38
38
|
propertyAttribute: string | null;
|
|
39
|
+
/**
|
|
40
|
+
* How the binding's `resourceKey` is built from the node's context values, e.g.
|
|
41
|
+
* `${metadata.folderPath}.${metadata.name}` for a business rule. Absent on
|
|
42
|
+
* entries materialized before the field was carried; the emitter then keys the
|
|
43
|
+
* binding by the bound value itself, as it always did.
|
|
44
|
+
*/
|
|
45
|
+
resourceKeyPattern?: string;
|
|
39
46
|
} | null;
|
|
40
47
|
}
|
|
41
48
|
/**
|
|
@@ -88,6 +95,11 @@ export interface RegistryTypeSpec {
|
|
|
88
95
|
/** The payload fields the registry declares; empty for types with dynamic inputs. */
|
|
89
96
|
inputs: RegistryInputField[];
|
|
90
97
|
output: RegistryOutput | null;
|
|
98
|
+
/**
|
|
99
|
+
* Output rows the type writes besides `output` — a business rule's `Error` row
|
|
100
|
+
* (`source="=Error"`). Each lands in `<id>_<name>`. Absent when the type has none.
|
|
101
|
+
*/
|
|
102
|
+
additionalOutputs?: RegistryOutput[];
|
|
91
103
|
/** e.g. `bpmn:messageEventDefinition` — what makes a message event a message event. */
|
|
92
104
|
eventDefinition: string | null;
|
|
93
105
|
/** True when authoring needs a tenant read (a connection, a process, a queue). */
|
|
@@ -347,6 +359,11 @@ export interface TypedNodeInput {
|
|
|
347
359
|
/** The type's emission spec, supplied inline — see `ActivityNodeOpts.spec`. */
|
|
348
360
|
spec?: RegistryTypeSpec;
|
|
349
361
|
}
|
|
362
|
+
/**
|
|
363
|
+
* The variables a typed node's ADDITIONAL output rows write (`<id>_<name>`), in
|
|
364
|
+
* registry order — a business rule's `<id>_Error`. Empty for every type without them.
|
|
365
|
+
*/
|
|
366
|
+
export declare function typedNodeAdditionalOutputVars(id: string, type: string, inline?: RegistryTypeSpec): string[];
|
|
350
367
|
/** The variable a typed node's output row writes, or `undefined` when it has none. */
|
|
351
368
|
export declare function typedNodeOutputVar(id: string, type: string, outputVar?: string, inline?: RegistryTypeSpec): string | undefined;
|
|
352
369
|
/** `uipath:input` payload rows exactly as the caller spelled them, deriving nothing. */
|
package/dist/bpmn/typed-node.js
CHANGED
|
@@ -191,6 +191,15 @@ function identifierize(name) {
|
|
|
191
191
|
.map((w, i) => (i === 0 ? w.charAt(0).toLowerCase() + w.slice(1) : w.charAt(0).toUpperCase() + w.slice(1)))
|
|
192
192
|
.join('');
|
|
193
193
|
}
|
|
194
|
+
/**
|
|
195
|
+
* The variables a typed node's ADDITIONAL output rows write (`<id>_<name>`), in
|
|
196
|
+
* registry order — a business rule's `<id>_Error`. Empty for every type without them.
|
|
197
|
+
*/
|
|
198
|
+
export function typedNodeAdditionalOutputVars(id, type, inline) {
|
|
199
|
+
// The row's name as it is, not identifierized: `Error` → `<id>_Error`, the variable a
|
|
200
|
+
// connector task's error row has always landed in.
|
|
201
|
+
return (registryType(type, inline).additionalOutputs ?? []).map((o) => `${id}_${o.name}`);
|
|
202
|
+
}
|
|
194
203
|
/** The variable a typed node's output row writes, or `undefined` when it has none. */
|
|
195
204
|
export function typedNodeOutputVar(id, type, outputVar, inline) {
|
|
196
205
|
const spec = registryType(type, inline);
|
|
@@ -536,6 +545,16 @@ varType) {
|
|
|
536
545
|
const out = outVar ? outputRow(m, spec, outVar) : undefined;
|
|
537
546
|
if (out)
|
|
538
547
|
rows.push(out);
|
|
548
|
+
// The rows the registry declares beside the main one, in its order and at the
|
|
549
|
+
// default variable each — a business rule's `Error` row. They sit between the
|
|
550
|
+
// type's own row and the author's, which is where every real export puts them.
|
|
551
|
+
const extraVars = typedNodeAdditionalOutputVars(id, type, spec);
|
|
552
|
+
(spec.additionalOutputs ?? []).forEach((o, i) => {
|
|
553
|
+
const props = { name: o.name, type: o.type, var: extraVars[i] };
|
|
554
|
+
if (o.source)
|
|
555
|
+
props.source = o.source;
|
|
556
|
+
rows.push(m.create('uipath:Output', props));
|
|
557
|
+
});
|
|
539
558
|
for (const [varId, source] of Object.entries(input.outputs ?? {})) {
|
|
540
559
|
// Type the row from the variable it writes, not `'string'` unconditionally.
|
|
541
560
|
//
|
package/dist/cli-library.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { connectorCheckOpts } from './core/cli-library.js';
|
|
2
|
+
import { synthesizedDefinitionRefusal } from './core/synthesized-definition.js';
|
|
2
3
|
import { connectorInputErrors } from './serialize.js';
|
|
3
4
|
export { openCliLibrary, connectorCheckOpts, eventResolveErrorFor, byoaCheckOpts } from './core/cli-library.js';
|
|
4
5
|
/**
|
|
@@ -10,7 +11,8 @@ export { openCliLibrary, connectorCheckOpts, eventResolveErrorFor, byoaCheckOpts
|
|
|
10
11
|
* cannot disagree.
|
|
11
12
|
*/
|
|
12
13
|
export function flowCheckOpts(library, bindings) {
|
|
13
|
-
|
|
14
|
+
// Flow's own refusal of an operation `prepare` synthesized from Integration Service (BPMN-only).
|
|
15
|
+
const shared = connectorCheckOpts(library, bindings, connectorInputErrors, synthesizedDefinitionRefusal);
|
|
14
16
|
if (!library)
|
|
15
17
|
return shared;
|
|
16
18
|
return {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Library, type ResolvedConnector } from './library.js';
|
|
1
|
+
import { ConnectorResolveError, Library, type ResolvedConnector } from './library.js';
|
|
2
2
|
import type { BindingsView } from './binding-messages.js';
|
|
3
3
|
import { type ConnectorCheckOpts } from './connector-checks.js';
|
|
4
4
|
import type { LookupResolutions, LookupSpec } from './lookups.js';
|
|
@@ -32,9 +32,15 @@ export declare function openCliLibrary(argv: readonly string[]): Library | undef
|
|
|
32
32
|
* recorded by `registry prepare --resolve` would be reported as still owed,
|
|
33
33
|
* telling the author to re-run a prepare they already ran.
|
|
34
34
|
*/
|
|
35
|
-
export declare function connectorCheckOpts(library: Library | undefined, bindings?: BindingsView, inputErrors?: ConnectorInputValidator): ConnectorCheckOpts & EventCheckOpts;
|
|
35
|
+
export declare function connectorCheckOpts(library: Library | undefined, bindings?: BindingsView, inputErrors?: ConnectorInputValidator, refuse?: ConnectorRefusal): ConnectorCheckOpts & EventCheckOpts;
|
|
36
36
|
/** A family's field-contract validator: `compile`'s refusal for these inputs, or `undefined`. */
|
|
37
37
|
export type ConnectorInputValidator = (rc: ResolvedConnector, inputs: Record<string, unknown>, resolutions: LookupResolutions, lookups: Readonly<Record<string, LookupSpec>>) => string | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* A family's own refusal of an operation the library DID resolve — Flow's
|
|
40
|
+
* `synthesizedDefinitionRefusal` — applied after `library.resolve`, so check
|
|
41
|
+
* and compile refuse the same entry with the same code and text.
|
|
42
|
+
*/
|
|
43
|
+
export type ConnectorRefusal = (rc: ResolvedConnector) => ConnectorResolveError | undefined;
|
|
38
44
|
/**
|
|
39
45
|
* `check`'s EVENT_UNKNOWN verdict: the message `library.resolveEvent` throws
|
|
40
46
|
* for a subscription, or `undefined` when it resolves.
|
package/dist/core/cli-library.js
CHANGED
|
@@ -57,12 +57,12 @@ export function openCliLibrary(argv) {
|
|
|
57
57
|
* recorded by `registry prepare --resolve` would be reported as still owed,
|
|
58
58
|
* telling the author to re-run a prepare they already ran.
|
|
59
59
|
*/
|
|
60
|
-
export function connectorCheckOpts(library, bindings, inputErrors = (rc, inputs, resolutions, lookups) => connectorInputErrors(rc, inputs, resolutions, lookups, { dialect: EQUALS_EXPR_DIALECT })) {
|
|
60
|
+
export function connectorCheckOpts(library, bindings, inputErrors = (rc, inputs, resolutions, lookups) => connectorInputErrors(rc, inputs, resolutions, lookups, { dialect: EQUALS_EXPR_DIALECT }), refuse) {
|
|
61
61
|
if (!library)
|
|
62
62
|
return bindings ? { bindings } : {};
|
|
63
63
|
// One memoized resolve per connector step, shared by `connectorResolveError`
|
|
64
64
|
// and `connectorInputs`, so the second hook does not resolve again.
|
|
65
|
-
const resolve = connectorResolver(library);
|
|
65
|
+
const resolve = connectorResolver(library, refuse);
|
|
66
66
|
return {
|
|
67
67
|
eventResolveError: eventResolveErrorFor(library),
|
|
68
68
|
...byoaCheckOpts(library),
|
|
@@ -129,14 +129,16 @@ const CONNECTOR_RESOLVE_FAILED = 'CONNECTOR_RESOLVE_FAILED';
|
|
|
129
129
|
* "unknown" used to let `check` pass a step `compile` then died on (#1015); the
|
|
130
130
|
* event twin, `eventResolveErrorFor`, already reported every throw.
|
|
131
131
|
*/
|
|
132
|
-
function connectorResolver(library) {
|
|
132
|
+
function connectorResolver(library, refuse) {
|
|
133
133
|
const memo = new Map();
|
|
134
134
|
return (spec) => {
|
|
135
135
|
const id = JSON.stringify([spec.key, spec.action, spec.version ?? null, spec.object ?? null]);
|
|
136
136
|
let r = memo.get(id);
|
|
137
137
|
if (!r) {
|
|
138
138
|
try {
|
|
139
|
-
|
|
139
|
+
const rc = library.resolve(spec.key, spec.action, spec.version, spec.object);
|
|
140
|
+
const refused = refuse?.(rc);
|
|
141
|
+
r = refused ? { error: { code: refused.code, message: refused.message } } : { rc };
|
|
140
142
|
}
|
|
141
143
|
catch (error) {
|
|
142
144
|
r = error instanceof ConnectorResolveError
|
|
@@ -15,7 +15,13 @@ export type ConnectorResolveCode =
|
|
|
15
15
|
* The index lists the operation, but its entry file or `.v1def.json` sidecar is
|
|
16
16
|
* missing or not valid JSON: the library on disk is incomplete (#1015).
|
|
17
17
|
*/
|
|
18
|
-
| 'CONNECTOR_LIBRARY_ENTRY_UNREADABLE'
|
|
18
|
+
| 'CONNECTOR_LIBRARY_ENTRY_UNREADABLE'
|
|
19
|
+
/**
|
|
20
|
+
* Flow only: the sidecar was synthesized from Integration Service by `registry
|
|
21
|
+
* prepare` (no registry node exists), and a Flow node needs the registry's
|
|
22
|
+
* definition. BPMN compiles the same entry (`core/synthesized-definition.ts`).
|
|
23
|
+
*/
|
|
24
|
+
| 'CONNECTOR_NO_FLOW_DEFINITION';
|
|
19
25
|
/**
|
|
20
26
|
* The refusal `Library.resolve` throws, with a `code` for each reason.
|
|
21
27
|
*
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* `skill-bpmn-outlook-waitfor-email` in `flow-v2-run-8` failed on exactly that last
|
|
19
19
|
* question, and in run 12 on having nowhere to put a payload condition.
|
|
20
20
|
*/
|
|
21
|
-
import { isLookupToken,
|
|
21
|
+
import { isLookupToken, resolvedEventValue, unresolvedLookupMessage, } from './lookups.js';
|
|
22
22
|
import { prepareCommand } from './cli-spelling.js';
|
|
23
23
|
import { EVENT_FILTER_OPERATORS, readEventFilter, eventFilterProblem, jmesStringProblem } from './event-filters.js';
|
|
24
24
|
import { EVENT_BYOA_UNVERIFIED, byoaUnverified } from './byoa.js';
|
|
@@ -129,7 +129,7 @@ export function checkEventResolution(subscription, where, kind, opts, error, war
|
|
|
129
129
|
for (const [key, value] of scope) {
|
|
130
130
|
if (!isLookupToken(value))
|
|
131
131
|
continue;
|
|
132
|
-
if (
|
|
132
|
+
if (resolvedEventValue(value, resolutions) !== undefined)
|
|
133
133
|
continue;
|
|
134
134
|
warn('LOOKUP_UNRESOLVED', `${where}: \`where.${key}\` ` + unresolvedLookupMessage(value)
|
|
135
135
|
+ `\nCompile will fail until this is recorded.`);
|
package/dist/core/lookups.d.ts
CHANGED
|
@@ -114,6 +114,11 @@ export declare function lookup<D extends LookupBearing, F extends keyof NonNulla
|
|
|
114
114
|
* and there are no typed aliases, so only the generic `.by(name, value)` is
|
|
115
115
|
* available. A consistent trade rather than a new one.
|
|
116
116
|
*
|
|
117
|
+
* On a connector EVENT's `where` (`.waitForEvent()` / `.eventTrigger()` /
|
|
118
|
+
* `onEvent()`), `action` is the event id and the token is matched against the
|
|
119
|
+
* resolution `prepare` recorded for the event as well, so this form and
|
|
120
|
+
* `lookup({ connector, event }, field)` resolve alike.
|
|
121
|
+
*
|
|
117
122
|
* @param key - The connector library key, e.g. `'uipath-salesforce-slack'`.
|
|
118
123
|
* @param action - The operation id, e.g. `'send-message-to-user'`.
|
|
119
124
|
* @param field - The lookup field, e.g. `'channel'`.
|
|
@@ -177,6 +182,23 @@ export type LookupResolutions = Readonly<Record<string, LookupResolution>>;
|
|
|
177
182
|
* @returns The connector-facing id, when this exact `(nodeType, field, by, value)` was resolved.
|
|
178
183
|
*/
|
|
179
184
|
export declare function resolvedValue(token: LookupToken, resolutions: LookupResolutions): string | undefined;
|
|
185
|
+
/**
|
|
186
|
+
* {@link resolvedValue} for a token on an event's `where`.
|
|
187
|
+
*
|
|
188
|
+
* `prepare` records an event's resolutions under the EVENT form of the node type,
|
|
189
|
+
* which the descriptor form `lookup({ connector, event }, field)` keys a token
|
|
190
|
+
* under too. The string form `lookup(key, action, field)` cannot know it is being
|
|
191
|
+
* used on an event and keys the token under the activity form, so a recorded
|
|
192
|
+
* resolution was never found for it: on the 2026-10-16 run an author whose
|
|
193
|
+
* `prepare --resolve parentFolderId:displayName=Inbox` had succeeded still saw
|
|
194
|
+
* `LOOKUP_UNRESOLVED` from `check` and a refusal from `compile`, and pasted the
|
|
195
|
+
* id in by hand. The token's own key is tried first; the event form second.
|
|
196
|
+
*
|
|
197
|
+
* @param token - The unresolved lookup, from either `lookup()` form.
|
|
198
|
+
* @param resolutions - Everything `prepare --resolve` has recorded.
|
|
199
|
+
* @returns The connector-facing id, when either key was resolved.
|
|
200
|
+
*/
|
|
201
|
+
export declare function resolvedEventValue(token: LookupToken, resolutions: LookupResolutions): string | undefined;
|
|
180
202
|
/**
|
|
181
203
|
* The message for a lookup nothing has resolved — the command that fixes it.
|
|
182
204
|
*
|
package/dist/core/lookups.js
CHANGED
|
@@ -166,6 +166,39 @@ export function resolvedValue(token, resolutions) {
|
|
|
166
166
|
const { nodeType, field, by, value } = token.__lookup;
|
|
167
167
|
return resolutions[lookupKey(nodeType, field, by, value)]?.value;
|
|
168
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* The event form of a node type — `uipath.connector.event.<key>.<op>` — for one
|
|
171
|
+
* written without a role segment, else `undefined`. A role-less type is what the
|
|
172
|
+
* string form `lookup(key, action, field)` keys a token under.
|
|
173
|
+
*/
|
|
174
|
+
function eventFormOf(nodeType) {
|
|
175
|
+
const m = /^uipath\.connector\.(?!event\.|trigger\.)(.+)$/.exec(nodeType);
|
|
176
|
+
return m ? `uipath.connector.event.${m[1]}` : undefined;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* {@link resolvedValue} for a token on an event's `where`.
|
|
180
|
+
*
|
|
181
|
+
* `prepare` records an event's resolutions under the EVENT form of the node type,
|
|
182
|
+
* which the descriptor form `lookup({ connector, event }, field)` keys a token
|
|
183
|
+
* under too. The string form `lookup(key, action, field)` cannot know it is being
|
|
184
|
+
* used on an event and keys the token under the activity form, so a recorded
|
|
185
|
+
* resolution was never found for it: on the 2026-10-16 run an author whose
|
|
186
|
+
* `prepare --resolve parentFolderId:displayName=Inbox` had succeeded still saw
|
|
187
|
+
* `LOOKUP_UNRESOLVED` from `check` and a refusal from `compile`, and pasted the
|
|
188
|
+
* id in by hand. The token's own key is tried first; the event form second.
|
|
189
|
+
*
|
|
190
|
+
* @param token - The unresolved lookup, from either `lookup()` form.
|
|
191
|
+
* @param resolutions - Everything `prepare --resolve` has recorded.
|
|
192
|
+
* @returns The connector-facing id, when either key was resolved.
|
|
193
|
+
*/
|
|
194
|
+
export function resolvedEventValue(token, resolutions) {
|
|
195
|
+
const own = resolvedValue(token, resolutions);
|
|
196
|
+
if (own !== undefined)
|
|
197
|
+
return own;
|
|
198
|
+
const { nodeType, field, by, value } = token.__lookup;
|
|
199
|
+
const eventForm = eventFormOf(nodeType);
|
|
200
|
+
return eventForm === undefined ? undefined : resolutions[lookupKey(eventForm, field, by, value)]?.value;
|
|
201
|
+
}
|
|
169
202
|
/**
|
|
170
203
|
* The message for a lookup nothing has resolved — the command that fixes it.
|
|
171
204
|
*
|
|
@@ -282,7 +315,7 @@ export function resolveEventWhere(where, resolutions, site) {
|
|
|
282
315
|
out[key] = value;
|
|
283
316
|
continue;
|
|
284
317
|
}
|
|
285
|
-
const resolved =
|
|
318
|
+
const resolved = resolvedEventValue(value, resolutions);
|
|
286
319
|
if (resolved === undefined)
|
|
287
320
|
throw new Error(`${site}: \`where.${key}\` ` + unresolvedLookupMessage(value));
|
|
288
321
|
out[key] = resolved;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one refusal Flow adds on top of `Library.resolve`: an operation whose
|
|
3
|
+
* sidecar `registry prepare` synthesized from Integration Service
|
|
4
|
+
* (`prepare/integration-service.ts`) has no registry node behind it. A BPMN
|
|
5
|
+
* `sendTask` carries its own identity and rows, so BPMN compiles it; a Flow
|
|
6
|
+
* node embeds the registry's whole definition — form, handles, model — which
|
|
7
|
+
* no describe supplies, so Flow refuses it here, in `check` and `compile`
|
|
8
|
+
* alike, instead of emitting a node the designer cannot open.
|
|
9
|
+
*/
|
|
10
|
+
import { ConnectorResolveError } from './connector-resolve-error.js';
|
|
11
|
+
import type { ResolvedConnector } from './library.js';
|
|
12
|
+
/** The marker `prepare` writes on a sidecar it built from Integration Service. */
|
|
13
|
+
export declare const SYNTHESIZED_FROM_INTEGRATION_SERVICE = "integration-service";
|
|
14
|
+
/** Flow's refusal for a resolved operation with no registry definition, or `undefined` when it has one. */
|
|
15
|
+
export declare function synthesizedDefinitionRefusal(rc: Pick<ResolvedConnector, 'v1def' | 'connectorKey' | 'nodeType'>): ConnectorResolveError | undefined;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one refusal Flow adds on top of `Library.resolve`: an operation whose
|
|
3
|
+
* sidecar `registry prepare` synthesized from Integration Service
|
|
4
|
+
* (`prepare/integration-service.ts`) has no registry node behind it. A BPMN
|
|
5
|
+
* `sendTask` carries its own identity and rows, so BPMN compiles it; a Flow
|
|
6
|
+
* node embeds the registry's whole definition — form, handles, model — which
|
|
7
|
+
* no describe supplies, so Flow refuses it here, in `check` and `compile`
|
|
8
|
+
* alike, instead of emitting a node the designer cannot open.
|
|
9
|
+
*/
|
|
10
|
+
import { ConnectorResolveError } from './connector-resolve-error.js';
|
|
11
|
+
/** The marker `prepare` writes on a sidecar it built from Integration Service. */
|
|
12
|
+
export const SYNTHESIZED_FROM_INTEGRATION_SERVICE = 'integration-service';
|
|
13
|
+
/** Flow's refusal for a resolved operation with no registry definition, or `undefined` when it has one. */
|
|
14
|
+
export function synthesizedDefinitionRefusal(rc) {
|
|
15
|
+
const origin = rc.v1def?.synthesizedFrom;
|
|
16
|
+
if (origin !== SYNTHESIZED_FROM_INTEGRATION_SERVICE)
|
|
17
|
+
return undefined;
|
|
18
|
+
const action = String(rc.nodeType).replace(/^uipath\.connector\.[^.]+\./, '');
|
|
19
|
+
return new ConnectorResolveError('CONNECTOR_NO_FLOW_DEFINITION', `connector ${rc.connectorKey} ${action} was prepared from Integration Service because the Flow node catalog serves no node for ${rc.connectorKey}; `
|
|
20
|
+
+ 'a Flow step or inline agent tool needs that node (its form, handles and model), so this operation compiles in BPMN only. '
|
|
21
|
+
+ `Author it as a BPMN .connector(), or check \`uip maestro flow registry search ${rc.connectorKey}\` after \`registry pull --force\` in case the catalog has since gained it.`);
|
|
22
|
+
}
|
|
@@ -130,4 +130,20 @@ export declare function isResourcesList(ctx: BuildContext, connectorKey: string,
|
|
|
130
130
|
* files are deliberately not reused: that output omitted the discovery
|
|
131
131
|
* capabilities and would silently mark every connector static.
|
|
132
132
|
*/
|
|
133
|
+
/** `uip is activities list`'s answer: its rows, or why it could not be read. */
|
|
134
|
+
export type IsActivitiesCatalog = {
|
|
135
|
+
rows: unknown[];
|
|
136
|
+
} | {
|
|
137
|
+
failure: string;
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* Cached `uip is activities list <key>`: the connector's activity catalog
|
|
141
|
+
* (every row, curated or not — the caller filters on `IsCurated`), or the
|
|
142
|
+
* failure in the CLI's own words ({@link failureReason}): a refusal, output
|
|
143
|
+
* that is not JSON, or a `Result: Failure` envelope (an expired token, a 5xx,
|
|
144
|
+
* a connector the tenant has not enabled). Cached beside the `is resources`
|
|
145
|
+
* payloads — it is the same catalog service, read without a connection — and
|
|
146
|
+
* a cached failure keeps its reason.
|
|
147
|
+
*/
|
|
148
|
+
export declare function isActivitiesCatalog(ctx: BuildContext, connectorKey: string): Promise<IsActivitiesCatalog>;
|
|
133
149
|
export declare function isConnectorGet(ctx: BuildContext, connectorKey: string): Promise<unknown>;
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
import { createHash } from "node:crypto";
|
|
17
17
|
import { pyJsonStringify } from "../codecs/pyjson.js";
|
|
18
18
|
import { sortedByKeys } from "../compat/compare.js";
|
|
19
|
-
import { isConnectorsMetadata, isResourcesDescribeObject, isResourcesDescribeOperation, isResourcesList as isResourcesListArgv, registryGet as registryGetArgv, } from "../uip/commands.js";
|
|
19
|
+
import { isActivitiesList as isActivitiesListArgv, isConnectorsMetadata, isResourcesDescribeObject, isResourcesDescribeOperation, isResourcesList as isResourcesListArgv, registryGet as registryGetArgv, } from "../uip/commands.js";
|
|
20
20
|
import { CACHED_FAILURE, isCachedFailure, safeCacheName } from "../uip/response-cache.js";
|
|
21
21
|
import { failureReason, isNodeNotFound } from "../uip/envelope.js";
|
|
22
22
|
import { get, isPyDict, pyOr, pyTruthy } from "./py.js";
|
|
@@ -251,11 +251,38 @@ export async function isResourcesList(ctx, connectorKey, options = {}) {
|
|
|
251
251
|
return Array.isArray(payload.Data) ? payload.Data : undefined;
|
|
252
252
|
}
|
|
253
253
|
/**
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
254
|
+
* Cached `uip is activities list <key>`: the connector's activity catalog
|
|
255
|
+
* (every row, curated or not — the caller filters on `IsCurated`), or the
|
|
256
|
+
* failure in the CLI's own words ({@link failureReason}): a refusal, output
|
|
257
|
+
* that is not JSON, or a `Result: Failure` envelope (an expired token, a 5xx,
|
|
258
|
+
* a connector the tenant has not enabled). Cached beside the `is resources`
|
|
259
|
+
* payloads — it is the same catalog service, read without a connection — and
|
|
260
|
+
* a cached failure keeps its reason.
|
|
258
261
|
*/
|
|
262
|
+
export async function isActivitiesCatalog(ctx, connectorKey) {
|
|
263
|
+
const name = `${safeCacheName(connectorKey, "_activities_list")}.json`;
|
|
264
|
+
if (ctx.caches.is.has(name)) {
|
|
265
|
+
const cached = ctx.caches.is.read(name);
|
|
266
|
+
const data = get(cached, "Data");
|
|
267
|
+
if (Array.isArray(data))
|
|
268
|
+
return { rows: data };
|
|
269
|
+
return { failure: String(pyOr(get(cached, "Message"), "the cached answer carries no rows")) };
|
|
270
|
+
}
|
|
271
|
+
const result = await runUip(ctx, isActivitiesListArgv(connectorKey));
|
|
272
|
+
if (refused(result))
|
|
273
|
+
return { failure: failureReason(result) };
|
|
274
|
+
const parsed = parseStdout(result.stdout);
|
|
275
|
+
if (parsed === undefined)
|
|
276
|
+
return { failure: failureReason(result) };
|
|
277
|
+
const payload = envelope(parsed);
|
|
278
|
+
if (get(payload, "Result") !== "Success") {
|
|
279
|
+
const failure = failureReason(result);
|
|
280
|
+
writeCache(ctx.caches.is, name, { Result: "Failure", Message: failure, [CACHED_FAILURE]: true });
|
|
281
|
+
return { failure };
|
|
282
|
+
}
|
|
283
|
+
writeCache(ctx.caches.is, name, payload);
|
|
284
|
+
return Array.isArray(payload.Data) ? { rows: payload.Data } : { failure: "the answer carries no Data rows" };
|
|
285
|
+
}
|
|
259
286
|
export async function isConnectorGet(ctx, connectorKey) {
|
|
260
287
|
const name = `${safeCacheName(connectorKey, "_connector_metadata")}.json`;
|
|
261
288
|
const firstRow = (payload) => {
|