@uipath/maestro-builder-sdk 6.14.5 → 6.15.1
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 +71 -71
- package/dist/api-members.md +324 -323
- package/dist/bpmn/bpmn-sdk.d.ts +16 -0
- package/dist/bpmn/bpmn-sdk.js +2 -0
- package/dist/bpmn/check-cli.js +22 -5
- package/dist/bpmn/check.d.ts +15 -1
- package/dist/bpmn/check.js +69 -2
- package/dist/bpmn/compile-cli.js +24 -14
- package/dist/bpmn/connector.js +29 -14
- package/dist/bpmn/decompile-cli.js +3 -3
- package/dist/bpmn/intsvc.js +5 -2
- package/dist/bpmn/serialize.d.ts +2 -0
- package/dist/bpmn/serialize.js +1 -1
- package/dist/case/compile-cli.js +6 -9
- package/dist/case/decompile-cli.js +2 -2
- package/dist/case/serialize.js +1 -1
- package/dist/check-cli.js +6 -35
- package/dist/check.d.ts +16 -148
- package/dist/check.js +33 -709
- package/dist/cli/commands/bpmn.js +15 -0
- package/dist/cli-library.d.ts +6 -25
- package/dist/cli-library.js +6 -73
- package/dist/core/cli-library.d.ts +47 -0
- package/dist/core/cli-library.js +150 -0
- package/dist/core/connector-checks.d.ts +226 -0
- package/dist/core/connector-checks.js +518 -0
- package/dist/core/connector-inputs.d.ts +80 -0
- package/dist/core/connector-inputs.js +453 -0
- package/dist/core/event-checks.d.ts +137 -0
- package/dist/core/event-checks.js +321 -0
- package/dist/decompile-cli.js +2 -2
- package/dist/decompile.js +1 -1
- package/dist/registry/generators/markdown/sections/example.d.ts +7 -1
- package/dist/registry/generators/markdown/sections/example.js +70 -5
- package/dist/registry/generators/markdown/sections/lookups.js +29 -8
- package/dist/registry/prepare/messages.js +6 -1
- package/dist/registry/prepare/source-import.d.ts +3 -3
- package/dist/registry/prepare/source-import.js +15 -4
- package/dist/registry/prepare/source-labels.js +31 -4
- package/dist/serialize.d.ts +11 -11
- package/dist/serialize.js +27 -420
- package/dist/workdir.d.ts +10 -0
- package/dist/workdir.js +14 -1
- package/package.json +2 -2
- /package/dist/{event-filters.d.ts → core/event-filters.d.ts} +0 -0
- /package/dist/{event-filters.js → core/event-filters.js} +0 -0
|
@@ -46,7 +46,15 @@ import { GUID_RE } from "../../core/binding-messages.js";
|
|
|
46
46
|
import { relativePosix } from "../compat/paths.js";
|
|
47
47
|
import { canonicalPath, flowSources, isRelativeSpecifier } from "./source-import.js";
|
|
48
48
|
import { inWorkDir } from "../../workdir.js";
|
|
49
|
-
const CALLEES = new Set([
|
|
49
|
+
const CALLEES = new Set([
|
|
50
|
+
"connector", "onEvent", "waitForEvent", "eventTrigger", "dataService",
|
|
51
|
+
]);
|
|
52
|
+
/**
|
|
53
|
+
* Data Fabric's connector key, spelled here rather than imported from
|
|
54
|
+
* `bpmn/data-service.ts` — `registry/` does not depend on a family's emitter.
|
|
55
|
+
* `.dataService()` is the one call that binds a connection without naming one.
|
|
56
|
+
*/
|
|
57
|
+
const DATA_SERVICE_KEY = "uipath-uipath-dataservice";
|
|
50
58
|
function isFile(path) {
|
|
51
59
|
try {
|
|
52
60
|
return statSync(path).isFile();
|
|
@@ -81,7 +89,8 @@ export function sourceBindingLabels(options) {
|
|
|
81
89
|
catch {
|
|
82
90
|
continue; // unreadable: it cannot be the author's working source
|
|
83
91
|
}
|
|
84
|
-
|
|
92
|
+
const family = file.endsWith(".bpmn.ts") ? "bpmn" : "flow";
|
|
93
|
+
readFile(file, relativePosix(root, file), text, options.connectorKey, moduleDirs, result, family);
|
|
85
94
|
}
|
|
86
95
|
return result;
|
|
87
96
|
}
|
|
@@ -115,7 +124,7 @@ function propertyName(name) {
|
|
|
115
124
|
return name.text;
|
|
116
125
|
return undefined;
|
|
117
126
|
}
|
|
118
|
-
function readFile(file, shown, text, connectorKey, moduleDirs, result) {
|
|
127
|
+
function readFile(file, shown, text, connectorKey, moduleDirs, result, family) {
|
|
119
128
|
const sf = ts.createSourceFile(file, text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
120
129
|
// `parseDiagnostics` is where the scanner records syntax problems (see core/expr-check.ts).
|
|
121
130
|
const problems = sf.parseDiagnostics;
|
|
@@ -316,6 +325,24 @@ function readFile(file, shown, text, connectorKey, moduleDirs, result) {
|
|
|
316
325
|
unplaceable(at, args.why);
|
|
317
326
|
else if (args.length === 0)
|
|
318
327
|
unplaceable(at, "spreads an empty array");
|
|
328
|
+
else if (family === "bpmn") {
|
|
329
|
+
// Every BPMN call leads with the ELEMENT ID, so the key is one further
|
|
330
|
+
// along than Flow's and the options are last. `.dataService()` names no
|
|
331
|
+
// connector at all — there is only one it can mean.
|
|
332
|
+
if (callee === "dataService") {
|
|
333
|
+
record(DATA_SERVICE_KEY, args[1], at);
|
|
334
|
+
}
|
|
335
|
+
else if (callee === "connector" || callee === "eventTrigger" || callee === "waitForEvent") {
|
|
336
|
+
const key = constString(args[1]);
|
|
337
|
+
// A computed key is not a reason to distrust the whole file here: a
|
|
338
|
+
// BPMN options object is a literal at the call site, and the label
|
|
339
|
+
// this reads is the author's own name for a binding, not a fact the
|
|
340
|
+
// emitter depends on. Flow's `unplaceable` exists because its first
|
|
341
|
+
// argument may be a DESCRIPTOR whose key decides everything.
|
|
342
|
+
if (key !== undefined)
|
|
343
|
+
record(key, args[callee === "connector" ? 4 : 3], at);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
319
346
|
else if (callee === "connector") {
|
|
320
347
|
const [first] = args;
|
|
321
348
|
const literalKey = constString(first);
|
|
@@ -325,7 +352,7 @@ function readFile(file, shown, text, connectorKey, moduleDirs, result) {
|
|
|
325
352
|
else
|
|
326
353
|
record(key, args[literalKey !== undefined ? 3 : 2], at);
|
|
327
354
|
}
|
|
328
|
-
else {
|
|
355
|
+
else if (callee === "onEvent" || callee === "waitForEvent") {
|
|
329
356
|
const [first] = args;
|
|
330
357
|
const subscription = constObject(first);
|
|
331
358
|
if (subscription) {
|
package/dist/serialize.d.ts
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import type { BuiltFlow } from './flow-sdk.js';
|
|
2
|
-
import type { Library
|
|
2
|
+
import type { Library } from './library.js';
|
|
3
3
|
import type { Bindings } from './bindings.js';
|
|
4
|
-
import {
|
|
4
|
+
import { connectorInputErrors as coreConnectorInputErrors } from './core/connector-inputs.js';
|
|
5
|
+
/**
|
|
6
|
+
* What `compile` would refuse these connector inputs with, or `undefined` when it
|
|
7
|
+
* would accept them — Flow's binding of the shared validator.
|
|
8
|
+
*
|
|
9
|
+
* `render` is not optional here: `renderDeep` is what REFUSES a lookup token in a
|
|
10
|
+
* position no lookup is resolved from (inside an opaque `object` input, say), and
|
|
11
|
+
* dropping it would make `check` quietly accept what `compile` throws on.
|
|
12
|
+
*/
|
|
13
|
+
export declare function connectorInputErrors(rc: Parameters<typeof coreConnectorInputErrors>[0], inputs: Record<string, unknown>, resolutions?: Parameters<typeof coreConnectorInputErrors>[2], lookups?: Parameters<typeof coreConnectorInputErrors>[3]): string | undefined;
|
|
5
14
|
/**
|
|
6
15
|
* A compile-time observation that does not stop emission. `check()` has its
|
|
7
16
|
* diagnostics; this is the serializer's own channel for things it can only see
|
|
@@ -82,12 +91,3 @@ export declare function serialize(built: BuiltFlow, opts?: SerializeOpts): any;
|
|
|
82
91
|
* placeholder and the step that carries it.
|
|
83
92
|
*/
|
|
84
93
|
export declare function assertBindingPlaceholdersResolve(flow: any): void;
|
|
85
|
-
/**
|
|
86
|
-
* What `compile` would refuse these connector inputs with, or `undefined` when it
|
|
87
|
-
* would accept them — the SAME code path `buildConnectorInputs` runs at emit time,
|
|
88
|
-
* so `check` and `compile` cannot disagree about a missing required field, an
|
|
89
|
-
* unknown or mistyped input, or a container part passed as a value. Unresolved
|
|
90
|
-
* lookups are the one thing skipped: `check` already reports them as
|
|
91
|
-
* LOOKUP_UNRESOLVED with the resolution recipe.
|
|
92
|
-
*/
|
|
93
|
-
export declare function connectorInputErrors(rc: ResolvedConnector, inputs: Record<string, unknown>, resolutions?: LookupResolutions, lookups?: Readonly<Record<string, LookupSpec>>): string | undefined;
|
package/dist/serialize.js
CHANGED
|
@@ -23,7 +23,23 @@ import { declaredExits, hasExits, stopsAtEnd } from './core/step-ports.js';
|
|
|
23
23
|
import { assertNodeEnvelope } from './core/node-envelope.js';
|
|
24
24
|
import { buildConfiguration, transportHttpMethod } from './config.js';
|
|
25
25
|
import { stableId } from './core/stable-id.js';
|
|
26
|
-
import { readEventFilter, eventFilterProblem, eventFilterJmes, eventFilterTreeLeaf } from './event-filters.js';
|
|
26
|
+
import { readEventFilter, eventFilterProblem, eventFilterJmes, eventFilterTreeLeaf } from './core/event-filters.js';
|
|
27
|
+
import { FLOW_EXPR_DIALECT } from './core/connector-checks.js';
|
|
28
|
+
import { buildConnectorInputs, connectorInputErrors as coreConnectorInputErrors, multipartBodyFieldName } from './core/connector-inputs.js';
|
|
29
|
+
/**
|
|
30
|
+
* What `compile` would refuse these connector inputs with, or `undefined` when it
|
|
31
|
+
* would accept them — Flow's binding of the shared validator.
|
|
32
|
+
*
|
|
33
|
+
* `render` is not optional here: `renderDeep` is what REFUSES a lookup token in a
|
|
34
|
+
* position no lookup is resolved from (inside an opaque `object` input, say), and
|
|
35
|
+
* dropping it would make `check` quietly accept what `compile` throws on.
|
|
36
|
+
*/
|
|
37
|
+
export function connectorInputErrors(rc, inputs, resolutions = {}, lookups = {}) {
|
|
38
|
+
return coreConnectorInputErrors(rc, inputs, resolutions, lookups, {
|
|
39
|
+
dialect: FLOW_EXPR_DIALECT,
|
|
40
|
+
render: (value) => renderDeep(value, NO_RENAME),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
27
43
|
import { isLookupToken, resolvedValue, unresolvedLookupMessage, } from './core/lookups.js';
|
|
28
44
|
import { declaredScriptShape, inferScriptShape } from './core/script-return.js';
|
|
29
45
|
import { migrateToSchemaVersion } from './migrate.js';
|
|
@@ -4654,12 +4670,16 @@ export function serialize(built, opts = {}) {
|
|
|
4654
4670
|
const { entries: connBindings } = connectionResourceBindings({ def: rc.v1def, connectorKey: rc.connectorKey, connectionId: conn, folderKey: folder }, resourceBindings);
|
|
4655
4671
|
for (const b of connBindings)
|
|
4656
4672
|
resourceBindings.set(b.id, b);
|
|
4657
|
-
const connectorInputs = buildConnectorInputs(rc, spec.inputs,
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4673
|
+
const connectorInputs = buildConnectorInputs(rc, spec.inputs, {
|
|
4674
|
+
dialect: FLOW_EXPR_DIALECT,
|
|
4675
|
+
render: (value) => renderDeep(value, scope.rename),
|
|
4676
|
+
resolutions: opts.library.lookupResolutions ?? {},
|
|
4677
|
+
// Only for the missing-required hint; a test double that is not a full
|
|
4678
|
+
// Library simply gets the plain message.
|
|
4679
|
+
lookups: typeof opts.library.lookupSpecs === 'function'
|
|
4680
|
+
? opts.library.lookupSpecs(spec.key, spec.action, spec.version, spec.object)
|
|
4681
|
+
: {},
|
|
4682
|
+
});
|
|
4663
4683
|
const replayInputs = {
|
|
4664
4684
|
...(connectorInputs.bodyParameters ?? {}),
|
|
4665
4685
|
...(connectorInputs.pathParameters ?? {}),
|
|
@@ -6615,419 +6635,6 @@ export function assertBindingPlaceholdersResolve(flow) {
|
|
|
6615
6635
|
+ `"invalid GUID <bindings.…>") and refuses to publish. This is a serializer defect, not an authoring `
|
|
6616
6636
|
+ `error; report it with the flow source.`);
|
|
6617
6637
|
}
|
|
6618
|
-
/**
|
|
6619
|
-
* Validate a connector's activity inputs against its declared field list and
|
|
6620
|
-
* return them ROUTED to the wire slots the platform reads. Catches the three
|
|
6621
|
-
* connector-input mistakes — missing required, unknown key, type mismatch — at
|
|
6622
|
-
* compile, where the author can act on them.
|
|
6623
|
-
*
|
|
6624
|
-
* Generated descriptors accept natural nested objects and arrays; the legacy
|
|
6625
|
-
* string form also accepts exact flat registry field ids. Routing remains the
|
|
6626
|
-
* library's business because a field's location is a property of the operation.
|
|
6627
|
-
* Each projected registry field lands in the slot its declaration names:
|
|
6628
|
-
*
|
|
6629
|
-
* ```jsonc
|
|
6630
|
-
* // connector('uipath-atlassian-jira','get-issue', { issueId: …, project: 'IN', issuetype: 'Task' })
|
|
6631
|
-
* "inputs": { "detail": {
|
|
6632
|
-
* "endpoint": "/curated_get_issue/{issueId}",
|
|
6633
|
-
* "pathParameters": { "issueId": "IN-28" },
|
|
6634
|
-
* "queryParameters": { "project": "IN", "issuetype": "Task" },
|
|
6635
|
-
* "bodyParameters": {} } }
|
|
6636
|
-
* ```
|
|
6637
|
-
*
|
|
6638
|
-
* That is the platform's own encoding, measured on the deployed corpus: 1,283
|
|
6639
|
-
* connector nodes in the nightly artifacts, 384 carrying `pathParameters` and
|
|
6640
|
-
* 576 carrying `queryParameters`, always as a FLAT map keyed by the parameter's
|
|
6641
|
-
* DECLARED name (`send_as`, `issuetype`, `channel_id` keep their spelling — no
|
|
6642
|
-
* camelCasing) and always as a SIBLING of `bodyParameters` rather than as a
|
|
6643
|
-
* location-tagged entry inside it.
|
|
6644
|
-
*/
|
|
6645
|
-
/** For a missing required lookup field: what it sends and how to resolve it. Empty for a plain field. */
|
|
6646
|
-
function missingLookupHint(field, lookup) {
|
|
6647
|
-
if (!lookup)
|
|
6648
|
-
return '';
|
|
6649
|
-
const target = `${lookup.objectName ?? lookup.path}.${lookup.value}`;
|
|
6650
|
-
const alias = Object.keys(lookup.aliases)[0];
|
|
6651
|
-
const call = alias
|
|
6652
|
-
? `lookup(<Descriptor>, '${field}').${alias}('<value>')`
|
|
6653
|
-
: `lookup(<Descriptor>, '${field}').by('${lookup.by[0] ?? 'id'}', '<value>')`;
|
|
6654
|
-
return ` — a lookup id (\`${target}\`), not a name or address: ${call}`;
|
|
6655
|
-
}
|
|
6656
|
-
/**
|
|
6657
|
-
* What `compile` would refuse these connector inputs with, or `undefined` when it
|
|
6658
|
-
* would accept them — the SAME code path `buildConnectorInputs` runs at emit time,
|
|
6659
|
-
* so `check` and `compile` cannot disagree about a missing required field, an
|
|
6660
|
-
* unknown or mistyped input, or a container part passed as a value. Unresolved
|
|
6661
|
-
* lookups are the one thing skipped: `check` already reports them as
|
|
6662
|
-
* LOOKUP_UNRESOLVED with the resolution recipe.
|
|
6663
|
-
*/
|
|
6664
|
-
export function connectorInputErrors(rc, inputs, resolutions = {}, lookups = {}) {
|
|
6665
|
-
try {
|
|
6666
|
-
buildConnectorInputs(rc, inputs, NO_RENAME, resolutions, lookups, { unresolvedLookups: 'ignore' });
|
|
6667
|
-
return undefined;
|
|
6668
|
-
}
|
|
6669
|
-
catch (e) {
|
|
6670
|
-
return e instanceof Error ? e.message : String(e);
|
|
6671
|
-
}
|
|
6672
|
-
}
|
|
6673
|
-
function buildConnectorInputs(rc, inputs, rename = NO_RENAME, resolutions = {}, lookups = {}, policy = {}) {
|
|
6674
|
-
const fields = (rc.fields ?? []);
|
|
6675
|
-
const byName = new Map(fields.map((f) => [f.name, f]));
|
|
6676
|
-
const errs = validateNestedConnectorKeys(fields, inputs, byName);
|
|
6677
|
-
const routed = {
|
|
6678
|
-
bodyParameters: {}, pathParameters: {}, queryParameters: {},
|
|
6679
|
-
};
|
|
6680
|
-
// The author's values for FILE parts, by part name. The part LIST itself comes
|
|
6681
|
-
// from the library (`rc.multipartParts`): a string-typed part is a transport
|
|
6682
|
-
// container the runtime composes from `bodyParameters`, so it is listed without
|
|
6683
|
-
// a value and is never an author input — see `readFields` in core/library.ts.
|
|
6684
|
-
const partValues = new Map();
|
|
6685
|
-
for (const f of fields) {
|
|
6686
|
-
const flat = Object.prototype.hasOwnProperty.call(inputs, f.name);
|
|
6687
|
-
let nested = flat
|
|
6688
|
-
? { found: true, value: inputs[f.name], projectedArrays: 0 }
|
|
6689
|
-
: readConnectorPath(inputs, f.name);
|
|
6690
|
-
if (!nested.found) {
|
|
6691
|
-
// A required LOOKUP field is missing far more often because the author does
|
|
6692
|
-
// not know what to put in it than because they forgot it exists: name the
|
|
6693
|
-
// id it sends and the helper that finds one, so "(string)" is not read as
|
|
6694
|
-
// "any string" — an e-mail into `fields.reporter.id` is exactly that misreading.
|
|
6695
|
-
if (f.required)
|
|
6696
|
-
errs.push(`missing required input "${f.name}" (${f.type ?? 'value'})${missingLookupHint(f.name, lookups[f.name])}`);
|
|
6697
|
-
continue;
|
|
6698
|
-
}
|
|
6699
|
-
// An unresolved lookup is swapped for its recorded id, or reported. It is
|
|
6700
|
-
// NOT rendered as a placeholder: emitting something for a value nobody
|
|
6701
|
-
// resolved would produce a flow that compiles and then talks to the wrong
|
|
6702
|
-
// record, which is exactly the failure the lookup surface exists to remove.
|
|
6703
|
-
//
|
|
6704
|
-
// A token is swapped wherever the field's value can hold one: the whole
|
|
6705
|
-
// value, or an element of an array at any depth. A multiselect is
|
|
6706
|
-
// `users: [lookup(…), lookup(…)]` — the form `ConnectorLookupValue`'s array
|
|
6707
|
-
// branch types and the generated `## Example` prints — and until this swapped
|
|
6708
|
-
// only the whole-value case, so every token inside the array reached
|
|
6709
|
-
// validation as an object ("expected string, got object") with every lookup
|
|
6710
|
-
// recorded. An unresolved element under 'ignore' becomes `null`, which
|
|
6711
|
-
// validation skips, so `check` still type-checks the resolved siblings.
|
|
6712
|
-
const unresolved = [];
|
|
6713
|
-
const swapped = swapLookupTokens(nested.value, resolutions, unresolved);
|
|
6714
|
-
if (unresolved.length) {
|
|
6715
|
-
if (policy.unresolvedLookups !== 'ignore') {
|
|
6716
|
-
for (const token of unresolved)
|
|
6717
|
-
errs.push(unresolvedLookupMessage(token));
|
|
6718
|
-
continue;
|
|
6719
|
-
}
|
|
6720
|
-
if (isLookupToken(nested.value))
|
|
6721
|
-
continue;
|
|
6722
|
-
}
|
|
6723
|
-
nested = { ...nested, value: swapped };
|
|
6724
|
-
// A FLAT key whose wire name ends in `[*]` names one array: an array value
|
|
6725
|
-
// there is checked element by element, as the nested form is. A flat scalar
|
|
6726
|
-
// keeps depth 0 and is emitted as the scalar it is — the product wraps it
|
|
6727
|
-
// into a one-element list (flow-v1 `convertDotNotationToNested`).
|
|
6728
|
-
const depth = flat
|
|
6729
|
-
? (f.name.endsWith('[*]') && Array.isArray(nested.value) ? 1 : 0)
|
|
6730
|
-
: nested.projectedArrays;
|
|
6731
|
-
validateConnectorFieldValue(f, nested.value, depth, errs);
|
|
6732
|
-
if (f.location === 'multipart') {
|
|
6733
|
-
partValues.set(f.name, renderDeep(nested.value, rename));
|
|
6734
|
-
continue;
|
|
6735
|
-
}
|
|
6736
|
-
const slot = SLOT_FOR_LOCATION[f.location ?? 'body'] ?? 'bodyParameters';
|
|
6737
|
-
routed[slot][f.name] = renderDeep(nested.value, rename);
|
|
6738
|
-
}
|
|
6739
|
-
if (errs.length) {
|
|
6740
|
-
// Name the location of each required field: a "missing required input" on a
|
|
6741
|
-
// path parameter reads very differently from one on a body field, and the
|
|
6742
|
-
// author's fix is the same either way (add it to the same flat `inputs` map).
|
|
6743
|
-
const req = fields
|
|
6744
|
-
.filter((f) => f.required)
|
|
6745
|
-
.map((f) => (f.location && f.location !== 'body' ? `${f.name} (${f.location})` : f.name))
|
|
6746
|
-
.join(', ') || '(none required)';
|
|
6747
|
-
// Phase 1 propagates connector discovery capability to every activity. When
|
|
6748
|
-
// one of those activities rejects an unknown field, point at the live-schema
|
|
6749
|
-
// preparation path; fully static activities on the same connector still
|
|
6750
|
-
// compile normally and never reach this hint.
|
|
6751
|
-
const schemaDynamic = !!rc.entry?.runtime?.requiresConnectionForSchema;
|
|
6752
|
-
const hasUnknown = errs.some((e) => e.startsWith('unknown input'));
|
|
6753
|
-
let hint = '';
|
|
6754
|
-
if (schemaDynamic && hasUnknown) {
|
|
6755
|
-
const prefix = `uipath.connector.${rc.connectorKey}.`;
|
|
6756
|
-
const action = String(rc.nodeType).startsWith(prefix)
|
|
6757
|
-
? String(rc.nodeType).slice(prefix.length)
|
|
6758
|
-
// `.pop()` on a split is never empty at runtime, but its type admits
|
|
6759
|
-
// `undefined`; the node type itself is the only sane fallback.
|
|
6760
|
-
: String(rc.nodeType).split('.').pop() ?? String(rc.nodeType);
|
|
6761
|
-
hint =
|
|
6762
|
-
` This connector supports connection-specific object or field discovery, and the ` +
|
|
6763
|
-
`static library does not contain this input. Run ` +
|
|
6764
|
-
`\`${prepareCommand(rc.connectorKey, action)}\` ` +
|
|
6765
|
-
`to load them ` +
|
|
6766
|
-
`(writes ./.flow-sdk/connectors-local/, which the compiler auto-detects). If that still ` +
|
|
6767
|
-
`does not carry this input, the operation resolves its fields from the VALUES of ` +
|
|
6768
|
-
`parent inputs: resolve every parent and re-run with ` +
|
|
6769
|
-
`\`-f <parent-field>=<value>\` for each — too few parents is refused, and a ` +
|
|
6770
|
-
`wrong value resolves back to just the parents. ` +
|
|
6771
|
-
`See the connector guide's parent-field loop.`;
|
|
6772
|
-
}
|
|
6773
|
-
throw new Error(`connector ${rc.nodeType}: ${errs.join('; ')}. Required fields: ${req}.${hint}`);
|
|
6774
|
-
}
|
|
6775
|
-
// Every declared part, in declaration order, as the designer lists them: the
|
|
6776
|
-
// container part valueless, a file part with the author's value when given. A
|
|
6777
|
-
// test double that resolves no `multipartParts` falls back to its multipart
|
|
6778
|
-
// fields, which are all file parts by construction.
|
|
6779
|
-
const declaredParts = rc.multipartParts
|
|
6780
|
-
?? fields.filter((f) => f.location === 'multipart').map((f) => ({ name: f.name, dataType: f.type ?? 'string' }));
|
|
6781
|
-
const multipartParameters = declaredParts.map((part) => (partValues.has(part.name)
|
|
6782
|
-
? { name: part.name, dataType: part.dataType, value: partValues.get(part.name) }
|
|
6783
|
-
: { name: part.name, dataType: part.dataType }));
|
|
6784
|
-
// A FIXED slot order, so a recompile is byte-identical no matter which order
|
|
6785
|
-
// the author listed the inputs in.
|
|
6786
|
-
const slots = multipartParameters.length === 0 || Object.keys(routed.bodyParameters).length > 0
|
|
6787
|
-
? { bodyParameters: routed.bodyParameters }
|
|
6788
|
-
: {};
|
|
6789
|
-
if (Object.keys(routed.pathParameters).length)
|
|
6790
|
-
slots.pathParameters = routed.pathParameters;
|
|
6791
|
-
if (Object.keys(routed.queryParameters).length)
|
|
6792
|
-
slots.queryParameters = routed.queryParameters;
|
|
6793
|
-
if (multipartParameters.length) {
|
|
6794
|
-
slots.multipartParameters = multipartParameters;
|
|
6795
|
-
slots.inputMetadata = { type: 'multipart', multipart: { bodyFieldName: multipartBodyFieldName(declaredParts) } };
|
|
6796
|
-
}
|
|
6797
|
-
return slots;
|
|
6798
|
-
}
|
|
6799
|
-
/**
|
|
6800
|
-
* `value` with every lookup token swapped for its recorded id: the value itself,
|
|
6801
|
-
* or the elements of an array at any depth. An unrecorded token is pushed to
|
|
6802
|
-
* `unresolved` and stands as `null`, so an array keeps its length.
|
|
6803
|
-
*
|
|
6804
|
-
* Objects are NOT descended: a lookup's place is a field's value or an element
|
|
6805
|
-
* of an array field (`ConnectorLookupValue`), and a nested path such as
|
|
6806
|
-
* `contacts[*].id` reaches here already projected to its leaf array. A token
|
|
6807
|
-
* anywhere else is refused by {@link renderDeep}.
|
|
6808
|
-
*/
|
|
6809
|
-
function swapLookupTokens(value, resolutions, unresolved) {
|
|
6810
|
-
if (isLookupToken(value)) {
|
|
6811
|
-
const resolved = resolvedValue(value, resolutions);
|
|
6812
|
-
if (resolved === undefined) {
|
|
6813
|
-
unresolved.push(value);
|
|
6814
|
-
return null;
|
|
6815
|
-
}
|
|
6816
|
-
return resolved;
|
|
6817
|
-
}
|
|
6818
|
-
if (Array.isArray(value))
|
|
6819
|
-
return value.map((item) => swapLookupTokens(item, resolutions, unresolved));
|
|
6820
|
-
return value;
|
|
6821
|
-
}
|
|
6822
|
-
/**
|
|
6823
|
-
* `inputMetadata.multipart.bodyFieldName` names the CONTAINER part — the one the
|
|
6824
|
-
* runtime composes from `bodyParameters`. Measured on the designer's own nodes in
|
|
6825
|
-
* the eval archive: `body` ×413, `RagRequest` ×1 (GenAI `content-generation`, whose
|
|
6826
|
-
* string part is named that), and `body` on 91 of 93 file-only operations. A
|
|
6827
|
-
* mis-named container is fatal at dispatch — hardcoding `body` for GenAI made
|
|
6828
|
-
* Integration Service answer `400 The request body is empty` (verified live
|
|
6829
|
-
* 2026-09-02) — so it is the string part's own name, with `body` only as the
|
|
6830
|
-
* default for an operation that has no string part.
|
|
6831
|
-
*/
|
|
6832
|
-
function multipartBodyFieldName(parts) {
|
|
6833
|
-
const containers = parts.filter((p) => p.dataType.toLowerCase() !== 'file');
|
|
6834
|
-
return containers.length === 1 ? containers[0].name : 'body';
|
|
6835
|
-
}
|
|
6836
|
-
const SLOT_FOR_LOCATION = {
|
|
6837
|
-
body: 'bodyParameters',
|
|
6838
|
-
query: 'queryParameters',
|
|
6839
|
-
path: 'pathParameters',
|
|
6840
|
-
};
|
|
6841
|
-
function connectorPath(name) {
|
|
6842
|
-
return name.split('.').map((raw) => ({
|
|
6843
|
-
key: raw.endsWith('[*]') ? raw.slice(0, -3) : raw,
|
|
6844
|
-
array: raw.endsWith('[*]'),
|
|
6845
|
-
}));
|
|
6846
|
-
}
|
|
6847
|
-
/** Read a natural nested authoring value using a registry path such as
|
|
6848
|
-
* `contacts[*].id`. Wildcards project one value per array element so the Flow
|
|
6849
|
-
* bodyParameters map can retain the registry's exact field ids. */
|
|
6850
|
-
function readConnectorPath(inputs, name) {
|
|
6851
|
-
const parts = connectorPath(name);
|
|
6852
|
-
const read = (value, index) => {
|
|
6853
|
-
if (index >= parts.length)
|
|
6854
|
-
return { found: true, value, projectedArrays: 0 };
|
|
6855
|
-
if (!value || typeof value !== 'object' || value instanceof Expr || Array.isArray(value))
|
|
6856
|
-
return { found: false, projectedArrays: 0 };
|
|
6857
|
-
const part = parts[index];
|
|
6858
|
-
const object = value;
|
|
6859
|
-
if (!Object.prototype.hasOwnProperty.call(object, part.key))
|
|
6860
|
-
return { found: false, projectedArrays: 0 };
|
|
6861
|
-
const next = object[part.key];
|
|
6862
|
-
if (!part.array)
|
|
6863
|
-
return read(next, index + 1);
|
|
6864
|
-
if (!Array.isArray(next))
|
|
6865
|
-
return { found: false, projectedArrays: 0 };
|
|
6866
|
-
if (index === parts.length - 1)
|
|
6867
|
-
return { found: true, value: next, projectedArrays: 1 };
|
|
6868
|
-
const projected = next.map((item) => read(item, index + 1));
|
|
6869
|
-
if (!projected.some((item) => item.found))
|
|
6870
|
-
return { found: false, projectedArrays: 0 };
|
|
6871
|
-
return {
|
|
6872
|
-
found: true,
|
|
6873
|
-
value: projected.map((item) => item.found ? item.value : null),
|
|
6874
|
-
projectedArrays: 1 + Math.max(0, ...projected.map((item) => item.projectedArrays)),
|
|
6875
|
-
};
|
|
6876
|
-
};
|
|
6877
|
-
return read(inputs, 0);
|
|
6878
|
-
}
|
|
6879
|
-
function buildConnectorInputTrie(fields) {
|
|
6880
|
-
const root = { children: new Map(), array: false };
|
|
6881
|
-
for (const field of fields) {
|
|
6882
|
-
let cursor = root;
|
|
6883
|
-
for (const part of connectorPath(field.name)) {
|
|
6884
|
-
let child = cursor.children.get(part.key);
|
|
6885
|
-
if (!child) {
|
|
6886
|
-
child = { children: new Map(), array: part.array };
|
|
6887
|
-
cursor.children.set(part.key, child);
|
|
6888
|
-
}
|
|
6889
|
-
child.array ||= part.array;
|
|
6890
|
-
cursor = child;
|
|
6891
|
-
}
|
|
6892
|
-
cursor.terminal = field;
|
|
6893
|
-
}
|
|
6894
|
-
return root;
|
|
6895
|
-
}
|
|
6896
|
-
/** Reject nested typos with the same unknown-key message as flat keys. */
|
|
6897
|
-
function validateNestedConnectorKeys(fields, inputs, exact) {
|
|
6898
|
-
const root = buildConnectorInputTrie(fields);
|
|
6899
|
-
const errs = [];
|
|
6900
|
-
const visit = (value, node, path) => {
|
|
6901
|
-
if (value instanceof Expr)
|
|
6902
|
-
return;
|
|
6903
|
-
if (node.array) {
|
|
6904
|
-
if (!Array.isArray(value)) {
|
|
6905
|
-
errs.push(`input "${path}" expected array, got ${typeof value}`);
|
|
6906
|
-
return;
|
|
6907
|
-
}
|
|
6908
|
-
for (const item of value)
|
|
6909
|
-
visit(item, { ...node, array: false }, `${path}[*]`);
|
|
6910
|
-
return;
|
|
6911
|
-
}
|
|
6912
|
-
// An opaque object/array terminal owns its complete value; there is no
|
|
6913
|
-
// child metadata with which to validate its internal keys.
|
|
6914
|
-
if (node.terminal && node.children.size === 0)
|
|
6915
|
-
return;
|
|
6916
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
6917
|
-
if (node.children.size > 0)
|
|
6918
|
-
errs.push(`input "${path}" expected object, got ${Array.isArray(value) ? 'array' : typeof value}`);
|
|
6919
|
-
return;
|
|
6920
|
-
}
|
|
6921
|
-
for (const [key, childValue] of Object.entries(value)) {
|
|
6922
|
-
const child = node.children.get(key);
|
|
6923
|
-
const childPath = path ? `${path}.${key}` : key;
|
|
6924
|
-
if (!child) {
|
|
6925
|
-
const near = nearestField(childPath, fields.map((f) => f.name));
|
|
6926
|
-
errs.push(`unknown input "${childPath}"${near ? ` — did you mean "${near}"?` : ''}`);
|
|
6927
|
-
}
|
|
6928
|
-
else {
|
|
6929
|
-
visit(childValue, child, childPath);
|
|
6930
|
-
}
|
|
6931
|
-
}
|
|
6932
|
-
};
|
|
6933
|
-
for (const [key, value] of Object.entries(inputs)) {
|
|
6934
|
-
if (exact.has(key))
|
|
6935
|
-
continue; // backwards-compatible flat authoring
|
|
6936
|
-
const node = root.children.get(key);
|
|
6937
|
-
if (!node) {
|
|
6938
|
-
const near = nearestField(key, [...exact.keys()]);
|
|
6939
|
-
errs.push(`unknown input "${key}"${near ? ` — did you mean "${near}"?` : ''}`);
|
|
6940
|
-
}
|
|
6941
|
-
else {
|
|
6942
|
-
visit(value, node, key);
|
|
6943
|
-
}
|
|
6944
|
-
}
|
|
6945
|
-
return errs;
|
|
6946
|
-
}
|
|
6947
|
-
function validateConnectorFieldValue(field, value, projectedArrays, errs) {
|
|
6948
|
-
if (projectedArrays > 0) {
|
|
6949
|
-
if (!Array.isArray(value)) {
|
|
6950
|
-
errs.push(`input "${field.name}" expected array projection, got ${typeof value}`);
|
|
6951
|
-
return;
|
|
6952
|
-
}
|
|
6953
|
-
for (const item of value) {
|
|
6954
|
-
if (item !== null && item !== undefined)
|
|
6955
|
-
validateConnectorFieldValue(field, item, projectedArrays - 1, errs);
|
|
6956
|
-
}
|
|
6957
|
-
return;
|
|
6958
|
-
}
|
|
6959
|
-
if (value instanceof Expr || value === null || value === undefined)
|
|
6960
|
-
return;
|
|
6961
|
-
if (field.type === 'array') {
|
|
6962
|
-
if (!Array.isArray(value))
|
|
6963
|
-
errs.push(`input "${field.name}" expected array, got ${typeof value}`);
|
|
6964
|
-
return;
|
|
6965
|
-
}
|
|
6966
|
-
if (field.type === 'object') {
|
|
6967
|
-
if (typeof value !== 'object' || Array.isArray(value))
|
|
6968
|
-
errs.push(`input "${field.name}" expected object, got ${Array.isArray(value) ? 'array' : typeof value}`);
|
|
6969
|
-
return;
|
|
6970
|
-
}
|
|
6971
|
-
if (field.type === 'string' || field.type === 'number' || field.type === 'boolean') {
|
|
6972
|
-
const actual = typeof value;
|
|
6973
|
-
const ok = actual === field.type ||
|
|
6974
|
-
(field.type === 'number' && actual === 'string' && value !== '' && !isNaN(Number(value)));
|
|
6975
|
-
if (!ok)
|
|
6976
|
-
errs.push(`input "${field.name}" expected ${field.type}, got ${actual}`);
|
|
6977
|
-
}
|
|
6978
|
-
}
|
|
6979
|
-
/**
|
|
6980
|
-
* The closest declared field name to `key`, for the "did you mean …?" half of an
|
|
6981
|
-
* unknown-input error.
|
|
6982
|
-
*
|
|
6983
|
-
* Three rules, tried in order: an exact case-insensitive match (`pagesize` →
|
|
6984
|
-
* `pageSize`), a substring either way (`summary` → `fields.summary`), and — the
|
|
6985
|
-
* one added for query/path parameters — a small EDIT DISTANCE. The first two miss
|
|
6986
|
-
* every substitution typo: `projekt` and `project` share no containment, so the
|
|
6987
|
-
* suggestion was simply absent, and it is absent exactly where it is needed most.
|
|
6988
|
-
* Parameter names are unfamiliar (`issuetype`, `nextPage`, `expansionLevel`), so
|
|
6989
|
-
* the author has no memory to fall back on.
|
|
6990
|
-
*
|
|
6991
|
-
* The threshold scales with the name's length (⌈len/4⌉, at least 1, at most 3), so
|
|
6992
|
-
* a short name needs a near-exact miss and a long one may be off by a few — and
|
|
6993
|
-
* two genuinely different fields are never conflated.
|
|
6994
|
-
*/
|
|
6995
|
-
function nearestField(key, names) {
|
|
6996
|
-
const lk = key.toLowerCase();
|
|
6997
|
-
const exact = names.find((n) => n.toLowerCase() === lk);
|
|
6998
|
-
if (exact)
|
|
6999
|
-
return exact;
|
|
7000
|
-
const contained = names.find((n) => n.toLowerCase().includes(lk) || lk.includes(n.toLowerCase()));
|
|
7001
|
-
if (contained)
|
|
7002
|
-
return contained;
|
|
7003
|
-
const budget = Math.min(3, Math.max(1, Math.ceil(lk.length / 4)));
|
|
7004
|
-
let best;
|
|
7005
|
-
let bestD = budget + 1;
|
|
7006
|
-
for (const n of names) {
|
|
7007
|
-
const d = editDistance(lk, n.toLowerCase());
|
|
7008
|
-
if (d < bestD) {
|
|
7009
|
-
bestD = d;
|
|
7010
|
-
best = n;
|
|
7011
|
-
}
|
|
7012
|
-
}
|
|
7013
|
-
return bestD <= budget ? best : undefined;
|
|
7014
|
-
}
|
|
7015
|
-
/** Levenshtein distance, two rows — enough for field-name suggestions. */
|
|
7016
|
-
function editDistance(a, b) {
|
|
7017
|
-
if (a === b)
|
|
7018
|
-
return 0;
|
|
7019
|
-
if (Math.abs(a.length - b.length) > 3)
|
|
7020
|
-
return 99; // cheap bail: beyond any budget
|
|
7021
|
-
let prev = Array.from({ length: b.length + 1 }, (_, j) => j);
|
|
7022
|
-
for (let i = 1; i <= a.length; i++) {
|
|
7023
|
-
const row = [i];
|
|
7024
|
-
for (let j = 1; j <= b.length; j++) {
|
|
7025
|
-
row[j] = Math.min(prev[j] + 1, row[j - 1] + 1, prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
7026
|
-
}
|
|
7027
|
-
prev = row;
|
|
7028
|
-
}
|
|
7029
|
-
return prev[b.length];
|
|
7030
|
-
}
|
|
7031
6638
|
/**
|
|
7032
6639
|
* Emit a transform's `inputs` in the canonical shape the node's runtime reads:
|
|
7033
6640
|
* `collection` as a BARE variable path (not `=js:`-prefixed — it is a path
|
package/dist/workdir.d.ts
CHANGED
|
@@ -33,3 +33,13 @@ export declare function ensureWorkDir(cwd?: string): void;
|
|
|
33
33
|
export declare function workDirBeside(path: string): string;
|
|
34
34
|
/** The default path of a decompiled source named `file`, creating the work directory. */
|
|
35
35
|
export declare function defaultSourceOut(file: string): string;
|
|
36
|
+
/**
|
|
37
|
+
* Write a decompiled source to `path`, creating its folder first if it is
|
|
38
|
+
* missing (`recursive: true`: an existing folder is not an error).
|
|
39
|
+
*
|
|
40
|
+
* `-o .flow-sdk/Order.flow.ts` on a fresh workspace has no `.flow-sdk/` yet;
|
|
41
|
+
* only the default path ({@link defaultSourceOut}) used to create it, so the
|
|
42
|
+
* documented explicit form failed with ENOENT and the author had to
|
|
43
|
+
* `mkdir -p` and retry.
|
|
44
|
+
*/
|
|
45
|
+
export declare function writeSource(path: string, text: string): void;
|
package/dist/workdir.js
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* rather than split across two places. When both exist, `.flow-sdk/` wins and
|
|
25
25
|
* stderr says the root one is ignored.
|
|
26
26
|
*/
|
|
27
|
-
import { existsSync, mkdirSync } from 'node:fs';
|
|
27
|
+
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
28
28
|
import { basename, dirname, join } from 'node:path';
|
|
29
29
|
/** The hidden directory, relative to the current directory. */
|
|
30
30
|
export const WORK_DIR = '.flow-sdk';
|
|
@@ -94,3 +94,16 @@ export function defaultSourceOut(file) {
|
|
|
94
94
|
ensureWorkDir();
|
|
95
95
|
return inWorkDir(file);
|
|
96
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Write a decompiled source to `path`, creating its folder first if it is
|
|
99
|
+
* missing (`recursive: true`: an existing folder is not an error).
|
|
100
|
+
*
|
|
101
|
+
* `-o .flow-sdk/Order.flow.ts` on a fresh workspace has no `.flow-sdk/` yet;
|
|
102
|
+
* only the default path ({@link defaultSourceOut}) used to create it, so the
|
|
103
|
+
* documented explicit form failed with ENOENT and the author had to
|
|
104
|
+
* `mkdir -p` and retry.
|
|
105
|
+
*/
|
|
106
|
+
export function writeSource(path, text) {
|
|
107
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
108
|
+
writeFileSync(path, text);
|
|
109
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uipath/maestro-builder-sdk",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.15.1",
|
|
4
4
|
"description": "Build UiPath Flow, Case, and BPMN artifacts by writing TypeScript.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://docs.uipath.com/maestro",
|
|
@@ -92,5 +92,5 @@
|
|
|
92
92
|
"@types/node": "^22.7.0",
|
|
93
93
|
"esbuild": "^0.28.1"
|
|
94
94
|
},
|
|
95
|
-
"gitref": "
|
|
95
|
+
"gitref": "a3707b159a49ed3b6611c55bed66622cb6dd1972"
|
|
96
96
|
}
|
|
File without changes
|
|
File without changes
|