@uipath/maestro-tool 1.202.2 → 1.203.0-preview.180
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/README.md +8 -0
- package/dist/THIRD-PARTY-NOTICES.md +83 -290
- package/dist/{authoring-818595dq.js → authoring-at68vr5g.js} +7 -5
- package/dist/{authoring-6r3wx3fa.js → authoring-sh284a0x.js} +3 -3
- package/dist/{authoring-61hqf5em.js → authoring-szfy0rh1.js} +5 -5
- package/dist/{binding-service-643e69bb.js → binding-service-8a05gy4k.js} +37 -29
- package/dist/{bpmn-action-type-validator-kx36sqhe.js → bpmn-action-type-validator-8kvyvvcf.js} +2 -2
- package/dist/bpmn-binding-resolver-5scq28mc.js +145 -0
- package/dist/{bpmn-branch-y1m9f10n.js → bpmn-branch-t12a7jvf.js} +50 -44
- package/dist/bpmn-format-service-ay6jd9he.js +69 -0
- package/dist/{bpmn-refresh-service-ew545y06.js → bpmn-refresh-service-ak1ctkwg.js} +4 -4
- package/dist/{bpmn-update-metadata-service-jhahhjab.js → bpmn-update-metadata-service-hqe4edze.js} +6 -5
- package/dist/{bpmn-validate-service-6z2dj8ac.js → bpmn-validate-service-48sfy3a2.js} +3 -3
- package/dist/{case-api-4mstfkzf.js → case-api-e3fmq685.js} +6 -6
- package/dist/case-bindings-service-nswc6v0v.js +240 -0
- package/dist/case-debug-studio-web-service-58gtqznh.js +135 -0
- package/dist/case-format-service-6s5e1ckk.js +84 -0
- package/dist/{case-pack-service-v7wtb0qt.js → case-pack-service-sg435he1.js} +62 -11
- package/dist/{case-validate-service-bnpaf0zt.js → case-validate-service-7s69shsa.js} +1424 -42
- package/dist/{connector-enrichment-service-scrqnke3.js → connector-enrichment-service-9rqr3esj.js} +27 -26
- package/dist/{connector-lookup-service-hnk08t3m.js → connector-lookup-service-pencasp9.js} +5 -5
- package/dist/{connector-service-581bhxaj.js → connector-service-k4cgcv2b.js} +224 -56
- package/dist/{connector-task-spec-y2tcthz5.js → connector-task-spec-13ex0x0m.js} +27 -26
- package/dist/{conversion-FQTRFHRX-03xh3xbp.js → conversion-FQTRFHRX-0y9br7gw.js} +6 -5
- package/dist/de-FGHEJRCI-gn4cf5gh.js +2697 -0
- package/dist/de-MUJPA4EB-dpg7t2rw.js +173 -0
- package/dist/{debug-conversation-trigger-nj04k7s2.js → debug-conversation-trigger-wgzjaasj.js} +17 -14
- package/dist/{debug-http-client-c4teyma3.js → debug-http-client-7dvbwp96.js} +2 -2
- package/dist/{debug-service-m5jdyz3q.js → debug-service-8cwxhgww.js} +3 -3
- package/dist/{edge-service-vv2yfsn4.js → edge-service-szpsaezp.js} +17 -14
- package/dist/{index-yqfqxh2r.js → embedded-file-reader-0sjt4nkj.js} +96 -16641
- package/dist/{en-TBNO2WUV-rfe87pgh.js → en-BWGKA5OE-1qkvb9rn.js} +2 -2
- package/dist/en-FBPVNKFN-dqt573xn.js +138 -0
- package/dist/en-TBNO2WUV-9nan43e5.js +1936 -0
- package/dist/{enrichment-service-a65qt7y1.js → enrichment-service-7n1tf75p.js} +4 -4
- package/dist/es-7SE7HX2X-8yh1aj2y.js +173 -0
- package/dist/es-KR3ZSNMP-0cszm2n3.js +2697 -0
- package/dist/es-MX-2MYFZU7X-3rek4r73.js +173 -0
- package/dist/es-MX-XXZJGC4R-b4fbwe9d.js +2697 -0
- package/dist/first-party-service-srxaftkn.js +27 -0
- package/dist/{flow-api-vqvvs0q2.js → flow-api-4vkpbmt3.js} +7 -7
- package/dist/{flow-debug-service-vd7bdyhg.js → flow-debug-service-9tad5ckh.js} +66 -41
- package/dist/{flow-eval-node-target-mve3v55h.js → flow-eval-node-target-g222rh2e.js} +22 -15
- package/dist/flow-eval-run-api-hashjgq1.js +13 -0
- package/dist/{flow-eval-run-service-rt9kg4wx.js → flow-eval-run-service-btghb02r.js} +109 -224
- package/dist/{flow-eval-service-ededh59n.js → flow-eval-service-ma5psrsd.js} +346 -302
- package/dist/{flow-init-service-4ve9tr0t.js → flow-init-service-662zhfd3.js} +22 -18
- package/dist/{flow-io-2dd3sx38.js → flow-io-3cqz0svg.js} +16 -13
- package/dist/{flow-migrate-service-nbe6et60.js → flow-migrate-service-ehx7nmsr.js} +18 -14
- package/dist/{flow-pack-service-b2n877d8.js → flow-pack-service-a3w3489r.js} +43 -35
- package/dist/flow-validate-service-02e21w4d.js +3555 -0
- package/dist/fr-MDFKNZ4C-h4a122k9.js +173 -0
- package/dist/fr-Y6NPVODW-q5ef3htr.js +2697 -0
- package/dist/{governance-fetcher-at165287.js → governance-fetcher-7vxmap1e.js} +5 -4
- package/dist/{headless-85f2cys0.js → headless-627q5my5.js} +42909 -1756
- package/dist/{hitl-service-xn1bt80z.js → hitl-service-fn94tk6z.js} +16 -13
- package/dist/{index-hfsfzg4k.js → index-3arytfxn.js} +6 -4
- package/dist/{index-d1yqqk6q.js → index-55cym5j7.js} +6 -4
- package/dist/{index-0j6xww0f.js → index-9f94yk28.js} +3 -3
- package/dist/{index-1kgxzh8x.js → index-fz4ttsde.js} +14 -2
- package/dist/index-gvwc29bb.js +16742 -0
- package/dist/{index-mkgrn150.js → index-j1azsfr5.js} +36 -33
- package/dist/index-kmp9fmhb.js +25 -0
- package/dist/{index-938ee96p.js → index-rtg24r4x.js} +1 -1
- package/dist/index-tzccx5ym.js +2961 -0
- package/dist/{index-acd6bpqc.js → index-ymsd3pyx.js} +12 -15
- package/dist/index.js +3 -3
- package/dist/{input-reader-1wda1a2v.js → input-reader-7pzkcw6t.js} +1 -1
- package/dist/{input-reader-svafwk7x.js → input-reader-j8rvde4z.js} +1 -1
- package/dist/ja-Q5PXKJ2V-tzf2rvz2.js +2697 -0
- package/dist/ja-UTQEAP34-8e0q6swp.js +173 -0
- package/dist/known-types-8dhhqk35.js +19 -0
- package/dist/ko-AXCHW2OK-askct7f6.js +173 -0
- package/dist/ko-AYBDSRZG-am0h0m2r.js +2697 -0
- package/dist/{maestro-automate-entry-gwntzkvj.js → maestro-automate-entry-jehpbg37.js} +2 -2
- package/dist/{maestro-debug-service-dvh7qbx8.js → maestro-debug-service-xhtszkrc.js} +10 -10
- package/dist/{maestro-pack-service-fam5zdm3.js → maestro-pack-service-fkkc5g3v.js} +12 -6
- package/dist/{node-ks3095fb.js → node-p4bde6qy.js} +2344 -376
- package/dist/node-service-frvz67y3.js +83 -0
- package/dist/{node-sync-service-zwvm30qg.js → node-sync-service-wa2t7c65.js} +14 -14
- package/dist/{packager-tool-me5q0naa.js → packager-tool-095jzn8v.js} +16 -6
- package/dist/{packager-tool-y5f2qhfe.js → packager-tool-19030vpg.js} +32 -2
- package/dist/{packager-tool-gtqkcj4x.js → packager-tool-2c1rbj85.js} +310 -13
- package/dist/packager-tool-2q1mffef.js +261 -0
- package/dist/{flow-validate-service-0hxf5080.js → packager-tool-2xrng5ff.js} +1956 -3065
- package/dist/{packager-tool-8dgxmjzx.js → packager-tool-30ky51ca.js} +6 -3
- package/dist/{packager-tool-d5mjdpbf.js → packager-tool-4r35ye9r.js} +5 -4
- package/dist/packager-tool-53w8skv5.js +262 -0
- package/dist/{packager-tool-db4jm1rz.js → packager-tool-56gcg9de.js} +1 -1
- package/dist/{packager-tool-29eg86w6.js → packager-tool-6rsegsby.js} +6 -7
- package/dist/packager-tool-7msx23gd.js +13 -0
- package/dist/packager-tool-7q6z6npd.js +1937 -0
- package/dist/packager-tool-8qp60s20.js +12 -0
- package/dist/{packager-tool-s1r7krkp.js → packager-tool-9pccpz3r.js} +1 -1
- package/dist/{packager-tool-m351tkkr.js → packager-tool-c95fhhys.js} +30 -8
- package/dist/{packager-tool-kg5sxneg.js → packager-tool-cmzzh4f8.js} +72 -73
- package/dist/{packager-tool-ffvmys2r.js → packager-tool-cr0z8cbj.js} +5 -5
- package/dist/{packager-tool-8fkt71n4.js → packager-tool-cs1ec1da.js} +4 -3
- package/dist/{packager-tool-xatnrtwp.js → packager-tool-d2813z49.js} +9 -9
- package/dist/packager-tool-d88rqwk8.js +135 -0
- package/dist/{packager-tool-pm3qh1hy.js → packager-tool-dh5nppv4.js} +1 -1
- package/dist/packager-tool-dqxz5v18.js +486 -0
- package/dist/{packager-tool-r87gxwqa.js → packager-tool-e87tzg4y.js} +2 -2
- package/dist/{packager-tool-w9fd7vzs.js → packager-tool-f33sg3vr.js} +9 -9
- package/dist/{packager-tool-cj2ka9jr.js → packager-tool-fer9mdsq.js} +2 -2
- package/dist/{packager-tool-70eh7rpg.js → packager-tool-fm67whdy.js} +23 -5
- package/dist/packager-tool-fq7wtkd3.js +21012 -0
- package/dist/packager-tool-fvm2dddf.js +6297 -0
- package/dist/packager-tool-gdtpsdn7.js +342 -0
- package/dist/{packager-tool-ejyeka5x.js → packager-tool-gpwec9sp.js} +2 -2
- package/dist/{packager-tool-pap09zfh.js → packager-tool-h8x54cvx.js} +1 -1
- package/dist/{packager-tool-9vh3df76.js → packager-tool-hdmhxdch.js} +7 -7
- package/dist/packager-tool-hnyecbk6.js +88 -0
- package/dist/{packager-tool-mtbak8t4.js → packager-tool-j00gcgbs.js} +2 -2
- package/dist/packager-tool-j78fwwhq.js +16 -0
- package/dist/packager-tool-jysvpt8p.js +11 -0
- package/dist/{packager-tool-jfv7t0c8.js → packager-tool-kb554twx.js} +94 -94
- package/dist/{packager-tool-k4x5yn3p.js → packager-tool-khm7zsm8.js} +4 -1
- package/dist/packager-tool-kj0qc4f4.js +269 -0
- package/dist/{packager-tool-12636p2y.js → packager-tool-mcjjj1hk.js} +4 -4
- package/dist/{packager-tool-bftjhmqz.js → packager-tool-mfrdmqmt.js} +124 -2
- package/dist/{packager-tool-qgh5yaeh.js → packager-tool-mj89jhvb.js} +1 -1
- package/dist/{packager-tool-ve35117x.js → packager-tool-mjz4hma1.js} +5791 -176
- package/dist/packager-tool-paq9c70w.js +1332 -0
- package/dist/{packager-tool-d8v984ah.js → packager-tool-q2gwvnf5.js} +15 -56
- package/dist/{packager-tool-nmznynbj.js → packager-tool-qff6ang6.js} +1 -1
- package/dist/{packager-tool-j79gbsee.js → packager-tool-rg5pkkf0.js} +6 -6
- package/dist/{packager-tool-7309ymg1.js → packager-tool-s1466723.js} +10 -13
- package/dist/{packager-tool-54b4v7wp.js → packager-tool-te6rrpsz.js} +205 -195
- package/dist/packager-tool-vcjq9xkc.js +1895 -0
- package/dist/{packager-tool-w20r6n9v.js → packager-tool-vck7ct1e.js} +2877 -2221
- package/dist/{packager-tool-e8td7b01.js → packager-tool-vdc8r21p.js} +10328 -16610
- package/dist/{packager-tool-nxw5yzs7.js → packager-tool-vqcnvaae.js} +1 -1
- package/dist/{packager-tool-7rz102fw.js → packager-tool-wd01egnx.js} +1 -1
- package/dist/{packager-tool-gxqd24n0.js → packager-tool-wger1vt9.js} +7 -7
- package/dist/{packager-tool-a6yfnjgx.js → packager-tool-wynrdnqj.js} +1 -1
- package/dist/packager-tool-x0eamrk4.js +104577 -0
- package/dist/packager-tool-x92czn5x.js +208 -0
- package/dist/{packager-tool-am67sn1c.js → packager-tool-xtkgeccn.js} +1 -1
- package/dist/packager-tool-y59jhapb.js +10369 -0
- package/dist/{packager-tool-317z6dc1.js → packager-tool-ydg78w9z.js} +23 -23
- package/dist/{packager-tool-rr8hveb9.js → packager-tool-ygrrb1zh.js} +1 -1
- package/dist/{packager-tool-f0kd5kq4.js → packager-tool-yrsrzz1a.js} +127 -12
- package/dist/{packager-tool-d9dpx9wn.js → packager-tool-z185rq2a.js} +1 -1
- package/dist/{packager-tool-79gws1zn.js → packager-tool-z6nkdv48.js} +270 -24
- package/dist/{packager-tool-802qm7jy.js → packager-tool-zzxt99e4.js} +3 -3
- package/dist/packager-tool.js +75 -218
- package/dist/{project-artifacts-9z7atbk7.js → project-artifacts-h5cydk2n.js} +1 -1
- package/dist/{project-validator-0rkrfmj0.js → project-validator-ek97s27p.js} +1 -1
- package/dist/pt-3DIO5QWG-59b3f4bc.js +173 -0
- package/dist/pt-BR-TKHNHMET-dn6zxk1d.js +2697 -0
- package/dist/pt-BR-Y6K6XHXK-mp3hwhw1.js +173 -0
- package/dist/pt-ROXU257Y-yd80gx6s.js +2697 -0
- package/dist/{resourcesSyncService-3pajvveg.js → resourcesSyncService-7s92g1hm.js} +28 -27
- package/dist/ro-AVAWPKOO-42m6am1w.js +2697 -0
- package/dist/ro-BWR3ONRR-vzx3hy8a.js +173 -0
- package/dist/ru-GSCT4ARZ-jrxwev63.js +10 -0
- package/dist/ru-WBLQ5B2U-s4em88p0.js +10 -0
- package/dist/sdd-lookup-7h7j2h6v.js +48 -0
- package/dist/{serialization-BXU6AKAN-dk0sqx89.js → serialization-BXU6AKAN-0ghhrsja.js} +6 -5
- package/dist/{shared-utils-twgpb9ax.js → shared-utils-6z7gnnc8.js} +1 -1
- package/dist/{strict-case-rules-zxnq80zm.js → strict-case-rules-byym8cvc.js} +141 -109
- package/dist/strict-sdd-audit-v156c9e3.js +2520 -0
- package/dist/tidy-service-zyy7wxc0.js +39 -0
- package/dist/{tool-pxfafj3m.js → tool-dejjyv8m.js} +213 -51
- package/dist/{tool-td30zmyt.js → tool-v0r5qzcc.js} +106 -146
- package/dist/tool.js +3 -3
- package/dist/tr-5IIHSFW5-hw4cewew.js +173 -0
- package/dist/tr-JMIP7ZSL-fb67ft9w.js +2696 -0
- package/dist/{trigger-enrichment-service-t3wcd12r.js → trigger-enrichment-service-ak1nrz5t.js} +30 -29
- package/dist/{variable-service-2q705dsx.js → variable-service-t9gsp8xs.js} +16 -13
- package/dist/{wait-for-flow-job-sx0ht8sb.js → wait-for-flow-job-1s8ka07s.js} +7 -7
- package/dist/{xml-949787yf.js → xml-3rzcgm9z.js} +1359 -659
- package/dist/zh-CN-IFFXSEJS-tke4mzty.js +2697 -0
- package/dist/zh-CN-R2KU4U5L-tc1hat59.js +173 -0
- package/dist/zh-TW-GNKMELBF-j674515r.js +173 -0
- package/dist/zh-TW-IYBNYAMG-wcan0qpb.js +2697 -0
- package/package.json +3 -2
- package/dist/bpmn-format-service-r67afbny.js +0 -477
- package/dist/case-debug-studio-web-service-kj1797m8.js +0 -336
- package/dist/known-types-eq32py2n.js +0 -20
- package/dist/node-service-teagjd9n.js +0 -75
- package/dist/packager-tool-15bstxb6.js +0 -4192
- package/dist/packager-tool-1zpm7wts.js +0 -7217
- package/dist/packager-tool-7tjxtpfp.js +0 -106
- package/dist/packager-tool-ckhyvcn5.js +0 -58
- package/dist/packager-tool-dz506qqq.js +0 -2120
- package/dist/packager-tool-hpc2j0zw.js +0 -27
- package/dist/packager-tool-jjpc0yka.js +0 -304
- package/dist/packager-tool-xtv236p7.js +0 -73
- package/dist/strict-sdd-audit-zc0n5vj6.js +0 -1024
- package/dist/tidy-service-hc9gex0v.js +0 -36
- /package/dist/{localization-xecwjpe2.js → localization-y790z9vm.js} +0 -0
|
@@ -1,2120 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ConnectionsApi,
|
|
3
|
-
ElementsApi,
|
|
4
|
-
buildResourceCompactSummary,
|
|
5
|
-
buildTriggerCompactSummary,
|
|
6
|
-
createApiClient,
|
|
7
|
-
fetchRawMetadata,
|
|
8
|
-
fetchTriggerMetadata,
|
|
9
|
-
normalizeConnectionStatus
|
|
10
|
-
} from "./packager-tool-1zpm7wts.js";
|
|
11
|
-
import {
|
|
12
|
-
BPMN_SPEC,
|
|
13
|
-
SUPPORTED_UIPATH_EXTENSION_TAGS
|
|
14
|
-
} from "./packager-tool-jfv7t0c8.js";
|
|
15
|
-
import {
|
|
16
|
-
ReleasesApi,
|
|
17
|
-
createOrchestratorConfig
|
|
18
|
-
} from "./packager-tool-d5mjdpbf.js";
|
|
19
|
-
import {
|
|
20
|
-
getLoginStatusAsync
|
|
21
|
-
} from "./packager-tool-f0kd5kq4.js";
|
|
22
|
-
import {
|
|
23
|
-
OutputFormatter,
|
|
24
|
-
RESULTS,
|
|
25
|
-
UIPATH_HOME_DIR,
|
|
26
|
-
catchError,
|
|
27
|
-
hashContent,
|
|
28
|
-
isGuid,
|
|
29
|
-
logger,
|
|
30
|
-
processContext
|
|
31
|
-
} from "./packager-tool-79gws1zn.js";
|
|
32
|
-
import {
|
|
33
|
-
getFileSystem
|
|
34
|
-
} from "./packager-tool-0v3s8r01.js";
|
|
35
|
-
import {
|
|
36
|
-
__require
|
|
37
|
-
} from "./packager-tool-1yh8g550.js";
|
|
38
|
-
|
|
39
|
-
// ../maestro-sdk/src/bpmn-validation/project-validator.ts
|
|
40
|
-
var KNOWN_UIPATH_TAGS = new Set(SUPPORTED_UIPATH_EXTENSION_TAGS);
|
|
41
|
-
var CALL_ACTIVITY_TYPES = Object.entries(BPMN_SPEC.extensionTypes).filter(([, contract]) => contract.bpmnElement === "bpmn:CallActivity").map(([type]) => type).sort((a, b) => a.localeCompare(b));
|
|
42
|
-
var CALL_ACTIVITY_PAYLOAD_TAGS = new Set(Object.values(BPMN_SPEC.extensionTypes).filter((contract) => contract.bpmnElement === "bpmn:CallActivity").map((contract) => contract.extensionTag).filter((tag) => typeof tag === "string"));
|
|
43
|
-
var SEMANTIC_VALIDATION_ERROR = "BPMN_VALIDATION_ERROR";
|
|
44
|
-
var BPMN_MODEL_NAMESPACE = "http://www.omg.org/spec/BPMN/20100524/MODEL";
|
|
45
|
-
var ACTIVITY_CONFIGURATION_VERSION = "activityConfigurationVersion";
|
|
46
|
-
var ACTIVITY_CONFIGURATION_V1 = "v1";
|
|
47
|
-
|
|
48
|
-
class BpmnValidateService {
|
|
49
|
-
fileSystem;
|
|
50
|
-
options;
|
|
51
|
-
constructor(fileSystem, options = {}) {
|
|
52
|
-
this.fileSystem = fileSystem;
|
|
53
|
-
this.options = options;
|
|
54
|
-
}
|
|
55
|
-
async validateProject(projectPath) {
|
|
56
|
-
if (!projectPath) {
|
|
57
|
-
return this.createFailure([
|
|
58
|
-
{
|
|
59
|
-
file: ".",
|
|
60
|
-
message: "Project path is required.",
|
|
61
|
-
instruction: "Provide a ProcessOrchestration project path."
|
|
62
|
-
}
|
|
63
|
-
]);
|
|
64
|
-
}
|
|
65
|
-
const files = await this.fileSystem.readdir(projectPath);
|
|
66
|
-
const bpmnFiles = files.filter((file) => file.endsWith(".bpmn"));
|
|
67
|
-
const diagnostics = [];
|
|
68
|
-
if (bpmnFiles.length === 0) {
|
|
69
|
-
diagnostics.push({
|
|
70
|
-
file: ".",
|
|
71
|
-
message: "No BPMN files were found.",
|
|
72
|
-
instruction: "Add at least one .bpmn process file."
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
const bindings = await this.loadBindings(projectPath, diagnostics);
|
|
76
|
-
for (const file of bpmnFiles) {
|
|
77
|
-
const filePath = this.fileSystem.joinPath(projectPath, file);
|
|
78
|
-
const content = await this.fileSystem.readFile(filePath);
|
|
79
|
-
if (content === null || content.byteLength === 0) {
|
|
80
|
-
diagnostics.push({
|
|
81
|
-
file,
|
|
82
|
-
message: "BPMN file could not be read or is empty.",
|
|
83
|
-
instruction: "Ensure the file exists and contains BPMN XML."
|
|
84
|
-
});
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
const xml = decodeFileContent(content);
|
|
88
|
-
this.validateBpmnFile(file, xml, bindings, diagnostics);
|
|
89
|
-
await this.runFileValidators({ projectPath, file, xml, fileSystem: this.fileSystem }, diagnostics);
|
|
90
|
-
}
|
|
91
|
-
await this.runProjectValidators({ projectPath, bpmnFiles, fileSystem: this.fileSystem }, diagnostics);
|
|
92
|
-
const errors = diagnostics.filter(isErrorDiagnostic);
|
|
93
|
-
if (errors.length > 0) {
|
|
94
|
-
return this.createFailure(errors);
|
|
95
|
-
}
|
|
96
|
-
const warnings = diagnostics.filter(isWarningDiagnostic);
|
|
97
|
-
if (warnings.length > 0) {
|
|
98
|
-
return {
|
|
99
|
-
isValid: true,
|
|
100
|
-
diagnostics,
|
|
101
|
-
message: this.createWarningMessage(warnings)
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
return {
|
|
105
|
-
isValid: true,
|
|
106
|
-
diagnostics: []
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
validateBpmnFile(file, xml, bindings, diagnostics) {
|
|
110
|
-
const parseResult = parseXml(xml);
|
|
111
|
-
diagnostics.push(...parseResult.diagnostics.map((message) => ({
|
|
112
|
-
file,
|
|
113
|
-
message,
|
|
114
|
-
instruction: "Fix the BPMN XML so tags are well-formed."
|
|
115
|
-
})));
|
|
116
|
-
if (!parseResult.root) {
|
|
117
|
-
diagnostics.push({
|
|
118
|
-
file,
|
|
119
|
-
message: "BPMN XML does not contain a root element.",
|
|
120
|
-
instruction: "Export a valid BPMN definitions document."
|
|
121
|
-
});
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
const definitions = findFirst(parseResult.root, (node) => hasLocalName(node, "definitions"));
|
|
125
|
-
if (!definitions) {
|
|
126
|
-
diagnostics.push({
|
|
127
|
-
file,
|
|
128
|
-
message: "BPMN definitions element is missing.",
|
|
129
|
-
instruction: "Use a BPMN document rooted at bpmn:definitions."
|
|
130
|
-
});
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
const processes = findChildren(definitions, (node) => hasLocalName(node, "process"));
|
|
134
|
-
if (processes.length === 0) {
|
|
135
|
-
diagnostics.push({
|
|
136
|
-
file,
|
|
137
|
-
message: "BPMN process element is missing.",
|
|
138
|
-
instruction: "Add a bpmn:process element to the BPMN file."
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
for (const process of processes) {
|
|
142
|
-
if (findDescendants(process, (node) => hasLocalName(node, "startEvent")).length === 0) {
|
|
143
|
-
diagnostics.push({
|
|
144
|
-
file,
|
|
145
|
-
element: describeElement(process),
|
|
146
|
-
message: "BPMN process has no start event.",
|
|
147
|
-
instruction: "Add a bpmn:StartEvent to each process."
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
this.validateRootEntryPointIds(file, process, diagnostics);
|
|
151
|
-
}
|
|
152
|
-
const availableBindings = new Map(bindings);
|
|
153
|
-
collectInlineBindings(definitions, availableBindings);
|
|
154
|
-
this.validateUiPathTags(file, definitions, availableBindings, diagnostics);
|
|
155
|
-
this.validateUnassignedVariables(file, definitions, diagnostics);
|
|
156
|
-
this.validateCallActivityReferences(file, definitions, diagnostics);
|
|
157
|
-
}
|
|
158
|
-
validateUnassignedVariables(file, definitions, diagnostics) {
|
|
159
|
-
for (const process of findChildren(definitions, (node) => hasLocalName(node, "process"))) {
|
|
160
|
-
this.reportUnassignedVariables(file, process, diagnostics);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
reportUnassignedVariables(file, process, diagnostics) {
|
|
164
|
-
const startEventIds = new Set(findChildren(process, (node) => hasLocalName(node, "startEvent")).filter(isManualStartEvent).map((node) => node.attributes.id).filter((id) => Boolean(id)));
|
|
165
|
-
const declaredById = new Map;
|
|
166
|
-
for (const block of findDescendants(process, (node) => hasLocalName(node, "variables"))) {
|
|
167
|
-
for (const decl of block.children) {
|
|
168
|
-
const declKind = getLocalName(decl.name);
|
|
169
|
-
if (declKind !== "input" && declKind !== "inputoutput" && declKind !== "output") {
|
|
170
|
-
continue;
|
|
171
|
-
}
|
|
172
|
-
const id = decl.attributes.id;
|
|
173
|
-
if (!id) {
|
|
174
|
-
continue;
|
|
175
|
-
}
|
|
176
|
-
const elementId = decl.attributes.elementId?.trim() ?? "";
|
|
177
|
-
const callerCanSupply = declKind === "input" && !!decl.attributes.name?.trim() && startEventIds.has(elementId);
|
|
178
|
-
declaredById.set(id, {
|
|
179
|
-
hasValueSource: !!decl.attributes.default?.trim() || callerCanSupply,
|
|
180
|
-
declKind,
|
|
181
|
-
element: describeElement(decl)
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
if (declaredById.size === 0) {
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
const assigned = new Set;
|
|
189
|
-
for (const output of findDescendants(process, (node) => hasLocalName(node, "output"))) {
|
|
190
|
-
const target = output.attributes.var;
|
|
191
|
-
const isDeclaration = output.parent !== undefined && hasLocalName(output.parent, "variables");
|
|
192
|
-
if (target && !isDeclaration) {
|
|
193
|
-
assigned.add(target);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
for (const id of collectVariableReads(process)) {
|
|
197
|
-
const declared = declaredById.get(id);
|
|
198
|
-
if (!declared || declared.hasValueSource || assigned.has(id)) {
|
|
199
|
-
continue;
|
|
200
|
-
}
|
|
201
|
-
diagnostics.push({
|
|
202
|
-
file,
|
|
203
|
-
element: declared.element,
|
|
204
|
-
message: `Variable "vars.${id}" is read but never assigned; it will be empty at run time.`,
|
|
205
|
-
instruction: declared.declKind === "input" ? "Give the variable a default, assign it through a uipath:output mapping, or bind it to an entry-point start event by giving it both a name and an elementId." : "Give the variable a default, or assign it through a uipath:output mapping before it is read.",
|
|
206
|
-
severity: "warning"
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
validateCallActivityReferences(file, definitions, diagnostics) {
|
|
211
|
-
for (const callActivity of findDescendants(definitions, (node) => hasLocalName(node, "callActivity"))) {
|
|
212
|
-
const hasUiPathPayload = findDescendants(callActivity, (node) => CALL_ACTIVITY_PAYLOAD_TAGS.has(node.name)).length > 0;
|
|
213
|
-
const calledElement = callActivity.attributes.calledElement?.trim() ?? "";
|
|
214
|
-
if (!hasUiPathPayload) {
|
|
215
|
-
diagnostics.push({
|
|
216
|
-
file,
|
|
217
|
-
element: describeElement(callActivity),
|
|
218
|
-
message: "Call activity has no uipath:activity payload, so it invokes nothing at run time.",
|
|
219
|
-
instruction: `Add a payload naming one of the call-activity types (${CALL_ACTIVITY_TYPES.join(", ")}), with a process resource binding.`
|
|
220
|
-
});
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
if (calledElement) {
|
|
224
|
-
diagnostics.push({
|
|
225
|
-
file,
|
|
226
|
-
element: describeElement(callActivity),
|
|
227
|
-
message: `Call activity attribute calledElement="${calledElement}" is ignored; the invoked process comes from the uipath:activity payload and its process binding.`,
|
|
228
|
-
instruction: "Remove calledElement, or ensure the uipath:activity payload names the process you intend to invoke.",
|
|
229
|
-
severity: "warning"
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
validateRootEntryPointIds(file, process, diagnostics) {
|
|
235
|
-
const rootStartEvents = findChildren(process, (node) => hasLocalName(node, "startEvent"));
|
|
236
|
-
for (const startEvent of rootStartEvents) {
|
|
237
|
-
const entryPointIds = findDescendants(startEvent, (node) => node.name === "uipath:entryPointId");
|
|
238
|
-
for (const entryPointId of entryPointIds) {
|
|
239
|
-
const value = entryPointId.attributes.value ?? "";
|
|
240
|
-
if (isGuid(value)) {
|
|
241
|
-
continue;
|
|
242
|
-
}
|
|
243
|
-
diagnostics.push({
|
|
244
|
-
file,
|
|
245
|
-
element: describeElement(startEvent),
|
|
246
|
-
message: `Root start event uipath:entryPointId value "${value}" must be a GUID.`,
|
|
247
|
-
instruction: "Set uipath:entryPointId value to a GUID and use the same value for the matching entry-points.json uniqueId."
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
validateUiPathTags(file, root, bindings, diagnostics) {
|
|
253
|
-
const uipathNodes = findDescendants(root, (node) => node.name.startsWith("uipath:"));
|
|
254
|
-
for (const node of uipathNodes) {
|
|
255
|
-
if (!KNOWN_UIPATH_TAGS.has(node.name)) {
|
|
256
|
-
diagnostics.push({
|
|
257
|
-
file,
|
|
258
|
-
element: describeElement(node),
|
|
259
|
-
message: `Unsupported UiPath extension tag "${node.name}".`,
|
|
260
|
-
instruction: `Use a supported UiPath extension tag: ${SUPPORTED_UIPATH_EXTENSION_TAGS.join(", ")}.`,
|
|
261
|
-
severity: "warning"
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
for (const loopCharacteristics of uipathNodes.filter((node) => node.name === "uipath:loopCharacteristics")) {
|
|
266
|
-
const extensionElements = loopCharacteristics.parent;
|
|
267
|
-
const owner = extensionElements && hasLocalName(extensionElements, "extensionElements") ? extensionElements.parent : undefined;
|
|
268
|
-
if (owner && hasLocalName(owner, "multiInstanceLoopCharacteristics")) {
|
|
269
|
-
continue;
|
|
270
|
-
}
|
|
271
|
-
diagnostics.push({
|
|
272
|
-
file,
|
|
273
|
-
element: describeElement(loopCharacteristics),
|
|
274
|
-
message: "uipath:loopCharacteristics must be nested under bpmn:multiInstanceLoopCharacteristics.",
|
|
275
|
-
instruction: "Move it into bpmn:multiInstanceLoopCharacteristics/bpmn:extensionElements and remove any copy from the activity's extensionElements."
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
const payloads = uipathNodes.filter((node) => ["uipath:activity", "uipath:event", "uipath:mapping"].includes(node.name));
|
|
279
|
-
for (const payload of payloads) {
|
|
280
|
-
this.validatePayload(file, payload, bindings, getOwningProcessActivityConfigurationVersion(payload), diagnostics);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
validatePayload(file, payload, bindings, workflowActivityConfigurationVersion, diagnostics) {
|
|
284
|
-
const owner = findBpmnOwner(payload);
|
|
285
|
-
if (!owner) {
|
|
286
|
-
diagnostics.push({
|
|
287
|
-
file,
|
|
288
|
-
element: describeElement(payload),
|
|
289
|
-
message: `${payload.name} must be inside bpmn:extensionElements on a BPMN element.`,
|
|
290
|
-
instruction: "Move the UiPath extension payload under the owning BPMN element's extensionElements."
|
|
291
|
-
});
|
|
292
|
-
return;
|
|
293
|
-
}
|
|
294
|
-
const typeNodes = childElements(payload, "uipath:type");
|
|
295
|
-
if (payload.attributes.type && typeNodes.length > 0) {
|
|
296
|
-
diagnostics.push({
|
|
297
|
-
file,
|
|
298
|
-
element: describeElement(owner),
|
|
299
|
-
message: `${payload.name} must not mix a type attribute with legacy uipath:type child elements.`,
|
|
300
|
-
instruction: "Use either the payload type attribute or one uipath:type child, not both."
|
|
301
|
-
});
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
if (!payload.attributes.type && typeNodes.length === 0 && !isImplicitVariablesMapping(payload, owner)) {
|
|
305
|
-
diagnostics.push({
|
|
306
|
-
file,
|
|
307
|
-
element: describeElement(owner),
|
|
308
|
-
message: `${payload.name} must contain exactly one uipath:type child or a type attribute.`,
|
|
309
|
-
instruction: "Add one uipath:type element with a supported value attribute, or set the payload type attribute."
|
|
310
|
-
});
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
if (!payload.attributes.type && typeNodes.length > 1) {
|
|
314
|
-
diagnostics.push({
|
|
315
|
-
file,
|
|
316
|
-
element: describeElement(owner),
|
|
317
|
-
message: `${payload.name} must contain exactly one uipath:type child or a type attribute.`,
|
|
318
|
-
instruction: "Add one uipath:type element with a supported value attribute, or set the payload type attribute."
|
|
319
|
-
});
|
|
320
|
-
return;
|
|
321
|
-
}
|
|
322
|
-
const typeNode = typeNodes[0];
|
|
323
|
-
const extensionType = payload.attributes.type ?? typeNode?.attributes.value ?? inferImplicitExtensionType(payload, owner);
|
|
324
|
-
if (!extensionType) {
|
|
325
|
-
diagnostics.push({
|
|
326
|
-
file,
|
|
327
|
-
element: describeElement(typeNode ?? payload),
|
|
328
|
-
message: "UiPath extension type is missing.",
|
|
329
|
-
instruction: "Set uipath:activity type or uipath:type value to a supported UiPath BPMN extension type."
|
|
330
|
-
});
|
|
331
|
-
return;
|
|
332
|
-
}
|
|
333
|
-
const contract = BPMN_SPEC.extensionTypes[extensionType];
|
|
334
|
-
if (!contract) {
|
|
335
|
-
const severity = this.options.unsupportedExtensionTypeSeverity ?? "warning";
|
|
336
|
-
diagnostics.push({
|
|
337
|
-
file,
|
|
338
|
-
element: describeElement(typeNode ?? payload),
|
|
339
|
-
message: `Unsupported UiPath extension type "${extensionType}".`,
|
|
340
|
-
instruction: severity === "error" ? `"${extensionType}" is not in this CLI's BPMN contract, so Orchestrator will reject the package at deploy (Error 1654). Use a supported extension type, remove the node, or upgrade the CLI to a version whose contract includes it.` : "Use an extension type present in the UiPath BPMN contract, or update the contract before packaging this project.",
|
|
341
|
-
severity
|
|
342
|
-
});
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
this.validateContractPlacement(file, payload, owner, contract, diagnostics);
|
|
346
|
-
const isActivityExecutionV1 = extensionType === "Intsvc.ActivityExecution" && isIntsvcActivityExecutionV1(getContextInputs(payload).find((input) => input.attributes.name === ACTIVITY_CONFIGURATION_VERSION)?.attributes.value, workflowActivityConfigurationVersion);
|
|
347
|
-
this.validateContext(file, payload, owner, contract, isActivityExecutionV1, diagnostics);
|
|
348
|
-
if (isActivityExecutionV1) {
|
|
349
|
-
this.validateActivityExecutionV1Bindings(file, payload, owner, bindings, diagnostics);
|
|
350
|
-
}
|
|
351
|
-
if (contract.extensionType === "Orchestrator.StartAgentJob") {
|
|
352
|
-
this.validateStartAgentJobBindings(file, payload, owner, bindings, diagnostics);
|
|
353
|
-
}
|
|
354
|
-
if (contract.extensionType === "Intsvc.EventTrigger" || contract.extensionType === "Intsvc.WaitForEvent") {
|
|
355
|
-
this.validateTriggerConnectionBinding(file, payload, owner, bindings, contract.extensionType === "Intsvc.WaitForEvent" ? "wait" : "trigger", diagnostics);
|
|
356
|
-
}
|
|
357
|
-
this.validateInputs(file, payload, owner, contract, diagnostics);
|
|
358
|
-
this.validateOutput(file, payload, owner, contract, diagnostics);
|
|
359
|
-
this.validateBindingReferences(file, payload, bindings, diagnostics);
|
|
360
|
-
}
|
|
361
|
-
validateContractPlacement(file, payload, owner, contract, diagnostics) {
|
|
362
|
-
if (payload.name !== contract.extensionTag) {
|
|
363
|
-
diagnostics.push({
|
|
364
|
-
file,
|
|
365
|
-
element: describeElement(payload),
|
|
366
|
-
message: `${contract.extensionType} must use ${contract.extensionTag}, not ${payload.name}.`,
|
|
367
|
-
instruction: `Change the extension tag to ${contract.extensionTag}.`
|
|
368
|
-
});
|
|
369
|
-
}
|
|
370
|
-
const allowedOwners = getAllowedOwnerTypes(contract);
|
|
371
|
-
if (!allowedOwners.some((allowedOwner) => getLocalName(allowedOwner) === getLocalName(owner.name) && resolveNamespace(owner) === BPMN_MODEL_NAMESPACE)) {
|
|
372
|
-
diagnostics.push({
|
|
373
|
-
file,
|
|
374
|
-
element: describeElement(owner),
|
|
375
|
-
message: `${contract.extensionType} cannot be attached to ${owner.name}.`,
|
|
376
|
-
instruction: `Attach it to one of: ${allowedOwners.join(", ")}.`
|
|
377
|
-
});
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
validateContext(file, payload, owner, contract, isActivityExecutionV1, diagnostics) {
|
|
381
|
-
const contextInputs = getContextInputs(payload);
|
|
382
|
-
const actual = new Map(contextInputs.map((input) => [input.attributes.name, input]));
|
|
383
|
-
const fields = contract.contextFields ?? [];
|
|
384
|
-
const allowDynamicPayloadShape = usesCanvasOwnedDynamicPayload(contract);
|
|
385
|
-
const allowed = new Set(fields.map((field) => field.name));
|
|
386
|
-
const requireDeclaredFields = !allowDynamicPayloadShape || isActivityExecutionV1;
|
|
387
|
-
const contractLabel = isActivityExecutionV1 ? "Integration Service activity" : contract.extensionType;
|
|
388
|
-
for (const field of fields) {
|
|
389
|
-
const input = actual.get(field.name);
|
|
390
|
-
if (requireDeclaredFields && field.required && !hasAttributeValue(input, "value")) {
|
|
391
|
-
diagnostics.push({
|
|
392
|
-
file,
|
|
393
|
-
element: describeElement(owner),
|
|
394
|
-
message: `${contractLabel} is missing required context input "${field.name}".`,
|
|
395
|
-
instruction: `Add <uipath:input name="${field.name}" value="..." /> inside uipath:context.`
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
if (input) {
|
|
399
|
-
if (isActivityExecutionV1 && field.type === "string" && input.attributes.type !== "string") {
|
|
400
|
-
diagnostics.push({
|
|
401
|
-
file,
|
|
402
|
-
element: describeElement(input),
|
|
403
|
-
message: `${contractLabel} context input "${field.name}" must use type="string".`,
|
|
404
|
-
instruction: 'Add type="string" to this context input.'
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
validateTypedValue(file, input, field.type, input.attributes.value, `context input "${field.name}"`, diagnostics);
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
for (const input of contextInputs) {
|
|
411
|
-
const name = input.attributes.name;
|
|
412
|
-
if (!name) {
|
|
413
|
-
diagnostics.push({
|
|
414
|
-
file,
|
|
415
|
-
element: describeElement(input),
|
|
416
|
-
message: "uipath:context input is missing a name attribute.",
|
|
417
|
-
instruction: "Set name to a context field from the UiPath BPMN contract."
|
|
418
|
-
});
|
|
419
|
-
} else if (!allowDynamicPayloadShape && allowed.size > 0 && !allowed.has(name)) {
|
|
420
|
-
diagnostics.push({
|
|
421
|
-
file,
|
|
422
|
-
element: describeElement(input),
|
|
423
|
-
message: `${contract.extensionType} does not support context input "${name}".`,
|
|
424
|
-
instruction: `Use one of: ${[...allowed].join(", ")}.`
|
|
425
|
-
});
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
validateActivityExecutionV1Bindings(file, payload, owner, bindings, diagnostics) {
|
|
430
|
-
const resourceKeys = this.validateConnectionBindings(file, payload, owner, bindings, diagnostics, "activity", [
|
|
431
|
-
{
|
|
432
|
-
contextName: "connection",
|
|
433
|
-
propertyAttribute: "ConnectionId"
|
|
434
|
-
},
|
|
435
|
-
{ contextName: "folderKey", propertyAttribute: "folderKey" }
|
|
436
|
-
]);
|
|
437
|
-
const connectionResourceKey = resourceKeys.get("connection");
|
|
438
|
-
const folderResourceKey = resourceKeys.get("folderKey");
|
|
439
|
-
if (connectionResourceKey && folderResourceKey && connectionResourceKey !== folderResourceKey) {
|
|
440
|
-
diagnostics.push({
|
|
441
|
-
file,
|
|
442
|
-
element: describeElement(owner),
|
|
443
|
-
message: "Integration Service activity connection and folder bindings refer to different connection resources.",
|
|
444
|
-
instruction: "Set the same resourceKey on both root bindings."
|
|
445
|
-
});
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
validateTriggerConnectionBinding(file, payload, owner, bindings, nodeLabel, diagnostics) {
|
|
449
|
-
this.validateConnectionBindings(file, payload, owner, bindings, diagnostics, nodeLabel, [
|
|
450
|
-
{
|
|
451
|
-
contextName: "connection",
|
|
452
|
-
propertyAttribute: "ConnectionId"
|
|
453
|
-
},
|
|
454
|
-
{
|
|
455
|
-
contextName: "connectionId",
|
|
456
|
-
propertyAttribute: "ConnectionId"
|
|
457
|
-
}
|
|
458
|
-
]);
|
|
459
|
-
}
|
|
460
|
-
validateConnectionBindings(file, payload, owner, bindings, diagnostics, nodeLabel, bindingFields) {
|
|
461
|
-
const inputByName = new Map(getConnectorInputs(payload).map((input) => [
|
|
462
|
-
input.attributes.name,
|
|
463
|
-
input
|
|
464
|
-
]));
|
|
465
|
-
const resourceKeys = new Map;
|
|
466
|
-
for (const field of bindingFields) {
|
|
467
|
-
const contextInput = inputByName.get(field.contextName);
|
|
468
|
-
if (!contextInput) {
|
|
469
|
-
continue;
|
|
470
|
-
}
|
|
471
|
-
const value = contextInput.attributes.value?.trim() ?? "";
|
|
472
|
-
if (!value) {
|
|
473
|
-
continue;
|
|
474
|
-
}
|
|
475
|
-
if (!value.startsWith("=bindings.")) {
|
|
476
|
-
diagnostics.push({
|
|
477
|
-
file,
|
|
478
|
-
element: describeElement(owner),
|
|
479
|
-
message: `Integration Service ${nodeLabel} context input "${field.contextName}" must reference a root binding.`,
|
|
480
|
-
instruction: `Set ${field.contextName} to =bindings.<bindingId> and declare that binding with resource="Connection" and propertyAttribute="${field.propertyAttribute}".`
|
|
481
|
-
});
|
|
482
|
-
continue;
|
|
483
|
-
}
|
|
484
|
-
const bindingId = value.slice("=bindings.".length);
|
|
485
|
-
const binding = bindings.get(bindingId);
|
|
486
|
-
if (!binding) {
|
|
487
|
-
continue;
|
|
488
|
-
}
|
|
489
|
-
const hasInvalidBindingShape = binding.resource !== "Connection" || binding.propertyAttribute !== field.propertyAttribute;
|
|
490
|
-
if (hasInvalidBindingShape) {
|
|
491
|
-
diagnostics.push({
|
|
492
|
-
file,
|
|
493
|
-
element: describeElement(owner),
|
|
494
|
-
message: `Integration Service ${nodeLabel} ${field.contextName} binding "${bindingId}" must use resource="Connection" and propertyAttribute="${field.propertyAttribute}".`,
|
|
495
|
-
instruction: `Set resource="Connection" and propertyAttribute="${field.propertyAttribute}" on that binding.`
|
|
496
|
-
});
|
|
497
|
-
}
|
|
498
|
-
const resourceKey = binding.resourceKey?.trim();
|
|
499
|
-
if (!resourceKey) {
|
|
500
|
-
diagnostics.push({
|
|
501
|
-
file,
|
|
502
|
-
element: describeElement(owner),
|
|
503
|
-
message: `Integration Service ${nodeLabel} ${field.contextName} binding "${bindingId}" is missing resourceKey.`,
|
|
504
|
-
instruction: "Set resourceKey on that root binding."
|
|
505
|
-
});
|
|
506
|
-
} else if (!hasInvalidBindingShape) {
|
|
507
|
-
resourceKeys.set(field.contextName, resourceKey);
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
return resourceKeys;
|
|
511
|
-
}
|
|
512
|
-
validateInputs(file, payload, owner, contract, diagnostics) {
|
|
513
|
-
const directInputs = childElements(payload, "uipath:input");
|
|
514
|
-
const actual = new Map(directInputs.map((input) => [input.attributes.name, input]));
|
|
515
|
-
if (usesCanvasOwnedDynamicPayload(contract)) {
|
|
516
|
-
this.validateDynamicDirectInputs(file, directInputs, diagnostics);
|
|
517
|
-
return;
|
|
518
|
-
}
|
|
519
|
-
if (contract.inputPattern === "none") {
|
|
520
|
-
if (contract.extensionType === "BPMN.Variables") {
|
|
521
|
-
this.validateVariableMappingInputs(file, directInputs, diagnostics);
|
|
522
|
-
return;
|
|
523
|
-
}
|
|
524
|
-
for (const input of directInputs) {
|
|
525
|
-
diagnostics.push({
|
|
526
|
-
file,
|
|
527
|
-
element: describeElement(input),
|
|
528
|
-
message: `${contract.extensionType} does not support direct input payload "${input.attributes.name ?? ""}".`,
|
|
529
|
-
instruction: "Remove the direct uipath:input payload."
|
|
530
|
-
});
|
|
531
|
-
}
|
|
532
|
-
return;
|
|
533
|
-
}
|
|
534
|
-
if (["mergedBody", "splitInputsItemData", "scriptArgs"].includes(contract.inputPattern)) {
|
|
535
|
-
const expectedName = getExpectedInputName(contract);
|
|
536
|
-
const input = actual.get(expectedName);
|
|
537
|
-
if (!input) {
|
|
538
|
-
diagnostics.push({
|
|
539
|
-
file,
|
|
540
|
-
element: describeElement(owner),
|
|
541
|
-
message: `${contract.extensionType} is missing required input payload "${expectedName}".`,
|
|
542
|
-
instruction: `Add <uipath:input name="${expectedName}" type="json" target="${getExpectedInputTarget(contract)}">...</uipath:input>.`
|
|
543
|
-
});
|
|
544
|
-
return;
|
|
545
|
-
}
|
|
546
|
-
validateInputShape(file, input, expectedName, contract, diagnostics);
|
|
547
|
-
for (const extraInput of directInputs) {
|
|
548
|
-
if (extraInput.attributes.name !== expectedName) {
|
|
549
|
-
diagnostics.push({
|
|
550
|
-
file,
|
|
551
|
-
element: describeElement(extraInput),
|
|
552
|
-
message: `${contract.extensionType} does not support input payload "${extraInput.attributes.name ?? ""}".`,
|
|
553
|
-
instruction: `Use only the "${expectedName}" input payload for ${contract.inputPattern}.`
|
|
554
|
-
});
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
return;
|
|
558
|
-
}
|
|
559
|
-
const fields = contract.inputFields ?? [];
|
|
560
|
-
const allowed = new Set(fields.map((field) => field.name));
|
|
561
|
-
for (const field of fields) {
|
|
562
|
-
const input = actual.get(field.name);
|
|
563
|
-
if (field.required && !input) {
|
|
564
|
-
diagnostics.push({
|
|
565
|
-
file,
|
|
566
|
-
element: describeElement(owner),
|
|
567
|
-
message: `${contract.extensionType} is missing required input payload "${field.name}".`,
|
|
568
|
-
instruction: `Add a direct uipath:input named "${field.name}".`
|
|
569
|
-
});
|
|
570
|
-
}
|
|
571
|
-
if (input) {
|
|
572
|
-
validateTypedValue(file, input, field.type, input.text || input.attributes.value, `input payload "${field.name}"`, diagnostics);
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
for (const input of directInputs) {
|
|
576
|
-
const name = input.attributes.name;
|
|
577
|
-
if (!name) {
|
|
578
|
-
diagnostics.push({
|
|
579
|
-
file,
|
|
580
|
-
element: describeElement(input),
|
|
581
|
-
message: "uipath:input is missing a name attribute.",
|
|
582
|
-
instruction: "Set name to an input field from the UiPath BPMN contract."
|
|
583
|
-
});
|
|
584
|
-
} else if (allowed.size > 0 && !allowed.has(name)) {
|
|
585
|
-
diagnostics.push({
|
|
586
|
-
file,
|
|
587
|
-
element: describeElement(input),
|
|
588
|
-
message: `${contract.extensionType} does not support input payload "${name}".`,
|
|
589
|
-
instruction: `Use one of: ${[...allowed].join(", ")}.`
|
|
590
|
-
});
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
validateDynamicDirectInputs(file, directInputs, diagnostics) {
|
|
595
|
-
for (const input of directInputs) {
|
|
596
|
-
if (!hasAttributeValue(input, "name")) {
|
|
597
|
-
diagnostics.push({
|
|
598
|
-
file,
|
|
599
|
-
element: describeElement(input),
|
|
600
|
-
message: "uipath:input is missing a name attribute.",
|
|
601
|
-
instruction: "Set name to the Integration Service payload field."
|
|
602
|
-
});
|
|
603
|
-
}
|
|
604
|
-
if (input.attributes.type === "json") {
|
|
605
|
-
validateJsonPayload(file, input, `input payload "${input.attributes.name ?? ""}"`, diagnostics);
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
validateVariableMappingInputs(file, directInputs, diagnostics) {
|
|
610
|
-
for (const input of directInputs) {
|
|
611
|
-
if (!hasAttributeValue(input, "name")) {
|
|
612
|
-
diagnostics.push({
|
|
613
|
-
file,
|
|
614
|
-
element: describeElement(input),
|
|
615
|
-
message: "BPMN.Variables mapping input is missing its name attribute.",
|
|
616
|
-
instruction: "Set name to the process variable being mapped."
|
|
617
|
-
});
|
|
618
|
-
}
|
|
619
|
-
if (input.attributes.type === "json") {
|
|
620
|
-
validateJsonPayload(file, input, `mapping input "${input.attributes.name ?? ""}"`, diagnostics);
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
validateOutput(file, payload, owner, contract, diagnostics) {
|
|
625
|
-
if (usesCanvasOwnedDynamicPayload(contract)) {
|
|
626
|
-
for (const output of childElements(payload, "uipath:output")) {
|
|
627
|
-
if (!hasAttributeValue(output, "var")) {
|
|
628
|
-
diagnostics.push({
|
|
629
|
-
file,
|
|
630
|
-
element: describeElement(output),
|
|
631
|
-
message: `${contract.extensionType} output is missing its var attribute.`,
|
|
632
|
-
instruction: "Set var to the variable that receives the output."
|
|
633
|
-
});
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
return;
|
|
637
|
-
}
|
|
638
|
-
if (!contract.outputName && !contract.outputType) {
|
|
639
|
-
return;
|
|
640
|
-
}
|
|
641
|
-
const outputs = childElements(payload, "uipath:output");
|
|
642
|
-
if (outputs.length === 0) {
|
|
643
|
-
diagnostics.push({
|
|
644
|
-
file,
|
|
645
|
-
element: describeElement(owner),
|
|
646
|
-
message: `${contract.extensionType} is missing an output payload.`,
|
|
647
|
-
instruction: "Add a uipath:output matching the UiPath BPMN contract."
|
|
648
|
-
});
|
|
649
|
-
return;
|
|
650
|
-
}
|
|
651
|
-
for (const output of outputs) {
|
|
652
|
-
if (contract.outputName && output.attributes.name !== contract.outputName) {
|
|
653
|
-
diagnostics.push({
|
|
654
|
-
file,
|
|
655
|
-
element: describeElement(output),
|
|
656
|
-
message: `${contract.extensionType} output name must be "${contract.outputName}".`,
|
|
657
|
-
instruction: `Set the uipath:output name attribute to "${contract.outputName}".`
|
|
658
|
-
});
|
|
659
|
-
}
|
|
660
|
-
if (contract.outputType && output.attributes.type !== contract.outputType) {
|
|
661
|
-
diagnostics.push({
|
|
662
|
-
file,
|
|
663
|
-
element: describeElement(output),
|
|
664
|
-
message: `${contract.extensionType} output type must be "${contract.outputType}".`,
|
|
665
|
-
instruction: `Set the uipath:output type attribute to "${contract.outputType}".`
|
|
666
|
-
});
|
|
667
|
-
}
|
|
668
|
-
if (contract.outputSource && output.attributes.source !== contract.outputSource) {
|
|
669
|
-
diagnostics.push({
|
|
670
|
-
file,
|
|
671
|
-
element: describeElement(output),
|
|
672
|
-
message: `${contract.extensionType} output source must be "${contract.outputSource}".`,
|
|
673
|
-
instruction: `Set the uipath:output source attribute to "${contract.outputSource}".`
|
|
674
|
-
});
|
|
675
|
-
}
|
|
676
|
-
if (!hasAttributeValue(output, "var")) {
|
|
677
|
-
diagnostics.push({
|
|
678
|
-
file,
|
|
679
|
-
element: describeElement(output),
|
|
680
|
-
message: `${contract.extensionType} output is missing its var attribute.`,
|
|
681
|
-
instruction: "Set var to the variable that receives the output."
|
|
682
|
-
});
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
validateBindingReferences(file, payload, bindings, diagnostics) {
|
|
687
|
-
const bindingReferences = findDescendants(payload, (node) => {
|
|
688
|
-
const value = node.attributes.value;
|
|
689
|
-
return value?.startsWith("=bindings.") === true;
|
|
690
|
-
});
|
|
691
|
-
for (const node of bindingReferences) {
|
|
692
|
-
const bindingId = node.attributes.value.slice("=bindings.".length);
|
|
693
|
-
if (!bindingId) {
|
|
694
|
-
diagnostics.push({
|
|
695
|
-
file,
|
|
696
|
-
element: describeElement(node),
|
|
697
|
-
message: "Binding reference is missing a binding id.",
|
|
698
|
-
instruction: 'Use a binding expression such as value="=bindings.<bindingId>".'
|
|
699
|
-
});
|
|
700
|
-
continue;
|
|
701
|
-
}
|
|
702
|
-
if (!bindings.has(bindingId)) {
|
|
703
|
-
diagnostics.push({
|
|
704
|
-
file,
|
|
705
|
-
element: describeElement(node),
|
|
706
|
-
message: `Binding reference "${bindingId}" was not found in project bindings.`,
|
|
707
|
-
instruction: "Add the binding to BPMN uipath:bindings or bindings_v2.json, or update the BPMN binding reference."
|
|
708
|
-
});
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
validateStartAgentJobBindings(file, payload, owner, bindings, diagnostics) {
|
|
713
|
-
const contexts = childElements(payload, "uipath:context");
|
|
714
|
-
const contextInputs = contexts.flatMap((context) => childElements(context, "uipath:input"));
|
|
715
|
-
const inputs = new Map(contextInputs.map((input) => [input.attributes.name, input]));
|
|
716
|
-
if (inputs.has("releaseKey") || inputs.has("folderId")) {
|
|
717
|
-
diagnostics.push({
|
|
718
|
-
file,
|
|
719
|
-
element: describeElement(owner),
|
|
720
|
-
message: "Orchestrator.StartAgentJob must use binding-backed name and folderPath context inputs, not releaseKey or folderId.",
|
|
721
|
-
instruction: 'Replace releaseKey/folderId with value="=bindings.<agentNameBindingId>" and value="=bindings.<agentFolderPathBindingId>".'
|
|
722
|
-
});
|
|
723
|
-
}
|
|
724
|
-
for (const expected of ["name", "folderPath"]) {
|
|
725
|
-
const input = inputs.get(expected);
|
|
726
|
-
const value = input?.attributes.value;
|
|
727
|
-
if (!value?.startsWith("=bindings.")) {
|
|
728
|
-
diagnostics.push({
|
|
729
|
-
file,
|
|
730
|
-
element: describeElement(input ?? owner),
|
|
731
|
-
message: `Orchestrator.StartAgentJob context input "${expected}" must reference project bindings.`,
|
|
732
|
-
instruction: `Set "${expected}" to a binding expression such as value="=bindings.<${expected}BindingId>".`
|
|
733
|
-
});
|
|
734
|
-
continue;
|
|
735
|
-
}
|
|
736
|
-
const bindingId = value.slice("=bindings.".length);
|
|
737
|
-
const binding = bindings.get(bindingId);
|
|
738
|
-
if (!binding) {
|
|
739
|
-
diagnostics.push({
|
|
740
|
-
file,
|
|
741
|
-
element: describeElement(input ?? owner),
|
|
742
|
-
message: `Orchestrator.StartAgentJob binding reference "${bindingId}" was not found in project bindings.`,
|
|
743
|
-
instruction: "Add agent name and folderPath bindings to BPMN uipath:bindings or bindings_v2.json before packaging this BPMN project."
|
|
744
|
-
});
|
|
745
|
-
continue;
|
|
746
|
-
}
|
|
747
|
-
if (binding.resource !== "process" || binding.resourceSubType !== "Agent" || binding.propertyAttribute !== expected) {
|
|
748
|
-
diagnostics.push({
|
|
749
|
-
file,
|
|
750
|
-
element: describeElement(input ?? owner),
|
|
751
|
-
message: `Orchestrator.StartAgentJob binding "${bindingId}" must target Agent ${expected}.`,
|
|
752
|
-
instruction: `Use a process binding with resourceSubType "Agent" and propertyAttribute "${expected}".`
|
|
753
|
-
});
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
async loadBindings(projectPath, diagnostics) {
|
|
758
|
-
const bindingsPath = this.fileSystem.joinPath(projectPath, "bindings_v2.json");
|
|
759
|
-
if (!await this.fileSystem.exists(bindingsPath)) {
|
|
760
|
-
return new Map;
|
|
761
|
-
}
|
|
762
|
-
const content = await this.fileSystem.readFile(bindingsPath);
|
|
763
|
-
if (content === null || content.byteLength === 0) {
|
|
764
|
-
diagnostics.push({
|
|
765
|
-
file: "bindings_v2.json",
|
|
766
|
-
message: "bindings_v2.json could not be read or is empty.",
|
|
767
|
-
instruction: "Write a valid bindings_v2.json file or remove malformed binding references from BPMN."
|
|
768
|
-
});
|
|
769
|
-
return new Map;
|
|
770
|
-
}
|
|
771
|
-
try {
|
|
772
|
-
const parsed = JSON.parse(decodeFileContent(content));
|
|
773
|
-
return collectBindings(parsed);
|
|
774
|
-
} catch (error) {
|
|
775
|
-
diagnostics.push({
|
|
776
|
-
file: "bindings_v2.json",
|
|
777
|
-
message: `bindings_v2.json is not valid JSON: ${String(error)}`,
|
|
778
|
-
instruction: "Fix bindings_v2.json so it contains valid JSON."
|
|
779
|
-
});
|
|
780
|
-
return new Map;
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
async runFileValidators(context, diagnostics) {
|
|
784
|
-
for (const validator of this.options.fileValidators ?? []) {
|
|
785
|
-
diagnostics.push(...await validator.validate(context));
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
async runProjectValidators(context, diagnostics) {
|
|
789
|
-
for (const validator of this.options.projectValidators ?? []) {
|
|
790
|
-
diagnostics.push(...await validator.validate(context));
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
createFailure(diagnostics) {
|
|
794
|
-
const message = diagnostics.map(formatDiagnostic).slice(0, 25).join(`
|
|
795
|
-
`);
|
|
796
|
-
const suffix = diagnostics.length > 25 ? `
|
|
797
|
-
...and ${diagnostics.length - 25} more BPMN validation error(s).` : "";
|
|
798
|
-
return {
|
|
799
|
-
isValid: false,
|
|
800
|
-
errorCode: SEMANTIC_VALIDATION_ERROR,
|
|
801
|
-
diagnostics,
|
|
802
|
-
message: `${message}${suffix}`
|
|
803
|
-
};
|
|
804
|
-
}
|
|
805
|
-
createWarningMessage(diagnostics) {
|
|
806
|
-
const message = diagnostics.map(formatDiagnostic).slice(0, 25).join(`
|
|
807
|
-
`);
|
|
808
|
-
const suffix = diagnostics.length > 25 ? `
|
|
809
|
-
...and ${diagnostics.length - 25} more BPMN validation warning(s).` : "";
|
|
810
|
-
return `${message}${suffix}`;
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
function decodeFileContent(content) {
|
|
814
|
-
return new TextDecoder().decode(content);
|
|
815
|
-
}
|
|
816
|
-
function parseXml(xml) {
|
|
817
|
-
const root = {
|
|
818
|
-
name: "#document",
|
|
819
|
-
attributes: {},
|
|
820
|
-
children: [],
|
|
821
|
-
text: ""
|
|
822
|
-
};
|
|
823
|
-
const stack = [root];
|
|
824
|
-
const diagnostics = [];
|
|
825
|
-
const tokenPattern = /<!\[CDATA\[([\s\S]*?)\]\]>|<!--[\s\S]*?-->|<\?[\s\S]*?\?>|<![^>]*>|<\/?[^>]+>/g;
|
|
826
|
-
let lastIndex = 0;
|
|
827
|
-
let match;
|
|
828
|
-
match = tokenPattern.exec(xml);
|
|
829
|
-
while (match !== null) {
|
|
830
|
-
const text = xml.slice(lastIndex, match.index);
|
|
831
|
-
if (text.trim()) {
|
|
832
|
-
stack[stack.length - 1].text += decodeXmlEntities(text.trim());
|
|
833
|
-
}
|
|
834
|
-
const token = match[0];
|
|
835
|
-
if (token.startsWith("<![CDATA[")) {
|
|
836
|
-
stack[stack.length - 1].text += match[1] ?? "";
|
|
837
|
-
} else if (token.startsWith("</")) {
|
|
838
|
-
const closeName = token.slice(2, -1).trim();
|
|
839
|
-
if (stack.length <= 1) {
|
|
840
|
-
diagnostics.push(`Unexpected closing tag "${closeName}".`);
|
|
841
|
-
} else if (stack[stack.length - 1].name !== closeName) {
|
|
842
|
-
let matchingOpenIndex = -1;
|
|
843
|
-
for (let i = stack.length - 1;i > 0; i -= 1) {
|
|
844
|
-
if (stack[i].name === closeName) {
|
|
845
|
-
matchingOpenIndex = i;
|
|
846
|
-
break;
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
if (matchingOpenIndex <= 0) {
|
|
850
|
-
diagnostics.push(`Unexpected closing tag "${closeName}".`);
|
|
851
|
-
} else {
|
|
852
|
-
diagnostics.push(`Mismatched closing tag "${closeName}".`);
|
|
853
|
-
while (stack.length > matchingOpenIndex) {
|
|
854
|
-
stack.pop();
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
} else {
|
|
858
|
-
stack.pop();
|
|
859
|
-
}
|
|
860
|
-
} else if (!token.startsWith("<!--") && !token.startsWith("<?") && !token.startsWith("<!")) {
|
|
861
|
-
const selfClosing = token.endsWith("/>");
|
|
862
|
-
const node = parseStartTag(token, selfClosing);
|
|
863
|
-
node.parent = stack[stack.length - 1];
|
|
864
|
-
stack[stack.length - 1].children.push(node);
|
|
865
|
-
if (!selfClosing) {
|
|
866
|
-
stack.push(node);
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
lastIndex = tokenPattern.lastIndex;
|
|
870
|
-
match = tokenPattern.exec(xml);
|
|
871
|
-
}
|
|
872
|
-
const trailing = xml.slice(lastIndex).trim();
|
|
873
|
-
if (trailing) {
|
|
874
|
-
stack[stack.length - 1].text += decodeXmlEntities(trailing);
|
|
875
|
-
}
|
|
876
|
-
if (stack.length > 1) {
|
|
877
|
-
diagnostics.push(`Unclosed tag "${stack[stack.length - 1].name}" in BPMN XML.`);
|
|
878
|
-
}
|
|
879
|
-
return {
|
|
880
|
-
root: root.children[0],
|
|
881
|
-
diagnostics
|
|
882
|
-
};
|
|
883
|
-
}
|
|
884
|
-
function parseStartTag(token, selfClosing) {
|
|
885
|
-
const body = token.slice(1, selfClosing ? -2 : -1).trim();
|
|
886
|
-
const nameMatch = /^([^\s/>]+)/.exec(body);
|
|
887
|
-
const name = nameMatch?.[1] ?? "";
|
|
888
|
-
const attributes = {};
|
|
889
|
-
const attributePattern = /([^\s=]+)\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
|
|
890
|
-
let match;
|
|
891
|
-
match = attributePattern.exec(body);
|
|
892
|
-
while (match !== null) {
|
|
893
|
-
attributes[match[1]] = decodeXmlEntities(match[2] ?? match[3] ?? "");
|
|
894
|
-
match = attributePattern.exec(body);
|
|
895
|
-
}
|
|
896
|
-
return {
|
|
897
|
-
name,
|
|
898
|
-
attributes,
|
|
899
|
-
children: [],
|
|
900
|
-
text: ""
|
|
901
|
-
};
|
|
902
|
-
}
|
|
903
|
-
function childElements(node, name) {
|
|
904
|
-
return node.children.filter((child) => child.name === name);
|
|
905
|
-
}
|
|
906
|
-
function findChildren(node, predicate) {
|
|
907
|
-
return node.children.filter(predicate);
|
|
908
|
-
}
|
|
909
|
-
function findDescendants(node, predicate) {
|
|
910
|
-
const matches = [];
|
|
911
|
-
for (const child of node.children) {
|
|
912
|
-
if (predicate(child)) {
|
|
913
|
-
matches.push(child);
|
|
914
|
-
}
|
|
915
|
-
matches.push(...findDescendants(child, predicate));
|
|
916
|
-
}
|
|
917
|
-
return matches;
|
|
918
|
-
}
|
|
919
|
-
function findFirst(node, predicate) {
|
|
920
|
-
if (predicate(node)) {
|
|
921
|
-
return node;
|
|
922
|
-
}
|
|
923
|
-
for (const child of node.children) {
|
|
924
|
-
const match = findFirst(child, predicate);
|
|
925
|
-
if (match) {
|
|
926
|
-
return match;
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
return;
|
|
930
|
-
}
|
|
931
|
-
function isManualStartEvent(startEvent) {
|
|
932
|
-
const hasEventDefinition = startEvent.children.some((child) => getLocalName(child.name).endsWith("eventdefinition"));
|
|
933
|
-
const hasUiPathEvent = findDescendants(startEvent, (node) => hasLocalName(node, "event")).length > 0;
|
|
934
|
-
return !hasEventDefinition && !hasUiPathEvent;
|
|
935
|
-
}
|
|
936
|
-
var VARIABLE_READ = /vars(?:\.([A-Za-z_$][\w$]*)|\[\s*["']([^"']+)["']\s*\])/g;
|
|
937
|
-
var FREE_TEXT_ELEMENTS = new Set(["documentation", "text"]);
|
|
938
|
-
function collectVariableReads(node, into = new Set) {
|
|
939
|
-
for (const value of Object.values(node.attributes)) {
|
|
940
|
-
if (!value.trimStart().startsWith("=")) {
|
|
941
|
-
continue;
|
|
942
|
-
}
|
|
943
|
-
for (const match of value.matchAll(VARIABLE_READ)) {
|
|
944
|
-
const id = match[1] ?? match[2];
|
|
945
|
-
if (id) {
|
|
946
|
-
into.add(id);
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
if (node.text && !FREE_TEXT_ELEMENTS.has(getLocalName(node.name))) {
|
|
951
|
-
for (const match of node.text.matchAll(VARIABLE_READ)) {
|
|
952
|
-
const id = match[1] ?? match[2];
|
|
953
|
-
if (id) {
|
|
954
|
-
into.add(id);
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
for (const child of node.children) {
|
|
959
|
-
collectVariableReads(child, into);
|
|
960
|
-
}
|
|
961
|
-
return into;
|
|
962
|
-
}
|
|
963
|
-
function hasLocalName(node, localName) {
|
|
964
|
-
return getLocalName(node.name) === localName.toLowerCase();
|
|
965
|
-
}
|
|
966
|
-
function getLocalName(name) {
|
|
967
|
-
return name.slice(name.lastIndexOf(":") + 1).toLowerCase();
|
|
968
|
-
}
|
|
969
|
-
function resolveNamespace(node) {
|
|
970
|
-
const separator = node.name.indexOf(":");
|
|
971
|
-
const prefix = separator === -1 ? "" : node.name.slice(0, separator);
|
|
972
|
-
const declaration = prefix ? `xmlns:${prefix}` : "xmlns";
|
|
973
|
-
let current = node;
|
|
974
|
-
while (current) {
|
|
975
|
-
if (Object.hasOwn(current.attributes, declaration)) {
|
|
976
|
-
return current.attributes[declaration];
|
|
977
|
-
}
|
|
978
|
-
current = current.parent;
|
|
979
|
-
}
|
|
980
|
-
return;
|
|
981
|
-
}
|
|
982
|
-
function findBpmnOwner(payload) {
|
|
983
|
-
const extensionElements = payload.parent;
|
|
984
|
-
if (extensionElements && hasLocalName(extensionElements, "extensionElements")) {
|
|
985
|
-
return extensionElements.parent;
|
|
986
|
-
}
|
|
987
|
-
if (payload.parent?.name.startsWith("bpmn:")) {
|
|
988
|
-
return payload.parent;
|
|
989
|
-
}
|
|
990
|
-
return;
|
|
991
|
-
}
|
|
992
|
-
function getContextInputs(payload) {
|
|
993
|
-
return childElements(payload, "uipath:context").flatMap((context) => childElements(context, "uipath:input"));
|
|
994
|
-
}
|
|
995
|
-
function getConnectorInputs(payload) {
|
|
996
|
-
return [
|
|
997
|
-
...getContextInputs(payload),
|
|
998
|
-
...childElements(payload, "uipath:input")
|
|
999
|
-
];
|
|
1000
|
-
}
|
|
1001
|
-
function isIntsvcActivityExecutionV1(nodeVersion, workflowVersion) {
|
|
1002
|
-
const version = nodeVersion ?? workflowVersion;
|
|
1003
|
-
return version === undefined || version === ACTIVITY_CONFIGURATION_V1;
|
|
1004
|
-
}
|
|
1005
|
-
function getOwningProcessActivityConfigurationVersion(payload) {
|
|
1006
|
-
let current = payload.parent;
|
|
1007
|
-
while (current && !hasLocalName(current, "process")) {
|
|
1008
|
-
current = current.parent;
|
|
1009
|
-
}
|
|
1010
|
-
if (!current) {
|
|
1011
|
-
return;
|
|
1012
|
-
}
|
|
1013
|
-
const extensionElements = findChildren(current, (node) => hasLocalName(node, "extensionElements"));
|
|
1014
|
-
return extensionElements.flatMap((node) => childElements(node, "uipath:intsvcActivityConfig")).at(0)?.attributes.version;
|
|
1015
|
-
}
|
|
1016
|
-
function describeElement(node) {
|
|
1017
|
-
const id = node.attributes.id ? `#${node.attributes.id}` : "";
|
|
1018
|
-
const name = node.attributes.name ? ` "${node.attributes.name}"` : "";
|
|
1019
|
-
return `${node.name}${id}${name}`;
|
|
1020
|
-
}
|
|
1021
|
-
function hasAttributeValue(node, attribute) {
|
|
1022
|
-
return Boolean(node?.attributes[attribute]?.trim());
|
|
1023
|
-
}
|
|
1024
|
-
function usesCanvasOwnedDynamicPayload(contract) {
|
|
1025
|
-
return contract.isDynamic === true || contract.extensionType.startsWith("Intsvc.") && contract.requiresDiscovery;
|
|
1026
|
-
}
|
|
1027
|
-
function getAllowedOwnerTypes(contract) {
|
|
1028
|
-
if (contract.extensionType === "BPMN.Variables") {
|
|
1029
|
-
return [
|
|
1030
|
-
"bpmn:StartEvent",
|
|
1031
|
-
"bpmn:EndEvent",
|
|
1032
|
-
"bpmn:Task",
|
|
1033
|
-
"bpmn:ScriptTask",
|
|
1034
|
-
"bpmn:SubProcess"
|
|
1035
|
-
];
|
|
1036
|
-
}
|
|
1037
|
-
const placementTypes = Object.values(contract.placements).map((placement) => placement.type);
|
|
1038
|
-
return placementTypes.length > 0 ? placementTypes : [contract.bpmnElement];
|
|
1039
|
-
}
|
|
1040
|
-
function inferImplicitExtensionType(payload, owner) {
|
|
1041
|
-
return isImplicitVariablesMapping(payload, owner) ? "BPMN.Variables" : undefined;
|
|
1042
|
-
}
|
|
1043
|
-
function isImplicitVariablesMapping(payload, owner) {
|
|
1044
|
-
return payload.name === "uipath:mapping" && !payload.attributes.type && childElements(payload, "uipath:type").length === 0 && hasLocalName(owner, "task");
|
|
1045
|
-
}
|
|
1046
|
-
function getExpectedInputName(contract) {
|
|
1047
|
-
if (contract.inputPattern === "splitInputsItemData") {
|
|
1048
|
-
return "ItemData";
|
|
1049
|
-
}
|
|
1050
|
-
if (contract.inputPattern === "scriptArgs") {
|
|
1051
|
-
return "args";
|
|
1052
|
-
}
|
|
1053
|
-
return contract.inputName ?? "body";
|
|
1054
|
-
}
|
|
1055
|
-
function getExpectedInputTarget(contract) {
|
|
1056
|
-
if (contract.inputPattern === "scriptArgs") {
|
|
1057
|
-
return "bodyField";
|
|
1058
|
-
}
|
|
1059
|
-
return contract.inputTarget ?? "body";
|
|
1060
|
-
}
|
|
1061
|
-
function validateInputShape(file, input, expectedName, contract, diagnostics) {
|
|
1062
|
-
if (input.attributes.type !== "json") {
|
|
1063
|
-
diagnostics.push({
|
|
1064
|
-
file,
|
|
1065
|
-
element: describeElement(input),
|
|
1066
|
-
message: `Input payload "${expectedName}" must declare type="json".`,
|
|
1067
|
-
instruction: 'Set the uipath:input type attribute to "json".'
|
|
1068
|
-
});
|
|
1069
|
-
}
|
|
1070
|
-
const expectedTarget = getExpectedInputTarget(contract);
|
|
1071
|
-
if (input.attributes.target !== expectedTarget) {
|
|
1072
|
-
diagnostics.push({
|
|
1073
|
-
file,
|
|
1074
|
-
element: describeElement(input),
|
|
1075
|
-
message: `Input payload "${expectedName}" must target "${expectedTarget}".`,
|
|
1076
|
-
instruction: `Set the uipath:input target attribute to "${expectedTarget}".`
|
|
1077
|
-
});
|
|
1078
|
-
}
|
|
1079
|
-
validateJsonPayload(file, input, `input payload "${expectedName}"`, diagnostics);
|
|
1080
|
-
}
|
|
1081
|
-
function validateTypedValue(file, node, type, value, label, diagnostics) {
|
|
1082
|
-
if (value === undefined || value === "") {
|
|
1083
|
-
return;
|
|
1084
|
-
}
|
|
1085
|
-
if (value.startsWith("=")) {
|
|
1086
|
-
return;
|
|
1087
|
-
}
|
|
1088
|
-
if (type === "number" && !Number.isFinite(Number(value))) {
|
|
1089
|
-
diagnostics.push({
|
|
1090
|
-
file,
|
|
1091
|
-
element: describeElement(node),
|
|
1092
|
-
message: `${label} must be a number.`,
|
|
1093
|
-
instruction: "Set the value to a numeric literal."
|
|
1094
|
-
});
|
|
1095
|
-
}
|
|
1096
|
-
if (type === "boolean" && !["true", "false"].includes(value)) {
|
|
1097
|
-
diagnostics.push({
|
|
1098
|
-
file,
|
|
1099
|
-
element: describeElement(node),
|
|
1100
|
-
message: `${label} must be a boolean.`,
|
|
1101
|
-
instruction: 'Set the value to "true" or "false".'
|
|
1102
|
-
});
|
|
1103
|
-
}
|
|
1104
|
-
if (["json", "object"].includes(type)) {
|
|
1105
|
-
validateJsonText(file, node, value, label, diagnostics);
|
|
1106
|
-
}
|
|
1107
|
-
}
|
|
1108
|
-
function validateJsonPayload(file, input, label, diagnostics) {
|
|
1109
|
-
const value = input.text.trim();
|
|
1110
|
-
if (!value || value.startsWith("=")) {
|
|
1111
|
-
return;
|
|
1112
|
-
}
|
|
1113
|
-
validateJsonText(file, input, value, label, diagnostics);
|
|
1114
|
-
}
|
|
1115
|
-
function validateJsonText(file, node, value, label, diagnostics) {
|
|
1116
|
-
try {
|
|
1117
|
-
JSON.parse(value);
|
|
1118
|
-
} catch (error) {
|
|
1119
|
-
diagnostics.push({
|
|
1120
|
-
file,
|
|
1121
|
-
element: describeElement(node),
|
|
1122
|
-
message: `${label} contains malformed JSON: ${String(error)}`,
|
|
1123
|
-
instruction: "Replace the payload with valid JSON.",
|
|
1124
|
-
severity: "error"
|
|
1125
|
-
});
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
function collectInlineBindings(root, bindings) {
|
|
1129
|
-
for (const binding of findDescendants(root, (node) => node.name === "uipath:binding")) {
|
|
1130
|
-
const id = binding.attributes.id;
|
|
1131
|
-
if (!id) {
|
|
1132
|
-
continue;
|
|
1133
|
-
}
|
|
1134
|
-
bindings.set(id, {
|
|
1135
|
-
id,
|
|
1136
|
-
resource: binding.attributes.resource,
|
|
1137
|
-
resourceKey: binding.attributes.resourceKey,
|
|
1138
|
-
resourceSubType: binding.attributes.resourceSubType,
|
|
1139
|
-
propertyAttribute: binding.attributes.propertyAttribute
|
|
1140
|
-
});
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1143
|
-
function collectBindings(parsed) {
|
|
1144
|
-
const bindings = new Map;
|
|
1145
|
-
collectBindingRecords(parsed, bindings);
|
|
1146
|
-
return bindings;
|
|
1147
|
-
}
|
|
1148
|
-
function collectBindingRecords(value, bindings) {
|
|
1149
|
-
if (Array.isArray(value)) {
|
|
1150
|
-
for (const item of value) {
|
|
1151
|
-
collectBindingRecords(item, bindings);
|
|
1152
|
-
}
|
|
1153
|
-
return;
|
|
1154
|
-
}
|
|
1155
|
-
if (!isRecord(value)) {
|
|
1156
|
-
return;
|
|
1157
|
-
}
|
|
1158
|
-
const id = getStringField(value, "bindingId") ?? getStringField(value, "bindingKey") ?? getStringField(value, "id");
|
|
1159
|
-
if (id) {
|
|
1160
|
-
bindings.set(id, {
|
|
1161
|
-
id,
|
|
1162
|
-
resource: getStringField(value, "resource"),
|
|
1163
|
-
resourceKey: getStringField(value, "resourceKey"),
|
|
1164
|
-
resourceSubType: getStringField(value, "resourceSubType"),
|
|
1165
|
-
propertyAttribute: getStringField(value, "propertyAttribute")
|
|
1166
|
-
});
|
|
1167
|
-
}
|
|
1168
|
-
for (const nested of Object.values(value)) {
|
|
1169
|
-
collectBindingRecords(nested, bindings);
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
function getStringField(value, field) {
|
|
1173
|
-
const fieldValue = value[field];
|
|
1174
|
-
return typeof fieldValue === "string" && fieldValue ? fieldValue : undefined;
|
|
1175
|
-
}
|
|
1176
|
-
function isRecord(value) {
|
|
1177
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1178
|
-
}
|
|
1179
|
-
function formatDiagnostic(diagnostic) {
|
|
1180
|
-
const element = diagnostic.element ? ` ${diagnostic.element}` : "";
|
|
1181
|
-
return `${diagnostic.file}${element}: ${diagnostic.message} ${diagnostic.instruction}`;
|
|
1182
|
-
}
|
|
1183
|
-
function isErrorDiagnostic(diagnostic) {
|
|
1184
|
-
return diagnostic.severity !== "warning";
|
|
1185
|
-
}
|
|
1186
|
-
function isWarningDiagnostic(diagnostic) {
|
|
1187
|
-
return diagnostic.severity === "warning";
|
|
1188
|
-
}
|
|
1189
|
-
function decodeXmlEntities(value) {
|
|
1190
|
-
return value.replace(/"/g, '"').replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
1191
|
-
}
|
|
1192
|
-
// ../maestro-sdk/src/registry/filter-parser.ts
|
|
1193
|
-
class FilterParseError extends Error {
|
|
1194
|
-
code;
|
|
1195
|
-
token;
|
|
1196
|
-
constructor(message, code, token) {
|
|
1197
|
-
super(message);
|
|
1198
|
-
this.code = code;
|
|
1199
|
-
this.token = token;
|
|
1200
|
-
this.name = "FilterParseError";
|
|
1201
|
-
}
|
|
1202
|
-
}
|
|
1203
|
-
function parseFilterString(filterString) {
|
|
1204
|
-
if (!filterString || filterString.trim() === "") {
|
|
1205
|
-
return { filters: [], logic: "AND" };
|
|
1206
|
-
}
|
|
1207
|
-
const parts = filterString.split(/,(?=[a-zA-Z_][a-zA-Z0-9_]*(?::[a-zA-Z]+)?=)/);
|
|
1208
|
-
const conditions = [];
|
|
1209
|
-
for (const part of parts) {
|
|
1210
|
-
const condition = parseFilterCondition(part.trim());
|
|
1211
|
-
conditions.push(condition);
|
|
1212
|
-
}
|
|
1213
|
-
return {
|
|
1214
|
-
filters: conditions,
|
|
1215
|
-
logic: "AND"
|
|
1216
|
-
};
|
|
1217
|
-
}
|
|
1218
|
-
function parseFilterCondition(conditionString) {
|
|
1219
|
-
const match = conditionString.match(/^([^:=]+)(?::([^=]+))?=(.+)$/);
|
|
1220
|
-
if (!match) {
|
|
1221
|
-
throw new FilterParseError(`Invalid filter format: '${conditionString}'. Expected 'field=value' or 'field:operator=value'.`, "INVALID_FORMAT", conditionString);
|
|
1222
|
-
}
|
|
1223
|
-
const [, fieldStr, operatorStr, valueStr] = match;
|
|
1224
|
-
const field = normalizeField(fieldStr.trim());
|
|
1225
|
-
const operator = operatorStr?.trim() || "equals";
|
|
1226
|
-
const value = valueStr.trim();
|
|
1227
|
-
if (!isValidField(field)) {
|
|
1228
|
-
throw new FilterParseError(`Invalid filter field: '${field}'. Supported fields: category, type, tags, displayname, nodetype, description.`, "INVALID_FIELD", field);
|
|
1229
|
-
}
|
|
1230
|
-
if (!isValidOperator(operator)) {
|
|
1231
|
-
throw new FilterParseError(`Invalid filter operator: '${operator}'. Supported operators: equals, contains, in, startsWith, endsWith.`, "INVALID_OPERATOR", operator);
|
|
1232
|
-
}
|
|
1233
|
-
return {
|
|
1234
|
-
field,
|
|
1235
|
-
operator,
|
|
1236
|
-
value
|
|
1237
|
-
};
|
|
1238
|
-
}
|
|
1239
|
-
function normalizeField(field) {
|
|
1240
|
-
const fieldMap = {
|
|
1241
|
-
category: "category",
|
|
1242
|
-
cat: "category",
|
|
1243
|
-
type: "type",
|
|
1244
|
-
nodetype: "nodetype",
|
|
1245
|
-
node_type: "nodetype",
|
|
1246
|
-
tags: "tags",
|
|
1247
|
-
tag: "tags",
|
|
1248
|
-
displayname: "displayname",
|
|
1249
|
-
display_name: "displayname",
|
|
1250
|
-
displayName: "displayname",
|
|
1251
|
-
name: "displayname",
|
|
1252
|
-
label: "displayname"
|
|
1253
|
-
};
|
|
1254
|
-
const normalized = field.toLowerCase().replace(/[-_\s]/g, "");
|
|
1255
|
-
return fieldMap[normalized] || field;
|
|
1256
|
-
}
|
|
1257
|
-
function isValidField(field) {
|
|
1258
|
-
const validFields = [
|
|
1259
|
-
"category",
|
|
1260
|
-
"type",
|
|
1261
|
-
"tags",
|
|
1262
|
-
"displayname",
|
|
1263
|
-
"nodetype",
|
|
1264
|
-
"description"
|
|
1265
|
-
];
|
|
1266
|
-
return validFields.includes(field);
|
|
1267
|
-
}
|
|
1268
|
-
function isValidOperator(operator) {
|
|
1269
|
-
const validOperators = [
|
|
1270
|
-
"equals",
|
|
1271
|
-
"contains",
|
|
1272
|
-
"in",
|
|
1273
|
-
"startsWith",
|
|
1274
|
-
"endsWith"
|
|
1275
|
-
];
|
|
1276
|
-
return validOperators.includes(operator);
|
|
1277
|
-
}
|
|
1278
|
-
|
|
1279
|
-
// ../maestro-sdk/src/registry/command.ts
|
|
1280
|
-
function flattenNodeForDisplay(n) {
|
|
1281
|
-
return {
|
|
1282
|
-
NodeType: n.nodeType,
|
|
1283
|
-
Category: n.category ?? "",
|
|
1284
|
-
DisplayName: n.display?.label ?? "",
|
|
1285
|
-
Description: n.description ?? n.display?.description ?? "",
|
|
1286
|
-
Version: n.version ?? "",
|
|
1287
|
-
Tags: (n.tags ?? []).join(", ")
|
|
1288
|
-
};
|
|
1289
|
-
}
|
|
1290
|
-
function flattenNodeWithAvailability(n, availableOnTenant) {
|
|
1291
|
-
return {
|
|
1292
|
-
...flattenNodeForDisplay(n),
|
|
1293
|
-
AvailableOnTenant: availableOnTenant
|
|
1294
|
-
};
|
|
1295
|
-
}
|
|
1296
|
-
async function mergeWithKnownSdkNodes(tenantNodes) {
|
|
1297
|
-
const { listKnownSdkNodes } = await import("./known-types-eq32py2n.js");
|
|
1298
|
-
const tenantTypes = new Set(tenantNodes.map((n) => n.nodeType?.toLowerCase()).filter((t) => typeof t === "string"));
|
|
1299
|
-
const merged = tenantNodes.map((node) => ({ node, availableOnTenant: true }));
|
|
1300
|
-
for (const known of listKnownSdkNodes()) {
|
|
1301
|
-
const type = known.nodeType?.toLowerCase();
|
|
1302
|
-
if (typeof type !== "string")
|
|
1303
|
-
continue;
|
|
1304
|
-
if (tenantTypes.has(type))
|
|
1305
|
-
continue;
|
|
1306
|
-
merged.push({ node: known, availableOnTenant: false });
|
|
1307
|
-
}
|
|
1308
|
-
return merged;
|
|
1309
|
-
}
|
|
1310
|
-
var registerRegistryCommand = (program, commandPrefix) => {
|
|
1311
|
-
const cmd = `uip ${commandPrefix} registry`;
|
|
1312
|
-
const registry = program.command("registry").description(`Browse and search the ${commandPrefix} node registry to discover available activities and connectors (OOTB + authenticated). Use 'pull' to sync, 'search' to find nodes, 'get' to inspect a node's full input/output schema.`).addHelpText("after", `
|
|
1313
|
-
Typical workflow:
|
|
1314
|
-
0. Login for full registry (optional, OOTB nodes available without login):
|
|
1315
|
-
uip login
|
|
1316
|
-
1. Sync the registry: ${cmd} pull
|
|
1317
|
-
2. Search for a node: ${cmd} search slack
|
|
1318
|
-
3. Inspect its schema: ${cmd} get <node-type>
|
|
1319
|
-
4. Extract input fields: ${cmd} get <node-type> --output-filter "Node.inputDefinition.fields"
|
|
1320
|
-
|
|
1321
|
-
Note: Without login, only built-in (OOTB) nodes are available. Login to access the full node registry including custom connectors.
|
|
1322
|
-
|
|
1323
|
-
In-solution discovery (--local):
|
|
1324
|
-
Run from inside a solution project directory (with a .uipx file in CWD, parent, or grandparent).
|
|
1325
|
-
Shows sibling projects (Agent, Process, Flow, Api) as invokable nodes.
|
|
1326
|
-
cd MySolution/MyFlow && ${cmd} list --local
|
|
1327
|
-
|
|
1328
|
-
Subcommands:
|
|
1329
|
-
pull Fetch and cache the full node registry (run once per session)
|
|
1330
|
-
list List cached nodes (default: first 20, --limit -1 for everything)
|
|
1331
|
-
search Find nodes by keyword and/or field filters ← start here
|
|
1332
|
-
get Get full schema for a single node (with connector input/output enrichment)`);
|
|
1333
|
-
const pullExamples = [
|
|
1334
|
-
{
|
|
1335
|
-
Description: "Sync the node registry",
|
|
1336
|
-
Command: `${cmd} pull`,
|
|
1337
|
-
Output: {
|
|
1338
|
-
Code: "NodePullSuccess",
|
|
1339
|
-
Data: {
|
|
1340
|
-
NodesCount: 184,
|
|
1341
|
-
FromCache: false,
|
|
1342
|
-
Source: "authenticated",
|
|
1343
|
-
CacheWritten: true,
|
|
1344
|
-
Message: "Synced fresh nodes"
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
];
|
|
1349
|
-
const listExamples = [
|
|
1350
|
-
{
|
|
1351
|
-
Description: "List cached nodes (every row carries AvailableOnTenant; FF-gated SDK-known nodes are included)",
|
|
1352
|
-
Command: `${cmd} list --limit 2`,
|
|
1353
|
-
Output: {
|
|
1354
|
-
Code: "NodeListSuccess",
|
|
1355
|
-
Data: [
|
|
1356
|
-
{
|
|
1357
|
-
NodeType: "uipath.slack.connector.send-message",
|
|
1358
|
-
Category: "connector",
|
|
1359
|
-
DisplayName: "Send Message",
|
|
1360
|
-
Description: "Post a message to a Slack channel",
|
|
1361
|
-
Version: "1.0.0",
|
|
1362
|
-
Tags: "slack, messaging",
|
|
1363
|
-
AvailableOnTenant: true
|
|
1364
|
-
},
|
|
1365
|
-
{
|
|
1366
|
-
NodeType: "uipath.human-in-the-loop",
|
|
1367
|
-
Category: "human-task",
|
|
1368
|
-
DisplayName: "Human in the Loop",
|
|
1369
|
-
Description: "Assign a task to a human and wait for completion",
|
|
1370
|
-
Version: "1.0.0",
|
|
1371
|
-
Tags: "human-task, hitl, human-in-the-loop, approval",
|
|
1372
|
-
AvailableOnTenant: false
|
|
1373
|
-
}
|
|
1374
|
-
]
|
|
1375
|
-
}
|
|
1376
|
-
}
|
|
1377
|
-
];
|
|
1378
|
-
const searchExamples = [
|
|
1379
|
-
{
|
|
1380
|
-
Description: "Search nodes by keyword (every row carries AvailableOnTenant; FF-gated SDK-known nodes are included)",
|
|
1381
|
-
Command: `${cmd} search slack`,
|
|
1382
|
-
Output: {
|
|
1383
|
-
Code: "NodeSearchSuccess",
|
|
1384
|
-
Data: [
|
|
1385
|
-
{
|
|
1386
|
-
NodeType: "uipath.slack.connector.send-message",
|
|
1387
|
-
Category: "connector",
|
|
1388
|
-
DisplayName: "Send Message",
|
|
1389
|
-
Description: "Post a message to a Slack channel",
|
|
1390
|
-
Version: "1.0.0",
|
|
1391
|
-
Tags: "slack, messaging",
|
|
1392
|
-
AvailableOnTenant: true
|
|
1393
|
-
}
|
|
1394
|
-
]
|
|
1395
|
-
}
|
|
1396
|
-
}
|
|
1397
|
-
];
|
|
1398
|
-
const getExamples = [
|
|
1399
|
-
{
|
|
1400
|
-
Description: "Get the full schema for a node",
|
|
1401
|
-
Command: `${cmd} get uipath.slack.connector.send-message`,
|
|
1402
|
-
Output: {
|
|
1403
|
-
Code: "NodeGetSuccess",
|
|
1404
|
-
Data: {
|
|
1405
|
-
Node: {
|
|
1406
|
-
nodeType: "uipath.slack.connector.send-message",
|
|
1407
|
-
category: "connector",
|
|
1408
|
-
display: { label: "Send Message" },
|
|
1409
|
-
inputDefinition: {
|
|
1410
|
-
fields: [{ name: "channel", required: true }]
|
|
1411
|
-
}
|
|
1412
|
-
}
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1415
|
-
}
|
|
1416
|
-
];
|
|
1417
|
-
registry.command("pull").description(`Pull and sync node data from ${commandPrefix} registry (login first for full registry)`).option("-f, --force", "Force refresh, ignore cache").examples(pullExamples).trackedAction(processContext, async (options) => {
|
|
1418
|
-
const { pullNodes } = await import("./node-sync-service-zwvm30qg.js");
|
|
1419
|
-
const [error, result] = await catchError(pullNodes({
|
|
1420
|
-
force: options.force
|
|
1421
|
-
}));
|
|
1422
|
-
if (error) {
|
|
1423
|
-
OutputFormatter.error({
|
|
1424
|
-
Result: RESULTS.Failure,
|
|
1425
|
-
Message: "Error pulling nodes",
|
|
1426
|
-
Instructions: error.message
|
|
1427
|
-
});
|
|
1428
|
-
processContext.exit(1);
|
|
1429
|
-
return;
|
|
1430
|
-
}
|
|
1431
|
-
const message = result.fromCache ? `Using cached nodes (age: ${result.ageMinutes}m)` : "Synced fresh nodes";
|
|
1432
|
-
const finalMessage = result.info ? `${message}. ${result.info}` : message;
|
|
1433
|
-
OutputFormatter.success({
|
|
1434
|
-
Result: RESULTS.Success,
|
|
1435
|
-
Code: "NodePullSuccess",
|
|
1436
|
-
Data: {
|
|
1437
|
-
NodesCount: result.syncedCount,
|
|
1438
|
-
FromCache: result.fromCache,
|
|
1439
|
-
AgeMinutes: result.ageMinutes,
|
|
1440
|
-
Source: result.source,
|
|
1441
|
-
CacheWritten: result.cacheWritten,
|
|
1442
|
-
Message: finalMessage,
|
|
1443
|
-
Info: result.info
|
|
1444
|
-
}
|
|
1445
|
-
});
|
|
1446
|
-
});
|
|
1447
|
-
registry.command("list").description("Sample cached nodes to inspect their config structure (default: first 20). Output merges tenant nodes with the SDK-known catalog; every row carries `AvailableOnTenant` so callers can filter FF-gated rows. Use 'search' to find nodes — use --limit -1 only if you need the full list (the result can be huge and unreadable).").addHelpText("after", `
|
|
1448
|
-
Examples:
|
|
1449
|
-
${cmd} list
|
|
1450
|
-
${cmd} list --limit 200
|
|
1451
|
-
${cmd} list --limit -1
|
|
1452
|
-
|
|
1453
|
-
In-solution (--local):
|
|
1454
|
-
cd MySolution/MyFlow && ${cmd} list --local`).option("-l, --limit <n>", "Number of nodes to return (default: 20, -1 for all)", (v) => Number.parseInt(v, 10), 20).option("--local", "Show only in-solution project nodes (requires .uipx in CWD, parent, or grandparent). Omits `AvailableOnTenant` (no tenant lookup).").examples(listExamples).trackedAction(processContext, async (options) => {
|
|
1455
|
-
if (Number.isNaN(options.limit) || options.limit < 1 && options.limit !== -1) {
|
|
1456
|
-
OutputFormatter.error({
|
|
1457
|
-
Result: RESULTS.Failure,
|
|
1458
|
-
Message: "Invalid --limit value",
|
|
1459
|
-
Instructions: "Provide a positive integer (e.g. --limit 100) or -1 for all nodes"
|
|
1460
|
-
});
|
|
1461
|
-
processContext.exit(1);
|
|
1462
|
-
return;
|
|
1463
|
-
}
|
|
1464
|
-
const { listNodes } = await import("./node-sync-service-zwvm30qg.js");
|
|
1465
|
-
const [error, tenantNodes] = await catchError(listNodes({ local: options.local }));
|
|
1466
|
-
if (error) {
|
|
1467
|
-
OutputFormatter.error({
|
|
1468
|
-
Result: RESULTS.Failure,
|
|
1469
|
-
Message: "Error listing nodes",
|
|
1470
|
-
Instructions: error.message
|
|
1471
|
-
});
|
|
1472
|
-
processContext.exit(1);
|
|
1473
|
-
return;
|
|
1474
|
-
}
|
|
1475
|
-
const showAll = options.limit === -1;
|
|
1476
|
-
if (options.local) {
|
|
1477
|
-
const totalCount2 = tenantNodes.length;
|
|
1478
|
-
const effectiveLimit2 = showAll ? totalCount2 : options.limit;
|
|
1479
|
-
const nodes = tenantNodes.slice(0, effectiveLimit2);
|
|
1480
|
-
const truncated2 = nodes.length < totalCount2;
|
|
1481
|
-
OutputFormatter.success({
|
|
1482
|
-
Result: RESULTS.Success,
|
|
1483
|
-
Code: "NodeListSuccess",
|
|
1484
|
-
Data: nodes.map(flattenNodeForDisplay)
|
|
1485
|
-
});
|
|
1486
|
-
if (truncated2) {
|
|
1487
|
-
logger.info(`Showing first ${nodes.length} of ${totalCount2}. Use --limit or --limit -1 to get more, or use 'search' to filter.`);
|
|
1488
|
-
}
|
|
1489
|
-
return;
|
|
1490
|
-
}
|
|
1491
|
-
const merged = await mergeWithKnownSdkNodes(tenantNodes);
|
|
1492
|
-
const totalCount = merged.length;
|
|
1493
|
-
const effectiveLimit = showAll ? totalCount : options.limit;
|
|
1494
|
-
const slice = merged.slice(0, effectiveLimit);
|
|
1495
|
-
const truncated = slice.length < totalCount;
|
|
1496
|
-
const gatedCount = merged.filter((m) => !m.availableOnTenant).length;
|
|
1497
|
-
OutputFormatter.success({
|
|
1498
|
-
Result: RESULTS.Success,
|
|
1499
|
-
Code: "NodeListSuccess",
|
|
1500
|
-
Data: slice.map((m) => flattenNodeWithAvailability(m.node, m.availableOnTenant))
|
|
1501
|
-
});
|
|
1502
|
-
if (truncated) {
|
|
1503
|
-
logger.info(`Showing first ${slice.length} of ${totalCount} (${gatedCount} not enabled on this tenant). Use --limit or --limit -1 to get more.`);
|
|
1504
|
-
} else if (gatedCount > 0) {
|
|
1505
|
-
logger.info(`${gatedCount} of ${totalCount} node type(s) are not enabled on this tenant (AvailableOnTenant=false).`);
|
|
1506
|
-
}
|
|
1507
|
-
});
|
|
1508
|
-
registry.command("search").description("Find nodes by keyword and/or field filters").addHelpText("after", `
|
|
1509
|
-
Filter syntax:
|
|
1510
|
-
field=value equality (default operator)
|
|
1511
|
-
field:contains=value field contains value
|
|
1512
|
-
field:startsWith=value field starts with value
|
|
1513
|
-
field:endsWith=value field ends with value
|
|
1514
|
-
field:in=value1,value2 field matches any of the values
|
|
1515
|
-
field1=v1,field2=v2 multiple conditions (AND logic)
|
|
1516
|
-
|
|
1517
|
-
Filterable fields:
|
|
1518
|
-
category / cat e.g. "connector", "agent"
|
|
1519
|
-
type / nodetype node type identifier
|
|
1520
|
-
tags / tag node tags
|
|
1521
|
-
displayname / name / label human-readable display name
|
|
1522
|
-
|
|
1523
|
-
Examples:
|
|
1524
|
-
${cmd} search slack
|
|
1525
|
-
${cmd} search --filter "category=connector"
|
|
1526
|
-
${cmd} search --filter "displayname:contains=send message"
|
|
1527
|
-
${cmd} search slack --filter "category=connector"
|
|
1528
|
-
${cmd} search --filter "tags:in=ai,automation"
|
|
1529
|
-
${cmd} search --filter "category=connector,displayname:contains=slack"
|
|
1530
|
-
|
|
1531
|
-
Tip: pipe through --output-filter to extract just the nodeType values:
|
|
1532
|
-
${cmd} search slack --output-filter "[*].NodeType"
|
|
1533
|
-
|
|
1534
|
-
In-solution (--local):
|
|
1535
|
-
cd MySolution/MyFlow && ${cmd} search agent --local`).argument("[keyword]", "Search term matched against nodeType, category, tags, display name, and description").option("-f, --filter <filter>", "Structured field filter: field=value or field:operator=value. Comma-separated = AND. Fields: category, type, tags, displayname, description.").option("--local", "Search only in-solution project nodes (requires .uipx in CWD, parent, or grandparent). Omits `AvailableOnTenant` (no tenant lookup).").examples(searchExamples).trackedAction(processContext, async (keyword, options) => {
|
|
1536
|
-
let parsedFilters;
|
|
1537
|
-
if (options.filter) {
|
|
1538
|
-
const [parseError, parsed] = catchError(() => parseFilterString(options.filter));
|
|
1539
|
-
if (parseError) {
|
|
1540
|
-
if (parseError instanceof FilterParseError) {
|
|
1541
|
-
OutputFormatter.error({
|
|
1542
|
-
Result: RESULTS.Failure,
|
|
1543
|
-
Message: "Invalid filter syntax",
|
|
1544
|
-
Instructions: `${parseError.message} Examples: category=agent, displayname:contains=http, tags:in=ai,automation.`
|
|
1545
|
-
});
|
|
1546
|
-
processContext.exit(1);
|
|
1547
|
-
return;
|
|
1548
|
-
}
|
|
1549
|
-
OutputFormatter.error({
|
|
1550
|
-
Result: RESULTS.Failure,
|
|
1551
|
-
Message: "Error searching nodes",
|
|
1552
|
-
Instructions: parseError.message
|
|
1553
|
-
});
|
|
1554
|
-
processContext.exit(1);
|
|
1555
|
-
return;
|
|
1556
|
-
}
|
|
1557
|
-
parsedFilters = parsed;
|
|
1558
|
-
}
|
|
1559
|
-
if (!keyword && !parsedFilters) {
|
|
1560
|
-
OutputFormatter.error({
|
|
1561
|
-
Result: RESULTS.Failure,
|
|
1562
|
-
Message: "Search requires either a keyword or filters",
|
|
1563
|
-
Instructions: "Provide a keyword or use --filter flag"
|
|
1564
|
-
});
|
|
1565
|
-
processContext.exit(1);
|
|
1566
|
-
return;
|
|
1567
|
-
}
|
|
1568
|
-
const {
|
|
1569
|
-
searchNodeSummariesWithAvailability,
|
|
1570
|
-
searchNodes,
|
|
1571
|
-
searchNodesWithFilters
|
|
1572
|
-
} = await import("./node-sync-service-zwvm30qg.js");
|
|
1573
|
-
if (options.local) {
|
|
1574
|
-
const [searchError2, results2] = await catchError(parsedFilters ? searchNodesWithFilters(keyword, parsedFilters, {
|
|
1575
|
-
local: true
|
|
1576
|
-
}) : searchNodes(keyword, { local: true }));
|
|
1577
|
-
if (searchError2) {
|
|
1578
|
-
OutputFormatter.error({
|
|
1579
|
-
Result: RESULTS.Failure,
|
|
1580
|
-
Message: "Error searching nodes",
|
|
1581
|
-
Instructions: searchError2.message
|
|
1582
|
-
});
|
|
1583
|
-
processContext.exit(1);
|
|
1584
|
-
return;
|
|
1585
|
-
}
|
|
1586
|
-
OutputFormatter.success({
|
|
1587
|
-
Result: RESULTS.Success,
|
|
1588
|
-
Code: "NodeSearchSuccess",
|
|
1589
|
-
Data: results2.map(flattenNodeForDisplay)
|
|
1590
|
-
});
|
|
1591
|
-
return;
|
|
1592
|
-
}
|
|
1593
|
-
const [searchError, results] = await catchError(searchNodeSummariesWithAvailability(keyword, parsedFilters));
|
|
1594
|
-
if (searchError) {
|
|
1595
|
-
OutputFormatter.error({
|
|
1596
|
-
Result: RESULTS.Failure,
|
|
1597
|
-
Message: "Error searching nodes",
|
|
1598
|
-
Instructions: searchError.message
|
|
1599
|
-
});
|
|
1600
|
-
processContext.exit(1);
|
|
1601
|
-
return;
|
|
1602
|
-
}
|
|
1603
|
-
OutputFormatter.success({
|
|
1604
|
-
Result: RESULTS.Success,
|
|
1605
|
-
Code: "NodeSearchSuccess",
|
|
1606
|
-
Data: results.map((result) => flattenNodeWithAvailability(result.node, result.availableOnTenant))
|
|
1607
|
-
});
|
|
1608
|
-
});
|
|
1609
|
-
registry.command("get").description("Get the full schema for a single node. For connector nodes (uipath.connector.*), the base manifest is automatically enriched with live metadata from Integration Service.").addHelpText("after", `
|
|
1610
|
-
IS-enriched fields (connector nodes only, fetched live from Integration Service):
|
|
1611
|
-
|
|
1612
|
-
Activity nodes (uipath.connector.*, not trigger):
|
|
1613
|
-
inputDefinition.fields Fields to configure. Each field has: name, displayName, type, required, and optionally reference (lookup: objectName, path, lookupValue, lookupNames, childPath) or enum.
|
|
1614
|
-
outputResponseDefinition Fields produced after the node runs.
|
|
1615
|
-
connectorMethodInfo HTTP details (method, path, operationId).
|
|
1616
|
-
--connection-id optional — adds custom fields specific to that account.
|
|
1617
|
-
|
|
1618
|
-
Trigger nodes (uipath.connector.trigger.*):
|
|
1619
|
-
eventParameters Fields that configure the trigger (e.g. which folder to watch).
|
|
1620
|
-
filterFields Fields used to filter which events fire the trigger.
|
|
1621
|
-
outputResponseDefinition Fields produced by the trigger event payload.
|
|
1622
|
-
eventMode "webhooks" or "polling".
|
|
1623
|
-
login + --connection-id required — without both, no enrichment is returned.
|
|
1624
|
-
|
|
1625
|
-
Examples:
|
|
1626
|
-
${cmd} get uipath.slack.connector.send-message
|
|
1627
|
-
${cmd} get uipath.connector.uipath-salesforce-slack.send-message --connection-id <id>
|
|
1628
|
-
${cmd} get uipath.connector.trigger.uipath-microsoft-outlook365.email-received --connection-id <id>
|
|
1629
|
-
|
|
1630
|
-
In-solution (--local):
|
|
1631
|
-
cd MySolution/MyFlow && ${cmd} get <node-type> --local`).argument("<node-type>", `Node type identifier — get it from '${cmd} search'`).option("--connection-id <id>", "Connection ID for Integration Service connector node enrichment. Required for connector trigger nodes; optional for connector activity nodes.").option("--local", "Get node from in-solution projects only (requires .uipx in CWD, parent, or grandparent)").examples(getExamples).trackedAction(processContext, async (nodeType, options) => {
|
|
1632
|
-
const { getNodeEnriched } = await import("./node-sync-service-zwvm30qg.js");
|
|
1633
|
-
const [error, result] = await catchError(getNodeEnriched(nodeType, options.connectionId, {
|
|
1634
|
-
local: options.local
|
|
1635
|
-
}));
|
|
1636
|
-
if (error) {
|
|
1637
|
-
OutputFormatter.error({
|
|
1638
|
-
Result: RESULTS.Failure,
|
|
1639
|
-
Message: "Error retrieving node",
|
|
1640
|
-
Instructions: error.message
|
|
1641
|
-
});
|
|
1642
|
-
processContext.exit(1);
|
|
1643
|
-
return;
|
|
1644
|
-
}
|
|
1645
|
-
if (!result) {
|
|
1646
|
-
OutputFormatter.error({
|
|
1647
|
-
Result: RESULTS.Failure,
|
|
1648
|
-
Message: "Node not found",
|
|
1649
|
-
Instructions: `No node found with node type: ${nodeType}`
|
|
1650
|
-
});
|
|
1651
|
-
processContext.exit(1);
|
|
1652
|
-
return;
|
|
1653
|
-
}
|
|
1654
|
-
OutputFormatter.success({
|
|
1655
|
-
Result: RESULTS.Success,
|
|
1656
|
-
Code: "NodeGetSuccess",
|
|
1657
|
-
Data: {
|
|
1658
|
-
Node: result
|
|
1659
|
-
}
|
|
1660
|
-
}, { preserveDataKeys: true });
|
|
1661
|
-
});
|
|
1662
|
-
};
|
|
1663
|
-
// ../maestro-sdk/src/registry/storage.ts
|
|
1664
|
-
var MAESTRO_REGISTRY_DIR = "maestro";
|
|
1665
|
-
var REGISTRY_FILE = "registry.json";
|
|
1666
|
-
var REGISTRY_META_FILE = "registry.meta.json";
|
|
1667
|
-
var CACHE_EXPIRATION_MS = 30 * 60 * 1000;
|
|
1668
|
-
async function resolveStorageDir() {
|
|
1669
|
-
const fs = getFileSystem();
|
|
1670
|
-
return fs.path.join(fs.env.homedir(), UIPATH_HOME_DIR, MAESTRO_REGISTRY_DIR);
|
|
1671
|
-
}
|
|
1672
|
-
async function saveRegistry(registry, processCountsByType) {
|
|
1673
|
-
const fs = getFileSystem();
|
|
1674
|
-
const dir = await resolveStorageDir();
|
|
1675
|
-
await fs.mkdir(dir);
|
|
1676
|
-
const content = JSON.stringify(registry, null, 2);
|
|
1677
|
-
const hash = await hashContent(content);
|
|
1678
|
-
const registryPath = fs.path.join(dir, REGISTRY_FILE);
|
|
1679
|
-
await fs.writeFile(registryPath, content);
|
|
1680
|
-
const meta = {
|
|
1681
|
-
lastSync: registry.lastSync,
|
|
1682
|
-
hash,
|
|
1683
|
-
extensionTypeCount: Object.keys(registry.static.extensionTypes).length,
|
|
1684
|
-
connectorCount: registry.discovered.connectors.length,
|
|
1685
|
-
processCount: registry.discovered.processes.length,
|
|
1686
|
-
processCountsByType
|
|
1687
|
-
};
|
|
1688
|
-
const metaPath = fs.path.join(dir, REGISTRY_META_FILE);
|
|
1689
|
-
await fs.writeFile(metaPath, JSON.stringify(meta, null, 2));
|
|
1690
|
-
}
|
|
1691
|
-
async function loadRegistry() {
|
|
1692
|
-
const fs = getFileSystem();
|
|
1693
|
-
const dir = await resolveStorageDir();
|
|
1694
|
-
const registryPath = fs.path.join(dir, REGISTRY_FILE);
|
|
1695
|
-
if (!await fs.exists(registryPath)) {
|
|
1696
|
-
return null;
|
|
1697
|
-
}
|
|
1698
|
-
const [readErr, content] = await catchError(fs.readFile(registryPath, "utf-8"));
|
|
1699
|
-
if (readErr || content === null) {
|
|
1700
|
-
return null;
|
|
1701
|
-
}
|
|
1702
|
-
const [parseErr, parsed] = catchError(() => JSON.parse(content));
|
|
1703
|
-
if (parseErr) {
|
|
1704
|
-
return null;
|
|
1705
|
-
}
|
|
1706
|
-
return parsed;
|
|
1707
|
-
}
|
|
1708
|
-
async function loadRegistryMetadata() {
|
|
1709
|
-
const fs = getFileSystem();
|
|
1710
|
-
const dir = await resolveStorageDir();
|
|
1711
|
-
const metaPath = fs.path.join(dir, REGISTRY_META_FILE);
|
|
1712
|
-
if (!await fs.exists(metaPath)) {
|
|
1713
|
-
return null;
|
|
1714
|
-
}
|
|
1715
|
-
const [readErr, content] = await catchError(fs.readFile(metaPath, "utf-8"));
|
|
1716
|
-
if (readErr || content === null) {
|
|
1717
|
-
return null;
|
|
1718
|
-
}
|
|
1719
|
-
const [parseErr, parsed] = catchError(() => JSON.parse(content));
|
|
1720
|
-
if (parseErr) {
|
|
1721
|
-
return null;
|
|
1722
|
-
}
|
|
1723
|
-
return parsed;
|
|
1724
|
-
}
|
|
1725
|
-
|
|
1726
|
-
// ../maestro-sdk/src/registry/registry-service.ts
|
|
1727
|
-
var ORCHESTRATOR_RESOURCE_TYPES = [
|
|
1728
|
-
"Process",
|
|
1729
|
-
"ProcessOrchestration",
|
|
1730
|
-
"Agent",
|
|
1731
|
-
"Flow",
|
|
1732
|
-
"Api",
|
|
1733
|
-
"Function",
|
|
1734
|
-
"CaseManagement",
|
|
1735
|
-
"BusinessRules"
|
|
1736
|
-
];
|
|
1737
|
-
function countProcessesByType(processes) {
|
|
1738
|
-
const counts = {};
|
|
1739
|
-
for (const type of ORCHESTRATOR_RESOURCE_TYPES) {
|
|
1740
|
-
counts[type] = 0;
|
|
1741
|
-
}
|
|
1742
|
-
for (const p of processes) {
|
|
1743
|
-
counts[p.type] = (counts[p.type] ?? 0) + 1;
|
|
1744
|
-
}
|
|
1745
|
-
return counts;
|
|
1746
|
-
}
|
|
1747
|
-
async function pullRegistry(options = {}) {
|
|
1748
|
-
const { force = false } = options;
|
|
1749
|
-
if (!force) {
|
|
1750
|
-
const meta = await loadRegistryMetadata();
|
|
1751
|
-
if (meta) {
|
|
1752
|
-
const ageMs = Date.now() - meta.lastSync;
|
|
1753
|
-
if (ageMs < CACHE_EXPIRATION_MS) {
|
|
1754
|
-
return {
|
|
1755
|
-
extensionTypeCount: meta.extensionTypeCount,
|
|
1756
|
-
connectorCount: meta.connectorCount,
|
|
1757
|
-
processCount: meta.processCount,
|
|
1758
|
-
processCountsByType: meta.processCountsByType ?? {},
|
|
1759
|
-
fromCache: true,
|
|
1760
|
-
ageMinutes: Math.round(ageMs / 60000),
|
|
1761
|
-
cacheWritten: false
|
|
1762
|
-
};
|
|
1763
|
-
}
|
|
1764
|
-
}
|
|
1765
|
-
}
|
|
1766
|
-
const { registry, authenticated, connectorDiscovery } = await buildRegistry();
|
|
1767
|
-
const processCountsByType = countProcessesByType(registry.discovered.processes);
|
|
1768
|
-
await saveRegistry(registry, processCountsByType);
|
|
1769
|
-
const connectorCount = registry.discovered.connectors.length;
|
|
1770
|
-
const processCount = registry.discovered.processes.length;
|
|
1771
|
-
return {
|
|
1772
|
-
extensionTypeCount: Object.keys(registry.static.extensionTypes).length,
|
|
1773
|
-
connectorCount,
|
|
1774
|
-
processCount,
|
|
1775
|
-
processCountsByType,
|
|
1776
|
-
fromCache: false,
|
|
1777
|
-
cacheWritten: true,
|
|
1778
|
-
info: getRegistryPullInfo(authenticated, connectorCount, processCount, connectorDiscovery)
|
|
1779
|
-
};
|
|
1780
|
-
}
|
|
1781
|
-
async function getRegistry() {
|
|
1782
|
-
const cached = await loadRegistry();
|
|
1783
|
-
if (cached) {
|
|
1784
|
-
return cached;
|
|
1785
|
-
}
|
|
1786
|
-
const { registry } = await buildRegistry();
|
|
1787
|
-
await saveRegistry(registry, countProcessesByType(registry.discovered.processes));
|
|
1788
|
-
return registry;
|
|
1789
|
-
}
|
|
1790
|
-
async function searchRegistry(keyword) {
|
|
1791
|
-
const registry = await getRegistry();
|
|
1792
|
-
const lower = keyword.toLowerCase();
|
|
1793
|
-
const extensionTypes = Object.values(registry.static.extensionTypes).filter((et) => et.extensionType.toLowerCase().includes(lower) || et.label.toLowerCase().includes(lower) || et.bpmnElement.toLowerCase().includes(lower) || et.extensionTag.toLowerCase().includes(lower) || (et.discoveryNotes ?? "").toLowerCase().includes(lower));
|
|
1794
|
-
const connectors = registry.discovered.connectors.filter((c) => c.key.toLowerCase().includes(lower) || c.name.toLowerCase().includes(lower) || c.activities.some((a) => a.displayName.toLowerCase().includes(lower) || a.name.toLowerCase().includes(lower)) || c.triggers.some((t) => t.displayName.toLowerCase().includes(lower) || t.name.toLowerCase().includes(lower)));
|
|
1795
|
-
const processes = registry.discovered.processes.filter((p) => p.name.toLowerCase().includes(lower) || p.processKey.toLowerCase().includes(lower) || (p.processVersion ?? "").toLowerCase().includes(lower) || p.key.toLowerCase().includes(lower) || p.type.toLowerCase().includes(lower) || p.folderKey.toLowerCase().includes(lower) || (p.feedId ?? "").toLowerCase().includes(lower));
|
|
1796
|
-
return { extensionTypes, connectors, processes };
|
|
1797
|
-
}
|
|
1798
|
-
async function getExtensionType(extensionType) {
|
|
1799
|
-
const registry = await getRegistry();
|
|
1800
|
-
const lower = extensionType.toLowerCase();
|
|
1801
|
-
const match = Object.entries(registry.static.extensionTypes).find(([key]) => key.toLowerCase() === lower);
|
|
1802
|
-
return match ? match[1] : null;
|
|
1803
|
-
}
|
|
1804
|
-
async function resolveConnectorKey(connectionId) {
|
|
1805
|
-
const [regErr, registry] = await catchError(getRegistry());
|
|
1806
|
-
if (!regErr && registry) {
|
|
1807
|
-
const cached = registry.discovered.connectors.find((connector) => connector.connections.some((conn) => conn.id === connectionId))?.key;
|
|
1808
|
-
if (cached) {
|
|
1809
|
-
return cached;
|
|
1810
|
-
}
|
|
1811
|
-
}
|
|
1812
|
-
const [apiErr, api] = await catchError(createApiClient(ConnectionsApi));
|
|
1813
|
-
if (apiErr) {
|
|
1814
|
-
return null;
|
|
1815
|
-
}
|
|
1816
|
-
const [connErr, connection] = await catchError(api.apiV1ConnectionsConnectionIdGet({ connectionId, allFolders: true }));
|
|
1817
|
-
if (connErr || !connection) {
|
|
1818
|
-
return null;
|
|
1819
|
-
}
|
|
1820
|
-
return connection.connector?.key ?? null;
|
|
1821
|
-
}
|
|
1822
|
-
async function getEnrichedActivityMetadata(objectName, connectionId, options = {}) {
|
|
1823
|
-
const [loginErr, loginStatus] = await catchError(getLoginStatusAsync());
|
|
1824
|
-
if (loginErr || loginStatus.loginStatus !== "Logged in" || !loginStatus.baseUrl || !loginStatus.accessToken || !loginStatus.organizationId || !loginStatus.tenantName) {
|
|
1825
|
-
logger.warn("Not logged in — cannot enrich from IS.");
|
|
1826
|
-
return null;
|
|
1827
|
-
}
|
|
1828
|
-
const connectorKey = await resolveConnectorKey(connectionId);
|
|
1829
|
-
if (!connectorKey) {
|
|
1830
|
-
logger.warn(`Cannot enrich: no connector found for connection ${connectionId}. Verify the connection id (and that you can access it).`);
|
|
1831
|
-
return null;
|
|
1832
|
-
}
|
|
1833
|
-
const [apiErr, api] = await catchError(createApiClient(ElementsApi));
|
|
1834
|
-
if (apiErr) {
|
|
1835
|
-
logger.warn(`IS enrichment failed: ${apiErr.message}`);
|
|
1836
|
-
return null;
|
|
1837
|
-
}
|
|
1838
|
-
if (options.isEvent) {
|
|
1839
|
-
if (!options.operation) {
|
|
1840
|
-
logger.warn("Event enrichment requires an operation — pass --operation (e.g. INCIDENT_CREATED).");
|
|
1841
|
-
return null;
|
|
1842
|
-
}
|
|
1843
|
-
const operation2 = options.operation.toUpperCase();
|
|
1844
|
-
const [err2, metadata2] = await fetchTriggerMetadata(api, connectorKey, operation2, objectName, connectionId);
|
|
1845
|
-
const isEmpty = !err2 && metadata2 !== null && metadata2 !== undefined && Object.keys(metadata2).length === 0;
|
|
1846
|
-
if (err2 || !metadata2 || isEmpty) {
|
|
1847
|
-
const message = err2?.message ?? (isEmpty ? "no event metadata returned (empty)" : "no event metadata returned");
|
|
1848
|
-
logger.warn(`IS event enrichment failed: ${message}`);
|
|
1849
|
-
return {
|
|
1850
|
-
error: `Event operation '${operation2}' on '${objectName}' could not be enriched: ${message}`
|
|
1851
|
-
};
|
|
1852
|
-
}
|
|
1853
|
-
return buildTriggerCompactSummary(metadata2, operation2);
|
|
1854
|
-
}
|
|
1855
|
-
const [err, metadata] = await catchError(fetchRawMetadata(api, connectorKey, objectName, connectionId));
|
|
1856
|
-
if (err || !metadata) {
|
|
1857
|
-
logger.warn(`IS enrichment failed: ${err?.message ?? "no metadata returned"}`);
|
|
1858
|
-
return null;
|
|
1859
|
-
}
|
|
1860
|
-
const operation = options.operation ?? soleOperation(metadata);
|
|
1861
|
-
return buildResourceCompactSummary(metadata, operation);
|
|
1862
|
-
}
|
|
1863
|
-
function soleOperation(metadata) {
|
|
1864
|
-
const inner = metadata.metadata;
|
|
1865
|
-
const methods = inner?.method ?? {};
|
|
1866
|
-
const keys = Object.keys(methods);
|
|
1867
|
-
return keys.length === 1 ? keys[0] : undefined;
|
|
1868
|
-
}
|
|
1869
|
-
function getRegistryPullInfo(authenticated, connectorCount, processCount, connectorDiscovery) {
|
|
1870
|
-
if (connectorCount > 0) {
|
|
1871
|
-
return;
|
|
1872
|
-
}
|
|
1873
|
-
if (!authenticated) {
|
|
1874
|
-
return "Only built-in (OOTB) extension types synced. Run 'uip login' to discover connectors and processes.";
|
|
1875
|
-
}
|
|
1876
|
-
if (connectorDiscovery === "failed") {
|
|
1877
|
-
return "Connector/action metadata could not be refreshed; synced built-in extension types and any available processes. Check Integration Service access or connectivity and retry with --force.";
|
|
1878
|
-
}
|
|
1879
|
-
if (processCount > 0) {
|
|
1880
|
-
return "No Integration Service connectors with connections were discovered; synced built-in extension types and discovered processes.";
|
|
1881
|
-
}
|
|
1882
|
-
return "No connector/action metadata or processes were discovered; synced built-in extension types. Check Integration Service and Orchestrator access if you expected tenant resources.";
|
|
1883
|
-
}
|
|
1884
|
-
async function buildRegistry() {
|
|
1885
|
-
const extensionTypes = BPMN_SPEC.extensionTypes;
|
|
1886
|
-
let connectors = [];
|
|
1887
|
-
let processes = [];
|
|
1888
|
-
let authenticated = false;
|
|
1889
|
-
let connectorDiscovery = "not-run";
|
|
1890
|
-
const auth = await getAuthContext();
|
|
1891
|
-
if (auth) {
|
|
1892
|
-
authenticated = true;
|
|
1893
|
-
const [discoverErr, discovered] = await catchError(discoverResources(auth));
|
|
1894
|
-
if (discoverErr) {
|
|
1895
|
-
logger.warn(`Resource discovery failed, using static only: ${discoverErr.message}`);
|
|
1896
|
-
connectorDiscovery = "failed";
|
|
1897
|
-
} else {
|
|
1898
|
-
connectors = discovered.connectors;
|
|
1899
|
-
processes = discovered.processes;
|
|
1900
|
-
connectorDiscovery = discovered.connectorDiscovery;
|
|
1901
|
-
}
|
|
1902
|
-
} else {
|
|
1903
|
-
logger.info("Not logged in — syncing OOTB extension types only.");
|
|
1904
|
-
}
|
|
1905
|
-
const registry = {
|
|
1906
|
-
version: BPMN_SPEC.version,
|
|
1907
|
-
lastSync: Date.now(),
|
|
1908
|
-
hash: "",
|
|
1909
|
-
static: {
|
|
1910
|
-
extensionTypes,
|
|
1911
|
-
bpmnElements: BPMN_SPEC.bpmnElements,
|
|
1912
|
-
serializationRules: BPMN_SPEC.serializationRules
|
|
1913
|
-
},
|
|
1914
|
-
discovered: { connectors, processes }
|
|
1915
|
-
};
|
|
1916
|
-
registry.hash = await hashContent(JSON.stringify(registry));
|
|
1917
|
-
return { registry, authenticated, connectorDiscovery };
|
|
1918
|
-
}
|
|
1919
|
-
async function getAuthContext() {
|
|
1920
|
-
const [err, status] = await catchError(getLoginStatusAsync());
|
|
1921
|
-
if (err || status.loginStatus !== "Logged in" || !status.baseUrl || !status.accessToken || !status.organizationId || !status.tenantName) {
|
|
1922
|
-
return null;
|
|
1923
|
-
}
|
|
1924
|
-
return {
|
|
1925
|
-
baseUrl: status.baseUrl,
|
|
1926
|
-
accessToken: status.accessToken,
|
|
1927
|
-
organizationId: status.organizationId.toString(),
|
|
1928
|
-
tenantName: status.tenantName
|
|
1929
|
-
};
|
|
1930
|
-
}
|
|
1931
|
-
async function discoverResources(auth) {
|
|
1932
|
-
const [connectorsResult, processesResult] = await Promise.allSettled([
|
|
1933
|
-
discoverConnectors(auth),
|
|
1934
|
-
discoverProcesses(auth)
|
|
1935
|
-
]);
|
|
1936
|
-
const connectorDiscovery = connectorsResult.status === "fulfilled" ? "succeeded" : "failed";
|
|
1937
|
-
const connectors = connectorsResult.status === "fulfilled" ? connectorsResult.value : [];
|
|
1938
|
-
const processes = processesResult.status === "fulfilled" ? processesResult.value : [];
|
|
1939
|
-
if (connectorsResult.status === "rejected") {
|
|
1940
|
-
logger.warn(`Connector discovery failed: ${String(connectorsResult.reason)}`);
|
|
1941
|
-
}
|
|
1942
|
-
if (processesResult.status === "rejected") {
|
|
1943
|
-
logger.warn(`Process discovery failed: ${String(processesResult.reason)}`);
|
|
1944
|
-
}
|
|
1945
|
-
return { connectors, processes, connectorDiscovery };
|
|
1946
|
-
}
|
|
1947
|
-
async function isGet(auth, path) {
|
|
1948
|
-
const url = `${auth.baseUrl}/${auth.organizationId}/${auth.tenantName}/connections_/api/v1/${path}`;
|
|
1949
|
-
logger.debug(`IS GET: ${url}`);
|
|
1950
|
-
const response = await fetch(url, {
|
|
1951
|
-
method: "GET",
|
|
1952
|
-
headers: {
|
|
1953
|
-
Authorization: `Bearer ${auth.accessToken}`,
|
|
1954
|
-
Accept: "application/json"
|
|
1955
|
-
}
|
|
1956
|
-
});
|
|
1957
|
-
if (!response.ok) {
|
|
1958
|
-
const text = await response.text();
|
|
1959
|
-
throw new Error(`IS ${path} failed: ${response.status} — ${text.substring(0, 300)}`);
|
|
1960
|
-
}
|
|
1961
|
-
return await response.json();
|
|
1962
|
-
}
|
|
1963
|
-
async function discoverConnectors(auth) {
|
|
1964
|
-
const allConnections = await fetchAllConnections();
|
|
1965
|
-
const connectors = new Map;
|
|
1966
|
-
for (const connection of allConnections) {
|
|
1967
|
-
const key = connection.connector?.key;
|
|
1968
|
-
if (!key) {
|
|
1969
|
-
continue;
|
|
1970
|
-
}
|
|
1971
|
-
const connector = connectors.get(key) ?? {
|
|
1972
|
-
key,
|
|
1973
|
-
name: connection.connector?.name ?? key,
|
|
1974
|
-
connections: []
|
|
1975
|
-
};
|
|
1976
|
-
connector.connections.push({
|
|
1977
|
-
id: connection.id ?? "",
|
|
1978
|
-
name: connection.name ?? "",
|
|
1979
|
-
state: normalizeConnectionStatus(connection.state),
|
|
1980
|
-
folder: connection.folder?.path ?? "",
|
|
1981
|
-
folderKey: connection.folder?.key ?? ""
|
|
1982
|
-
});
|
|
1983
|
-
connectors.set(key, connector);
|
|
1984
|
-
}
|
|
1985
|
-
logger.debug(`Found ${connectors.size} connectors across ${allConnections.length} connections`);
|
|
1986
|
-
const results = await Promise.allSettled([...connectors.values()].map(async (connector) => {
|
|
1987
|
-
const [activities, triggers] = await Promise.all([
|
|
1988
|
-
fetchActivities(auth, connector.key),
|
|
1989
|
-
fetchTriggers(auth, connector.key)
|
|
1990
|
-
]);
|
|
1991
|
-
return {
|
|
1992
|
-
...connector,
|
|
1993
|
-
activities,
|
|
1994
|
-
triggers
|
|
1995
|
-
};
|
|
1996
|
-
}));
|
|
1997
|
-
return results.filter((r) => r.status === "fulfilled").map((r) => r.value);
|
|
1998
|
-
}
|
|
1999
|
-
var CONNECTIONS_PAGE_SIZE = 100;
|
|
2000
|
-
var MAX_CONNECTION_PAGES = 20;
|
|
2001
|
-
async function fetchAllConnections() {
|
|
2002
|
-
const api = await createApiClient(ConnectionsApi);
|
|
2003
|
-
const connections = [];
|
|
2004
|
-
const seenIds = new Set;
|
|
2005
|
-
let previousPageIds;
|
|
2006
|
-
for (let pageIndex = 0;pageIndex < MAX_CONNECTION_PAGES; pageIndex++) {
|
|
2007
|
-
const page = await api.apiV1ConnectionsGet({
|
|
2008
|
-
allFolders: true,
|
|
2009
|
-
mostRecentFirst: true,
|
|
2010
|
-
pageIndex,
|
|
2011
|
-
pageSize: CONNECTIONS_PAGE_SIZE
|
|
2012
|
-
});
|
|
2013
|
-
for (const connection of page) {
|
|
2014
|
-
const id = connection.id;
|
|
2015
|
-
if (!id || seenIds.has(id)) {
|
|
2016
|
-
continue;
|
|
2017
|
-
}
|
|
2018
|
-
seenIds.add(id);
|
|
2019
|
-
connections.push(connection);
|
|
2020
|
-
}
|
|
2021
|
-
if (page.length < CONNECTIONS_PAGE_SIZE) {
|
|
2022
|
-
return connections;
|
|
2023
|
-
}
|
|
2024
|
-
const pageIds = JSON.stringify(page.map((connection) => connection.id ?? null));
|
|
2025
|
-
if (pageIds === previousPageIds) {
|
|
2026
|
-
throw new Error("Connection discovery returned an identical page twice; refusing to save incomplete results.");
|
|
2027
|
-
}
|
|
2028
|
-
previousPageIds = pageIds;
|
|
2029
|
-
}
|
|
2030
|
-
throw new Error(`Connection discovery exceeded the ${MAX_CONNECTION_PAGES}-page safety limit; refusing to return incomplete results.`);
|
|
2031
|
-
}
|
|
2032
|
-
async function fetchActivities(auth, connectorKey) {
|
|
2033
|
-
const baseUrl = `${auth.baseUrl}/${auth.organizationId}/${auth.tenantName}`;
|
|
2034
|
-
const url = `${baseUrl}/elements_/v3/element/connectors/${connectorKey}/objects`;
|
|
2035
|
-
const [err, response] = await catchError(fetch(url, {
|
|
2036
|
-
method: "GET",
|
|
2037
|
-
headers: {
|
|
2038
|
-
Authorization: `Bearer ${auth.accessToken}`,
|
|
2039
|
-
Accept: "application/json"
|
|
2040
|
-
}
|
|
2041
|
-
}));
|
|
2042
|
-
if (err || !response.ok) {
|
|
2043
|
-
logger.debug(`Activities for ${connectorKey}: ${err?.message ?? `status ${response?.status}`}`);
|
|
2044
|
-
return [];
|
|
2045
|
-
}
|
|
2046
|
-
const [parseErr, data] = await catchError(response.json());
|
|
2047
|
-
if (parseErr || !Array.isArray(data)) {
|
|
2048
|
-
return [];
|
|
2049
|
-
}
|
|
2050
|
-
return data.map((a) => ({
|
|
2051
|
-
name: a.name ?? "",
|
|
2052
|
-
displayName: a.displayName ?? a.name ?? "",
|
|
2053
|
-
objectName: a.objectName ?? a.name ?? "",
|
|
2054
|
-
method: a.method ?? "POST",
|
|
2055
|
-
operation: a.operation ?? ""
|
|
2056
|
-
}));
|
|
2057
|
-
}
|
|
2058
|
-
async function fetchTriggers(auth, connectorKey) {
|
|
2059
|
-
const [err, raw] = await catchError(isGet(auth, `connectors/${connectorKey}/triggers`));
|
|
2060
|
-
if (err) {
|
|
2061
|
-
logger.debug(`Triggers for ${connectorKey}: ${err.message}`);
|
|
2062
|
-
return [];
|
|
2063
|
-
}
|
|
2064
|
-
if (!Array.isArray(raw)) {
|
|
2065
|
-
return [];
|
|
2066
|
-
}
|
|
2067
|
-
return raw.map((t) => ({
|
|
2068
|
-
name: t.name ?? "",
|
|
2069
|
-
displayName: t.displayName ?? t.name ?? "",
|
|
2070
|
-
objectName: t.objectName ?? t.name ?? ""
|
|
2071
|
-
}));
|
|
2072
|
-
}
|
|
2073
|
-
async function discoverProcesses(auth) {
|
|
2074
|
-
const [err, sdkConfig] = await catchError(createOrchestratorConfig({ tenant: auth.tenantName }));
|
|
2075
|
-
if (err) {
|
|
2076
|
-
logger.warn(`Orchestrator config failed: ${err.message}`);
|
|
2077
|
-
return [];
|
|
2078
|
-
}
|
|
2079
|
-
const releasesApi = new ReleasesApi(sdkConfig);
|
|
2080
|
-
const results = await Promise.allSettled(ORCHESTRATOR_RESOURCE_TYPES.map((type) => fetchReleasesByType(releasesApi, type)));
|
|
2081
|
-
return results.flatMap((r, i) => {
|
|
2082
|
-
if (r.status === "fulfilled") {
|
|
2083
|
-
return r.value;
|
|
2084
|
-
}
|
|
2085
|
-
logger.warn(`Release discovery failed for type ${ORCHESTRATOR_RESOURCE_TYPES[i]}: ${r.reason}`);
|
|
2086
|
-
return [];
|
|
2087
|
-
});
|
|
2088
|
-
}
|
|
2089
|
-
var RELEASES_PAGE_SIZE = 100;
|
|
2090
|
-
async function fetchReleasesByType(releasesApi, type) {
|
|
2091
|
-
const all = [];
|
|
2092
|
-
let skip = 0;
|
|
2093
|
-
while (true) {
|
|
2094
|
-
const response = await releasesApi.releasesListReleases({
|
|
2095
|
-
$filter: `ProcessType eq '${type}'`,
|
|
2096
|
-
$orderby: "Name asc",
|
|
2097
|
-
$top: RELEASES_PAGE_SIZE,
|
|
2098
|
-
$skip: skip
|
|
2099
|
-
});
|
|
2100
|
-
const page = (response.value ?? []).map((r) => ({
|
|
2101
|
-
key: r.key ?? "",
|
|
2102
|
-
name: r.name ?? "",
|
|
2103
|
-
processKey: r.processKey ?? "",
|
|
2104
|
-
processVersion: r.processVersion,
|
|
2105
|
-
type,
|
|
2106
|
-
folder: r.organizationUnitFullyQualifiedName ?? "",
|
|
2107
|
-
folderKey: r.folderKey ?? "",
|
|
2108
|
-
feedId: r.feedId
|
|
2109
|
-
}));
|
|
2110
|
-
all.push(...page);
|
|
2111
|
-
if (page.length < RELEASES_PAGE_SIZE) {
|
|
2112
|
-
break;
|
|
2113
|
-
}
|
|
2114
|
-
skip += RELEASES_PAGE_SIZE;
|
|
2115
|
-
}
|
|
2116
|
-
return all;
|
|
2117
|
-
}
|
|
2118
|
-
export { BpmnValidateService, FilterParseError, parseFilterString, registerRegistryCommand, CACHE_EXPIRATION_MS, saveRegistry, loadRegistry, loadRegistryMetadata, ORCHESTRATOR_RESOURCE_TYPES, countProcessesByType, pullRegistry, getRegistry, searchRegistry, getExtensionType, getEnrichedActivityMetadata };
|
|
2119
|
-
|
|
2120
|
-
//# debugId=B4A478A5C1EED29164756E2164756E21
|