@uipath/maestro-builder-sdk 6.11.5 → 6.12.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 +83 -56
- package/dist/api-members.md +362 -261
- package/dist/bpmn/bpmn-expr-check.js +8 -1
- package/dist/bpmn/bpmn-sdk.d.ts +1026 -27
- package/dist/bpmn/bpmn-sdk.js +1328 -83
- package/dist/bpmn/check-cli.js +19 -4
- package/dist/bpmn/check.d.ts +1 -1
- package/dist/bpmn/check.js +529 -13
- package/dist/bpmn/connector.d.ts +2 -2
- package/dist/bpmn/connector.js +74 -37
- package/dist/bpmn/data-service.d.ts +124 -0
- package/dist/bpmn/data-service.js +98 -0
- package/dist/bpmn/decompile-cli.js +70 -8
- package/dist/bpmn/decompile.d.ts +71 -0
- package/dist/bpmn/decompile.js +845 -31
- package/dist/bpmn/graph.d.ts +69 -0
- package/dist/bpmn/graph.js +98 -0
- package/dist/bpmn/intsvc.js +2 -2
- package/dist/bpmn/known-methods.d.ts +15 -0
- package/dist/bpmn/known-methods.js +271 -0
- package/dist/bpmn/lift-verify.d.ts +40 -0
- package/dist/bpmn/lift-verify.js +133 -0
- package/dist/bpmn/serialize.d.ts +65 -1
- package/dist/bpmn/serialize.js +169 -22
- package/dist/bpmn/typed-node.d.ts +37 -6
- package/dist/bpmn/typed-node.js +81 -19
- package/dist/check-cli.js +2 -1
- package/dist/check.d.ts +13 -0
- package/dist/check.js +11 -0
- package/dist/cli/commands/bpmn.js +7 -1
- package/dist/cli-library.d.ts +7 -0
- package/dist/cli-library.js +19 -0
- package/dist/compile-cli.js +8 -5
- package/dist/core/byoa.d.ts +69 -0
- package/dist/core/byoa.js +98 -0
- package/dist/core/library.d.ts +23 -0
- package/dist/core/library.js +47 -1
- package/dist/core/lookup-store.d.ts +12 -0
- package/dist/core/lookup-store.js +25 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +3 -0
- package/dist/registry/generators/descriptors.d.ts +1 -1
- package/dist/registry/generators/descriptors.js +20 -2
- package/dist/registry/generators/markdown/page.js +3 -1
- package/dist/registry/generators/markdown/sections/event-facts.d.ts +2 -0
- package/dist/registry/generators/markdown/sections/event-facts.js +45 -0
- package/dist/registry/library-build/events.d.ts +32 -3
- package/dist/registry/library-build/events.js +48 -19
- package/dist/registry/prepare/byoa-ledger.d.ts +12 -0
- package/dist/registry/prepare/byoa-ledger.js +26 -0
- package/dist/registry/prepare/event.d.ts +5 -1
- package/dist/registry/prepare/event.js +22 -8
- package/dist/registry/prepare/messages.d.ts +2 -0
- package/dist/registry/prepare/messages.js +4 -0
- package/dist/serialize.js +24 -1
- package/package.json +2 -2
package/dist/bpmn/typed-node.js
CHANGED
|
@@ -19,12 +19,16 @@ export function registryTypeNames() {
|
|
|
19
19
|
* `FLOW_SDK_BPMN_REGISTRY_FALLBACK=0` to keep resolution snapshot-only.
|
|
20
20
|
*
|
|
21
21
|
* @param type - The extension type, e.g. `'Intsvc.UnifiedHttpRequest'`.
|
|
22
|
+
* @param inline - A spec supplied by the author, which is used as is.
|
|
22
23
|
* @returns Its emission spec.
|
|
23
24
|
* @throws If the type is in neither the snapshot nor the registry — which means a
|
|
24
25
|
* typo, or no `uip` to ask.
|
|
25
26
|
*/
|
|
26
|
-
export function registryType(type) {
|
|
27
|
-
|
|
27
|
+
export function registryType(type, inline) {
|
|
28
|
+
// An inline spec wins over both the snapshot and the tenant — it is the author
|
|
29
|
+
// saying "emit THIS", for a type the snapshot lacks and no `uip` can describe, or
|
|
30
|
+
// for one the snapshot describes wrongly. See `ActivityNodeOpts.spec`.
|
|
31
|
+
const spec = inline ?? REGISTRY.types[type] ?? resolveFromRegistry(type);
|
|
28
32
|
if (!spec) {
|
|
29
33
|
throw new Error(`unknown extension type "${type}". The committed registry snapshot has ${REGISTRY.provenance.typeCount} types `
|
|
30
34
|
+ `(uip ${REGISTRY.provenance.uipVersion}) and the registry could not be asked for this one either — `
|
|
@@ -82,7 +86,22 @@ export const REGISTRY_GAPS = {
|
|
|
82
86
|
// which `uip maestro bpmn validate` has always accepted. So the guard was right
|
|
83
87
|
// that they are undeclared and wrong that they are invalid — recorded here rather
|
|
84
88
|
// than by loosening the guard, which is what catches an author's typo.
|
|
85
|
-
|
|
89
|
+
// `requiredContext` — context fields the PLATFORM requires and the registry
|
|
90
|
+
// snapshot does not mark required (or, here, does not declare at all).
|
|
91
|
+
//
|
|
92
|
+
// `Intsvc.ActivityExecution` must carry `activity`; without it `uip maestro bpmn
|
|
93
|
+
// validate` reports
|
|
94
|
+
//
|
|
95
|
+
// [canvas] [ERROR] EMPTY_REQUIRED_FIELD: The field 'activity' in node 'op' is
|
|
96
|
+
// required but has no value.
|
|
97
|
+
//
|
|
98
|
+
// and `check` could not know, because it derives required fields from the snapshot.
|
|
99
|
+
// The connector emitter has always written `activity`, so this only bites a
|
|
100
|
+
// hand-rolled `.activity()` — but it bit it at the LAST rung. The divergence is
|
|
101
|
+
// measured rather than assumed: `bpmn-registry-required-drift.test.ts` diffs the
|
|
102
|
+
// required-field set the canvas validator carries against the snapshot's, over every
|
|
103
|
+
// type both know, and asserts the only difference is what this table records.
|
|
104
|
+
'Intsvc.ActivityExecution': { extraContext: ['activity', 'resourceKey'], requiredContext: ['activity'] },
|
|
86
105
|
// `outputRowsFixed` — the type permits EXACTLY the output row its spec declares.
|
|
87
106
|
// A mapped second row is refused:
|
|
88
107
|
//
|
|
@@ -115,8 +134,8 @@ function identifierize(name) {
|
|
|
115
134
|
.join('');
|
|
116
135
|
}
|
|
117
136
|
/** The variable a typed node's output row writes, or `undefined` when it has none. */
|
|
118
|
-
export function typedNodeOutputVar(id, type, outputVar) {
|
|
119
|
-
const spec = registryType(type);
|
|
137
|
+
export function typedNodeOutputVar(id, type, outputVar, inline) {
|
|
138
|
+
const spec = registryType(type, inline);
|
|
120
139
|
if (!spec.output)
|
|
121
140
|
return undefined;
|
|
122
141
|
return outputVar ?? `${id}_${identifierize(spec.output.name)}`;
|
|
@@ -182,7 +201,13 @@ function contextRows(m, type, spec, supplied, verbatim = false) {
|
|
|
182
201
|
// it and the author never learns why their setting did nothing — so it is
|
|
183
202
|
// refused. The exception is a type with a recorded `extraContext` gap, where the
|
|
184
203
|
// platform is known to accept fields the registry omits.
|
|
185
|
-
const known = new Set([
|
|
204
|
+
const known = new Set([
|
|
205
|
+
...spec.context.map((f) => f.name),
|
|
206
|
+
...(REGISTRY_GAPS[type]?.extraContext ?? []),
|
|
207
|
+
// A field the platform REQUIRES is necessarily one it permits, so recording it as
|
|
208
|
+
// required is enough — an `extraContext` twin is not also needed.
|
|
209
|
+
...(REGISTRY_GAPS[type]?.requiredContext ?? []),
|
|
210
|
+
]);
|
|
186
211
|
const unknown = Object.keys(supplied).filter((k) => !known.has(k));
|
|
187
212
|
if (unknown.length) {
|
|
188
213
|
throw new Error(`context field(s) ${unknown.map((u) => `"${u}"`).join(', ')} are not declared by this extension type. ` +
|
|
@@ -234,10 +259,16 @@ function inputRows(m, spec, inputs) {
|
|
|
234
259
|
// platform does not read, and one no validator complains about.
|
|
235
260
|
//
|
|
236
261
|
// So: the declared field decides when there is one, the key when there is not.
|
|
262
|
+
//
|
|
263
|
+
// The same split decides the TYPE of an undeclared field. Every undeclared value
|
|
264
|
+
// used to be written as a `json` body, so a scalar came out JSON-quoted —
|
|
265
|
+
// `recordId` as `"1111-…"`, quotes included, where every real export writes
|
|
266
|
+
// `type="string" value="1111-…"`. A structured value is still a json body; a
|
|
267
|
+
// scalar is written as the `value` attribute, the way a declared scalar is.
|
|
237
268
|
const declared = new Map(spec.inputs.map((f) => [f.name, f]));
|
|
238
269
|
return keys.map((k) => {
|
|
239
270
|
const field = declared.get(k);
|
|
240
|
-
return payloadInput(m, k, field?.target ?? k, inputs[k], field?.type ??
|
|
271
|
+
return payloadInput(m, k, field?.target ?? k, inputs[k], field?.type ?? inferredInputType(inputs[k]));
|
|
241
272
|
});
|
|
242
273
|
}
|
|
243
274
|
if (!spec.inputName)
|
|
@@ -277,6 +308,23 @@ function payloadInput(m, name, target, value, type) {
|
|
|
277
308
|
props.target = target;
|
|
278
309
|
return m.create('uipath:Input', props);
|
|
279
310
|
}
|
|
311
|
+
/** `json` for an object or array, `string` for anything else — see `inputRows`. */
|
|
312
|
+
function inferredInputType(value) {
|
|
313
|
+
return value !== null && typeof value === 'object' ? 'json' : 'string';
|
|
314
|
+
}
|
|
315
|
+
/** `uipath:input` payload rows exactly as the caller spelled them, deriving nothing. */
|
|
316
|
+
export function verbatimInputRows(m, rows) {
|
|
317
|
+
return rows.map((row) => {
|
|
318
|
+
const props = { name: row.name, type: row.type ?? 'string' };
|
|
319
|
+
if (row.target !== undefined)
|
|
320
|
+
props.target = row.target;
|
|
321
|
+
if (row.value !== undefined)
|
|
322
|
+
props.value = row.value;
|
|
323
|
+
if (row.body !== undefined)
|
|
324
|
+
props.body = jsonBody(row.body);
|
|
325
|
+
return m.create('uipath:Input', props);
|
|
326
|
+
});
|
|
327
|
+
}
|
|
280
328
|
/** The `uipath:output` row, when the type declares one. */
|
|
281
329
|
function outputRow(m, spec, varName) {
|
|
282
330
|
if (!spec.output)
|
|
@@ -308,7 +356,7 @@ export function createTypedNode(m, id, type, input,
|
|
|
308
356
|
* Optional so a caller with no variable context still gets today's behaviour.
|
|
309
357
|
*/
|
|
310
358
|
varType) {
|
|
311
|
-
const spec = registryType(type);
|
|
359
|
+
const spec = registryType(type, input.spec);
|
|
312
360
|
if (!spec.element || !spec.tag) {
|
|
313
361
|
throw new Error(`extension type "${type}" has no element/tag in the registry snapshot; it cannot be authored.`);
|
|
314
362
|
}
|
|
@@ -352,7 +400,10 @@ varType) {
|
|
|
352
400
|
}
|
|
353
401
|
payload.context = m.create('uipath:Context', context);
|
|
354
402
|
}
|
|
355
|
-
|
|
403
|
+
// Spelled out by the caller: emit exactly these rows, deriving nothing — see
|
|
404
|
+
// TypedNodeInput.inputRows. This is what lets a connector's per-parameter rows
|
|
405
|
+
// (`name="start" type="string" value="0" target="query"`) survive a round trip.
|
|
406
|
+
const ins = input.inputRows ? verbatimInputRows(m, input.inputRows) : inputRows(m, spec, input.inputs ?? {});
|
|
356
407
|
if (ins.length)
|
|
357
408
|
payload.input = ins;
|
|
358
409
|
const rows = [];
|
|
@@ -365,7 +416,7 @@ varType) {
|
|
|
365
416
|
payload.output = rows;
|
|
366
417
|
return finish(m, spec, id, input.name, payload);
|
|
367
418
|
}
|
|
368
|
-
const outVar = typedNodeOutputVar(id, type, input.outputVar);
|
|
419
|
+
const outVar = typedNodeOutputVar(id, type, input.outputVar, spec);
|
|
369
420
|
const out = outVar ? outputRow(m, spec, outVar) : undefined;
|
|
370
421
|
if (out)
|
|
371
422
|
rows.push(out);
|
|
@@ -476,8 +527,8 @@ function moddleElementOf(element) {
|
|
|
476
527
|
* @param type - The extension type.
|
|
477
528
|
* @returns Its fields that must be bound, with the target each binding needs.
|
|
478
529
|
*/
|
|
479
|
-
export function bindingRequiredFields(type) {
|
|
480
|
-
return registryType(type).context.filter((f) => f.bindingInfo !== null && f.required);
|
|
530
|
+
export function bindingRequiredFields(type, inline) {
|
|
531
|
+
return registryType(type, inline).context.filter((f) => f.bindingInfo !== null && f.required);
|
|
481
532
|
}
|
|
482
533
|
/**
|
|
483
534
|
* Required context fields the author left unset and the registry cannot default.
|
|
@@ -490,10 +541,21 @@ export function bindingRequiredFields(type) {
|
|
|
490
541
|
* @param context - The context values the author supplied.
|
|
491
542
|
* @returns The names of required fields with no value and no default.
|
|
492
543
|
*/
|
|
493
|
-
export function missingRequiredContext(type, context = {}) {
|
|
494
|
-
|
|
495
|
-
|
|
544
|
+
export function missingRequiredContext(type, context = {}, inline) {
|
|
545
|
+
// The platform's test is `isNilOrEmpty(field.value)`, so a field present and BLANK is
|
|
546
|
+
// as missing as one absent — and blank is the likelier authoring slip of the two, a
|
|
547
|
+
// value that came from an expression or a variable that had nothing in it. Checking
|
|
548
|
+
// only for `undefined` let every one of those through to the last rung.
|
|
549
|
+
const unset = (name) => {
|
|
550
|
+
const v = context[name];
|
|
551
|
+
return v === undefined || v === null || (typeof v === 'string' && v.trim() === '');
|
|
552
|
+
};
|
|
553
|
+
const declared = registryType(type, inline)
|
|
554
|
+
.context.filter((f) => f.required && unset(f.name) && (f.default ?? '') === '')
|
|
496
555
|
.map((f) => f.name);
|
|
556
|
+
// Fields the platform requires that the snapshot does not — see `REGISTRY_GAPS`.
|
|
557
|
+
const gaps = (REGISTRY_GAPS[type]?.requiredContext ?? []).filter((name) => !declared.includes(name) && unset(name));
|
|
558
|
+
return [...declared, ...gaps];
|
|
497
559
|
}
|
|
498
560
|
/**
|
|
499
561
|
* Required PAYLOAD fields the author left unset.
|
|
@@ -519,9 +581,9 @@ export function missingRequiredInputs(type, inputs = {},
|
|
|
519
581
|
* genuinely does not spell (it writes `connection`). Reading both makes this
|
|
520
582
|
* agree with the CLI instead of guessing on either side of it.
|
|
521
583
|
*/
|
|
522
|
-
context = {}) {
|
|
584
|
+
context = {}, inline) {
|
|
523
585
|
const notEnforced = new Set(REGISTRY_GAPS[type]?.notEnforcedInputs ?? []);
|
|
524
|
-
return registryType(type)
|
|
586
|
+
return registryType(type, inline)
|
|
525
587
|
.inputs.filter((f) => f.required &&
|
|
526
588
|
!notEnforced.has(f.name) &&
|
|
527
589
|
inputs[f.name] === undefined &&
|
|
@@ -560,8 +622,8 @@ context = {}) {
|
|
|
560
622
|
* @param type - The extension type.
|
|
561
623
|
* @returns The required payload field names that cannot be expressed, or `[]`.
|
|
562
624
|
*/
|
|
563
|
-
export function unsatisfiableRequiredInputs(type) {
|
|
564
|
-
const spec = registryType(type);
|
|
625
|
+
export function unsatisfiableRequiredInputs(type, inline) {
|
|
626
|
+
const spec = registryType(type, inline);
|
|
565
627
|
if (spec.inputPattern !== 'mergedBody' && spec.inputPattern !== 'splitInputsItemData')
|
|
566
628
|
return [];
|
|
567
629
|
return spec.inputs.filter((f) => f.required).map((f) => f.name);
|
package/dist/check-cli.js
CHANGED
|
@@ -9,7 +9,7 @@ import { reportDiagnostics } from './cli-result.js';
|
|
|
9
9
|
import { existsSync } from 'node:fs';
|
|
10
10
|
import { resolveFlowFile, loadBuiltFlow } from './load.js';
|
|
11
11
|
import { check } from './check.js';
|
|
12
|
-
import { eventResolveErrorFor, openCliLibrary } from './cli-library.js';
|
|
12
|
+
import { byoaCheckOpts, eventResolveErrorFor, openCliLibrary } from './cli-library.js';
|
|
13
13
|
import { Bindings } from './bindings.js';
|
|
14
14
|
import { connectorInputErrors } from './serialize.js';
|
|
15
15
|
import { runWhenInvokedDirectly } from './cli-run.js';
|
|
@@ -41,6 +41,7 @@ function checkOpts(argv) {
|
|
|
41
41
|
return bindings ? { bindings } : {};
|
|
42
42
|
return {
|
|
43
43
|
eventResolveError: eventResolveErrorFor(library),
|
|
44
|
+
...byoaCheckOpts(library),
|
|
44
45
|
eventParams: (connector, event, object) => {
|
|
45
46
|
try {
|
|
46
47
|
return library.eventParameterFields(connector, event, object);
|
package/dist/check.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { type LookupResolutions, type LookupSpec } from './core/lookups.js';
|
|
|
2
2
|
import type { SchemaReadiness } from './core/library.js';
|
|
3
3
|
import type { BuiltFlow } from './flow-sdk.js';
|
|
4
4
|
import { type BindingsView } from './core/binding-messages.js';
|
|
5
|
+
import { type ByoaEventFacts, type ByoaLedger } from './core/byoa.js';
|
|
5
6
|
export interface Diagnostic {
|
|
6
7
|
level: 'error' | 'warning';
|
|
7
8
|
code: string;
|
|
@@ -116,6 +117,18 @@ export interface CheckOpts {
|
|
|
116
117
|
lookups?: (connector: string, action: string, version?: string, object?: string) => Readonly<Record<string, LookupSpec>> | undefined;
|
|
117
118
|
/** Everything `registry prepare --resolve` has recorded in the local overlay. */
|
|
118
119
|
resolutions?: LookupResolutions;
|
|
120
|
+
/**
|
|
121
|
+
* A connector event's BYOA and webhook-URL facts, or `undefined` when unknown.
|
|
122
|
+
* Supplied by the CLIs from `library.eventFacts` — the call `serialize` makes
|
|
123
|
+
* for the same refusal — so check and compile cannot disagree; omitted (no
|
|
124
|
+
* library) → EVENT_BYOA_UNVERIFIED is not checked.
|
|
125
|
+
*/
|
|
126
|
+
eventFacts?: (connector: string, event: string, object?: string) => ByoaEventFacts | undefined;
|
|
127
|
+
/**
|
|
128
|
+
* The BYOA checks `registry prepare` recorded — the `byoa` section of the
|
|
129
|
+
* overlay's `resolutions.json`, as `library.byoaVerifications` loads it.
|
|
130
|
+
*/
|
|
131
|
+
byoa?: ByoaLedger;
|
|
119
132
|
/**
|
|
120
133
|
* Whether a connector step's schema is materialized locally — the fact behind
|
|
121
134
|
* the OBJECT_UNPREPARED and CUSTOM_FIELDS_UNPREPARED warnings. Supplied by the
|
package/dist/check.js
CHANGED
|
@@ -18,6 +18,7 @@ import { childStepLists, declaredExits, hasExits, stopsAtEnd } from './core/step
|
|
|
18
18
|
import { FLOW_SCHEMA_VERSIONS } from './format-profile.js';
|
|
19
19
|
import { compareSchemaVersions, schemaVersionRefusal } from './schema-version.js';
|
|
20
20
|
import { prepareCommand, resolveFlagArg } from './core/cli-spelling.js';
|
|
21
|
+
import { EVENT_BYOA_UNVERIFIED, byoaUnverified } from './core/byoa.js';
|
|
21
22
|
export function check(built, opts = {}) {
|
|
22
23
|
return checkFlow(built, [], opts);
|
|
23
24
|
}
|
|
@@ -1953,6 +1954,13 @@ function eventParamsFor(subscription, opts) {
|
|
|
1953
1954
|
* the fallback case for a connector the shipped library was not generated
|
|
1954
1955
|
* against. A warning, because compile still succeeds. Prints the one
|
|
1955
1956
|
* command that makes the event checkable.
|
|
1957
|
+
* - `EVENT_BYOA_UNVERIFIED` — an ERROR: the event only fires on a BYOA
|
|
1958
|
+
* (bring-your-own-app) connection, and the bound connection id has no record
|
|
1959
|
+
* of `prepare` having checked it (`core/byoa.ts` has the rule and when it
|
|
1960
|
+
* stays silent). A standard connection deploys and the trigger never fires;
|
|
1961
|
+
* the usual way in is binding the connection by preparing one of the
|
|
1962
|
+
* connector's ACTIONS, which checks nothing about BYOA. `compile` throws the
|
|
1963
|
+
* same text.
|
|
1956
1964
|
* - `EVENT_FILTER_UNKNOWN_FIELD` — a filter leaf naming a field outside the
|
|
1957
1965
|
* prepared vocabulary is an ERROR, not a warning, because the platform's own
|
|
1958
1966
|
* configure DROPS an unmatched leaf and reports Success: the deployed
|
|
@@ -1988,6 +1996,9 @@ function checkEventResolution(subscription, where, kind, opts, error, warn, even
|
|
|
1988
1996
|
return;
|
|
1989
1997
|
}
|
|
1990
1998
|
const object = typeof sub.object === 'string' && sub.object !== '' ? sub.object : undefined;
|
|
1999
|
+
const byoa = opts.eventFacts && byoaUnverified(where, { connector: connectorKey, event: eventName, ...(object ? { object } : {}), connection: sub.connection }, opts.eventFacts(connectorKey, eventName, object), opts.byoa, opts.bindings);
|
|
2000
|
+
if (byoa)
|
|
2001
|
+
error(EVENT_BYOA_UNVERIFIED, byoa);
|
|
1991
2002
|
if (eventParams?.objectUnprepared && object) {
|
|
1992
2003
|
// The generic template answers for every object, so resolution SUCCEEDS
|
|
1993
2004
|
// and nothing downstream notices that the object named here has no
|
|
@@ -18,6 +18,7 @@ export function registerBpmnCommands(program) {
|
|
|
18
18
|
.description('Check authored TypeScript. Check a .bpmn with `uip maestro bpmn validate`.')
|
|
19
19
|
.argument('<input>', 'a .bpmn.ts path or bare name')
|
|
20
20
|
.option('--source', 'check authored TypeScript')
|
|
21
|
+
.option('--graph', 'also print the resolved wiring: every element with the flows leaving it, implied edges marked ~>')
|
|
21
22
|
.addOption(retiredCompiledOption())
|
|
22
23
|
.action(async (input, options) => {
|
|
23
24
|
let mode;
|
|
@@ -32,7 +33,10 @@ export function registerBpmnCommands(program) {
|
|
|
32
33
|
refuse('flow-sdk bpmn check --compiled: use `uip maestro bpmn validate <file.bpmn>`');
|
|
33
34
|
return;
|
|
34
35
|
}
|
|
35
|
-
|
|
36
|
+
const argv = [input];
|
|
37
|
+
if (options.graph)
|
|
38
|
+
argv.push('--graph');
|
|
39
|
+
await invoke('bpmn check', () => loadTool(import.meta.url, '../../bpmn/check-cli.js'), argv);
|
|
36
40
|
});
|
|
37
41
|
family.command('tidy')
|
|
38
42
|
.description('Add diagram layout to a compiled .bpmn artifact.')
|
|
@@ -49,10 +53,12 @@ export function registerBpmnCommands(program) {
|
|
|
49
53
|
.argument('<input>', 'input .bpmn path')
|
|
50
54
|
.option('-o, --output <file>', 'output .bpmn.ts path')
|
|
51
55
|
.option('--import <specifier>', 'SDK import specifier in generated source')
|
|
56
|
+
.option('--style <style>', 'explicit (default) writes one call per element and flow; nested lifts boundary events, gateways and event sub-processes into the nesting constructs where the graph is provably the same')
|
|
52
57
|
.action(async (input, options) => {
|
|
53
58
|
const argv = [input];
|
|
54
59
|
addValue(argv, '-o', options.output);
|
|
55
60
|
addValue(argv, '--import', options.import);
|
|
61
|
+
addValue(argv, '--style', options.style);
|
|
56
62
|
await invoke('bpmn decompile', () => loadTool(import.meta.url, '../../bpmn/decompile-cli.js'), argv);
|
|
57
63
|
});
|
|
58
64
|
family
|
package/dist/cli-library.d.ts
CHANGED
|
@@ -19,3 +19,10 @@ export declare function openCliLibrary(argv: readonly string[]): Library | undef
|
|
|
19
19
|
* exactly when compile would throw, with the same text.
|
|
20
20
|
*/
|
|
21
21
|
export declare function eventResolveErrorFor(library: Library): NonNullable<CheckOpts['eventResolveError']>;
|
|
22
|
+
/**
|
|
23
|
+
* `check`'s EVENT_BYOA_UNVERIFIED inputs: the event facts and the recorded BYOA
|
|
24
|
+
* checks, from the same `library.eventFacts` / `library.byoaVerifications` that
|
|
25
|
+
* `serialize` reads for the same refusal. Both CLIs spread this into their
|
|
26
|
+
* check options, so the inner loop and the emit rung refuse the same flows.
|
|
27
|
+
*/
|
|
28
|
+
export declare function byoaCheckOpts(library: Library): Pick<CheckOpts, 'eventFacts' | 'byoa'>;
|
package/dist/cli-library.js
CHANGED
|
@@ -54,3 +54,22 @@ export function eventResolveErrorFor(library) {
|
|
|
54
54
|
}
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* `check`'s EVENT_BYOA_UNVERIFIED inputs: the event facts and the recorded BYOA
|
|
59
|
+
* checks, from the same `library.eventFacts` / `library.byoaVerifications` that
|
|
60
|
+
* `serialize` reads for the same refusal. Both CLIs spread this into their
|
|
61
|
+
* check options, so the inner loop and the emit rung refuse the same flows.
|
|
62
|
+
*/
|
|
63
|
+
export function byoaCheckOpts(library) {
|
|
64
|
+
return {
|
|
65
|
+
eventFacts: (connector, event, object) => {
|
|
66
|
+
try {
|
|
67
|
+
return library.eventFacts(connector, event, object);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return undefined; // unknown fails open, same as every library callback
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
byoa: library.byoaVerifications,
|
|
74
|
+
};
|
|
75
|
+
}
|
package/dist/compile-cli.js
CHANGED
|
@@ -23,12 +23,13 @@ import { basename, dirname, join, resolve } from 'node:path';
|
|
|
23
23
|
import { resolveFlowFile, loadBuiltFlow } from './load.js';
|
|
24
24
|
import { serializeWithSidecars, connectorInputErrors } from './serialize.js';
|
|
25
25
|
import { check } from './check.js';
|
|
26
|
-
import { eventResolveErrorFor, openCliLibrary } from './cli-library.js';
|
|
26
|
+
import { byoaCheckOpts, eventResolveErrorFor, openCliLibrary } from './cli-library.js';
|
|
27
27
|
import { Bindings } from './bindings.js';
|
|
28
28
|
import { runWhenInvokedDirectly } from './cli-run.js';
|
|
29
29
|
import { connectionResources, mergeBindingsV2, readBindingsV2 } from './bindings-v2.js';
|
|
30
30
|
import { ensureTypeScriptRuntime } from './node-runtime.js';
|
|
31
31
|
import { BindingRefusal } from './core/binding-messages.js';
|
|
32
|
+
import { ByoaRefusal } from './core/byoa.js';
|
|
32
33
|
function opt(argv, name) {
|
|
33
34
|
const i = argv.indexOf(name);
|
|
34
35
|
return i >= 0 ? argv[i + 1] : undefined;
|
|
@@ -96,6 +97,7 @@ export async function run(argv) {
|
|
|
96
97
|
// printed as `✗ [EVENT_UNKNOWN]` before stopping instead of hiding
|
|
97
98
|
// behind "1 error(s); not emitting".
|
|
98
99
|
eventResolveError: eventResolveErrorFor(library),
|
|
100
|
+
...byoaCheckOpts(library),
|
|
99
101
|
eventParams: (connector, event) => {
|
|
100
102
|
try {
|
|
101
103
|
return library.eventParameterFields(connector, event);
|
|
@@ -134,16 +136,17 @@ export async function run(argv) {
|
|
|
134
136
|
// check()'s. Warnings are never fatal: the .flow is still written, exit stays 0.
|
|
135
137
|
// A binding LABEL the serializer refuses (its own name as the resourceKey, or a
|
|
136
138
|
// label a populated bindings.json does not declare) is an error, not a
|
|
137
|
-
// warning
|
|
138
|
-
//
|
|
139
|
-
//
|
|
139
|
+
// warning, and so is a BYOA-only event on a connection `prepare` never checked
|
|
140
|
+
// (EVENT_BYOA_UNVERIFIED): the check pass above reports each first, and this is
|
|
141
|
+
// the same refusal in the same `✗ [CODE]` shape when that pass was skipped
|
|
142
|
+
// (`--no-check`, or an emit-only project). Nothing is written.
|
|
140
143
|
const warnings = [];
|
|
141
144
|
let emitted;
|
|
142
145
|
try {
|
|
143
146
|
emitted = serializeWithSidecars(built, { library, bindings, warn: (w) => warnings.push(w) });
|
|
144
147
|
}
|
|
145
148
|
catch (error) {
|
|
146
|
-
if (!(error instanceof BindingRefusal))
|
|
149
|
+
if (!(error instanceof BindingRefusal || error instanceof ByoaRefusal))
|
|
147
150
|
throw error;
|
|
148
151
|
for (const w of warnings)
|
|
149
152
|
console.error(` ⚠ [${w.code}] ${w.message}`);
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/** The diagnostic code, in `check` and on the error `serialize` throws. */
|
|
2
|
+
export declare const EVENT_BYOA_UNVERIFIED = "EVENT_BYOA_UNVERIFIED";
|
|
3
|
+
/** One verification `prepare` recorded. Only a PASSING check is ever written. */
|
|
4
|
+
export interface ByoaRecord {
|
|
5
|
+
byoa: true;
|
|
6
|
+
/** The connection's `Name`, so a person reading the file can tell which one it is. */
|
|
7
|
+
connectionName?: string;
|
|
8
|
+
/** `YYYY-MM-DD`: a resolved answer is a snapshot, like every other ledger row. */
|
|
9
|
+
verifiedAt?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The ledger's `byoa` section: `byoa[<event node type>][<connection id>]`.
|
|
13
|
+
*
|
|
14
|
+
* Keyed per EVENT, not per connector — Slack `send-reply` needs no BYOA, the
|
|
15
|
+
* message trigger does — and per CONNECTION, so swapping the connection in
|
|
16
|
+
* bindings.json un-verifies the event until it is prepared again.
|
|
17
|
+
*/
|
|
18
|
+
export type ByoaLedger = Record<string, Record<string, ByoaRecord>>;
|
|
19
|
+
/** The event facts the rule reads, as `Library.eventFacts` answers them. */
|
|
20
|
+
export interface ByoaEventFacts {
|
|
21
|
+
byoaConnection: boolean;
|
|
22
|
+
isWebhookUrlVisible: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The key an event's record is stored under: the WAIT form of the node type,
|
|
26
|
+
* the form `prepare` stores an event's overlay and lookup rows under, whichever
|
|
27
|
+
* role (`onEvent` or `waitForEvent`) the flow uses it in.
|
|
28
|
+
*/
|
|
29
|
+
export declare function byoaLedgerKey(connectorKey: string, event: string): string;
|
|
30
|
+
/** The record for `connectionId` under `nodeType`, matching the id case-insensitively. */
|
|
31
|
+
export declare function byoaRecordFor(ledger: ByoaLedger | undefined, nodeType: string, connectionId: string): ByoaRecord | undefined;
|
|
32
|
+
/** What `compile` throws for the refusal; `code` lets the CLI print it as `✗ [CODE]`. */
|
|
33
|
+
export declare class ByoaRefusal extends Error {
|
|
34
|
+
readonly code = "EVENT_BYOA_UNVERIFIED";
|
|
35
|
+
constructor(message: string);
|
|
36
|
+
}
|
|
37
|
+
/** One subscription, as the rule needs it. */
|
|
38
|
+
export interface ByoaSubscription {
|
|
39
|
+
connector: string;
|
|
40
|
+
event: string;
|
|
41
|
+
object?: string;
|
|
42
|
+
/** The `connection:` label as authored — a bindings.json name, or a connection id. */
|
|
43
|
+
connection?: unknown;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Why a BYOA-only event cannot be trusted to fire on its bound connection, or
|
|
47
|
+
* `undefined` when there is nothing to refuse. The ONE condition behind
|
|
48
|
+
* `check`'s EVENT_BYOA_UNVERIFIED error and `compile`'s refusal, so the two
|
|
49
|
+
* cannot disagree.
|
|
50
|
+
*
|
|
51
|
+
* Refuses when the event's facts say BYOA and the connection resolves to a real
|
|
52
|
+
* connection id with no `byoa` record for this event. Silent when:
|
|
53
|
+
*
|
|
54
|
+
* - the facts are unknown or say no BYOA;
|
|
55
|
+
* - the connection does not resolve to a real id — no bindings.json, a label it
|
|
56
|
+
* does not declare, or a placeholder (`0000…`, `<…>`, the label itself). There
|
|
57
|
+
* is no id to look a record up by, and nothing is lost: such a flow cannot
|
|
58
|
+
* run its trigger on any connection, and BINDING_SELF_NAME /
|
|
59
|
+
* BINDING_UNDECLARED / CONNECTION_STUB already say so.
|
|
60
|
+
*
|
|
61
|
+
* @param where - The call site, as the other event diagnostics name it (`trigger`, `waitForEvent "x"`).
|
|
62
|
+
* @param sub - The subscription.
|
|
63
|
+
* @param facts - The event's facts from the library, or `undefined` when unknown.
|
|
64
|
+
* @param ledger - The overlay ledger's `byoa` section.
|
|
65
|
+
* @param bindings - Resolves the `connection:` label to an id.
|
|
66
|
+
*/
|
|
67
|
+
export declare function byoaUnverified(where: string, sub: ByoaSubscription, facts: ByoaEventFacts | undefined, ledger: ByoaLedger | undefined, bindings: {
|
|
68
|
+
resolve(name?: string): string | undefined;
|
|
69
|
+
} | undefined): string | undefined;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* core/byoa — connector events that only fire on a BYOA connection (#848).
|
|
3
|
+
*
|
|
4
|
+
* BYOA is "bring your own app": an Integration Service connection created with
|
|
5
|
+
* the customer's own OAuth app. Some event objects need one — Slack
|
|
6
|
+
* `message-received-in-slack` is a webhook the customer's Slack app delivers,
|
|
7
|
+
* so on a standard connection the trigger deploys, validates, and never fires.
|
|
8
|
+
*
|
|
9
|
+
* Only a live call can tell a BYOA connection from a standard one, and `registry
|
|
10
|
+
* prepare` makes it: on an event whose facts say BYOA it lists the tenant's BYOA
|
|
11
|
+
* connections, refuses a connection that is not among them, and records one
|
|
12
|
+
* that is under `byoa[<event node type>][<connection id>]` in
|
|
13
|
+
* `connectors-local/resolutions.json`. This module is the offline half: the
|
|
14
|
+
* record's shape, and the one condition `check` reports and `compile` throws
|
|
15
|
+
* when a BYOA event's bound connection carries no record — typically because
|
|
16
|
+
* the connection was bound by preparing an ACTION of the same connector, which
|
|
17
|
+
* checks nothing about BYOA. `check` stays offline: it checks that the live
|
|
18
|
+
* check happened, never the tenant.
|
|
19
|
+
*
|
|
20
|
+
* No `node:fs` here — `check` and `serialize` import this module, and the file
|
|
21
|
+
* read lives in `lookup-store.ts` beside the ledger's other reader.
|
|
22
|
+
*/
|
|
23
|
+
import { GUID_RE } from './binding-messages.js';
|
|
24
|
+
import { prepareCommand } from './cli-spelling.js';
|
|
25
|
+
/** The diagnostic code, in `check` and on the error `serialize` throws. */
|
|
26
|
+
export const EVENT_BYOA_UNVERIFIED = 'EVENT_BYOA_UNVERIFIED';
|
|
27
|
+
/**
|
|
28
|
+
* The key an event's record is stored under: the WAIT form of the node type,
|
|
29
|
+
* the form `prepare` stores an event's overlay and lookup rows under, whichever
|
|
30
|
+
* role (`onEvent` or `waitForEvent`) the flow uses it in.
|
|
31
|
+
*/
|
|
32
|
+
export function byoaLedgerKey(connectorKey, event) {
|
|
33
|
+
return `uipath.connector.event.${connectorKey}.${event}`;
|
|
34
|
+
}
|
|
35
|
+
/** The record for `connectionId` under `nodeType`, matching the id case-insensitively. */
|
|
36
|
+
export function byoaRecordFor(ledger, nodeType, connectionId) {
|
|
37
|
+
const perEvent = ledger?.[nodeType];
|
|
38
|
+
if (!perEvent || typeof perEvent !== 'object')
|
|
39
|
+
return undefined;
|
|
40
|
+
const wanted = connectionId.toLowerCase();
|
|
41
|
+
for (const [id, record] of Object.entries(perEvent)) {
|
|
42
|
+
if (id.toLowerCase() === wanted && record && typeof record === 'object' && record.byoa === true)
|
|
43
|
+
return record;
|
|
44
|
+
}
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
/** What `compile` throws for the refusal; `code` lets the CLI print it as `✗ [CODE]`. */
|
|
48
|
+
export class ByoaRefusal extends Error {
|
|
49
|
+
code = EVENT_BYOA_UNVERIFIED;
|
|
50
|
+
constructor(message) {
|
|
51
|
+
super(message);
|
|
52
|
+
this.name = 'ByoaRefusal';
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** An all-zero GUID: a placeholder no tenant issues (compile's CONNECTION_STUB). */
|
|
56
|
+
const STUB_GUID_RE = /^0{8}-0{4}-0{4}-0{4}-/;
|
|
57
|
+
/**
|
|
58
|
+
* Why a BYOA-only event cannot be trusted to fire on its bound connection, or
|
|
59
|
+
* `undefined` when there is nothing to refuse. The ONE condition behind
|
|
60
|
+
* `check`'s EVENT_BYOA_UNVERIFIED error and `compile`'s refusal, so the two
|
|
61
|
+
* cannot disagree.
|
|
62
|
+
*
|
|
63
|
+
* Refuses when the event's facts say BYOA and the connection resolves to a real
|
|
64
|
+
* connection id with no `byoa` record for this event. Silent when:
|
|
65
|
+
*
|
|
66
|
+
* - the facts are unknown or say no BYOA;
|
|
67
|
+
* - the connection does not resolve to a real id — no bindings.json, a label it
|
|
68
|
+
* does not declare, or a placeholder (`0000…`, `<…>`, the label itself). There
|
|
69
|
+
* is no id to look a record up by, and nothing is lost: such a flow cannot
|
|
70
|
+
* run its trigger on any connection, and BINDING_SELF_NAME /
|
|
71
|
+
* BINDING_UNDECLARED / CONNECTION_STUB already say so.
|
|
72
|
+
*
|
|
73
|
+
* @param where - The call site, as the other event diagnostics name it (`trigger`, `waitForEvent "x"`).
|
|
74
|
+
* @param sub - The subscription.
|
|
75
|
+
* @param facts - The event's facts from the library, or `undefined` when unknown.
|
|
76
|
+
* @param ledger - The overlay ledger's `byoa` section.
|
|
77
|
+
* @param bindings - Resolves the `connection:` label to an id.
|
|
78
|
+
*/
|
|
79
|
+
export function byoaUnverified(where, sub, facts, ledger, bindings) {
|
|
80
|
+
if (!facts?.byoaConnection)
|
|
81
|
+
return undefined;
|
|
82
|
+
if (typeof sub.connection !== 'string' || sub.connection === '')
|
|
83
|
+
return undefined;
|
|
84
|
+
const label = sub.connection;
|
|
85
|
+
const id = bindings?.resolve(label) ?? label;
|
|
86
|
+
if (!GUID_RE.test(id) || STUB_GUID_RE.test(id))
|
|
87
|
+
return undefined;
|
|
88
|
+
if (byoaRecordFor(ledger, byoaLedgerKey(sub.connector, sub.event), id))
|
|
89
|
+
return undefined;
|
|
90
|
+
const named = label === id ? `connection ${id}` : `connection \`${label}\` (${id})`;
|
|
91
|
+
const object = sub.object ? ` --object ${sub.object}` : '';
|
|
92
|
+
return `${where}: \`${sub.connector}\` \`${sub.event}\` requires a BYOA (bring-your-own-app) connection — `
|
|
93
|
+
+ `one created with your own OAuth app — and ${named} was never checked against that requirement. `
|
|
94
|
+
+ `A standard connection deploys and the trigger never fires. Prepare the event on this connection, `
|
|
95
|
+
+ `which refuses a standard connection and records a BYOA one:\n`
|
|
96
|
+
+ ` ${prepareCommand(sub.connector, sub.event)}${object} --connection-id ${id}\n`
|
|
97
|
+
+ `BYOA connections (rows for \`${sub.connector}\`): uip is connections list --byoa --all-folders`;
|
|
98
|
+
}
|
package/dist/core/library.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type LookupResolutions, type LookupSpec } from './lookups.js';
|
|
2
|
+
import type { ByoaEventFacts, ByoaLedger } from './byoa.js';
|
|
2
3
|
export interface ResolvedConnector {
|
|
3
4
|
nodeType: string;
|
|
4
5
|
version: string;
|
|
@@ -229,6 +230,12 @@ export declare class Library {
|
|
|
229
230
|
* id from someone else's connection.
|
|
230
231
|
*/
|
|
231
232
|
readonly lookupResolutions: LookupResolutions;
|
|
233
|
+
/**
|
|
234
|
+
* The BYOA checks `prepare` recorded (`resolutions.json` → `byoa`), from the
|
|
235
|
+
* overlay only, for the same reason as {@link lookupResolutions}: whether a
|
|
236
|
+
* connection is BYOA is a fact about one tenant.
|
|
237
|
+
*/
|
|
238
|
+
readonly byoaVerifications: ByoaLedger;
|
|
232
239
|
/** Collections the ledger declares connector-invariant; overlay unions over baked. */
|
|
233
240
|
private readonly invariantCollections;
|
|
234
241
|
constructor(dir: string, opts?: LibraryOpts);
|
|
@@ -348,6 +355,22 @@ export declare class Library {
|
|
|
348
355
|
* raw library snapshot carries ZERO trigger/event entries).
|
|
349
356
|
*/
|
|
350
357
|
eventParameterFields(key: string, event: string, objectName?: string): EventParameterFields | undefined;
|
|
358
|
+
/**
|
|
359
|
+
* Whether a connector event needs a BYOA (bring-your-own-app) connection, and
|
|
360
|
+
* whether its webhook URL must be registered by hand — or `undefined` when no
|
|
361
|
+
* layer carries the event in either form.
|
|
362
|
+
*
|
|
363
|
+
* Read from the `.v1def.json` sidecar, where the library build stamps the
|
|
364
|
+
* facts from the event's `triggers objects` row and `prepare` stores them on
|
|
365
|
+
* the overlay. A fact is TRUE when ANY layer, in either form (`.event.` or
|
|
366
|
+
* `.trigger.`), says so: the requirement belongs to the event object, not to
|
|
367
|
+
* a tenant, and an overlay written by an older `prepare` — which stored the
|
|
368
|
+
* `describe` answer, `false` for Slack `message-received-in-slack` — must not
|
|
369
|
+
* hide the true fact a newer baked library carries.
|
|
370
|
+
*
|
|
371
|
+
* `check` and `compile` read this through the one rule in `core/byoa.ts`.
|
|
372
|
+
*/
|
|
373
|
+
eventFacts(key: string, event: string, objectName?: string): ByoaEventFacts | undefined;
|
|
351
374
|
/**
|
|
352
375
|
* The filterable-field names a connector event declares — the vocabulary a
|
|
353
376
|
* `filters` leaf may name.
|