@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.
Files changed (144) hide show
  1. package/dist/column-plugin.js +3 -7
  2. package/dist/column-plugin.js.map +1 -1
  3. package/dist/flow/flow-logic/flow-logic-diagnostics.js +5 -5
  4. package/dist/flow/flow-logic/flow-logic-diagnostics.js.map +1 -1
  5. package/dist/flow/plugins/flow-action-definition-plugin.js +1229 -54
  6. package/dist/flow/plugins/flow-action-definition-plugin.js.map +1 -1
  7. package/dist/flow/plugins/flow-data-pill-plugin.js +5 -2
  8. package/dist/flow/plugins/flow-data-pill-plugin.js.map +1 -1
  9. package/dist/flow/plugins/flow-definition-plugin.js +16 -42
  10. package/dist/flow/plugins/flow-definition-plugin.js.map +1 -1
  11. package/dist/flow/plugins/flow-diagnostics-plugin.d.ts +2 -2
  12. package/dist/flow/plugins/flow-diagnostics-plugin.js +2 -2
  13. package/dist/flow/plugins/flow-instance-plugin.js +68 -22
  14. package/dist/flow/plugins/flow-instance-plugin.js.map +1 -1
  15. package/dist/flow/plugins/step-definition-plugin.js +2 -1
  16. package/dist/flow/plugins/step-definition-plugin.js.map +1 -1
  17. package/dist/flow/plugins/step-instance-plugin.d.ts +9 -1
  18. package/dist/flow/plugins/step-instance-plugin.js +649 -136
  19. package/dist/flow/plugins/step-instance-plugin.js.map +1 -1
  20. package/dist/flow/plugins/wfa-datapill-plugin.js +20 -5
  21. package/dist/flow/plugins/wfa-datapill-plugin.js.map +1 -1
  22. package/dist/flow/post-install.js +1 -0
  23. package/dist/flow/post-install.js.map +1 -1
  24. package/dist/flow/utils/complex-object-resolver.js +4 -1
  25. package/dist/flow/utils/complex-object-resolver.js.map +1 -1
  26. package/dist/flow/utils/complex-objects.js +1 -1
  27. package/dist/flow/utils/complex-objects.js.map +1 -1
  28. package/dist/flow/utils/flow-constants.d.ts +66 -2
  29. package/dist/flow/utils/flow-constants.js +402 -6
  30. package/dist/flow/utils/flow-constants.js.map +1 -1
  31. package/dist/flow/utils/flow-io-to-record.d.ts +1 -1
  32. package/dist/flow/utils/flow-io-to-record.js +37 -16
  33. package/dist/flow/utils/flow-io-to-record.js.map +1 -1
  34. package/dist/flow/utils/flow-shapes.js +4 -0
  35. package/dist/flow/utils/flow-shapes.js.map +1 -1
  36. package/dist/flow/utils/label-cache-parser.d.ts +9 -2
  37. package/dist/flow/utils/label-cache-parser.js +32 -4
  38. package/dist/flow/utils/label-cache-parser.js.map +1 -1
  39. package/dist/flow/utils/pill-shape-helpers.d.ts +15 -0
  40. package/dist/flow/utils/pill-shape-helpers.js +35 -0
  41. package/dist/flow/utils/pill-shape-helpers.js.map +1 -0
  42. package/dist/flow/utils/pill-string-parser.js +1 -0
  43. package/dist/flow/utils/pill-string-parser.js.map +1 -1
  44. package/dist/flow/utils/schema-to-flow-object.d.ts +6 -1
  45. package/dist/flow/utils/schema-to-flow-object.js +131 -15
  46. package/dist/flow/utils/schema-to-flow-object.js.map +1 -1
  47. package/dist/flow/utils/utils.d.ts +1 -0
  48. package/dist/flow/utils/utils.js +6 -1
  49. package/dist/flow/utils/utils.js.map +1 -1
  50. package/dist/form-plugin.js +7 -9
  51. package/dist/form-plugin.js.map +1 -1
  52. package/dist/inbound-email-action-plugin.d.ts +10 -0
  53. package/dist/inbound-email-action-plugin.js +128 -0
  54. package/dist/inbound-email-action-plugin.js.map +1 -0
  55. package/dist/index.d.ts +4 -0
  56. package/dist/index.js +4 -0
  57. package/dist/index.js.map +1 -1
  58. package/dist/instance-scan-plugin.js +0 -5
  59. package/dist/instance-scan-plugin.js.map +1 -1
  60. package/dist/property-plugin.js +1 -1
  61. package/dist/property-plugin.js.map +1 -1
  62. package/dist/record-plugin.d.ts +7 -0
  63. package/dist/record-plugin.js +10 -2
  64. package/dist/record-plugin.js.map +1 -1
  65. package/dist/rest-api-plugin.js +8 -1
  66. package/dist/rest-api-plugin.js.map +1 -1
  67. package/dist/schedule-script/scheduled-script-plugin.js +8 -3
  68. package/dist/schedule-script/scheduled-script-plugin.js.map +1 -1
  69. package/dist/service-catalog/service-catalog-base.d.ts +18 -18
  70. package/dist/service-catalog/service-catalog-base.js +22 -22
  71. package/dist/service-catalog/service-catalog-base.js.map +1 -1
  72. package/dist/service-portal/header-footer-plugin.d.ts +2 -0
  73. package/dist/service-portal/header-footer-plugin.js +50 -0
  74. package/dist/service-portal/header-footer-plugin.js.map +1 -0
  75. package/dist/service-portal/menu-plugin.js +3 -22
  76. package/dist/service-portal/menu-plugin.js.map +1 -1
  77. package/dist/service-portal/page-plugin.js +3 -24
  78. package/dist/service-portal/page-plugin.js.map +1 -1
  79. package/dist/service-portal/page-route-map-plugin.d.ts +2 -0
  80. package/dist/service-portal/page-route-map-plugin.js +114 -0
  81. package/dist/service-portal/page-route-map-plugin.js.map +1 -0
  82. package/dist/service-portal/portal-plugin.js +21 -8
  83. package/dist/service-portal/portal-plugin.js.map +1 -1
  84. package/dist/service-portal/utils.d.ts +40 -2
  85. package/dist/service-portal/utils.js +283 -2
  86. package/dist/service-portal/utils.js.map +1 -1
  87. package/dist/service-portal/widget-plugin.js +9 -218
  88. package/dist/service-portal/widget-plugin.js.map +1 -1
  89. package/dist/static-content-plugin.js +4 -0
  90. package/dist/static-content-plugin.js.map +1 -1
  91. package/dist/table-plugin.js +190 -26
  92. package/dist/table-plugin.js.map +1 -1
  93. package/dist/ui-action-plugin.js +1 -4
  94. package/dist/ui-action-plugin.js.map +1 -1
  95. package/dist/ui-page-plugin.js +68 -13
  96. package/dist/ui-page-plugin.js.map +1 -1
  97. package/dist/view-plugin.js +8 -3
  98. package/dist/view-plugin.js.map +1 -1
  99. package/dist/workspace-plugin.js +39 -36
  100. package/dist/workspace-plugin.js.map +1 -1
  101. package/package.json +5 -4
  102. package/src/column-plugin.ts +3 -8
  103. package/src/flow/flow-logic/flow-logic-diagnostics.ts +5 -6
  104. package/src/flow/plugins/flow-action-definition-plugin.ts +1581 -61
  105. package/src/flow/plugins/flow-data-pill-plugin.ts +5 -2
  106. package/src/flow/plugins/flow-definition-plugin.ts +12 -47
  107. package/src/flow/plugins/flow-diagnostics-plugin.ts +2 -2
  108. package/src/flow/plugins/flow-instance-plugin.ts +98 -22
  109. package/src/flow/plugins/step-definition-plugin.ts +2 -1
  110. package/src/flow/plugins/step-instance-plugin.ts +772 -156
  111. package/src/flow/plugins/wfa-datapill-plugin.ts +25 -5
  112. package/src/flow/post-install.ts +1 -0
  113. package/src/flow/utils/complex-object-resolver.ts +4 -1
  114. package/src/flow/utils/complex-objects.ts +1 -1
  115. package/src/flow/utils/flow-constants.ts +421 -5
  116. package/src/flow/utils/flow-io-to-record.ts +43 -17
  117. package/src/flow/utils/flow-shapes.ts +4 -0
  118. package/src/flow/utils/label-cache-parser.ts +33 -4
  119. package/src/flow/utils/pill-shape-helpers.ts +42 -0
  120. package/src/flow/utils/pill-string-parser.ts +1 -0
  121. package/src/flow/utils/schema-to-flow-object.ts +183 -15
  122. package/src/flow/utils/utils.ts +12 -1
  123. package/src/form-plugin.ts +1 -3
  124. package/src/inbound-email-action-plugin.ts +145 -0
  125. package/src/index.ts +4 -0
  126. package/src/instance-scan-plugin.ts +0 -5
  127. package/src/property-plugin.ts +4 -1
  128. package/src/record-plugin.ts +14 -4
  129. package/src/rest-api-plugin.ts +7 -1
  130. package/src/schedule-script/scheduled-script-plugin.ts +14 -3
  131. package/src/service-catalog/service-catalog-base.ts +22 -22
  132. package/src/service-portal/header-footer-plugin.ts +57 -0
  133. package/src/service-portal/menu-plugin.ts +1 -23
  134. package/src/service-portal/page-plugin.ts +3 -28
  135. package/src/service-portal/page-route-map-plugin.ts +124 -0
  136. package/src/service-portal/portal-plugin.ts +33 -10
  137. package/src/service-portal/utils.ts +404 -3
  138. package/src/service-portal/widget-plugin.ts +14 -290
  139. package/src/static-content-plugin.ts +3 -0
  140. package/src/table-plugin.ts +226 -36
  141. package/src/ui-action-plugin.ts +1 -8
  142. package/src/ui-page-plugin.ts +76 -13
  143. package/src/view-plugin.ts +10 -4
  144. 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)('ActionDefinition', ['sys_hub_action_type_definition'])],
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
- logger.warn(`Unsupported record type: ${record.getTable()}, Action definition is not implemented yet`);
156
- return { success: false };
157
- // TODO Uncomment this in next release
158
- // const inputs = buildVariableShapes(descendants.query('sys_hub_action_input'), descendants)
159
- // const outputs = buildVariableShapes(descendants.query('sys_hub_action_output'), descendants)
160
- // // Create the CallExpressionShape for the action definition
161
- // const callExpression = new CallExpressionShape({
162
- // source: record,
163
- // callee: 'ActionDefinition',
164
- // args: [
165
- // record.transform(({ $ }) => ({
166
- // $id: $.val(NowIdShape.from(record)),
167
- // name: $,
168
- // annotation: $.def(''),
169
- // description: $.def(''),
170
- // natlang: $.def(''),
171
- // access: $.def('public'),
172
- // category: $.def(''),
173
- // protection: $.from('sys_policy').def(''),
174
- // inputs: $.val(inputs),
175
- // outputs: $.val(outputs),
176
- // })),
177
- // ],
178
- // })
179
- // // Try to get the existing identifier from the record source, fallback to slugified name
180
- // const actionName =
181
- // getIdentifierFromRecord(record) ??
182
- // slugifyString(String(record.get('internal_name')?.getValue() || record.get('name')?.getValue()))
183
- // return {
184
- // success: true,
185
- // value: new VariableStatementShape({
186
- // source: record,
187
- // variableName: actionName,
188
- // initializer: callExpression,
189
- // isExported: true,
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 &#13; (XML carriage return entities) that the platform embeds in script values
1138
+ const cleanScript = inputsObj['script'].replace(/&#13;/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
- if (callExpression.getCallee() !== 'ActionDefinition') {
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 actionDefinitionRecord = await factory.createRecord({
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
- let actionBody;
266
- actionBody = !callExpression.getArgument(1)?.if(sdk_build_core_1.UndefinedShape)
267
- ? callExpression.getArgument(1)?.as(arrow_function_plugin_1.ArrowFunctionShape)
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 source = v.getSource();
272
- if (source instanceof step_instance_plugin_1.StepInstanceShape) {
273
- const instanceRecord = v.as(sdk_build_core_1.Record);
274
- const record = instanceRecord.merge({
275
- action: actionDefinitionRecord.getId().getValue(),
276
- });
277
- relatedRecords.push(record);
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 {