@uipath/maestro-builder-sdk 5.2.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/LICENSE +21 -0
- package/README.md +322 -0
- package/dist/bindings-v2.d.ts +37 -0
- package/dist/bindings-v2.js +155 -0
- package/dist/bindings.d.ts +6 -0
- package/dist/bindings.js +6 -0
- package/dist/bpmn/bpmn-expr-check.d.ts +41 -0
- package/dist/bpmn/bpmn-expr-check.js +218 -0
- package/dist/bpmn/bpmn-sdk.d.ts +2257 -0
- package/dist/bpmn/bpmn-sdk.js +1476 -0
- package/dist/bpmn/check-cli.d.ts +3 -0
- package/dist/bpmn/check-cli.js +32 -0
- package/dist/bpmn/check.d.ts +25 -0
- package/dist/bpmn/check.js +430 -0
- package/dist/bpmn/compile-cli.d.ts +3 -0
- package/dist/bpmn/compile-cli.js +60 -0
- package/dist/bpmn/connector.d.ts +56 -0
- package/dist/bpmn/connector.js +199 -0
- package/dist/bpmn/decompile-cli.d.ts +8 -0
- package/dist/bpmn/decompile-cli.js +54 -0
- package/dist/bpmn/decompile.d.ts +6 -0
- package/dist/bpmn/decompile.js +1108 -0
- package/dist/bpmn/format-profile.d.ts +27 -0
- package/dist/bpmn/format-profile.js +11 -0
- package/dist/bpmn/intsvc.d.ts +77 -0
- package/dist/bpmn/intsvc.js +155 -0
- package/dist/bpmn/load.d.ts +4 -0
- package/dist/bpmn/load.js +22 -0
- package/dist/bpmn/merge-cli.d.ts +3 -0
- package/dist/bpmn/merge-cli.js +45 -0
- package/dist/bpmn/merge.d.ts +19 -0
- package/dist/bpmn/merge.js +293 -0
- package/dist/bpmn/registry-types.json +1869 -0
- package/dist/bpmn/serialize.d.ts +28 -0
- package/dist/bpmn/serialize.js +757 -0
- package/dist/bpmn/tidy-cli.d.ts +2 -0
- package/dist/bpmn/tidy-cli.js +39 -0
- package/dist/bpmn/typed-node.d.ts +382 -0
- package/dist/bpmn/typed-node.js +551 -0
- package/dist/bpmn/uipath-moddle.v1.json +823 -0
- package/dist/bpmn/xml-ranges.d.ts +45 -0
- package/dist/bpmn/xml-ranges.js +151 -0
- package/dist/case/case-expr-check.d.ts +35 -0
- package/dist/case/case-expr-check.js +85 -0
- package/dist/case/case-sdk.d.ts +1282 -0
- package/dist/case/case-sdk.js +923 -0
- package/dist/case/check-cli.d.ts +3 -0
- package/dist/case/check-cli.js +31 -0
- package/dist/case/check.d.ts +18 -0
- package/dist/case/check.js +456 -0
- package/dist/case/compile-cli.d.ts +3 -0
- package/dist/case/compile-cli.js +291 -0
- package/dist/case/decompile-cli.d.ts +9 -0
- package/dist/case/decompile-cli.js +117 -0
- package/dist/case/decompile.d.ts +187 -0
- package/dist/case/decompile.js +1170 -0
- package/dist/case/entry-points.d.ts +79 -0
- package/dist/case/entry-points.js +118 -0
- package/dist/case/format-profile.d.ts +60 -0
- package/dist/case/format-profile.js +79 -0
- package/dist/case/ids.d.ts +24 -0
- package/dist/case/ids.js +76 -0
- package/dist/case/load.d.ts +12 -0
- package/dist/case/load.js +32 -0
- package/dist/case/preserve.d.ts +133 -0
- package/dist/case/preserve.js +105 -0
- package/dist/case/serialize.d.ts +10 -0
- package/dist/case/serialize.js +1414 -0
- package/dist/check-cli.d.ts +3 -0
- package/dist/check-cli.js +118 -0
- package/dist/check.d.ts +146 -0
- package/dist/check.js +4898 -0
- package/dist/cli/commands/bpmn.d.ts +2 -0
- package/dist/cli/commands/bpmn.js +73 -0
- package/dist/cli/commands/case.d.ts +2 -0
- package/dist/cli/commands/case.js +60 -0
- package/dist/cli/commands/flow.d.ts +2 -0
- package/dist/cli/commands/flow.js +99 -0
- package/dist/cli/commands/registry.d.ts +10 -0
- package/dist/cli/commands/registry.js +170 -0
- package/dist/cli/common.d.ts +30 -0
- package/dist/cli/common.js +92 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +18 -0
- package/dist/cli-run.d.ts +2 -0
- package/dist/cli-run.js +42 -0
- package/dist/compile-cli.d.ts +3 -0
- package/dist/compile-cli.js +160 -0
- package/dist/config.d.ts +47 -0
- package/dist/config.js +201 -0
- package/dist/core/actions.d.ts +3244 -0
- package/dist/core/actions.js +1062 -0
- package/dist/core/binding-messages.d.ts +1 -0
- package/dist/core/binding-messages.js +16 -0
- package/dist/core/bindings.d.ts +12 -0
- package/dist/core/bindings.js +67 -0
- package/dist/core/ceql.d.ts +73 -0
- package/dist/core/ceql.js +272 -0
- package/dist/core/cli-spelling.d.ts +39 -0
- package/dist/core/cli-spelling.js +62 -0
- package/dist/core/connector-raw-node.d.ts +21 -0
- package/dist/core/connector-raw-node.js +47 -0
- package/dist/core/connectors.d.ts +184 -0
- package/dist/core/connectors.js +128 -0
- package/dist/core/expr-check.d.ts +93 -0
- package/dist/core/expr-check.js +186 -0
- package/dist/core/expr.d.ts +323 -0
- package/dist/core/expr.js +400 -0
- package/dist/core/index.d.ts +10 -0
- package/dist/core/index.js +10 -0
- package/dist/core/library.d.ts +440 -0
- package/dist/core/library.js +1183 -0
- package/dist/core/list-envelope.d.ts +45 -0
- package/dist/core/list-envelope.js +97 -0
- package/dist/core/lookup-store.d.ts +22 -0
- package/dist/core/lookup-store.js +85 -0
- package/dist/core/lookups.d.ts +227 -0
- package/dist/core/lookups.js +231 -0
- package/dist/core/node-classes.d.ts +234 -0
- package/dist/core/node-classes.js +158 -0
- package/dist/core/script-return.d.ts +68 -0
- package/dist/core/script-return.js +197 -0
- package/dist/core/stable-id.d.ts +28 -0
- package/dist/core/stable-id.js +37 -0
- package/dist/core-definitions.json +16249 -0
- package/dist/decompile-cli.d.ts +3 -0
- package/dist/decompile-cli.js +131 -0
- package/dist/decompile.d.ts +138 -0
- package/dist/decompile.js +2482 -0
- package/dist/emit.d.ts +10 -0
- package/dist/emit.js +60 -0
- package/dist/event-filters.d.ts +130 -0
- package/dist/event-filters.js +137 -0
- package/dist/flow-expr-check.d.ts +17 -0
- package/dist/flow-expr-check.js +193 -0
- package/dist/flow-sdk.d.ts +1341 -0
- package/dist/flow-sdk.js +1164 -0
- package/dist/flow-variables.d.ts +13 -0
- package/dist/flow-variables.js +42 -0
- package/dist/format-profile.d.ts +72 -0
- package/dist/format-profile.js +97 -0
- package/dist/generators/_connections.py +287 -0
- package/dist/generators/_events.py +355 -0
- package/dist/generators/_lookups.py +114 -0
- package/dist/generators/_naming.py +185 -0
- package/dist/generators/_objects.py +299 -0
- package/dist/generators/_resolve.py +273 -0
- package/dist/generators/convert_library_to_md.py +1441 -0
- package/dist/generators/extract_library.py +125 -0
- package/dist/generators/generate_connectors_ts.py +936 -0
- package/dist/generators/generate_library_json.py +2527 -0
- package/dist/generators/generate_triggers_ts.py +110 -0
- package/dist/generators/materialize_registry.py +155 -0
- package/dist/generators/prepare_connector.py +1688 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +35 -0
- package/dist/ixp-definition.json +1982 -0
- package/dist/library.d.ts +6 -0
- package/dist/library.js +6 -0
- package/dist/load.d.ts +16 -0
- package/dist/load.js +95 -0
- package/dist/merge-cli.d.ts +3 -0
- package/dist/merge-cli.js +47 -0
- package/dist/merge.d.ts +125 -0
- package/dist/merge.js +0 -0
- package/dist/migrate.d.ts +15 -0
- package/dist/migrate.js +245 -0
- package/dist/node-runtime.d.ts +3 -0
- package/dist/node-runtime.js +22 -0
- package/dist/package-name.d.ts +17 -0
- package/dist/package-name.js +17 -0
- package/dist/ref-resolve.d.ts +77 -0
- package/dist/ref-resolve.js +170 -0
- package/dist/registry/cache.d.ts +55 -0
- package/dist/registry/cache.js +134 -0
- package/dist/registry/curated-overlay.d.ts +16 -0
- package/dist/registry/curated-overlay.js +62 -0
- package/dist/registry/extractor.d.ts +22 -0
- package/dist/registry/extractor.js +18 -0
- package/dist/registry/materialize-bin.d.ts +2 -0
- package/dist/registry/materialize-bin.js +7 -0
- package/dist/registry/materialize-cli.d.ts +1 -0
- package/dist/registry/materialize-cli.js +46 -0
- package/dist/registry/materializer.d.ts +23 -0
- package/dist/registry/materializer.js +34 -0
- package/dist/registry/path-cli.d.ts +1 -0
- package/dist/registry/path-cli.js +56 -0
- package/dist/registry/prepare-cli.d.ts +62 -0
- package/dist/registry/prepare-cli.js +185 -0
- package/dist/registry/pull-cli.d.ts +1 -0
- package/dist/registry/pull-cli.js +274 -0
- package/dist/registry/python.d.ts +26 -0
- package/dist/registry/python.js +46 -0
- package/dist/registry/search-cli.d.ts +52 -0
- package/dist/registry/search-cli.js +159 -0
- package/dist/schema-version.d.ts +165 -0
- package/dist/schema-version.js +148 -0
- package/dist/serialize.d.ts +64 -0
- package/dist/serialize.js +6398 -0
- package/lib/README.md +112 -0
- package/lib/library-json.samples/index.json +158 -0
- package/lib/library-json.samples/invariant-collections.json +83 -0
- package/lib/library-json.samples/list-envelopes.json +123 -0
- package/lib/library-json.samples/uipath-atlassian-jira/create-issue@1.0.0.json +131 -0
- package/lib/library-json.samples/uipath-atlassian-jira/create-issue@1.0.0.v1def.json +306 -0
- package/lib/library-json.samples/uipath-atlassian-jira/generic/create_issue/create@1.0.0.json +86 -0
- package/lib/library-json.samples/uipath-atlassian-jira/generic/create_issue/create@1.0.0.v1def.json +208 -0
- package/lib/library-json.samples/uipath-atlassian-jira/get-issue@1.0.0.json +2040 -0
- package/lib/library-json.samples/uipath-atlassian-jira/get-issue@1.0.0.v1def.json +195 -0
- package/lib/library-json.samples/uipath-google-vertex/execute-google-vertex-agent@1.0.0.json +169 -0
- package/lib/library-json.samples/uipath-google-vertex/execute-google-vertex-agent@1.0.0.v1def.json +209 -0
- package/lib/library-json.samples/uipath-http-webhook/event-http-webhook@1.0.0.json +63 -0
- package/lib/library-json.samples/uipath-http-webhook/event-http-webhook@1.0.0.v1def.json +262 -0
- package/lib/library-json.samples/uipath-http-webhook/trigger-http-webhook@1.0.0.json +63 -0
- package/lib/library-json.samples/uipath-http-webhook/trigger-http-webhook@1.0.0.v1def.json +213 -0
- package/lib/library-json.samples/uipath-microsoft-azureactivedirectory/list-groups@1.0.0.json +227 -0
- package/lib/library-json.samples/uipath-microsoft-azureactivedirectory/list-groups@1.0.0.v1def.json +373 -0
- package/lib/library-json.samples/uipath-microsoft-onedrive/event-file-created@1.0.0.json +116 -0
- package/lib/library-json.samples/uipath-microsoft-onedrive/event-file-created@1.0.0.v1def.json +369 -0
- package/lib/library-json.samples/uipath-microsoft-onedrive/event-list-item-added@1.0.0.json +56 -0
- package/lib/library-json.samples/uipath-microsoft-onedrive/event-list-item-added@1.0.0.v1def.json +256 -0
- package/lib/library-json.samples/uipath-microsoft-onedrive/trigger-file-created@1.0.0.json +116 -0
- package/lib/library-json.samples/uipath-microsoft-onedrive/trigger-file-created@1.0.0.v1def.json +320 -0
- package/lib/library-json.samples/uipath-microsoft-onedrive/trigger-list-item-added@1.0.0.json +56 -0
- package/lib/library-json.samples/uipath-microsoft-onedrive/trigger-list-item-added@1.0.0.v1def.json +207 -0
- package/lib/library-json.samples/uipath-microsoft-outlook365/event-email-received@1.0.0.json +107 -0
- package/lib/library-json.samples/uipath-microsoft-outlook365/event-email-received@1.0.0.v1def.json +264 -0
- package/lib/library-json.samples/uipath-microsoft-outlook365/send-email@1.0.0.json +142 -0
- package/lib/library-json.samples/uipath-microsoft-outlook365/send-email@1.0.0.v1def.json +306 -0
- package/lib/library-json.samples/uipath-microsoft-outlook365/trigger-email-received@1.0.0.json +107 -0
- package/lib/library-json.samples/uipath-microsoft-outlook365/trigger-email-received@1.0.0.v1def.json +217 -0
- package/lib/library-json.samples/uipath-microsoft-powerautomate/invoke-a-microsoft-power-automate-flow@1.0.0.json +85 -0
- package/lib/library-json.samples/uipath-microsoft-powerautomate/invoke-a-microsoft-power-automate-flow@1.0.0.v1def.json +209 -0
- package/lib/library-json.samples/uipath-salesforce-slack/send-message-to-user@1.0.0.json +755 -0
- package/lib/library-json.samples/uipath-salesforce-slack/send-message-to-user@1.0.0.v1def.json +513 -0
- package/lib/library-json.samples/uipath-uipath-dataservice/query-entity-records@1.0.0.json +122 -0
- package/lib/library-json.samples/uipath-uipath-dataservice/query-entity-records@1.0.0.v1def.json +227 -0
- package/package.json +85 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function bindingSelfNameMessage(where: string, name: string, attribute: 'connection' | 'folder'): string;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The refusal for a bindings.json entry that answers with its own name — shared by
|
|
3
|
+
* `serialize` (which throws it) and `check` (BINDING_SELF_NAME), so the author reads
|
|
4
|
+
* the same sentence from the inner loop as from compile. Before this, `check`
|
|
5
|
+
* printed `0 error(s)` for a source `compile` then refused (teams-decision,
|
|
6
|
+
* 2026-09-01, turn 22 → 23).
|
|
7
|
+
*/
|
|
8
|
+
import { prepareCommand } from './cli-spelling.js';
|
|
9
|
+
export function bindingSelfNameMessage(where, name, attribute) {
|
|
10
|
+
return `${where}: binding "${name}" resolves to its own name, so the ${attribute} would compile to the `
|
|
11
|
+
+ `literal string "${name}" and the run would fault with "'Connection' has an invalid GUID value: `
|
|
12
|
+
+ `'${name}'". Set its "resourceKey" to the real id: \`${prepareCommand('<connector-key>', '<action>')}\` `
|
|
13
|
+
+ `discovers the connection and writes both the connection and the folder entry into bindings.json; by hand, `
|
|
14
|
+
+ `read Id and FolderKey from \`uip is connections list --all-folders --output json\` (the default listing `
|
|
15
|
+
+ `shows only your own folder, so a connection shared from another folder is invisible without --all-folders).`;
|
|
16
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare class Bindings {
|
|
2
|
+
private list;
|
|
3
|
+
constructor(file?: string);
|
|
4
|
+
/** Resolve by binding `name` or `id`; returns its resourceKey (or default). */
|
|
5
|
+
resolve(name?: string): string | undefined;
|
|
6
|
+
/** Every symbolic name the file declares — what an author CAN write in `connection:` / `folder:`. */
|
|
7
|
+
declared(): string[];
|
|
8
|
+
/** The full binding entry by `name` or `id` — for consumers that emit a binding
|
|
9
|
+
* registry + `=bindings.<id>` refs (the Case caseplan) rather than inlining the
|
|
10
|
+
* resourceKey (the Flow node). */
|
|
11
|
+
entry(name?: string): Record<string, unknown> | undefined;
|
|
12
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bindings — resolve symbolic connection/folder names to their resource keys
|
|
3
|
+
* from a bindings.json (same format the Flow Code arm uses).
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
6
|
+
export class Bindings {
|
|
7
|
+
list = [];
|
|
8
|
+
constructor(file) {
|
|
9
|
+
if (file && existsSync(file)) {
|
|
10
|
+
this.list = normalizeBindings(JSON.parse(readFileSync(file, 'utf8')));
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/** Resolve by binding `name` or `id`; returns its resourceKey (or default). */
|
|
14
|
+
resolve(name) {
|
|
15
|
+
if (!name)
|
|
16
|
+
return undefined;
|
|
17
|
+
const b = this.list.find((x) => x.name === name || x.id === name);
|
|
18
|
+
if (!b)
|
|
19
|
+
return undefined;
|
|
20
|
+
return (b.resourceKey ?? b.default);
|
|
21
|
+
}
|
|
22
|
+
/** Every symbolic name the file declares — what an author CAN write in `connection:` / `folder:`. */
|
|
23
|
+
declared() {
|
|
24
|
+
const names = [];
|
|
25
|
+
for (const b of this.list) {
|
|
26
|
+
const n = (b?.name ?? b?.id);
|
|
27
|
+
if (typeof n === 'string' && !names.includes(n))
|
|
28
|
+
names.push(n);
|
|
29
|
+
}
|
|
30
|
+
return names;
|
|
31
|
+
}
|
|
32
|
+
/** The full binding entry by `name` or `id` — for consumers that emit a binding
|
|
33
|
+
* registry + `=bindings.<id>` refs (the Case caseplan) rather than inlining the
|
|
34
|
+
* resourceKey (the Flow node). */
|
|
35
|
+
entry(name) {
|
|
36
|
+
if (!name)
|
|
37
|
+
return undefined;
|
|
38
|
+
return this.list.find((x) => x.name === name || x.id === name);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Accept the shapes a real `bindings.json` turns up in, rather than assuming the array.
|
|
43
|
+
*
|
|
44
|
+
* The declared format is `{ bindings: [ { id, name, resourceKey, default, … } ] }`. But this
|
|
45
|
+
* file name is shared: the local BPMN engine's `--bindings` reads a NAME→VALUE MAP, either
|
|
46
|
+
* flat or under a `bindings` key, and anyone running a process locally writes one of those.
|
|
47
|
+
* The old code did `parsed.bindings ?? []` and then `.find(…)` on it, so a map crashed
|
|
48
|
+
* `compile` with `this.list.find is not a function` — no file named, no format explained. It
|
|
49
|
+
* cost real debugging time when a bindings.json written for a local run happened to sit in the
|
|
50
|
+
* compile directory, and an author told to write one for a live run can easily produce the map
|
|
51
|
+
* form.
|
|
52
|
+
*
|
|
53
|
+
* A map entry becomes `{ id, name, resourceKey, default }` with the value in all three
|
|
54
|
+
* key-ish slots, which is what `resolve`/`entry` read, so both formats behave the same.
|
|
55
|
+
*/
|
|
56
|
+
function normalizeBindings(parsed) {
|
|
57
|
+
if (parsed === null || typeof parsed !== 'object')
|
|
58
|
+
return [];
|
|
59
|
+
const inner = 'bindings' in parsed ? parsed.bindings : parsed;
|
|
60
|
+
if (Array.isArray(inner))
|
|
61
|
+
return inner;
|
|
62
|
+
if (inner === null || typeof inner !== 'object')
|
|
63
|
+
return [];
|
|
64
|
+
return Object.entries(inner)
|
|
65
|
+
.filter(([, value]) => typeof value === 'string')
|
|
66
|
+
.map(([id, value]) => ({ id, name: id, resourceKey: value, default: value }));
|
|
67
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* core/ceql — parse a CEQL filter expression into the canonical filter tree.
|
|
3
|
+
*
|
|
4
|
+
* ## Why a filter needs two representations
|
|
5
|
+
*
|
|
6
|
+
* An Integration Service list operation takes a server-side filter through a
|
|
7
|
+
* "FilterBuilder" parameter — usually named `where`, sometimes `q`. Two
|
|
8
|
+
* consumers read it, and they want different things:
|
|
9
|
+
*
|
|
10
|
+
* - the RUNTIME dials IS with a CEQL string, `inputs.detail.queryParameters.where`;
|
|
11
|
+
* - the Studio Web CANVAS renders the filter widget from a structured tree, at
|
|
12
|
+
* `essentialConfiguration.savedFilterTrees.where` inside the node's
|
|
13
|
+
* `=jsonString:`-encoded `inputs.detail.configuration`.
|
|
14
|
+
*
|
|
15
|
+
* A string cannot be reverse-engineered into the widget's model reliably, so the
|
|
16
|
+
* artifact carries both. Author only the string and the flow RUNS correctly while
|
|
17
|
+
* the FilterBuilder shows nothing when the activity is reopened — which is why
|
|
18
|
+
* product validation rejects a `queryParameters.<name>` with no matching
|
|
19
|
+
* `savedFilterTrees.<name>` (MST-9265), even though a debug run would pass.
|
|
20
|
+
*
|
|
21
|
+
* The v1 CLI solved this by making the TREE the input (`node configure
|
|
22
|
+
* --detail.filter`) and compiling the string from it. The builder SDK has no
|
|
23
|
+
* such command, so this goes the other way: authors write the CEQL they would
|
|
24
|
+
* write anyway, and the tree is DERIVED. One source of truth either way.
|
|
25
|
+
*
|
|
26
|
+
* ## Scope
|
|
27
|
+
*
|
|
28
|
+
* Deliberately a literal-only parser. A filter whose operand is a runtime
|
|
29
|
+
* expression compiles to a `{var_…}` placeholder plus `inputs.detail.filterVariables`
|
|
30
|
+
* on the v1 path; nothing here can produce that from a `=js:` string, and
|
|
31
|
+
* guessing would emit a tree that disagrees with the query. Such a filter keeps
|
|
32
|
+
* its runtime half and gets no tree — the same state as before this existed,
|
|
33
|
+
* rather than a wrong one.
|
|
34
|
+
*/
|
|
35
|
+
/** A leaf's comparison operator, in the canonical PascalCase the canvas stores. */
|
|
36
|
+
export type CeqlOperator = 'Equals' | 'NotEquals' | 'LessThan' | 'LessThanOrEqual' | 'GreaterThan' | 'GreaterThanOrEqual' | 'Contains' | 'NotContains' | 'StartsWith' | 'NotStartsWith' | 'EndsWith' | 'NotEndsWith' | 'Like' | 'NotLike' | 'IsNull' | 'IsNotNull';
|
|
37
|
+
/** One comparison — `<field> <operator> <value>`. */
|
|
38
|
+
export interface CeqlFilter {
|
|
39
|
+
id: string;
|
|
40
|
+
operator: CeqlOperator;
|
|
41
|
+
value?: {
|
|
42
|
+
value: unknown;
|
|
43
|
+
rawString: string;
|
|
44
|
+
isLiteral: true;
|
|
45
|
+
};
|
|
46
|
+
uiId?: string;
|
|
47
|
+
}
|
|
48
|
+
/** A group of comparisons joined by one logical operator. Groups nest. */
|
|
49
|
+
export interface CeqlTree {
|
|
50
|
+
/** 0 = And, 1 = Or — how this level's siblings combine. */
|
|
51
|
+
groupOperator: 0 | 1;
|
|
52
|
+
index: number;
|
|
53
|
+
filters: CeqlFilter[];
|
|
54
|
+
groups: CeqlTree[];
|
|
55
|
+
uuId?: string;
|
|
56
|
+
}
|
|
57
|
+
/** Raised when an expression is not CEQL this parser can represent. */
|
|
58
|
+
export declare class CeqlParseError extends Error {
|
|
59
|
+
readonly expression: string;
|
|
60
|
+
constructor(message: string, expression: string);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Parse a CEQL filter expression into the canvas's filter tree.
|
|
64
|
+
*
|
|
65
|
+
* `seed` makes the tree's ids deterministic — recompiling an unchanged flow must
|
|
66
|
+
* be byte-identical, so the node id and parameter name are folded in rather than
|
|
67
|
+
* minting random uuids the way the designer does.
|
|
68
|
+
*
|
|
69
|
+
* Throws {@link CeqlParseError} with the reason when the expression is not CEQL
|
|
70
|
+
* this can represent. That is the point: the alternative is a tenant-side
|
|
71
|
+
* `[102003] Integration Services bad request` with nothing naming the step.
|
|
72
|
+
*/
|
|
73
|
+
export declare function parseCeql(expression: string, seed: string): CeqlTree;
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* core/ceql — parse a CEQL filter expression into the canonical filter tree.
|
|
3
|
+
*
|
|
4
|
+
* ## Why a filter needs two representations
|
|
5
|
+
*
|
|
6
|
+
* An Integration Service list operation takes a server-side filter through a
|
|
7
|
+
* "FilterBuilder" parameter — usually named `where`, sometimes `q`. Two
|
|
8
|
+
* consumers read it, and they want different things:
|
|
9
|
+
*
|
|
10
|
+
* - the RUNTIME dials IS with a CEQL string, `inputs.detail.queryParameters.where`;
|
|
11
|
+
* - the Studio Web CANVAS renders the filter widget from a structured tree, at
|
|
12
|
+
* `essentialConfiguration.savedFilterTrees.where` inside the node's
|
|
13
|
+
* `=jsonString:`-encoded `inputs.detail.configuration`.
|
|
14
|
+
*
|
|
15
|
+
* A string cannot be reverse-engineered into the widget's model reliably, so the
|
|
16
|
+
* artifact carries both. Author only the string and the flow RUNS correctly while
|
|
17
|
+
* the FilterBuilder shows nothing when the activity is reopened — which is why
|
|
18
|
+
* product validation rejects a `queryParameters.<name>` with no matching
|
|
19
|
+
* `savedFilterTrees.<name>` (MST-9265), even though a debug run would pass.
|
|
20
|
+
*
|
|
21
|
+
* The v1 CLI solved this by making the TREE the input (`node configure
|
|
22
|
+
* --detail.filter`) and compiling the string from it. The builder SDK has no
|
|
23
|
+
* such command, so this goes the other way: authors write the CEQL they would
|
|
24
|
+
* write anyway, and the tree is DERIVED. One source of truth either way.
|
|
25
|
+
*
|
|
26
|
+
* ## Scope
|
|
27
|
+
*
|
|
28
|
+
* Deliberately a literal-only parser. A filter whose operand is a runtime
|
|
29
|
+
* expression compiles to a `{var_…}` placeholder plus `inputs.detail.filterVariables`
|
|
30
|
+
* on the v1 path; nothing here can produce that from a `=js:` string, and
|
|
31
|
+
* guessing would emit a tree that disagrees with the query. Such a filter keeps
|
|
32
|
+
* its runtime half and gets no tree — the same state as before this existed,
|
|
33
|
+
* rather than a wrong one.
|
|
34
|
+
*/
|
|
35
|
+
import { stableId } from './stable-id.js';
|
|
36
|
+
/** Raised when an expression is not CEQL this parser can represent. */
|
|
37
|
+
export class CeqlParseError extends Error {
|
|
38
|
+
expression;
|
|
39
|
+
constructor(message, expression) {
|
|
40
|
+
super(message);
|
|
41
|
+
this.expression = expression;
|
|
42
|
+
this.name = 'CeqlParseError';
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Word operators, longest first so `Not Contains` wins over `Contains` and
|
|
47
|
+
* `Is Not Null` over `Is Null`. Matched case-insensitively with flexible inner
|
|
48
|
+
* spacing, because the platform's own rendering is `Starts With` but authors
|
|
49
|
+
* write `startswith` as often as not.
|
|
50
|
+
*/
|
|
51
|
+
const WORD_OPERATORS = [
|
|
52
|
+
[/^is\s+not\s+null\b/i, 'IsNotNull'],
|
|
53
|
+
[/^is\s+null\b/i, 'IsNull'],
|
|
54
|
+
[/^not\s+starts\s*with\b/i, 'NotStartsWith'],
|
|
55
|
+
[/^not\s+ends\s*with\b/i, 'NotEndsWith'],
|
|
56
|
+
[/^not\s+contains\b/i, 'NotContains'],
|
|
57
|
+
[/^not\s+like\b/i, 'NotLike'],
|
|
58
|
+
[/^starts\s*with\b/i, 'StartsWith'],
|
|
59
|
+
[/^ends\s*with\b/i, 'EndsWith'],
|
|
60
|
+
[/^contains\b/i, 'Contains'],
|
|
61
|
+
[/^like\b/i, 'Like'],
|
|
62
|
+
];
|
|
63
|
+
/** Symbol operators, longest first so `<=` wins over `<` and `!=` over `=`. */
|
|
64
|
+
const SYMBOL_OPERATORS = [
|
|
65
|
+
['>=', 'GreaterThanOrEqual'],
|
|
66
|
+
['<=', 'LessThanOrEqual'],
|
|
67
|
+
['!=', 'NotEquals'],
|
|
68
|
+
['>', 'GreaterThan'],
|
|
69
|
+
['<', 'LessThan'],
|
|
70
|
+
['=', 'Equals'],
|
|
71
|
+
];
|
|
72
|
+
/**
|
|
73
|
+
* The OData spellings CEQL rejects. Named explicitly because the failure is
|
|
74
|
+
* otherwise a `[102003] Integration Services bad request` from the tenant, with
|
|
75
|
+
* nothing pointing at the operator.
|
|
76
|
+
*/
|
|
77
|
+
const ODATA_ALIASES = {
|
|
78
|
+
eq: '=', ne: '!=', gt: '>', ge: '>=', lt: '<', le: '<=',
|
|
79
|
+
};
|
|
80
|
+
class Scanner {
|
|
81
|
+
src;
|
|
82
|
+
pos = 0;
|
|
83
|
+
constructor(src) {
|
|
84
|
+
this.src = src;
|
|
85
|
+
}
|
|
86
|
+
get rest() { return this.src.slice(this.pos); }
|
|
87
|
+
skipSpace() {
|
|
88
|
+
while (this.pos < this.src.length && /\s/.test(this.src[this.pos]))
|
|
89
|
+
this.pos++;
|
|
90
|
+
}
|
|
91
|
+
/** Consume `text` (case-insensitive) when it is next; report whether it was. */
|
|
92
|
+
eat(text) {
|
|
93
|
+
this.skipSpace();
|
|
94
|
+
if (this.src.slice(this.pos, this.pos + text.length).toLowerCase() !== text.toLowerCase())
|
|
95
|
+
return false;
|
|
96
|
+
// A word must not be consumed out of the middle of an identifier.
|
|
97
|
+
if (/\w$/.test(text)) {
|
|
98
|
+
const after = this.src[this.pos + text.length];
|
|
99
|
+
if (after !== undefined && /[\w.]/.test(after))
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
this.pos += text.length;
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
atEnd() { this.skipSpace(); return this.pos >= this.src.length; }
|
|
106
|
+
}
|
|
107
|
+
/** `displayName`, `user.department` — bare, never quoted (a quoted token is a value). */
|
|
108
|
+
function readField(s) {
|
|
109
|
+
s.skipSpace();
|
|
110
|
+
const m = /^[A-Za-z_][\w]*(?:\.[A-Za-z_][\w]*)*/.exec(s.rest);
|
|
111
|
+
if (!m) {
|
|
112
|
+
if (/^['"]/.test(s.rest)) {
|
|
113
|
+
throw new CeqlParseError(`quoted field name at "${s.rest.slice(0, 24)}" — CEQL reads a quoted token as a value, `
|
|
114
|
+
+ 'so write the field bare: `displayName = \'active\'`, never `\'displayName\' = \'active\'`.', s.src);
|
|
115
|
+
}
|
|
116
|
+
throw new CeqlParseError(`expected a field name at "${s.rest.slice(0, 24) || '<end>'}"`, s.src);
|
|
117
|
+
}
|
|
118
|
+
s.pos += m[0].length;
|
|
119
|
+
return m[0];
|
|
120
|
+
}
|
|
121
|
+
function readOperator(s) {
|
|
122
|
+
s.skipSpace();
|
|
123
|
+
for (const [pattern, op] of WORD_OPERATORS) {
|
|
124
|
+
const m = pattern.exec(s.rest);
|
|
125
|
+
if (m) {
|
|
126
|
+
s.pos += m[0].length;
|
|
127
|
+
return op;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const alias = /^([A-Za-z]+)\b/.exec(s.rest);
|
|
131
|
+
if (alias && ODATA_ALIASES[alias[1].toLowerCase()]) {
|
|
132
|
+
const wrong = alias[1];
|
|
133
|
+
throw new CeqlParseError(`\`${wrong}\` is an OData operator; CEQL uses \`${ODATA_ALIASES[wrong.toLowerCase()]}\`.`, s.src);
|
|
134
|
+
}
|
|
135
|
+
for (const [text, op] of SYMBOL_OPERATORS) {
|
|
136
|
+
if (s.src.startsWith(text, s.pos)) {
|
|
137
|
+
s.pos += text.length;
|
|
138
|
+
return op;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
throw new CeqlParseError(`expected an operator at "${s.rest.slice(0, 24) || '<end>'}"`, s.src);
|
|
142
|
+
}
|
|
143
|
+
/** A single-quoted string, a number, or a boolean. Doubled `''` escapes a quote. */
|
|
144
|
+
function readValue(s) {
|
|
145
|
+
s.skipSpace();
|
|
146
|
+
if (s.rest.startsWith("'")) {
|
|
147
|
+
let i = 1;
|
|
148
|
+
let text = '';
|
|
149
|
+
for (;;) {
|
|
150
|
+
if (i >= s.rest.length)
|
|
151
|
+
throw new CeqlParseError('unterminated string value', s.src);
|
|
152
|
+
const ch = s.rest[i];
|
|
153
|
+
if (ch === "'") {
|
|
154
|
+
if (s.rest[i + 1] === "'") {
|
|
155
|
+
text += "'";
|
|
156
|
+
i += 2;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
i++;
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
text += ch;
|
|
163
|
+
i++;
|
|
164
|
+
}
|
|
165
|
+
s.pos += i;
|
|
166
|
+
return { value: text, rawString: JSON.stringify(text) };
|
|
167
|
+
}
|
|
168
|
+
if (s.rest.startsWith('"')) {
|
|
169
|
+
throw new CeqlParseError('double-quoted value — CEQL reads a double-quoted token as a COLUMN reference, not a string. '
|
|
170
|
+
+ "Single-quote it: `accountNumber = 'ACC123'`.", s.src);
|
|
171
|
+
}
|
|
172
|
+
const bool = /^(true|false)\b/i.exec(s.rest);
|
|
173
|
+
if (bool) {
|
|
174
|
+
s.pos += bool[0].length;
|
|
175
|
+
const value = bool[0].toLowerCase() === 'true';
|
|
176
|
+
return { value, rawString: String(value) };
|
|
177
|
+
}
|
|
178
|
+
const num = /^-?\d+(?:\.\d+)?/.exec(s.rest);
|
|
179
|
+
if (num) {
|
|
180
|
+
s.pos += num[0].length;
|
|
181
|
+
return { value: Number(num[0]), rawString: num[0] };
|
|
182
|
+
}
|
|
183
|
+
throw new CeqlParseError(`expected a quoted string, number, or boolean at "${s.rest.slice(0, 24) || '<end>'}"`, s.src);
|
|
184
|
+
}
|
|
185
|
+
function readComparison(s, seed, ordinal) {
|
|
186
|
+
const id = readField(s);
|
|
187
|
+
const operator = readOperator(s);
|
|
188
|
+
const uiId = stableId(`${seed}:${id}:${ordinal}`);
|
|
189
|
+
if (operator === 'IsNull' || operator === 'IsNotNull')
|
|
190
|
+
return { id, operator, uiId };
|
|
191
|
+
const { value, rawString } = readValue(s);
|
|
192
|
+
return { id, operator, value: { value, rawString, isLiteral: true }, uiId };
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* One level: comparisons and parenthesised sub-groups joined by a SINGLE logical
|
|
196
|
+
* operator.
|
|
197
|
+
*
|
|
198
|
+
* Mixing `AND` and `OR` at one level is refused rather than guessed. The tree has
|
|
199
|
+
* exactly one `groupOperator` per level, so `a = 1 AND b = 2 OR c = 3` has no
|
|
200
|
+
* faithful representation — and picking a precedence would silently emit a tree
|
|
201
|
+
* that filters differently from the string beside it, which is the one outcome
|
|
202
|
+
* worse than not supporting it. Parenthesise to say which was meant.
|
|
203
|
+
*/
|
|
204
|
+
function readGroup(s, seed, index, depth) {
|
|
205
|
+
if (depth > 32)
|
|
206
|
+
throw new CeqlParseError('filter nests too deeply', s.src);
|
|
207
|
+
const filters = [];
|
|
208
|
+
const groups = [];
|
|
209
|
+
let groupOperator;
|
|
210
|
+
let ordinal = 0;
|
|
211
|
+
for (;;) {
|
|
212
|
+
s.skipSpace();
|
|
213
|
+
if (s.rest.startsWith('(')) {
|
|
214
|
+
s.pos++;
|
|
215
|
+
groups.push(readGroup(s, `${seed}:g${groups.length}`, groups.length, depth + 1));
|
|
216
|
+
s.skipSpace();
|
|
217
|
+
if (!s.eat(')'))
|
|
218
|
+
throw new CeqlParseError('unbalanced parenthesis', s.src);
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
filters.push(readComparison(s, seed, ordinal++));
|
|
222
|
+
}
|
|
223
|
+
const before = s.pos;
|
|
224
|
+
if (s.eat('AND')) {
|
|
225
|
+
if (groupOperator === 1) {
|
|
226
|
+
s.pos = before;
|
|
227
|
+
throw new CeqlParseError('AND and OR are mixed at one level. A filter tree carries one operator per level, so '
|
|
228
|
+
+ 'parenthesise the intent: `(a AND b) OR c`.', s.src);
|
|
229
|
+
}
|
|
230
|
+
groupOperator = 0;
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
if (s.eat('OR')) {
|
|
234
|
+
if (groupOperator === 0) {
|
|
235
|
+
s.pos = before;
|
|
236
|
+
throw new CeqlParseError('AND and OR are mixed at one level. A filter tree carries one operator per level, so '
|
|
237
|
+
+ 'parenthesise the intent: `(a OR b) AND c`.', s.src);
|
|
238
|
+
}
|
|
239
|
+
groupOperator = 1;
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
return { groupOperator: groupOperator ?? 0, index, filters, groups, uuId: stableId(`${seed}:group`) };
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Parse a CEQL filter expression into the canvas's filter tree.
|
|
248
|
+
*
|
|
249
|
+
* `seed` makes the tree's ids deterministic — recompiling an unchanged flow must
|
|
250
|
+
* be byte-identical, so the node id and parameter name are folded in rather than
|
|
251
|
+
* minting random uuids the way the designer does.
|
|
252
|
+
*
|
|
253
|
+
* Throws {@link CeqlParseError} with the reason when the expression is not CEQL
|
|
254
|
+
* this can represent. That is the point: the alternative is a tenant-side
|
|
255
|
+
* `[102003] Integration Services bad request` with nothing naming the step.
|
|
256
|
+
*/
|
|
257
|
+
export function parseCeql(expression, seed) {
|
|
258
|
+
const trimmed = expression.trim();
|
|
259
|
+
if (trimmed === '')
|
|
260
|
+
throw new CeqlParseError('empty filter expression', expression);
|
|
261
|
+
const s = new Scanner(trimmed);
|
|
262
|
+
const tree = readGroup(s, seed, 0, 0);
|
|
263
|
+
if (!s.atEnd()) {
|
|
264
|
+
throw new CeqlParseError(`unexpected "${s.rest.slice(0, 24)}" after the filter`, expression);
|
|
265
|
+
}
|
|
266
|
+
// A lone parenthesised expression parses as a group holding one group. Lift it,
|
|
267
|
+
// so `(a = 1)` and `a = 1` produce the same tree.
|
|
268
|
+
if (tree.filters.length === 0 && tree.groups.length === 1) {
|
|
269
|
+
return { ...tree.groups[0], index: tree.index, uuId: tree.uuId };
|
|
270
|
+
}
|
|
271
|
+
return tree;
|
|
272
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* core/cli-spelling — naming the CLI the user is actually holding.
|
|
3
|
+
*
|
|
4
|
+
* Every remedy `check` prints is a command meant to be pasted. Which binary
|
|
5
|
+
* spells it depends on how the SDK was reached, and the SDK cannot see that:
|
|
6
|
+
* `@uipath/cli` resolves the workspace SDK and spawns `dist/cli/index.js`, so
|
|
7
|
+
* by the time this code runs, `process.argv[1]` is the SDK's own bin either
|
|
8
|
+
* way. The caller knows, and passes it down in `FLOW_SDK_INVOKED_AS`.
|
|
9
|
+
*
|
|
10
|
+
* The default stays `npx flow-sdk`, which is what a workspace with no `uip`
|
|
11
|
+
* needs and what the original hard-coding was protecting.
|
|
12
|
+
*/
|
|
13
|
+
/** The spelling used when nothing says otherwise. */
|
|
14
|
+
export declare const DEFAULT_CLI_SPELLING = "npx flow-sdk";
|
|
15
|
+
/** Just the variable this module reads — see {@link cliSpelling} for why. */
|
|
16
|
+
export interface CliSpellingEnv {
|
|
17
|
+
FLOW_SDK_INVOKED_AS?: string | undefined;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* How to spell the CLI in a printed remedy.
|
|
21
|
+
*
|
|
22
|
+
* @param env - Environment to read; defaults to the ambient `process.env`.
|
|
23
|
+
* @returns `uip maestro` when the caller said so, else `npx flow-sdk`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function cliSpelling(env?: CliSpellingEnv): string;
|
|
26
|
+
/**
|
|
27
|
+
* A `registry prepare` command line, spelled for whoever is driving.
|
|
28
|
+
*
|
|
29
|
+
* @param args - Everything after `registry prepare`, e.g. the connector key and action.
|
|
30
|
+
* @returns The full command, no trailing newline.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* prepareCommand('uipath-salesforce-slack', 'send-message-to-user');
|
|
35
|
+
* // -> "npx flow-sdk registry prepare uipath-salesforce-slack send-message-to-user"
|
|
36
|
+
* // -> "uip maestro registry prepare …" when FLOW_SDK_INVOKED_AS="uip maestro"
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare function prepareCommand(...args: string[]): string;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* core/cli-spelling — naming the CLI the user is actually holding.
|
|
3
|
+
*
|
|
4
|
+
* Every remedy `check` prints is a command meant to be pasted. Which binary
|
|
5
|
+
* spells it depends on how the SDK was reached, and the SDK cannot see that:
|
|
6
|
+
* `@uipath/cli` resolves the workspace SDK and spawns `dist/cli/index.js`, so
|
|
7
|
+
* by the time this code runs, `process.argv[1]` is the SDK's own bin either
|
|
8
|
+
* way. The caller knows, and passes it down in `FLOW_SDK_INVOKED_AS`.
|
|
9
|
+
*
|
|
10
|
+
* The default stays `npx flow-sdk`, which is what a workspace with no `uip`
|
|
11
|
+
* needs and what the original hard-coding was protecting.
|
|
12
|
+
*/
|
|
13
|
+
/** The spelling used when nothing says otherwise. */
|
|
14
|
+
export const DEFAULT_CLI_SPELLING = 'npx flow-sdk';
|
|
15
|
+
/**
|
|
16
|
+
* Lowercase words, single-spaced, at most three.
|
|
17
|
+
*
|
|
18
|
+
* The value is interpolated into a command a human is invited to paste into a
|
|
19
|
+
* shell, so it is validated rather than trusted: an unset-but-inherited or
|
|
20
|
+
* malformed value must degrade to the default, not travel into someone's
|
|
21
|
+
* terminal. `npx flow-sdk` and `uip maestro` both satisfy it.
|
|
22
|
+
*/
|
|
23
|
+
const SPELLING = /^[a-z][a-z0-9-]*(?: [a-z][a-z0-9-]*){0,2}$/;
|
|
24
|
+
/**
|
|
25
|
+
* `process.env`, read through `globalThis` so this module needs no Node types.
|
|
26
|
+
*
|
|
27
|
+
* `core/lookups.ts` imports this one, and the generated connector modules are
|
|
28
|
+
* type-checked against the SDK SOURCE under `strict` with no `@types/node`
|
|
29
|
+
* (see `tests/connectors.test.ts`). Naming `NodeJS.ProcessEnv` or a bare
|
|
30
|
+
* `process` here fails that check, which is a real constraint and not a
|
|
31
|
+
* formality: it is the same graph an author's `tsc` walks.
|
|
32
|
+
*/
|
|
33
|
+
function ambientEnv() {
|
|
34
|
+
const global = globalThis;
|
|
35
|
+
return global.process?.env ?? {};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* How to spell the CLI in a printed remedy.
|
|
39
|
+
*
|
|
40
|
+
* @param env - Environment to read; defaults to the ambient `process.env`.
|
|
41
|
+
* @returns `uip maestro` when the caller said so, else `npx flow-sdk`.
|
|
42
|
+
*/
|
|
43
|
+
export function cliSpelling(env = ambientEnv()) {
|
|
44
|
+
const hint = (env.FLOW_SDK_INVOKED_AS ?? '').trim();
|
|
45
|
+
return SPELLING.test(hint) ? hint : DEFAULT_CLI_SPELLING;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A `registry prepare` command line, spelled for whoever is driving.
|
|
49
|
+
*
|
|
50
|
+
* @param args - Everything after `registry prepare`, e.g. the connector key and action.
|
|
51
|
+
* @returns The full command, no trailing newline.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* prepareCommand('uipath-salesforce-slack', 'send-message-to-user');
|
|
56
|
+
* // -> "npx flow-sdk registry prepare uipath-salesforce-slack send-message-to-user"
|
|
57
|
+
* // -> "uip maestro registry prepare …" when FLOW_SDK_INVOKED_AS="uip maestro"
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export function prepareCommand(...args) {
|
|
61
|
+
return [cliSpelling(), 'registry', 'prepare', ...args.filter(Boolean)].join(' ');
|
|
62
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Why a `rawNode` may not carry a connector node type, or `undefined` when the
|
|
3
|
+
* type is not a connector's.
|
|
4
|
+
*
|
|
5
|
+
* Every entry of the connector library is `uipath.connector.<key>.<action>`
|
|
6
|
+
* (7,455 of 7,455 measured), and every one of them needs what only
|
|
7
|
+
* {@link connector} produces: `inputs.detail` routed by field location plus the
|
|
8
|
+
* flow-level connection/folder bindings. A raw node renders the author's inputs
|
|
9
|
+
* as they are, so the artifact compiles, `validate` merely warns that the
|
|
10
|
+
* connector "is not configured", and the run has no connection to call. Measured
|
|
11
|
+
* on the eval archive: 4 of 4 Data Fabric flows authored this way failed their
|
|
12
|
+
* checker, each after `compile` had refused an input the static library does not
|
|
13
|
+
* carry — the raw node was the escape hatch around that refusal.
|
|
14
|
+
*
|
|
15
|
+
* Event and trigger types (`uipath.connector.event.*`, `uipath.connector.trigger.*`)
|
|
16
|
+
* are named separately: they belong to {@link onEvent}, not to a step.
|
|
17
|
+
*
|
|
18
|
+
* @param nodeType - The definition's `nodeType`, as passed to {@link rawNode}.
|
|
19
|
+
* @returns The refusal to report, or `undefined` for a non-connector type.
|
|
20
|
+
*/
|
|
21
|
+
export declare function connectorRawNodeRefusal(nodeType: string): string | undefined;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal: the one rule that keeps `rawNode` from standing in for `connector()`.
|
|
3
|
+
* Not re-exported from the package entry point.
|
|
4
|
+
*/
|
|
5
|
+
import { prepareCommand } from './cli-spelling.js';
|
|
6
|
+
/**
|
|
7
|
+
* Why a `rawNode` may not carry a connector node type, or `undefined` when the
|
|
8
|
+
* type is not a connector's.
|
|
9
|
+
*
|
|
10
|
+
* Every entry of the connector library is `uipath.connector.<key>.<action>`
|
|
11
|
+
* (7,455 of 7,455 measured), and every one of them needs what only
|
|
12
|
+
* {@link connector} produces: `inputs.detail` routed by field location plus the
|
|
13
|
+
* flow-level connection/folder bindings. A raw node renders the author's inputs
|
|
14
|
+
* as they are, so the artifact compiles, `validate` merely warns that the
|
|
15
|
+
* connector "is not configured", and the run has no connection to call. Measured
|
|
16
|
+
* on the eval archive: 4 of 4 Data Fabric flows authored this way failed their
|
|
17
|
+
* checker, each after `compile` had refused an input the static library does not
|
|
18
|
+
* carry — the raw node was the escape hatch around that refusal.
|
|
19
|
+
*
|
|
20
|
+
* Event and trigger types (`uipath.connector.event.*`, `uipath.connector.trigger.*`)
|
|
21
|
+
* are named separately: they belong to {@link onEvent}, not to a step.
|
|
22
|
+
*
|
|
23
|
+
* @param nodeType - The definition's `nodeType`, as passed to {@link rawNode}.
|
|
24
|
+
* @returns The refusal to report, or `undefined` for a non-connector type.
|
|
25
|
+
*/
|
|
26
|
+
export function connectorRawNodeRefusal(nodeType) {
|
|
27
|
+
if (typeof nodeType !== 'string' || !nodeType.startsWith('uipath.connector.'))
|
|
28
|
+
return undefined;
|
|
29
|
+
const rest = nodeType.slice('uipath.connector.'.length);
|
|
30
|
+
const isTrigger = rest.startsWith('event.') || rest.startsWith('trigger.');
|
|
31
|
+
if (isTrigger) {
|
|
32
|
+
return (`rawNode("${nodeType}") names a connector event, which cannot be placed as a step and cannot be `
|
|
33
|
+
+ `authored raw: use onEvent(...) as the flow's trigger (or waitForEvent(...) mid-flow) so the `
|
|
34
|
+
+ `subscription and its connection binding are emitted.`);
|
|
35
|
+
}
|
|
36
|
+
const dot = rest.lastIndexOf('.');
|
|
37
|
+
const key = dot > 0 ? rest.slice(0, dot) : rest;
|
|
38
|
+
const action = dot > 0 ? rest.slice(dot + 1) : '<action>';
|
|
39
|
+
return (`rawNode("${nodeType}") names a connector operation. A raw node keeps its inputs verbatim, so the `
|
|
40
|
+
+ `emitted node would have no inputs.detail and no connection binding: \`validate\` only warns `
|
|
41
|
+
+ `("Connector is not configured") and the run can never reach Integration Service. `
|
|
42
|
+
+ `Author it as connector('${key}', '${action}', { ...inputs }, { connection: '<binding>', folder: '<binding>' }). `
|
|
43
|
+
+ `If compile then refuses an input the static library does not carry, run `
|
|
44
|
+
+ `\`${prepareCommand(key, action)}\` — it discovers the connection, writes bindings.json `
|
|
45
|
+
+ `and loads the connection-resolved fields into ./connectors-local/ (add \`-f <parent>=<value>\`, e.g. `
|
|
46
|
+
+ `\`-f entityName=<Entity>\`, when the operation's fields depend on a parent input).`);
|
|
47
|
+
}
|