@uipath/maestro-builder-sdk 6.70.9 → 6.71.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/check.js +11 -11
- package/dist/core/event-subscription.d.ts +113 -0
- package/dist/core/event-subscription.js +356 -0
- package/dist/core/nodes/registry.d.ts +3 -1
- package/dist/core/nodes/registry.js +22 -3
- package/dist/decompile.js +0 -7
- package/dist/flow-node-type.d.ts +25 -0
- package/dist/nodes/connector/flow/event-wait.d.ts +33 -0
- package/dist/nodes/connector/flow/event-wait.js +162 -0
- package/dist/nodes/connector/flow/family.d.ts +1 -1
- package/dist/nodes/connector/flow/family.js +3 -2
- package/dist/serialize.js +20 -391
- package/package.json +2 -2
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A mid-flow wait for an Integration Service event — `waitForEvent(subscription)`,
|
|
3
|
+
* a node type per event: `uipath.connector.event.<connectorKey>.<event>`. The
|
|
4
|
+
* type owns every name under `uipath.connector.event.` that names an event,
|
|
5
|
+
* nested inside the connector operations' `uipath.connector.` prefix, whose
|
|
6
|
+
* narrowing leaves it alone. Per-tenant: the definition is the library's event
|
|
7
|
+
* entry, and so is its version (`versionedByDefinition`).
|
|
8
|
+
*
|
|
9
|
+
* The same node type opens a flow as its start trigger (`onEvent`). That place
|
|
10
|
+
* stays the pipeline's until the triggers port: serialize places the trigger, and
|
|
11
|
+
* decompile reads a root `uipath.connector.event.*` node as one. This type is the
|
|
12
|
+
* node as a step, with an incoming edge.
|
|
13
|
+
*
|
|
14
|
+
* The subscription's emission — `inputs.detail`, the library's resolution, the
|
|
15
|
+
* refusals compile makes with and without a library — is shared with the trigger,
|
|
16
|
+
* so it stays in `core/event-subscription.ts` and is called here through
|
|
17
|
+
* `ctx.events`; the subscription's check rules stay in `core/event-checks.ts` and
|
|
18
|
+
* run through `ctx.eventSubscription`, with their codes.
|
|
19
|
+
*
|
|
20
|
+
* Moved from serialize.ts / check.ts / decompile.ts in Phase 4 of
|
|
21
|
+
* docs/NODE_TYPE_ARCHITECTURE_DESIGN.md.
|
|
22
|
+
*/
|
|
23
|
+
import type { ActionSpec } from '../../../core/actions.js';
|
|
24
|
+
import type { FlowVersionedNodeType } from '../../../flow-node-type.js';
|
|
25
|
+
export type EventWaitSpec = Extract<ActionSpec, {
|
|
26
|
+
kind: 'waitEvent';
|
|
27
|
+
}>;
|
|
28
|
+
/** `<connector>.<event>` after the prefix, split on the last dot; undefined for a name with no event. */
|
|
29
|
+
export declare function splitEvent(typeName: string): {
|
|
30
|
+
connector: string;
|
|
31
|
+
event: string;
|
|
32
|
+
} | undefined;
|
|
33
|
+
export declare const eventWaitNodeType: FlowVersionedNodeType<EventWaitSpec>;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { missingBindingLabels } from '../../../core/binding-messages.js';
|
|
2
|
+
import { prepareCommand } from '../../../core/cli-spelling.js';
|
|
3
|
+
import { commentText } from '../../../core/comment-text.js';
|
|
4
|
+
import { eventFilterFromTreeLeaf } from '../../../core/event-filters.js';
|
|
5
|
+
import { eventDetail, eventTarget, libraryRequiredForEvent, refuseUnverifiedByoa, resolveEvent } from '../../../core/event-subscription.js';
|
|
6
|
+
import { Expr } from '../../../core/expr.js';
|
|
7
|
+
import { tsLiteral, tsObject, tsOptions } from '../../../core/ts-literal.js';
|
|
8
|
+
const EVENT_PREFIX = 'uipath.connector.event.';
|
|
9
|
+
/** `<connector>.<event>` after the prefix, split on the last dot; undefined for a name with no event. */
|
|
10
|
+
export function splitEvent(typeName) {
|
|
11
|
+
if (!typeName.startsWith(EVENT_PREFIX))
|
|
12
|
+
return undefined;
|
|
13
|
+
const rest = typeName.slice(EVENT_PREFIX.length);
|
|
14
|
+
const dot = rest.lastIndexOf('.');
|
|
15
|
+
if (dot <= 0)
|
|
16
|
+
return undefined;
|
|
17
|
+
return { connector: rest.slice(0, dot), event: rest.slice(dot + 1) };
|
|
18
|
+
}
|
|
19
|
+
function serializeEventWait(spec, ctx) {
|
|
20
|
+
const sub = spec.subscription;
|
|
21
|
+
const site = `waitForEvent "${ctx.step}"`;
|
|
22
|
+
if (!ctx.library)
|
|
23
|
+
throw libraryRequiredForEvent(ctx.events, sub, ctx.nodeId, site, site);
|
|
24
|
+
const rc = resolveEvent(ctx.library, 'event', sub, site);
|
|
25
|
+
refuseUnverifiedByoa(ctx.events, site, sub);
|
|
26
|
+
const detail = eventDetail(rc, sub, ctx.nodeId, ctx.events, site);
|
|
27
|
+
// The platform reaches the connection through the flow's `bindings[]`, as for
|
|
28
|
+
// a connector operation (board G-19). `eventDetail` refused a node without
|
|
29
|
+
// both labels, so both resolve here.
|
|
30
|
+
const target = eventTarget(sub);
|
|
31
|
+
const connectionId = ctx.resolveBinding(sub.connection, 'connection', site, target);
|
|
32
|
+
const folderKey = ctx.resolveBinding(sub.folder, 'folder', site, target);
|
|
33
|
+
ctx.bindConnection({
|
|
34
|
+
model: rc.v1def?.model,
|
|
35
|
+
connectorKey: rc.connectorKey, connectionId: connectionId,
|
|
36
|
+
...(folderKey !== undefined ? { folderKey } : {}),
|
|
37
|
+
});
|
|
38
|
+
// The node is the event the author named (`rc.nodeType`); a definition naming
|
|
39
|
+
// another is written under that name, so the node and its definitions[] entry agree.
|
|
40
|
+
const v1def = rc.v1def;
|
|
41
|
+
const def = v1def.nodeType === rc.nodeType ? v1def : { ...v1def, nodeType: rc.nodeType };
|
|
42
|
+
return {
|
|
43
|
+
definition: { source: 'snapshot', nodeType: def.nodeType, version: def.version, manifest: def },
|
|
44
|
+
inputs: { detail },
|
|
45
|
+
// A wait continues on `output`, as every connector node does.
|
|
46
|
+
port: 'output',
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/** A `where` value as decompile reads it: an expression back to an `Expr`, wire text as text, any other JSON as is. */
|
|
50
|
+
function liftValue(v, ctx) {
|
|
51
|
+
const raw = Array.isArray(v) ? undefined : ctx.sourceText(v);
|
|
52
|
+
if (raw === undefined)
|
|
53
|
+
return v;
|
|
54
|
+
return raw.startsWith('=js:') ? new Expr(raw.slice(4)) : raw;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The subscription back from the node: the `where` scope from
|
|
58
|
+
* `detail.eventParameters`, which carries it verbatim, and the filters from the
|
|
59
|
+
* filter tree inside `detail.configuration`. Both are also encoded into the
|
|
60
|
+
* generated `filterExpression`, but reading the structured copies avoids parsing
|
|
61
|
+
* a string compile generated.
|
|
62
|
+
*/
|
|
63
|
+
function liftEventWait(node, ctx) {
|
|
64
|
+
const n = node;
|
|
65
|
+
const split = splitEvent(n.type);
|
|
66
|
+
if (!split)
|
|
67
|
+
return undefined;
|
|
68
|
+
const detail = (n.inputs?.detail ?? undefined);
|
|
69
|
+
const where = Object.entries((detail?.eventParameters ?? {}))
|
|
70
|
+
.filter(([, v]) => v !== undefined && v !== null && String(v) !== '')
|
|
71
|
+
.map(([k, v]) => [k, liftValue(v, ctx)]);
|
|
72
|
+
// The filter tree lives inside the `=jsonString:` configuration blob, and so
|
|
73
|
+
// does the one fact that says whether `detail.objectName` was the AUTHOR's
|
|
74
|
+
// choice: a GENERIC event (`activityType: GenericTrigger`) carries the object
|
|
75
|
+
// the author named, a curated one carries its built-in object.
|
|
76
|
+
let filters = [];
|
|
77
|
+
let generic = false;
|
|
78
|
+
const config = detail?.configuration;
|
|
79
|
+
if (typeof config === 'string' && config.startsWith('=jsonString:')) {
|
|
80
|
+
try {
|
|
81
|
+
const parsed = JSON.parse(config.slice('=jsonString:'.length));
|
|
82
|
+
generic = parsed?.essentialConfiguration?.instanceParameters?.activityType === 'GenericTrigger';
|
|
83
|
+
const rows = (parsed?.essentialConfiguration?.filter?.filters ?? []);
|
|
84
|
+
filters = rows
|
|
85
|
+
.map((f) => eventFilterFromTreeLeaf(f))
|
|
86
|
+
.filter((f) => f !== undefined);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// A hand-edited blob is not ours to guess at — the scope and identity
|
|
90
|
+
// still recover, and the filters simply do not come back.
|
|
91
|
+
filters = [];
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const object = generic && typeof detail?.objectName === 'string' && detail.objectName !== ''
|
|
95
|
+
? detail.objectName
|
|
96
|
+
: undefined;
|
|
97
|
+
const connection = typeof detail?.connectionId === 'string' ? detail.connectionId : undefined;
|
|
98
|
+
const folder = typeof detail?.connectionFolderKey === 'string' ? detail.connectionFolderKey : undefined;
|
|
99
|
+
// A connection the file never picked (`connectionId: null`): the source cannot
|
|
100
|
+
// invent labels for it, and compile and check refuse a node without both
|
|
101
|
+
// (BINDING_MISSING, flow-builder-sdk#890), so the source says so at the node
|
|
102
|
+
// and the run warns once.
|
|
103
|
+
const missing = missingBindingLabels({ connection, folder });
|
|
104
|
+
if (missing.length > 0) {
|
|
105
|
+
ctx.warn(`node ${tsLiteral(n.id)} (${n.type}) has no ${missing.join(' or ')} in the .flow (an unbound draft). `
|
|
106
|
+
+ `The source leaves ${missing.length > 1 ? 'them' : 'it'} out, and compile and check refuse it (BINDING_MISSING) `
|
|
107
|
+
+ `until it names both labels: run \`${prepareCommand(split.connector, split.event)}\`, then pass `
|
|
108
|
+
+ `\`connection\` and \`folder\`.`);
|
|
109
|
+
}
|
|
110
|
+
return { kind: 'waitEvent', ...split, where, object, filters, connection, folder, missing };
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* `waitForEvent({ … })`, the stringly form: the subscription as one object. The
|
|
114
|
+
* unbound marker sits inside the call, after the object, where the lift has
|
|
115
|
+
* always put it.
|
|
116
|
+
*/
|
|
117
|
+
function printEventWait(l, ctx) {
|
|
118
|
+
const value = (v) => (v instanceof Expr ? ctx.value(v) : ctx.json(v));
|
|
119
|
+
const filters = l.filters.map((f) => tsOptions([
|
|
120
|
+
['field', tsLiteral(f.field)],
|
|
121
|
+
[f.operator, typeof f.value === 'string' ? tsLiteral(f.value) : JSON.stringify(f.value)],
|
|
122
|
+
]));
|
|
123
|
+
const subscription = tsOptions([
|
|
124
|
+
['connector', tsLiteral(l.connector)],
|
|
125
|
+
['event', tsLiteral(l.event)],
|
|
126
|
+
['where', l.where.length ? tsObject(l.where.map(([k, v]) => [k, value(v)])) : undefined],
|
|
127
|
+
['object', l.object !== undefined ? tsLiteral(l.object) : undefined],
|
|
128
|
+
['filters', filters.length ? `[${filters.join(', ')}]` : undefined],
|
|
129
|
+
['connection', l.connection !== undefined ? tsLiteral(l.connection) : undefined],
|
|
130
|
+
['folder', l.folder !== undefined ? tsLiteral(l.folder) : undefined],
|
|
131
|
+
]);
|
|
132
|
+
const marker = l.missing.length > 0
|
|
133
|
+
? ` /* TODO: bind ${commentText(l.missing.join(' and '))}; check refuses this node until it has both labels (BINDING_MISSING) */`
|
|
134
|
+
: '';
|
|
135
|
+
return { factory: 'waitForEvent', positional: [subscription + marker] };
|
|
136
|
+
}
|
|
137
|
+
const eventWait10 = {
|
|
138
|
+
kind: 'waitEvent', typeName: `${EVENT_PREFIX}*`, version: '1.0.0', factory: 'waitForEvent',
|
|
139
|
+
definition: 'per-tenant',
|
|
140
|
+
// An event wait resumes or it does not: it has no error port to route.
|
|
141
|
+
errorPort: false,
|
|
142
|
+
serialize: (spec, ctx) => serializeEventWait(spec, ctx),
|
|
143
|
+
deserialize: (node, ctx) => liftEventWait(node, ctx),
|
|
144
|
+
print: (spec, ctx) => printEventWait(spec, ctx),
|
|
145
|
+
check: (spec, ctx) => ctx.eventSubscription(spec.subscription, `waitForEvent "${ctx.step}"`),
|
|
146
|
+
namedNode: (spec) => {
|
|
147
|
+
const sub = spec.subscription;
|
|
148
|
+
return {
|
|
149
|
+
typeName: `${EVENT_PREFIX}${sub?.connector}.${sub?.event}`,
|
|
150
|
+
...(typeof sub?.version === 'string' ? { version: sub.version } : {}),
|
|
151
|
+
};
|
|
152
|
+
},
|
|
153
|
+
expressions: () => [],
|
|
154
|
+
output: () => ({ kind: 'opaque' }),
|
|
155
|
+
};
|
|
156
|
+
export const eventWaitNodeType = {
|
|
157
|
+
kind: 'waitEvent', typeName: `${EVENT_PREFIX}*`, typeNamePrefix: EVENT_PREFIX, factory: 'waitForEvent',
|
|
158
|
+
typeNameMatches: (typeName) => splitEvent(typeName) !== undefined,
|
|
159
|
+
versionedByDefinition: true,
|
|
160
|
+
defaultVersion: eventWait10.version,
|
|
161
|
+
versions: { [eventWait10.version]: eventWait10 },
|
|
162
|
+
};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/** The connector node family, as flow-node-registry.ts lists it: an Integration Service connector operation. */
|
|
1
|
+
/** The connector node family, as flow-node-registry.ts lists it: an Integration Service connector operation, and a mid-flow event wait. */
|
|
2
2
|
import type { FlowNodeFamily } from '../../../flow-node-type.js';
|
|
3
3
|
export declare const connectorFlowFamily: FlowNodeFamily;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { eventWaitNodeType } from './event-wait.js';
|
|
1
2
|
import { connectorNodeType } from './node-type.js';
|
|
2
3
|
export const connectorFlowFamily = {
|
|
3
4
|
id: 'connector',
|
|
4
5
|
title: 'Integration Service connector',
|
|
5
|
-
types: [connectorNodeType],
|
|
6
|
-
// Its rules' codes are the pipeline's, shared with
|
|
6
|
+
types: [connectorNodeType, eventWaitNodeType],
|
|
7
|
+
// Its rules' codes are the pipeline's, shared with the triggers (ctx.connector, ctx.eventSubscription).
|
|
7
8
|
diagnostics: {},
|
|
8
9
|
};
|