@uipath/maestro-builder-sdk 6.68.3 → 6.68.5
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 +34 -33
- package/dist/api-members.md +162 -158
- package/dist/bindings-v2.d.ts +2 -9
- package/dist/bindings-v2.js +10 -20
- package/dist/case/bindings-v2.d.ts +3 -0
- package/dist/case/bindings-v2.js +281 -0
- package/dist/case/case-sdk.d.ts +33 -3
- package/dist/case/case-sdk.js +1 -0
- package/dist/case/check.js +6 -8
- package/dist/case/compile-cli.js +7 -201
- package/dist/case/decompile.js +25 -9
- package/dist/case/external.d.ts +27 -16
- package/dist/case/external.js +46 -19
- package/dist/case/serialize.js +17 -8
- package/dist/core/bindings-v2-resource.d.ts +51 -0
- package/dist/core/bindings-v2-resource.js +50 -0
- package/dist/core/refusals.d.ts +1 -1
- package/dist/core/refusals.js +1 -1
- package/dist/index.d.ts +1 -1
- package/package.json +2 -2
package/dist/bindings-v2.d.ts
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
import type { ConnectionUse } from './core/compile-refusal.js';
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
resource: string;
|
|
5
|
-
key: string;
|
|
6
|
-
id?: string;
|
|
7
|
-
value: Record<string, unknown>;
|
|
8
|
-
metadata: Record<string, unknown>;
|
|
9
|
-
[k: string]: unknown;
|
|
10
|
-
}
|
|
2
|
+
import { type BindingsV2Resource } from './core/bindings-v2-resource.js';
|
|
3
|
+
export type { BindingsV2Resource };
|
|
11
4
|
/** The `bindings_v2.json` document. */
|
|
12
5
|
export interface BindingsV2Document {
|
|
13
6
|
version: string;
|
package/dist/bindings-v2.js
CHANGED
|
@@ -55,10 +55,11 @@
|
|
|
55
55
|
*/
|
|
56
56
|
import { BindingRefusal, connectionConnectorConflictMessage } from './core/binding-messages.js';
|
|
57
57
|
import { isRecord } from './core/node-envelope.js';
|
|
58
|
+
import { BINDINGS_V2_DOCUMENT_VERSION, connectionResource, RESOURCE_BINDINGS_VERSION, } from './core/bindings-v2-resource.js';
|
|
58
59
|
/** The document version pack emits. */
|
|
59
|
-
const DOCUMENT_VERSION =
|
|
60
|
+
const DOCUMENT_VERSION = BINDINGS_V2_DOCUMENT_VERSION;
|
|
60
61
|
/** The per-resource metadata version pack stamps. */
|
|
61
|
-
const BINDINGS_VERSION =
|
|
62
|
+
const BINDINGS_VERSION = RESOURCE_BINDINGS_VERSION;
|
|
62
63
|
function nonEmptyString(value) {
|
|
63
64
|
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
64
65
|
}
|
|
@@ -235,26 +236,15 @@ export function connectionResources(flow) {
|
|
|
235
236
|
const binding = candidates[0];
|
|
236
237
|
const displayLabel = nonEmptyString(binding.name) ?? key;
|
|
237
238
|
const label = isRecord(node.display) ? nonEmptyString(node.display.label) : undefined;
|
|
238
|
-
|
|
239
|
+
// The same row Case writes (core/bindings-v2-resource.ts); pack writes no folderKey slot.
|
|
240
|
+
resources.push(connectionResource({
|
|
239
241
|
resource: 'connection',
|
|
240
242
|
key,
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
isExpression: false,
|
|
247
|
-
displayName: displayLabel,
|
|
248
|
-
},
|
|
249
|
-
},
|
|
250
|
-
metadata: {
|
|
251
|
-
ActivityName: label ?? nonEmptyString(node.id) ?? key,
|
|
252
|
-
BindingsVersion: BINDINGS_VERSION,
|
|
253
|
-
DisplayLabel: displayLabel,
|
|
254
|
-
UseConnectionService: 'true',
|
|
255
|
-
Connector: connector,
|
|
256
|
-
},
|
|
257
|
-
});
|
|
243
|
+
connectionId: nonEmptyString(binding.default) ?? key,
|
|
244
|
+
displayLabel,
|
|
245
|
+
activityName: label ?? nonEmptyString(node.id) ?? key,
|
|
246
|
+
connector,
|
|
247
|
+
}));
|
|
258
248
|
}
|
|
259
249
|
return resources;
|
|
260
250
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type BindingsV2Resource } from '../core/bindings-v2-resource.js';
|
|
2
|
+
/** Every resource the caseplan's root bindings declare, in the order their rows first appear. */
|
|
3
|
+
export declare function deriveCaseBindingsV2(caseplan: Record<string, unknown>): BindingsV2Resource[];
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `bindings_v2.json` resources a caseplan's root `bindings[]` rows declare:
|
|
3
|
+
* the root-row pass of `uip maestro case bindings sync` (`deriveCaseBindingsV2`
|
|
4
|
+
* in cli `packages/case-tool/src/services/case-bindings-service.ts`). It is
|
|
5
|
+
* NOT the whole CLI function; see "Not ported" below.
|
|
6
|
+
*
|
|
7
|
+
* `uip maestro case validate --strict` re-derives the sidecar with that
|
|
8
|
+
* function and compares it to the file on disk entry by entry, as serialized
|
|
9
|
+
* JSON (`STRICT_BINDINGS_SIDECAR_STALE`), so field order matters as well as
|
|
10
|
+
* field values. The CLI is a restricted package the SDK cannot depend on, so
|
|
11
|
+
* this is a copy; `tests/case-bindings-v2-parity.test.ts` holds it to the
|
|
12
|
+
* CLI's own output, one resource kind at a time, from a fixture the real CLI
|
|
13
|
+
* function derived. Re-record that fixture with
|
|
14
|
+
* `bun typescript/tests/fixtures/case-bindings-v2/record.ts <uipath-cli checkout>`.
|
|
15
|
+
*
|
|
16
|
+
* Every entry comes from the root `bindings[]` rows, grouped by
|
|
17
|
+
* `(resource, resourceKey)`:
|
|
18
|
+
*
|
|
19
|
+
* - `Connection` → the connection row Flow writes too
|
|
20
|
+
* ({@link connectionResource}); its connector and activity label come from
|
|
21
|
+
* the connector context that references the row's binding id.
|
|
22
|
+
* - `app` / `process` / `ixpDeployment` → the Case Designer shape.
|
|
23
|
+
* - any other resource → the plain shape.
|
|
24
|
+
*
|
|
25
|
+
* Not ported — the CLI function's other passes:
|
|
26
|
+
*
|
|
27
|
+
* - `EventTrigger` / `Property` entries for a connector start trigger
|
|
28
|
+
* (`deriveEventTriggerResources`). The SDK writes these triggers, so this
|
|
29
|
+
* is a live gap: builder-sdk#1557, a known-gap row in the parity test.
|
|
30
|
+
* - The stored-binding overlay: entries a connector task keeps in its
|
|
31
|
+
* `metadata` context's `body.bindings`, merged into the derived entry's
|
|
32
|
+
* metadata or appended. The serializer's connector `metadata` body has no
|
|
33
|
+
* `bindings` key, so there is nothing to overlay today.
|
|
34
|
+
* - `Role` entries from `metadata.caseRoles`. The SDK neither authors nor
|
|
35
|
+
* preserves `caseRoles` (it is not in preserve.ts's metadata keys).
|
|
36
|
+
*
|
|
37
|
+
* If the serializer starts writing either of the last two, port the pass and
|
|
38
|
+
* add a task for it to the parity fixture (`fixtures/case-bindings-v2/plan.ts`).
|
|
39
|
+
*/
|
|
40
|
+
import { isRecord } from '../core/node-envelope.js';
|
|
41
|
+
import { connectionResource, RESOURCE_BINDINGS_VERSION, } from '../core/bindings-v2-resource.js';
|
|
42
|
+
import { isInSolutionKey, SOLUTION_FOLDER } from './resource-identity.js';
|
|
43
|
+
const CONNECTION_RESOURCE = 'Connection';
|
|
44
|
+
/** The placeholder a manual-mode http-request task binds; nothing declares it. */
|
|
45
|
+
const IMPLICIT_CONNECTION = 'ImplicitConnection';
|
|
46
|
+
const CONNECTION_ID_ATTRIBUTE = 'ConnectionId';
|
|
47
|
+
const FOLDER_KEY_ATTRIBUTE = 'folderKey';
|
|
48
|
+
const BINDING_REFERENCE_PREFIX = '=bindings.';
|
|
49
|
+
function asBindingRow(value) {
|
|
50
|
+
if (!isRecord(value))
|
|
51
|
+
return undefined;
|
|
52
|
+
return typeof value.id === 'string'
|
|
53
|
+
&& typeof value.resource === 'string'
|
|
54
|
+
&& typeof value.resourceKey === 'string'
|
|
55
|
+
&& value.resourceKey !== ''
|
|
56
|
+
&& typeof value.propertyAttribute === 'string'
|
|
57
|
+
&& (value.default === undefined || typeof value.default === 'string')
|
|
58
|
+
? value
|
|
59
|
+
: undefined;
|
|
60
|
+
}
|
|
61
|
+
/** Every resource the caseplan's root bindings declare, in the order their rows first appear. */
|
|
62
|
+
export function deriveCaseBindingsV2(caseplan) {
|
|
63
|
+
const groups = new Map();
|
|
64
|
+
for (const entry of Array.isArray(caseplan.bindings) ? caseplan.bindings : []) {
|
|
65
|
+
const binding = asBindingRow(entry);
|
|
66
|
+
if (!binding)
|
|
67
|
+
continue;
|
|
68
|
+
if (binding.resource === CONNECTION_RESOURCE && binding.resourceKey === IMPLICIT_CONNECTION)
|
|
69
|
+
continue;
|
|
70
|
+
const groupKey = `${binding.resource}:${binding.resourceKey}`;
|
|
71
|
+
const group = groups.get(groupKey);
|
|
72
|
+
if (group)
|
|
73
|
+
group.push(binding);
|
|
74
|
+
else
|
|
75
|
+
groups.set(groupKey, [binding]);
|
|
76
|
+
}
|
|
77
|
+
const connectorUses = collectConnectorUses(caseplan);
|
|
78
|
+
const labels = bindingLabels(caseplan.nodes);
|
|
79
|
+
return [...groups.values()].map((group) => group[0].resource === CONNECTION_RESOURCE
|
|
80
|
+
? connectionEntry(group, connectorUses)
|
|
81
|
+
: resourceEntry(group, labels));
|
|
82
|
+
}
|
|
83
|
+
function attributeDefault(group, attribute) {
|
|
84
|
+
return group.find((binding) => binding.propertyAttribute === attribute)?.default;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* A connection: its `ConnectionId` row labels it, its `folderKey` row (keyed
|
|
88
|
+
* by the connection, not the folder) gives the folder slot.
|
|
89
|
+
*/
|
|
90
|
+
function connectionEntry(group, uses) {
|
|
91
|
+
const key = group[0].resourceKey;
|
|
92
|
+
const use = group.map((binding) => uses.get(binding.id)).find((found) => found !== undefined);
|
|
93
|
+
const label = (group.find((binding) => binding.propertyAttribute === CONNECTION_ID_ATTRIBUTE) ?? group[0]).name;
|
|
94
|
+
return connectionResource({
|
|
95
|
+
resource: CONNECTION_RESOURCE,
|
|
96
|
+
key,
|
|
97
|
+
connectionId: attributeDefault(group, CONNECTION_ID_ATTRIBUTE) ?? key,
|
|
98
|
+
...(typeof label === 'string' && label !== '' && { displayLabel: label }),
|
|
99
|
+
folderKey: attributeDefault(group, FOLDER_KEY_ATTRIBUTE),
|
|
100
|
+
activityName: use?.activityName,
|
|
101
|
+
connector: use?.connectorKey,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The Case Designer's label for each value slot of an app or process entry.
|
|
106
|
+
* These resources get the Designer shape (cli#5003, MST-16424): an `id`, each
|
|
107
|
+
* slot flagged and labelled, and the `BindingsVersion` / `SolutionsSupport`
|
|
108
|
+
* metadata without which deploy cannot override the entry.
|
|
109
|
+
*/
|
|
110
|
+
const DESIGNER_SLOT_NAMES = new Map([
|
|
111
|
+
['app', { name: 'App name', folderPath: 'Folder path', solutionsSupport: true }],
|
|
112
|
+
['process', { name: 'Process name', folderPath: 'Folder path', solutionsSupport: true }],
|
|
113
|
+
['ixpDeployment', { name: 'name', folderPath: 'Folder path', solutionsSupport: false }],
|
|
114
|
+
]);
|
|
115
|
+
/**
|
|
116
|
+
* One sidecar entry for a group of root binding rows that share a resource and
|
|
117
|
+
* a resource key. The subtype comes from the rows only: a Flow's rows carry
|
|
118
|
+
* none, so its entry has none either, as the Designer saves it.
|
|
119
|
+
*
|
|
120
|
+
* `metadata.ActivityName` labels a Designer-shaped entry with the first task
|
|
121
|
+
* that references one of its rows ({@link bindingLabels}).
|
|
122
|
+
*/
|
|
123
|
+
function resourceEntry(group, labels) {
|
|
124
|
+
const resource = group[0].resource;
|
|
125
|
+
const key = group[0].resourceKey;
|
|
126
|
+
// An in-solution resource binds an empty folderPath in the caseplan, while
|
|
127
|
+
// the sidecar names its folder by the sentinel the key carries.
|
|
128
|
+
const folderPath = isInSolutionKey(key) ? SOLUTION_FOLDER : attributeDefault(group, 'folderPath');
|
|
129
|
+
const name = attributeDefault(group, 'name');
|
|
130
|
+
const subType = group.map((binding) => binding.resourceSubType).find((found) => found !== undefined);
|
|
131
|
+
const designer = DESIGNER_SLOT_NAMES.get(resource);
|
|
132
|
+
if (designer) {
|
|
133
|
+
const value = {};
|
|
134
|
+
if (folderPath !== undefined) {
|
|
135
|
+
value.folderPath = { defaultValue: folderPath, isExpression: false, displayName: designer.folderPath };
|
|
136
|
+
}
|
|
137
|
+
if (name !== undefined) {
|
|
138
|
+
value.name = { defaultValue: name, isExpression: false, displayName: designer.name };
|
|
139
|
+
}
|
|
140
|
+
const activityName = group.map((binding) => labels.get(binding.id)).find((found) => found !== undefined);
|
|
141
|
+
return {
|
|
142
|
+
resource,
|
|
143
|
+
key,
|
|
144
|
+
id: `${resource}${key}`,
|
|
145
|
+
value,
|
|
146
|
+
metadata: {
|
|
147
|
+
...(activityName !== undefined && { ActivityName: activityName }),
|
|
148
|
+
BindingsVersion: RESOURCE_BINDINGS_VERSION,
|
|
149
|
+
DisplayLabel: 'name',
|
|
150
|
+
...(designer.solutionsSupport && { SolutionsSupport: 'true' }),
|
|
151
|
+
...(subType !== undefined && { subType }),
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
const value = {};
|
|
156
|
+
if (name !== undefined)
|
|
157
|
+
value.name = { defaultValue: name };
|
|
158
|
+
if (folderPath !== undefined)
|
|
159
|
+
value.folderPath = { defaultValue: folderPath };
|
|
160
|
+
// A solution-resource picker (such as a Data Fabric entity) carries its folder key as a slot of its own.
|
|
161
|
+
const folderKey = attributeDefault(group, FOLDER_KEY_ATTRIBUTE);
|
|
162
|
+
if (folderKey !== undefined)
|
|
163
|
+
value.folderKey = { defaultValue: folderKey };
|
|
164
|
+
return {
|
|
165
|
+
resource,
|
|
166
|
+
key,
|
|
167
|
+
value,
|
|
168
|
+
metadata: {
|
|
169
|
+
BindingsVersion: RESOURCE_BINDINGS_VERSION,
|
|
170
|
+
...(subType !== undefined && { subType }),
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/** A task's or condition's `displayName`, a trigger's `display.label`, or a legacy node's `label`. */
|
|
175
|
+
function ownLabel(node) {
|
|
176
|
+
const display = isRecord(node.display) ? node.display.label : undefined;
|
|
177
|
+
return [node.displayName, display, node.label].find((label) => typeof label === 'string' && label !== '');
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Every value under `root`, each with the label of the nearest object above it
|
|
181
|
+
* that names itself ({@link ownLabel}) — the CLI's `walkLabelled`.
|
|
182
|
+
*/
|
|
183
|
+
function walkLabelled(root, visit) {
|
|
184
|
+
const walk = (node, label) => {
|
|
185
|
+
visit(node, label);
|
|
186
|
+
if (Array.isArray(node)) {
|
|
187
|
+
for (const item of node)
|
|
188
|
+
walk(item, label);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (!isRecord(node))
|
|
192
|
+
return;
|
|
193
|
+
const scoped = ownLabel(node) ?? label;
|
|
194
|
+
for (const value of Object.values(node))
|
|
195
|
+
walk(value, scoped);
|
|
196
|
+
};
|
|
197
|
+
walk(root, undefined);
|
|
198
|
+
}
|
|
199
|
+
/** A `{ name, value }` context array that names a connector, by entry name; else undefined. */
|
|
200
|
+
function connectorContext(value) {
|
|
201
|
+
if (!Array.isArray(value))
|
|
202
|
+
return undefined;
|
|
203
|
+
const byName = new Map();
|
|
204
|
+
for (const entry of value) {
|
|
205
|
+
if (!isRecord(entry) || typeof entry.name !== 'string')
|
|
206
|
+
return undefined;
|
|
207
|
+
if (typeof entry.value === 'string')
|
|
208
|
+
byName.set(entry.name, entry.value);
|
|
209
|
+
}
|
|
210
|
+
return byName.has('connectorKey') ? byName : undefined;
|
|
211
|
+
}
|
|
212
|
+
function referencedBindingId(context) {
|
|
213
|
+
const connection = context?.get('connection');
|
|
214
|
+
return connection?.startsWith(BINDING_REFERENCE_PREFIX)
|
|
215
|
+
? connection.slice(BINDING_REFERENCE_PREFIX.length)
|
|
216
|
+
: undefined;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* The connector whose authentication an http-request task reuses: its `body`
|
|
220
|
+
* input's `targetConnector`. Its context names the generic HTTP connector
|
|
221
|
+
* whatever the request calls, while the connection belongs to the target.
|
|
222
|
+
*/
|
|
223
|
+
function httpTargetConnector(node) {
|
|
224
|
+
if (!isRecord(node) || node.type !== 'http-request' || !isRecord(node.data) || !Array.isArray(node.data.inputs)) {
|
|
225
|
+
return undefined;
|
|
226
|
+
}
|
|
227
|
+
for (const input of node.data.inputs) {
|
|
228
|
+
if (!isRecord(input) || input.name !== 'body' || !isRecord(input.body))
|
|
229
|
+
continue;
|
|
230
|
+
const target = input.body.targetConnector;
|
|
231
|
+
if (typeof target === 'string' && target !== '')
|
|
232
|
+
return target;
|
|
233
|
+
}
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* The connector of each connection binding, keyed by binding id, from the
|
|
238
|
+
* connector contexts that reference it (`connection: "=bindings.<id>"`). The
|
|
239
|
+
* first use wins, as the CLI's `collectConnectorKeys` decides it.
|
|
240
|
+
*/
|
|
241
|
+
function collectConnectorUses(caseplan) {
|
|
242
|
+
const byBindingId = new Map();
|
|
243
|
+
walkLabelled(caseplan, (node, label) => {
|
|
244
|
+
// Before the context array beneath it, which would otherwise claim the
|
|
245
|
+
// binding for the generic HTTP connector.
|
|
246
|
+
const target = httpTargetConnector(node);
|
|
247
|
+
if (target !== undefined && isRecord(node)) {
|
|
248
|
+
const data = node.data;
|
|
249
|
+
const bindingId = referencedBindingId(connectorContext(data.context));
|
|
250
|
+
if (bindingId !== undefined && !byBindingId.has(bindingId)) {
|
|
251
|
+
byBindingId.set(bindingId, { connectorKey: target, activityName: ownLabel(node) ?? label });
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (!Array.isArray(node))
|
|
255
|
+
return;
|
|
256
|
+
const context = connectorContext(node);
|
|
257
|
+
const connectorKey = context?.get('connectorKey');
|
|
258
|
+
const bindingId = referencedBindingId(context);
|
|
259
|
+
if (connectorKey !== undefined && bindingId !== undefined && !byBindingId.has(bindingId)) {
|
|
260
|
+
byBindingId.set(bindingId, { connectorKey, activityName: label });
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
return byBindingId;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* The label of the first object that references each binding id
|
|
267
|
+
* (`=bindings.<id>`), in document order: the nearest enclosing `displayName`,
|
|
268
|
+
* `display.label` or `label`. The same walk as the CLI's
|
|
269
|
+
* `collectBindingLabels`, so both name an entry after the same task.
|
|
270
|
+
*/
|
|
271
|
+
function bindingLabels(nodes) {
|
|
272
|
+
const found = new Map();
|
|
273
|
+
walkLabelled(nodes ?? [], (node, label) => {
|
|
274
|
+
if (typeof node !== 'string' || label === undefined || !node.startsWith(BINDING_REFERENCE_PREFIX))
|
|
275
|
+
return;
|
|
276
|
+
const id = node.slice(BINDING_REFERENCE_PREFIX.length);
|
|
277
|
+
if (!found.has(id))
|
|
278
|
+
found.set(id, label);
|
|
279
|
+
});
|
|
280
|
+
return found;
|
|
281
|
+
}
|
package/dist/case/case-sdk.d.ts
CHANGED
|
@@ -437,9 +437,10 @@ export type ConnectorSpecData = Extract<ActionSpec, {
|
|
|
437
437
|
* and `hybrid` as `Async…Execution`, and writes the value itself into the task's
|
|
438
438
|
* `executionType` context.
|
|
439
439
|
*
|
|
440
|
-
* `hybrid`
|
|
441
|
-
*
|
|
442
|
-
*
|
|
440
|
+
* An `async` or `hybrid` object also answers through an event, which the
|
|
441
|
+
* Designer saves next to it; pass that pair as {@link ExternalTaskOptions.event}.
|
|
442
|
+
* A `hybrid` task without it is refused (EXTERNAL_TASK_EVENT), since the task
|
|
443
|
+
* would have nothing to wait on.
|
|
443
444
|
*
|
|
444
445
|
* It does not decide whether the stage waits for the task. A task the stage must
|
|
445
446
|
* not wait on is `.required(false)`, whatever its execution type. For example, a
|
|
@@ -447,6 +448,27 @@ export type ConnectorSpecData = Extract<ActionSpec, {
|
|
|
447
448
|
* it.
|
|
448
449
|
*/
|
|
449
450
|
export type ExternalExecutionMode = 'sync' | 'async' | 'hybrid';
|
|
451
|
+
/**
|
|
452
|
+
* The event an `async` or `hybrid` external object answers with, as Integration
|
|
453
|
+
* Service reports it for the connector object.
|
|
454
|
+
*
|
|
455
|
+
* @remarks
|
|
456
|
+
* Like the execution type, a property of the connector object, not a choice:
|
|
457
|
+
* the Case Designer reads it from the object's metadata (its first event mode
|
|
458
|
+
* and that mode's first event type) and saves it as the task's `operation` and
|
|
459
|
+
* `eventMode` context rows, and as `eventType` / `eventMode` in the metadata's
|
|
460
|
+
* `activityPropertyConfiguration`. Copy it from the same two rows of the
|
|
461
|
+
* activity's `case spec` result (`caseShape.context`). `decompile` lifts it from
|
|
462
|
+
* a saved task. For
|
|
463
|
+
* example, the Power Automate "Invoke a Flow" object (`hybrid`) answers with
|
|
464
|
+
* `{ operation: 'TRIGGER_CREATED', mode: 'polling' }`.
|
|
465
|
+
*/
|
|
466
|
+
export interface ExternalEventPair {
|
|
467
|
+
/** The event operation, e.g. `TRIGGER_CREATED`: the task's `operation` context row. */
|
|
468
|
+
operation: string;
|
|
469
|
+
/** How the event is received: the task's `eventMode` context row. */
|
|
470
|
+
mode: 'polling' | 'webhooks';
|
|
471
|
+
}
|
|
450
472
|
/** Required wiring and typed inputs for an external agent/workflow invocation. */
|
|
451
473
|
export interface ExternalTaskOptions<I extends Record<string, unknown>> {
|
|
452
474
|
/** Symbolic Integration Service connection name declared in `bindings.json`. */
|
|
@@ -460,6 +482,12 @@ export interface ExternalTaskOptions<I extends Record<string, unknown>> {
|
|
|
460
482
|
* `.required(false)`. See {@link ExternalExecutionMode}.
|
|
461
483
|
*/
|
|
462
484
|
mode: ExternalExecutionMode;
|
|
485
|
+
/**
|
|
486
|
+
* The event an `async` or `hybrid` object answers with. Required for `hybrid`;
|
|
487
|
+
* optional for `async`; refused for `sync`, which answers inline. See
|
|
488
|
+
* {@link ExternalEventPair}.
|
|
489
|
+
*/
|
|
490
|
+
event?: ExternalEventPair;
|
|
463
491
|
/** Inputs statically checked by the generated connector descriptor. */
|
|
464
492
|
inputs: I;
|
|
465
493
|
}
|
|
@@ -467,6 +495,8 @@ export interface ExternalTaskOptions<I extends Record<string, unknown>> {
|
|
|
467
495
|
export interface ExternalTaskSpecData {
|
|
468
496
|
/** The connector object's `executionType`. */
|
|
469
497
|
mode: ExternalExecutionMode;
|
|
498
|
+
/** The event an `async` or `hybrid` object answers with, when given. */
|
|
499
|
+
event?: ExternalEventPair;
|
|
470
500
|
/** Shared connector action identity, inputs, and symbolic bindings. */
|
|
471
501
|
connector: ConnectorSpecData;
|
|
472
502
|
}
|
package/dist/case/case-sdk.js
CHANGED
|
@@ -334,6 +334,7 @@ class TaskBuilder {
|
|
|
334
334
|
this.setKind(kind);
|
|
335
335
|
this._external = {
|
|
336
336
|
mode: opts.mode,
|
|
337
|
+
...(opts.event !== undefined ? { event: { ...opts.event } } : {}),
|
|
337
338
|
connector: makeConnector(descriptor, opts.inputs, {
|
|
338
339
|
connection: opts.connection,
|
|
339
340
|
folder: opts.folder,
|
package/dist/case/check.js
CHANGED
|
@@ -6,7 +6,7 @@ import { schemaVersionRefusal } from '../schema-version.js';
|
|
|
6
6
|
import { caseJsonPreservation, caseJsonPreservationErrors, preservedBindings } from './preserve.js';
|
|
7
7
|
import { EQUALS_EXPR_DIALECT, checkBindingLabels, checkConnectorContract, checkConnectorLookups, checkConnectorSchema, } from '../core/connector-checks.js';
|
|
8
8
|
import { checkEventResolution } from '../core/event-checks.js';
|
|
9
|
-
import { EXTERNAL_TASK_DESCRIPTOR_CONTRACT,
|
|
9
|
+
import { EXTERNAL_TASK_DESCRIPTOR_CONTRACT, EXTERNAL_TASK_EVENT, externalEventRefusal, isExternalExecutionMode } from './external.js';
|
|
10
10
|
import { isResourceLabelRefusal, resolveResourceRef } from './resource-identity.js';
|
|
11
11
|
import { taskShapeProblems } from './task-shape.js';
|
|
12
12
|
import { isConnectorPlaceholder } from './connector-placeholder.js';
|
|
@@ -623,13 +623,11 @@ function checkStage(stage, diags, refStage, refPlacement, bindings) {
|
|
|
623
623
|
message: `${family} task "${t.name}" mode must be the connector object's executionType: "sync", "async" or "hybrid".`,
|
|
624
624
|
where: t.name,
|
|
625
625
|
});
|
|
626
|
-
else
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
code:
|
|
630
|
-
|
|
631
|
-
where: t.name,
|
|
632
|
-
});
|
|
626
|
+
else {
|
|
627
|
+
const refused = externalEventRefusal(`${family} task "${t.name}"`, t.external.mode, t.external.event);
|
|
628
|
+
if (refused)
|
|
629
|
+
diags.push({ level: 'error', code: EXTERNAL_TASK_EVENT, message: refused, where: t.name });
|
|
630
|
+
}
|
|
633
631
|
if (!t.external.connector?.key?.trim() || !t.external.connector?.action?.trim())
|
|
634
632
|
diags.push({
|
|
635
633
|
level: 'error',
|