@servicenow/sdk-build-plugins 4.6.1 → 4.7.1

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 (279) hide show
  1. package/dist/acl-plugin.js +0 -3
  2. package/dist/acl-plugin.js.map +1 -1
  3. package/dist/applicability-plugin.js +0 -2
  4. package/dist/applicability-plugin.js.map +1 -1
  5. package/dist/application-menu-plugin.js +0 -2
  6. package/dist/application-menu-plugin.js.map +1 -1
  7. package/dist/arrow-function-plugin.js +0 -1
  8. package/dist/arrow-function-plugin.js.map +1 -1
  9. package/dist/atf/test-plugin.js +0 -2
  10. package/dist/atf/test-plugin.js.map +1 -1
  11. package/dist/basic-syntax-plugin.js +0 -1
  12. package/dist/basic-syntax-plugin.js.map +1 -1
  13. package/dist/business-rule-plugin.js +0 -1
  14. package/dist/business-rule-plugin.js.map +1 -1
  15. package/dist/call-expression-plugin.js +0 -1
  16. package/dist/call-expression-plugin.js.map +1 -1
  17. package/dist/claims-plugin.js +0 -1
  18. package/dist/claims-plugin.js.map +1 -1
  19. package/dist/client-script-plugin.js +0 -1
  20. package/dist/client-script-plugin.js.map +1 -1
  21. package/dist/column-plugin.js +24 -7
  22. package/dist/column-plugin.js.map +1 -1
  23. package/dist/cross-scope-privilege-plugin.js +0 -1
  24. package/dist/cross-scope-privilege-plugin.js.map +1 -1
  25. package/dist/dashboard/dashboard-plugin.js +0 -2
  26. package/dist/dashboard/dashboard-plugin.js.map +1 -1
  27. package/dist/data-plugin.js +0 -1
  28. package/dist/data-plugin.js.map +1 -1
  29. package/dist/data-policy-plugin.d.ts +2 -0
  30. package/dist/data-policy-plugin.js +276 -0
  31. package/dist/data-policy-plugin.js.map +1 -0
  32. package/dist/email-notification-plugin.js +2 -3
  33. package/dist/email-notification-plugin.js.map +1 -1
  34. package/dist/flow/flow-logic/flow-logic-constants.d.ts +2 -0
  35. package/dist/flow/flow-logic/flow-logic-constants.js +6 -1
  36. package/dist/flow/flow-logic/flow-logic-constants.js.map +1 -1
  37. package/dist/flow/flow-logic/flow-logic-diagnostics.js +192 -56
  38. package/dist/flow/flow-logic/flow-logic-diagnostics.js.map +1 -1
  39. package/dist/flow/flow-logic/flow-logic-plugin-helpers.d.ts +2 -1
  40. package/dist/flow/flow-logic/flow-logic-plugin-helpers.js +44 -5
  41. package/dist/flow/flow-logic/flow-logic-plugin-helpers.js.map +1 -1
  42. package/dist/flow/flow-logic/flow-logic-plugin.js +279 -29
  43. package/dist/flow/flow-logic/flow-logic-plugin.js.map +1 -1
  44. package/dist/flow/flow-logic/flow-logic-shapes.d.ts +15 -0
  45. package/dist/flow/flow-logic/flow-logic-shapes.js +25 -1
  46. package/dist/flow/flow-logic/flow-logic-shapes.js.map +1 -1
  47. package/dist/flow/plugins/approval-rules-plugin.js +0 -1
  48. package/dist/flow/plugins/approval-rules-plugin.js.map +1 -1
  49. package/dist/flow/plugins/flow-action-definition-plugin.js +804 -205
  50. package/dist/flow/plugins/flow-action-definition-plugin.js.map +1 -1
  51. package/dist/flow/plugins/flow-data-pill-plugin.js +3 -5
  52. package/dist/flow/plugins/flow-data-pill-plugin.js.map +1 -1
  53. package/dist/flow/plugins/flow-definition-plugin.js +84 -17
  54. package/dist/flow/plugins/flow-definition-plugin.js.map +1 -1
  55. package/dist/flow/plugins/flow-diagnostics-plugin.js +65 -3
  56. package/dist/flow/plugins/flow-diagnostics-plugin.js.map +1 -1
  57. package/dist/flow/plugins/flow-instance-plugin.js +13 -5
  58. package/dist/flow/plugins/flow-instance-plugin.js.map +1 -1
  59. package/dist/flow/plugins/flow-trigger-instance-plugin.js +0 -1
  60. package/dist/flow/plugins/flow-trigger-instance-plugin.js.map +1 -1
  61. package/dist/flow/plugins/inline-script-plugin.js +0 -1
  62. package/dist/flow/plugins/inline-script-plugin.js.map +1 -1
  63. package/dist/flow/plugins/step-definition-plugin.js +0 -2
  64. package/dist/flow/plugins/step-definition-plugin.js.map +1 -1
  65. package/dist/flow/plugins/step-instance-plugin.js +216 -77
  66. package/dist/flow/plugins/step-instance-plugin.js.map +1 -1
  67. package/dist/flow/plugins/trigger-plugin.js +0 -2
  68. package/dist/flow/plugins/trigger-plugin.js.map +1 -1
  69. package/dist/flow/plugins/wfa-datapill-plugin.js +0 -1
  70. package/dist/flow/plugins/wfa-datapill-plugin.js.map +1 -1
  71. package/dist/flow/utils/datapill-transformer.js +9 -5
  72. package/dist/flow/utils/datapill-transformer.js.map +1 -1
  73. package/dist/flow/utils/flow-constants.d.ts +12 -0
  74. package/dist/flow/utils/flow-constants.js +17 -3
  75. package/dist/flow/utils/flow-constants.js.map +1 -1
  76. package/dist/flow/utils/flow-io-to-record.d.ts +1 -1
  77. package/dist/flow/utils/flow-io-to-record.js +21 -13
  78. package/dist/flow/utils/flow-io-to-record.js.map +1 -1
  79. package/dist/flow/utils/flow-pill-utils.d.ts +26 -0
  80. package/dist/flow/utils/flow-pill-utils.js +50 -0
  81. package/dist/flow/utils/flow-pill-utils.js.map +1 -0
  82. package/dist/flow/utils/flow-stage-processor.d.ts +138 -0
  83. package/dist/flow/utils/flow-stage-processor.js +665 -0
  84. package/dist/flow/utils/flow-stage-processor.js.map +1 -0
  85. package/dist/flow/utils/pill-string-parser.js +28 -43
  86. package/dist/flow/utils/pill-string-parser.js.map +1 -1
  87. package/dist/flow/utils/utils.d.ts +11 -6
  88. package/dist/flow/utils/utils.js +37 -28
  89. package/dist/flow/utils/utils.js.map +1 -1
  90. package/dist/form-plugin.js +4 -14
  91. package/dist/form-plugin.js.map +1 -1
  92. package/dist/html-import-plugin.js +0 -1
  93. package/dist/html-import-plugin.js.map +1 -1
  94. package/dist/import-sets-plugin.js +0 -2
  95. package/dist/import-sets-plugin.js.map +1 -1
  96. package/dist/inbound-email-action-plugin.js +0 -1
  97. package/dist/inbound-email-action-plugin.js.map +1 -1
  98. package/dist/index.d.ts +2 -1
  99. package/dist/index.js +5 -1
  100. package/dist/index.js.map +1 -1
  101. package/dist/instance-scan-plugin.js +0 -7
  102. package/dist/instance-scan-plugin.js.map +1 -1
  103. package/dist/json-plugin.js +0 -1
  104. package/dist/json-plugin.js.map +1 -1
  105. package/dist/list-plugin.js +4 -1
  106. package/dist/list-plugin.js.map +1 -1
  107. package/dist/now-attach-plugin.js +0 -1
  108. package/dist/now-attach-plugin.js.map +1 -1
  109. package/dist/now-config-plugin.js +0 -1
  110. package/dist/now-config-plugin.js.map +1 -1
  111. package/dist/now-id-plugin.js +0 -1
  112. package/dist/now-id-plugin.js.map +1 -1
  113. package/dist/now-include-plugin.js +0 -1
  114. package/dist/now-include-plugin.js.map +1 -1
  115. package/dist/now-ref-plugin.js +0 -1
  116. package/dist/now-ref-plugin.js.map +1 -1
  117. package/dist/now-unresolved-plugin.js +0 -1
  118. package/dist/now-unresolved-plugin.js.map +1 -1
  119. package/dist/package-json-plugin.js +3 -2
  120. package/dist/package-json-plugin.js.map +1 -1
  121. package/dist/property-plugin.js +0 -2
  122. package/dist/property-plugin.js.map +1 -1
  123. package/dist/record-plugin.d.ts +2 -0
  124. package/dist/record-plugin.js +2 -2
  125. package/dist/record-plugin.js.map +1 -1
  126. package/dist/repack/lint/Rules.d.ts +1 -2
  127. package/dist/rest-api-plugin.js +6 -5
  128. package/dist/rest-api-plugin.js.map +1 -1
  129. package/dist/role-plugin.js +0 -1
  130. package/dist/role-plugin.js.map +1 -1
  131. package/dist/schedule-script/scheduled-script-plugin.js +5 -4
  132. package/dist/schedule-script/scheduled-script-plugin.js.map +1 -1
  133. package/dist/script-action-plugin.js +0 -2
  134. package/dist/script-action-plugin.js.map +1 -1
  135. package/dist/script-include-plugin.js +0 -4
  136. package/dist/script-include-plugin.js.map +1 -1
  137. package/dist/server-module-plugin/index.js +2 -3
  138. package/dist/server-module-plugin/index.js.map +1 -1
  139. package/dist/service-catalog/catalog-clientscript-plugin.js +0 -2
  140. package/dist/service-catalog/catalog-clientscript-plugin.js.map +1 -1
  141. package/dist/service-catalog/catalog-item-plugin.js +0 -2
  142. package/dist/service-catalog/catalog-item-plugin.js.map +1 -1
  143. package/dist/service-catalog/catalog-ui-policy-plugin.js +0 -2
  144. package/dist/service-catalog/catalog-ui-policy-plugin.js.map +1 -1
  145. package/dist/service-catalog/sc-record-producer-plugin.js +0 -2
  146. package/dist/service-catalog/sc-record-producer-plugin.js.map +1 -1
  147. package/dist/service-catalog/variable-set-plugin.js +0 -2
  148. package/dist/service-catalog/variable-set-plugin.js.map +1 -1
  149. package/dist/service-portal/angular-provider-plugin.js +0 -2
  150. package/dist/service-portal/angular-provider-plugin.js.map +1 -1
  151. package/dist/service-portal/dependency-plugin.js +3 -5
  152. package/dist/service-portal/dependency-plugin.js.map +1 -1
  153. package/dist/service-portal/header-footer-plugin.js +3 -5
  154. package/dist/service-portal/header-footer-plugin.js.map +1 -1
  155. package/dist/service-portal/menu-plugin.js +0 -1
  156. package/dist/service-portal/menu-plugin.js.map +1 -1
  157. package/dist/service-portal/page-plugin.js +0 -1
  158. package/dist/service-portal/page-plugin.js.map +1 -1
  159. package/dist/service-portal/page-route-map-plugin.js +0 -1
  160. package/dist/service-portal/page-route-map-plugin.js.map +1 -1
  161. package/dist/service-portal/portal-plugin.js +0 -2
  162. package/dist/service-portal/portal-plugin.js.map +1 -1
  163. package/dist/service-portal/theme-plugin.js +0 -2
  164. package/dist/service-portal/theme-plugin.js.map +1 -1
  165. package/dist/service-portal/widget-plugin.js +3 -5
  166. package/dist/service-portal/widget-plugin.js.map +1 -1
  167. package/dist/sla-plugin.js +0 -2
  168. package/dist/sla-plugin.js.map +1 -1
  169. package/dist/static-content-plugin.js +32 -3
  170. package/dist/static-content-plugin.js.map +1 -1
  171. package/dist/table-plugin.js +102 -11
  172. package/dist/table-plugin.js.map +1 -1
  173. package/dist/ui-action-plugin.js +26 -17
  174. package/dist/ui-action-plugin.js.map +1 -1
  175. package/dist/ui-page-plugin.js +159 -17
  176. package/dist/ui-page-plugin.js.map +1 -1
  177. package/dist/ui-policy-plugin.js +0 -1
  178. package/dist/ui-policy-plugin.js.map +1 -1
  179. package/dist/user-preference-plugin.js +0 -2
  180. package/dist/user-preference-plugin.js.map +1 -1
  181. package/dist/utils.d.ts +1 -9
  182. package/dist/utils.js +0 -14
  183. package/dist/utils.js.map +1 -1
  184. package/dist/ux-list-menu-config-plugin.js +0 -2
  185. package/dist/ux-list-menu-config-plugin.js.map +1 -1
  186. package/dist/view-plugin.js +0 -1
  187. package/dist/view-plugin.js.map +1 -1
  188. package/dist/workspace-plugin.js +0 -2
  189. package/dist/workspace-plugin.js.map +1 -1
  190. package/package.json +6 -6
  191. package/src/acl-plugin.ts +1 -4
  192. package/src/applicability-plugin.ts +0 -2
  193. package/src/application-menu-plugin.ts +0 -2
  194. package/src/arrow-function-plugin.ts +0 -1
  195. package/src/atf/test-plugin.ts +0 -2
  196. package/src/basic-syntax-plugin.ts +0 -1
  197. package/src/business-rule-plugin.ts +1 -2
  198. package/src/call-expression-plugin.ts +0 -1
  199. package/src/claims-plugin.ts +0 -1
  200. package/src/client-script-plugin.ts +1 -2
  201. package/src/column-plugin.ts +29 -9
  202. package/src/cross-scope-privilege-plugin.ts +1 -2
  203. package/src/dashboard/dashboard-plugin.ts +0 -2
  204. package/src/data-plugin.ts +0 -1
  205. package/src/data-policy-plugin.ts +333 -0
  206. package/src/email-notification-plugin.ts +8 -4
  207. package/src/flow/flow-logic/flow-logic-constants.ts +6 -0
  208. package/src/flow/flow-logic/flow-logic-diagnostics.ts +236 -58
  209. package/src/flow/flow-logic/flow-logic-plugin-helpers.ts +59 -6
  210. package/src/flow/flow-logic/flow-logic-plugin.ts +368 -38
  211. package/src/flow/flow-logic/flow-logic-shapes.ts +25 -0
  212. package/src/flow/plugins/approval-rules-plugin.ts +0 -1
  213. package/src/flow/plugins/flow-action-definition-plugin.ts +940 -208
  214. package/src/flow/plugins/flow-data-pill-plugin.ts +3 -5
  215. package/src/flow/plugins/flow-definition-plugin.ts +159 -26
  216. package/src/flow/plugins/flow-diagnostics-plugin.ts +89 -3
  217. package/src/flow/plugins/flow-instance-plugin.ts +26 -12
  218. package/src/flow/plugins/flow-trigger-instance-plugin.ts +0 -1
  219. package/src/flow/plugins/inline-script-plugin.ts +0 -1
  220. package/src/flow/plugins/step-definition-plugin.ts +0 -2
  221. package/src/flow/plugins/step-instance-plugin.ts +259 -65
  222. package/src/flow/plugins/trigger-plugin.ts +0 -2
  223. package/src/flow/plugins/wfa-datapill-plugin.ts +0 -1
  224. package/src/flow/utils/datapill-transformer.ts +13 -5
  225. package/src/flow/utils/flow-constants.ts +19 -1
  226. package/src/flow/utils/flow-io-to-record.ts +29 -19
  227. package/src/flow/utils/flow-pill-utils.ts +48 -0
  228. package/src/flow/utils/flow-stage-processor.ts +831 -0
  229. package/src/flow/utils/pill-string-parser.ts +29 -47
  230. package/src/flow/utils/utils.ts +39 -35
  231. package/src/form-plugin.ts +5 -15
  232. package/src/html-import-plugin.ts +0 -1
  233. package/src/import-sets-plugin.ts +0 -2
  234. package/src/inbound-email-action-plugin.ts +1 -2
  235. package/src/index.ts +7 -1
  236. package/src/instance-scan-plugin.ts +0 -7
  237. package/src/json-plugin.ts +0 -1
  238. package/src/list-plugin.ts +6 -2
  239. package/src/now-attach-plugin.ts +0 -1
  240. package/src/now-config-plugin.ts +0 -1
  241. package/src/now-id-plugin.ts +0 -1
  242. package/src/now-include-plugin.ts +0 -1
  243. package/src/now-ref-plugin.ts +0 -1
  244. package/src/now-unresolved-plugin.ts +0 -1
  245. package/src/package-json-plugin.ts +8 -3
  246. package/src/property-plugin.ts +0 -2
  247. package/src/record-plugin.ts +3 -3
  248. package/src/repack/lint/Rules.ts +1 -1
  249. package/src/rest-api-plugin.ts +7 -6
  250. package/src/role-plugin.ts +1 -2
  251. package/src/schedule-script/scheduled-script-plugin.ts +11 -5
  252. package/src/script-action-plugin.ts +0 -2
  253. package/src/script-include-plugin.ts +0 -4
  254. package/src/server-module-plugin/index.ts +2 -3
  255. package/src/service-catalog/catalog-clientscript-plugin.ts +0 -2
  256. package/src/service-catalog/catalog-item-plugin.ts +0 -2
  257. package/src/service-catalog/catalog-ui-policy-plugin.ts +0 -2
  258. package/src/service-catalog/sc-record-producer-plugin.ts +0 -2
  259. package/src/service-catalog/variable-set-plugin.ts +0 -2
  260. package/src/service-portal/angular-provider-plugin.ts +0 -2
  261. package/src/service-portal/dependency-plugin.ts +0 -2
  262. package/src/service-portal/header-footer-plugin.ts +0 -2
  263. package/src/service-portal/menu-plugin.ts +1 -2
  264. package/src/service-portal/page-plugin.ts +1 -2
  265. package/src/service-portal/page-route-map-plugin.ts +1 -2
  266. package/src/service-portal/portal-plugin.ts +0 -2
  267. package/src/service-portal/theme-plugin.ts +0 -2
  268. package/src/service-portal/widget-plugin.ts +0 -2
  269. package/src/sla-plugin.ts +0 -2
  270. package/src/static-content-plugin.ts +37 -4
  271. package/src/table-plugin.ts +118 -16
  272. package/src/ui-action-plugin.ts +30 -17
  273. package/src/ui-page-plugin.ts +188 -20
  274. package/src/ui-policy-plugin.ts +1 -2
  275. package/src/user-preference-plugin.ts +0 -2
  276. package/src/utils.ts +0 -15
  277. package/src/ux-list-menu-config-plugin.ts +0 -2
  278. package/src/view-plugin.ts +0 -1
  279. package/src/workspace-plugin.ts +0 -2
