@servicenow/sdk-build-plugins 4.5.0 → 4.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/column-plugin.js +3 -7
- package/dist/column-plugin.js.map +1 -1
- package/dist/flow/flow-logic/flow-logic-diagnostics.js +5 -5
- package/dist/flow/flow-logic/flow-logic-diagnostics.js.map +1 -1
- package/dist/flow/plugins/flow-action-definition-plugin.js +1229 -54
- package/dist/flow/plugins/flow-action-definition-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-data-pill-plugin.js +5 -2
- package/dist/flow/plugins/flow-data-pill-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-definition-plugin.js +16 -42
- package/dist/flow/plugins/flow-definition-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-diagnostics-plugin.d.ts +2 -2
- package/dist/flow/plugins/flow-diagnostics-plugin.js +2 -2
- package/dist/flow/plugins/flow-instance-plugin.js +68 -22
- package/dist/flow/plugins/flow-instance-plugin.js.map +1 -1
- package/dist/flow/plugins/step-definition-plugin.js +2 -1
- package/dist/flow/plugins/step-definition-plugin.js.map +1 -1
- package/dist/flow/plugins/step-instance-plugin.d.ts +9 -1
- package/dist/flow/plugins/step-instance-plugin.js +649 -136
- package/dist/flow/plugins/step-instance-plugin.js.map +1 -1
- package/dist/flow/plugins/wfa-datapill-plugin.js +20 -5
- package/dist/flow/plugins/wfa-datapill-plugin.js.map +1 -1
- package/dist/flow/post-install.js +1 -0
- package/dist/flow/post-install.js.map +1 -1
- package/dist/flow/utils/complex-object-resolver.js +4 -1
- package/dist/flow/utils/complex-object-resolver.js.map +1 -1
- package/dist/flow/utils/complex-objects.js +1 -1
- package/dist/flow/utils/complex-objects.js.map +1 -1
- package/dist/flow/utils/flow-constants.d.ts +66 -2
- package/dist/flow/utils/flow-constants.js +402 -6
- package/dist/flow/utils/flow-constants.js.map +1 -1
- package/dist/flow/utils/flow-io-to-record.d.ts +1 -1
- package/dist/flow/utils/flow-io-to-record.js +37 -16
- package/dist/flow/utils/flow-io-to-record.js.map +1 -1
- package/dist/flow/utils/flow-shapes.js +4 -0
- package/dist/flow/utils/flow-shapes.js.map +1 -1
- package/dist/flow/utils/label-cache-parser.d.ts +9 -2
- package/dist/flow/utils/label-cache-parser.js +32 -4
- package/dist/flow/utils/label-cache-parser.js.map +1 -1
- package/dist/flow/utils/pill-shape-helpers.d.ts +15 -0
- package/dist/flow/utils/pill-shape-helpers.js +35 -0
- package/dist/flow/utils/pill-shape-helpers.js.map +1 -0
- package/dist/flow/utils/pill-string-parser.js +1 -0
- package/dist/flow/utils/pill-string-parser.js.map +1 -1
- package/dist/flow/utils/schema-to-flow-object.d.ts +6 -1
- package/dist/flow/utils/schema-to-flow-object.js +131 -15
- package/dist/flow/utils/schema-to-flow-object.js.map +1 -1
- package/dist/flow/utils/utils.d.ts +1 -0
- package/dist/flow/utils/utils.js +6 -1
- package/dist/flow/utils/utils.js.map +1 -1
- package/dist/form-plugin.js +7 -9
- package/dist/form-plugin.js.map +1 -1
- package/dist/inbound-email-action-plugin.d.ts +10 -0
- package/dist/inbound-email-action-plugin.js +128 -0
- package/dist/inbound-email-action-plugin.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/instance-scan-plugin.js +0 -5
- package/dist/instance-scan-plugin.js.map +1 -1
- package/dist/property-plugin.js +1 -1
- package/dist/property-plugin.js.map +1 -1
- package/dist/record-plugin.d.ts +7 -0
- package/dist/record-plugin.js +10 -2
- package/dist/record-plugin.js.map +1 -1
- package/dist/rest-api-plugin.js +8 -1
- package/dist/rest-api-plugin.js.map +1 -1
- package/dist/schedule-script/scheduled-script-plugin.js +8 -3
- package/dist/schedule-script/scheduled-script-plugin.js.map +1 -1
- package/dist/service-catalog/service-catalog-base.d.ts +18 -18
- package/dist/service-catalog/service-catalog-base.js +22 -22
- package/dist/service-catalog/service-catalog-base.js.map +1 -1
- package/dist/service-portal/header-footer-plugin.d.ts +2 -0
- package/dist/service-portal/header-footer-plugin.js +50 -0
- package/dist/service-portal/header-footer-plugin.js.map +1 -0
- package/dist/service-portal/menu-plugin.js +3 -22
- package/dist/service-portal/menu-plugin.js.map +1 -1
- package/dist/service-portal/page-plugin.js +3 -24
- package/dist/service-portal/page-plugin.js.map +1 -1
- package/dist/service-portal/page-route-map-plugin.d.ts +2 -0
- package/dist/service-portal/page-route-map-plugin.js +114 -0
- package/dist/service-portal/page-route-map-plugin.js.map +1 -0
- package/dist/service-portal/portal-plugin.js +21 -8
- package/dist/service-portal/portal-plugin.js.map +1 -1
- package/dist/service-portal/utils.d.ts +40 -2
- package/dist/service-portal/utils.js +283 -2
- package/dist/service-portal/utils.js.map +1 -1
- package/dist/service-portal/widget-plugin.js +9 -218
- package/dist/service-portal/widget-plugin.js.map +1 -1
- package/dist/static-content-plugin.js +4 -0
- package/dist/static-content-plugin.js.map +1 -1
- package/dist/table-plugin.js +190 -26
- package/dist/table-plugin.js.map +1 -1
- package/dist/ui-action-plugin.js +1 -4
- package/dist/ui-action-plugin.js.map +1 -1
- package/dist/ui-page-plugin.js +68 -13
- package/dist/ui-page-plugin.js.map +1 -1
- package/dist/view-plugin.js +8 -3
- package/dist/view-plugin.js.map +1 -1
- package/dist/workspace-plugin.js +39 -36
- package/dist/workspace-plugin.js.map +1 -1
- package/package.json +5 -4
- package/src/column-plugin.ts +3 -8
- package/src/flow/flow-logic/flow-logic-diagnostics.ts +5 -6
- package/src/flow/plugins/flow-action-definition-plugin.ts +1581 -61
- package/src/flow/plugins/flow-data-pill-plugin.ts +5 -2
- package/src/flow/plugins/flow-definition-plugin.ts +12 -47
- package/src/flow/plugins/flow-diagnostics-plugin.ts +2 -2
- package/src/flow/plugins/flow-instance-plugin.ts +98 -22
- package/src/flow/plugins/step-definition-plugin.ts +2 -1
- package/src/flow/plugins/step-instance-plugin.ts +772 -156
- package/src/flow/plugins/wfa-datapill-plugin.ts +25 -5
- package/src/flow/post-install.ts +1 -0
- package/src/flow/utils/complex-object-resolver.ts +4 -1
- package/src/flow/utils/complex-objects.ts +1 -1
- package/src/flow/utils/flow-constants.ts +421 -5
- package/src/flow/utils/flow-io-to-record.ts +43 -17
- package/src/flow/utils/flow-shapes.ts +4 -0
- package/src/flow/utils/label-cache-parser.ts +33 -4
- package/src/flow/utils/pill-shape-helpers.ts +42 -0
- package/src/flow/utils/pill-string-parser.ts +1 -0
- package/src/flow/utils/schema-to-flow-object.ts +183 -15
- package/src/flow/utils/utils.ts +12 -1
- package/src/form-plugin.ts +1 -3
- package/src/inbound-email-action-plugin.ts +145 -0
- package/src/index.ts +4 -0
- package/src/instance-scan-plugin.ts +0 -5
- package/src/property-plugin.ts +4 -1
- package/src/record-plugin.ts +14 -4
- package/src/rest-api-plugin.ts +7 -1
- package/src/schedule-script/scheduled-script-plugin.ts +14 -3
- package/src/service-catalog/service-catalog-base.ts +22 -22
- package/src/service-portal/header-footer-plugin.ts +57 -0
- package/src/service-portal/menu-plugin.ts +1 -23
- package/src/service-portal/page-plugin.ts +3 -28
- package/src/service-portal/page-route-map-plugin.ts +124 -0
- package/src/service-portal/portal-plugin.ts +33 -10
- package/src/service-portal/utils.ts +404 -3
- package/src/service-portal/widget-plugin.ts +14 -290
- package/src/static-content-plugin.ts +3 -0
- package/src/table-plugin.ts +226 -36
- package/src/ui-action-plugin.ts +1 -8
- package/src/ui-page-plugin.ts +76 -13
- package/src/view-plugin.ts +10 -4
- package/src/workspace-plugin.ts +43 -43
|
@@ -5,9 +5,786 @@ const sdk_build_core_1 = require("@servicenow/sdk-build-core");
|
|
|
5
5
|
const flow_io_to_record_1 = require("../utils/flow-io-to-record");
|
|
6
6
|
const flow_to_xml_1 = require("../utils/flow-to-xml");
|
|
7
7
|
const flow_constants_1 = require("../utils/flow-constants");
|
|
8
|
+
const column_helper_1 = require("../../column/column-helper");
|
|
9
|
+
const schema_to_flow_object_1 = require("../utils/schema-to-flow-object");
|
|
10
|
+
const flow_instance_plugin_1 = require("./flow-instance-plugin");
|
|
8
11
|
const arrow_function_plugin_1 = require("../../arrow-function-plugin");
|
|
9
12
|
const utils_1 = require("../../utils");
|
|
13
|
+
const inline_script_plugin_1 = require("./inline-script-plugin");
|
|
10
14
|
const step_instance_plugin_1 = require("./step-instance-plugin");
|
|
15
|
+
const now_id_plugin_1 = require("../../now-id-plugin");
|
|
16
|
+
const now_include_plugin_1 = require("../../now-include-plugin");
|
|
17
|
+
const utils_2 = require("../utils/utils");
|
|
18
|
+
const pill_string_parser_1 = require("../utils/pill-string-parser");
|
|
19
|
+
const label_cache_parser_1 = require("../utils/label-cache-parser");
|
|
20
|
+
const column_helper_2 = require("../../column/column-helper");
|
|
21
|
+
const pill_shape_helpers_1 = require("../utils/pill-shape-helpers");
|
|
22
|
+
/**
|
|
23
|
+
* Resolves inline scripts for a step's inputs by querying sys_hub_input_scripts descendants.
|
|
24
|
+
* Replaces 'fd-scripted' placeholder values with FDInlineScriptCallShape (wfa.inlineScript()).
|
|
25
|
+
*/
|
|
26
|
+
function resolveInlineScripts(inputsObj, stepSysId, allInputScripts, stepInstance) {
|
|
27
|
+
const inputScriptRecords = allInputScripts.filter((r) => r.get('instance')?.asString()?.getValue() === stepSysId);
|
|
28
|
+
for (const scriptRecord of inputScriptRecords) {
|
|
29
|
+
const inputName = scriptRecord.get('input_name')?.asString()?.getValue();
|
|
30
|
+
const scriptJson = scriptRecord.get('script')?.asString()?.getValue();
|
|
31
|
+
if (!inputName || !scriptJson) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
let scriptData;
|
|
35
|
+
try {
|
|
36
|
+
scriptData = JSON.parse(scriptJson);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const currentValue = inputsObj[inputName];
|
|
42
|
+
if (currentValue instanceof sdk_build_core_1.TemplateValueShape) {
|
|
43
|
+
// Replace fd-scripted sub-fields within TemplateValue with inline script shapes
|
|
44
|
+
const templateObj = currentValue.getTemplateValue();
|
|
45
|
+
const newProperties = {};
|
|
46
|
+
const entries = templateObj.getValue();
|
|
47
|
+
for (const [fieldKey, fieldValue] of Object.entries(entries)) {
|
|
48
|
+
const fieldScript = scriptData[fieldKey];
|
|
49
|
+
if (fieldScript?.scriptActive && fieldScript.script) {
|
|
50
|
+
newProperties[fieldKey] = new inline_script_plugin_1.FDInlineScriptCallShape({
|
|
51
|
+
source: stepInstance,
|
|
52
|
+
scriptContent: fieldScript.script,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
newProperties[fieldKey] = fieldValue;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Also add scripted fields not in original template
|
|
60
|
+
for (const [fieldKey, fieldScript] of Object.entries(scriptData)) {
|
|
61
|
+
if (fieldScript?.scriptActive && fieldScript.script && !(fieldKey in entries)) {
|
|
62
|
+
newProperties[fieldKey] = new inline_script_plugin_1.FDInlineScriptCallShape({
|
|
63
|
+
source: stepInstance,
|
|
64
|
+
scriptContent: fieldScript.script,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
inputsObj[inputName] = new sdk_build_core_1.TemplateValueShape({ source: stepInstance, value: newProperties });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Parameter name for the action body arrow function — aligned with subflow pattern */
|
|
73
|
+
const ACTION_PILL_PARAM_NAME = 'params';
|
|
74
|
+
/** Arrow delimiter used in label_cache labels — matches Flow Designer UI */
|
|
75
|
+
const LABEL_DELIMITER = '➛';
|
|
76
|
+
/**
|
|
77
|
+
* Generates a unique variable name from a label, appending _2, _3, etc. for duplicates.
|
|
78
|
+
*/
|
|
79
|
+
function generateUniqueVarName(label, usedNames) {
|
|
80
|
+
const base = (0, flow_constants_1.slugifyString)(label);
|
|
81
|
+
if (!usedNames.has(base)) {
|
|
82
|
+
return base;
|
|
83
|
+
}
|
|
84
|
+
let counter = 2;
|
|
85
|
+
while (usedNames.has(`${base}_${counter}`)) {
|
|
86
|
+
counter++;
|
|
87
|
+
}
|
|
88
|
+
return `${base}_${counter}`;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Resolves a wfa.dataPill() CallExpressionShape to a pill string.
|
|
92
|
+
* Handles both action pills (params.inputs.xxx → {{action.xxx|type}}) and
|
|
93
|
+
* step pills (stepVar.prop → {{step[CID].prop|type}}).
|
|
94
|
+
* Returns undefined for non-pill shapes or unresolvable step references.
|
|
95
|
+
*/
|
|
96
|
+
function resolveAnyPillFromShape(fieldShape, cidMap) {
|
|
97
|
+
if (!(fieldShape instanceof sdk_build_core_1.CallExpressionShape) || fieldShape.getCallee() !== 'wfa.dataPill') {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
const expressionArg = fieldShape.getArgument(0, false);
|
|
101
|
+
if (!(expressionArg instanceof sdk_build_core_1.PropertyAccessShape)) {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
const propertyNames = [];
|
|
105
|
+
expressionArg.getElements().forEach((el) => {
|
|
106
|
+
if (el instanceof sdk_build_core_1.IdentifierShape) {
|
|
107
|
+
propertyNames.push(el.getName());
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
if (propertyNames.length < 2) {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
const typeArg = fieldShape.getArgument(1);
|
|
114
|
+
const dataType = typeArg?.ifString()?.getValue();
|
|
115
|
+
const typeSuffix = dataType ? `|${dataType}` : '';
|
|
116
|
+
if (propertyNames[1] === 'inputs') {
|
|
117
|
+
// Action pill: params.inputs.xxx → {{action.xxx|type}}
|
|
118
|
+
const pathParts = propertyNames.slice(2);
|
|
119
|
+
return { pill: `{{action.${pathParts.join('.')}${typeSuffix}}}`, isStep: false };
|
|
120
|
+
}
|
|
121
|
+
// Step pill: stepVar.prop → {{step[CID].prop|type}}
|
|
122
|
+
const varName = propertyNames[0];
|
|
123
|
+
const stepCid = varName ? cidMap.get(varName) : undefined;
|
|
124
|
+
if (!stepCid) {
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
const pathParts = propertyNames.slice(1);
|
|
128
|
+
return { pill: `{{step[${stepCid}].${pathParts.join('.')}${typeSuffix}}}`, isStep: true };
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Strips the |type suffix from a pill string.
|
|
132
|
+
* e.g., "{{step[CID].record.number|string}}" → "{{step[CID].record.number}}"
|
|
133
|
+
* Note: Mirrors stripPillType in step-instance-plugin.ts (kept separate to avoid exporting private functions).
|
|
134
|
+
*/
|
|
135
|
+
function stripPillTypeSuffix(pill) {
|
|
136
|
+
return pill.replace(/\|[^}]+/, '');
|
|
137
|
+
}
|
|
138
|
+
/** Regex for extracting step pill type annotations — same pattern as STEP_PILL_TYPE_REGEX in step-instance-plugin.ts */
|
|
139
|
+
const STEP_PILL_WITH_TYPE_REGEX = /\{\{step\[([^\]]+)\]\.([^|}]+)(?:\|([^}]+))?\}\}/g;
|
|
140
|
+
/** Extracts the step pill type annotation from a pill string and stores it in pillTypeMap for label_cache. */
|
|
141
|
+
function collectStepPillType(pill, pillTypeMap) {
|
|
142
|
+
for (const match of pill.matchAll(STEP_PILL_WITH_TYPE_REGEX)) {
|
|
143
|
+
const [, cid, pillPath, dataType] = match;
|
|
144
|
+
if (cid && pillPath && dataType) {
|
|
145
|
+
pillTypeMap.set(`${cid}::${pillPath}`, dataType);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/** Creates a sys_element_mapping record — shared by all cases in resolveUnresolvedStepPills. */
|
|
150
|
+
function createElementMapping(factory, source, field, id, stepDefinitionSysId, value) {
|
|
151
|
+
return factory.createRecord({
|
|
152
|
+
source,
|
|
153
|
+
table: 'sys_element_mapping',
|
|
154
|
+
properties: {
|
|
155
|
+
field,
|
|
156
|
+
id,
|
|
157
|
+
table: `var__m_sys_flow_step_definition_input_${stepDefinitionSysId}`,
|
|
158
|
+
value,
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Resolves a TemplateExpressionShape, replacing step pill spans with resolved pill strings.
|
|
164
|
+
* Action pills and plain text spans are preserved from the original template.
|
|
165
|
+
* Returns the full resolved string and whether any step pills were resolved.
|
|
166
|
+
*/
|
|
167
|
+
function resolveStepPillsInTemplate(templateShape, cidMap, pillTypeMap) {
|
|
168
|
+
let result = templateShape.getLiteralText();
|
|
169
|
+
let hasStepPills = false;
|
|
170
|
+
for (const span of templateShape.getSpans()) {
|
|
171
|
+
const expr = span.getExpression();
|
|
172
|
+
const resolved = resolveAnyPillFromShape(expr, cidMap);
|
|
173
|
+
if (resolved) {
|
|
174
|
+
if (resolved.isStep) {
|
|
175
|
+
if (pillTypeMap) {
|
|
176
|
+
collectStepPillType(resolved.pill, pillTypeMap);
|
|
177
|
+
}
|
|
178
|
+
hasStepPills = true;
|
|
179
|
+
}
|
|
180
|
+
result += stripPillTypeSuffix(resolved.pill);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
// Expression may be an already-resolved PillShape (from SDK auto-processing).
|
|
184
|
+
// Strip type suffix so action pills match the expected format.
|
|
185
|
+
const val = String(expr.getValue?.() ?? '');
|
|
186
|
+
result += val.startsWith('{{') ? stripPillTypeSuffix(val) : val;
|
|
187
|
+
}
|
|
188
|
+
result += span.getLiteralText();
|
|
189
|
+
}
|
|
190
|
+
if (!hasStepPills) {
|
|
191
|
+
return undefined;
|
|
192
|
+
}
|
|
193
|
+
return { result, hasStepPills };
|
|
194
|
+
}
|
|
195
|
+
async function resolveUnresolvedStepPills(steps, cidMap, pillTypeMap, factory) {
|
|
196
|
+
if (cidMap.size === 0 || steps.length === 0) {
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
const newRecords = [];
|
|
200
|
+
for (const { shape: stepShape, stepInstanceSysId, stepDefinitionSysId } of steps) {
|
|
201
|
+
const inputs = stepShape.getInputs();
|
|
202
|
+
if (!inputs) {
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
const valuesProperties = inputs.properties({ resolve: false });
|
|
206
|
+
for (const [key, valueShape] of Object.entries(valuesProperties)) {
|
|
207
|
+
if (key === 'inputVariables' || key === 'outputVariables' || key === 'errorHandlingType') {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
// Case 1: Simple step pill — wfa.dataPill(stepVar.prop, 'type')
|
|
211
|
+
const resolved = resolveAnyPillFromShape(valueShape, cidMap);
|
|
212
|
+
if (resolved?.isStep) {
|
|
213
|
+
collectStepPillType(resolved.pill, pillTypeMap);
|
|
214
|
+
newRecords.push(await createElementMapping(factory, stepShape, key, stepInstanceSysId, stepDefinitionSysId, stripPillTypeSuffix(resolved.pill)));
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
// Case 2: TemplateExpressionShape with step pills
|
|
218
|
+
if (valueShape.is(sdk_build_core_1.TemplateExpressionShape)) {
|
|
219
|
+
const templateResolved = resolveStepPillsInTemplate(valueShape, cidMap, pillTypeMap);
|
|
220
|
+
if (templateResolved) {
|
|
221
|
+
newRecords.push(await createElementMapping(factory, stepShape, key, stepInstanceSysId, stepDefinitionSysId, templateResolved.result));
|
|
222
|
+
}
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
// Case 3: TemplateValueShape — scan nested fields for step pills
|
|
226
|
+
if (valueShape.is(sdk_build_core_1.TemplateValueShape)) {
|
|
227
|
+
const templateObj = valueShape.getTemplateValue();
|
|
228
|
+
const entries = [];
|
|
229
|
+
let hasStepPills = false;
|
|
230
|
+
for (const [field, fieldShape] of templateObj.entries({ resolve: false })) {
|
|
231
|
+
const fieldResolved = resolveAnyPillFromShape(fieldShape, cidMap);
|
|
232
|
+
if (fieldResolved?.isStep) {
|
|
233
|
+
collectStepPillType(fieldResolved.pill, pillTypeMap);
|
|
234
|
+
entries.push(`${field}=${stripPillTypeSuffix(fieldResolved.pill)}`);
|
|
235
|
+
hasStepPills = true;
|
|
236
|
+
}
|
|
237
|
+
else if (fieldShape.is(sdk_build_core_1.TemplateExpressionShape)) {
|
|
238
|
+
const resolved = resolveStepPillsInTemplate(fieldShape, cidMap, pillTypeMap);
|
|
239
|
+
if (resolved) {
|
|
240
|
+
entries.push(`${field}=${resolved.result}`);
|
|
241
|
+
hasStepPills = true;
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
const val = fieldShape.getValue?.();
|
|
245
|
+
entries.push(`${field}=${String(val ?? '')}`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
const val = String(fieldShape.getValue?.() ?? '');
|
|
250
|
+
entries.push(`${field}=${val.startsWith('{{') ? stripPillTypeSuffix(val) : val}`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (hasStepPills) {
|
|
254
|
+
newRecords.push(await createElementMapping(factory, stepShape, key, stepInstanceSysId, stepDefinitionSysId, entries.join('^')));
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return newRecords;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Extracts step pill info from sys_element_mapping records created during step processing.
|
|
263
|
+
* Scans the `value` field of each sys_element_mapping record for step pill patterns
|
|
264
|
+
* and deduplicates by (cid, pillPath).
|
|
265
|
+
*
|
|
266
|
+
* This follows the same post-processing pattern as flow-definition-plugin which extracts
|
|
267
|
+
* pills from already-created records rather than during shape transformation.
|
|
268
|
+
*/
|
|
269
|
+
function extractStepPillsFromRecords(records, cidToLabelMap, pillTypeMap) {
|
|
270
|
+
const pills = [];
|
|
271
|
+
const seen = new Set();
|
|
272
|
+
for (const topRec of records) {
|
|
273
|
+
// sys_element_mapping records are nested inside step instance records;
|
|
274
|
+
// flat() expands them so we can scan pill values.
|
|
275
|
+
for (const rec of topRec.flat()) {
|
|
276
|
+
if (rec.getTable() !== 'sys_element_mapping') {
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
const value = rec.get('value')?.asString()?.getValue() ?? '';
|
|
280
|
+
const matches = value.matchAll(STEP_PILL_WITH_TYPE_REGEX);
|
|
281
|
+
for (const match of matches) {
|
|
282
|
+
const [, cid, pillPath, dataType] = match;
|
|
283
|
+
if (!cid || !pillPath) {
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
const key = `${cid}::${pillPath}`;
|
|
287
|
+
if (seen.has(key)) {
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
seen.add(key);
|
|
291
|
+
// Prefer type from pillTypeMap (collected from wfa.dataPill() args before stripping),
|
|
292
|
+
// fall back to regex capture from element_mapping, then default to 'string'
|
|
293
|
+
const resolvedType = pillTypeMap?.get(key) ?? dataType ?? 'string';
|
|
294
|
+
pills.push({
|
|
295
|
+
cid,
|
|
296
|
+
stepLabel: cidToLabelMap.get(cid) ?? '',
|
|
297
|
+
pillPath,
|
|
298
|
+
dataType: resolvedType,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return pills;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Extracts action dot-walk pill info from sys_element_mapping records.
|
|
307
|
+
* Scans for {{action.X.Y...}} patterns where the path has 2+ segments (i.e., deeper than top-level input).
|
|
308
|
+
* Deduplicates by full path. Uses pillTypeMap for type resolution.
|
|
309
|
+
*/
|
|
310
|
+
function extractActionDotWalkPills(records, pillTypeMap) {
|
|
311
|
+
const pills = [];
|
|
312
|
+
const seen = new Set();
|
|
313
|
+
const regex = /\{\{action\.([^|}]+)(?:\|([^}]+))?\}\}/g;
|
|
314
|
+
for (const topRec of records) {
|
|
315
|
+
for (const rec of topRec.flat()) {
|
|
316
|
+
if (rec.getTable() !== 'sys_element_mapping') {
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
const value = rec.get('value')?.asString()?.getValue() ?? '';
|
|
320
|
+
for (const match of value.matchAll(regex)) {
|
|
321
|
+
const [, path, dataType] = match;
|
|
322
|
+
if (!path) {
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
const segments = path.split('.');
|
|
326
|
+
// Only dot-walk pills (2+ segments) — top-level ones are already in inputsConfig
|
|
327
|
+
if (segments.length < 2) {
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
if (seen.has(path)) {
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
seen.add(path);
|
|
334
|
+
const resolvedType = pillTypeMap?.get(`action::${path}`) ?? dataType ?? 'string';
|
|
335
|
+
pills.push({
|
|
336
|
+
fullPath: path,
|
|
337
|
+
parentField: segments[0] ?? '',
|
|
338
|
+
columnName: segments[segments.length - 1] ?? '',
|
|
339
|
+
dataType: resolvedType,
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return pills;
|
|
345
|
+
}
|
|
346
|
+
/** Converts snake_case to Title Case. e.g., "short_description" → "Short Description" */
|
|
347
|
+
function titleCase(s) {
|
|
348
|
+
return s.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Builds the label_cache JSON string from action input definitions, step output pills,
|
|
352
|
+
* action dot-walk pills, and step status entries — matching the format the Flow Designer UI generates.
|
|
353
|
+
*
|
|
354
|
+
* @param inputsConfig - The ObjectShape for the action's inputs configuration
|
|
355
|
+
* @param opts.stepPills - Step output pill info collected during body processing
|
|
356
|
+
* @param opts.dotWalkPills - Action dot-walk pill info (e.g., action.incident.description)
|
|
357
|
+
* @param opts.cidToLabelMap - Map of step CID → label, used for __step_status__ entries
|
|
358
|
+
* @returns JSON string of label cache entries, or empty string if no inputs
|
|
359
|
+
*/
|
|
360
|
+
function buildActionLabelCache(inputsConfig, opts) {
|
|
361
|
+
const { stepPills, dotWalkPills, cidToLabelMap } = opts ?? {};
|
|
362
|
+
const entries = [];
|
|
363
|
+
// Action input entries — also build inputLabelMap for dot-walk resolution in a single pass
|
|
364
|
+
const inputLabelMap = new Map();
|
|
365
|
+
for (const [fieldName, fieldShape] of inputsConfig.entries({ resolve: false })) {
|
|
366
|
+
if (!(fieldShape instanceof sdk_build_core_1.CallExpressionShape)) {
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
const columnApiName = fieldShape.getCallee();
|
|
370
|
+
const baseType = column_helper_2.COLUMN_API_TO_TYPE[columnApiName] ?? 'string';
|
|
371
|
+
const config = fieldShape.getArgument(0)?.ifObject()?.asObject();
|
|
372
|
+
const label = config?.get('label')?.ifString()?.getValue() ?? fieldName;
|
|
373
|
+
const referenceTable = config?.get('referenceTable')?.ifString()?.getValue() ?? '';
|
|
374
|
+
const referenceDisplay = referenceTable ? label : '';
|
|
375
|
+
inputLabelMap.set(fieldName, label);
|
|
376
|
+
entries.push({
|
|
377
|
+
name: `{{action.${fieldName}}}`,
|
|
378
|
+
label: `action${LABEL_DELIMITER}${label}`,
|
|
379
|
+
type: 'action',
|
|
380
|
+
ref: referenceTable,
|
|
381
|
+
reference_display: referenceDisplay,
|
|
382
|
+
base_type: baseType,
|
|
383
|
+
parent_table_name: '',
|
|
384
|
+
column_name: '',
|
|
385
|
+
choices: null,
|
|
386
|
+
attributes: {},
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
// Action dot-walk pill entries (e.g., action.incident.description)
|
|
390
|
+
if (dotWalkPills) {
|
|
391
|
+
for (const pill of dotWalkPills) {
|
|
392
|
+
const segments = pill.fullPath.split('.');
|
|
393
|
+
// Build label with all segments: action➛Incident➛Caller Id➛Email
|
|
394
|
+
const labelParts = segments.map((seg, i) => (i === 0 ? (inputLabelMap.get(seg) ?? seg) : titleCase(seg)));
|
|
395
|
+
const displayLabel = titleCase(pill.columnName);
|
|
396
|
+
entries.push({
|
|
397
|
+
name: `{{action.${pill.fullPath}}}`,
|
|
398
|
+
label: `action${LABEL_DELIMITER}${labelParts.join(LABEL_DELIMITER)}`,
|
|
399
|
+
type: 'action',
|
|
400
|
+
ref: '',
|
|
401
|
+
reference_display: displayLabel,
|
|
402
|
+
base_type: pill.dataType,
|
|
403
|
+
parent_table_name: pill.parentField,
|
|
404
|
+
column_name: pill.columnName,
|
|
405
|
+
choices: null,
|
|
406
|
+
attributes: {},
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
// __step_status__ entries for each step
|
|
411
|
+
if (cidToLabelMap) {
|
|
412
|
+
for (const [cid, stepLabel] of cidToLabelMap.entries()) {
|
|
413
|
+
entries.push({
|
|
414
|
+
name: `{{step[${cid}].__step_status__}}`,
|
|
415
|
+
label: `step${LABEL_DELIMITER}${stepLabel}${LABEL_DELIMITER}Step Status`,
|
|
416
|
+
type: 'step',
|
|
417
|
+
ref: '',
|
|
418
|
+
reference_display: '',
|
|
419
|
+
base_type: 'object',
|
|
420
|
+
parent_table_name: '',
|
|
421
|
+
column_name: '',
|
|
422
|
+
choices: null,
|
|
423
|
+
attributes: {},
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
// Step output pill entries
|
|
428
|
+
if (stepPills) {
|
|
429
|
+
for (const pill of stepPills) {
|
|
430
|
+
// Split path: "record.short_description" → column_name = "short_description"
|
|
431
|
+
const pathParts = pill.pillPath.split('.');
|
|
432
|
+
const columnName = pathParts[pathParts.length - 1] ?? '';
|
|
433
|
+
const displayLabel = titleCase(columnName);
|
|
434
|
+
entries.push({
|
|
435
|
+
name: `{{step[${pill.cid}].${pill.pillPath}}}`,
|
|
436
|
+
label: `step${LABEL_DELIMITER}${pill.stepLabel}${LABEL_DELIMITER}${displayLabel}`,
|
|
437
|
+
type: 'step',
|
|
438
|
+
ref: '',
|
|
439
|
+
reference_display: displayLabel,
|
|
440
|
+
base_type: pill.dataType,
|
|
441
|
+
parent_table_name: '',
|
|
442
|
+
column_name: columnName,
|
|
443
|
+
choices: null,
|
|
444
|
+
attributes: {},
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return entries.length > 0 ? JSON.stringify(entries) : '';
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Converts a string value containing action pills ({{action.xxx}}) into a wfa.dataPill() shape.
|
|
452
|
+
* Handles both simple pills (entire value is a pill) and template pills (mixed text + pills).
|
|
453
|
+
*
|
|
454
|
+
* @returns The converted shape, or undefined if no pills were found/converted.
|
|
455
|
+
*/
|
|
456
|
+
function convertActionPillToShape(value, source, diagnostics, labelCacheMap) {
|
|
457
|
+
const pattern = (0, pill_string_parser_1.detectPillPattern)(value);
|
|
458
|
+
if (pattern === 'none') {
|
|
459
|
+
return undefined;
|
|
460
|
+
}
|
|
461
|
+
const shape = (0, pill_string_parser_1.convertPillStringToShape)(value, source, diagnostics, ACTION_PILL_PARAM_NAME);
|
|
462
|
+
if (!shape) {
|
|
463
|
+
return undefined;
|
|
464
|
+
}
|
|
465
|
+
const pillNames = (0, pill_shape_helpers_1.extractDataPillNames)(value);
|
|
466
|
+
// Single pill: wrap the PropertyAccessShape with wfa.dataPill()
|
|
467
|
+
if (shape instanceof sdk_build_core_1.PropertyAccessShape) {
|
|
468
|
+
const pillName = pillNames[0];
|
|
469
|
+
const dataType = (pillName && labelCacheMap?.get(pillName)) || 'string';
|
|
470
|
+
return (0, pill_shape_helpers_1.wrapWithDataPillCall)(shape, source, dataType);
|
|
471
|
+
}
|
|
472
|
+
if (shape instanceof sdk_build_core_1.IdentifierShape) {
|
|
473
|
+
return (0, pill_shape_helpers_1.wrapWithDataPillCall)(shape, source);
|
|
474
|
+
}
|
|
475
|
+
// Template expression: wrap each PropertyAccessShape span with wfa.dataPill()
|
|
476
|
+
if (shape instanceof sdk_build_core_1.TemplateExpressionShape) {
|
|
477
|
+
const originalSpans = shape.getSpans();
|
|
478
|
+
const wrappedSpans = [];
|
|
479
|
+
let pillIndex = 0;
|
|
480
|
+
for (const span of originalSpans) {
|
|
481
|
+
const expression = span.getExpression();
|
|
482
|
+
const literalText = span.getLiteralText();
|
|
483
|
+
if (expression instanceof sdk_build_core_1.PropertyAccessShape || expression instanceof sdk_build_core_1.IdentifierShape) {
|
|
484
|
+
const pillName = pillNames[pillIndex];
|
|
485
|
+
const dataType = (pillName && labelCacheMap?.get(pillName)) || 'string';
|
|
486
|
+
const wrappedExpr = (0, pill_shape_helpers_1.wrapWithDataPillCall)(expression, source, dataType);
|
|
487
|
+
pillIndex++;
|
|
488
|
+
wrappedSpans.push(new sdk_build_core_1.TemplateSpanShape({
|
|
489
|
+
source,
|
|
490
|
+
expression: wrappedExpr,
|
|
491
|
+
literalText,
|
|
492
|
+
}));
|
|
493
|
+
}
|
|
494
|
+
else {
|
|
495
|
+
wrappedSpans.push(span);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
return new sdk_build_core_1.TemplateExpressionShape({
|
|
499
|
+
source,
|
|
500
|
+
literalText: shape.getLiteralText(),
|
|
501
|
+
spans: wrappedSpans,
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
return undefined;
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Duck-typing check to determine if a value is a Shape instance.
|
|
508
|
+
* Since Shape is imported as a type-only import, we can't use instanceof.
|
|
509
|
+
*/
|
|
510
|
+
function isShapeInstance(val) {
|
|
511
|
+
return (val instanceof sdk_build_core_1.TemplateValueShape ||
|
|
512
|
+
val instanceof sdk_build_core_1.CallExpressionShape ||
|
|
513
|
+
val instanceof sdk_build_core_1.ObjectShape ||
|
|
514
|
+
val instanceof sdk_build_core_1.ArrayShape ||
|
|
515
|
+
val instanceof now_include_plugin_1.NowIncludeShape ||
|
|
516
|
+
val instanceof inline_script_plugin_1.FDInlineScriptCallShape);
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Processes all values in an inputsObj, converting action pill strings to wfa.dataPill() shapes.
|
|
520
|
+
* Also handles pills inside TemplateValueShape fields and nested objects like inputVariables.
|
|
521
|
+
*/
|
|
522
|
+
function convertActionPillsInInputs(inputsObj, source, diagnostics, labelCacheMap) {
|
|
523
|
+
for (const [key, val] of Object.entries(inputsObj)) {
|
|
524
|
+
if (typeof val === 'string' && (0, pill_string_parser_1.detectPillPattern)(val) !== 'none') {
|
|
525
|
+
const converted = convertActionPillToShape(val, source, diagnostics, labelCacheMap);
|
|
526
|
+
if (converted) {
|
|
527
|
+
inputsObj[key] = converted;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
else if (val instanceof sdk_build_core_1.TemplateValueShape) {
|
|
531
|
+
// Process pills inside TemplateValue fields.
|
|
532
|
+
// Use .properties() to get Shape instances directly, not .getValue()
|
|
533
|
+
// which calls base Shape.getValue() returning Symbols for non-primitive shapes.
|
|
534
|
+
const templateProps = val.getTemplateValue().properties();
|
|
535
|
+
let hasChanges = false;
|
|
536
|
+
const newTemplateValue = {};
|
|
537
|
+
for (const [field, fieldShape] of Object.entries(templateProps)) {
|
|
538
|
+
if (fieldShape.isString()) {
|
|
539
|
+
const fieldVal = fieldShape.getValue();
|
|
540
|
+
if ((0, pill_string_parser_1.detectPillPattern)(fieldVal) !== 'none') {
|
|
541
|
+
const converted = convertActionPillToShape(fieldVal, source, diagnostics, labelCacheMap);
|
|
542
|
+
if (converted) {
|
|
543
|
+
newTemplateValue[field] = converted;
|
|
544
|
+
hasChanges = true;
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
newTemplateValue[field] = fieldShape;
|
|
550
|
+
}
|
|
551
|
+
if (hasChanges) {
|
|
552
|
+
inputsObj[key] = new sdk_build_core_1.TemplateValueShape({ source, value: newTemplateValue });
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
else if (val && typeof val === 'object' && !Array.isArray(val) && !isShapeInstance(val)) {
|
|
556
|
+
// Recursively process nested plain objects (e.g., inputVariables, outputVariables)
|
|
557
|
+
// This handles script step input variables which are nested objects with 'value' fields
|
|
558
|
+
const nestedObj = val;
|
|
559
|
+
for (const nestedVal of Object.values(nestedObj)) {
|
|
560
|
+
if (nestedVal &&
|
|
561
|
+
typeof nestedVal === 'object' &&
|
|
562
|
+
!Array.isArray(nestedVal) &&
|
|
563
|
+
!isShapeInstance(nestedVal)) {
|
|
564
|
+
const innerObj = nestedVal;
|
|
565
|
+
// Check if this nested object has a 'value' field with a pill string
|
|
566
|
+
if (typeof innerObj['value'] === 'string' && (0, pill_string_parser_1.detectPillPattern)(innerObj['value']) !== 'none') {
|
|
567
|
+
const converted = convertActionPillToShape(innerObj['value'], source, diagnostics, labelCacheMap);
|
|
568
|
+
if (converted) {
|
|
569
|
+
innerObj['value'] = converted;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
/** Regex to find step[UUID] pills */
|
|
578
|
+
const STEP_PILL_REGEX = /\{\{step\[([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\]\.([^|}]+)(?:\|[^}]*)?\}\}/g;
|
|
579
|
+
/**
|
|
580
|
+
* Converts step output datapill strings ({{step[UUID].property}}) in inputsObj to
|
|
581
|
+
* wfa.dataPill(varName.property, 'type') using the CID-to-identifier map.
|
|
582
|
+
* Handles both simple pills and template patterns (pill mixed with text).
|
|
583
|
+
*/
|
|
584
|
+
function convertStepPillsInInputs(inputsObj, source, cidToIdentifierMap, labelCacheMap) {
|
|
585
|
+
for (const [key, val] of Object.entries(inputsObj)) {
|
|
586
|
+
if (typeof val === 'string') {
|
|
587
|
+
const converted = convertStepPillString(val, source, cidToIdentifierMap, labelCacheMap);
|
|
588
|
+
if (converted) {
|
|
589
|
+
inputsObj[key] = converted;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
else if (val instanceof sdk_build_core_1.TemplateExpressionShape) {
|
|
593
|
+
// A TemplateExpressionShape may still contain raw {{step[UUID].xxx}} pills in its
|
|
594
|
+
// literal text segments if convertActionPillsInInputs converted action pills first,
|
|
595
|
+
// turning the original string into a template expression with step pills left in text.
|
|
596
|
+
const resolved = resolveStepPillsInTemplateExpression(val, source, cidToIdentifierMap, labelCacheMap);
|
|
597
|
+
if (resolved) {
|
|
598
|
+
inputsObj[key] = resolved;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
else if (val instanceof sdk_build_core_1.TemplateValueShape) {
|
|
602
|
+
// Recurse into TemplateValue properties to resolve step pills.
|
|
603
|
+
// Use .properties() to get Shape instances directly, not .getValue()
|
|
604
|
+
// which calls base Shape.getValue() returning Symbols for non-primitive shapes.
|
|
605
|
+
const templateProps = val.getTemplateValue().properties();
|
|
606
|
+
let hasChanges = false;
|
|
607
|
+
const newTemplateValue = {};
|
|
608
|
+
for (const [field, fieldShape] of Object.entries(templateProps)) {
|
|
609
|
+
if (fieldShape.isString()) {
|
|
610
|
+
const strVal = fieldShape.getValue();
|
|
611
|
+
const converted = convertStepPillString(strVal, source, cidToIdentifierMap, labelCacheMap);
|
|
612
|
+
if (converted) {
|
|
613
|
+
newTemplateValue[field] = converted;
|
|
614
|
+
hasChanges = true;
|
|
615
|
+
continue;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
// Keep the original Shape instance as-is (already processed by convertActionPillsInInputs)
|
|
619
|
+
newTemplateValue[field] = fieldShape;
|
|
620
|
+
}
|
|
621
|
+
if (hasChanges) {
|
|
622
|
+
inputsObj[key] = new sdk_build_core_1.TemplateValueShape({ source, value: newTemplateValue });
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Resolves a single step pill match to a wfa.dataPill() CallExpressionShape.
|
|
629
|
+
*/
|
|
630
|
+
function resolveStepPillMatch(uuid, property, source, cidToIdentifierMap, labelCacheMap) {
|
|
631
|
+
const identifier = cidToIdentifierMap.get(uuid);
|
|
632
|
+
if (!identifier) {
|
|
633
|
+
return undefined;
|
|
634
|
+
}
|
|
635
|
+
const expression = new sdk_build_core_1.PropertyAccessShape({
|
|
636
|
+
source,
|
|
637
|
+
elements: [identifier, property],
|
|
638
|
+
});
|
|
639
|
+
const pillName = `step[${uuid}].${property}`;
|
|
640
|
+
const dataType = labelCacheMap?.get(pillName) || 'string';
|
|
641
|
+
return (0, pill_shape_helpers_1.wrapWithDataPillCall)(expression, source, dataType);
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* Resolves step pills remaining in the literal text segments of a TemplateExpressionShape.
|
|
645
|
+
* This handles the case where convertActionPillsInInputs converted action pills first,
|
|
646
|
+
* producing a TemplateExpressionShape whose head/span literal texts still contain
|
|
647
|
+
* raw {{step[UUID].xxx}} pill references that need to be resolved.
|
|
648
|
+
*
|
|
649
|
+
* Algorithm: flatten the template into alternating text/expression segments, expand any
|
|
650
|
+
* step pills found in text segments, then rebuild a TemplateExpressionShape.
|
|
651
|
+
*/
|
|
652
|
+
function resolveStepPillsInTemplateExpression(expr, source, cidToIdentifierMap, labelCacheMap) {
|
|
653
|
+
// Flatten into alternating text/expression segments
|
|
654
|
+
const segments = [{ kind: 'text', value: expr.getLiteralText() }];
|
|
655
|
+
for (const span of expr.getSpans()) {
|
|
656
|
+
segments.push({ kind: 'expr', value: span.getExpression() });
|
|
657
|
+
segments.push({ kind: 'text', value: span.getLiteralText() });
|
|
658
|
+
}
|
|
659
|
+
// Expand step pills in each text segment
|
|
660
|
+
const expanded = [];
|
|
661
|
+
let resolvedCount = 0;
|
|
662
|
+
for (const seg of segments) {
|
|
663
|
+
if (seg.kind === 'expr') {
|
|
664
|
+
expanded.push(seg);
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
667
|
+
const text = seg.value;
|
|
668
|
+
const matches = Array.from(text.matchAll(STEP_PILL_REGEX));
|
|
669
|
+
if (matches.length === 0) {
|
|
670
|
+
expanded.push(seg);
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
let lastIdx = 0;
|
|
674
|
+
for (const match of matches) {
|
|
675
|
+
const uuid = match[1];
|
|
676
|
+
const property = match[2];
|
|
677
|
+
if (!uuid || !property) {
|
|
678
|
+
continue;
|
|
679
|
+
}
|
|
680
|
+
const pillExpr = resolveStepPillMatch(uuid, property, source, cidToIdentifierMap, labelCacheMap);
|
|
681
|
+
if (!pillExpr) {
|
|
682
|
+
// Unresolved pill (CID not in identifier map) — leave the raw {{step[UUID].xxx}}
|
|
683
|
+
// text in the output. This is intentional graceful degradation: the pill text is
|
|
684
|
+
// preserved as-is rather than silently dropped, so the output remains debuggable.
|
|
685
|
+
continue;
|
|
686
|
+
}
|
|
687
|
+
resolvedCount++;
|
|
688
|
+
const matchStart = match.index ?? 0;
|
|
689
|
+
expanded.push({ kind: 'text', value: text.substring(lastIdx, matchStart) });
|
|
690
|
+
expanded.push({ kind: 'expr', value: pillExpr });
|
|
691
|
+
lastIdx = matchStart + match[0].length;
|
|
692
|
+
}
|
|
693
|
+
expanded.push({ kind: 'text', value: text.substring(lastIdx) });
|
|
694
|
+
}
|
|
695
|
+
if (resolvedCount === 0) {
|
|
696
|
+
return undefined;
|
|
697
|
+
}
|
|
698
|
+
// Merge consecutive text segments
|
|
699
|
+
const merged = [];
|
|
700
|
+
for (const seg of expanded) {
|
|
701
|
+
const last = merged[merged.length - 1];
|
|
702
|
+
if (seg.kind === 'text' && last?.kind === 'text') {
|
|
703
|
+
last.value += seg.value;
|
|
704
|
+
}
|
|
705
|
+
else {
|
|
706
|
+
merged.push(seg);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
// Rebuild TemplateExpressionShape: first text is the head, then (expr, text) pairs form spans
|
|
710
|
+
const head = merged[0]?.kind === 'text' ? merged[0].value : '';
|
|
711
|
+
const startIdx = merged[0]?.kind === 'text' ? 1 : 0;
|
|
712
|
+
const newSpans = [];
|
|
713
|
+
for (let i = startIdx; i < merged.length; i += 2) {
|
|
714
|
+
const exprSeg = merged[i];
|
|
715
|
+
const textSeg = merged[i + 1];
|
|
716
|
+
if (exprSeg?.kind === 'expr') {
|
|
717
|
+
newSpans.push(new sdk_build_core_1.TemplateSpanShape({
|
|
718
|
+
source,
|
|
719
|
+
expression: exprSeg.value,
|
|
720
|
+
literalText: textSeg?.kind === 'text' ? textSeg.value : '',
|
|
721
|
+
}));
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
return new sdk_build_core_1.TemplateExpressionShape({ source, literalText: head, spans: newSpans });
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Converts step pill strings to wfa.dataPill() shapes.
|
|
728
|
+
* Handles both:
|
|
729
|
+
* - Simple: "{{step[UUID].record.short_description}}" → wfa.dataPill(varName.record.short_description, 'string')
|
|
730
|
+
* - Template: "{{step[UUID].record.short_description}} is text" → `${wfa.dataPill(...)} is text`
|
|
731
|
+
*/
|
|
732
|
+
function convertStepPillString(value, source, cidToIdentifierMap, labelCacheMap) {
|
|
733
|
+
// Find all step pill matches
|
|
734
|
+
const matches = Array.from(value.matchAll(STEP_PILL_REGEX));
|
|
735
|
+
if (matches.length === 0) {
|
|
736
|
+
return undefined;
|
|
737
|
+
}
|
|
738
|
+
// Simple case: entire value is a single pill
|
|
739
|
+
const firstMatch = matches[0];
|
|
740
|
+
if (matches.length === 1 && firstMatch && firstMatch.index === 0 && firstMatch[0].length === value.length) {
|
|
741
|
+
const uuid = firstMatch[1];
|
|
742
|
+
const property = firstMatch[2];
|
|
743
|
+
if (uuid && property) {
|
|
744
|
+
return resolveStepPillMatch(uuid, property, source, cidToIdentifierMap, labelCacheMap);
|
|
745
|
+
}
|
|
746
|
+
return undefined;
|
|
747
|
+
}
|
|
748
|
+
// Template case: mixed text + pills → TemplateExpressionShape
|
|
749
|
+
const spans = [];
|
|
750
|
+
let head = '';
|
|
751
|
+
let lastIndex = 0;
|
|
752
|
+
for (let i = 0; i < matches.length; i++) {
|
|
753
|
+
const match = matches[i];
|
|
754
|
+
const uuid = match?.[1];
|
|
755
|
+
const property = match?.[2];
|
|
756
|
+
if (!match || !uuid || !property) {
|
|
757
|
+
continue;
|
|
758
|
+
}
|
|
759
|
+
const pillExpr = resolveStepPillMatch(uuid, property, source, cidToIdentifierMap, labelCacheMap);
|
|
760
|
+
if (!pillExpr) {
|
|
761
|
+
continue;
|
|
762
|
+
}
|
|
763
|
+
const matchStart = match.index ?? 0;
|
|
764
|
+
const textBefore = value.substring(lastIndex, matchStart);
|
|
765
|
+
if (spans.length === 0) {
|
|
766
|
+
head = textBefore;
|
|
767
|
+
}
|
|
768
|
+
// Find text after this pill (until next pill or end of string)
|
|
769
|
+
const matchEnd = matchStart + match[0].length;
|
|
770
|
+
const nextMatch = matches[i + 1];
|
|
771
|
+
const textAfter = nextMatch ? value.substring(matchEnd, nextMatch.index) : value.substring(matchEnd);
|
|
772
|
+
spans.push(new sdk_build_core_1.TemplateSpanShape({
|
|
773
|
+
source,
|
|
774
|
+
expression: pillExpr,
|
|
775
|
+
literalText: textAfter,
|
|
776
|
+
}));
|
|
777
|
+
lastIndex = matchEnd;
|
|
778
|
+
}
|
|
779
|
+
if (spans.length === 0) {
|
|
780
|
+
return undefined;
|
|
781
|
+
}
|
|
782
|
+
return new sdk_build_core_1.TemplateExpressionShape({
|
|
783
|
+
source,
|
|
784
|
+
literalText: head,
|
|
785
|
+
spans,
|
|
786
|
+
});
|
|
787
|
+
}
|
|
11
788
|
const actionDefRelationships = {
|
|
12
789
|
sys_hub_step_instance: {
|
|
13
790
|
via: 'action',
|
|
@@ -107,10 +884,14 @@ const actionDefRelationships = {
|
|
|
107
884
|
},
|
|
108
885
|
},
|
|
109
886
|
},
|
|
887
|
+
sys_hub_pill_compound: {
|
|
888
|
+
via: 'attached_to',
|
|
889
|
+
descendant: true,
|
|
890
|
+
},
|
|
110
891
|
};
|
|
111
892
|
exports.ActionDefinitionPlugin = sdk_build_core_1.Plugin.create({
|
|
112
893
|
name: 'ActionDefinitionPlugin',
|
|
113
|
-
docs: [(0, utils_1.createSdkDocEntry)('
|
|
894
|
+
docs: [(0, utils_1.createSdkDocEntry)('Action', ['sys_hub_action_type_definition'])],
|
|
114
895
|
records: {
|
|
115
896
|
sys_hub_action_type_definition: {
|
|
116
897
|
relationships: {
|
|
@@ -151,44 +932,383 @@ exports.ActionDefinitionPlugin = sdk_build_core_1.Plugin.create({
|
|
|
151
932
|
descendant: true,
|
|
152
933
|
},
|
|
153
934
|
},
|
|
154
|
-
toShape(record, { logger }) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
//
|
|
158
|
-
//
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
//
|
|
172
|
-
//
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
//
|
|
180
|
-
//
|
|
181
|
-
//
|
|
182
|
-
//
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
935
|
+
async toShape(record, { descendants, diagnostics, logger }) {
|
|
936
|
+
const actionSysId = record.getId().getValue();
|
|
937
|
+
// Fall back to Record API for actions with error evaluation conditions (not yet fully supported).
|
|
938
|
+
// We check for sys_hub_status_condition records (the actual conditions) rather than
|
|
939
|
+
// sys_hub_action_status_metadata (which can exist as empty shells without conditions).
|
|
940
|
+
const actionStatusMetadataIds = descendants
|
|
941
|
+
.query('sys_hub_action_status_metadata')
|
|
942
|
+
.filter((r) => r.get('action_type_id')?.asString()?.getValue() === actionSysId)
|
|
943
|
+
.map((r) => r.getId().getValue());
|
|
944
|
+
const hasErrorEvaluation = descendants
|
|
945
|
+
.query('sys_hub_status_condition')
|
|
946
|
+
.some((r) => actionStatusMetadataIds.includes(r.get('action_status_metadata_id')?.asString()?.getValue() ?? ''));
|
|
947
|
+
if (hasErrorEvaluation) {
|
|
948
|
+
const actionName = record.get('name')?.getValue() ?? actionSysId;
|
|
949
|
+
logger.warn(`Action '${actionName}' has error evaluation — falling back to Record API`);
|
|
950
|
+
return { success: false };
|
|
951
|
+
}
|
|
952
|
+
// Filter inputs/outputs to only those belonging to this action definition,
|
|
953
|
+
// excluding snapshot-parented records
|
|
954
|
+
const actionInputs = descendants
|
|
955
|
+
.query('sys_hub_action_input')
|
|
956
|
+
.filter((r) => r.get('model')?.asString()?.getValue() === actionSysId);
|
|
957
|
+
const actionOutputs = descendants
|
|
958
|
+
.query('sys_hub_action_output')
|
|
959
|
+
.filter((r) => r.get('model')?.asString()?.getValue() === actionSysId);
|
|
960
|
+
// Fall back to Record API for custom actions with user-defined outputs (not yet fully supported).
|
|
961
|
+
// Core actions (in CORE_ACTIONS_SYS_ID_NAME_MAP) are fully supported and should not fall back.
|
|
962
|
+
// System-generated outputs (__action_status__, __dont_treat_as_error__) are excluded —
|
|
963
|
+
// every action has these automatically, they are not custom outputs.
|
|
964
|
+
const isCoreAction = actionSysId in flow_constants_1.CORE_ACTIONS_SYS_ID_NAME_MAP;
|
|
965
|
+
const customOutputs = actionOutputs.filter((r) => {
|
|
966
|
+
const element = r.get('element')?.asString()?.getValue() ?? '';
|
|
967
|
+
return element !== '__action_status__' && element !== '__dont_treat_as_error__';
|
|
968
|
+
});
|
|
969
|
+
if (!isCoreAction && customOutputs.length > 0) {
|
|
970
|
+
const actionName = record.get('name')?.getValue() ?? actionSysId;
|
|
971
|
+
logger.warn(`Custom action '${actionName}' has outputs — falling back to Record API`);
|
|
972
|
+
return { success: false };
|
|
973
|
+
}
|
|
974
|
+
// Build snapshot output value map: element name → assigned value
|
|
975
|
+
// Output values are stored in two places on the snapshot:
|
|
976
|
+
// 1. sys_variable_value records (plain text values)
|
|
977
|
+
// 2. sys_element_mapping records (datapill/template values)
|
|
978
|
+
const snapshotOutputValues = new Map();
|
|
979
|
+
const snapshots = descendants.query('sys_hub_action_type_snapshot');
|
|
980
|
+
if (snapshots.length > 0) {
|
|
981
|
+
const snapshotId = snapshots[0]?.getId()?.getValue();
|
|
982
|
+
// Check sys_element_mapping for datapill values (field=element name, id=snapshot id)
|
|
983
|
+
const elementMappings = descendants
|
|
984
|
+
.query('sys_element_mapping')
|
|
985
|
+
.filter((r) => r.get('id')?.asString()?.getValue() === snapshotId);
|
|
986
|
+
for (const mapping of elementMappings) {
|
|
987
|
+
const fieldName = mapping.get('field')?.asString()?.getValue();
|
|
988
|
+
const value = mapping.get('value')?.ifString()?.getValue();
|
|
989
|
+
if (fieldName && value !== undefined && value !== '') {
|
|
990
|
+
snapshotOutputValues.set(fieldName, value);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
// Check sys_variable_value for plain text values (on snapshot outputs)
|
|
994
|
+
const snapshotOutputs = descendants
|
|
995
|
+
.query('sys_hub_action_output')
|
|
996
|
+
.filter((r) => r.get('model')?.asString()?.getValue() === snapshotId);
|
|
997
|
+
for (const snapshotOutput of snapshotOutputs) {
|
|
998
|
+
const elementName = snapshotOutput.get('element')?.asString()?.getValue();
|
|
999
|
+
if (!elementName || snapshotOutputValues.has(elementName)) {
|
|
1000
|
+
continue;
|
|
1001
|
+
}
|
|
1002
|
+
const snapshotOutputSysId = snapshotOutput.getId().getValue();
|
|
1003
|
+
const varValues = descendants
|
|
1004
|
+
.query('sys_variable_value')
|
|
1005
|
+
.filter((v) => v.get('variable')?.asString()?.getValue() === snapshotOutputSysId);
|
|
1006
|
+
if (varValues.length > 0) {
|
|
1007
|
+
const value = varValues[0]?.get('value')?.ifString()?.getValue();
|
|
1008
|
+
if (value !== undefined && value !== '') {
|
|
1009
|
+
snapshotOutputValues.set(elementName, value);
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
const inputs = (0, flow_io_to_record_1.buildVariableShapes)(actionInputs, descendants);
|
|
1015
|
+
const outputs = (0, flow_io_to_record_1.buildVariableShapes)(actionOutputs, descendants, undefined, snapshotOutputValues);
|
|
1016
|
+
// Extract label_cache from action definition for datapill type info
|
|
1017
|
+
let labelCacheMap;
|
|
1018
|
+
try {
|
|
1019
|
+
const labelCacheValue = record.get('label_cache')?.asString()?.getValue();
|
|
1020
|
+
if (labelCacheValue) {
|
|
1021
|
+
labelCacheMap = (0, label_cache_parser_1.createLableCacheNameToTypeMap)(labelCacheValue, logger);
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
catch {
|
|
1025
|
+
labelCacheMap = undefined;
|
|
1026
|
+
}
|
|
1027
|
+
// Build step instance shapes for the action body
|
|
1028
|
+
// Filter to only step instances belonging to this action definition,
|
|
1029
|
+
// excluding snapshot-parented step instances
|
|
1030
|
+
const stepInstances = descendants
|
|
1031
|
+
.query('sys_hub_step_instance')
|
|
1032
|
+
.filter((s) => s.get('action')?.asString()?.getValue() === actionSysId);
|
|
1033
|
+
const stepShapes = [];
|
|
1034
|
+
const cidToIdentifierMap = new Map();
|
|
1035
|
+
if (stepInstances.length > 0) {
|
|
1036
|
+
const sortedSteps = [...stepInstances].sort((a, b) => {
|
|
1037
|
+
const orderA = Number(a.get('order')?.getValue() ?? 0);
|
|
1038
|
+
const orderB = Number(b.get('order')?.getValue() ?? 0);
|
|
1039
|
+
return orderA - orderB;
|
|
1040
|
+
});
|
|
1041
|
+
const allVariableValues = descendants.query('sys_variable_value');
|
|
1042
|
+
const allElementMappings = descendants.query('sys_element_mapping');
|
|
1043
|
+
const usedVarNames = new Set();
|
|
1044
|
+
const allInputScripts = descendants.query('sys_hub_input_scripts');
|
|
1045
|
+
const allExtInputs = descendants.query('sys_hub_step_ext_input');
|
|
1046
|
+
const allExtOutputs = descendants.query('sys_hub_step_ext_output');
|
|
1047
|
+
const allDocumentation = descendants.query('sys_documentation');
|
|
1048
|
+
for (const stepInstance of sortedSteps) {
|
|
1049
|
+
const stepSysId = stepInstance.getId().getValue();
|
|
1050
|
+
const stepTypeSysId = stepInstance.get('step_type')?.asString()?.getValue();
|
|
1051
|
+
if (!stepTypeSysId) {
|
|
1052
|
+
continue;
|
|
1053
|
+
}
|
|
1054
|
+
const builtInDef = flow_constants_1.BUILT_IN_STEP_DEFINITIONS[stepTypeSysId];
|
|
1055
|
+
if (!builtInDef) {
|
|
1056
|
+
logger.warn(`Unsupported action step type ${stepTypeSysId} found, falling back to Record() api. Action = ${actionSysId}`);
|
|
1057
|
+
return { success: false };
|
|
1058
|
+
}
|
|
1059
|
+
// Filter variable values for this step instance
|
|
1060
|
+
const stepVarValues = allVariableValues.filter((v) => v.get('document_key')?.asString()?.getValue() === stepSysId);
|
|
1061
|
+
// Build reverse lookup: element name → uiType
|
|
1062
|
+
const elementNameToUiType = {};
|
|
1063
|
+
for (const entry of Object.values(builtInDef.inputs)) {
|
|
1064
|
+
elementNameToUiType[(0, flow_constants_1.getVarEntryName)(entry)] = (0, flow_constants_1.getVarEntryType)(entry);
|
|
1065
|
+
}
|
|
1066
|
+
for (const entry of Object.values(builtInDef.outputs)) {
|
|
1067
|
+
elementNameToUiType[(0, flow_constants_1.getVarEntryName)(entry)] = (0, flow_constants_1.getVarEntryType)(entry);
|
|
1068
|
+
}
|
|
1069
|
+
const inputsObj = {};
|
|
1070
|
+
// Process sys_variable_value records (simple scalar values)
|
|
1071
|
+
for (const varValue of stepVarValues) {
|
|
1072
|
+
const variableSysId = varValue.get('variable')?.asString()?.getValue();
|
|
1073
|
+
const value = varValue.get('value')?.asString()?.getValue();
|
|
1074
|
+
if (variableSysId) {
|
|
1075
|
+
const entry = builtInDef.inputs[variableSysId] ?? builtInDef.outputs[variableSysId];
|
|
1076
|
+
const elementName = entry ? (0, flow_constants_1.getVarEntryName)(entry) : undefined;
|
|
1077
|
+
const uiType = entry ? (0, flow_constants_1.getVarEntryType)(entry) : undefined;
|
|
1078
|
+
if (elementName && value != null) {
|
|
1079
|
+
inputsObj[elementName] = uiType
|
|
1080
|
+
? (0, flow_instance_plugin_1.normalizeInputValue)(value, uiType, stepInstance)
|
|
1081
|
+
: value;
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
// Process sys_element_mapping records (datapill/template values)
|
|
1086
|
+
// Build a lookup of ext input datapill values from element_mappings
|
|
1087
|
+
const extInputMappingValues = {};
|
|
1088
|
+
const stepElementMappings = allElementMappings.filter((m) => m.get('id')?.asString()?.getValue() === stepSysId);
|
|
1089
|
+
for (const mapping of stepElementMappings) {
|
|
1090
|
+
const rawFieldName = mapping.get('field')?.asString()?.getValue();
|
|
1091
|
+
const value = mapping.get('value')?.asString()?.getValue();
|
|
1092
|
+
const table = mapping.get('table')?.asString()?.getValue() ?? '';
|
|
1093
|
+
if (!rawFieldName || !value) {
|
|
1094
|
+
continue;
|
|
1095
|
+
}
|
|
1096
|
+
// Ext input element_mappings have table var__m_sys_hub_step_ext_input_*
|
|
1097
|
+
// Collect their values but don't add to inputsObj (they go under inputVariables)
|
|
1098
|
+
if (table.startsWith('var__m_sys_hub_step_ext_input_')) {
|
|
1099
|
+
extInputMappingValues[rawFieldName] = value;
|
|
1100
|
+
continue;
|
|
1101
|
+
}
|
|
1102
|
+
// Translate platform-internal field names (e.g. __snc_dont_fail_on_error)
|
|
1103
|
+
// to their Fluent API names (e.g. dont_fail_flow_on_error)
|
|
1104
|
+
const fieldName = flow_constants_1.ELEMENT_MAPPING_FIELD_ALIASES[rawFieldName] ?? rawFieldName;
|
|
1105
|
+
const uiType = elementNameToUiType[fieldName];
|
|
1106
|
+
inputsObj[fieldName] = uiType ? (0, flow_instance_plugin_1.normalizeInputValue)(value, uiType, stepInstance) : value;
|
|
1107
|
+
}
|
|
1108
|
+
// Default platform-omitted boolean/choice inputs when the step definition supports them
|
|
1109
|
+
const allElementNames = Object.values(builtInDef.inputs).map(flow_constants_1.getVarEntryName);
|
|
1110
|
+
if (allElementNames.includes('sort_type') && inputsObj['sort_type'] === undefined) {
|
|
1111
|
+
inputsObj['sort_type'] = 'sort_asc';
|
|
1112
|
+
}
|
|
1113
|
+
if (allElementNames.includes('set_workflow') && inputsObj['set_workflow'] === undefined) {
|
|
1114
|
+
inputsObj['set_workflow'] = true;
|
|
1115
|
+
}
|
|
1116
|
+
if (allElementNames.includes('set_autosysfields') &&
|
|
1117
|
+
inputsObj['set_autosysfields'] === undefined) {
|
|
1118
|
+
inputsObj['set_autosysfields'] = true;
|
|
1119
|
+
}
|
|
1120
|
+
if (allElementNames.includes('log_level') && inputsObj['log_level'] === undefined) {
|
|
1121
|
+
inputsObj['log_level'] = 'info';
|
|
1122
|
+
}
|
|
1123
|
+
// Map error_handling_type number to Fluent string
|
|
1124
|
+
const errorHandlingRaw = stepInstance.get('error_handling_type');
|
|
1125
|
+
const errorHandlingType = errorHandlingRaw?.ifNumber()?.getValue() ?? Number(errorHandlingRaw?.ifString()?.getValue());
|
|
1126
|
+
inputsObj['errorHandlingType'] =
|
|
1127
|
+
errorHandlingType === 2 ? 'dont_stop_the_action' : 'stop_the_action';
|
|
1128
|
+
// For script steps, ensure required_run_time is set (default to 'instance' if missing)
|
|
1129
|
+
if (flow_constants_1.BUILT_IN_STEP_SYS_ID_NAME_MAP[stepTypeSysId] === 'script' &&
|
|
1130
|
+
!inputsObj['required_run_time']) {
|
|
1131
|
+
inputsObj['required_run_time'] = 'instance';
|
|
1132
|
+
}
|
|
1133
|
+
// Resolve inline scripts: replace fd-scripted placeholders with wfa.inlineScript()
|
|
1134
|
+
resolveInlineScripts(inputsObj, stepSysId, allInputScripts, stepInstance);
|
|
1135
|
+
// Externalize script field to a separate .js file via Now.include()
|
|
1136
|
+
if (typeof inputsObj['script'] === 'string' && inputsObj['script']) {
|
|
1137
|
+
// Strip (XML carriage return entities) that the platform embeds in script values
|
|
1138
|
+
const cleanScript = inputsObj['script'].replace(/ /g, '');
|
|
1139
|
+
inputsObj['script'] = new now_include_plugin_1.NowIncludeShape({
|
|
1140
|
+
source: stepInstance,
|
|
1141
|
+
path: `./scripts/${stepSysId}.js`,
|
|
1142
|
+
includedText: cleanScript,
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
// Build inputVariables from sys_hub_step_ext_input records
|
|
1146
|
+
const stepExtInputs = allExtInputs.filter((r) => r.get('model_id')?.asString()?.getValue() === stepSysId);
|
|
1147
|
+
if (stepExtInputs.length > 0) {
|
|
1148
|
+
const inputVariablesObj = {};
|
|
1149
|
+
for (const extInput of stepExtInputs) {
|
|
1150
|
+
const elementName = extInput.get('element')?.asString()?.getValue();
|
|
1151
|
+
if (!elementName) {
|
|
1152
|
+
continue;
|
|
1153
|
+
}
|
|
1154
|
+
const extInputSysId = extInput.getId().getValue();
|
|
1155
|
+
const label = extInput.get('label')?.asString()?.getValue() || elementName;
|
|
1156
|
+
// Find value from sys_variable_value or element_mapping (datapill)
|
|
1157
|
+
// Prioritize pills from sys_element_mapping over static values from sys_variable_value
|
|
1158
|
+
const varValueRecord = stepVarValues.find((v) => v.get('variable')?.asString()?.getValue() === extInputSysId);
|
|
1159
|
+
const value = extInputMappingValues[elementName] ||
|
|
1160
|
+
varValueRecord?.get('value')?.asString()?.getValue() ||
|
|
1161
|
+
'';
|
|
1162
|
+
const inputEntry = { label };
|
|
1163
|
+
if (value) {
|
|
1164
|
+
inputEntry['value'] = value;
|
|
1165
|
+
}
|
|
1166
|
+
const inputDefault = extInput.get('default_value')?.asString()?.getValue();
|
|
1167
|
+
if (inputDefault) {
|
|
1168
|
+
inputEntry['defaultValue'] = inputDefault;
|
|
1169
|
+
}
|
|
1170
|
+
inputVariablesObj[elementName] = inputEntry;
|
|
1171
|
+
}
|
|
1172
|
+
inputsObj['inputVariables'] = inputVariablesObj;
|
|
1173
|
+
}
|
|
1174
|
+
// Build outputVariables from sys_hub_step_ext_output records
|
|
1175
|
+
const stepExtOutputs = allExtOutputs.filter((r) => r.get('model_id')?.asString()?.getValue() === stepSysId);
|
|
1176
|
+
if (stepExtOutputs.length > 0) {
|
|
1177
|
+
const outputVariablesObj = {};
|
|
1178
|
+
for (const extOutput of stepExtOutputs) {
|
|
1179
|
+
const elementName = extOutput.get('element')?.asString()?.getValue();
|
|
1180
|
+
if (!elementName) {
|
|
1181
|
+
continue;
|
|
1182
|
+
}
|
|
1183
|
+
const label = extOutput.get('label')?.asString()?.getValue() || elementName;
|
|
1184
|
+
const mandatory = extOutput.get('mandatory')?.asString()?.getValue() === 'true';
|
|
1185
|
+
const internalType = extOutput.get('internal_type')?.asString()?.getValue() ?? 'string';
|
|
1186
|
+
// Check attributes for uiType to handle cases where internal_type is generic
|
|
1187
|
+
const attributes = extOutput.get('attributes')?.asString()?.getValue() ?? '';
|
|
1188
|
+
const uiType = (0, schema_to_flow_object_1.getAttributeValue)(attributes, 'uiType') ?? internalType;
|
|
1189
|
+
const columnApiName = column_helper_1.COLUMN_TYPE_TO_API[uiType] ?? 'StringColumn';
|
|
1190
|
+
const columnProps = { label };
|
|
1191
|
+
if (mandatory) {
|
|
1192
|
+
columnProps['mandatory'] = true;
|
|
1193
|
+
}
|
|
1194
|
+
const maxLength = extOutput.get('max_length')?.asString()?.getValue();
|
|
1195
|
+
if (maxLength) {
|
|
1196
|
+
columnProps['maxLength'] = Number(maxLength);
|
|
1197
|
+
}
|
|
1198
|
+
// Hint is stored in sys_documentation, not on the ext_output record
|
|
1199
|
+
const extOutputName = extOutput.get('name')?.asString()?.getValue();
|
|
1200
|
+
const docRecord = allDocumentation.find((d) => d.get('name')?.asString()?.getValue() === extOutputName &&
|
|
1201
|
+
d.get('element')?.asString()?.getValue() === elementName);
|
|
1202
|
+
const hint = docRecord?.get('hint')?.asString()?.getValue();
|
|
1203
|
+
if (hint) {
|
|
1204
|
+
columnProps['hint'] = hint;
|
|
1205
|
+
}
|
|
1206
|
+
const referenceTable = extOutput.get('reference')?.asString()?.getValue();
|
|
1207
|
+
if (referenceTable &&
|
|
1208
|
+
(columnApiName === 'ReferenceColumn' || columnApiName === 'ListColumn')) {
|
|
1209
|
+
columnProps['referenceTable'] = referenceTable;
|
|
1210
|
+
}
|
|
1211
|
+
const defaultValue = extOutput.get('default_value')?.asString()?.getValue();
|
|
1212
|
+
if (defaultValue) {
|
|
1213
|
+
columnProps['default'] = defaultValue;
|
|
1214
|
+
}
|
|
1215
|
+
outputVariablesObj[elementName] = new sdk_build_core_1.CallExpressionShape({
|
|
1216
|
+
source: extOutput,
|
|
1217
|
+
callee: columnApiName,
|
|
1218
|
+
args: [
|
|
1219
|
+
new sdk_build_core_1.ObjectShape({
|
|
1220
|
+
source: extOutput,
|
|
1221
|
+
properties: columnProps,
|
|
1222
|
+
}),
|
|
1223
|
+
],
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
inputsObj['outputVariables'] = outputVariablesObj;
|
|
1227
|
+
}
|
|
1228
|
+
// Convert action input pills ({{action.xxx}}) to wfa.dataPill(params.inputs.xxx, 'type')
|
|
1229
|
+
convertActionPillsInInputs(inputsObj, stepInstance, diagnostics, labelCacheMap);
|
|
1230
|
+
// Convert step output pills ({{step[UUID].xxx}}) to wfa.dataPill(varName.xxx, 'type')
|
|
1231
|
+
// Earlier steps are already in cidToIdentifierMap since steps are processed in order
|
|
1232
|
+
convertStepPillsInInputs(inputsObj, stepInstance, cidToIdentifierMap, labelCacheMap);
|
|
1233
|
+
const configProperties = {
|
|
1234
|
+
$id: now_id_plugin_1.NowIdShape.from(stepInstance),
|
|
1235
|
+
};
|
|
1236
|
+
const label = stepInstance.get('label')?.asString()?.getValue();
|
|
1237
|
+
if (label && label !== builtInDef.name) {
|
|
1238
|
+
configProperties['label'] = label;
|
|
1239
|
+
}
|
|
1240
|
+
// Built-in/OOB step: emit actionStep.xxx reference
|
|
1241
|
+
const stepIdentifier = (0, utils_2.getBuiltInStepIdentifier)(stepTypeSysId);
|
|
1242
|
+
// Built-in/OOB step: emit actionStep.xxx identifier reference
|
|
1243
|
+
const stepDefShape = stepIdentifier
|
|
1244
|
+
? new sdk_build_core_1.IdentifierShape({ source: stepInstance, name: stepIdentifier })
|
|
1245
|
+
: new sdk_build_core_1.IdentifierShape({ source: stepInstance, name: builtInDef.name });
|
|
1246
|
+
// Map CID → variable identifier for step datapill resolution in later steps
|
|
1247
|
+
const cid = stepInstance.get('cid')?.asString()?.getValue();
|
|
1248
|
+
const varName = generateUniqueVarName(label || builtInDef.name, usedVarNames);
|
|
1249
|
+
usedVarNames.add(varName);
|
|
1250
|
+
if (cid) {
|
|
1251
|
+
cidToIdentifierMap.set(cid, new sdk_build_core_1.IdentifierShape({ source: stepInstance, name: varName }));
|
|
1252
|
+
}
|
|
1253
|
+
const stepCallExpr = new sdk_build_core_1.CallExpressionShape({
|
|
1254
|
+
source: stepInstance,
|
|
1255
|
+
callee: 'wfa.actionStep',
|
|
1256
|
+
args: [
|
|
1257
|
+
stepDefShape,
|
|
1258
|
+
new sdk_build_core_1.ObjectShape({
|
|
1259
|
+
source: stepInstance,
|
|
1260
|
+
properties: configProperties,
|
|
1261
|
+
}),
|
|
1262
|
+
new sdk_build_core_1.ObjectShape({
|
|
1263
|
+
source: stepInstance,
|
|
1264
|
+
properties: inputsObj,
|
|
1265
|
+
}),
|
|
1266
|
+
],
|
|
1267
|
+
});
|
|
1268
|
+
stepShapes.push(new sdk_build_core_1.VariableStatementShape({
|
|
1269
|
+
source: stepInstance,
|
|
1270
|
+
variableName: varName,
|
|
1271
|
+
initializer: stepCallExpr,
|
|
1272
|
+
}));
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
// Build args: config + optional body with step instances
|
|
1276
|
+
const actionConfig = record.transform(({ $ }) => ({
|
|
1277
|
+
$id: $.val(now_id_plugin_1.NowIdShape.from(record)),
|
|
1278
|
+
name: $,
|
|
1279
|
+
annotation: $.def(''),
|
|
1280
|
+
description: $.def(''),
|
|
1281
|
+
access: $.def('public'),
|
|
1282
|
+
category: $.def(''),
|
|
1283
|
+
protection: $.from('sys_policy').def(''),
|
|
1284
|
+
inputs: $.val(inputs),
|
|
1285
|
+
outputs: $.val(outputs),
|
|
1286
|
+
}));
|
|
1287
|
+
const args = [actionConfig];
|
|
1288
|
+
if (stepShapes.length > 0) {
|
|
1289
|
+
args.push(new arrow_function_plugin_1.ArrowFunctionShape({
|
|
1290
|
+
source: record,
|
|
1291
|
+
parameters: [ACTION_PILL_PARAM_NAME],
|
|
1292
|
+
statements: stepShapes,
|
|
1293
|
+
}));
|
|
1294
|
+
}
|
|
1295
|
+
const callExpression = new sdk_build_core_1.CallExpressionShape({
|
|
1296
|
+
source: record,
|
|
1297
|
+
callee: 'Action',
|
|
1298
|
+
args,
|
|
1299
|
+
});
|
|
1300
|
+
// Try to get the existing identifier from the record source, fallback to slugified name
|
|
1301
|
+
const actionName = (0, utils_2.getIdentifierFromRecord)(record) ??
|
|
1302
|
+
(0, flow_constants_1.slugifyString)(String(record.get('internal_name')?.getValue() || record.get('name')?.getValue()));
|
|
1303
|
+
return {
|
|
1304
|
+
success: true,
|
|
1305
|
+
value: new sdk_build_core_1.VariableStatementShape({
|
|
1306
|
+
source: record,
|
|
1307
|
+
variableName: actionName,
|
|
1308
|
+
initializer: callExpression,
|
|
1309
|
+
isExported: true,
|
|
1310
|
+
}),
|
|
1311
|
+
};
|
|
192
1312
|
},
|
|
193
1313
|
async diff(existing, incoming, descendants, context) {
|
|
194
1314
|
return (0, sdk_build_core_1.deleteMultipleDiff)(existing, incoming, descendants, context);
|
|
@@ -209,18 +1329,26 @@ exports.ActionDefinitionPlugin = sdk_build_core_1.Plugin.create({
|
|
|
209
1329
|
sys_complex_object: {
|
|
210
1330
|
coalesce: ['name'],
|
|
211
1331
|
},
|
|
1332
|
+
sys_hub_action_status_metadata: {
|
|
1333
|
+
coalesce: ['action_type_id'],
|
|
1334
|
+
},
|
|
1335
|
+
sys_hub_status_condition: {
|
|
1336
|
+
coalesce: ['action_status_metadata_id', 'order'],
|
|
1337
|
+
},
|
|
212
1338
|
},
|
|
213
1339
|
shapes: [
|
|
214
1340
|
{
|
|
215
1341
|
shape: sdk_build_core_1.CallExpressionShape,
|
|
216
1342
|
fileTypes: ['fluent'],
|
|
217
|
-
async toRecord(callExpression, { factory, diagnostics }) {
|
|
218
|
-
|
|
1343
|
+
async toRecord(callExpression, { factory, diagnostics, transform }) {
|
|
1344
|
+
const callee = callExpression.getCallee();
|
|
1345
|
+
if (callee !== 'Action') {
|
|
219
1346
|
return { success: false };
|
|
220
1347
|
}
|
|
221
1348
|
const relatedRecords = [];
|
|
222
1349
|
const actionConfiguration = callExpression.getArgument(0).asObject();
|
|
223
|
-
const
|
|
1350
|
+
const inputsConfig = actionConfiguration.get('inputs').ifObject()?.asObject();
|
|
1351
|
+
let actionDefinitionRecord = await factory.createRecord({
|
|
224
1352
|
source: callExpression,
|
|
225
1353
|
table: 'sys_hub_action_type_definition',
|
|
226
1354
|
explicitId: actionConfiguration.get('$id'),
|
|
@@ -237,7 +1365,6 @@ exports.ActionDefinitionPlugin = sdk_build_core_1.Plugin.create({
|
|
|
237
1365
|
system_level: $.val(false),
|
|
238
1366
|
})),
|
|
239
1367
|
});
|
|
240
|
-
const inputsConfig = actionConfiguration.get('inputs').ifObject()?.asObject();
|
|
241
1368
|
if (inputsConfig) {
|
|
242
1369
|
const { variableRecords: inputRecords, complexObjectRecords: inputComplexObjects } = await (0, flow_io_to_record_1.buildVariableRecords)({
|
|
243
1370
|
variablesConfig: inputsConfig,
|
|
@@ -262,19 +1389,67 @@ exports.ActionDefinitionPlugin = sdk_build_core_1.Plugin.create({
|
|
|
262
1389
|
});
|
|
263
1390
|
relatedRecords.push(...outputRecords, ...outputComplexObjects);
|
|
264
1391
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
: undefined;
|
|
1392
|
+
// arg[1] is the optional body (ArrowFunction) containing step instances
|
|
1393
|
+
const arg1 = callExpression.getArgument(1);
|
|
1394
|
+
const actionBody = arg1 && !arg1.if(sdk_build_core_1.UndefinedShape) ? arg1.as(arrow_function_plugin_1.ArrowFunctionShape) : undefined;
|
|
269
1395
|
const allInstances = actionBody?.getStatements();
|
|
1396
|
+
const cidMap = new Map();
|
|
1397
|
+
const cidToLabelMap = new Map();
|
|
1398
|
+
const pillTypeMap = new Map();
|
|
1399
|
+
const stepInfos = [];
|
|
1400
|
+
let order = 1;
|
|
270
1401
|
for (const [, v] of (allInstances ?? []).entries()) {
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
1402
|
+
const isVarStatement = v instanceof sdk_build_core_1.VariableStatementShape;
|
|
1403
|
+
const innerShape = isVarStatement ? v.getInitializer() : v;
|
|
1404
|
+
if (innerShape.getSource() instanceof step_instance_plugin_1.StepInstanceShape) {
|
|
1405
|
+
const stepShape = innerShape.getSource();
|
|
1406
|
+
stepShape.setCidMap(cidMap);
|
|
1407
|
+
stepShape.setPillTypeMap(pillTypeMap);
|
|
1408
|
+
const stepResult = await transform.toRecord(innerShape, step_instance_plugin_1.StepInstancePlugin);
|
|
1409
|
+
if (stepResult.success) {
|
|
1410
|
+
const record = stepResult.value.merge({
|
|
1411
|
+
action: actionDefinitionRecord.getId().getValue(),
|
|
1412
|
+
order: order,
|
|
1413
|
+
});
|
|
1414
|
+
relatedRecords.push(record);
|
|
1415
|
+
stepInfos.push({
|
|
1416
|
+
shape: stepShape,
|
|
1417
|
+
stepInstanceSysId: record.getId().getValue(),
|
|
1418
|
+
stepDefinitionSysId: stepShape.getStepDefinitionSysId(),
|
|
1419
|
+
});
|
|
1420
|
+
if (isVarStatement) {
|
|
1421
|
+
const varName = v.getVariableName().getName();
|
|
1422
|
+
const stepCid = record.get('cid')?.asString()?.getValue() ?? '';
|
|
1423
|
+
const stepLabel = record.get('label')?.asString()?.getValue() || varName || 'Step';
|
|
1424
|
+
if (varName && stepCid) {
|
|
1425
|
+
cidMap.set(varName, stepCid);
|
|
1426
|
+
cidToLabelMap.set(stepCid, stepLabel);
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
order++;
|
|
1430
|
+
}
|
|
1431
|
+
else if (isVarStatement) {
|
|
1432
|
+
// Step processing failed - log diagnostic to help debug cascading failures
|
|
1433
|
+
const varName = v.getVariableName().getName();
|
|
1434
|
+
diagnostics.error(innerShape, `Step '${varName}' failed to process. Subsequent steps referencing this step's outputs may not resolve correctly.`);
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
// Re-resolve step pills that couldn't be resolved during auto-processing
|
|
1439
|
+
// (cidMap was empty). Now cidMap is fully populated — walk StepInstanceShape
|
|
1440
|
+
// inputs directly and create correct sys_element_mapping records.
|
|
1441
|
+
const resolvedStepPillRecords = await resolveUnresolvedStepPills(stepInfos, cidMap, pillTypeMap, factory);
|
|
1442
|
+
relatedRecords.push(...resolvedStepPillRecords);
|
|
1443
|
+
const collectedStepPills = extractStepPillsFromRecords(relatedRecords, cidToLabelMap, pillTypeMap);
|
|
1444
|
+
const dotWalkPills = extractActionDotWalkPills(relatedRecords, pillTypeMap);
|
|
1445
|
+
if (inputsConfig) {
|
|
1446
|
+
const labelCacheJson = buildActionLabelCache(inputsConfig, {
|
|
1447
|
+
stepPills: collectedStepPills,
|
|
1448
|
+
dotWalkPills,
|
|
1449
|
+
cidToLabelMap,
|
|
1450
|
+
});
|
|
1451
|
+
if (labelCacheJson) {
|
|
1452
|
+
actionDefinitionRecord = actionDefinitionRecord.merge({ label_cache: labelCacheJson });
|
|
278
1453
|
}
|
|
279
1454
|
}
|
|
280
1455
|
return {
|