@uipath/maestro-builder-sdk 6.8.4 → 6.8.6
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 +1 -1
- package/dist/check-cli.js +13 -16
- package/dist/check.d.ts +20 -0
- package/dist/check.js +46 -12
- package/dist/cli/commands/flow.js +2 -0
- package/dist/cli-library.d.ts +21 -0
- package/dist/cli-library.js +56 -0
- package/dist/compile-cli.js +12 -11
- package/dist/core/cli-spelling.d.ts +10 -0
- package/dist/core/cli-spelling.js +24 -0
- package/dist/core/library.d.ts +20 -0
- package/dist/core/library.js +97 -20
- package/dist/core/lookups.js +4 -2
- package/dist/core/node-classes.d.ts +8 -0
- package/dist/core/node-classes.js +27 -3
- package/dist/core/typed-raw-node.d.ts +97 -0
- package/dist/core/typed-raw-node.js +199 -0
- package/dist/registry/generators/markdown/sections/lookups.js +20 -5
- package/dist/registry/library-build/build.js +2 -1
- package/dist/registry/library-build/context.d.ts +26 -0
- package/dist/registry/library-build/context.js +25 -12
- package/dist/registry/library-build/events.js +3 -2
- package/dist/registry/prepare/actions.d.ts +42 -5
- package/dist/registry/prepare/actions.js +90 -16
- package/dist/registry/prepare/connections.js +2 -3
- package/dist/registry/prepare/event.d.ts +15 -6
- package/dist/registry/prepare/event.js +19 -11
- package/dist/registry/prepare/ledger.d.ts +3 -1
- package/dist/registry/prepare/ledger.js +55 -5
- package/dist/registry/prepare/messages.d.ts +40 -4
- package/dist/registry/prepare/messages.js +97 -15
- package/dist/registry/prepare/pipeline.js +19 -10
- package/dist/registry/prepare/resolve.d.ts +27 -2
- package/dist/registry/prepare/resolve.js +71 -8
- package/dist/registry/uip/envelope.d.ts +44 -9
- package/dist/registry/uip/envelope.js +80 -0
- package/dist/serialize.js +82 -9
- package/lib/library-json.samples/index.json +18 -0
- package/lib/library-json.samples/uipath-getresponse-getresponse/send-newsletter@1.0.0.json +159 -0
- package/lib/library-json.samples/uipath-getresponse-getresponse/send-newsletter@1.0.0.v1def.json +323 -0
- package/lib/library-json.samples/uipath-salesforce-slack/create-group-direct-message@1.0.0.json +149 -0
- package/lib/library-json.samples/uipath-salesforce-slack/create-group-direct-message@1.0.0.v1def.json +237 -0
- package/package.json +2 -2
package/dist/api-index.md
CHANGED
|
@@ -201,7 +201,7 @@ could hand you spans for a version you do not have.
|
|
|
201
201
|
| `QueueItemInputs` | interface | `dist/core/actions.d.ts:1950-2018` | Option shapes | 10 field(s) |
|
|
202
202
|
| `QueuePriority` | type | `dist/core/actions.d.ts:1948-1949` | Supporting types | The three priorities an Orchestrator queue item can carry. |
|
|
203
203
|
| `ran` | function | `dist/core/expr.d.ts:90-136` | Expressions and types | Did this step run? |
|
|
204
|
-
| `rawNode` | function | `dist/core/node-classes.d.ts:180-
|
|
204
|
+
| `rawNode` | function | `dist/core/node-classes.d.ts:180-242` | Supporting types | Place a node this SDK has no factory for, carrying its definition verbatim (`uipath.exotic.thing@2.1` and the manifest… |
|
|
205
205
|
| `RawReference` | type | `dist/core/lookups.d.ts:190-198` | Supporting types | |
|
|
206
206
|
| `refuseLookupToken` | function | `dist/core/lookups.d.ts:207-226` | Generated descriptors | Refuse a lookup token where the family cannot resolve one. |
|
|
207
207
|
| `RemoteA2aToolRef` | interface | `dist/core/actions.d.ts:1649-1669` | Supporting types | A remote A2A (agent-to-agent) endpoint the agent may delegate to. |
|
package/dist/check-cli.js
CHANGED
|
@@ -6,15 +6,15 @@
|
|
|
6
6
|
* Prints diagnostics; exits non-zero if any errors.
|
|
7
7
|
*/
|
|
8
8
|
import { existsSync } from 'node:fs';
|
|
9
|
-
import { join } from 'node:path';
|
|
10
9
|
import { resolveFlowFile, loadBuiltFlow } from './load.js';
|
|
11
10
|
import { check } from './check.js';
|
|
12
|
-
import {
|
|
11
|
+
import { eventResolveErrorFor, openCliLibrary } from './cli-library.js';
|
|
13
12
|
import { Bindings } from './bindings.js';
|
|
14
13
|
import { connectorInputErrors } from './serialize.js';
|
|
15
14
|
import { runWhenInvokedDirectly } from './cli-run.js';
|
|
16
15
|
import { ensureTypeScriptRuntime } from './node-runtime.js';
|
|
17
|
-
|
|
16
|
+
/** Flags that take a value, so the value is never read as the source argument. */
|
|
17
|
+
const VALUE_FLAGS = new Set(['--library', '--bindings', '--connectors-local']);
|
|
18
18
|
/**
|
|
19
19
|
* Library lookups for the checks whose facts live in the connector corpus:
|
|
20
20
|
* event parameters and measured list envelopes.
|
|
@@ -23,33 +23,30 @@ import { resolveLibraryDir } from './registry/cache.js';
|
|
|
23
23
|
* function of the built flow — with no library it simply cannot know whether an
|
|
24
24
|
* empty `where` is correct, so it fails open (a warning, not an error). Given a
|
|
25
25
|
* library, `compile` and `check` make the same call with the facts in hand.
|
|
26
|
-
* `--library`
|
|
26
|
+
* `--library` and `--connectors-local` mirror compile-cli's flags and defaults,
|
|
27
|
+
* through the same `openCliLibrary`.
|
|
27
28
|
*/
|
|
28
29
|
function checkOpts(argv) {
|
|
29
|
-
const flag = argv.indexOf('--library');
|
|
30
|
-
const libDir = resolveLibraryDir(flag >= 0 ? argv[flag + 1] : undefined);
|
|
31
30
|
// bindings.json beside the source, exactly as compile-cli finds it — so the
|
|
32
31
|
// own-name refusal compile would raise is reported here first.
|
|
33
32
|
const bindFlag = argv.indexOf('--bindings');
|
|
34
33
|
const bindFile = bindFlag >= 0 ? argv[bindFlag + 1] : (existsSync('bindings.json') ? 'bindings.json' : undefined);
|
|
35
34
|
const bindings = bindFile ? new Bindings(bindFile) : undefined;
|
|
36
|
-
if (!libDir || !existsSync(join(libDir, 'index.json')))
|
|
37
|
-
return bindings ? { bindings } : {};
|
|
38
35
|
// The overlay matters here as much as it does for compile: `resolutions.json`
|
|
39
36
|
// lives in it, and without it every already-resolved lookup would be reported
|
|
40
37
|
// as still owed — telling the author to re-run a `prepare` they already ran.
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
:
|
|
44
|
-
const library = new Library(libDir, { overlayDir });
|
|
38
|
+
const library = openCliLibrary(argv);
|
|
39
|
+
if (!library)
|
|
40
|
+
return bindings ? { bindings } : {};
|
|
45
41
|
return {
|
|
42
|
+
eventResolveError: eventResolveErrorFor(library),
|
|
46
43
|
eventParams: (connector, event, object) => {
|
|
47
44
|
try {
|
|
48
45
|
return library.eventParameterFields(connector, event, object);
|
|
49
46
|
}
|
|
50
47
|
catch {
|
|
51
|
-
// An unresolvable operation is EVENT_UNKNOWN's business
|
|
52
|
-
//
|
|
48
|
+
// An unresolvable operation is EVENT_UNKNOWN's business, reported from
|
|
49
|
+
// `eventResolveError` above; here it just means "unknown".
|
|
53
50
|
return undefined;
|
|
54
51
|
}
|
|
55
52
|
},
|
|
@@ -96,9 +93,9 @@ function checkOpts(argv) {
|
|
|
96
93
|
};
|
|
97
94
|
}
|
|
98
95
|
export async function run(argv) {
|
|
99
|
-
const arg = argv.find((a) => !a.startsWith('--')) ?? argv[0];
|
|
96
|
+
const arg = argv.find((a, i) => !a.startsWith('--') && !VALUE_FLAGS.has(argv[i - 1])) ?? argv[0];
|
|
100
97
|
if (!arg || arg === '-h' || arg === '--help') {
|
|
101
|
-
console.error('usage: check [--library <dir>] [--bindings <file>] <flow.ts | BaseName>');
|
|
98
|
+
console.error('usage: check [--library <dir>] [--connectors-local <dir>] [--bindings <file>] <flow.ts | BaseName>');
|
|
102
99
|
return 2;
|
|
103
100
|
}
|
|
104
101
|
const file = resolveFlowFile(arg);
|
package/dist/check.d.ts
CHANGED
|
@@ -75,6 +75,26 @@ export interface CheckOpts {
|
|
|
75
75
|
* and cannot be slowed down by one.
|
|
76
76
|
*/
|
|
77
77
|
eventParams?: (connector: string, event: string, object?: string) => EventParamFields | undefined;
|
|
78
|
+
/**
|
|
79
|
+
* What `compile` would refuse a connector event subscription with, or
|
|
80
|
+
* `undefined` when the library resolves it. `kind` is the node form compile
|
|
81
|
+
* asks for: `trigger` for `onEvent` (the flow's trigger and every entry point),
|
|
82
|
+
* `event` for `waitForEvent`. Supplied by the CLIs from the same
|
|
83
|
+
* `library.resolveEvent(kind, connector, event, version, object)` call
|
|
84
|
+
* `serialize` makes (`eventResolveErrorFor` in `cli-library.ts`), so the two
|
|
85
|
+
* cannot disagree; omitted (no library) → not checked.
|
|
86
|
+
*
|
|
87
|
+
* Before this hook `check` could only warn EVENT_UNPREPARED here, because
|
|
88
|
+
* `eventParams` answers `undefined` for "cannot resolve" and for "no facts"
|
|
89
|
+
* alike: it printed `0 error(s)` on a source `compile` then refused
|
|
90
|
+
* (2026-09-23, `skill-flow-datafabric-trigger-lifecycle`, v2 commands 31 and 46).
|
|
91
|
+
*/
|
|
92
|
+
eventResolveError?: (kind: 'trigger' | 'event', subscription: {
|
|
93
|
+
connector: string;
|
|
94
|
+
event: string;
|
|
95
|
+
version?: string;
|
|
96
|
+
object?: string;
|
|
97
|
+
}) => string | undefined;
|
|
78
98
|
/**
|
|
79
99
|
* Resolve a connector event's FILTERABLE field names — the vocabulary a
|
|
80
100
|
* `filters` leaf may name — sourced the same way as `eventParams`. A GENERIC
|
package/dist/check.js
CHANGED
|
@@ -15,7 +15,7 @@ import { hitlDerivesOutcomeHandles, hitlExitModel, hitlRoutesPerOutcome } from '
|
|
|
15
15
|
import { declaredExits, hasExits, stopsAtEnd } from './core/step-ports.js';
|
|
16
16
|
import { FLOW_SCHEMA_VERSIONS } from './format-profile.js';
|
|
17
17
|
import { compareSchemaVersions, schemaVersionRefusal } from './schema-version.js';
|
|
18
|
-
import { prepareCommand } from './core/cli-spelling.js';
|
|
18
|
+
import { prepareCommand, resolveFlagArg } from './core/cli-spelling.js';
|
|
19
19
|
export function check(built, opts = {}) {
|
|
20
20
|
return checkFlow(built, [], opts);
|
|
21
21
|
}
|
|
@@ -529,7 +529,7 @@ function checkFlow(built, ancestors, opts = {}) {
|
|
|
529
529
|
...(suggestion ? { suggestion } : {}),
|
|
530
530
|
});
|
|
531
531
|
checkEventSubscription(s.spec.subscription, `waitForEvent "${stepName}"`, push('error'), push('warning'), eventParamsFor(s.spec.subscription, opts));
|
|
532
|
-
checkEventResolution(s.spec.subscription, `waitForEvent "${stepName}"`, opts, push('error'), push('warning'), eventParamsFor(s.spec.subscription, opts));
|
|
532
|
+
checkEventResolution(s.spec.subscription, `waitForEvent "${stepName}"`, 'event', opts, push('error'), push('warning'), eventParamsFor(s.spec.subscription, opts));
|
|
533
533
|
}
|
|
534
534
|
if (s.spec.kind === 'subflow') {
|
|
535
535
|
// Only a real child goes in the map: a missing one is reported by
|
|
@@ -1264,6 +1264,9 @@ function checkFlow(built, ancestors, opts = {}) {
|
|
|
1264
1264
|
function checkConnectorLookups(step, spec, opts, diags) {
|
|
1265
1265
|
const specs = opts.lookups?.(spec.key, spec.action, spec.version, spec.object);
|
|
1266
1266
|
const resolutions = opts.resolutions ?? {};
|
|
1267
|
+
// One LOOKUP_LITERAL_ID per field: a multiselect of three pasted ids is one
|
|
1268
|
+
// mistake with one remedy, not three.
|
|
1269
|
+
const literalWarned = new Set();
|
|
1267
1270
|
const visit = (value, path) => {
|
|
1268
1271
|
if (isLookupToken(value)) {
|
|
1269
1272
|
// A match key the field does not declare is certainly wrong, and the
|
|
@@ -1304,7 +1307,17 @@ function checkConnectorLookups(step, spec, opts, diags) {
|
|
|
1304
1307
|
return;
|
|
1305
1308
|
}
|
|
1306
1309
|
if (Array.isArray(value)) {
|
|
1307
|
-
|
|
1310
|
+
// An element is named by the WIRE name the lookup table is keyed on —
|
|
1311
|
+
// `users[*]`, `contacts[*].id` — not by its index. `users.0` matched no
|
|
1312
|
+
// spec, so a pasted id, a token with a bad match key, or a runtime value
|
|
1313
|
+
// inside a multiselect passed every rule below unseen. A name that already
|
|
1314
|
+
// is the array's (a flat `'users[*]'` key, or a field typed as an array)
|
|
1315
|
+
// stays as it is.
|
|
1316
|
+
const last = path[path.length - 1] ?? '';
|
|
1317
|
+
const element = last.endsWith('[*]') || specs?.[path.join('.')]
|
|
1318
|
+
? path
|
|
1319
|
+
: [...path.slice(0, -1), `${last}[*]`];
|
|
1320
|
+
value.forEach((v) => visit(v, element));
|
|
1308
1321
|
return;
|
|
1309
1322
|
}
|
|
1310
1323
|
if (value && typeof value === 'object' && !(value instanceof Expr)) {
|
|
@@ -1329,6 +1342,9 @@ function checkConnectorLookups(step, spec, opts, diags) {
|
|
|
1329
1342
|
const literal = value instanceof Expr ? value.value : value;
|
|
1330
1343
|
if (typeof literal !== 'string')
|
|
1331
1344
|
return;
|
|
1345
|
+
if (literalWarned.has(field))
|
|
1346
|
+
return;
|
|
1347
|
+
literalWarned.add(field);
|
|
1332
1348
|
const { target, call } = lookupPhrases(field, lookup, spec);
|
|
1333
1349
|
diags.push({
|
|
1334
1350
|
level: 'warning',
|
|
@@ -1340,7 +1356,7 @@ function checkConnectorLookups(step, spec, opts, diags) {
|
|
|
1340
1356
|
+ `compile nor deploy can tell. Write what you mean and resolve it late:\n`
|
|
1341
1357
|
+ ` ${call}\n`
|
|
1342
1358
|
+ `then record the value once with \`${prepareCommand(spec.key)} `
|
|
1343
|
-
+ `${spec.action} --resolve ${field
|
|
1359
|
+
+ `${spec.action} --resolve ${resolveFlagArg(field, lookup.by[0] ?? 'id', '<value>')}\`.`,
|
|
1344
1360
|
step,
|
|
1345
1361
|
});
|
|
1346
1362
|
};
|
|
@@ -1557,7 +1573,7 @@ function runtimeLookupDiagnostic(step, spec, field, lookup, value) {
|
|
|
1557
1573
|
+ `neither compile nor validate can tell. Resolve the value you know instead:\n`
|
|
1558
1574
|
+ ` ${call}\n`
|
|
1559
1575
|
+ `then record it once with \`${prepareCommand(spec.key, spec.action)} `
|
|
1560
|
-
+ `--resolve ${field
|
|
1576
|
+
+ `--resolve ${resolveFlagArg(field, lookup.by[0] ?? 'id', '<value>')}\`; or read the id from a step whose `
|
|
1561
1577
|
+ `output returned one (\`${lookup.value}\`).`
|
|
1562
1578
|
+ (isEmail
|
|
1563
1579
|
? `\n\`${single.raw}\` reads \`${leaf}\`, an e-mail address, not a \`${target}\`.`
|
|
@@ -1939,12 +1955,18 @@ function eventParamsFor(subscription, opts) {
|
|
|
1939
1955
|
}
|
|
1940
1956
|
/**
|
|
1941
1957
|
* The event-side resolution rules — the trigger twin of `checkConnectorLookups`,
|
|
1942
|
-
* plus
|
|
1958
|
+
* plus three rules with no activity counterpart:
|
|
1943
1959
|
*
|
|
1944
|
-
* - `
|
|
1945
|
-
*
|
|
1946
|
-
*
|
|
1947
|
-
*
|
|
1960
|
+
* - `EVENT_UNKNOWN` — an ERROR: a library is loaded and it cannot resolve the
|
|
1961
|
+
* subscription, so `compile` will refuse it. The message is the resolver's
|
|
1962
|
+
* own (`eventResolveError`), the same text compile throws. Checked first; when
|
|
1963
|
+
* it fires, EVENT_UNPREPARED and the filter rules below are skipped for this
|
|
1964
|
+
* subscription, since there is no entry for them to read.
|
|
1965
|
+
* - `EVENT_UNPREPARED` — the event RESOLVES but the library knows nothing
|
|
1966
|
+
* about its parameters (or no `eventResolveError` resolver was supplied):
|
|
1967
|
+
* the fallback case for a connector the shipped library was not generated
|
|
1968
|
+
* against. A warning, because compile still succeeds. Prints the one
|
|
1969
|
+
* command that makes the event checkable.
|
|
1948
1970
|
* - `EVENT_FILTER_UNKNOWN_FIELD` — a filter leaf naming a field outside the
|
|
1949
1971
|
* prepared vocabulary is an ERROR, not a warning, because the platform's own
|
|
1950
1972
|
* configure DROPS an unmatched leaf and reports Success: the deployed
|
|
@@ -1956,7 +1978,7 @@ function eventParamsFor(subscription, opts) {
|
|
|
1956
1978
|
* (compile repeats it as the hard failure), with the recorded answer looked up
|
|
1957
1979
|
* under the event-form node type — the form `prepare` stores.
|
|
1958
1980
|
*/
|
|
1959
|
-
function checkEventResolution(subscription, where, opts, error, warn, eventParams) {
|
|
1981
|
+
function checkEventResolution(subscription, where, kind, opts, error, warn, eventParams) {
|
|
1960
1982
|
const sub = subscription;
|
|
1961
1983
|
if (!sub || typeof sub.connector !== 'string' || typeof sub.event !== 'string'
|
|
1962
1984
|
|| sub.connector === '' || sub.event === '')
|
|
@@ -1967,6 +1989,18 @@ function checkEventResolution(subscription, where, opts, error, warn, eventParam
|
|
|
1967
1989
|
// by interpolating them into template literals, which accept anything.
|
|
1968
1990
|
const connectorKey = sub.connector;
|
|
1969
1991
|
const eventName = sub.event;
|
|
1992
|
+
// Exactly what `serialize` passes to `resolveEvent` — `sub.version` and
|
|
1993
|
+
// `sub.object` as the author wrote them — so the verdict is compile's.
|
|
1994
|
+
const unresolved = opts.eventResolveError?.(kind, {
|
|
1995
|
+
connector: connectorKey,
|
|
1996
|
+
event: eventName,
|
|
1997
|
+
...(typeof sub.version === 'string' ? { version: sub.version } : {}),
|
|
1998
|
+
...(typeof sub.object === 'string' ? { object: sub.object } : {}),
|
|
1999
|
+
});
|
|
2000
|
+
if (unresolved !== undefined) {
|
|
2001
|
+
error('EVENT_UNKNOWN', `${where}: ${unresolved}`);
|
|
2002
|
+
return;
|
|
2003
|
+
}
|
|
1970
2004
|
const object = typeof sub.object === 'string' && sub.object !== '' ? sub.object : undefined;
|
|
1971
2005
|
if (eventParams?.objectUnprepared && object) {
|
|
1972
2006
|
// The generic template answers for every object, so resolution SUCCEEDS
|
|
@@ -2243,7 +2277,7 @@ function checkTrigger(spec, diags, opts = {}) {
|
|
|
2243
2277
|
if (spec?.kind === 'event') {
|
|
2244
2278
|
const sub = spec.subscription;
|
|
2245
2279
|
checkEventSubscription(sub, 'trigger', at, push('warning'), eventParamsFor(sub, opts));
|
|
2246
|
-
checkEventResolution(sub, 'trigger', opts, at, push('warning'), eventParamsFor(sub, opts));
|
|
2280
|
+
checkEventResolution(sub, 'trigger', 'trigger', opts, at, push('warning'), eventParamsFor(sub, opts));
|
|
2247
2281
|
return;
|
|
2248
2282
|
}
|
|
2249
2283
|
// manual() is the explicit spelling of the default — nothing to validate.
|
|
@@ -25,6 +25,7 @@ export function registerFlowCommands(program) {
|
|
|
25
25
|
.option('--source', 'check authored TypeScript')
|
|
26
26
|
.option('--compiled', 'check the emitted artifact')
|
|
27
27
|
.option('--library <dir>', 'connector library directory')
|
|
28
|
+
.option('--connectors-local <dir>', 'connection-resolved connector overlay')
|
|
28
29
|
.option('--json', 'print compiled-check diagnostics as JSON')
|
|
29
30
|
.option('--uip', 'resolve missing schemas with the UiPath CLI')
|
|
30
31
|
.option('--max-errors <count>', 'stop after this many compiled-check errors')
|
|
@@ -50,6 +51,7 @@ export function registerFlowCommands(program) {
|
|
|
50
51
|
if (mode === 'source') {
|
|
51
52
|
const argv = [input];
|
|
52
53
|
addValue(argv, '--library', options.library);
|
|
54
|
+
addValue(argv, '--connectors-local', options.connectorsLocal);
|
|
53
55
|
await invoke('flow check', () => loadTool(import.meta.url, '../../check-cli.js'), argv);
|
|
54
56
|
return;
|
|
55
57
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { CheckOpts } from './check.js';
|
|
2
|
+
import { Library } from './library.js';
|
|
3
|
+
/**
|
|
4
|
+
* The library for this invocation, or `undefined` when there is none.
|
|
5
|
+
*
|
|
6
|
+
* `--library <dir>`, else `$FLOW_SDK_LIBRARY_JSON`, else the cache `registry
|
|
7
|
+
* pull` filled (`resolveLibraryDir`). The overlay is `--connectors-local <dir>`,
|
|
8
|
+
* else `./connectors-local` when it has an `index.json` — the directory
|
|
9
|
+
* `registry prepare` writes by default.
|
|
10
|
+
*/
|
|
11
|
+
export declare function openCliLibrary(argv: readonly string[]): Library | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* `check`'s EVENT_UNKNOWN verdict: the message `library.resolveEvent` throws
|
|
14
|
+
* for a subscription, or `undefined` when it resolves.
|
|
15
|
+
*
|
|
16
|
+
* This is the call `serialize` makes for `onEvent` (kind `trigger`, for the
|
|
17
|
+
* flow's trigger and each entry point) and for `waitForEvent` (kind `event`),
|
|
18
|
+
* with the subscription's own `version` and `object`, so check reports an error
|
|
19
|
+
* exactly when compile would throw, with the same text.
|
|
20
|
+
*/
|
|
21
|
+
export declare function eventResolveErrorFor(library: Library): NonNullable<CheckOpts['eventResolveError']>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli-library — the connector library a flow CLI opens, and the event verdict
|
|
3
|
+
* `check` shares with `compile`.
|
|
4
|
+
*
|
|
5
|
+
* `check-cli` and `compile-cli` used to open the library each in their own way:
|
|
6
|
+
* compile honoured `--connectors-local <dir>`, check ignored the flag and only
|
|
7
|
+
* auto-detected `./connectors-local`. As long as check only WARNED from the
|
|
8
|
+
* library that was harmless. Now check reports EVENT_UNKNOWN as an ERROR when
|
|
9
|
+
* the library cannot resolve an event, so a different library would make check
|
|
10
|
+
* refuse a source compile accepts. Both CLIs open it here instead.
|
|
11
|
+
*/
|
|
12
|
+
import { existsSync } from 'node:fs';
|
|
13
|
+
import { join } from 'node:path';
|
|
14
|
+
import { Library } from './library.js';
|
|
15
|
+
import { resolveLibraryDir } from './registry/cache.js';
|
|
16
|
+
/** The value after `name` in `argv`, or `undefined`. */
|
|
17
|
+
function flagValue(argv, name) {
|
|
18
|
+
const i = argv.indexOf(name);
|
|
19
|
+
return i >= 0 ? argv[i + 1] : undefined;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The library for this invocation, or `undefined` when there is none.
|
|
23
|
+
*
|
|
24
|
+
* `--library <dir>`, else `$FLOW_SDK_LIBRARY_JSON`, else the cache `registry
|
|
25
|
+
* pull` filled (`resolveLibraryDir`). The overlay is `--connectors-local <dir>`,
|
|
26
|
+
* else `./connectors-local` when it has an `index.json` — the directory
|
|
27
|
+
* `registry prepare` writes by default.
|
|
28
|
+
*/
|
|
29
|
+
export function openCliLibrary(argv) {
|
|
30
|
+
const libDir = resolveLibraryDir(flagValue(argv, '--library'));
|
|
31
|
+
if (!libDir || !existsSync(join(libDir, 'index.json')))
|
|
32
|
+
return undefined;
|
|
33
|
+
const overlayDir = flagValue(argv, '--connectors-local')
|
|
34
|
+
?? (existsSync(join('connectors-local', 'index.json')) ? 'connectors-local' : undefined);
|
|
35
|
+
return new Library(libDir, { overlayDir });
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* `check`'s EVENT_UNKNOWN verdict: the message `library.resolveEvent` throws
|
|
39
|
+
* for a subscription, or `undefined` when it resolves.
|
|
40
|
+
*
|
|
41
|
+
* This is the call `serialize` makes for `onEvent` (kind `trigger`, for the
|
|
42
|
+
* flow's trigger and each entry point) and for `waitForEvent` (kind `event`),
|
|
43
|
+
* with the subscription's own `version` and `object`, so check reports an error
|
|
44
|
+
* exactly when compile would throw, with the same text.
|
|
45
|
+
*/
|
|
46
|
+
export function eventResolveErrorFor(library) {
|
|
47
|
+
return (kind, sub) => {
|
|
48
|
+
try {
|
|
49
|
+
library.resolveEvent(kind, sub.connector, sub.event, sub.version, sub.object);
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
return error instanceof Error ? error.message : String(error);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
package/dist/compile-cli.js
CHANGED
|
@@ -22,8 +22,7 @@ import { basename, dirname, join, resolve } from 'node:path';
|
|
|
22
22
|
import { resolveFlowFile, loadBuiltFlow } from './load.js';
|
|
23
23
|
import { serializeWithSidecars, connectorInputErrors } from './serialize.js';
|
|
24
24
|
import { check } from './check.js';
|
|
25
|
-
import {
|
|
26
|
-
import { resolveLibraryDir } from './registry/cache.js';
|
|
25
|
+
import { eventResolveErrorFor, openCliLibrary } from './cli-library.js';
|
|
27
26
|
import { Bindings } from './bindings.js';
|
|
28
27
|
import { runWhenInvokedDirectly } from './cli-run.js';
|
|
29
28
|
import { connectionResources, mergeBindingsV2, readBindingsV2 } from './bindings-v2.js';
|
|
@@ -50,14 +49,11 @@ export async function run(argv) {
|
|
|
50
49
|
// --library, else $FLOW_SDK_LIBRARY_JSON, else the shared cache `registry pull`
|
|
51
50
|
// filled. Resolving through one helper is what stops `pull` and `compile`
|
|
52
51
|
// disagreeing about where the library is — they did, and a pulled library was
|
|
53
|
-
// invisible to this command.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
// connection-resolved field schemas. See core/library.ts (LibraryOpts.overlayDir).
|
|
59
|
-
const overlayDir = opt(argv, '--connectors-local')
|
|
60
|
-
?? (existsSync(join('connectors-local', 'index.json')) ? 'connectors-local' : undefined);
|
|
52
|
+
// invisible to this command. The `prepare-connector`/`prepare-trigger` overlay
|
|
53
|
+
// is `--connectors-local`, else `./connectors-local` (like bindings.json); it
|
|
54
|
+
// shadows the baked library with full, connection-resolved field schemas. See
|
|
55
|
+
// core/library.ts (LibraryOpts.overlayDir). `check-cli` opens it the same way.
|
|
56
|
+
//
|
|
61
57
|
// Only load the library when it's actually present; flows without connectors
|
|
62
58
|
// (e.g. HTTP-only) then compile fine even if the connector cache isn't built.
|
|
63
59
|
// A connector action with no library resolves to a clear error in serialize().
|
|
@@ -66,11 +62,16 @@ export async function run(argv) {
|
|
|
66
62
|
// event operation declares any event parameters) can only be answered from the
|
|
67
63
|
// library, and answering it from an assumption instead is what made
|
|
68
64
|
// EVENT_NO_SCOPE an error for HTTP Webhook, whose only correct `where` is empty.
|
|
69
|
-
const library =
|
|
65
|
+
const library = openCliLibrary(argv);
|
|
66
|
+
const bindFile = opt(argv, '--bindings') ?? (existsSync('bindings.json') ? 'bindings.json' : undefined);
|
|
70
67
|
const bindings = new Bindings(bindFile);
|
|
71
68
|
if (!noCheck) {
|
|
72
69
|
const diags = check(built, library
|
|
73
70
|
? {
|
|
71
|
+
// The refusal serialize() would throw for an event it cannot resolve,
|
|
72
|
+
// printed as `✗ [EVENT_UNKNOWN]` before stopping instead of hiding
|
|
73
|
+
// behind "1 error(s); not emitting".
|
|
74
|
+
eventResolveError: eventResolveErrorFor(library),
|
|
74
75
|
eventParams: (connector, event) => {
|
|
75
76
|
try {
|
|
76
77
|
return library.eventParameterFields(connector, event);
|
|
@@ -37,3 +37,13 @@ export declare function cliSpelling(env?: CliSpellingEnv): string;
|
|
|
37
37
|
* ```
|
|
38
38
|
*/
|
|
39
39
|
export declare function prepareCommand(...args: string[]): string;
|
|
40
|
+
/**
|
|
41
|
+
* The argument of one `--resolve <field>:<by>=<value>`, single-quoted when the
|
|
42
|
+
* shell would otherwise mangle it.
|
|
43
|
+
*
|
|
44
|
+
* @param field - The lookup field's wire name, e.g. `channel` or `users[*]`.
|
|
45
|
+
* @param by - The match key, e.g. `profile.email`.
|
|
46
|
+
* @param value - The value to match, or the `<value>` placeholder.
|
|
47
|
+
* @returns `channel:profile.email=d@x.com` as is; `'users[*]:profile.real_name=Coder Evals'` quoted.
|
|
48
|
+
*/
|
|
49
|
+
export declare function resolveFlagArg(field: string, by: string, value: string): string;
|
|
@@ -60,3 +60,27 @@ export function cliSpelling(env = ambientEnv()) {
|
|
|
60
60
|
export function prepareCommand(...args) {
|
|
61
61
|
return [cliSpelling(), 'registry', 'prepare', ...args.filter(Boolean)].join(' ');
|
|
62
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Characters that make a `--resolve` argument unsafe to paste unquoted. `[*]`
|
|
65
|
+
* is a shell glob (every multiselect wire name, `users[*]`), whitespace splits
|
|
66
|
+
* a real name (`Coder Evals`) into two arguments, and quotes, `$`, backslash
|
|
67
|
+
* and the control operators change what the shell hands over. The `<value>`
|
|
68
|
+
* placeholder a template prints is stripped before the test, so a template
|
|
69
|
+
* with nothing else unsafe keeps its bytes.
|
|
70
|
+
*/
|
|
71
|
+
const SHELL_UNSAFE = /[\s'"`$\\[\]*?;&|(){}!#~<>]/;
|
|
72
|
+
/**
|
|
73
|
+
* The argument of one `--resolve <field>:<by>=<value>`, single-quoted when the
|
|
74
|
+
* shell would otherwise mangle it.
|
|
75
|
+
*
|
|
76
|
+
* @param field - The lookup field's wire name, e.g. `channel` or `users[*]`.
|
|
77
|
+
* @param by - The match key, e.g. `profile.email`.
|
|
78
|
+
* @param value - The value to match, or the `<value>` placeholder.
|
|
79
|
+
* @returns `channel:profile.email=d@x.com` as is; `'users[*]:profile.real_name=Coder Evals'` quoted.
|
|
80
|
+
*/
|
|
81
|
+
export function resolveFlagArg(field, by, value) {
|
|
82
|
+
const arg = `${field}:${by}=${value}`;
|
|
83
|
+
if (!SHELL_UNSAFE.test(arg.replaceAll('<value>', '')))
|
|
84
|
+
return arg;
|
|
85
|
+
return `'${arg.replaceAll("'", `'\\''`)}'`;
|
|
86
|
+
}
|
package/dist/core/library.d.ts
CHANGED
|
@@ -402,6 +402,26 @@ export declare class Library {
|
|
|
402
402
|
* that. It resorted to grepping the library's JSON out of `node_modules`.
|
|
403
403
|
*/
|
|
404
404
|
private describeMiss;
|
|
405
|
+
/** Every direct operation id under `prefix` (no further dot), across layers, sorted and de-duplicated. */
|
|
406
|
+
private opsUnder;
|
|
407
|
+
/** Whether any layer indexes `nodeType` (at `version`, when one is asked for). */
|
|
408
|
+
private carries;
|
|
409
|
+
/** The connector key is in no entry at all. */
|
|
410
|
+
private noConnector;
|
|
411
|
+
/**
|
|
412
|
+
* The event half of {@link describeMiss}: which of the connector's EVENTS the
|
|
413
|
+
* library carries, read from BOTH namespaces.
|
|
414
|
+
*
|
|
415
|
+
* Both, because the library stores almost every event in one form only: the
|
|
416
|
+
* baseline snapshot of 2026-09-23 (`2a4405dd…`) has 994 `.event.` entries and 4
|
|
417
|
+
* `.trigger.` ones, and a start trigger resolves from its `.event.` sibling
|
|
418
|
+
* ({@link resolveEvent}). Reading the asked namespace alone answered `No
|
|
419
|
+
* connector "uipath-uipath-dataservice"` for a typo'd `onEvent` event on a
|
|
420
|
+
* connector whose events were all right there, and the same words for a
|
|
421
|
+
* connector present with eight actions and no events at all (2026-09-23 eval,
|
|
422
|
+
* `skill-flow-datafabric-trigger-lifecycle`, v2 commands 45/46).
|
|
423
|
+
*/
|
|
424
|
+
private describeEventMiss;
|
|
405
425
|
/**
|
|
406
426
|
* Resolve the op WITHIN one layer, selecting the entry by `objectName` for a
|
|
407
427
|
* GENERIC operation. Returns undefined when the layer does not carry the op;
|
package/dist/core/library.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { readFileSync, existsSync } from 'node:fs';
|
|
10
10
|
import { join } from 'node:path';
|
|
11
|
+
import { prepareCommand } from './cli-spelling.js';
|
|
11
12
|
import { loadListEnvelopes, lookupListEnvelope } from './list-envelope.js';
|
|
12
13
|
import { lookupSpecOf } from './lookups.js';
|
|
13
14
|
import { loadInvariantCollections, loadLookupResolutions } from './lookup-store.js';
|
|
@@ -245,11 +246,18 @@ export class Library {
|
|
|
245
246
|
// own `registry get` answers for both forms (see `deriveTriggerForm`).
|
|
246
247
|
if (kind !== 'trigger')
|
|
247
248
|
throw error;
|
|
249
|
+
const siblingType = `uipath.connector.event.${key}.${event}`;
|
|
248
250
|
let sibling;
|
|
249
251
|
try {
|
|
250
|
-
sibling = this.resolveType(
|
|
252
|
+
sibling = this.resolveType(siblingType, version, objectName);
|
|
251
253
|
}
|
|
252
|
-
catch {
|
|
254
|
+
catch (siblingError) {
|
|
255
|
+
// The wait form IS in the library and refused the request itself — a
|
|
256
|
+
// GENERIC event with no `object`, or a disagreeing one. That refusal is
|
|
257
|
+
// the true answer; replacing it with the start form's "not in library"
|
|
258
|
+
// told the author the connector was missing.
|
|
259
|
+
if (this.carries(siblingType, version))
|
|
260
|
+
throw siblingError;
|
|
253
261
|
throw error;
|
|
254
262
|
}
|
|
255
263
|
rc = deriveTriggerForm(sibling, nodeType);
|
|
@@ -528,7 +536,7 @@ export class Library {
|
|
|
528
536
|
+ `\`…connector.trigger.…\` starts a flow, \`…connector.event.…\` waits mid-flow.`
|
|
529
537
|
: '';
|
|
530
538
|
throw new Error(`connector not in library: ${nodeType}${version ? '@' + version : ''}.${hint}`
|
|
531
|
-
+ this.describeMiss(nodeType));
|
|
539
|
+
+ this.describeMiss(nodeType, version, objectName));
|
|
532
540
|
}
|
|
533
541
|
/**
|
|
534
542
|
* Turn a lookup miss into something actionable: which operations this connector
|
|
@@ -541,7 +549,7 @@ export class Library {
|
|
|
541
549
|
* `connector not in library: …send-mail-v2.` said nothing that would reveal
|
|
542
550
|
* that. It resorted to grepping the library's JSON out of `node_modules`.
|
|
543
551
|
*/
|
|
544
|
-
describeMiss(nodeType) {
|
|
552
|
+
describeMiss(nodeType, version, objectName) {
|
|
545
553
|
// `uipath.connector.<key>.<op>`, or `uipath.connector.(trigger|event).<key>.<ev>`
|
|
546
554
|
// for the two event namespaces. No connector key contains a dot.
|
|
547
555
|
const parts = nodeType.split('.');
|
|
@@ -550,23 +558,12 @@ export class Library {
|
|
|
550
558
|
const asked = (eventNs ? parts.slice(4) : parts.slice(3)).join('.');
|
|
551
559
|
if (!key)
|
|
552
560
|
return '';
|
|
553
|
-
const prefix = eventNs ? `uipath.connector.${parts[2]}.${key}.` : `uipath.connector.${key}.`;
|
|
554
|
-
const ops = [...new Set(this.layers.flatMap((l) => l.entries
|
|
555
|
-
.map((e) => String(e.nodeType ?? ''))
|
|
556
|
-
.filter((t) => t.startsWith(prefix))
|
|
557
|
-
.map((t) => t.slice(prefix.length))))].sort();
|
|
558
561
|
const where = ` Searched: ${this.layers.map((l) => l.dir).join(', ')}.`;
|
|
559
|
-
if (
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
const near = nearest(key, keys);
|
|
565
|
-
return ` No connector "${key}" in the library (${keys.length} indexed).`
|
|
566
|
-
+ (near && near !== key ? ` Closest: ${near}.` : '')
|
|
567
|
-
+ where
|
|
568
|
-
+ ' If the connector is newer than the library, refresh it with `uip maestro flow registry pull --force`.';
|
|
569
|
-
}
|
|
562
|
+
if (eventNs)
|
|
563
|
+
return this.describeEventMiss(parts[2], key, asked, where, version, objectName);
|
|
564
|
+
const ops = this.opsUnder(`uipath.connector.${key}.`);
|
|
565
|
+
if (!ops.length)
|
|
566
|
+
return this.noConnector(key, where);
|
|
570
567
|
const near = asked ? nearest(asked, ops) : undefined;
|
|
571
568
|
const listed = ops.length <= 12
|
|
572
569
|
? `Operations: ${ops.join(', ')}.`
|
|
@@ -580,6 +577,86 @@ export class Library {
|
|
|
580
577
|
+ ' — `uip maestro case spec` reports the latter, and the two can differ.'
|
|
581
578
|
+ ' If the operation is newer than the library, refresh it with `uip maestro flow registry pull --force`.';
|
|
582
579
|
}
|
|
580
|
+
/** Every direct operation id under `prefix` (no further dot), across layers, sorted and de-duplicated. */
|
|
581
|
+
opsUnder(prefix) {
|
|
582
|
+
return [...new Set(this.layers.flatMap((l) => l.entries
|
|
583
|
+
.map((e) => String(e.nodeType ?? ''))
|
|
584
|
+
.filter((t) => t.startsWith(prefix))
|
|
585
|
+
.map((t) => t.slice(prefix.length))
|
|
586
|
+
.filter((op) => op !== '' && !op.includes('.'))))].sort();
|
|
587
|
+
}
|
|
588
|
+
/** Whether any layer indexes `nodeType` (at `version`, when one is asked for). */
|
|
589
|
+
carries(nodeType, version) {
|
|
590
|
+
return this.layers.some((l) => l.entries.some((e) => e.nodeType === nodeType && (!version || e.version === version)));
|
|
591
|
+
}
|
|
592
|
+
/** The connector key is in no entry at all. */
|
|
593
|
+
noConnector(key, where) {
|
|
594
|
+
const keys = [...new Set(this.layers.flatMap((l) => l.entries.map((e) => {
|
|
595
|
+
const p = String(e.nodeType ?? '').split('.');
|
|
596
|
+
return (p[2] === 'trigger' || p[2] === 'event' ? p[3] : p[2]) ?? '';
|
|
597
|
+
})))].filter(Boolean).sort();
|
|
598
|
+
const near = nearest(key, keys);
|
|
599
|
+
return ` No connector "${key}" in the library (${keys.length} indexed).`
|
|
600
|
+
+ (near && near !== key ? ` Closest: ${near}.` : '')
|
|
601
|
+
+ where
|
|
602
|
+
+ ' If the connector is newer than the library, refresh it with `uip maestro flow registry pull --force`.';
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* The event half of {@link describeMiss}: which of the connector's EVENTS the
|
|
606
|
+
* library carries, read from BOTH namespaces.
|
|
607
|
+
*
|
|
608
|
+
* Both, because the library stores almost every event in one form only: the
|
|
609
|
+
* baseline snapshot of 2026-09-23 (`2a4405dd…`) has 994 `.event.` entries and 4
|
|
610
|
+
* `.trigger.` ones, and a start trigger resolves from its `.event.` sibling
|
|
611
|
+
* ({@link resolveEvent}). Reading the asked namespace alone answered `No
|
|
612
|
+
* connector "uipath-uipath-dataservice"` for a typo'd `onEvent` event on a
|
|
613
|
+
* connector whose events were all right there, and the same words for a
|
|
614
|
+
* connector present with eight actions and no events at all (2026-09-23 eval,
|
|
615
|
+
* `skill-flow-datafabric-trigger-lifecycle`, v2 commands 45/46).
|
|
616
|
+
*/
|
|
617
|
+
describeEventMiss(kind, key, asked, where, version, objectName) {
|
|
618
|
+
const starts = this.opsUnder(`uipath.connector.trigger.${key}.`);
|
|
619
|
+
const waits = this.opsUnder(`uipath.connector.event.${key}.`);
|
|
620
|
+
// A start trigger resolves from either form; a mid-flow wait needs the `.event.` form itself.
|
|
621
|
+
const usable = kind === 'trigger' ? [...new Set([...starts, ...waits])].sort() : waits;
|
|
622
|
+
const refresh = ' If the event is newer than the library, refresh it with `uip maestro flow registry pull --force`.';
|
|
623
|
+
if (usable.length) {
|
|
624
|
+
if (usable.includes(asked)) {
|
|
625
|
+
// Only a version can make a carried event miss here: every other refusal
|
|
626
|
+
// of a carried entry is thrown by the entry itself.
|
|
627
|
+
return ` Connector "${key}" carries event "${asked}", but not at version ${version ?? '(any)'}.`
|
|
628
|
+
+ ` Drop the version, or use one the library's index.json lists for it.` + where;
|
|
629
|
+
}
|
|
630
|
+
const onlyStart = kind === 'event' && starts.includes(asked);
|
|
631
|
+
const near = onlyStart ? undefined : nearest(asked, usable);
|
|
632
|
+
const listed = usable.length <= 12
|
|
633
|
+
? `Events: ${usable.join(', ')}.`
|
|
634
|
+
: `Events (${usable.length}, first 12): ${usable.slice(0, 12).join(', ')}, … — `
|
|
635
|
+
+ `the full list is every \`nodeType\` under "${key}" in the library's index.json.`;
|
|
636
|
+
return ` Connector "${key}" is present but carries no event "${asked}"`
|
|
637
|
+
+ (onlyStart
|
|
638
|
+
? ` that can be waited on: it is here only as a start trigger (\`uipath.connector.trigger.${key}.${asked}\`), `
|
|
639
|
+
+ `and \`waitForEvent\` needs its \`uipath.connector.event.\` form.`
|
|
640
|
+
: '.')
|
|
641
|
+
+ (near ? ` Closest: ${near}.` : '')
|
|
642
|
+
+ ` ${listed}`
|
|
643
|
+
+ where
|
|
644
|
+
+ refresh;
|
|
645
|
+
}
|
|
646
|
+
if (starts.length) {
|
|
647
|
+
return ` Connector "${key}" carries only start triggers in this library (${starts.join(', ')}), `
|
|
648
|
+
+ `and \`waitForEvent\` needs the \`uipath.connector.event.\` form.` + where + refresh;
|
|
649
|
+
}
|
|
650
|
+
const actions = this.opsUnder(`uipath.connector.${key}.`).length;
|
|
651
|
+
if (!actions)
|
|
652
|
+
return this.noConnector(key, where);
|
|
653
|
+
const prepare = prepareCommand(key, asked, ...(objectName ? ['--object', objectName] : []));
|
|
654
|
+
return ` Connector "${key}" is present (${actions} action${actions === 1 ? '' : 's'}) but this library carries `
|
|
655
|
+
+ `no events for it.` + where
|
|
656
|
+
+ ` Prepare the event against a live connection: ${prepare}. If prepare reports that the registry serves `
|
|
657
|
+
+ `no event for "${key}" either (after \`uip maestro flow registry pull --force\`, if its catalog looks stale), `
|
|
658
|
+
+ `refreshing the library will not add one: it is built from the same catalog.`;
|
|
659
|
+
}
|
|
583
660
|
/**
|
|
584
661
|
* Resolve the op WITHIN one layer, selecting the entry by `objectName` for a
|
|
585
662
|
* GENERIC operation. Returns undefined when the layer does not carry the op;
|