@@ -3,19 +3,22 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ActionDefinitionPlugin = void 0;
4
4
  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
+ const built_in_complex_objects_1 = require("../utils/built-in-complex-objects");
6
7
  const flow_to_xml_1 = require("../utils/flow-to-xml");
7
8
  const flow_constants_1 = require("../utils/flow-constants");
9
+ const flow_pill_utils_1 = require("../utils/flow-pill-utils");
8
10
  const column_helper_1 = require("../../column/column-helper");
9
11
  const schema_to_flow_object_1 = require("../utils/schema-to-flow-object");
10
12
  const flow_instance_plugin_1 = require("./flow-instance-plugin");
13
+ const flow_shapes_1 = require("../utils/flow-shapes");
11
14
  const arrow_function_plugin_1 = require("../../arrow-function-plugin");
12
- const utils_1 = require("../../utils");
13
15
  const inline_script_plugin_1 = require("./inline-script-plugin");
14
16
  const step_instance_plugin_1 = require("./step-instance-plugin");
15
17
  const now_id_plugin_1 = require("../../now-id-plugin");
16
18
  const now_include_plugin_1 = require("../../now-include-plugin");
17
- const utils_2 = require("../utils/utils");
19
+ const utils_1 = require("../utils/utils");
18
20
  const pill_string_parser_1 = require("../utils/pill-string-parser");
21
+ const data_pill_shapes_1 = require("../utils/data-pill-shapes");
19
22
  const label_cache_parser_1 = require("../utils/label-cache-parser");
