@uipath/maestro-builder-sdk 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +322 -0
- package/dist/bindings-v2.d.ts +37 -0
- package/dist/bindings-v2.js +155 -0
- package/dist/bindings.d.ts +6 -0
- package/dist/bindings.js +6 -0
- package/dist/bpmn/bpmn-expr-check.d.ts +41 -0
- package/dist/bpmn/bpmn-expr-check.js +218 -0
- package/dist/bpmn/bpmn-sdk.d.ts +2257 -0
- package/dist/bpmn/bpmn-sdk.js +1476 -0
- package/dist/bpmn/check-cli.d.ts +3 -0
- package/dist/bpmn/check-cli.js +32 -0
- package/dist/bpmn/check.d.ts +25 -0
- package/dist/bpmn/check.js +430 -0
- package/dist/bpmn/compile-cli.d.ts +3 -0
- package/dist/bpmn/compile-cli.js +60 -0
- package/dist/bpmn/connector.d.ts +56 -0
- package/dist/bpmn/connector.js +199 -0
- package/dist/bpmn/decompile-cli.d.ts +8 -0
- package/dist/bpmn/decompile-cli.js +54 -0
- package/dist/bpmn/decompile.d.ts +6 -0
- package/dist/bpmn/decompile.js +1108 -0
- package/dist/bpmn/format-profile.d.ts +27 -0
- package/dist/bpmn/format-profile.js +11 -0
- package/dist/bpmn/intsvc.d.ts +77 -0
- package/dist/bpmn/intsvc.js +155 -0
- package/dist/bpmn/load.d.ts +4 -0
- package/dist/bpmn/load.js +22 -0
- package/dist/bpmn/merge-cli.d.ts +3 -0
- package/dist/bpmn/merge-cli.js +45 -0
- package/dist/bpmn/merge.d.ts +19 -0
- package/dist/bpmn/merge.js +293 -0
- package/dist/bpmn/registry-types.json +1869 -0
- package/dist/bpmn/serialize.d.ts +28 -0
- package/dist/bpmn/serialize.js +757 -0
- package/dist/bpmn/tidy-cli.d.ts +2 -0
- package/dist/bpmn/tidy-cli.js +39 -0
- package/dist/bpmn/typed-node.d.ts +382 -0
- package/dist/bpmn/typed-node.js +551 -0
- package/dist/bpmn/uipath-moddle.v1.json +823 -0
- package/dist/bpmn/xml-ranges.d.ts +45 -0
- package/dist/bpmn/xml-ranges.js +151 -0
- package/dist/case/case-expr-check.d.ts +35 -0
- package/dist/case/case-expr-check.js +85 -0
- package/dist/case/case-sdk.d.ts +1282 -0
- package/dist/case/case-sdk.js +923 -0
- package/dist/case/check-cli.d.ts +3 -0
- package/dist/case/check-cli.js +31 -0
- package/dist/case/check.d.ts +18 -0
- package/dist/case/check.js +456 -0
- package/dist/case/compile-cli.d.ts +3 -0
- package/dist/case/compile-cli.js +291 -0
- package/dist/case/decompile-cli.d.ts +9 -0
- package/dist/case/decompile-cli.js +117 -0
- package/dist/case/decompile.d.ts +187 -0
- package/dist/case/decompile.js +1170 -0
- package/dist/case/entry-points.d.ts +79 -0
- package/dist/case/entry-points.js +118 -0
- package/dist/case/format-profile.d.ts +60 -0
- package/dist/case/format-profile.js +79 -0
- package/dist/case/ids.d.ts +24 -0
- package/dist/case/ids.js +76 -0
- package/dist/case/load.d.ts +12 -0
- package/dist/case/load.js +32 -0
- package/dist/case/preserve.d.ts +133 -0
- package/dist/case/preserve.js +105 -0
- package/dist/case/serialize.d.ts +10 -0
- package/dist/case/serialize.js +1414 -0
- package/dist/check-cli.d.ts +3 -0
- package/dist/check-cli.js +118 -0
- package/dist/check.d.ts +146 -0
- package/dist/check.js +4898 -0
- package/dist/cli/commands/bpmn.d.ts +2 -0
- package/dist/cli/commands/bpmn.js +73 -0
- package/dist/cli/commands/case.d.ts +2 -0
- package/dist/cli/commands/case.js +60 -0
- package/dist/cli/commands/flow.d.ts +2 -0
- package/dist/cli/commands/flow.js +99 -0
- package/dist/cli/commands/registry.d.ts +10 -0
- package/dist/cli/commands/registry.js +170 -0
- package/dist/cli/common.d.ts +30 -0
- package/dist/cli/common.js +92 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +18 -0
- package/dist/cli-run.d.ts +2 -0
- package/dist/cli-run.js +42 -0
- package/dist/compile-cli.d.ts +3 -0
- package/dist/compile-cli.js +160 -0
- package/dist/config.d.ts +47 -0
- package/dist/config.js +201 -0
- package/dist/core/actions.d.ts +3244 -0
- package/dist/core/actions.js +1062 -0
- package/dist/core/binding-messages.d.ts +1 -0
- package/dist/core/binding-messages.js +16 -0
- package/dist/core/bindings.d.ts +12 -0
- package/dist/core/bindings.js +67 -0
- package/dist/core/ceql.d.ts +73 -0
- package/dist/core/ceql.js +272 -0
- package/dist/core/cli-spelling.d.ts +39 -0
- package/dist/core/cli-spelling.js +62 -0
- package/dist/core/connector-raw-node.d.ts +21 -0
- package/dist/core/connector-raw-node.js +47 -0
- package/dist/core/connectors.d.ts +184 -0
- package/dist/core/connectors.js +128 -0
- package/dist/core/expr-check.d.ts +93 -0
- package/dist/core/expr-check.js +186 -0
- package/dist/core/expr.d.ts +323 -0
- package/dist/core/expr.js +400 -0
- package/dist/core/index.d.ts +10 -0
- package/dist/core/index.js +10 -0
- package/dist/core/library.d.ts +440 -0
- package/dist/core/library.js +1183 -0
- package/dist/core/list-envelope.d.ts +45 -0
- package/dist/core/list-envelope.js +97 -0
- package/dist/core/lookup-store.d.ts +22 -0
- package/dist/core/lookup-store.js +85 -0
- package/dist/core/lookups.d.ts +227 -0
- package/dist/core/lookups.js +231 -0
- package/dist/core/node-classes.d.ts +234 -0
- package/dist/core/node-classes.js +158 -0
- package/dist/core/script-return.d.ts +68 -0
- package/dist/core/script-return.js +197 -0
- package/dist/core/stable-id.d.ts +28 -0
- package/dist/core/stable-id.js +37 -0
- package/dist/core-definitions.json +16249 -0
- package/dist/decompile-cli.d.ts +3 -0
- package/dist/decompile-cli.js +131 -0
- package/dist/decompile.d.ts +138 -0
- package/dist/decompile.js +2482 -0
- package/dist/emit.d.ts +10 -0
- package/dist/emit.js +60 -0
- package/dist/event-filters.d.ts +130 -0
- package/dist/event-filters.js +137 -0
- package/dist/flow-expr-check.d.ts +17 -0
- package/dist/flow-expr-check.js +193 -0
- package/dist/flow-sdk.d.ts +1341 -0
- package/dist/flow-sdk.js +1164 -0
- package/dist/flow-variables.d.ts +13 -0
- package/dist/flow-variables.js +42 -0
- package/dist/format-profile.d.ts +72 -0
- package/dist/format-profile.js +97 -0
- package/dist/generators/_connections.py +287 -0
- package/dist/generators/_events.py +355 -0
- package/dist/generators/_lookups.py +114 -0
- package/dist/generators/_naming.py +185 -0
- package/dist/generators/_objects.py +299 -0
- package/dist/generators/_resolve.py +273 -0
- package/dist/generators/convert_library_to_md.py +1441 -0
- package/dist/generators/extract_library.py +125 -0
- package/dist/generators/generate_connectors_ts.py +936 -0
- package/dist/generators/generate_library_json.py +2527 -0
- package/dist/generators/generate_triggers_ts.py +110 -0
- package/dist/generators/materialize_registry.py +155 -0
- package/dist/generators/prepare_connector.py +1688 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +35 -0
- package/dist/ixp-definition.json +1982 -0
- package/dist/library.d.ts +6 -0
- package/dist/library.js +6 -0
- package/dist/load.d.ts +16 -0
- package/dist/load.js +95 -0
- package/dist/merge-cli.d.ts +3 -0
- package/dist/merge-cli.js +47 -0
- package/dist/merge.d.ts +125 -0
- package/dist/merge.js +0 -0
- package/dist/migrate.d.ts +15 -0
- package/dist/migrate.js +245 -0
- package/dist/node-runtime.d.ts +3 -0
- package/dist/node-runtime.js +22 -0
- package/dist/package-name.d.ts +17 -0
- package/dist/package-name.js +17 -0
- package/dist/ref-resolve.d.ts +77 -0
- package/dist/ref-resolve.js +170 -0
- package/dist/registry/cache.d.ts +55 -0
- package/dist/registry/cache.js +134 -0
- package/dist/registry/curated-overlay.d.ts +16 -0
- package/dist/registry/curated-overlay.js +62 -0
- package/dist/registry/extractor.d.ts +22 -0
- package/dist/registry/extractor.js +18 -0
- package/dist/registry/materialize-bin.d.ts +2 -0
- package/dist/registry/materialize-bin.js +7 -0
- package/dist/registry/materialize-cli.d.ts +1 -0
- package/dist/registry/materialize-cli.js +46 -0
- package/dist/registry/materializer.d.ts +23 -0
- package/dist/registry/materializer.js +34 -0
- package/dist/registry/path-cli.d.ts +1 -0
- package/dist/registry/path-cli.js +56 -0
- package/dist/registry/prepare-cli.d.ts +62 -0
- package/dist/registry/prepare-cli.js +185 -0
- package/dist/registry/pull-cli.d.ts +1 -0
- package/dist/registry/pull-cli.js +274 -0
- package/dist/registry/python.d.ts +26 -0
- package/dist/registry/python.js +46 -0
- package/dist/registry/search-cli.d.ts +52 -0
- package/dist/registry/search-cli.js +159 -0
- package/dist/schema-version.d.ts +165 -0
- package/dist/schema-version.js +148 -0
- package/dist/serialize.d.ts +64 -0
- package/dist/serialize.js +6398 -0
- package/lib/README.md +112 -0
- package/lib/library-json.samples/index.json +158 -0
- package/lib/library-json.samples/invariant-collections.json +83 -0
- package/lib/library-json.samples/list-envelopes.json +123 -0
- package/lib/library-json.samples/uipath-atlassian-jira/create-issue@1.0.0.json +131 -0
- package/lib/library-json.samples/uipath-atlassian-jira/create-issue@1.0.0.v1def.json +306 -0
- package/lib/library-json.samples/uipath-atlassian-jira/generic/create_issue/create@1.0.0.json +86 -0
- package/lib/library-json.samples/uipath-atlassian-jira/generic/create_issue/create@1.0.0.v1def.json +208 -0
- package/lib/library-json.samples/uipath-atlassian-jira/get-issue@1.0.0.json +2040 -0
- package/lib/library-json.samples/uipath-atlassian-jira/get-issue@1.0.0.v1def.json +195 -0
- package/lib/library-json.samples/uipath-google-vertex/execute-google-vertex-agent@1.0.0.json +169 -0
- package/lib/library-json.samples/uipath-google-vertex/execute-google-vertex-agent@1.0.0.v1def.json +209 -0
- package/lib/library-json.samples/uipath-http-webhook/event-http-webhook@1.0.0.json +63 -0
- package/lib/library-json.samples/uipath-http-webhook/event-http-webhook@1.0.0.v1def.json +262 -0
- package/lib/library-json.samples/uipath-http-webhook/trigger-http-webhook@1.0.0.json +63 -0
- package/lib/library-json.samples/uipath-http-webhook/trigger-http-webhook@1.0.0.v1def.json +213 -0
- package/lib/library-json.samples/uipath-microsoft-azureactivedirectory/list-groups@1.0.0.json +227 -0
- package/lib/library-json.samples/uipath-microsoft-azureactivedirectory/list-groups@1.0.0.v1def.json +373 -0
- package/lib/library-json.samples/uipath-microsoft-onedrive/event-file-created@1.0.0.json +116 -0
- package/lib/library-json.samples/uipath-microsoft-onedrive/event-file-created@1.0.0.v1def.json +369 -0
- package/lib/library-json.samples/uipath-microsoft-onedrive/event-list-item-added@1.0.0.json +56 -0
- package/lib/library-json.samples/uipath-microsoft-onedrive/event-list-item-added@1.0.0.v1def.json +256 -0
- package/lib/library-json.samples/uipath-microsoft-onedrive/trigger-file-created@1.0.0.json +116 -0
- package/lib/library-json.samples/uipath-microsoft-onedrive/trigger-file-created@1.0.0.v1def.json +320 -0
- package/lib/library-json.samples/uipath-microsoft-onedrive/trigger-list-item-added@1.0.0.json +56 -0
- package/lib/library-json.samples/uipath-microsoft-onedrive/trigger-list-item-added@1.0.0.v1def.json +207 -0
- package/lib/library-json.samples/uipath-microsoft-outlook365/event-email-received@1.0.0.json +107 -0
- package/lib/library-json.samples/uipath-microsoft-outlook365/event-email-received@1.0.0.v1def.json +264 -0
- package/lib/library-json.samples/uipath-microsoft-outlook365/send-email@1.0.0.json +142 -0
- package/lib/library-json.samples/uipath-microsoft-outlook365/send-email@1.0.0.v1def.json +306 -0
- package/lib/library-json.samples/uipath-microsoft-outlook365/trigger-email-received@1.0.0.json +107 -0
- package/lib/library-json.samples/uipath-microsoft-outlook365/trigger-email-received@1.0.0.v1def.json +217 -0
- package/lib/library-json.samples/uipath-microsoft-powerautomate/invoke-a-microsoft-power-automate-flow@1.0.0.json +85 -0
- package/lib/library-json.samples/uipath-microsoft-powerautomate/invoke-a-microsoft-power-automate-flow@1.0.0.v1def.json +209 -0
- package/lib/library-json.samples/uipath-salesforce-slack/send-message-to-user@1.0.0.json +755 -0
- package/lib/library-json.samples/uipath-salesforce-slack/send-message-to-user@1.0.0.v1def.json +513 -0
- package/lib/library-json.samples/uipath-uipath-dataservice/query-entity-records@1.0.0.json +122 -0
- package/lib/library-json.samples/uipath-uipath-dataservice/query-entity-records@1.0.0.v1def.json +227 -0
- package/package.json +85 -0
|
@@ -0,0 +1,1170 @@
|
|
|
1
|
+
import { CASE_FORMAT_PROFILE, CASE_SCHEMA_VERSIONS } from './format-profile.js';
|
|
2
|
+
import { preserveSchemaVersion } from '../schema-version.js';
|
|
3
|
+
const TASK_KIND_METHOD = {
|
|
4
|
+
process: 'process',
|
|
5
|
+
agent: 'agent',
|
|
6
|
+
rpa: 'rpa',
|
|
7
|
+
'api-workflow': 'apiWorkflow',
|
|
8
|
+
'case-management': 'caseManagement',
|
|
9
|
+
'flow-process': 'flowProcess',
|
|
10
|
+
};
|
|
11
|
+
function str(value) {
|
|
12
|
+
return JSON.stringify(value).replace(/[\u2028\u2029]/g, (char) => `\\u${char.charCodeAt(0).toString(16)}`);
|
|
13
|
+
}
|
|
14
|
+
function boolLit(value) {
|
|
15
|
+
return value ? 'true' : 'false';
|
|
16
|
+
}
|
|
17
|
+
function key(value) {
|
|
18
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(value) ? value : str(value);
|
|
19
|
+
}
|
|
20
|
+
function literal(value) {
|
|
21
|
+
const rendered = JSON.stringify(value);
|
|
22
|
+
if (rendered === undefined)
|
|
23
|
+
throw new Error('cannot decompile a non-JSON value.');
|
|
24
|
+
return rendered.replace(/[\u2028\u2029]/g, (char) => `\\u${char.charCodeAt(0).toString(16)}`);
|
|
25
|
+
}
|
|
26
|
+
function opts(entries) {
|
|
27
|
+
const parts = entries.filter((entry) => entry[1] !== undefined)
|
|
28
|
+
.map(([name, value]) => `${key(name)}: ${value}`);
|
|
29
|
+
return parts.length ? `{ ${parts.join(', ')} }` : '';
|
|
30
|
+
}
|
|
31
|
+
class Emit {
|
|
32
|
+
lines = [];
|
|
33
|
+
line(depth, text) {
|
|
34
|
+
this.lines.push(' '.repeat(depth) + text);
|
|
35
|
+
}
|
|
36
|
+
toString() {
|
|
37
|
+
return this.lines.join('\n');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function nodeType(node) {
|
|
41
|
+
return String(node.type ?? '');
|
|
42
|
+
}
|
|
43
|
+
function isStage(node) {
|
|
44
|
+
return nodeType(node).endsWith('Stage') && !isTrigger(node);
|
|
45
|
+
}
|
|
46
|
+
function isTrigger(node) {
|
|
47
|
+
return nodeType(node) === 'case-management:Trigger' || nodeType(node) === 'uipath.case.trigger';
|
|
48
|
+
}
|
|
49
|
+
function addSlaRefs(slas, refs) {
|
|
50
|
+
for (const sla of slas ?? []) {
|
|
51
|
+
if (sla.id && sla.displayName)
|
|
52
|
+
refs.slaNameById.set(sla.id, sla.displayName);
|
|
53
|
+
for (const escalation of sla.escalationRule ?? []) {
|
|
54
|
+
if (escalation.id && escalation.displayName)
|
|
55
|
+
refs.escalationNameById.set(escalation.id, escalation.displayName);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function buildRefs(plan, library, diagnostics) {
|
|
60
|
+
const refs = {
|
|
61
|
+
stageLabelById: new Map(),
|
|
62
|
+
taskNameById: new Map(),
|
|
63
|
+
slaNameById: new Map(),
|
|
64
|
+
escalationNameById: new Map(),
|
|
65
|
+
bindingById: new Map(),
|
|
66
|
+
library,
|
|
67
|
+
diagnostics,
|
|
68
|
+
};
|
|
69
|
+
for (const binding of plan.bindings ?? [])
|
|
70
|
+
if (binding.id)
|
|
71
|
+
refs.bindingById.set(binding.id, binding);
|
|
72
|
+
addSlaRefs(plan.metadata?.slaRules, refs);
|
|
73
|
+
for (const node of plan.nodes ?? []) {
|
|
74
|
+
if (!isStage(node))
|
|
75
|
+
continue;
|
|
76
|
+
if (node.id && node.data?.label)
|
|
77
|
+
refs.stageLabelById.set(node.id, node.data.label);
|
|
78
|
+
addSlaRefs(node.data?.slaRules, refs);
|
|
79
|
+
for (const lane of node.data?.tasks ?? [])
|
|
80
|
+
for (const task of lane)
|
|
81
|
+
if (task.id && task.displayName)
|
|
82
|
+
refs.taskNameById.set(task.id, task.displayName);
|
|
83
|
+
}
|
|
84
|
+
return refs;
|
|
85
|
+
}
|
|
86
|
+
function diagnose(refs, diagnostic) {
|
|
87
|
+
if (!refs.diagnostics)
|
|
88
|
+
throw new Error(diagnostic.message);
|
|
89
|
+
refs.diagnostics.push(diagnostic);
|
|
90
|
+
}
|
|
91
|
+
function contextMap(bag) {
|
|
92
|
+
return new Map((bag?.context ?? []).map((row) => [String(row.name ?? ''), row]));
|
|
93
|
+
}
|
|
94
|
+
function waitConnectorSpec(bag, owner, refs) {
|
|
95
|
+
if (!bag || bag.serviceType !== CASE_FORMAT_PROFILE.waitConnectorServiceType)
|
|
96
|
+
throw new Error(`${owner} has an unsupported wait-for-connector subscription.`);
|
|
97
|
+
const allowed = new Set(['serviceType', 'context', 'inputs', 'outputs', 'bindings']);
|
|
98
|
+
if (Object.keys(bag).some((name) => !allowed.has(name)))
|
|
99
|
+
throw new Error(`${owner} has an unsupported wait-for-connector subscription field.`);
|
|
100
|
+
if (bag.context === undefined) {
|
|
101
|
+
const hasResolvedFields = ['inputs', 'outputs', 'bindings'].some((name) => {
|
|
102
|
+
const value = bag[name];
|
|
103
|
+
return Array.isArray(value) && value.length > 0;
|
|
104
|
+
});
|
|
105
|
+
if (!hasResolvedFields)
|
|
106
|
+
return undefined;
|
|
107
|
+
return resolvedSubscriptionSource(bag, owner, 'event', refs);
|
|
108
|
+
}
|
|
109
|
+
const context = contextMap(bag);
|
|
110
|
+
const isPlaceholder = bag.context.length === 2 && context.size === 2
|
|
111
|
+
&& context.has('connectorKey') && context.has('operation')
|
|
112
|
+
&& ['inputs', 'outputs', 'bindings'].every((name) => {
|
|
113
|
+
const value = bag[name];
|
|
114
|
+
return value === undefined || (Array.isArray(value) && value.length === 0);
|
|
115
|
+
});
|
|
116
|
+
const connectorKey = context.get('connectorKey')?.value;
|
|
117
|
+
const operation = context.get('operation')?.value;
|
|
118
|
+
if (typeof connectorKey !== 'string' || typeof operation !== 'string')
|
|
119
|
+
throw new Error(`${owner} has an incomplete wait-for-connector subscription context.`);
|
|
120
|
+
if (isPlaceholder) {
|
|
121
|
+
if (connectorKey === 'placeholder' && operation === 'placeholder')
|
|
122
|
+
return '{}';
|
|
123
|
+
return opts([
|
|
124
|
+
['connectorKey', str(connectorKey)],
|
|
125
|
+
['operation', str(operation)],
|
|
126
|
+
]);
|
|
127
|
+
}
|
|
128
|
+
return resolvedSubscriptionSource(bag, owner, 'event', refs);
|
|
129
|
+
}
|
|
130
|
+
function encodedSubscriptionConfig(context, owner) {
|
|
131
|
+
const encoded = context.get('metadata')?.body?.activityPropertyConfiguration?.configuration;
|
|
132
|
+
if (typeof encoded !== 'string' || !encoded.startsWith('=jsonString:'))
|
|
133
|
+
throw new Error(`${owner} has missing or malformed subscription metadata configuration.`);
|
|
134
|
+
try {
|
|
135
|
+
return JSON.parse(encoded.slice('=jsonString:'.length))?.essentialConfiguration ?? {};
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
throw new Error(`${owner} has malformed subscription metadata configuration JSON.`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function subscriptionWhere(bag, owner) {
|
|
142
|
+
const inputs = bag.inputs;
|
|
143
|
+
if (!Array.isArray(inputs) || inputs.length !== 1)
|
|
144
|
+
throw new Error(`${owner} must carry exactly one resolved subscription input.`);
|
|
145
|
+
const row = inputs[0];
|
|
146
|
+
if (row?.name !== 'body')
|
|
147
|
+
throw new Error(`${owner} has an unsupported subscription input.`);
|
|
148
|
+
if (row.value === '' && row.body === undefined)
|
|
149
|
+
return undefined;
|
|
150
|
+
const queryParams = row.body?.queryParams;
|
|
151
|
+
if (!queryParams || typeof queryParams !== 'object' || Array.isArray(queryParams))
|
|
152
|
+
throw new Error(`${owner} has an unsupported subscription body.`);
|
|
153
|
+
const entries = Object.entries(queryParams);
|
|
154
|
+
if (entries.some(([, value]) => typeof value !== 'string'))
|
|
155
|
+
throw new Error(`${owner} has a non-string subscription parameter.`);
|
|
156
|
+
return Object.fromEntries(entries);
|
|
157
|
+
}
|
|
158
|
+
function subscriptionFilters(config, owner) {
|
|
159
|
+
const tree = config?.filter;
|
|
160
|
+
if (tree === null || tree === undefined)
|
|
161
|
+
return undefined;
|
|
162
|
+
if (!Array.isArray(tree.filters) || (tree.groups?.length ?? 0) !== 0)
|
|
163
|
+
throw new Error(`${owner} has an unsupported nested subscription filter.`);
|
|
164
|
+
const filters = tree.filters.map((entry) => {
|
|
165
|
+
if (entry?.operator !== 'Contains' || typeof entry.id !== 'string' || typeof entry.value?.value !== 'string')
|
|
166
|
+
throw new Error(`${owner} has an unsupported subscription filter operator.`);
|
|
167
|
+
return { field: entry.id, contains: entry.value.value };
|
|
168
|
+
});
|
|
169
|
+
return filters.length ? filters : undefined;
|
|
170
|
+
}
|
|
171
|
+
function resolvedSubscriptionSource(bag, owner, kind, refs) {
|
|
172
|
+
if (!refs.library)
|
|
173
|
+
throw new Error(`${owner} needs the connector library; pass DecompileOptions.library or --library.`);
|
|
174
|
+
const context = contextMap(bag);
|
|
175
|
+
const connectorKey = context.get('connectorKey')?.value;
|
|
176
|
+
const operation = context.get('operation')?.value;
|
|
177
|
+
const objectName = context.get('objectName')?.value;
|
|
178
|
+
if ([connectorKey, operation, objectName].some((value) => typeof value !== 'string'))
|
|
179
|
+
throw new Error(`${owner} has an incomplete resolved subscription context.`);
|
|
180
|
+
const connection = bindingName(context.get('connection')?.value, refs);
|
|
181
|
+
const folder = bindingName(context.get('folderKey')?.value, refs);
|
|
182
|
+
if (context.has('connection') && connection === undefined)
|
|
183
|
+
throw new Error(`${owner} connection must reference a root binding.`);
|
|
184
|
+
if (context.has('folderKey') && folder === undefined)
|
|
185
|
+
throw new Error(`${owner} folderKey must reference a root binding.`);
|
|
186
|
+
const config = encodedSubscriptionConfig(context, owner);
|
|
187
|
+
const identity = refs.library.identifyEvent(kind, {
|
|
188
|
+
connectorKey: String(connectorKey),
|
|
189
|
+
objectName: String(objectName),
|
|
190
|
+
eventOperation: String(operation),
|
|
191
|
+
eventMode: context.get('metadata')?.body?.activityPropertyConfiguration?.eventMode,
|
|
192
|
+
version: config.packageVersion,
|
|
193
|
+
});
|
|
194
|
+
const where = subscriptionWhere(bag, owner);
|
|
195
|
+
const filters = subscriptionFilters(config, owner);
|
|
196
|
+
return opts([
|
|
197
|
+
['connector', str(identity.key)],
|
|
198
|
+
['event', str(identity.event)],
|
|
199
|
+
['where', where !== undefined ? literal(where) : undefined],
|
|
200
|
+
['filters', filters !== undefined ? literal(filters) : undefined],
|
|
201
|
+
['connection', connection !== undefined ? str(connection) : undefined],
|
|
202
|
+
['folder', folder !== undefined ? str(folder) : undefined],
|
|
203
|
+
['version', identity.version !== '1.0.0' ? str(identity.version) : undefined],
|
|
204
|
+
]);
|
|
205
|
+
}
|
|
206
|
+
function emitRule(rule, refs) {
|
|
207
|
+
const entries = [];
|
|
208
|
+
const stageIds = rule.selectedStageIds ?? (rule.selectedStageId !== undefined ? [rule.selectedStageId] : undefined);
|
|
209
|
+
if (stageIds !== undefined) {
|
|
210
|
+
if (stageIds.length !== 1)
|
|
211
|
+
throw new Error(`rule "${rule.rule}" selects ${stageIds.length} stages; multi-stage selection is not authorable.`);
|
|
212
|
+
let label = refs.stageLabelById.get(stageIds[0]);
|
|
213
|
+
if (label === undefined) {
|
|
214
|
+
const message = `rule "${rule.rule}" references unknown stage id "${stageIds[0]}".`;
|
|
215
|
+
diagnose(refs, {
|
|
216
|
+
code: 'UNKNOWN_STAGE_REFERENCE',
|
|
217
|
+
message,
|
|
218
|
+
location: `rule:${rule.id ?? rule.rule}`,
|
|
219
|
+
});
|
|
220
|
+
label = `__UNRESOLVED_STAGE_ID__:${stageIds[0]}`;
|
|
221
|
+
}
|
|
222
|
+
entries.push(['stage', str(label)]);
|
|
223
|
+
}
|
|
224
|
+
if (rule.selectedTasksIds !== undefined) {
|
|
225
|
+
const names = rule.selectedTasksIds.map((id) => {
|
|
226
|
+
const name = refs.taskNameById.get(id);
|
|
227
|
+
if (name !== undefined)
|
|
228
|
+
return str(name);
|
|
229
|
+
const message = `rule "${rule.rule}" references unknown task id "${id}".`;
|
|
230
|
+
diagnose(refs, {
|
|
231
|
+
code: 'UNKNOWN_TASK_REFERENCE',
|
|
232
|
+
message,
|
|
233
|
+
location: `rule:${rule.id ?? rule.rule}`,
|
|
234
|
+
});
|
|
235
|
+
return str(`__UNRESOLVED_TASK_ID__:${id}`);
|
|
236
|
+
});
|
|
237
|
+
entries.push(['tasks', `[${names.join(', ')}]`]);
|
|
238
|
+
}
|
|
239
|
+
if (rule.rule === 'wait-for-connector') {
|
|
240
|
+
const connector = waitConnectorSpec(rule.uipath, `rule "${rule.rule}"`, refs);
|
|
241
|
+
if (connector && connector !== '{}')
|
|
242
|
+
entries.push(['connector', connector]);
|
|
243
|
+
}
|
|
244
|
+
if (rule.rule === 'sla-status-change') {
|
|
245
|
+
const sla = rule.slaId && refs.slaNameById.get(rule.slaId);
|
|
246
|
+
if (!sla)
|
|
247
|
+
throw new Error(`sla-status-change rule references unnamed or unknown SLA id "${rule.slaId ?? ''}".`);
|
|
248
|
+
entries.push(['sla', str(sla)]);
|
|
249
|
+
if (rule.escalationId !== undefined) {
|
|
250
|
+
const escalation = refs.escalationNameById.get(rule.escalationId);
|
|
251
|
+
if (!escalation)
|
|
252
|
+
throw new Error(`sla-status-change rule references unnamed or unknown escalation id "${rule.escalationId}".`);
|
|
253
|
+
entries.push(['escalation', str(escalation)]);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (rule.conditionExpression !== undefined)
|
|
257
|
+
entries.push(['expression', str(rule.conditionExpression)]);
|
|
258
|
+
const options = opts(entries);
|
|
259
|
+
return options ? `rule(${str(rule.rule)}, ${options})` : `rule(${str(rule.rule)})`;
|
|
260
|
+
}
|
|
261
|
+
function emitRuleArg(condition, refs) {
|
|
262
|
+
const groups = condition.rules ?? [];
|
|
263
|
+
const rendered = groups.map((group) => group.map((rule) => emitRule(rule, refs)));
|
|
264
|
+
if (rendered.length === 1)
|
|
265
|
+
return rendered[0].length === 1 ? rendered[0][0] : `[${rendered[0].join(', ')}]`;
|
|
266
|
+
return `[${rendered.map((group) => `[${group.join(', ')}]`).join(', ')}]`;
|
|
267
|
+
}
|
|
268
|
+
function emitEntryWhen(out, depth, condition, refs, stage) {
|
|
269
|
+
// Emit the name whenever the document carries one. This used to drop it when it
|
|
270
|
+
// equalled the serializer's old constant default ("Entry rule"), which was safe
|
|
271
|
+
// only while that default was a constant: now that an omitted name is DERIVED
|
|
272
|
+
// from the owner, dropping it would make decompile → compile rename the rule.
|
|
273
|
+
// Round-trip identity over the existing corpus beats a tidier generated source.
|
|
274
|
+
const displayName = condition.displayName ? str(condition.displayName) : undefined;
|
|
275
|
+
const options = opts([
|
|
276
|
+
['displayName', displayName],
|
|
277
|
+
['isInterrupting', stage && condition.isInterrupting !== undefined ? boolLit(condition.isInterrupting) : undefined],
|
|
278
|
+
]);
|
|
279
|
+
const arg = emitRuleArg(condition, refs);
|
|
280
|
+
out.line(depth, options ? `.entryWhen(${arg}, ${options})` : `.entryWhen(${arg})`);
|
|
281
|
+
}
|
|
282
|
+
function emitExitWhen(out, depth, condition, refs, selectNextStage) {
|
|
283
|
+
// See emitEntryWhen: the name is preserved verbatim so the round trip is exact.
|
|
284
|
+
const displayName = condition.displayName ? str(condition.displayName) : undefined;
|
|
285
|
+
let exitToStage;
|
|
286
|
+
if (condition.exitToStageId !== undefined) {
|
|
287
|
+
let label = refs.stageLabelById.get(condition.exitToStageId);
|
|
288
|
+
if (label === undefined) {
|
|
289
|
+
const message = `exit condition routes to unknown stage id "${condition.exitToStageId}".`;
|
|
290
|
+
diagnose(refs, {
|
|
291
|
+
code: 'UNKNOWN_EXIT_TARGET',
|
|
292
|
+
message,
|
|
293
|
+
location: `condition:${condition.id ?? condition.displayName ?? 'exit'}`,
|
|
294
|
+
});
|
|
295
|
+
label = `__UNRESOLVED_STAGE_ID__:${condition.exitToStageId}`;
|
|
296
|
+
}
|
|
297
|
+
exitToStage = str(label);
|
|
298
|
+
}
|
|
299
|
+
const options = opts([
|
|
300
|
+
['displayName', displayName],
|
|
301
|
+
['marksStageComplete', condition.marksStageComplete !== undefined ? boolLit(condition.marksStageComplete) : undefined],
|
|
302
|
+
['type', condition.type !== undefined ? str(condition.type) : undefined],
|
|
303
|
+
['exitToStage', exitToStage],
|
|
304
|
+
['selectNextStage', selectNextStage],
|
|
305
|
+
]);
|
|
306
|
+
const arg = emitRuleArg(condition, refs);
|
|
307
|
+
out.line(depth, options ? `.exitWhen(${arg}, ${options})` : `.exitWhen(${arg})`);
|
|
308
|
+
}
|
|
309
|
+
function emitCompleteWhen(out, depth, condition, refs) {
|
|
310
|
+
// See emitEntryWhen: the name is preserved verbatim so the round trip is exact.
|
|
311
|
+
const displayName = condition.displayName
|
|
312
|
+
? str(condition.displayName)
|
|
313
|
+
: undefined;
|
|
314
|
+
const options = opts([
|
|
315
|
+
['displayName', displayName],
|
|
316
|
+
['marksCaseComplete', condition.marksCaseComplete === false ? 'false' : undefined],
|
|
317
|
+
]);
|
|
318
|
+
const arg = emitRuleArg(condition, refs);
|
|
319
|
+
out.line(depth, options ? `.completeWhen(${arg}, ${options})` : `.completeWhen(${arg})`);
|
|
320
|
+
}
|
|
321
|
+
function actionFields(rows) {
|
|
322
|
+
if (!rows?.length)
|
|
323
|
+
return undefined;
|
|
324
|
+
return `[${rows.map((row) => opts([
|
|
325
|
+
['name', str(row.name ?? '')],
|
|
326
|
+
['type', row.type && row.type !== 'string' ? str(row.type) : undefined],
|
|
327
|
+
['subType', row.subType !== undefined ? str(row.subType) : undefined],
|
|
328
|
+
['displayName', row.displayName !== undefined ? str(row.displayName) : undefined],
|
|
329
|
+
['value', row.value !== undefined ? literal(row.value) : undefined],
|
|
330
|
+
['required', row.required !== undefined ? literal(row.required) : undefined],
|
|
331
|
+
['options', row.options !== undefined ? literal(row.options) : undefined],
|
|
332
|
+
])).join(', ')}]`;
|
|
333
|
+
}
|
|
334
|
+
function bindingName(value, refs) {
|
|
335
|
+
if (typeof value !== 'string' || !value.startsWith('=bindings.'))
|
|
336
|
+
return undefined;
|
|
337
|
+
const id = value.slice('=bindings.'.length);
|
|
338
|
+
return refs.bindingById.get(id)?.id ?? id;
|
|
339
|
+
}
|
|
340
|
+
function bindingDefault(value, refs) {
|
|
341
|
+
if (typeof value !== 'string' || !value.startsWith('=bindings.'))
|
|
342
|
+
return value;
|
|
343
|
+
const id = value.slice('=bindings.'.length);
|
|
344
|
+
return refs.bindingById.get(id)?.default ?? value;
|
|
345
|
+
}
|
|
346
|
+
function selectNextStageByExit(plan, refs) {
|
|
347
|
+
const raw = plan.metadata?.waitForHumanSelectNextStageDFConnector;
|
|
348
|
+
if (raw === undefined)
|
|
349
|
+
return new Map();
|
|
350
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw))
|
|
351
|
+
throw new Error('waitForHumanSelectNextStageDFConnector must be a connector object.');
|
|
352
|
+
const connector = raw;
|
|
353
|
+
const allowed = new Set(['id', 'elementId', 'type', 'displayName', 'shouldRunOnlyOnce', 'data']);
|
|
354
|
+
const unknown = Object.keys(connector).find((name) => !allowed.has(name));
|
|
355
|
+
if (unknown)
|
|
356
|
+
throw new Error(`select-next-stage connector has unsupported field "${unknown}".`);
|
|
357
|
+
if (connector.id !== 'SelectNextStageConnector'
|
|
358
|
+
|| connector.elementId !== 'root-SelectNextStageConnector'
|
|
359
|
+
|| connector.type !== 'wait-for-connector'
|
|
360
|
+
|| connector.displayName !== 'Select next stage'
|
|
361
|
+
|| connector.shouldRunOnlyOnce !== false)
|
|
362
|
+
throw new Error('select-next-stage connector has unsupported identity or task settings.');
|
|
363
|
+
const data = connector.data;
|
|
364
|
+
if (!data || typeof data !== 'object' || Array.isArray(data) || data.serviceType !== CASE_FORMAT_PROFILE.waitConnectorServiceType)
|
|
365
|
+
throw new Error('select-next-stage connector has an unsupported data envelope.');
|
|
366
|
+
const dataAllowed = new Set(['serviceType', 'context', 'inputs', 'outputs', 'bindings']);
|
|
367
|
+
const unknownData = Object.keys(data).find((name) => !dataAllowed.has(name));
|
|
368
|
+
if (unknownData)
|
|
369
|
+
throw new Error(`select-next-stage connector data has unsupported field "${unknownData}".`);
|
|
370
|
+
for (const name of ['inputs', 'outputs', 'bindings'])
|
|
371
|
+
if (!Array.isArray(data[name]) || data[name].length !== 0)
|
|
372
|
+
throw new Error(`select-next-stage connector data.${name} must be an empty array.`);
|
|
373
|
+
if (!Array.isArray(data.context))
|
|
374
|
+
throw new Error('select-next-stage connector data.context must be an array.');
|
|
375
|
+
const context = contextMap({ context: data.context });
|
|
376
|
+
if (context.size !== data.context.length)
|
|
377
|
+
throw new Error('select-next-stage connector context names must be unique.');
|
|
378
|
+
const contextAllowed = new Set(['connectorKey', 'connection', 'resourceKey', 'folderKey', 'objectName', 'operation']);
|
|
379
|
+
const unknownContext = [...context.keys()].find((name) => !contextAllowed.has(name));
|
|
380
|
+
if (unknownContext)
|
|
381
|
+
throw new Error(`select-next-stage connector has unsupported context "${unknownContext}".`);
|
|
382
|
+
const objectName = context.get('objectName')?.value;
|
|
383
|
+
if (typeof objectName !== 'string' || !objectName.trim())
|
|
384
|
+
throw new Error('select-next-stage connector requires a non-empty objectName context value.');
|
|
385
|
+
const connectorKey = context.get('connectorKey')?.value;
|
|
386
|
+
const operation = context.get('operation')?.value;
|
|
387
|
+
const connection = bindingName(context.get('connection')?.value, refs);
|
|
388
|
+
const folder = bindingName(context.get('folderKey')?.value, refs);
|
|
389
|
+
const hasConnectorFields = connectorKey !== undefined || operation !== undefined
|
|
390
|
+
|| context.has('connection') || context.has('resourceKey') || context.has('folderKey');
|
|
391
|
+
let connectorSource;
|
|
392
|
+
if (hasConnectorFields) {
|
|
393
|
+
if (typeof connectorKey !== 'string' || typeof operation !== 'string')
|
|
394
|
+
throw new Error('select-next-stage full connector requires string connectorKey and operation context values.');
|
|
395
|
+
if (context.has('connection') !== context.has('resourceKey'))
|
|
396
|
+
throw new Error('select-next-stage connector connection and resourceKey context values must appear together.');
|
|
397
|
+
if (context.has('connection') && connection === undefined)
|
|
398
|
+
throw new Error('select-next-stage connector connection must reference a root binding.');
|
|
399
|
+
if (context.has('folderKey') && folder === undefined)
|
|
400
|
+
throw new Error('select-next-stage connector folderKey must reference a root binding.');
|
|
401
|
+
connectorSource = opts([
|
|
402
|
+
['connectorKey', str(connectorKey)],
|
|
403
|
+
['operation', str(operation)],
|
|
404
|
+
['connection', connection !== undefined ? str(connection) : undefined],
|
|
405
|
+
['folder', folder !== undefined ? str(folder) : undefined],
|
|
406
|
+
]);
|
|
407
|
+
}
|
|
408
|
+
const exits = (plan.nodes ?? []).filter(isStage)
|
|
409
|
+
.flatMap((stage) => stage.data?.exitConditions ?? [])
|
|
410
|
+
.filter((condition) => condition.type === 'wait-for-user');
|
|
411
|
+
if (exits.length !== 1)
|
|
412
|
+
throw new Error(`select-next-stage connector requires exactly one wait-for-user exit; found ${exits.length}.`);
|
|
413
|
+
return new Map([[exits[0], opts([
|
|
414
|
+
['objectName', str(objectName)],
|
|
415
|
+
['connector', connectorSource],
|
|
416
|
+
])]]);
|
|
417
|
+
}
|
|
418
|
+
function connectorVersion(context) {
|
|
419
|
+
const metadata = context.get('metadata')?.body;
|
|
420
|
+
const encoded = metadata?.activityPropertyConfiguration?.configuration;
|
|
421
|
+
if (typeof encoded === 'string' && encoded.startsWith('=jsonString:')) {
|
|
422
|
+
try {
|
|
423
|
+
return JSON.parse(encoded.slice('=jsonString:'.length))?.essentialConfiguration?.packageVersion;
|
|
424
|
+
}
|
|
425
|
+
catch {
|
|
426
|
+
throw new Error('connector task has malformed activity metadata configuration JSON.');
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
const activity = metadata?.activityMetadata?.activity?.configuration;
|
|
430
|
+
if (typeof activity === 'string') {
|
|
431
|
+
try {
|
|
432
|
+
return JSON.parse(activity)?.version;
|
|
433
|
+
}
|
|
434
|
+
catch {
|
|
435
|
+
throw new Error('connector task has malformed activity configuration JSON.');
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
return undefined;
|
|
439
|
+
}
|
|
440
|
+
function toCamel(value) {
|
|
441
|
+
return value.replace(/_([a-z0-9])/gi, (_match, char) => String(char).toUpperCase());
|
|
442
|
+
}
|
|
443
|
+
function connectorCall(task, refs, library) {
|
|
444
|
+
if (!library)
|
|
445
|
+
throw new Error(`connector task "${task.displayName ?? task.id ?? '?'}" needs the connector library; pass DecompileOptions.library or --library.`);
|
|
446
|
+
const data = task.data ?? {};
|
|
447
|
+
const context = contextMap({ context: data.context });
|
|
448
|
+
const connectorKey = context.get('connectorKey')?.value;
|
|
449
|
+
const method = context.get('method')?.value;
|
|
450
|
+
const endpoint = context.get('path')?.value;
|
|
451
|
+
const objectName = context.get('objectName')?.value;
|
|
452
|
+
if ([connectorKey, method, endpoint, objectName].some((value) => typeof value !== 'string'))
|
|
453
|
+
throw new Error(`connector task "${task.displayName ?? task.id ?? '?'}" has an incomplete resolved context.`);
|
|
454
|
+
const identity = library.identifyAction({
|
|
455
|
+
connectorKey: String(connectorKey),
|
|
456
|
+
httpMethod: String(method),
|
|
457
|
+
endpoint: String(endpoint),
|
|
458
|
+
objectName: String(objectName),
|
|
459
|
+
version: connectorVersion(context),
|
|
460
|
+
});
|
|
461
|
+
const resolved = library.resolve(identity.key, identity.action, identity.version, identity.object);
|
|
462
|
+
const parameterByWireName = new Map();
|
|
463
|
+
for (const parameter of resolved.entry.operation?.parameters ?? [])
|
|
464
|
+
parameterByWireName.set(toCamel(String(parameter.name)), String(parameter.name));
|
|
465
|
+
const inputs = {};
|
|
466
|
+
for (const slot of Array.isArray(data.inputs) ? data.inputs : []) {
|
|
467
|
+
const body = slot?.body;
|
|
468
|
+
if (!body || typeof body !== 'object' || Array.isArray(body))
|
|
469
|
+
continue;
|
|
470
|
+
for (const [wireName, value] of Object.entries(body)) {
|
|
471
|
+
const authoredName = slot.name === 'body' ? wireName : (parameterByWireName.get(wireName) ?? wireName);
|
|
472
|
+
inputs[authoredName] = value;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
const connection = bindingName(context.get('connection')?.value, refs);
|
|
476
|
+
const folder = bindingName(context.get('folderKey')?.value, refs);
|
|
477
|
+
const options = opts([
|
|
478
|
+
['connection', connection !== undefined ? str(connection) : undefined],
|
|
479
|
+
['folder', folder !== undefined ? str(folder) : undefined],
|
|
480
|
+
['version', identity.version !== '1.0.0' ? str(identity.version) : undefined],
|
|
481
|
+
['object', identity.object !== undefined ? str(identity.object) : undefined],
|
|
482
|
+
]);
|
|
483
|
+
return `.connector(${str(identity.key)}, ${str(identity.action)}, ${literal(inputs)}${options ? `, ${options}` : ''})`;
|
|
484
|
+
}
|
|
485
|
+
function externalCall(task, refs, library) {
|
|
486
|
+
const kind = task.type;
|
|
487
|
+
const owner = `${kind} task "${task.displayName ?? task.id ?? '?'}"`;
|
|
488
|
+
if (!library)
|
|
489
|
+
throw new Error(`${owner} needs the connector library; pass DecompileOptions.library or --library.`);
|
|
490
|
+
const data = task.data ?? {};
|
|
491
|
+
const allowed = new Set(['serviceType', 'context', 'inputs', 'outputs', 'bindings']);
|
|
492
|
+
const unknownData = Object.keys(data).find((name) => !allowed.has(name));
|
|
493
|
+
if (unknownData)
|
|
494
|
+
throw new Error(`${owner} has unsupported data field "${unknownData}".`);
|
|
495
|
+
if (!Array.isArray(data.bindings) || data.bindings.length !== 0)
|
|
496
|
+
throw new Error(`${owner} data.bindings must be an empty array; bindings live at case root.`);
|
|
497
|
+
const modes = kind === 'external-agent'
|
|
498
|
+
? new Map([
|
|
499
|
+
[CASE_FORMAT_PROFILE.externalAgentSyncServiceType, 'sync'],
|
|
500
|
+
[CASE_FORMAT_PROFILE.externalAgentAsyncServiceType, 'async'],
|
|
501
|
+
])
|
|
502
|
+
: new Map([
|
|
503
|
+
[CASE_FORMAT_PROFILE.externalWorkflowSyncServiceType, 'sync'],
|
|
504
|
+
[CASE_FORMAT_PROFILE.externalWorkflowAsyncServiceType, 'async'],
|
|
505
|
+
]);
|
|
506
|
+
const mode = modes.get(data.serviceType);
|
|
507
|
+
if (!mode)
|
|
508
|
+
throw new Error(`${owner} has unsupported serviceType "${String(data.serviceType ?? '')}".`);
|
|
509
|
+
if (!Array.isArray(data.context))
|
|
510
|
+
throw new Error(`${owner} requires a resolved context array.`);
|
|
511
|
+
const expectedContext = ['activity', 'connectorKey', 'connection', 'folderKey', 'method', 'path'];
|
|
512
|
+
if (data.context.map((row) => row.name).join('\0') !== expectedContext.join('\0'))
|
|
513
|
+
throw new Error(`${owner} context must contain exactly ${expectedContext.join(', ')} in canonical order.`);
|
|
514
|
+
const context = contextMap(data);
|
|
515
|
+
const stringValue = (name) => {
|
|
516
|
+
const row = context.get(name);
|
|
517
|
+
if (row?.type !== 'string' || typeof row.value !== 'string' || !row.value)
|
|
518
|
+
throw new Error(`${owner} context ${name} must be a non-empty string.`);
|
|
519
|
+
return row.value;
|
|
520
|
+
};
|
|
521
|
+
const binding = (name) => {
|
|
522
|
+
const value = stringValue(name);
|
|
523
|
+
if (!value.startsWith('=bindings.'))
|
|
524
|
+
throw new Error(`${owner} context ${name} must reference a root binding.`);
|
|
525
|
+
const id = value.slice('=bindings.'.length);
|
|
526
|
+
if (!refs.bindingById.has(id))
|
|
527
|
+
throw new Error(`${owner} context ${name} references missing root binding "${id}".`);
|
|
528
|
+
return id;
|
|
529
|
+
};
|
|
530
|
+
const identity = library.identifyAction({
|
|
531
|
+
connectorKey: stringValue('connectorKey'),
|
|
532
|
+
httpMethod: stringValue('method'),
|
|
533
|
+
endpoint: stringValue('path'),
|
|
534
|
+
});
|
|
535
|
+
const resolved = library.resolve(identity.key, identity.action, identity.version, identity.object);
|
|
536
|
+
const expectedSubType = kind === 'external-agent' ? 'AgentExecution' : 'ProcessExecution';
|
|
537
|
+
if (resolved.entry.operation?.subType !== expectedSubType)
|
|
538
|
+
throw new Error(`${owner} resolves to ${String(resolved.entry.operation?.subType ?? 'no subtype')}, not ${expectedSubType}.`);
|
|
539
|
+
if (stringValue('activity') !== resolved.activityTypeId)
|
|
540
|
+
throw new Error(`${owner} activity does not match its descriptor.`);
|
|
541
|
+
if (!Array.isArray(data.inputs))
|
|
542
|
+
throw new Error(`${owner} inputs must be an array.`);
|
|
543
|
+
const fields = new Map(resolved.fields.map((field) => [field.name, field]));
|
|
544
|
+
const inputs = {};
|
|
545
|
+
for (const row of data.inputs) {
|
|
546
|
+
const name = String(row?.name ?? '');
|
|
547
|
+
if (!name || row.id !== name || row.target !== name || !fields.has(name))
|
|
548
|
+
throw new Error(`${owner} has unsupported input row "${name}".`);
|
|
549
|
+
inputs[name] = row.value;
|
|
550
|
+
}
|
|
551
|
+
const missing = resolved.fields.find((field) => field.required && !(field.name in inputs));
|
|
552
|
+
if (missing)
|
|
553
|
+
throw new Error(`${owner} is missing required input "${missing.name}".`);
|
|
554
|
+
if (!Array.isArray(data.outputs) || data.outputs.length !== 1)
|
|
555
|
+
throw new Error(`${owner} must carry exactly one response output.`);
|
|
556
|
+
const output = data.outputs[0];
|
|
557
|
+
if (output.name !== 'response' || output.type !== 'json' || output.source !== '.'
|
|
558
|
+
|| typeof output.var !== 'string' || output.id !== output.var)
|
|
559
|
+
throw new Error(`${owner} has an unsupported response output.`);
|
|
560
|
+
const descriptor = `{ nodeType: ${str(resolved.nodeType)}, version: ${str(resolved.version)} }`;
|
|
561
|
+
const options = opts([
|
|
562
|
+
['connection', str(binding('connection'))],
|
|
563
|
+
['folder', str(binding('folderKey'))],
|
|
564
|
+
['mode', str(mode)],
|
|
565
|
+
['inputs', literal(inputs)],
|
|
566
|
+
]);
|
|
567
|
+
const method = kind === 'external-agent' ? 'externalAgent' : 'externalWorkflow';
|
|
568
|
+
return `.${method}(${descriptor}, ${options})`;
|
|
569
|
+
}
|
|
570
|
+
function emitTaskKind(task, refs, library) {
|
|
571
|
+
const kind = task.type;
|
|
572
|
+
const data = task.data ?? {};
|
|
573
|
+
if (TASK_KIND_METHOD[kind]) {
|
|
574
|
+
if (kind !== 'flow-process' && data.name === undefined && data.folderPath === undefined) {
|
|
575
|
+
if ((Array.isArray(data.inputs) && data.inputs.length) || (Array.isArray(data.outputs) && data.outputs.length))
|
|
576
|
+
throw new Error(`unresolved ${kind} task "${task.displayName ?? task.id ?? '?'}" cannot carry input or output bindings.`);
|
|
577
|
+
return `.unresolved(${str(kind)})`;
|
|
578
|
+
}
|
|
579
|
+
const name = bindingDefault(data.name, refs);
|
|
580
|
+
const folderPath = bindingDefault(data.folderPath, refs);
|
|
581
|
+
const folder = folderPath ? `, ${opts([['folder', str(folderPath)]])}` : '';
|
|
582
|
+
return `.${TASK_KIND_METHOD[kind]}(${str(name ?? '')}${folder})`;
|
|
583
|
+
}
|
|
584
|
+
if (kind === 'action') {
|
|
585
|
+
let recipient;
|
|
586
|
+
if (data.recipient) {
|
|
587
|
+
const type = Number(data.recipient.Type);
|
|
588
|
+
if (!Number.isFinite(type))
|
|
589
|
+
throw new Error(`task recipient has a non-numeric type "${data.recipient.Type}".`);
|
|
590
|
+
recipient = type === 2
|
|
591
|
+
? str(data.recipient.Value)
|
|
592
|
+
: opts([['type', String(type)], ['value', str(data.recipient.Value)]]);
|
|
593
|
+
}
|
|
594
|
+
const spec = opts([
|
|
595
|
+
['title', data.taskTitle !== undefined ? str(data.taskTitle) : undefined],
|
|
596
|
+
['priority', data.priority && data.priority !== 'Medium' ? str(data.priority) : undefined],
|
|
597
|
+
['recipient', recipient],
|
|
598
|
+
['labels', data.labels !== undefined ? str(data.labels) : undefined],
|
|
599
|
+
['actionCatalogName', data.actionCatalogName !== undefined ? str(data.actionCatalogName) : undefined],
|
|
600
|
+
['inputs', actionFields(data.inputs)],
|
|
601
|
+
['outputs', actionFields(data.outputs)],
|
|
602
|
+
]);
|
|
603
|
+
return `.action(${spec})`;
|
|
604
|
+
}
|
|
605
|
+
if (kind === 'execute-connector-activity')
|
|
606
|
+
return connectorCall(task, refs, library);
|
|
607
|
+
if (kind === 'external-agent' || kind === 'external-workflow')
|
|
608
|
+
return externalCall(task, refs, library);
|
|
609
|
+
if (kind === 'wait-for-timer') {
|
|
610
|
+
const spec = opts([
|
|
611
|
+
['duration', data.timeDuration !== undefined ? str(data.timeDuration) : undefined],
|
|
612
|
+
['date', data.timeDate !== undefined ? str(data.timeDate) : undefined],
|
|
613
|
+
['cycle', data.timeCycle !== undefined ? str(data.timeCycle) : undefined],
|
|
614
|
+
]);
|
|
615
|
+
return `.waitForTimer(${spec})`;
|
|
616
|
+
}
|
|
617
|
+
if (kind === 'wait-for-connector') {
|
|
618
|
+
const spec = waitConnectorSpec(data, `wait-for-connector task "${task.displayName ?? task.id ?? '?'}"`, refs);
|
|
619
|
+
return spec === undefined ? '.waitForConnector()' : `.waitForConnector(${spec})`;
|
|
620
|
+
}
|
|
621
|
+
const message = `unsupported task kind "${kind}" for task "${task.displayName ?? task.id ?? '?'}".`;
|
|
622
|
+
diagnose(refs, {
|
|
623
|
+
code: 'UNSUPPORTED_TASK_KIND',
|
|
624
|
+
message,
|
|
625
|
+
location: `task:${task.id ?? task.displayName ?? '?'}`,
|
|
626
|
+
});
|
|
627
|
+
return `.__UNRESOLVED_TASK_KIND__(${str(kind)}, ${literal(data)})`;
|
|
628
|
+
}
|
|
629
|
+
function ioArg(rows, kind) {
|
|
630
|
+
const valueField = kind === 'inputs' ? 'value' : 'source';
|
|
631
|
+
const entries = rows.map((row) => {
|
|
632
|
+
const name = kind === 'inputs' ? row.name : row.var;
|
|
633
|
+
const value = str(row[valueField] ?? '');
|
|
634
|
+
const cell = row.type && row.type !== 'string'
|
|
635
|
+
? `{ ${valueField}: ${value}, type: ${str(row.type)} }`
|
|
636
|
+
: value;
|
|
637
|
+
return `${key(String(name ?? ''))}: ${cell}`;
|
|
638
|
+
});
|
|
639
|
+
return `{ ${entries.join(', ')} }`;
|
|
640
|
+
}
|
|
641
|
+
function emitTask(out, depth, task, lane, refs, library) {
|
|
642
|
+
out.line(depth, `.task(${str(task.displayName ?? '')}, t => t`);
|
|
643
|
+
const inner = depth + 1;
|
|
644
|
+
out.line(inner, emitTaskKind(task, refs, library));
|
|
645
|
+
if (TASK_KIND_METHOD[task.type]) {
|
|
646
|
+
const data = task.data ?? {};
|
|
647
|
+
if (Array.isArray(data.inputs) && data.inputs.length)
|
|
648
|
+
out.line(inner, `.inputs(${ioArg(data.inputs, 'inputs')})`);
|
|
649
|
+
if (Array.isArray(data.outputs) && data.outputs.length)
|
|
650
|
+
out.line(inner, `.outputs(${ioArg(data.outputs, 'outputs')})`);
|
|
651
|
+
}
|
|
652
|
+
if (task.isRequired !== undefined)
|
|
653
|
+
out.line(inner, task.isRequired ? '.required()' : '.required(false)');
|
|
654
|
+
if (task.shouldRunOnlyOnce !== undefined)
|
|
655
|
+
out.line(inner, task.shouldRunOnlyOnce ? '.runOnce()' : '.runOnReEntry()');
|
|
656
|
+
if (task.description)
|
|
657
|
+
out.line(inner, `.description(${str(task.description)})`);
|
|
658
|
+
if (task.skipCondition)
|
|
659
|
+
out.line(inner, `.skipWhen(${str(task.skipCondition)})`);
|
|
660
|
+
for (const condition of task.entryConditions ?? [])
|
|
661
|
+
emitEntryWhen(out, inner, condition, refs, false);
|
|
662
|
+
out.line(depth, lane > 0 ? `, ${opts([['lane', String(lane)]])})` : ')');
|
|
663
|
+
}
|
|
664
|
+
function escalationSource(escalation, slaName, index) {
|
|
665
|
+
if (escalation.action?.type !== 'notification')
|
|
666
|
+
throw new Error(`SLA escalation "${escalation.displayName ?? escalation.id ?? index}" has unsupported action "${escalation.action?.type}".`);
|
|
667
|
+
const recipients = escalation.action.recipients ?? [];
|
|
668
|
+
if (recipients.length === 0)
|
|
669
|
+
throw new Error(`SLA escalation "${escalation.displayName ?? escalation.id ?? index}" has no notification recipients.`);
|
|
670
|
+
const notify = recipients.map((recipient) => {
|
|
671
|
+
if (recipient.scope !== 'User' && recipient.scope !== 'UserGroup')
|
|
672
|
+
throw new Error(`SLA escalation "${escalation.displayName ?? escalation.id ?? index}" has unsupported recipient scope "${recipient.scope}".`);
|
|
673
|
+
const helper = recipient.scope === 'User' ? 'toUser' : 'toGroup';
|
|
674
|
+
const target = recipient.target ?? '';
|
|
675
|
+
const value = recipient.value !== undefined && recipient.value !== target ? `, ${str(recipient.value)}` : '';
|
|
676
|
+
return `${helper}(${str(target)}${value})`;
|
|
677
|
+
});
|
|
678
|
+
const generatedName = `Escalation rule ${index + 1} - ${slaName}`;
|
|
679
|
+
const trigger = escalation.triggerInfo?.type;
|
|
680
|
+
if (trigger !== 'at-risk' && trigger !== 'sla-breached')
|
|
681
|
+
throw new Error(`SLA escalation "${escalation.displayName ?? escalation.id ?? index}" has unsupported trigger "${trigger}".`);
|
|
682
|
+
return `escalation(${opts([
|
|
683
|
+
['displayName', escalation.displayName && escalation.displayName !== generatedName ? str(escalation.displayName) : undefined],
|
|
684
|
+
['trigger', str(trigger)],
|
|
685
|
+
['atRiskPercentage', trigger === 'at-risk' && escalation.triggerInfo?.atRiskPercentage !== undefined
|
|
686
|
+
? String(escalation.triggerInfo.atRiskPercentage)
|
|
687
|
+
: undefined],
|
|
688
|
+
['notify', `[${notify.join(', ')}]`],
|
|
689
|
+
])})`;
|
|
690
|
+
}
|
|
691
|
+
function slaSource(sla, index) {
|
|
692
|
+
const displayName = sla.displayName ?? `SLA ${index + 1}`;
|
|
693
|
+
const escalations = (sla.escalationRule ?? []).map((entry, escalationIndex) => escalationSource(entry, displayName, escalationIndex));
|
|
694
|
+
return opts([
|
|
695
|
+
['displayName', sla.displayName !== undefined ? str(sla.displayName) : undefined],
|
|
696
|
+
['count', sla.count !== undefined ? String(sla.count) : undefined],
|
|
697
|
+
['unit', sla.unit !== undefined ? str(sla.unit) : undefined],
|
|
698
|
+
['when', sla.expression !== undefined && sla.expression !== '=js:true' ? str(sla.expression) : undefined],
|
|
699
|
+
['escalations', escalations.length ? `[${escalations.join(', ')}]` : undefined],
|
|
700
|
+
]);
|
|
701
|
+
}
|
|
702
|
+
function emitStage(out, depth, stage, refs, library, selectNextStage) {
|
|
703
|
+
const exception = stage.type.endsWith('ExceptionStage') || stage.stageType === 'secondary' || stage.data?.stageType === 'secondary';
|
|
704
|
+
const data = stage.data ?? {};
|
|
705
|
+
out.line(depth, `.${exception ? 'exceptionStage' : 'stage'}(${str(data.label ?? '')}, s => s`);
|
|
706
|
+
const inner = depth + 1;
|
|
707
|
+
if (data.description)
|
|
708
|
+
out.line(inner, `.description(${str(data.description)})`);
|
|
709
|
+
if (data.isRequired !== undefined)
|
|
710
|
+
out.line(inner, data.isRequired ? '.required()' : '.required(false)');
|
|
711
|
+
for (const condition of data.entryConditions ?? [])
|
|
712
|
+
emitEntryWhen(out, inner, condition, refs, true);
|
|
713
|
+
const lanes = data.tasks ?? [];
|
|
714
|
+
lanes.forEach((lane, laneIndex) => lane.forEach((task) => emitTask(out, inner, task, laneIndex, refs, library)));
|
|
715
|
+
(data.slaRules ?? []).forEach((sla, index) => out.line(inner, `.sla(${slaSource(sla, index)})`));
|
|
716
|
+
for (const condition of data.exitConditions ?? [])
|
|
717
|
+
emitExitWhen(out, inner, condition, refs, selectNextStage.get(condition));
|
|
718
|
+
out.line(depth, ')');
|
|
719
|
+
}
|
|
720
|
+
function varSpec(entry) {
|
|
721
|
+
const body = entry.body ?? entry._jsonSchema;
|
|
722
|
+
if (entry.type === 'jsonSchema' && body !== undefined && entry.default === undefined)
|
|
723
|
+
return `jsonSchema(${literal(body)})`;
|
|
724
|
+
if (entry.default !== undefined || (entry.type === 'jsonSchema' && body !== undefined)) {
|
|
725
|
+
return opts([
|
|
726
|
+
['type', str(entry.type)],
|
|
727
|
+
['default', entry.default !== undefined ? literal(entry.default) : undefined],
|
|
728
|
+
['body', entry.type === 'jsonSchema' && body !== undefined ? literal(body) : undefined],
|
|
729
|
+
]);
|
|
730
|
+
}
|
|
731
|
+
return str(entry.type);
|
|
732
|
+
}
|
|
733
|
+
function isResolvedConnectorOutput(row, triggerId, resolved) {
|
|
734
|
+
if (!resolved || row.elementId !== triggerId)
|
|
735
|
+
return false;
|
|
736
|
+
return (row.name === 'response' && /^=response\d*$/.test(String(row.source ?? '')))
|
|
737
|
+
|| (row.name === 'Error' && /^=Error\d*$/.test(String(row.source ?? '')));
|
|
738
|
+
}
|
|
739
|
+
function producedVariableNames(plan) {
|
|
740
|
+
const produced = new Set();
|
|
741
|
+
for (const node of plan.nodes ?? []) {
|
|
742
|
+
if (isTrigger(node)) {
|
|
743
|
+
const { bag } = triggerData(node);
|
|
744
|
+
const bridges = bridgeRows(plan, node.id ?? '', bag);
|
|
745
|
+
const resolved = (bag?.context?.length ?? 0) > 2;
|
|
746
|
+
for (const row of bag?.outputs ?? [])
|
|
747
|
+
if (!bridges.has(row) && !isResolvedConnectorOutput(row, node.id ?? '', resolved) && row.var)
|
|
748
|
+
produced.add(row.var);
|
|
749
|
+
continue;
|
|
750
|
+
}
|
|
751
|
+
if (!isStage(node))
|
|
752
|
+
continue;
|
|
753
|
+
for (const lane of node.data?.tasks ?? [])
|
|
754
|
+
for (const task of lane)
|
|
755
|
+
if (TASK_KIND_METHOD[task.type])
|
|
756
|
+
for (const row of task.data?.outputs ?? [])
|
|
757
|
+
if (row.var)
|
|
758
|
+
produced.add(row.var);
|
|
759
|
+
}
|
|
760
|
+
return produced;
|
|
761
|
+
}
|
|
762
|
+
function emitVariables(out, depth, plan, triggerNameById) {
|
|
763
|
+
const variables = plan.variables ?? {};
|
|
764
|
+
const produced = producedVariableNames(plan);
|
|
765
|
+
for (const input of variables.inputs ?? []) {
|
|
766
|
+
const from = input.elementId && triggerNameById.get(input.elementId);
|
|
767
|
+
const fromOpts = from ? `, { from: ${from} }` : '';
|
|
768
|
+
out.line(depth, `.input({ ${key(input.name)}: ${varSpec(input)} }${fromOpts})`);
|
|
769
|
+
}
|
|
770
|
+
for (const output of variables.outputs ?? [])
|
|
771
|
+
out.line(depth, `.output({ ${key(output.name)}: ${varSpec(output)} })`);
|
|
772
|
+
const declaredElsewhere = new Set([...(variables.inputs ?? []), ...(variables.outputs ?? [])].map((entry) => entry.name));
|
|
773
|
+
for (const variable of variables.inputOutputs ?? []) {
|
|
774
|
+
if (declaredElsewhere.has(variable.name) || (variable.custom && produced.has(variable.name)))
|
|
775
|
+
continue;
|
|
776
|
+
const body = variable.body ?? variable._jsonSchema;
|
|
777
|
+
const type = variable.type === 'jsonSchema' && body !== undefined
|
|
778
|
+
? `jsonSchema(${literal(body)})`
|
|
779
|
+
: str(variable.type);
|
|
780
|
+
const defaultValue = variable.default !== undefined ? `, ${literal(variable.default)}` : '';
|
|
781
|
+
out.line(depth, `.var(${str(variable.name)}, ${type}${defaultValue})`);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
function triggerData(node) {
|
|
785
|
+
const data = node.data ?? {};
|
|
786
|
+
if (node.type === 'uipath.case.trigger') {
|
|
787
|
+
return {
|
|
788
|
+
label: data.display?.label,
|
|
789
|
+
description: data.description ?? data.display?.description,
|
|
790
|
+
bag: data.inputs,
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
return { label: data.label, description: data.description, bag: data.uipath };
|
|
794
|
+
}
|
|
795
|
+
function bridgeRows(plan, triggerId, bag) {
|
|
796
|
+
const inputs = (plan.variables?.inputs ?? []).filter((entry) => entry.elementId === triggerId);
|
|
797
|
+
const bridges = new Set();
|
|
798
|
+
for (const row of bag?.outputs ?? []) {
|
|
799
|
+
if (inputs.some((input) => row.var === input.name && row.source === `=vars.${input.id}`))
|
|
800
|
+
bridges.add(row);
|
|
801
|
+
}
|
|
802
|
+
return bridges;
|
|
803
|
+
}
|
|
804
|
+
function triggerSource(node, index, plan, refs) {
|
|
805
|
+
const { label, description, bag } = triggerData(node);
|
|
806
|
+
const name = label !== `Trigger ${index + 1}` ? str(label) : undefined;
|
|
807
|
+
if (!bag?.serviceType || bag.serviceType === 'None')
|
|
808
|
+
return `manualTrigger(${opts([['name', name], ['description', description ? str(description) : undefined]])})`;
|
|
809
|
+
if (bag.serviceType === 'Intsvc.TimerTrigger' || bag.serviceType === 'timer') {
|
|
810
|
+
if (bag.timerType !== undefined && bag.timerType !== 'timeCycle')
|
|
811
|
+
throw new Error(`timer trigger "${label ?? node.id ?? index}" has unsupported timerType "${bag.timerType}".`);
|
|
812
|
+
return `timerTrigger(${opts([
|
|
813
|
+
['every', bag.timeCycle !== undefined ? str(bag.timeCycle) : undefined],
|
|
814
|
+
['name', name],
|
|
815
|
+
['description', description ? str(description) : undefined],
|
|
816
|
+
])})`;
|
|
817
|
+
}
|
|
818
|
+
if (bag.serviceType === 'Intsvc.EventTrigger') {
|
|
819
|
+
const bridges = bridgeRows(plan, node.id ?? '', bag);
|
|
820
|
+
const resolved = bag.context !== undefined || bag.inputs !== undefined || bag.bindings !== undefined;
|
|
821
|
+
const outputs = (bag.outputs ?? []).filter((row) => !bridges.has(row) && !isResolvedConnectorOutput(row, node.id ?? '', resolved));
|
|
822
|
+
const subscription = resolved
|
|
823
|
+
? resolvedSubscriptionSource(bag, `event trigger "${label ?? node.id ?? index}"`, 'trigger', refs)
|
|
824
|
+
: undefined;
|
|
825
|
+
const outputShape = outputs.length
|
|
826
|
+
? `{ ${outputs.map((row) => {
|
|
827
|
+
const source = str(row.source ?? '');
|
|
828
|
+
const cell = row.type && row.type !== 'string'
|
|
829
|
+
? `{ source: ${source}, type: ${str(row.type)} }`
|
|
830
|
+
: source;
|
|
831
|
+
return `${key(String(row.var ?? row.name ?? ''))}: ${cell}`;
|
|
832
|
+
}).join(', ')} }`
|
|
833
|
+
: undefined;
|
|
834
|
+
return `eventTrigger(${opts([
|
|
835
|
+
['name', name],
|
|
836
|
+
['description', description ? str(description) : undefined],
|
|
837
|
+
['subscription', subscription],
|
|
838
|
+
['outputs', outputShape],
|
|
839
|
+
])})`;
|
|
840
|
+
}
|
|
841
|
+
throw new Error(`trigger "${label ?? node.id ?? index}" has unsupported serviceType "${bag.serviceType}".`);
|
|
842
|
+
}
|
|
843
|
+
function caseAppSource(config) {
|
|
844
|
+
if (typeof config.caseSummary !== 'string')
|
|
845
|
+
throw new Error('caseAppConfig.caseSummary must be a string.');
|
|
846
|
+
if (!Array.isArray(config.sections))
|
|
847
|
+
throw new Error('caseAppConfig.sections must be an array.');
|
|
848
|
+
const sections = config.sections.map((section, index) => {
|
|
849
|
+
if (typeof section.id !== 'string' || typeof section.title !== 'string' || typeof section.details !== 'string')
|
|
850
|
+
throw new Error(`caseAppConfig section ${index + 1} must have string id, title, and details fields.`);
|
|
851
|
+
let details;
|
|
852
|
+
try {
|
|
853
|
+
details = JSON.parse(section.details);
|
|
854
|
+
}
|
|
855
|
+
catch {
|
|
856
|
+
throw new Error(`caseAppConfig section "${section.title}" details is not a JSON-encoded object.`);
|
|
857
|
+
}
|
|
858
|
+
if (!details || typeof details !== 'object' || Array.isArray(details))
|
|
859
|
+
throw new Error(`caseAppConfig section "${section.title}" details is not a JSON-encoded object.`);
|
|
860
|
+
const entries = Object.entries(details);
|
|
861
|
+
if (entries.length > 6)
|
|
862
|
+
throw new Error(`caseAppConfig section "${section.title}" details has more than 6 entries.`);
|
|
863
|
+
const nested = entries.find(([, value]) => value !== null && !['string', 'number', 'boolean'].includes(typeof value));
|
|
864
|
+
if (nested)
|
|
865
|
+
throw new Error(`caseAppConfig section "${section.title}" detail "${nested[0]}" is not primitive.`);
|
|
866
|
+
return { id: section.id, title: section.title, details };
|
|
867
|
+
});
|
|
868
|
+
return literal({ summary: config.caseSummary, sections });
|
|
869
|
+
}
|
|
870
|
+
function refuseUnsupportedAuthoring(plan) {
|
|
871
|
+
if (plan.metadata?.adhocCaseEndData !== undefined)
|
|
872
|
+
throw new Error('adhocCaseEndData is a legacy Case Manager field and is unavailable at the V30 authoring floor.');
|
|
873
|
+
for (const node of plan.nodes ?? []) {
|
|
874
|
+
if (!isStage(node))
|
|
875
|
+
continue;
|
|
876
|
+
if (node.data?.adhocOrchestrationData !== undefined)
|
|
877
|
+
throw new Error(`stage "${node.data.label ?? node.id ?? '?'}" has legacy adhocOrchestrationData, which is unavailable at the V30 authoring floor.`);
|
|
878
|
+
if (node.data?.adhocCaseEndData !== undefined)
|
|
879
|
+
throw new Error(`stage "${node.data.label ?? node.id ?? '?'}" has legacy adhocCaseEndData, which is unavailable at the V30 authoring floor.`);
|
|
880
|
+
for (const lane of node.data?.tasks ?? []) {
|
|
881
|
+
for (const task of lane) {
|
|
882
|
+
if (task.conditions !== undefined || task.data?.conditions !== undefined)
|
|
883
|
+
throw new Error(`task "${task.displayName ?? task.id ?? '?'}" has a conditions[] grid, which the current-floor converter retains but does not execute.`);
|
|
884
|
+
if (task.nonSequentialRunConditions !== undefined || task.data?.nonSequentialRunConditions !== undefined)
|
|
885
|
+
throw new Error(`task "${task.displayName ?? task.id ?? '?'}" has nonSequentialRunConditions, which is absent from the current task schema and does not affect scheduler rules.`);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
function conditionIdentity(condition) {
|
|
891
|
+
return {
|
|
892
|
+
...(condition.id ? { id: condition.id } : {}),
|
|
893
|
+
rules: (condition.rules ?? []).map((group) => group.map((rule) => ({ ...(rule.id ? { id: rule.id } : {}) }))),
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
function slaIdentity(sla) {
|
|
897
|
+
return {
|
|
898
|
+
...(sla.id ? { id: sla.id } : {}),
|
|
899
|
+
escalations: (sla.escalationRule ?? []).map((entry) => ({ ...(entry.id ? { id: entry.id } : {}) })),
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
function identityPreservation(plan) {
|
|
903
|
+
return {
|
|
904
|
+
...(plan.id ? { id: plan.id } : {}),
|
|
905
|
+
triggers: (plan.nodes ?? []).filter(isTrigger).map((node) => ({ ...(node.id ? { id: node.id } : {}) })),
|
|
906
|
+
stages: (plan.nodes ?? []).filter(isStage).map((stage) => ({
|
|
907
|
+
label: stage.data?.label ?? '',
|
|
908
|
+
...(stage.id ? { id: stage.id } : {}),
|
|
909
|
+
tasks: (stage.data?.tasks ?? []).map((lane) => lane.map((task) => ({
|
|
910
|
+
name: task.displayName ?? '',
|
|
911
|
+
...(task.id ? { id: task.id } : {}),
|
|
912
|
+
entryConditions: (task.entryConditions ?? []).map(conditionIdentity),
|
|
913
|
+
inputs: (task.data?.inputs ?? []).map((row) => ({ name: row.name ?? '', ...(row.id ? { id: row.id } : {}) })),
|
|
914
|
+
outputs: (task.data?.outputs ?? []).map((row) => ({ name: row.name ?? '', ...(row.id ? { id: row.id } : {}) })),
|
|
915
|
+
}))),
|
|
916
|
+
entryConditions: (stage.data?.entryConditions ?? []).map(conditionIdentity),
|
|
917
|
+
exitConditions: (stage.data?.exitConditions ?? []).map(conditionIdentity),
|
|
918
|
+
slaRules: (stage.data?.slaRules ?? []).map(slaIdentity),
|
|
919
|
+
})),
|
|
920
|
+
caseExitRules: (plan.metadata?.caseExitRules ?? []).map(conditionIdentity),
|
|
921
|
+
slaRules: (plan.metadata?.slaRules ?? []).map(slaIdentity),
|
|
922
|
+
variables: Object.fromEntries(['inputs', 'outputs', 'inputOutputs'].map((lane) => [
|
|
923
|
+
lane,
|
|
924
|
+
(plan.variables?.[lane] ?? []).map((row) => ({ name: row.name, ...(row.id ? { id: row.id } : {}) })),
|
|
925
|
+
])),
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
function collectPreservation(plan) {
|
|
929
|
+
const preservation = { identities: identityPreservation(plan) };
|
|
930
|
+
const topKnown = new Set(['id', 'name', 'description', 'version', 'bindings', 'variables', 'metadata', 'nodes', 'edges', 'layout']);
|
|
931
|
+
const topLevel = Object.fromEntries(Object.entries(plan).filter(([name]) => !topKnown.has(name)));
|
|
932
|
+
if (Object.keys(topLevel).length)
|
|
933
|
+
preservation.topLevel = topLevel;
|
|
934
|
+
const metadataKnown = new Set([
|
|
935
|
+
'caseIdentifier', 'caseIdentifierType', 'caseAppEnabled', 'caseAppConfig', 'publishVersion', 'caseExitRules', 'slaRules',
|
|
936
|
+
'allowAdhocOptionalStageTasks', 'waitForHumanSelectNextStageDFConnector',
|
|
937
|
+
]);
|
|
938
|
+
const metadata = Object.fromEntries(Object.entries(plan.metadata ?? {}).filter(([name]) => !metadataKnown.has(name)));
|
|
939
|
+
if (Object.keys(metadata).length)
|
|
940
|
+
preservation.metadata = metadata;
|
|
941
|
+
const caseAppConfig = plan.metadata?.caseAppConfig;
|
|
942
|
+
if (caseAppConfig) {
|
|
943
|
+
const known = new Set(['caseSummary', 'sections', 'stageOrder', 'hiddenStageIds']);
|
|
944
|
+
const unknown = Object.keys(caseAppConfig).filter((name) => !known.has(name));
|
|
945
|
+
if (unknown.length)
|
|
946
|
+
throw new Error(`caseAppConfig has unsupported field "${unknown[0]}", which is not authorable.`);
|
|
947
|
+
const fields = Object.fromEntries(['stageOrder', 'hiddenStageIds']
|
|
948
|
+
.filter((name) => Object.prototype.hasOwnProperty.call(caseAppConfig, name))
|
|
949
|
+
.map((name) => [name, caseAppConfig[name]]));
|
|
950
|
+
const sections = (Array.isArray(caseAppConfig.sections) ? caseAppConfig.sections : []).flatMap((section) => {
|
|
951
|
+
if (!section || typeof section !== 'object')
|
|
952
|
+
return [];
|
|
953
|
+
const sectionKnown = new Set(['id', 'title', 'details', 'error', 'titleError']);
|
|
954
|
+
const sectionUnknown = Object.keys(section).filter((name) => !sectionKnown.has(name));
|
|
955
|
+
if (sectionUnknown.length)
|
|
956
|
+
throw new Error(`caseAppConfig section has unsupported field "${sectionUnknown[0]}", which is not authorable.`);
|
|
957
|
+
const preservedFields = Object.fromEntries(['error', 'titleError']
|
|
958
|
+
.filter((name) => Object.prototype.hasOwnProperty.call(section, name))
|
|
959
|
+
.map((name) => [name, section[name]]));
|
|
960
|
+
return typeof section.id === 'string' && Object.keys(preservedFields).length
|
|
961
|
+
? [{ id: section.id, fields: preservedFields }]
|
|
962
|
+
: [];
|
|
963
|
+
});
|
|
964
|
+
if (Object.keys(fields).length || sections.length)
|
|
965
|
+
preservation.caseAppConfigFields = {
|
|
966
|
+
...(Object.keys(fields).length ? { fields } : {}),
|
|
967
|
+
...(sections.length ? { sections } : {}),
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
const preservedNodes = [];
|
|
971
|
+
(plan.nodes ?? []).forEach((node, index) => {
|
|
972
|
+
if (isStage(node) || isTrigger(node))
|
|
973
|
+
return;
|
|
974
|
+
if (node.type !== 'case-management:StickyNote')
|
|
975
|
+
throw new Error(`unsupported top-level node construct "${node.type ?? '(missing)'}".`);
|
|
976
|
+
preservedNodes.push({ index, node });
|
|
977
|
+
});
|
|
978
|
+
if (preservedNodes.length)
|
|
979
|
+
preservation.nodes = preservedNodes;
|
|
980
|
+
const nodeOrder = (plan.nodes ?? []).flatMap((node) => typeof node.id === 'string' ? [node.id] : []);
|
|
981
|
+
if (nodeOrder.length)
|
|
982
|
+
preservation.nodeOrder = nodeOrder;
|
|
983
|
+
if ((plan.edges?.length ?? 0) > 0)
|
|
984
|
+
preservation.edges = plan.edges;
|
|
985
|
+
if (Object.prototype.hasOwnProperty.call(plan, 'layout'))
|
|
986
|
+
preservation.layout = plan.layout;
|
|
987
|
+
else {
|
|
988
|
+
const inlineLayout = Object.fromEntries((plan.nodes ?? []).flatMap((node) => {
|
|
989
|
+
const positioned = node;
|
|
990
|
+
return positioned.id && positioned.position !== undefined
|
|
991
|
+
? [[positioned.id, { position: positioned.position }]]
|
|
992
|
+
: [];
|
|
993
|
+
}));
|
|
994
|
+
if (Object.keys(inlineLayout).length)
|
|
995
|
+
preservation.layout = { nodes: inlineLayout };
|
|
996
|
+
else
|
|
997
|
+
preservation.omitLayout = true;
|
|
998
|
+
}
|
|
999
|
+
const triggerDataKnown = new Set(['label', 'description', 'uipath', 'display', 'inputs', 'typeVersion']);
|
|
1000
|
+
const triggerDataPreserved = new Set(['parentElement', 'isInvalidDropTarget', 'isPendingParent']);
|
|
1001
|
+
const triggerDataFields = (plan.nodes ?? []).filter(isTrigger).map((trigger) => Object.fromEntries(Object.entries(trigger.data ?? {}).filter(([name]) => !triggerDataKnown.has(name))));
|
|
1002
|
+
for (const [index, fields] of triggerDataFields.entries())
|
|
1003
|
+
for (const field of Object.keys(fields))
|
|
1004
|
+
if (!triggerDataPreserved.has(field))
|
|
1005
|
+
throw new Error(`trigger ${index + 1} has unsupported data field "${field}", which is not allowed in the collision-refusing preservation channel.`);
|
|
1006
|
+
if (triggerDataFields.some((fields) => Object.keys(fields).length))
|
|
1007
|
+
preservation.triggerDataFields = triggerDataFields;
|
|
1008
|
+
const taskKnown = new Set([
|
|
1009
|
+
'id', 'displayName', 'type', 'data', 'shouldRunOnlyOnce', 'isRequired', 'skipCondition', 'description',
|
|
1010
|
+
'entryConditions', 'conditions', 'nonSequentialRunConditions',
|
|
1011
|
+
]);
|
|
1012
|
+
const taskFields = [];
|
|
1013
|
+
const omitTaskElementIds = [];
|
|
1014
|
+
const omitStagePositions = [];
|
|
1015
|
+
for (const stage of (plan.nodes ?? []).filter(isStage)) {
|
|
1016
|
+
const label = stage.data?.label ?? '';
|
|
1017
|
+
if (!Object.prototype.hasOwnProperty.call(stage, 'position'))
|
|
1018
|
+
omitStagePositions.push(label);
|
|
1019
|
+
(stage.data?.tasks ?? []).forEach((lane, laneIndex) => lane.forEach((task) => {
|
|
1020
|
+
// `elementId` is serializer-owned for every task now, so it is dropped
|
|
1021
|
+
// here rather than preserved: `compile` reconstructs the same
|
|
1022
|
+
// `<stageId>-<taskId>` from ids preservation already pins, and writing it
|
|
1023
|
+
// through the channel too would be a redundant write of a value the
|
|
1024
|
+
// serializer owns. Anything else unrecognised on a task has no home.
|
|
1025
|
+
// A source document that carries no `elementId` must not gain one: see
|
|
1026
|
+
// `omitTaskElementIds` in preserve.ts.
|
|
1027
|
+
if (!Object.prototype.hasOwnProperty.call(task, 'elementId'))
|
|
1028
|
+
omitTaskElementIds.push({ stage: label, lane: laneIndex, task: task.displayName ?? '' });
|
|
1029
|
+
const fields = Object.fromEntries(Object.entries(task).filter(([name]) => !taskKnown.has(name) && name !== 'elementId'));
|
|
1030
|
+
for (const field of Object.keys(fields))
|
|
1031
|
+
throw new Error(`task "${task.displayName ?? task.id ?? '?'}" has unsupported field "${field}", which is not allowed in the collision-refusing preservation channel.`);
|
|
1032
|
+
if (Object.keys(fields).length)
|
|
1033
|
+
taskFields.push({ stage: label, lane: laneIndex, task: task.displayName ?? '', fields });
|
|
1034
|
+
}));
|
|
1035
|
+
}
|
|
1036
|
+
if (taskFields.length)
|
|
1037
|
+
preservation.taskFields = taskFields;
|
|
1038
|
+
if (omitTaskElementIds.length)
|
|
1039
|
+
preservation.omitTaskElementIds = omitTaskElementIds;
|
|
1040
|
+
if (omitStagePositions.length)
|
|
1041
|
+
preservation.omitStagePositions = omitStagePositions;
|
|
1042
|
+
const produced = producedVariableNames(plan);
|
|
1043
|
+
const variableKnown = new Set(['id', 'name', 'type', 'default', 'body', '_jsonSchema', 'elementId', 'var', 'custom']);
|
|
1044
|
+
const variableFields = {};
|
|
1045
|
+
const legacyVariableDefaults = {};
|
|
1046
|
+
const variableOrder = {};
|
|
1047
|
+
for (const lane of ['inputs', 'outputs', 'inputOutputs']) {
|
|
1048
|
+
const rows = plan.variables?.[lane] ?? [];
|
|
1049
|
+
if (rows.length)
|
|
1050
|
+
variableOrder[lane] = rows.map((row) => row.name);
|
|
1051
|
+
const byName = {};
|
|
1052
|
+
const defaultsByName = {};
|
|
1053
|
+
for (const row of rows) {
|
|
1054
|
+
const fields = Object.fromEntries(Object.entries(row).filter(([name]) => !variableKnown.has(name)));
|
|
1055
|
+
for (const field of Object.keys(fields))
|
|
1056
|
+
if (!['subType', 'canonicalId', 'camelizedId', 'variableType', 'internal'].includes(field))
|
|
1057
|
+
throw new Error(`variable "${row.name}" has unsupported field "${field}", which is not allowed in the collision-refusing preservation channel.`);
|
|
1058
|
+
if (lane === 'inputOutputs' && row.custom && !produced.has(row.name))
|
|
1059
|
+
fields.custom = true;
|
|
1060
|
+
const formalOutput = (plan.variables?.outputs ?? []).find((candidate) => candidate.name === row.name);
|
|
1061
|
+
if (lane === 'inputOutputs' && formalOutput && row.default !== undefined && formalOutput.default !== row.default)
|
|
1062
|
+
fields.default = row.default;
|
|
1063
|
+
if (lane === 'outputs' && row.var !== undefined)
|
|
1064
|
+
fields.var = row.var;
|
|
1065
|
+
if (Object.keys(fields).length)
|
|
1066
|
+
byName[row.name] = fields;
|
|
1067
|
+
if (row.default !== undefined && typeof row.default !== 'string')
|
|
1068
|
+
defaultsByName[row.name] = row.default;
|
|
1069
|
+
}
|
|
1070
|
+
if (Object.keys(byName).length)
|
|
1071
|
+
variableFields[lane] = byName;
|
|
1072
|
+
if (Object.keys(defaultsByName).length)
|
|
1073
|
+
legacyVariableDefaults[lane] = defaultsByName;
|
|
1074
|
+
}
|
|
1075
|
+
if (Object.keys(variableFields).length)
|
|
1076
|
+
preservation.variableFields = variableFields;
|
|
1077
|
+
if (Object.keys(legacyVariableDefaults).length)
|
|
1078
|
+
preservation.legacyVariableDefaults = legacyVariableDefaults;
|
|
1079
|
+
if (Object.keys(variableOrder).length)
|
|
1080
|
+
preservation.variableOrder = variableOrder;
|
|
1081
|
+
return Object.keys(preservation).length ? preservation : undefined;
|
|
1082
|
+
}
|
|
1083
|
+
function slugId(plan) {
|
|
1084
|
+
if (plan.id && /^case-[A-Za-z0-9]{10}$/.test(plan.id))
|
|
1085
|
+
return plan.id;
|
|
1086
|
+
const base = plan.name ?? plan.id ?? 'case';
|
|
1087
|
+
return base.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'case';
|
|
1088
|
+
}
|
|
1089
|
+
export function decompileWithDiagnostics(plan, options = {}) {
|
|
1090
|
+
const diagnostics = [];
|
|
1091
|
+
refuseUnsupportedAuthoring(plan);
|
|
1092
|
+
const preservation = collectPreservation(plan);
|
|
1093
|
+
const refs = buildRefs(plan, options.library, options.bestEffort ? diagnostics : undefined);
|
|
1094
|
+
const selectNextStage = selectNextStageByExit(plan, refs);
|
|
1095
|
+
const metadata = plan.metadata ?? {};
|
|
1096
|
+
const triggers = (plan.nodes ?? []).filter(isTrigger);
|
|
1097
|
+
const triggerNameById = new Map();
|
|
1098
|
+
const out = new Emit();
|
|
1099
|
+
triggers.forEach((trigger, index) => {
|
|
1100
|
+
const variableName = `trigger${index + 1}`;
|
|
1101
|
+
if (trigger.id)
|
|
1102
|
+
triggerNameById.set(trigger.id, variableName);
|
|
1103
|
+
out.line(0, `const ${variableName} = ${triggerSource(trigger, index, plan, refs)};`);
|
|
1104
|
+
});
|
|
1105
|
+
if (triggers.length)
|
|
1106
|
+
out.line(0, '');
|
|
1107
|
+
out.line(0, `${preservation ? 'const caseDefinition =' : 'export default'} casePlan(${str(slugId(plan))})`);
|
|
1108
|
+
const depth = 1;
|
|
1109
|
+
// The declared grammar version first, mirroring the document's own key order
|
|
1110
|
+
// and emitted even when it equals this SDK's write version: preserving means
|
|
1111
|
+
// RE-DECLARING, so a later write-floor bump cannot silently upgrade a plan a
|
|
1112
|
+
// decompile already captured. A version this build cannot write is reported in
|
|
1113
|
+
// the source instead of being dropped — the source still compiles (it writes
|
|
1114
|
+
// the SDK's version), which is why this is a comment rather than one of the
|
|
1115
|
+
// repair diagnostics that mark a source intentionally unbuildable.
|
|
1116
|
+
const schema = preserveSchemaVersion(CASE_SCHEMA_VERSIONS, plan.version);
|
|
1117
|
+
if (schema.call)
|
|
1118
|
+
out.line(depth, schema.call);
|
|
1119
|
+
if (schema.problem)
|
|
1120
|
+
out.line(depth, `// TODO: ${schema.problem}`);
|
|
1121
|
+
if (plan.name !== undefined)
|
|
1122
|
+
out.line(depth, `.name(${str(plan.name)})`);
|
|
1123
|
+
if (plan.description !== undefined)
|
|
1124
|
+
out.line(depth, `.description(${str(plan.description)})`);
|
|
1125
|
+
if (metadata.caseIdentifier !== undefined) {
|
|
1126
|
+
const type = metadata.caseIdentifierType && metadata.caseIdentifierType !== 'constant'
|
|
1127
|
+
? `, ${str(metadata.caseIdentifierType)}`
|
|
1128
|
+
: '';
|
|
1129
|
+
out.line(depth, `.identifier(${str(metadata.caseIdentifier)}${type})`);
|
|
1130
|
+
}
|
|
1131
|
+
if (metadata.caseAppConfig !== undefined)
|
|
1132
|
+
out.line(depth, `.caseApp(${caseAppSource(metadata.caseAppConfig)})`);
|
|
1133
|
+
else if (metadata.caseAppEnabled)
|
|
1134
|
+
out.line(depth, '.caseApp()');
|
|
1135
|
+
if (metadata.caseAppConfig !== undefined && metadata.caseAppEnabled === false)
|
|
1136
|
+
out.line(depth, '.caseApp(false)');
|
|
1137
|
+
if (metadata.allowAdhocOptionalStageTasks !== undefined) {
|
|
1138
|
+
if (typeof metadata.allowAdhocOptionalStageTasks !== 'boolean')
|
|
1139
|
+
throw new Error('allowAdhocOptionalStageTasks must be boolean.');
|
|
1140
|
+
out.line(depth, metadata.allowAdhocOptionalStageTasks ? '.allowAdhocTasks()' : '.allowAdhocTasks(false)');
|
|
1141
|
+
}
|
|
1142
|
+
emitVariables(out, depth, plan, triggerNameById);
|
|
1143
|
+
triggers.forEach((_trigger, index) => out.line(depth, `.trigger(trigger${index + 1})`));
|
|
1144
|
+
(metadata.slaRules ?? []).forEach((sla, index) => out.line(depth, `.sla(${slaSource(sla, index)})`));
|
|
1145
|
+
for (const node of plan.nodes ?? [])
|
|
1146
|
+
if (isStage(node))
|
|
1147
|
+
emitStage(out, depth, node, refs, options.library, selectNextStage);
|
|
1148
|
+
for (const condition of metadata.caseExitRules ?? [])
|
|
1149
|
+
emitCompleteWhen(out, depth, condition, refs);
|
|
1150
|
+
out.line(depth, '.build();');
|
|
1151
|
+
if (preservation) {
|
|
1152
|
+
out.line(0, '');
|
|
1153
|
+
out.line(0, `export default preserveCaseJson(caseDefinition, ${literal(preservation)});`);
|
|
1154
|
+
}
|
|
1155
|
+
const imports = ['casePlan', 'rule', 'manualTrigger', 'timerTrigger', 'eventTrigger', 'jsonSchema', 'escalation', 'toUser', 'toGroup'];
|
|
1156
|
+
if (preservation)
|
|
1157
|
+
imports.push('preserveCaseJson');
|
|
1158
|
+
const diagnosticHeader = diagnostics.length
|
|
1159
|
+
? `${[
|
|
1160
|
+
'// BEST-EFFORT DECOMPILE: this source is intentionally not buildable until every marker below is repaired.',
|
|
1161
|
+
...diagnostics.map((diagnostic) => `// [${diagnostic.code}] ${diagnostic.location}: ${diagnostic.message}`
|
|
1162
|
+
.replace(/[\r\n]+/g, ' ')),
|
|
1163
|
+
].join('\n')}\n\n`
|
|
1164
|
+
: '';
|
|
1165
|
+
const source = `import { ${imports.join(', ')} } from ${str(options.importSpecifier ?? './case-sdk.js')};\n\n${diagnosticHeader}${out.toString()}\n`;
|
|
1166
|
+
return { source, diagnostics };
|
|
1167
|
+
}
|
|
1168
|
+
export function decompile(plan, options = {}) {
|
|
1169
|
+
return decompileWithDiagnostics(plan, options).source;
|
|
1170
|
+
}
|