20
23
  const column_helper_2 = require("../../column/column-helper");
21
24
  const pill_shape_helpers_1 = require("../utils/pill-shape-helpers");
@@ -127,70 +130,52 @@ function resolveAnyPillFromShape(fieldShape, cidMap) {
127
130
  const pathParts = propertyNames.slice(1);
128
131
  return { pill: `{{step[${stepCid}].${pathParts.join('.')}${typeSuffix}}}`, isStep: true };
129
132
  }
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
- }
133
+ // stripPillType, collectPillTypes, and STEP_PILL_TYPE_REGEX are imported from ../utils/flow-pill-utils
149
134
  /** Creates a sys_element_mapping record — shared by all cases in resolveUnresolvedStepPills. */
150
- function createElementMapping(factory, source, field, id, stepDefinitionSysId, value) {
135
+ function createElementMapping(factory, source, field, id, mappingTable, value) {
151
136
  return factory.createRecord({
152
137
  source,
153
138
  table: 'sys_element_mapping',
154
139
  properties: {
155
140
  field,
156
141
  id,
157
- table: `var__m_sys_flow_step_definition_input_${stepDefinitionSysId}`,
142
+ table: mappingTable,
158
143
  value,
159
144
  },
160
145
  });
161
146
  }
162
147
  /**
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.
148
+ * Resolves ALL pills in a TemplateExpressionShape both action pills (PillShape) and
149
+ * step pills (wfa.dataPill CallExpressionShape). Returns the full resolved pill string
150
+ * with type suffixes stripped. Plain text spans are preserved verbatim.
166
151
  */
167
- function resolveStepPillsInTemplate(templateShape, cidMap, pillTypeMap) {
152
+ function resolveAllPillsInTemplate(templateShape, cidMap, pillTypeMap) {
168
153
  let result = templateShape.getLiteralText();
169
- let hasStepPills = false;
170
154
  for (const span of templateShape.getSpans()) {
171
155
  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;
156
+ if (expr instanceof data_pill_shapes_1.PillShape) {
157
+ const raw = expr.getValue();
158
+ if (pillTypeMap) {
159
+ (0, flow_pill_utils_1.collectPillTypes)(raw, pillTypeMap);
179
160
  }
180
- result += stripPillTypeSuffix(resolved.pill);
161
+ result += (0, flow_pill_utils_1.stripPillType)(raw);
181
162
  }
182
163
  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;
164
+ const resolved = resolveAnyPillFromShape(expr, cidMap);
165
+ if (resolved) {
166
+ if (resolved.isStep && pillTypeMap) {
167
+ (0, flow_pill_utils_1.collectPillTypes)(resolved.pill, pillTypeMap);
168
+ }
169
+ result += (0, flow_pill_utils_1.stripPillType)(resolved.pill);
170
+ }
171
+ else {
172
+ const val = String(expr.getValue?.() ?? '');
173
+ result += val.startsWith('{{') ? (0, flow_pill_utils_1.stripPillType)(val) : val;
174
+ }
187
175
  }
188
176
  result += span.getLiteralText();
189
177
  }
190
- if (!hasStepPills) {
191
- return undefined;
192
- }
193
- return { result, hasStepPills };
178
+ return result;
194
179
  }
195
180
  async function resolveUnresolvedStepPills(steps, cidMap, pillTypeMap, factory) {
196
181
  if (cidMap.size === 0 || steps.length === 0) {
@@ -203,6 +188,33 @@ async function resolveUnresolvedStepPills(steps, cidMap, pillTypeMap, factory) {
203
188
  continue;
204
189
  }
205
190
  const valuesProperties = inputs.properties({ resolve: false });
191
+ // Handle inputVariables — nested object where each entry's `value` may contain step pills.
192
+ // Ext inputs use a different element_mapping table than regular step definition inputs.
193
+ const inputVariablesShape = valuesProperties['inputVariables'];
194
+ if (inputVariablesShape) {
195
+ const inputVarObj = inputVariablesShape.asObject();
196
+ const inputVarProps = inputVarObj.properties({ resolve: false });
197
+ const extInputTableName = `${flow_constants_1.EXT_INPUT_TABLE_PREFIX}${stepInstanceSysId}`;
198
+ for (const [varName, configShapeRaw] of Object.entries(inputVarProps)) {
199
+ const configObj = configShapeRaw.asObject();
200
+ const varValueShape = configObj.properties({ resolve: false })['value'];
201
+ if (!varValueShape) {
202
+ continue;
203
+ }
204
+ const resolved = resolveAnyPillFromShape(varValueShape, cidMap);
205
+ if (resolved?.isStep) {
206
+ (0, flow_pill_utils_1.collectPillTypes)(resolved.pill, pillTypeMap);
207
+ newRecords.push(await createElementMapping(factory, stepShape, varName, stepInstanceSysId, extInputTableName, (0, flow_pill_utils_1.stripPillType)(resolved.pill)));
208
+ }
209
+ else if (varValueShape.is(sdk_build_core_1.TemplateExpressionShape)) {
210
+ const resolvedText = resolveAllPillsInTemplate(varValueShape, cidMap, pillTypeMap);
211
+ if (resolvedText.includes('{{step[')) {
212
+ newRecords.push(await createElementMapping(factory, stepShape, varName, stepInstanceSysId, extInputTableName, resolvedText));
213
+ }
214
+ }
215
+ }
216
+ }
217
+ const stepDefTableName = `${flow_constants_1.STEP_DEF_INPUT_TABLE_PREFIX}${stepDefinitionSysId}`;
206
218
  for (const [key, valueShape] of Object.entries(valuesProperties)) {
207
219
  if (key === 'inputVariables' || key === 'outputVariables' || key === 'errorHandlingType') {
208
220
  continue;
@@ -210,15 +222,17 @@ async function resolveUnresolvedStepPills(steps, cidMap, pillTypeMap, factory) {
210
222
  // Case 1: Simple step pill — wfa.dataPill(stepVar.prop, 'type')
211
223
  const resolved = resolveAnyPillFromShape(valueShape, cidMap);
212
224
  if (resolved?.isStep) {
213
- collectStepPillType(resolved.pill, pillTypeMap);
214
- newRecords.push(await createElementMapping(factory, stepShape, key, stepInstanceSysId, stepDefinitionSysId, stripPillTypeSuffix(resolved.pill)));
225
+ (0, flow_pill_utils_1.collectPillTypes)(resolved.pill, pillTypeMap);
226
+ newRecords.push(await createElementMapping(factory, stepShape, key, stepInstanceSysId, stepDefTableName, (0, flow_pill_utils_1.stripPillType)(resolved.pill)));
215
227
  continue;
216
228
  }
217
229
  // Case 2: TemplateExpressionShape with step pills
218
230
  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));
231
+ const resolvedText = resolveAllPillsInTemplate(valueShape, cidMap, pillTypeMap);
232
+ // Only emit if step pills were resolved — action-only pills are already
233
+ // handled by SDK auto-processing.
234
+ if (resolvedText.includes('{{step[')) {
235
+ newRecords.push(await createElementMapping(factory, stepShape, key, stepInstanceSysId, stepDefTableName, resolvedText));
222
236
  }
223
237
  continue;
224
238
  }
@@ -230,28 +244,109 @@ async function resolveUnresolvedStepPills(steps, cidMap, pillTypeMap, factory) {
230
244
  for (const [field, fieldShape] of templateObj.entries({ resolve: false })) {
231
245
  const fieldResolved = resolveAnyPillFromShape(fieldShape, cidMap);
232
246
  if (fieldResolved?.isStep) {
233
- collectStepPillType(fieldResolved.pill, pillTypeMap);
234
- entries.push(`${field}=${stripPillTypeSuffix(fieldResolved.pill)}`);
247
+ (0, flow_pill_utils_1.collectPillTypes)(fieldResolved.pill, pillTypeMap);
248
+ entries.push(`${field}=${(0, flow_pill_utils_1.stripPillType)(fieldResolved.pill)}`);
235
249
  hasStepPills = true;
236
250
  }
237
251
  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}`);
252
+ const resolvedText = resolveAllPillsInTemplate(fieldShape, cidMap, pillTypeMap);
253
+ entries.push(`${field}=${resolvedText}`);
254
+ if (resolvedText.includes('{{step[')) {
241
255
  hasStepPills = true;
242
256
  }
243
- else {
244
- const val = fieldShape.getValue?.();
245
- entries.push(`${field}=${String(val ?? '')}`);
246
- }
247
257
  }
248
258
  else {
249
259
  const val = String(fieldShape.getValue?.() ?? '');
250
- entries.push(`${field}=${val.startsWith('{{') ? stripPillTypeSuffix(val) : val}`);
260
+ entries.push(`${field}=${val.startsWith('{{') ? (0, flow_pill_utils_1.stripPillType)(val) : val}`);
251
261
  }
252
262
  }
253
263
  if (hasStepPills) {
254
- newRecords.push(await createElementMapping(factory, stepShape, key, stepInstanceSysId, stepDefinitionSysId, entries.join('^')));
264
+ newRecords.push(await createElementMapping(factory, stepShape, key, stepInstanceSysId, stepDefTableName, entries.join('^')));
265
+ }
266
+ continue;
267
+ }
268
+ // Case 4: ApprovalDueDateShape — resolve step pill in 'date' field
269
+ // During initial auto-processing, cidMap was empty so the step pill in 'date'
270
+ // couldn't be resolved. Now cidMap is populated, so resolve and create element_mapping.
271
+ if (valueShape.is(flow_shapes_1.ApprovalDueDateShape)) {
272
+ const dueDateShape = valueShape;
273
+ const dateFieldShape = dueDateShape.get('date', false);
274
+ const dateResolved = resolveAnyPillFromShape(dateFieldShape, cidMap);
275
+ if (dateResolved?.isStep) {
276
+ (0, flow_pill_utils_1.collectPillTypes)(dateResolved.pill, pillTypeMap);
277
+ // Build the full JSON with the resolved step pill in the date field.
278
+ // getApprovalDueDate() resolves all properties (turning the unresolvable
279
+ // CallExpressionShape into a Symbol that JSON.stringify drops).
280
+ // Override the date field with the resolved pill string.
281
+ const dueDate = dueDateShape.getApprovalDueDate();
282
+ const correctedShape = new flow_shapes_1.ApprovalDueDateShape({
283
+ source: dueDateShape.getSource(),
284
+ value: { ...dueDate, date: (0, flow_pill_utils_1.stripPillType)(dateResolved.pill) },
285
+ });
286
+ const jsonValue = correctedShape.toString().getValue();
287
+ newRecords.push(await createElementMapping(factory, stepShape, key, stepInstanceSysId, stepDefTableName, jsonValue));
288
+ }
289
+ continue;
290
+ }
291
+ // Case 5: ApprovalRulesShape — resolve step pills in users/groups arrays.
292
+ // Uses the same structural walk as resolveApprovalRulesPills in step-instance-plugin
293
+ // but with resolveAnyPillFromShape (which has access to cidMap).
294
+ if (valueShape.is(flow_shapes_1.ApprovalRulesShape)) {
295
+ const rulesShape = valueShape;
296
+ const rules = rulesShape.getApprovalRules();
297
+ let hasStepPills = false;
298
+ const ruleSetsShape = rulesShape.get('ruleSets', false).ifArray();
299
+ ruleSetsShape?.getElements(false).forEach((ruleSetShape, rsIdx) => {
300
+ const rulesArrayShape = ruleSetShape.ifObject()?.get('rules', false).ifArray();
301
+ rulesArrayShape?.getElements(false).forEach((ruleShape, rIdx) => {
302
+ ruleShape
303
+ .ifArray()
304
+ ?.getElements(false)
305
+ .forEach((conditionShape, cIdx) => {
306
+ const conditionObj = conditionShape.ifObject();
307
+ if (!conditionObj) {
308
+ return;
309
+ }
310
+ const condition = rules.ruleSets?.[rsIdx]?.rules?.[rIdx]?.[cIdx];
311
+ if (!condition) {
312
+ return;
313
+ }
314
+ // Resolve step pills in users array
315
+ conditionObj
316
+ .get('users', false)
317
+ .ifArray()
318
+ ?.getElements(false)
319
+ .forEach((userShape, uIdx) => {
320
+ const resolved = resolveAnyPillFromShape(userShape, cidMap);
321
+ if (resolved?.isStep && condition.users) {
322
+ (0, flow_pill_utils_1.collectPillTypes)(resolved.pill, pillTypeMap);
323
+ condition.users[uIdx] = (0, flow_pill_utils_1.stripPillType)(resolved.pill);
324
+ hasStepPills = true;
325
+ }
326
+ });
327
+ // Resolve step pills in groups array
328
+ conditionObj
329
+ .get('groups', false)
330
+ .ifArray()
331
+ ?.getElements(false)
332
+ .forEach((groupShape, gIdx) => {
333
+ const resolved = resolveAnyPillFromShape(groupShape, cidMap);
334
+ if (resolved?.isStep && condition.groups) {
335
+ (0, flow_pill_utils_1.collectPillTypes)(resolved.pill, pillTypeMap);
336
+ condition.groups[gIdx] = (0, flow_pill_utils_1.stripPillType)(resolved.pill);
337
+ hasStepPills = true;
338
+ }
339
+ });
340
+ });
341
+ });
342
+ });
343
+ if (hasStepPills) {
344
+ const correctedShape = new flow_shapes_1.ApprovalRulesShape({
345
+ source: rulesShape.getSource(),
346
+ value: rules,
347
+ });
348
+ const jsonValue = correctedShape.toString().getValue();
349
+ newRecords.push(await createElementMapping(factory, stepShape, key, stepInstanceSysId, stepDefTableName, jsonValue));
255
350
  }
256
351
  }
257
352
  }
@@ -259,51 +354,68 @@ async function resolveUnresolvedStepPills(steps, cidMap, pillTypeMap, factory) {
259
354
  return newRecords;
260
355
  }
261
356
  /**
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.
357
+ * Collects all pill-bearing text strings from records.
358
+ * Scans sys_element_mapping.value, sys_hub_status_condition.condition and .status fields.
268
359
  */
269
- function extractStepPillsFromRecords(records, cidToLabelMap, pillTypeMap) {
270
- const pills = [];
271
- const seen = new Set();
360
+ function collectPillTextsFromRecords(records) {
361
+ const texts = [];
272
362
  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
363
  for (const rec of topRec.flat()) {
276
- if (rec.getTable() !== 'sys_element_mapping') {
277
- continue;
364
+ const table = rec.getTable();
365
+ if (table === 'sys_element_mapping') {
366
+ const value = rec.get('value')?.asString()?.getValue() ?? '';
367
+ if (value) {
368
+ texts.push(value);
369
+ }
278
370
  }
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;
371
+ else if (table === 'sys_hub_status_condition') {
372
+ const condition = rec.get('condition')?.asString()?.getValue() ?? '';
373
+ if (condition) {
374
+ texts.push(condition);
285
375
  }
286
- const key = `${cid}::${pillPath}`;
287
- if (seen.has(key)) {
288
- continue;
376
+ const status = rec.get('status')?.asString()?.getValue() ?? '';
377
+ if (status) {
378
+ texts.push(status);
289
379
  }
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
380
  }
301
381
  }
302
382
  }
383
+ return texts;
384
+ }
385
+ function extractStepPillsFromRecords(records, cidToLabelMap, pillTypeMap) {
386
+ const pills = [];
387
+ const seen = new Set();
388
+ for (const text of collectPillTextsFromRecords(records)) {
389
+ const matches = text.matchAll(flow_pill_utils_1.STEP_PILL_TYPE_REGEX);
390
+ for (const match of matches) {
391
+ const [, cid, pillPath, dataType] = match;
392
+ if (!cid || !pillPath) {
393
+ continue;
394
+ }
395
+ // Skip base __step_status__ — already handled in buildActionLabelCache via cidToLabelMap
396
+ if (pillPath === '__step_status__') {
397
+ continue;
398
+ }
399
+ const key = `${cid}::${pillPath}`;
400
+ if (seen.has(key)) {
401
+ continue;
402
+ }
403
+ seen.add(key);
404
+ // Prefer type from pillTypeMap (collected from wfa.dataPill() args before stripping),
405
+ // fall back to regex capture from element_mapping, then default to 'string'
406
+ const resolvedType = pillTypeMap?.get(key) ?? dataType ?? 'string';
407
+ pills.push({
408
+ cid,
409
+ stepLabel: cidToLabelMap.get(cid) ?? '',
410
+ pillPath,
411
+ dataType: resolvedType,
412
+ });
413
+ }
414
+ }
303
415
  return pills;
304
416
  }
305
417
  /**
306
- * Extracts action dot-walk pill info from sys_element_mapping records.
418
+ * Extracts action dot-walk pill info from records.
307
419
  * Scans for {{action.X.Y...}} patterns where the path has 2+ segments (i.e., deeper than top-level input).
308
420
  * Deduplicates by full path. Uses pillTypeMap for type resolution.
309
421
  */
@@ -311,34 +423,28 @@ function extractActionDotWalkPills(records, pillTypeMap) {
311
423
  const pills = [];
312
424
  const seen = new Set();
313
425
  const regex = /\{\{action\.([^|}]+)(?:\|([^}]+))?\}\}/g;
314
- for (const topRec of records) {
315
- for (const rec of topRec.flat()) {
316
- if (rec.getTable() !== 'sys_element_mapping') {
426
+ for (const text of collectPillTextsFromRecords(records)) {
427
+ for (const match of text.matchAll(regex)) {
428
+ const [, path, dataType] = match;
429
+ if (!path) {
317
430
  continue;
318
431
  }
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
- });
432
+ const segments = path.split('.');
433
+ // Only dot-walk pills (2+ segments) — top-level ones are already in inputsConfig
434
+ if (segments.length < 2) {
435
+ continue;
436
+ }
437
+ if (seen.has(path)) {
438
+ continue;
341
439
  }
440
+ seen.add(path);
441
+ const resolvedType = pillTypeMap?.get(`action::${path}`) ?? dataType ?? 'string';
442
+ pills.push({
443
+ fullPath: path,
444
+ parentField: segments[0] ?? '',
445
+ columnName: segments[segments.length - 1] ?? '',
446
+ dataType: resolvedType,
447
+ });
342
448
  }
343
449
  }
344
450
  return pills;
@@ -574,6 +680,66 @@ function convertActionPillsInInputs(inputsObj, source, diagnostics, labelCacheMa
574
680
  }
575
681
  }
576
682
  }
683
+ /**
684
+ * Converts pill strings buried inside ApprovalRulesShape and ApprovalDueDateShape values
685
+ * to wfa.dataPill() shapes. These structured shapes are created by normalizeInputValue()
686
+ * before convertActionPillsInInputs runs, so their nested pill strings are unreachable
687
+ * by the normal string-based pill conversion.
688
+ */
689
+ function convertPillsInStructuredShapes(inputsObj, source, diagnostics, labelCacheMap) {
690
+ for (const [key, val] of Object.entries(inputsObj)) {
691
+ if (val instanceof flow_shapes_1.ApprovalRulesShape) {
692
+ const rules = val.getApprovalRules();
693
+ let changed = false;
694
+ const newRuleSets = rules.ruleSets?.map((ruleSet) => ({
695
+ ...ruleSet,
696
+ rules: ruleSet.rules?.map((rule) => rule.map((condition) => ({
697
+ ...condition,
698
+ users: condition.users?.map((user) => {
699
+ const s = String(user);
700
+ if ((0, pill_string_parser_1.detectPillPattern)(s) !== 'none') {
701
+ const shape = convertActionPillToShape(s, source, diagnostics, labelCacheMap);
702
+ if (shape) {
703
+ changed = true;
704
+ return shape;
705
+ }
706
+ }
707
+ return user;
708
+ }),
709
+ groups: condition.groups?.map((group) => {
710
+ const s = String(group);
711
+ if ((0, pill_string_parser_1.detectPillPattern)(s) !== 'none') {
712
+ const shape = convertActionPillToShape(s, source, diagnostics, labelCacheMap);
713
+ if (shape) {
714
+ changed = true;
715
+ return shape;
716
+ }
717
+ }
718
+ return group;
719
+ }),
720
+ }))),
721
+ }));
722
+ if (changed && newRuleSets) {
723
+ inputsObj[key] = new flow_shapes_1.ApprovalRulesShape({
724
+ source,
725
+ value: { ...rules, ruleSets: newRuleSets },
726
+ });
727
+ }
728
+ }
729
+ else if (val instanceof flow_shapes_1.ApprovalDueDateShape) {
730
+ const dueDate = val.getApprovalDueDate();
731
+ if (dueDate.date && (0, pill_string_parser_1.detectPillPattern)(dueDate.date) !== 'none') {
732
+ const shape = convertActionPillToShape(dueDate.date, source, diagnostics, labelCacheMap);
733
+ if (shape) {
734
+ inputsObj[key] = new flow_shapes_1.ApprovalDueDateShape({
735
+ source,
736
+ value: { ...dueDate, date: shape },
737
+ });
738
+ }
739
+ }
740
+ }
741
+ }
742
+ }
577
743
  /** Regex to find step[UUID] pills */
578
744
  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
745
  /**
@@ -622,6 +788,84 @@ function convertStepPillsInInputs(inputsObj, source, cidToIdentifierMap, labelCa
622
788
  inputsObj[key] = new sdk_build_core_1.TemplateValueShape({ source, value: newTemplateValue });
623
789
  }
624
790
  }
791
+ else if (val instanceof flow_shapes_1.ApprovalDueDateShape) {
792
+ const dueDate = val.getApprovalDueDate();
793
+ if (dueDate.date && typeof dueDate.date === 'string' && (0, pill_string_parser_1.detectPillPattern)(dueDate.date) !== 'none') {
794
+ const shape = convertStepPillString(dueDate.date, source, cidToIdentifierMap, labelCacheMap);
795
+ if (shape) {
796
+ inputsObj[key] = new flow_shapes_1.ApprovalDueDateShape({
797
+ source,
798
+ value: { ...dueDate, date: shape },
799
+ });
800
+ }
801
+ }
802
+ }
803
+ else if (val instanceof flow_shapes_1.ApprovalRulesShape) {
804
+ const rules = val.getApprovalRules();
805
+ let changed = false;
806
+ const newRuleSets = rules.ruleSets?.map((ruleSet) => ({
807
+ ...ruleSet,
808
+ rules: ruleSet.rules?.map((rule) => rule.map((condition) => ({
809
+ ...condition,
810
+ users: condition.users?.map((user) => {
811
+ const s = String(user);
812
+ if ((0, pill_string_parser_1.detectPillPattern)(s) !== 'none') {
813
+ const shape = convertStepPillString(s, source, cidToIdentifierMap, labelCacheMap);
814
+ if (shape) {
815
+ changed = true;
816
+ return shape;
817
+ }
818
+ }
819
+ return user;
820
+ }),
821
+ groups: condition.groups?.map((group) => {
822
+ const s = String(group);
823
+ if ((0, pill_string_parser_1.detectPillPattern)(s) !== 'none') {
824
+ const shape = convertStepPillString(s, source, cidToIdentifierMap, labelCacheMap);
825
+ if (shape) {
826
+ changed = true;
827
+ return shape;
828
+ }
829
+ }
830
+ return group;
831
+ }),
832
+ }))),
833
+ }));
834
+ if (changed && newRuleSets) {
835
+ inputsObj[key] = new flow_shapes_1.ApprovalRulesShape({
836
+ source,
837
+ value: { ...rules, ruleSets: newRuleSets },
838
+ });
839
+ }
840
+ }
841
+ else if (val && typeof val === 'object' && !Array.isArray(val) && !isShapeInstance(val)) {
842
+ // Recursively process nested plain objects (e.g., inputVariables)
843
+ // This handles script step input variables which have 'value' fields with step pills
844
+ const nestedObj = val;
845
+ for (const nestedVal of Object.values(nestedObj)) {
846
+ if (nestedVal &&
847
+ typeof nestedVal === 'object' &&
848
+ !Array.isArray(nestedVal) &&
849
+ !isShapeInstance(nestedVal)) {
850
+ const innerObj = nestedVal;
851
+ const innerValue = innerObj['value'];
852
+ if (typeof innerValue === 'string') {
853
+ const converted = convertStepPillString(innerValue, source, cidToIdentifierMap, labelCacheMap);
854
+ if (converted) {
855
+ innerObj['value'] = converted;
856
+ }
857
+ }
858
+ else if (innerValue instanceof sdk_build_core_1.TemplateExpressionShape) {
859
+ // Handle case where convertActionPillsInInputs already converted action pills,
860
+ // leaving step pills in the template literal text segments
861
+ const resolved = resolveStepPillsInTemplateExpression(innerValue, source, cidToIdentifierMap, labelCacheMap);
862
+ if (resolved) {
863
+ innerObj['value'] = resolved;
864
+ }
865
+ }
866
+ }
867
+ }
868
+ }
625
869
  }
626
870
  }
627
871
  /**
@@ -632,9 +876,12 @@ function resolveStepPillMatch(uuid, property, source, cidToIdentifierMap, labelC
632
876
  if (!identifier) {
633
877
  return undefined;
634
878
  }
879
+ // Split dot-walk property path into individual segments (e.g., "record.manager.email" → ["record", "manager", "email"])
880
+ // PropertyAccessShape needs each path segment as a separate element for correct code generation
881
+ const pathParts = property.split('.');
635
882
  const expression = new sdk_build_core_1.PropertyAccessShape({
636
883
  source,
637
- elements: [identifier, property],
884
+ elements: [identifier, ...pathParts],
638
885
  });
639
886
  const pillName = `step[${uuid}].${property}`;
640
887
  const dataType = labelCacheMap?.get(pillName) || 'string';
@@ -889,9 +1136,118 @@ const actionDefRelationships = {
889
1136
  descendant: true,
890
1137
  },
891
1138
  };
1139
+ /**
1140
+ * Resolves any shape (string literal, number, template expression, dataPill call)
1141
+ * to its final string representation with pill references resolved.
1142
+ */
1143
+ function resolveToString(shape, cidMap, pillTypeMap) {
1144
+ if (shape instanceof sdk_build_core_1.TemplateExpressionShape) {
1145
+ return resolveAllPillsInTemplate(shape, cidMap, pillTypeMap);
1146
+ }
1147
+ if (shape instanceof data_pill_shapes_1.PillShape) {
1148
+ const raw = shape.getValue();
1149
+ (0, flow_pill_utils_1.collectPillTypes)(raw, pillTypeMap);
1150
+ return (0, flow_pill_utils_1.stripPillType)(raw);
1151
+ }
1152
+ const resolved = resolveAnyPillFromShape(shape, cidMap);
1153
+ if (resolved) {
1154
+ if (resolved.isStep) {
1155
+ (0, flow_pill_utils_1.collectPillTypes)(resolved.pill, pillTypeMap);
1156
+ }
1157
+ return (0, flow_pill_utils_1.stripPillType)(resolved.pill);
1158
+ }
1159
+ return String(shape.getValue?.() ?? '');
1160
+ }
1161
+ /**
1162
+ * Builds the complex JSON `status` field for a sys_hub_status_condition record.
1163
+ * The status contains complexObject (runtime values), complexObjectSchema
1164
+ * (FlowDesigner:FDACTIONSTATUS with SimpleMapFacet field descriptors), and type facets.
1165
+ *
1166
+ * When a field value contains datapill references ({{...}}), it goes into the
1167
+ * `mapped` field of the SimpleMapFacet; otherwise static values go into
1168
+ * complexObject.$cv.$v and mapped is "{}".
1169
+ */
1170
+ function buildStatusConditionJson(codeValue, messageValue) {
1171
+ const hasPillInCode = codeValue.includes('{{');
1172
+ const hasPillInMessage = messageValue.includes('{{');
1173
+ const codeMapped = hasPillInCode ? JSON.stringify({ code: codeValue }) : '{}';
1174
+ const messageMapped = hasPillInMessage ? JSON.stringify({ message: messageValue }) : '{}';
1175
+ const codeCV = hasPillInCode ? '' : codeValue;
1176
+ const messageCV = hasPillInMessage ? '' : messageValue;
1177
+ // Derive the complexObjectSchema from the built-in FDACTIONSTATUS definition
1178
+ // rather than hardcoding it, so it stays in sync with the canonical schema.
1179
+ const fdActionStatus = built_in_complex_objects_1.builtInComplexObjects['FDACTIONSTATUS'];
1180
+ if (!fdActionStatus) {
1181
+ throw new Error('FDACTIONSTATUS not found in builtInComplexObjects');
1182
+ }
1183
+ const baseSchema = JSON.parse(fdActionStatus.data.serialized_content);
1184
+ const fdSchema = baseSchema['FlowDesigner:FDACTIONSTATUS'];
1185
+ // Inject `mapped` into the field facets for code and message
1186
+ for (const [fieldName, mapped] of [
1187
+ ['code', codeMapped],
1188
+ ['message', messageMapped],
1189
+ ]) {
1190
+ const facetKey = `${fieldName}.$field_facets`;
1191
+ const facets = JSON.parse(fdSchema[facetKey].SimpleMapFacet);
1192
+ facets.mapped = mapped;
1193
+ fdSchema[facetKey].SimpleMapFacet = JSON.stringify(facets);
1194
+ }
1195
+ return JSON.stringify({
1196
+ version: '1.0',
1197
+ complexObject: {
1198
+ code: { $cv: { $c: 'java.lang.String', $v: codeCV } },
1199
+ message: { $cv: { $c: 'java.lang.String', $v: messageCV } },
1200
+ },
1201
+ complexObjectSchema: baseSchema,
1202
+ serializationFormat: 'JSON',
1203
+ });
1204
+ }
1205
+ /**
1206
+ * Parses a status JSON blob from a sys_hub_status_condition record and extracts
1207
+ * the code and message values. Datapill references live in the `mapped` field
1208
+ * of the SimpleMapFacet; static values live in complexObject.$cv.$v.
1209
+ */
1210
+ function parseStatusConditionJson(statusJson, logger) {
1211
+ try {
1212
+ const parsed = JSON.parse(statusJson);
1213
+ const co = parsed?.complexObject ?? {};
1214
+ const schema = parsed?.complexObjectSchema ?? {};
1215
+ const fdSchema = schema['FlowDesigner:FDACTIONSTATUS'] ?? {};
1216
+ // Extract code
1217
+ let code = co?.code?.$cv?.$v ?? '';
1218
+ const codeFacetsRaw = fdSchema['code.$field_facets']?.SimpleMapFacet;
1219
+ if (codeFacetsRaw) {
1220
+ const codeFacets = typeof codeFacetsRaw === 'string' ? JSON.parse(codeFacetsRaw) : codeFacetsRaw;
1221
+ const codeMapped = codeFacets?.mapped;
1222
+ if (codeMapped && codeMapped !== '{}') {
1223
+ const mappedObj = typeof codeMapped === 'string' ? JSON.parse(codeMapped) : codeMapped;
1224
+ if (mappedObj?.code) {
1225
+ code = mappedObj.code;
1226
+ }
1227
+ }
1228
+ }
1229
+ // Extract message
1230
+ let message = co?.message?.$cv?.$v ?? '';
1231
+ const msgFacetsRaw = fdSchema['message.$field_facets']?.SimpleMapFacet;
1232
+ if (msgFacetsRaw) {
1233
+ const msgFacets = typeof msgFacetsRaw === 'string' ? JSON.parse(msgFacetsRaw) : msgFacetsRaw;
1234
+ const msgMapped = msgFacets?.mapped;
1235
+ if (msgMapped && msgMapped !== '{}') {
1236
+ const mappedObj = typeof msgMapped === 'string' ? JSON.parse(msgMapped) : msgMapped;
1237
+ if (mappedObj?.message) {
1238
+ message = mappedObj.message;
1239
+ }
1240
+ }
1241
+ }
1242
+ return { code, message };
1243
+ }
1244
+ catch (e) {
1245
+ logger?.warn(`parseStatusConditionJson: failed to parse status JSON — ${e}`);
1246
+ return { code: '', message: '' };
1247
+ }
1248
+ }
892
1249
  exports.ActionDefinitionPlugin = sdk_build_core_1.Plugin.create({
893
1250
  name: 'ActionDefinitionPlugin',
894
- docs: [(0, utils_1.createSdkDocEntry)('Action', ['sys_hub_action_type_definition'])],
895
1251
  records: {
896
1252
  sys_hub_action_type_definition: {
897
1253
  relationships: {
@@ -901,6 +1257,10 @@ exports.ActionDefinitionPlugin = sdk_build_core_1.Plugin.create({
901
1257
  descendant: true,
902
1258
  relationships: {
903
1259
  ...actionDefRelationships,
1260
+ sys_variable_value: {
1261
+ via: 'document_key',
1262
+ descendant: true,
1263
+ },
904
1264
  sys_element_mapping: {
905
1265
  via: 'id',
906
1266
  descendant: true,
@@ -934,85 +1294,31 @@ exports.ActionDefinitionPlugin = sdk_build_core_1.Plugin.create({
934
1294
  },
935
1295
  async toShape(record, { descendants, diagnostics, logger }) {
936
1296
  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
1297
+ // Query error evaluation descendants for this action.
1298
+ // sys_hub_action_status_metadata is the parent container;
1299
+ // sys_hub_status_condition holds the individual conditions.
1300
+ const actionStatusMetadata = descendants
941
1301
  .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
1302
+ .filter((r) => r.get('action_type_id')?.asString()?.getValue() === actionSysId);
1303
+ const actionStatusMetadataIds = actionStatusMetadata.map((r) => r.getId().getValue());
1304
+ const statusConditions = descendants
945
1305
  .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
- }
1306
+ .filter((r) => actionStatusMetadataIds.includes(r.get('action_status_metadata_id')?.asString()?.getValue() ?? ''));
952
1307
  // Filter inputs/outputs to only those belonging to this action definition,
953
- // excluding snapshot-parented records
1308
+ // excluding snapshot-parented records, and sort by <order> to preserve
1309
+ // the order defined in the XML (instance document order is not guaranteed)
1310
+ const sortByOrder = (a, b) => Number(a.get('order')?.asString()?.getValue() ?? 0) -
1311
+ Number(b.get('order')?.asString()?.getValue() ?? 0);
954
1312
  const actionInputs = descendants
955
1313
  .query('sys_hub_action_input')
956
- .filter((r) => r.get('model')?.asString()?.getValue() === actionSysId);
1314
+ .filter((r) => r.get('model')?.asString()?.getValue() === actionSysId)
1315
+ .sort(sortByOrder);
957
1316
  const actionOutputs = descendants
958
1317
  .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
- }
1318
+ .filter((r) => r.get('model')?.asString()?.getValue() === actionSysId)
1319
+ .sort(sortByOrder);
1014
1320
  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);
1321
+ const outputs = (0, flow_io_to_record_1.buildVariableShapes)(actionOutputs, descendants);
1016
1322
  // Extract label_cache from action definition for datapill type info
1017
1323
  let labelCacheMap;
1018
1324
  try {
@@ -1095,7 +1401,7 @@ exports.ActionDefinitionPlugin = sdk_build_core_1.Plugin.create({
1095
1401
  }
1096
1402
  // Ext input element_mappings have table var__m_sys_hub_step_ext_input_*
1097
1403
  // Collect their values but don't add to inputsObj (they go under inputVariables)
1098
- if (table.startsWith('var__m_sys_hub_step_ext_input_')) {
1404
+ if (table.startsWith(flow_constants_1.EXT_INPUT_TABLE_PREFIX)) {
1099
1405
  extInputMappingValues[rawFieldName] = value;
1100
1406
  continue;
1101
1407
  }
@@ -1156,8 +1462,8 @@ exports.ActionDefinitionPlugin = sdk_build_core_1.Plugin.create({
1156
1462
  // Find value from sys_variable_value or element_mapping (datapill)
1157
1463
  // Prioritize pills from sys_element_mapping over static values from sys_variable_value
1158
1464
  const varValueRecord = stepVarValues.find((v) => v.get('variable')?.asString()?.getValue() === extInputSysId);
1159
- const value = extInputMappingValues[elementName] ||
1160
- varValueRecord?.get('value')?.asString()?.getValue() ||
1465
+ const value = extInputMappingValues[elementName] ??
1466
+ varValueRecord?.get('value')?.asString()?.getValue() ??
1161
1467
  '';
1162
1468
  const inputEntry = { label };
1163
1469
  if (value) {
@@ -1181,17 +1487,17 @@ exports.ActionDefinitionPlugin = sdk_build_core_1.Plugin.create({
1181
1487
  continue;
1182
1488
  }
1183
1489
  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';
1490
+ const mandatory = extOutput.get('mandatory')?.ifString()?.getValue() === 'true';
1491
+ const internalType = extOutput.get('internal_type')?.ifString()?.getValue() ?? 'string';
1186
1492
  // Check attributes for uiType to handle cases where internal_type is generic
1187
- const attributes = extOutput.get('attributes')?.asString()?.getValue() ?? '';
1493
+ const attributes = extOutput.get('attributes')?.ifString()?.getValue() ?? '';
1188
1494
  const uiType = (0, schema_to_flow_object_1.getAttributeValue)(attributes, 'uiType') ?? internalType;
1189
1495
  const columnApiName = column_helper_1.COLUMN_TYPE_TO_API[uiType] ?? 'StringColumn';
1190
1496
  const columnProps = { label };
1191
1497
  if (mandatory) {
1192
1498
  columnProps['mandatory'] = true;
1193
1499
  }
1194
- const maxLength = extOutput.get('max_length')?.asString()?.getValue();
1500
+ const maxLength = extOutput.get('max_length')?.ifString()?.getValue();
1195
1501
  if (maxLength) {
1196
1502
  columnProps['maxLength'] = Number(maxLength);
1197
1503
  }
@@ -1203,12 +1509,12 @@ exports.ActionDefinitionPlugin = sdk_build_core_1.Plugin.create({
1203
1509
  if (hint) {
1204
1510
  columnProps['hint'] = hint;
1205
1511
  }
1206
- const referenceTable = extOutput.get('reference')?.asString()?.getValue();
1512
+ const referenceTable = extOutput.get('reference')?.ifString()?.getValue();
1207
1513
  if (referenceTable &&
1208
1514
  (columnApiName === 'ReferenceColumn' || columnApiName === 'ListColumn')) {
1209
1515
  columnProps['referenceTable'] = referenceTable;
1210
1516
  }
1211
- const defaultValue = extOutput.get('default_value')?.asString()?.getValue();
1517
+ const defaultValue = extOutput.get('default_value')?.ifString()?.getValue();
1212
1518
  if (defaultValue) {
1213
1519
  columnProps['default'] = defaultValue;
1214
1520
  }
@@ -1227,6 +1533,9 @@ exports.ActionDefinitionPlugin = sdk_build_core_1.Plugin.create({
1227
1533
  }
1228
1534
  // Convert action input pills ({{action.xxx}}) to wfa.dataPill(params.inputs.xxx, 'type')
1229
1535
  convertActionPillsInInputs(inputsObj, stepInstance, diagnostics, labelCacheMap);
1536
+ // Convert pills buried inside ApprovalRulesShape/ApprovalDueDateShape
1537
+ // (these were already parsed by normalizeInputValue before pill conversion)
1538
+ convertPillsInStructuredShapes(inputsObj, stepInstance, diagnostics, labelCacheMap);
1230
1539
  // Convert step output pills ({{step[UUID].xxx}}) to wfa.dataPill(varName.xxx, 'type')
1231
1540
  // Earlier steps are already in cidToIdentifierMap since steps are processed in order
1232
1541
  convertStepPillsInInputs(inputsObj, stepInstance, cidToIdentifierMap, labelCacheMap);
@@ -1238,7 +1547,7 @@ exports.ActionDefinitionPlugin = sdk_build_core_1.Plugin.create({
1238
1547
  configProperties['label'] = label;
1239
1548
  }
1240
1549
  // Built-in/OOB step: emit actionStep.xxx reference
1241
- const stepIdentifier = (0, utils_2.getBuiltInStepIdentifier)(stepTypeSysId);
1550
+ const stepIdentifier = (0, utils_1.getBuiltInStepIdentifier)(stepTypeSysId);
1242
1551
  // Built-in/OOB step: emit actionStep.xxx identifier reference
1243
1552
  const stepDefShape = stepIdentifier
1244
1553
  ? new sdk_build_core_1.IdentifierShape({ source: stepInstance, name: stepIdentifier })
@@ -1272,6 +1581,150 @@ exports.ActionDefinitionPlugin = sdk_build_core_1.Plugin.create({
1272
1581
  }));
1273
1582
  }
1274
1583
  }
1584
+ // Reconstruct wfa.errorEvaluation([...]) from sys_hub_status_condition records.
1585
+ // This must appear before wfa.assignActionOutputs in the generated Fluent file.
1586
+ if (statusConditions.length > 0) {
1587
+ const sortedConditions = [...statusConditions].sort(sortByOrder);
1588
+ const conditionShapes = [];
1589
+ for (const cond of sortedConditions) {
1590
+ const label = cond.get('label')?.asString()?.getValue() ?? '';
1591
+ const condition = cond.get('condition')?.asString()?.getValue() ?? '';
1592
+ const dontTreatAsError = cond.get('dont_treat_as_error')?.asString()?.getValue() === 'true';
1593
+ const statusJson = cond.get('status')?.asString()?.getValue() ?? '{}';
1594
+ const { code, message } = parseStatusConditionJson(statusJson, logger);
1595
+ // Build status props as plain object, resolve pills, then wrap
1596
+ const statusProps = {};
1597
+ statusProps['code'] = /^\d+$/.test(code) ? Number(code) : code;
1598
+ statusProps['message'] = message;
1599
+ convertActionPillsInInputs(statusProps, cond, diagnostics, labelCacheMap);
1600
+ convertStepPillsInInputs(statusProps, cond, cidToIdentifierMap, labelCacheMap);
1601
+ // Build condition props as plain object, resolve pills on condition only.
1602
+ // label is a display name and must NOT be pill-converted;
1603
+ // status pills are already resolved above via statusProps.
1604
+ const conditionProps = { condition };
1605
+ convertActionPillsInInputs(conditionProps, cond, diagnostics, labelCacheMap);
1606
+ convertStepPillsInInputs(conditionProps, cond, cidToIdentifierMap, labelCacheMap);
1607
+ const condProps = {
1608
+ label,
1609
+ condition: conditionProps['condition'],
1610
+ status: new sdk_build_core_1.ObjectShape({ source: cond, properties: statusProps }),
1611
+ };
1612
+ if (dontTreatAsError) {
1613
+ condProps['dontTreatAsError'] = true;
1614
+ }
1615
+ conditionShapes.push(new sdk_build_core_1.ObjectShape({ source: cond, properties: condProps }));
1616
+ }
1617
+ const statusSource = actionStatusMetadata[0] ?? record;
1618
+ stepShapes.push(new sdk_build_core_1.CallExpressionShape({
1619
+ source: statusSource,
1620
+ callee: flow_constants_1.ERROR_EVALUATION_CALLEE,
1621
+ args: [
1622
+ new sdk_build_core_1.ArrayShape({
1623
+ source: statusSource,
1624
+ elements: conditionShapes,
1625
+ }),
1626
+ ],
1627
+ }));
1628
+ }
1629
+ // Reconstruct wfa.assignActionOutputs() from output element_mappings.
1630
+ // Element mappings for output assignments can be keyed to the action def sys_id
1631
+ // (draft actions) or a snapshot sys_id (published actions). Check both.
1632
+ {
1633
+ const candidateIds = new Set([actionSysId]);
1634
+ const snapshots = descendants
1635
+ .query('sys_hub_action_type_snapshot')
1636
+ .filter((s) => s.get('parent_action')?.asString()?.getValue() === actionSysId);
1637
+ for (const snap of snapshots) {
1638
+ candidateIds.add(snap.getId().getValue());
1639
+ }
1640
+ // Find element_mappings for action outputs keyed to any candidate id
1641
+ const outputElementMappings = descendants
1642
+ .query('sys_element_mapping')
1643
+ .filter((m) => candidateIds.has(m.get('id')?.asString()?.getValue() ?? '') &&
1644
+ (m.get('table')?.asString()?.getValue() ?? '').startsWith(flow_constants_1.ACTION_OUTPUT_TABLE_PREFIX));
1645
+ // Build output element name lookup from all output records
1646
+ // (action-parented and snapshot-parented) for sys_variable_value resolution
1647
+ const varSysIdToElement = new Map();
1648
+ const snapshotOutputs = descendants
1649
+ .query('sys_hub_action_output')
1650
+ .filter((r) => candidateIds.has(r.get('model')?.asString()?.getValue() ?? ''));
1651
+ for (const outputRec of [...snapshotOutputs, ...actionOutputs]) {
1652
+ const element = outputRec.get('element')?.asString()?.getValue();
1653
+ if (element) {
1654
+ varSysIdToElement.set(outputRec.getId().getValue(), element);
1655
+ }
1656
+ }
1657
+ // Gather sys_variable_value records for static output values
1658
+ const outputVarValues = descendants.query('sys_variable_value').filter((v) => {
1659
+ const docKey = v.get('document_key')?.asString()?.getValue() ?? '';
1660
+ return candidateIds.has(docKey);
1661
+ });
1662
+ if (outputElementMappings.length > 0 || outputVarValues.length > 0) {
1663
+ const valuesObj = {};
1664
+ const sourceRecord = snapshots[0] ?? record;
1665
+ // Static values from sys_variable_value
1666
+ for (const varValue of outputVarValues) {
1667
+ const variableSysId = varValue.get('variable')?.asString()?.getValue();
1668
+ const value = varValue.get('value')?.asString()?.getValue();
1669
+ const outputName = variableSysId ? varSysIdToElement.get(variableSysId) : undefined;
1670
+ // Skip internal platform outputs and missing values (null/undefined)
1671
+ if (!outputName || outputName.startsWith('__') || value == null) {
1672
+ continue;
1673
+ }
1674
+ // Skip FlowObject/FlowArray complex object schema serializations.
1675
+ // These sys_variable_value records store the output's type structure
1676
+ // (already declared via FlowObject/FlowArray in Fluent), not user-assigned values.
1677
+ if (value.includes('"complexObjectSchema"') || value.includes('"$COCollectionField"')) {
1678
+ continue;
1679
+ }
1680
+ // sys_variable_value can contain pill strings (e.g. {{action.variable|string}})
1681
+ // Strip type suffix (|string, |reference) before pill parsing
1682
+ const pillStripped = value.replace(/\|[a-z_]+\}\}/g, '}}');
1683
+ valuesObj[outputName] = pillStripped;
1684
+ }
1685
+ // Datapill values from sys_element_mapping (overrides var values when both exist)
1686
+ for (const mapping of outputElementMappings) {
1687
+ const outputName = mapping.get('field')?.asString()?.getValue();
1688
+ const pillValue = mapping.get('value')?.asString()?.getValue();
1689
+ // Skip internal outputs, missing values, and empty pill strings.
1690
+ // An empty pill value means "no datapill assignment" — the static value
1691
+ // from sys_variable_value (if any) should be preserved.
1692
+ if (!outputName || !pillValue || outputName.startsWith('__')) {
1693
+ continue;
1694
+ }
1695
+ valuesObj[outputName] = pillValue;
1696
+ }
1697
+ // Two-pass pill resolution (same approach as step inputs):
1698
+ // Pass 1: Convert action pills ({{action.xxx}}) → dataPill shapes
1699
+ convertActionPillsInInputs(valuesObj, sourceRecord, diagnostics, labelCacheMap);
1700
+ // Pass 2: Convert step pills ({{step[UUID].xxx}}) → dataPill shapes
1701
+ convertStepPillsInInputs(valuesObj, sourceRecord, cidToIdentifierMap, labelCacheMap);
1702
+ if (Object.keys(valuesObj).length > 0) {
1703
+ // Use params.outputs reference instead of duplicating the full schema
1704
+ const outputsRef = new sdk_build_core_1.PropertyAccessShape({
1705
+ source: sourceRecord,
1706
+ elements: [
1707
+ new sdk_build_core_1.IdentifierShape({
1708
+ source: sourceRecord,
1709
+ name: ACTION_PILL_PARAM_NAME,
1710
+ }),
1711
+ 'outputs',
1712
+ ],
1713
+ });
1714
+ stepShapes.push(new sdk_build_core_1.CallExpressionShape({
1715
+ source: sourceRecord,
1716
+ callee: flow_constants_1.ASSIGN_ACTION_OUTPUTS_CALLEE,
1717
+ args: [
1718
+ outputsRef,
1719
+ new sdk_build_core_1.ObjectShape({
1720
+ source: sourceRecord,
1721
+ properties: valuesObj,
1722
+ }),
1723
+ ],
1724
+ }));
1725
+ }
1726
+ }
1727
+ }
1275
1728
  // Build args: config + optional body with step instances
1276
1729
  const actionConfig = record.transform(({ $ }) => ({
1277
1730
  $id: $.val(now_id_plugin_1.NowIdShape.from(record)),
@@ -1280,7 +1733,7 @@ exports.ActionDefinitionPlugin = sdk_build_core_1.Plugin.create({
1280
1733
  description: $.def(''),
1281
1734
  access: $.def('public'),
1282
1735
  category: $.def(''),
1283
- protection: $.from('sys_policy').def(''),
1736
+ protectionPolicy: $.from('sys_policy').def(''),
1284
1737
  inputs: $.val(inputs),
1285
1738
  outputs: $.val(outputs),
1286
1739
  }));
@@ -1298,7 +1751,7 @@ exports.ActionDefinitionPlugin = sdk_build_core_1.Plugin.create({
1298
1751
  args,
1299
1752
  });
1300
1753
  // Try to get the existing identifier from the record source, fallback to slugified name
1301
- const actionName = (0, utils_2.getIdentifierFromRecord)(record) ??
1754
+ const actionName = (0, utils_1.getIdentifierFromRecord)(record) ??
1302
1755
  (0, flow_constants_1.slugifyString)(String(record.get('internal_name')?.getValue() || record.get('name')?.getValue()));
1303
1756
  return {
1304
1757
  success: true,
@@ -1335,6 +1788,9 @@ exports.ActionDefinitionPlugin = sdk_build_core_1.Plugin.create({
1335
1788
  sys_hub_status_condition: {
1336
1789
  coalesce: ['action_status_metadata_id', 'order'],
1337
1790
  },
1791
+ sys_element_mapping: {
1792
+ coalesce: ['id', 'table', 'field'],
1793
+ },
1338
1794
  },
1339
1795
  shapes: [
1340
1796
  {
@@ -1354,12 +1810,11 @@ exports.ActionDefinitionPlugin = sdk_build_core_1.Plugin.create({
1354
1810
  explicitId: actionConfiguration.get('$id'),
1355
1811
  properties: actionConfiguration.transform(({ $ }) => ({
1356
1812
  name: $,
1357
- internal_name: $.from('name').map((n) => (0, flow_constants_1.slugifyString)(n.getValue())),
1358
1813
  annotation: $.def(''),
1359
1814
  description: $.def(''),
1360
1815
  access: $.def('public'),
1361
1816
  category: $.def(''),
1362
- sys_policy: $.from('protection').def(''),
1817
+ sys_policy: $.from('protectionPolicy').def(''),
1363
1818
  active: $.val(true),
1364
1819
  state: $.val('draft'),
1365
1820
  system_level: $.val(false),
@@ -1397,10 +1852,25 @@ exports.ActionDefinitionPlugin = sdk_build_core_1.Plugin.create({
1397
1852
  const cidToLabelMap = new Map();
1398
1853
  const pillTypeMap = new Map();
1399
1854
  const stepInfos = [];
1855
+ let assignActionOutputsShape;
1856
+ let errorEvaluationShape;
1400
1857
  let order = 1;
1401
1858
  for (const [, v] of (allInstances ?? []).entries()) {
1402
1859
  const isVarStatement = v instanceof sdk_build_core_1.VariableStatementShape;
1403
1860
  const innerShape = isVarStatement ? v.getInitializer() : v;
1861
+ if (innerShape instanceof sdk_build_core_1.CallExpressionShape &&
1862
+ innerShape.getCallee() === flow_constants_1.ASSIGN_ACTION_OUTPUTS_CALLEE) {
1863
+ assignActionOutputsShape = innerShape;
1864
+ continue;
1865
+ }
1866
+ if (innerShape instanceof sdk_build_core_1.CallExpressionShape &&
1867
+ innerShape.getCallee() === flow_constants_1.ERROR_EVALUATION_CALLEE) {
1868
+ errorEvaluationShape = innerShape;
1869
+ if (assignActionOutputsShape) {
1870
+ diagnostics.error(innerShape, `wfa.errorEvaluation() must appear before wfa.assignActionOutputs() in the action body.`);
1871
+ }
1872
+ continue;
1873
+ }
1404
1874
  if (innerShape.getSource() instanceof step_instance_plugin_1.StepInstanceShape) {
1405
1875
  const stepShape = innerShape.getSource();
1406
1876
  stepShape.setCidMap(cidMap);
@@ -1440,6 +1910,135 @@ exports.ActionDefinitionPlugin = sdk_build_core_1.Plugin.create({
1440
1910
  // inputs directly and create correct sys_element_mapping records.
1441
1911
  const resolvedStepPillRecords = await resolveUnresolvedStepPills(stepInfos, cidMap, pillTypeMap, factory);
1442
1912
  relatedRecords.push(...resolvedStepPillRecords);
1913
+ // Create sys_element_mapping records for assignActionOutputs values.
1914
+ // For draft actions, element_mappings reference the action definition sys_id directly.
1915
+ if (assignActionOutputsShape) {
1916
+ const actionDefId = actionDefinitionRecord.getId().getValue();
1917
+ const valuesArg = assignActionOutputsShape.getArgument(1)?.asObject();
1918
+ // Validate that assigned output names match declared outputs
1919
+ if (valuesArg && !outputsConfig) {
1920
+ diagnostics.error(assignActionOutputsShape, 'assignActionOutputs is used but no outputs are declared in the action config');
1921
+ }
1922
+ if (valuesArg && outputsConfig) {
1923
+ const declaredOutputNames = new Set(outputsConfig.keys());
1924
+ const assignedOutputNames = new Set();
1925
+ for (const [outputName] of valuesArg.entries({ resolve: false })) {
1926
+ assignedOutputNames.add(outputName);
1927
+ if (!declaredOutputNames.has(outputName)) {
1928
+ diagnostics.error(valuesArg.get(outputName), `Unknown output '${outputName}'. Available outputs: ${[...declaredOutputNames].join(', ') || '(none)'}`);
1929
+ }
1930
+ }
1931
+ for (const declaredName of declaredOutputNames) {
1932
+ if (!assignedOutputNames.has(declaredName)) {
1933
+ diagnostics.error(assignActionOutputsShape, `Output '${declaredName}' is declared but not assigned in assignActionOutputs. All declared outputs must be assigned a value.`);
1934
+ }
1935
+ }
1936
+ }
1937
+ if (valuesArg) {
1938
+ const entries = valuesArg.entries({ resolve: false });
1939
+ for (const [outputName, valueShape] of entries) {
1940
+ const shape = valueShape;
1941
+ // Validate that the output value is not an empty string
1942
+ if (shape.isString() && shape.getValue() === '') {
1943
+ diagnostics.error(valuesArg.get(outputName), `Output '${outputName}' is assigned an empty string. All action outputs must be assigned a non-empty value.`);
1944
+ }
1945
+ const pillValue = resolveToString(shape, cidMap, pillTypeMap);
1946
+ const elementMapping = await factory.createRecord({
1947
+ source: assignActionOutputsShape,
1948
+ table: 'sys_element_mapping',
1949
+ properties: {
1950
+ field: outputName,
1951
+ id: actionDefId,
1952
+ table: `${flow_constants_1.ACTION_OUTPUT_TABLE_PREFIX}${actionDefId}`,
1953
+ value: pillValue,
1954
+ },
1955
+ });
1956
+ relatedRecords.push(elementMapping);
1957
+ }
1958
+ }
1959
+ }
1960
+ // ── Error Evaluation ─────────────────────────────────────────────
1961
+ // wfa.errorEvaluation([...]) → sys_hub_action_status_metadata + sys_hub_status_condition records
1962
+ if (errorEvaluationShape) {
1963
+ const actionDefId = actionDefinitionRecord.getId().getValue();
1964
+ // Create the single sys_hub_action_status_metadata record for this action
1965
+ const statusMetadataRecord = await factory.createRecord({
1966
+ source: errorEvaluationShape,
1967
+ table: 'sys_hub_action_status_metadata',
1968
+ properties: {
1969
+ action_type_id: actionDefId,
1970
+ },
1971
+ });
1972
+ relatedRecords.push(statusMetadataRecord);
1973
+ const metadataId = statusMetadataRecord.getId().getValue();
1974
+ // Parse the array of conditions (arg 0)
1975
+ const conditionsArg = errorEvaluationShape.getArgument(0);
1976
+ if (conditionsArg?.isArray()) {
1977
+ const elements = conditionsArg.asArray().getElements(false);
1978
+ let condOrder = 1;
1979
+ for (const condElement of elements) {
1980
+ if (!condElement.isObject()) {
1981
+ continue;
1982
+ }
1983
+ const condObj = condElement.asObject();
1984
+ // Extract fields from { label, condition, status, dontTreatAsError }
1985
+ const labelShape = condObj.get('label');
1986
+ const conditionShape = condObj.get('condition');
1987
+ const statusShape = condObj.get('status')?.ifObject()?.asObject();
1988
+ const dontTreatShape = condObj.get('dontTreatAsError');
1989
+ const label = labelShape?.ifString()?.getValue() ?? '';
1990
+ const condition = conditionShape ? resolveToString(conditionShape, cidMap, pillTypeMap) : '';
1991
+ const dontTreatRaw = dontTreatShape?.getValue?.();
1992
+ const dontTreatAsError = dontTreatRaw === true || dontTreatRaw === 'true';
1993
+ // Resolve status code and message (may contain datapills).
1994
+ // Use entries({ resolve: false }) to get raw shapes — without this,
1995
+ // PillShape values get auto-resolved and lose their pill reference.
1996
+ let codeValue = '';
1997
+ let messageValue = '';
1998
+ if (statusShape) {
1999
+ for (const [field, fieldShape] of statusShape.entries({ resolve: false })) {
2000
+ const shape = fieldShape;
2001
+ if (field === 'code') {
2002
+ codeValue = resolveToString(shape, cidMap, pillTypeMap);
2003
+ }
2004
+ else if (field === 'message') {
2005
+ messageValue = resolveToString(shape, cidMap, pillTypeMap);
2006
+ }
2007
+ }
2008
+ }
2009
+ const statusJson = buildStatusConditionJson(codeValue, messageValue);
2010
+ const conditionRecord = await factory.createRecord({
2011
+ source: errorEvaluationShape,
2012
+ table: 'sys_hub_status_condition',
2013
+ properties: {
2014
+ action_status_metadata_id: metadataId,
2015
+ condition,
2016
+ dont_treat_as_error: dontTreatAsError,
2017
+ label,
2018
+ order: condOrder,
2019
+ status: statusJson,
2020
+ },
2021
+ });
2022
+ relatedRecords.push(conditionRecord);
2023
+ condOrder++;
2024
+ }
2025
+ }
2026
+ // Create the __action_status__ element_mapping record.
2027
+ // The platform requires this to connect error evaluation status
2028
+ // conditions to the action's output. Its value is the base
2029
+ // FDACTIONSTATUS JSON with empty code/message mapped fields.
2030
+ const actionStatusMapping = await factory.createRecord({
2031
+ source: errorEvaluationShape,
2032
+ table: 'sys_element_mapping',
2033
+ properties: {
2034
+ field: '__action_status__',
2035
+ id: actionDefId,
2036
+ table: `${flow_constants_1.ACTION_OUTPUT_TABLE_PREFIX}${actionDefId}`,
2037
+ value: buildStatusConditionJson('', ''),
2038
+ },
2039
+ });
2040
+ relatedRecords.push(actionStatusMapping);
2041
+ }
1443
2042
  const collectedStepPills = extractStepPillsFromRecords(relatedRecords, cidToLabelMap, pillTypeMap);
1444
2043
  const dotWalkPills = extractActionDotWalkPills(relatedRecords, pillTypeMap);
1445
2044
  if (inputsConfig) {