@servicenow/sdk-build-plugins 4.2.0 → 4.4.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 (349) hide show
  1. package/dist/acl-plugin.js +11 -0
  2. package/dist/acl-plugin.js.map +1 -1
  3. package/dist/applicability-plugin.d.ts +2 -0
  4. package/dist/applicability-plugin.js +72 -0
  5. package/dist/applicability-plugin.js.map +1 -0
  6. package/dist/atf/test-plugin.js +5 -2
  7. package/dist/atf/test-plugin.js.map +1 -1
  8. package/dist/basic-syntax-plugin.js +7 -1
  9. package/dist/basic-syntax-plugin.js.map +1 -1
  10. package/dist/business-rule-plugin.js +1 -0
  11. package/dist/business-rule-plugin.js.map +1 -1
  12. package/dist/call-expression-plugin.js +1 -107
  13. package/dist/call-expression-plugin.js.map +1 -1
  14. package/dist/column/column-to-record.d.ts +10 -3
  15. package/dist/column/column-to-record.js +44 -7
  16. package/dist/column/column-to-record.js.map +1 -1
  17. package/dist/column-plugin.d.ts +3 -1
  18. package/dist/column-plugin.js +12 -12
  19. package/dist/column-plugin.js.map +1 -1
  20. package/dist/dashboard/dashboard-component-property-defaults.d.ts +152 -0
  21. package/dist/dashboard/dashboard-component-property-defaults.js +264 -0
  22. package/dist/dashboard/dashboard-component-property-defaults.js.map +1 -0
  23. package/dist/dashboard/dashboard-component-resolver.d.ts +13 -0
  24. package/dist/dashboard/dashboard-component-resolver.js +69 -0
  25. package/dist/dashboard/dashboard-component-resolver.js.map +1 -0
  26. package/dist/dashboard/dashboard-plugin.d.ts +12 -0
  27. package/dist/dashboard/dashboard-plugin.js +397 -0
  28. package/dist/dashboard/dashboard-plugin.js.map +1 -0
  29. package/dist/data-plugin.d.ts +3 -0
  30. package/dist/data-plugin.js +61 -113
  31. package/dist/data-plugin.js.map +1 -1
  32. package/dist/email-notification-plugin.d.ts +2 -0
  33. package/dist/email-notification-plugin.js +541 -0
  34. package/dist/email-notification-plugin.js.map +1 -0
  35. package/dist/flow/constants/flow-plugin-constants.d.ts +58 -0
  36. package/dist/flow/constants/flow-plugin-constants.js +70 -0
  37. package/dist/flow/constants/flow-plugin-constants.js.map +1 -0
  38. package/dist/flow/flow-logic/flow-logic-constants.d.ts +38 -0
  39. package/dist/flow/flow-logic/flow-logic-constants.js +118 -0
  40. package/dist/flow/flow-logic/flow-logic-constants.js.map +1 -0
  41. package/dist/flow/flow-logic/flow-logic-diagnostics.d.ts +19 -0
  42. package/dist/flow/flow-logic/flow-logic-diagnostics.js +503 -0
  43. package/dist/flow/flow-logic/flow-logic-diagnostics.js.map +1 -0
  44. package/dist/flow/flow-logic/flow-logic-plugin-helpers.d.ts +62 -0
  45. package/dist/flow/flow-logic/flow-logic-plugin-helpers.js +2092 -0
  46. package/dist/flow/flow-logic/flow-logic-plugin-helpers.js.map +1 -0
  47. package/dist/flow/flow-logic/flow-logic-plugin.d.ts +52 -0
  48. package/dist/flow/flow-logic/flow-logic-plugin.js +283 -0
  49. package/dist/flow/flow-logic/flow-logic-plugin.js.map +1 -0
  50. package/dist/flow/flow-logic/flow-logic-shapes.d.ts +104 -0
  51. package/dist/flow/flow-logic/flow-logic-shapes.js +201 -0
  52. package/dist/flow/flow-logic/flow-logic-shapes.js.map +1 -0
  53. package/dist/flow/plugins/approval-rules-plugin.d.ts +2 -0
  54. package/dist/flow/plugins/approval-rules-plugin.js +49 -0
  55. package/dist/flow/plugins/approval-rules-plugin.js.map +1 -0
  56. package/dist/flow/plugins/flow-action-definition-plugin.d.ts +2 -0
  57. package/dist/flow/plugins/flow-action-definition-plugin.js +286 -0
  58. package/dist/flow/plugins/flow-action-definition-plugin.js.map +1 -0
  59. package/dist/flow/plugins/flow-data-pill-plugin.d.ts +9 -0
  60. package/dist/flow/plugins/flow-data-pill-plugin.js +212 -0
  61. package/dist/flow/plugins/flow-data-pill-plugin.js.map +1 -0
  62. package/dist/flow/plugins/flow-definition-plugin.d.ts +2 -0
  63. package/dist/flow/plugins/flow-definition-plugin.js +1668 -0
  64. package/dist/flow/plugins/flow-definition-plugin.js.map +1 -0
  65. package/dist/flow/plugins/flow-diagnostics-plugin.d.ts +26 -0
  66. package/dist/flow/plugins/flow-diagnostics-plugin.js +217 -0
  67. package/dist/flow/plugins/flow-diagnostics-plugin.js.map +1 -0
  68. package/dist/flow/plugins/flow-instance-plugin.d.ts +12 -0
  69. package/dist/flow/plugins/flow-instance-plugin.js +1205 -0
  70. package/dist/flow/plugins/flow-instance-plugin.js.map +1 -0
  71. package/dist/flow/plugins/flow-trigger-instance-plugin.d.ts +2 -0
  72. package/dist/flow/plugins/flow-trigger-instance-plugin.js +338 -0
  73. package/dist/flow/plugins/flow-trigger-instance-plugin.js.map +1 -0
  74. package/dist/flow/plugins/inline-script-plugin.d.ts +39 -0
  75. package/dist/flow/plugins/inline-script-plugin.js +80 -0
  76. package/dist/flow/plugins/inline-script-plugin.js.map +1 -0
  77. package/dist/flow/plugins/step-definition-plugin.d.ts +5 -0
  78. package/dist/flow/plugins/step-definition-plugin.js +71 -0
  79. package/dist/flow/plugins/step-definition-plugin.js.map +1 -0
  80. package/dist/flow/plugins/step-instance-plugin.d.ts +31 -0
  81. package/dist/flow/plugins/step-instance-plugin.js +339 -0
  82. package/dist/flow/plugins/step-instance-plugin.js.map +1 -0
  83. package/dist/flow/plugins/trigger-plugin.d.ts +2 -0
  84. package/dist/flow/plugins/trigger-plugin.js +96 -0
  85. package/dist/flow/plugins/trigger-plugin.js.map +1 -0
  86. package/dist/flow/plugins/wfa-datapill-plugin.d.ts +15 -0
  87. package/dist/flow/plugins/wfa-datapill-plugin.js +178 -0
  88. package/dist/flow/plugins/wfa-datapill-plugin.js.map +1 -0
  89. package/dist/flow/utils/approval-rules-processor.d.ts +13 -0
  90. package/dist/flow/utils/approval-rules-processor.js +267 -0
  91. package/dist/flow/utils/approval-rules-processor.js.map +1 -0
  92. package/dist/flow/utils/built-in-complex-objects.d.ts +19 -0
  93. package/dist/flow/utils/built-in-complex-objects.js +62 -0
  94. package/dist/flow/utils/built-in-complex-objects.js.map +1 -0
  95. package/dist/flow/utils/complex-object-resolver.d.ts +8 -0
  96. package/dist/flow/utils/complex-object-resolver.js +614 -0
  97. package/dist/flow/utils/complex-object-resolver.js.map +1 -0
  98. package/dist/flow/utils/complex-objects.d.ts +36 -0
  99. package/dist/flow/utils/complex-objects.js +481 -0
  100. package/dist/flow/utils/complex-objects.js.map +1 -0
  101. package/dist/flow/utils/data-pill-shapes.d.ts +58 -0
  102. package/dist/flow/utils/data-pill-shapes.js +135 -0
  103. package/dist/flow/utils/data-pill-shapes.js.map +1 -0
  104. package/dist/flow/utils/datapill-transformer.d.ts +110 -0
  105. package/dist/flow/utils/datapill-transformer.js +503 -0
  106. package/dist/flow/utils/datapill-transformer.js.map +1 -0
  107. package/dist/flow/utils/flow-constants.d.ts +72 -0
  108. package/dist/flow/utils/flow-constants.js +230 -0
  109. package/dist/flow/utils/flow-constants.js.map +1 -0
  110. package/dist/flow/utils/flow-io-to-record.d.ts +44 -0
  111. package/dist/flow/utils/flow-io-to-record.js +409 -0
  112. package/dist/flow/utils/flow-io-to-record.js.map +1 -0
  113. package/dist/flow/utils/flow-shapes.d.ts +161 -0
  114. package/dist/flow/utils/flow-shapes.js +255 -0
  115. package/dist/flow/utils/flow-shapes.js.map +1 -0
  116. package/dist/flow/utils/flow-to-xml.d.ts +16 -0
  117. package/dist/flow/utils/flow-to-xml.js +237 -0
  118. package/dist/flow/utils/flow-to-xml.js.map +1 -0
  119. package/dist/flow/utils/flow-variable-processor.d.ts +51 -0
  120. package/dist/flow/utils/flow-variable-processor.js +69 -0
  121. package/dist/flow/utils/flow-variable-processor.js.map +1 -0
  122. package/dist/flow/utils/label-cache-parser.d.ts +7 -0
  123. package/dist/flow/utils/label-cache-parser.js +24 -0
  124. package/dist/flow/utils/label-cache-parser.js.map +1 -0
  125. package/dist/flow/utils/label-cache-processor.d.ts +119 -0
  126. package/dist/flow/utils/label-cache-processor.js +719 -0
  127. package/dist/flow/utils/label-cache-processor.js.map +1 -0
  128. package/dist/flow/utils/pill-string-parser.d.ts +88 -0
  129. package/dist/flow/utils/pill-string-parser.js +306 -0
  130. package/dist/flow/utils/pill-string-parser.js.map +1 -0
  131. package/dist/flow/utils/schema-to-flow-object.d.ts +22 -0
  132. package/dist/flow/utils/schema-to-flow-object.js +318 -0
  133. package/dist/flow/utils/schema-to-flow-object.js.map +1 -0
  134. package/dist/flow/utils/service-catalog.d.ts +47 -0
  135. package/dist/flow/utils/service-catalog.js +137 -0
  136. package/dist/flow/utils/service-catalog.js.map +1 -0
  137. package/dist/flow/utils/utils.d.ts +117 -0
  138. package/dist/flow/utils/utils.js +345 -0
  139. package/dist/flow/utils/utils.js.map +1 -0
  140. package/dist/index.d.ts +20 -1
  141. package/dist/index.js +21 -1
  142. package/dist/index.js.map +1 -1
  143. package/dist/list-plugin.js +1 -1
  144. package/dist/list-plugin.js.map +1 -1
  145. package/dist/now-attach-plugin.d.ts +1 -0
  146. package/dist/now-attach-plugin.js +10 -10
  147. package/dist/now-attach-plugin.js.map +1 -1
  148. package/dist/now-ref-plugin.js +1 -1
  149. package/dist/now-ref-plugin.js.map +1 -1
  150. package/dist/record-plugin.d.ts +29 -0
  151. package/dist/record-plugin.js +66 -7
  152. package/dist/record-plugin.js.map +1 -1
  153. package/dist/repack/index.d.ts +2 -0
  154. package/dist/repack/index.js +8 -0
  155. package/dist/repack/index.js.map +1 -1
  156. package/dist/rest-api-plugin.js +54 -44
  157. package/dist/rest-api-plugin.js.map +1 -1
  158. package/dist/server-module-plugin/index.d.ts +10 -0
  159. package/dist/server-module-plugin/index.js +83 -59
  160. package/dist/server-module-plugin/index.js.map +1 -1
  161. package/dist/service-catalog/catalog-clientscript-plugin.d.ts +2 -0
  162. package/dist/service-catalog/catalog-clientscript-plugin.js +117 -0
  163. package/dist/service-catalog/catalog-clientscript-plugin.js.map +1 -0
  164. package/dist/service-catalog/catalog-item-plugin.d.ts +2 -0
  165. package/dist/service-catalog/catalog-item-plugin.js +115 -0
  166. package/dist/service-catalog/catalog-item-plugin.js.map +1 -0
  167. package/dist/service-catalog/catalog-ui-policy-plugin.d.ts +2 -0
  168. package/dist/service-catalog/catalog-ui-policy-plugin.js +266 -0
  169. package/dist/service-catalog/catalog-ui-policy-plugin.js.map +1 -0
  170. package/dist/service-catalog/index.d.ts +5 -0
  171. package/dist/service-catalog/index.js +22 -0
  172. package/dist/service-catalog/index.js.map +1 -0
  173. package/dist/service-catalog/record-to-shape.d.ts +6 -0
  174. package/dist/service-catalog/record-to-shape.js +93 -0
  175. package/dist/service-catalog/record-to-shape.js.map +1 -0
  176. package/dist/service-catalog/sc-record-producer-plugin.d.ts +2 -0
  177. package/dist/service-catalog/sc-record-producer-plugin.js +140 -0
  178. package/dist/service-catalog/sc-record-producer-plugin.js.map +1 -0
  179. package/dist/service-catalog/service-catalog-base.d.ts +311 -0
  180. package/dist/service-catalog/service-catalog-base.js +542 -0
  181. package/dist/service-catalog/service-catalog-base.js.map +1 -0
  182. package/dist/service-catalog/service-catalog-diagnostics.d.ts +45 -0
  183. package/dist/service-catalog/service-catalog-diagnostics.js +172 -0
  184. package/dist/service-catalog/service-catalog-diagnostics.js.map +1 -0
  185. package/dist/service-catalog/shape-to-record.d.ts +8 -0
  186. package/dist/service-catalog/shape-to-record.js +235 -0
  187. package/dist/service-catalog/shape-to-record.js.map +1 -0
  188. package/dist/service-catalog/utils.d.ts +323 -0
  189. package/dist/service-catalog/utils.js +1216 -0
  190. package/dist/service-catalog/utils.js.map +1 -0
  191. package/dist/service-catalog/variable-helper.d.ts +43 -0
  192. package/dist/service-catalog/variable-helper.js +92 -0
  193. package/dist/service-catalog/variable-helper.js.map +1 -0
  194. package/dist/service-catalog/variable-set-plugin.d.ts +2 -0
  195. package/dist/service-catalog/variable-set-plugin.js +175 -0
  196. package/dist/service-catalog/variable-set-plugin.js.map +1 -0
  197. package/dist/service-catalog/variables-transform.d.ts +139 -0
  198. package/dist/service-catalog/variables-transform.js +403 -0
  199. package/dist/service-catalog/variables-transform.js.map +1 -0
  200. package/dist/sla/sla-validators.d.ts +61 -0
  201. package/dist/sla/sla-validators.js +224 -0
  202. package/dist/sla/sla-validators.js.map +1 -0
  203. package/dist/sla-plugin.d.ts +5 -0
  204. package/dist/sla-plugin.js +280 -0
  205. package/dist/sla-plugin.js.map +1 -0
  206. package/dist/static-content-plugin.js +25 -2
  207. package/dist/static-content-plugin.js.map +1 -1
  208. package/dist/table-plugin.js +32 -15
  209. package/dist/table-plugin.js.map +1 -1
  210. package/dist/ui-page-plugin.js +832 -19
  211. package/dist/ui-page-plugin.js.map +1 -1
  212. package/dist/ui-policy-plugin.js +5 -7
  213. package/dist/ui-policy-plugin.js.map +1 -1
  214. package/dist/utils.d.ts +10 -1
  215. package/dist/utils.js +16 -0
  216. package/dist/utils.js.map +1 -1
  217. package/dist/ux-list-menu-config-plugin.d.ts +2 -0
  218. package/dist/ux-list-menu-config-plugin.js +292 -0
  219. package/dist/ux-list-menu-config-plugin.js.map +1 -0
  220. package/dist/workspace-plugin/chrome-tab.d.ts +2 -0
  221. package/dist/workspace-plugin/chrome-tab.js +46 -0
  222. package/dist/workspace-plugin/chrome-tab.js.map +1 -0
  223. package/dist/workspace-plugin/constants.d.ts +52 -0
  224. package/dist/workspace-plugin/constants.js +56 -0
  225. package/dist/workspace-plugin/constants.js.map +1 -0
  226. package/dist/workspace-plugin/fluent-utils.d.ts +9 -0
  227. package/dist/workspace-plugin/fluent-utils.js +60 -0
  228. package/dist/workspace-plugin/fluent-utils.js.map +1 -0
  229. package/dist/workspace-plugin/page.d.ts +8 -0
  230. package/dist/workspace-plugin/page.js +108 -0
  231. package/dist/workspace-plugin/page.js.map +1 -0
  232. package/dist/workspace-plugin/screen.d.ts +1 -0
  233. package/dist/workspace-plugin/screen.js +38 -0
  234. package/dist/workspace-plugin/screen.js.map +1 -0
  235. package/dist/workspace-plugin/templates/index.d.ts +10 -0
  236. package/dist/workspace-plugin/templates/index.js +20 -0
  237. package/dist/workspace-plugin/templates/index.js.map +1 -0
  238. package/dist/workspace-plugin/templates/record-page-composition.d.ts +1 -0
  239. package/dist/workspace-plugin/templates/record-page-composition.js +4043 -0
  240. package/dist/workspace-plugin/templates/record-page-composition.js.map +1 -0
  241. package/dist/workspace-plugin/templates/record-page-data.d.ts +1 -0
  242. package/dist/workspace-plugin/templates/record-page-data.js +527 -0
  243. package/dist/workspace-plugin/templates/record-page-data.js.map +1 -0
  244. package/dist/workspace-plugin/templates/record-page-interalEventMappings.d.ts +1 -0
  245. package/dist/workspace-plugin/templates/record-page-interalEventMappings.js +39 -0
  246. package/dist/workspace-plugin/templates/record-page-interalEventMappings.js.map +1 -0
  247. package/dist/workspace-plugin/templates/record-page-layoutModel.d.ts +1 -0
  248. package/dist/workspace-plugin/templates/record-page-layoutModel.js +55 -0
  249. package/dist/workspace-plugin/templates/record-page-layoutModel.js.map +1 -0
  250. package/dist/workspace-plugin/templates/record-page-properties.d.ts +1 -0
  251. package/dist/workspace-plugin/templates/record-page-properties.js +135 -0
  252. package/dist/workspace-plugin/templates/record-page-properties.js.map +1 -0
  253. package/dist/workspace-plugin/templates/record-page.d.ts +3 -0
  254. package/dist/workspace-plugin/templates/record-page.js +8 -0
  255. package/dist/workspace-plugin/templates/record-page.js.map +1 -0
  256. package/dist/workspace-plugin.d.ts +2 -0
  257. package/dist/workspace-plugin.js +453 -0
  258. package/dist/workspace-plugin.js.map +1 -0
  259. package/package.json +10 -12
  260. package/src/acl-plugin.ts +16 -1
  261. package/src/applicability-plugin.ts +82 -0
  262. package/src/atf/test-plugin.ts +6 -3
  263. package/src/basic-syntax-plugin.ts +10 -1
  264. package/src/business-rule-plugin.ts +2 -1
  265. package/src/call-expression-plugin.ts +2 -130
  266. package/src/column/column-to-record.ts +54 -8
  267. package/src/column-plugin.ts +29 -13
  268. package/src/dashboard/dashboard-component-property-defaults.ts +277 -0
  269. package/src/dashboard/dashboard-component-resolver.ts +69 -0
  270. package/src/dashboard/dashboard-plugin.ts +450 -0
  271. package/src/data-plugin.ts +67 -139
  272. package/src/email-notification-plugin.ts +850 -0
  273. package/src/flow/constants/flow-plugin-constants.ts +79 -0
  274. package/src/flow/flow-logic/flow-logic-constants.ts +120 -0
  275. package/src/flow/flow-logic/flow-logic-diagnostics.ts +591 -0
  276. package/src/flow/flow-logic/flow-logic-plugin-helpers.ts +2550 -0
  277. package/src/flow/flow-logic/flow-logic-plugin.ts +337 -0
  278. package/src/flow/flow-logic/flow-logic-shapes.ts +215 -0
  279. package/src/flow/plugins/approval-rules-plugin.ts +48 -0
  280. package/src/flow/plugins/flow-action-definition-plugin.ts +295 -0
  281. package/src/flow/plugins/flow-data-pill-plugin.ts +258 -0
  282. package/src/flow/plugins/flow-definition-plugin.ts +2173 -0
  283. package/src/flow/plugins/flow-diagnostics-plugin.ts +280 -0
  284. package/src/flow/plugins/flow-instance-plugin.ts +1499 -0
  285. package/src/flow/plugins/flow-trigger-instance-plugin.ts +444 -0
  286. package/src/flow/plugins/inline-script-plugin.ts +83 -0
  287. package/src/flow/plugins/step-definition-plugin.ts +67 -0
  288. package/src/flow/plugins/step-instance-plugin.ts +431 -0
  289. package/src/flow/plugins/trigger-plugin.ts +95 -0
  290. package/src/flow/plugins/wfa-datapill-plugin.ts +213 -0
  291. package/src/flow/utils/approval-rules-processor.ts +298 -0
  292. package/src/flow/utils/built-in-complex-objects.ts +81 -0
  293. package/src/flow/utils/complex-object-resolver.ts +875 -0
  294. package/src/flow/utils/complex-objects.ts +656 -0
  295. package/src/flow/utils/data-pill-shapes.ts +165 -0
  296. package/src/flow/utils/datapill-transformer.ts +632 -0
  297. package/src/flow/utils/flow-constants.ts +285 -0
  298. package/src/flow/utils/flow-io-to-record.ts +533 -0
  299. package/src/flow/utils/flow-shapes.ts +296 -0
  300. package/src/flow/utils/flow-to-xml.ts +318 -0
  301. package/src/flow/utils/flow-variable-processor.ts +100 -0
  302. package/src/flow/utils/label-cache-parser.ts +37 -0
  303. package/src/flow/utils/label-cache-processor.ts +870 -0
  304. package/src/flow/utils/pill-string-parser.ts +375 -0
  305. package/src/flow/utils/schema-to-flow-object.ts +385 -0
  306. package/src/flow/utils/service-catalog.ts +174 -0
  307. package/src/flow/utils/utils.ts +395 -0
  308. package/src/index.ts +20 -1
  309. package/src/list-plugin.ts +1 -1
  310. package/src/now-attach-plugin.ts +14 -11
  311. package/src/now-ref-plugin.ts +1 -1
  312. package/src/record-plugin.ts +76 -11
  313. package/src/repack/index.ts +14 -0
  314. package/src/rest-api-plugin.ts +62 -50
  315. package/src/server-module-plugin/index.ts +112 -86
  316. package/src/service-catalog/catalog-clientscript-plugin.ts +140 -0
  317. package/src/service-catalog/catalog-item-plugin.ts +162 -0
  318. package/src/service-catalog/catalog-ui-policy-plugin.ts +324 -0
  319. package/src/service-catalog/index.ts +5 -0
  320. package/src/service-catalog/record-to-shape.ts +109 -0
  321. package/src/service-catalog/sc-record-producer-plugin.ts +201 -0
  322. package/src/service-catalog/service-catalog-base.ts +600 -0
  323. package/src/service-catalog/service-catalog-diagnostics.ts +254 -0
  324. package/src/service-catalog/shape-to-record.ts +279 -0
  325. package/src/service-catalog/utils.ts +1455 -0
  326. package/src/service-catalog/variable-helper.ts +135 -0
  327. package/src/service-catalog/variable-set-plugin.ts +197 -0
  328. package/src/service-catalog/variables-transform.ts +438 -0
  329. package/src/sla/sla-validators.ts +331 -0
  330. package/src/sla-plugin.ts +358 -0
  331. package/src/static-content-plugin.ts +25 -2
  332. package/src/table-plugin.ts +49 -16
  333. package/src/ui-page-plugin.ts +1063 -20
  334. package/src/ui-policy-plugin.ts +5 -9
  335. package/src/utils.ts +24 -1
  336. package/src/ux-list-menu-config-plugin.ts +312 -0
  337. package/src/workspace-plugin/chrome-tab.ts +44 -0
  338. package/src/workspace-plugin/constants.ts +53 -0
  339. package/src/workspace-plugin/fluent-utils.ts +60 -0
  340. package/src/workspace-plugin/page.ts +139 -0
  341. package/src/workspace-plugin/screen.ts +34 -0
  342. package/src/workspace-plugin/templates/index.ts +17 -0
  343. package/src/workspace-plugin/templates/record-page-composition.ts +4051 -0
  344. package/src/workspace-plugin/templates/record-page-data.ts +523 -0
  345. package/src/workspace-plugin/templates/record-page-interalEventMappings.ts +35 -0
  346. package/src/workspace-plugin/templates/record-page-layoutModel.ts +51 -0
  347. package/src/workspace-plugin/templates/record-page-properties.ts +131 -0
  348. package/src/workspace-plugin/templates/record-page.ts +6 -0
  349. package/src/workspace-plugin.ts +574 -0
@@ -0,0 +1,850 @@
1
+ import {
2
+ CallExpressionShape,
3
+ Plugin,
4
+ isGUID,
5
+ Shape,
6
+ type ObjectShape,
7
+ type Diagnostics,
8
+ } from '@servicenow/sdk-build-core'
9
+ import { NowIdShape } from './now-id-plugin'
10
+ import type { EmailNotification } from '@servicenow/sdk-core/runtime/notification'
11
+
12
+ // Use index access types instead of exported utility types
13
+ type TriggerConditions = NonNullable<EmailNotification<string>['triggerConditions']>
14
+ type RecipientDetails = NonNullable<EmailNotification<string>['recipientDetails']>
15
+ type EmailContent = NonNullable<EmailNotification<string>['emailContent']>
16
+ type Digest = NonNullable<EmailNotification<string>['digest']>
17
+
18
+ const DEFAULT_EMAIL_CATEGORY = 'c97d83137f4432005f58108c3ffa917a'
19
+ const DEFAULT_DIGEST_SEPARATOR_HTML = '<p>&nbsp;</p>\n<hr>\n<p>&nbsp;</p>'
20
+ const DEFAULT_DIGEST_SEPARATOR_TEXT =
21
+ '--------------------------------------------------------------------------------\n'
22
+
23
+ // Helper to conditionally add property to object, Skips undefined and empty strings, but includes false/0/null
24
+ const addProperty = (obj: Record<string, unknown>, key: string, value: unknown) => {
25
+ if (value === undefined) {
26
+ return
27
+ }
28
+ if (typeof value === 'string' && value === '') {
29
+ return
30
+ } // Skip empty strings
31
+ obj[key] = value
32
+ }
33
+
34
+ // Utility function to get a nested object
35
+ const getNestedObjectProp = (arg: ObjectShape, key: string): ObjectShape | null => {
36
+ return arg.get(key).ifObject()?.asObject() || null // Returns the object or null
37
+ }
38
+
39
+ // Utility function to join array elements into a comma-separated string
40
+ const joinArrayToString = (array: Shape | undefined): string => {
41
+ return array?.ifArray() ? array.asArray().getValue().join(',') : '' // Join array elements with a comma
42
+ }
43
+
44
+ // Helper to convert comma-separated string to array (used in toShape)
45
+ const commaStringToArray = (value: string | undefined): string[] | undefined => {
46
+ if (!value || value.trim() === '') {
47
+ return undefined
48
+ }
49
+ return value
50
+ .split(',')
51
+ .map((v) => v.trim())
52
+ .filter((v) => v !== '')
53
+ }
54
+
55
+ // Helper to validate email format (basic validation)
56
+ const isValidEmail = (email: string): boolean => {
57
+ // Simple email regex: allows most valid emails while catching obvious errors
58
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
59
+ return emailRegex.test(email)
60
+ }
61
+
62
+ const showGuidFieldDiagnostic = (
63
+ inputReceived: Shape | undefined,
64
+ fieldName: string,
65
+ tableName: string,
66
+ diagnostics: Diagnostics
67
+ ) => {
68
+ if (inputReceived) {
69
+ const receivedValue = inputReceived.getValue()
70
+ diagnostics.error(
71
+ inputReceived,
72
+ `'${fieldName}' must be a valid GUID or a Record<'${tableName}'>. Received: '${receivedValue}'`
73
+ )
74
+ }
75
+ }
76
+
77
+ export const EmailNotificationPlugin = Plugin.create({
78
+ name: 'EmailNotificationPlugin',
79
+ records: {
80
+ sysevent_email_action: {
81
+ toShape(record) {
82
+ const generationType = record.get('generation_type')?.getValue() ?? 'engine'
83
+
84
+ return {
85
+ success: true,
86
+ value: new CallExpressionShape({
87
+ source: record,
88
+ callee: 'EmailNotification',
89
+ args: [
90
+ record.transform(({ $ }) => ({
91
+ $id: $.val(NowIdShape.from(record)),
92
+ table: $.from('collection'),
93
+ name: $,
94
+ description: $.def(''),
95
+ category: $,
96
+ notificationType: $.from('type').def('email'),
97
+ active: $.toBoolean().def(true),
98
+ mandatory: $.toBoolean().def(false),
99
+ enableDynamicTranslation: $.from('enable_dynamic_translation').toBoolean().def(false),
100
+
101
+ // Build triggerConditions using $.from().map()
102
+ triggerConditions: $.from(
103
+ 'generation_type',
104
+ 'action_insert',
105
+ 'action_update',
106
+ 'event_name',
107
+ 'weight',
108
+ 'order',
109
+ 'condition',
110
+ 'advanced_condition',
111
+ 'item_table',
112
+ 'affected_field_on_event',
113
+ 'item'
114
+ ).map(
115
+ (
116
+ genType,
117
+ actionInsert,
118
+ actionUpdate,
119
+ eventName,
120
+ weight,
121
+ order,
122
+ condition,
123
+ advancedCondition,
124
+ itemTable,
125
+ affectedField,
126
+ item
127
+ ) => {
128
+ const triggerConditions: TriggerConditions = {
129
+ generationType: (genType?.getValue() ?? 'engine') as NonNullable<
130
+ TriggerConditions['generationType']
131
+ >,
132
+ }
133
+
134
+ const genTypeValue = genType?.getValue() ?? 'engine'
135
+ if (genTypeValue === 'engine') {
136
+ addProperty(
137
+ triggerConditions,
138
+ 'onRecordInsert',
139
+ actionInsert?.toBoolean().getValue()
140
+ )
141
+ addProperty(
142
+ triggerConditions,
143
+ 'onRecordUpdate',
144
+ actionUpdate?.toBoolean().getValue()
145
+ )
146
+ } else if (genTypeValue === 'event') {
147
+ addProperty(triggerConditions, 'eventName', eventName?.getValue())
148
+ }
149
+
150
+ const weightValue = weight?.getValue()
151
+ if (weightValue !== undefined && weightValue !== '' && weightValue !== null) {
152
+ const weightNum = Number(weightValue)
153
+ if (!isNaN(weightNum) && weightNum !== 0) {
154
+ triggerConditions.weight = weightNum
155
+ }
156
+ }
157
+
158
+ const orderValue = order?.getValue()
159
+ if (orderValue !== undefined && orderValue !== '' && orderValue !== null) {
160
+ const orderNum = Number(orderValue)
161
+ if (!isNaN(orderNum) && orderNum !== 100) {
162
+ triggerConditions.order = orderNum
163
+ }
164
+ }
165
+
166
+ addProperty(triggerConditions, 'condition', condition?.getValue())
167
+ addProperty(
168
+ triggerConditions,
169
+ 'advancedCondition',
170
+ advancedCondition?.getValue()
171
+ )
172
+ addProperty(triggerConditions, 'itemTable', itemTable?.getValue())
173
+ addProperty(
174
+ triggerConditions,
175
+ 'affectedFieldOnEvent',
176
+ affectedField?.getValue()
177
+ )
178
+ addProperty(triggerConditions, 'item', item?.getValue())
179
+
180
+ return triggerConditions
181
+ }
182
+ ),
183
+
184
+ // Build recipientDetails using $.from().map()
185
+ recipientDetails: $.from(
186
+ 'recipient_users',
187
+ 'recipient_fields',
188
+ 'recipient_groups',
189
+ 'exclude_delegates',
190
+ 'subscribable',
191
+ 'send_self',
192
+ 'event_parm_1',
193
+ 'event_parm_2'
194
+ ).map(
195
+ (
196
+ users,
197
+ fields,
198
+ groups,
199
+ excludeDelegates,
200
+ subscribable,
201
+ sendSelf,
202
+ eventParm1,
203
+ eventParm2
204
+ ) => {
205
+ const recipientDetails: RecipientDetails = {}
206
+
207
+ addProperty(
208
+ recipientDetails,
209
+ 'recipientUsers',
210
+ commaStringToArray(users?.getValue() as string)
211
+ )
212
+ addProperty(
213
+ recipientDetails,
214
+ 'recipientFields',
215
+ commaStringToArray(fields?.getValue() as string)
216
+ )
217
+ addProperty(
218
+ recipientDetails,
219
+ 'recipientGroups',
220
+ commaStringToArray(groups?.getValue() as string)
221
+ )
222
+ addProperty(
223
+ recipientDetails,
224
+ 'excludeDelegates',
225
+ excludeDelegates?.toBoolean().getValue()
226
+ )
227
+ addProperty(
228
+ recipientDetails,
229
+ 'isSubscribableByAllUsers',
230
+ subscribable?.toBoolean().getValue()
231
+ )
232
+ addProperty(recipientDetails, 'sendToCreator', sendSelf?.toBoolean().getValue())
233
+
234
+ if (generationType === 'event') {
235
+ addProperty(
236
+ recipientDetails,
237
+ 'eventParm1WithRecipient',
238
+ eventParm1?.toBoolean().getValue()
239
+ )
240
+ addProperty(
241
+ recipientDetails,
242
+ 'eventParm2WithRecipient',
243
+ eventParm2?.toBoolean().getValue()
244
+ )
245
+ }
246
+
247
+ return Object.keys(recipientDetails).length > 0 ? recipientDetails : undefined
248
+ }
249
+ ),
250
+
251
+ // Build emailContent using $.from().map()
252
+ emailContent: $.from(
253
+ 'content_type',
254
+ 'message_html',
255
+ 'message_text',
256
+ 'template',
257
+ 'style',
258
+ 'subject',
259
+ 'message',
260
+ 'sms_alternate',
261
+ 'importance',
262
+ 'from',
263
+ 'reply_to',
264
+ 'message_list',
265
+ 'include_attachments',
266
+ 'omit_watermark',
267
+ 'push_message_only',
268
+ 'force_delivery'
269
+ ).map(
270
+ (
271
+ contentTypeField,
272
+ messageHtml,
273
+ messageText,
274
+ template,
275
+ style,
276
+ subject,
277
+ message,
278
+ smsAlternate,
279
+ importance,
280
+ from,
281
+ replyTo,
282
+ messageList,
283
+ includeAttachments,
284
+ omitWatermark,
285
+ pushMessageOnly,
286
+ forceDelivery
287
+ ) => {
288
+ const emailContent: EmailContent = {}
289
+
290
+ const contentTypeValue = contentTypeField?.getValue() || 'text/html'
291
+ if (contentTypeValue !== 'text/html') {
292
+ emailContent.contentType = contentTypeValue as NonNullable<
293
+ EmailContent['contentType']
294
+ >
295
+ }
296
+
297
+ if (
298
+ contentTypeValue === 'text/html' ||
299
+ contentTypeValue === 'multipart/mixed'
300
+ ) {
301
+ addProperty(emailContent, 'messageHtml', messageHtml?.getValue())
302
+ }
303
+ if (
304
+ contentTypeValue === 'text/plain' ||
305
+ contentTypeValue === 'multipart/mixed'
306
+ ) {
307
+ addProperty(emailContent, 'messageText', messageText?.getValue())
308
+ }
309
+
310
+ addProperty(emailContent, 'template', template?.getValue())
311
+ addProperty(emailContent, 'style', style?.getValue())
312
+ addProperty(emailContent, 'subject', subject?.getValue())
313
+ addProperty(emailContent, 'message', message?.getValue())
314
+ addProperty(emailContent, 'smsAlternate', smsAlternate?.getValue())
315
+ addProperty(emailContent, 'importance', importance?.getValue())
316
+ addProperty(emailContent, 'from', from?.getValue())
317
+ addProperty(emailContent, 'replyTo', replyTo?.getValue())
318
+ addProperty(
319
+ emailContent,
320
+ 'pushMessageList',
321
+ commaStringToArray(messageList?.getValue() as string)
322
+ )
323
+ addProperty(
324
+ emailContent,
325
+ 'includeAttachments',
326
+ includeAttachments?.toBoolean().getValue()
327
+ )
328
+ addProperty(
329
+ emailContent,
330
+ 'omitWatermark',
331
+ omitWatermark?.toBoolean().getValue()
332
+ )
333
+ addProperty(
334
+ emailContent,
335
+ 'pushMessageOnly',
336
+ pushMessageOnly?.toBoolean().getValue()
337
+ )
338
+ addProperty(
339
+ emailContent,
340
+ 'forceDelivery',
341
+ forceDelivery?.toBoolean().getValue()
342
+ )
343
+
344
+ return Object.keys(emailContent).length > 0 ? emailContent : undefined
345
+ }
346
+ ),
347
+
348
+ // Build digest using $.from().map()
349
+ digest: $.from(
350
+ 'digestable',
351
+ 'default_digest',
352
+ 'default_interval',
353
+ 'digest_type',
354
+ 'digest_template',
355
+ 'digest_subject',
356
+ 'digest_html',
357
+ 'digest_text',
358
+ 'digest_separator_html',
359
+ 'digest_separator_text',
360
+ 'digest_from',
361
+ 'digest_reply_to'
362
+ ).map(
363
+ (
364
+ digestable,
365
+ defaultDigest,
366
+ defaultInterval,
367
+ digestType,
368
+ digestTemplate,
369
+ digestSubject,
370
+ digestHtml,
371
+ digestText,
372
+ separatorHtml,
373
+ separatorText,
374
+ digestFrom,
375
+ digestReplyTo
376
+ ) => {
377
+ const allowDigest = digestable?.toBoolean().getValue()
378
+ if (!allowDigest) {
379
+ return undefined
380
+ }
381
+
382
+ const digest: Digest = { allow: true }
383
+
384
+ addProperty(digest, 'default', defaultDigest?.toBoolean().getValue())
385
+ addProperty(digest, 'defaultInterval', defaultInterval?.getValue())
386
+
387
+ const digestTypeValue = digestType?.getValue()
388
+ if (digestTypeValue && digestTypeValue !== 'single') {
389
+ digest.type = digestTypeValue as NonNullable<Digest['type']>
390
+ }
391
+
392
+ addProperty(digest, 'template', digestTemplate?.getValue())
393
+ addProperty(digest, 'subject', digestSubject?.getValue())
394
+ addProperty(digest, 'html', digestHtml?.getValue())
395
+ addProperty(digest, 'text', digestText?.getValue())
396
+
397
+ const separatorHtmlValue = separatorHtml?.getValue() as string
398
+ if (
399
+ separatorHtmlValue &&
400
+ separatorHtmlValue !== DEFAULT_DIGEST_SEPARATOR_HTML
401
+ ) {
402
+ digest.separatorHtml = separatorHtmlValue
403
+ }
404
+
405
+ const separatorTextValue = separatorText?.getValue() as string
406
+ if (
407
+ separatorTextValue &&
408
+ separatorTextValue !== DEFAULT_DIGEST_SEPARATOR_TEXT
409
+ ) {
410
+ digest.separatorText = separatorTextValue
411
+ }
412
+
413
+ addProperty(digest, 'from', digestFrom?.getValue())
414
+ addProperty(digest, 'replyTo', digestReplyTo?.getValue())
415
+
416
+ return digest
417
+ }
418
+ ),
419
+ })),
420
+ ],
421
+ }),
422
+ }
423
+ },
424
+ },
425
+ },
426
+ shapes: [
427
+ {
428
+ shape: CallExpressionShape,
429
+ fileTypes: ['fluent'],
430
+ async toRecord(callExpression, { factory, diagnostics }) {
431
+ if (callExpression.getCallee() !== 'EmailNotification') {
432
+ return { success: false }
433
+ }
434
+
435
+ const arg = callExpression.getArgument(0).asObject()
436
+
437
+ // Extract nested objects
438
+ const triggerConditions = getNestedObjectProp(arg, 'triggerConditions')
439
+ const recipientDetails = getNestedObjectProp(arg, 'recipientDetails')
440
+ const emailContent = getNestedObjectProp(arg, 'emailContent')
441
+ const digest = getNestedObjectProp(arg, 'digest')
442
+
443
+ // Handle recipientUsers - array of string (GUID/email) or Record<'sys_user'>
444
+ let recipientUsers: string | undefined
445
+ if (recipientDetails) {
446
+ const recipientUsersArg = recipientDetails.get('recipientUsers')
447
+ if (recipientUsersArg?.isArray()) {
448
+ const elements = recipientUsersArg.getElements()
449
+ const userList: string[] = []
450
+
451
+ elements.forEach((element, index) => {
452
+ if (element.isRecord()) {
453
+ // Extract ID from Record
454
+ userList.push(element.getId().getValue())
455
+ } else if (element.isString()) {
456
+ const str = element.getValue()
457
+ // Accept either GUID or email address
458
+ if (isGUID(str) || isValidEmail(str)) {
459
+ userList.push(str)
460
+ } else {
461
+ diagnostics.error(
462
+ element,
463
+ `'recipientDetails.recipientUsers[${index}]' must be a valid GUID or email address or a Record<'sys_user'>. Received: '${str}'`
464
+ )
465
+ }
466
+ }
467
+ })
468
+
469
+ if (userList.length > 0) {
470
+ recipientUsers = userList.join(',')
471
+ }
472
+ }
473
+ }
474
+
475
+ const recipientFields = joinArrayToString(recipientDetails?.get('recipientFields'))
476
+
477
+ // Handle recipientGroups - array of string (GUID) or Record<'sys_user_group'>
478
+ let recipientGroups: string | undefined
479
+ if (recipientDetails) {
480
+ const recipientGroupsArg = recipientDetails.get('recipientGroups')
481
+ if (recipientGroupsArg?.isArray()) {
482
+ const elements = recipientGroupsArg.getElements()
483
+ const guidList: string[] = []
484
+
485
+ elements.forEach((element, index) => {
486
+ if (element.isRecord()) {
487
+ // Extract ID from Record
488
+ guidList.push(element.getId().getValue())
489
+ } else if (element.isString()) {
490
+ const str = element.getValue()
491
+ // Only accept GUID (no email for groups)
492
+ if (isGUID(str)) {
493
+ guidList.push(str)
494
+ } else {
495
+ showGuidFieldDiagnostic(
496
+ element,
497
+ `recipientDetails.recipientGroups[${index}]`,
498
+ 'sys_user_group',
499
+ diagnostics
500
+ )
501
+ }
502
+ }
503
+ })
504
+
505
+ if (guidList.length > 0) {
506
+ recipientGroups = guidList.join(',')
507
+ }
508
+ }
509
+ }
510
+
511
+ // Handle pushMessageList - array of string (GUID) or Record<'sys_push_notif_msg'>
512
+ let pushMessageList: string | undefined
513
+ if (emailContent) {
514
+ const pushMessageListArg = emailContent.get('pushMessageList')
515
+ if (pushMessageListArg?.isArray()) {
516
+ const elements = pushMessageListArg.getElements()
517
+ const guidList: string[] = []
518
+
519
+ elements.forEach((element, index) => {
520
+ if (element.isRecord()) {
521
+ // Extract ID from Record
522
+ guidList.push(element.getId().getValue())
523
+ } else if (element.isString()) {
524
+ const str = element.getValue()
525
+ // Add GUID string
526
+ if (isGUID(str)) {
527
+ guidList.push(str)
528
+ } else {
529
+ showGuidFieldDiagnostic(
530
+ element,
531
+ `emailContent.pushMessageList[${index}]`,
532
+ 'sys_push_notif_msg',
533
+ diagnostics
534
+ )
535
+ }
536
+ }
537
+ })
538
+
539
+ // Convert to comma-separated string
540
+ if (guidList.length > 0) {
541
+ pushMessageList = guidList.join(',')
542
+ }
543
+ }
544
+ }
545
+
546
+ const digestSeparatorHtml = digest?.get('separatorHtml')?.isDefined()
547
+ ? digest.get('separatorHtml').asString().withContentType('cdata')
548
+ : Shape.from(callExpression, DEFAULT_DIGEST_SEPARATOR_HTML).asString().withContentType('cdata')
549
+ const digestSeparatorText = digest?.get('separatorText')?.isDefined()
550
+ ? digest.get('separatorText').asString().withContentType('cdata')
551
+ : Shape.from(callExpression, DEFAULT_DIGEST_SEPARATOR_TEXT).asString().withContentType('cdata')
552
+
553
+ // Handle template field - can be string (GUID) or Record<'sysevent_email_template'>
554
+ let templateValue: string | undefined
555
+ if (emailContent) {
556
+ const templateArg = emailContent.get('template')
557
+ if (templateArg) {
558
+ if (templateArg.isRecord()) {
559
+ // If it's a Record, extract the ID
560
+ templateValue = templateArg.getId().getValue()
561
+ } else if (templateArg.isString()) {
562
+ const templateStr = templateArg.getValue()
563
+ // If it's a string GUID, use it
564
+ if (isGUID(templateStr)) {
565
+ templateValue = templateStr
566
+ } else {
567
+ showGuidFieldDiagnostic(
568
+ templateArg,
569
+ 'emailContent.template',
570
+ 'sysevent_email_template',
571
+ diagnostics
572
+ )
573
+ }
574
+ }
575
+ }
576
+ }
577
+
578
+ // Handle style field - can be string (GUID) or Record<'sys_email_style'>
579
+ let styleValue: string | undefined
580
+ if (emailContent) {
581
+ const styleArg = emailContent.get('style')
582
+ if (styleArg) {
583
+ if (styleArg.isRecord()) {
584
+ // If it's a Record, extract the ID
585
+ styleValue = styleArg.getId().getValue()
586
+ } else if (styleArg.isString()) {
587
+ const styleStr = styleArg.getValue()
588
+ // If it's a string GUID, use it
589
+ if (isGUID(styleStr)) {
590
+ styleValue = styleStr
591
+ } else {
592
+ showGuidFieldDiagnostic(styleArg, 'emailContent.style', 'sys_email_style', diagnostics)
593
+ }
594
+ }
595
+ }
596
+ }
597
+
598
+ // Handle defaultInterval field - can be string (GUID) or Record<'sys_email_digest_interval'>
599
+ let defaultIntervalValue: string | undefined
600
+ if (digest) {
601
+ const defaultIntervalArg = digest.get('defaultInterval')
602
+ if (defaultIntervalArg) {
603
+ if (defaultIntervalArg.isRecord()) {
604
+ // If it's a Record, extract the ID
605
+ defaultIntervalValue = defaultIntervalArg.getId().getValue()
606
+ } else if (defaultIntervalArg.isString()) {
607
+ const defaultIntervalStr = defaultIntervalArg.getValue()
608
+ // If it's a string GUID, use it
609
+ if (isGUID(defaultIntervalStr)) {
610
+ defaultIntervalValue = defaultIntervalStr
611
+ } else {
612
+ showGuidFieldDiagnostic(
613
+ defaultIntervalArg,
614
+ 'digest.defaultInterval',
615
+ 'sys_email_digest_interval',
616
+ diagnostics
617
+ )
618
+ }
619
+ }
620
+ }
621
+ }
622
+
623
+ // Handle digest template field - can be string (GUID) or Record<'sysevent_email_template'>
624
+ let digestTemplateValue: string | undefined
625
+ if (digest) {
626
+ const digestTemplateArg = digest.get('template')
627
+ if (digestTemplateArg) {
628
+ if (digestTemplateArg.isRecord()) {
629
+ // If it's a Record, extract the ID
630
+ digestTemplateValue = digestTemplateArg.getId().getValue()
631
+ } else if (digestTemplateArg.isString()) {
632
+ const digestTemplateStr = digestTemplateArg.getValue()
633
+ // If it's a string GUID, use it
634
+ if (isGUID(digestTemplateStr)) {
635
+ digestTemplateValue = digestTemplateStr
636
+ } else {
637
+ showGuidFieldDiagnostic(
638
+ digestTemplateArg,
639
+ 'digest.template',
640
+ 'sysevent_email_template',
641
+ diagnostics
642
+ )
643
+ }
644
+ }
645
+ }
646
+ }
647
+
648
+ // Business rule validations
649
+ const notificationType = arg.get('notificationType')?.getValue() ?? 'email'
650
+ const generationType = triggerConditions?.get('generationType')?.getValue() ?? 'engine'
651
+
652
+ // Validate: engine type requires at least one of onRecordInsert or onRecordUpdate
653
+ if (generationType === 'engine' && triggerConditions) {
654
+ const onRecordInsert = triggerConditions.get('onRecordInsert')?.getValue()
655
+ const onRecordUpdate = triggerConditions.get('onRecordUpdate')?.getValue()
656
+
657
+ if (!onRecordInsert && !onRecordUpdate) {
658
+ diagnostics.error(
659
+ triggerConditions,
660
+ "When triggerConditions.generationType is 'engine', at least one of 'onRecordInsert' or 'onRecordUpdate' must be true."
661
+ )
662
+ }
663
+ }
664
+
665
+ // Validate: event type requires eventName
666
+ if (generationType === 'event' && triggerConditions) {
667
+ const eventName = triggerConditions.get('eventName')?.getValue()
668
+ if (!eventName || (typeof eventName === 'string' && eventName.trim() === '')) {
669
+ diagnostics.error(
670
+ triggerConditions,
671
+ "When triggerConditions.generationType is 'event', 'eventName' is required."
672
+ )
673
+ }
674
+ }
675
+
676
+ // Validate: vcalendar does not support digest
677
+ if (notificationType === 'vcalendar' && digest) {
678
+ const allowDigest = digest.get('allow')?.getValue()
679
+ if (allowDigest === true) {
680
+ diagnostics.warn(
681
+ digest,
682
+ "Digest functionality is not supported for notificationType 'vcalendar'. The digest configuration will be ignored."
683
+ )
684
+ }
685
+ }
686
+
687
+ // Validate: eventParm fields only apply to event generation type
688
+ if (generationType !== 'event' && recipientDetails) {
689
+ const eventParm1 = recipientDetails.get('eventParm1WithRecipient')?.getValue()
690
+ const eventParm2 = recipientDetails.get('eventParm2WithRecipient')?.getValue()
691
+
692
+ if (eventParm1 === true || eventParm2 === true) {
693
+ diagnostics.warn(
694
+ recipientDetails,
695
+ `'eventParm1WithRecipient' and 'eventParm2WithRecipient' only apply when triggerConditions.generationType is 'event'. Current type is '${generationType}'.`
696
+ )
697
+ }
698
+ }
699
+
700
+ // Validate: content type and message field alignment
701
+ if (emailContent && !emailContent.get('template')?.getValue()) {
702
+ const contentType = emailContent.get('contentType')?.getValue() ?? 'text/html'
703
+ const hasMessageHtml = emailContent.get('messageHtml') !== undefined
704
+ const hasMessageText = emailContent.get('messageText') !== undefined
705
+ const messageHtmlValue = emailContent.get('messageHtml')?.getValue()
706
+ const messageTextValue = emailContent.get('messageText')?.getValue()
707
+
708
+ if (contentType === 'text/html') {
709
+ // Warn if messageHtml is missing
710
+ if (!hasMessageHtml || !messageHtmlValue) {
711
+ diagnostics.warn(
712
+ emailContent,
713
+ `Content type is 'text/html' but 'messageHtml' is not provided. Please provide 'messageHtml' for HTML content.`
714
+ )
715
+ }
716
+ // Warn if messageText is provided (it will be ignored)
717
+ if (hasMessageText && messageTextValue) {
718
+ diagnostics.warn(
719
+ emailContent,
720
+ `Content type is 'text/html': 'messageText' will be ignored. Only 'messageHtml' will be used.`
721
+ )
722
+ }
723
+ } else if (contentType === 'text/plain') {
724
+ // Warn if messageText is missing
725
+ if (!hasMessageText || !messageTextValue) {
726
+ diagnostics.warn(
727
+ emailContent,
728
+ `Content type is 'text/plain' but 'messageText' is not provided. Please provide 'messageText' for plain text content.`
729
+ )
730
+ }
731
+ // Warn if messageHtml is provided (it will be ignored)
732
+ if (hasMessageHtml && messageHtmlValue) {
733
+ diagnostics.warn(
734
+ emailContent,
735
+ `Content type is 'text/plain': 'messageHtml' will be ignored. Only 'messageText' will be used.`
736
+ )
737
+ }
738
+ } else if (contentType === 'multipart/mixed') {
739
+ // Warn if both are missing
740
+ const hasAnyContent =
741
+ (hasMessageHtml && messageHtmlValue) || (hasMessageText && messageTextValue)
742
+ if (!hasAnyContent) {
743
+ diagnostics.warn(
744
+ emailContent,
745
+ `Content type is 'multipart/mixed' but neither 'messageHtml' nor 'messageText' is provided. Please provide at least one or both.`
746
+ )
747
+ }
748
+ }
749
+ }
750
+
751
+ // Handle category field - can be string (GUID) or Record<'sys_notification_category'>
752
+ const categoryArg = arg.get('category')
753
+ let categoryValue = DEFAULT_EMAIL_CATEGORY
754
+
755
+ if (categoryArg) {
756
+ if (categoryArg.isRecord()) {
757
+ // If it's a Record, extract the ID
758
+ categoryValue = categoryArg.getId().getValue()
759
+ } else if (categoryArg.isString()) {
760
+ const categoryStr = categoryArg.getValue()
761
+ // If it's a string GUID, use it
762
+ if (isGUID(categoryStr)) {
763
+ categoryValue = categoryStr
764
+ } else {
765
+ showGuidFieldDiagnostic(categoryArg, 'category', 'sys_notification_category', diagnostics)
766
+ }
767
+ }
768
+ }
769
+
770
+ const emailAction = await factory.createRecord({
771
+ explicitId: arg.get('$id'),
772
+ source: callExpression,
773
+ table: 'sysevent_email_action',
774
+ properties: arg.transform(({ $ }) => ({
775
+ // Base properties
776
+ name: $,
777
+ description: $.def(''),
778
+ collection: $.from('table'),
779
+ category: $.val(categoryValue),
780
+ type: $.from('notificationType').def('email'),
781
+ active: $.def(true),
782
+ mandatory: $.def(false),
783
+ enable_dynamic_translation: $.from('enableDynamicTranslation').def(false),
784
+
785
+ // Trigger conditions
786
+ generation_type: $.val(triggerConditions?.get('generationType')).def('engine'),
787
+ action_insert: $.val(triggerConditions?.get('onRecordInsert')).def(false),
788
+ action_update: $.val(triggerConditions?.get('onRecordUpdate')).def(false),
789
+ event_name: $.val(triggerConditions?.get('eventName')).def(''),
790
+ condition: $.val(triggerConditions?.get('condition')).def(''),
791
+ advanced_condition: $.val(triggerConditions?.get('advancedCondition')).def(''),
792
+ item_table: $.val(triggerConditions?.get('itemTable')).def(''),
793
+ affected_field_on_event: $.val(triggerConditions?.get('affectedFieldOnEvent')).def(''),
794
+ item: $.val(triggerConditions?.get('item')).def('event.parm1'),
795
+ order: $.val(triggerConditions?.get('order')).toNumber().def(100),
796
+ weight: $.val(triggerConditions?.get('weight')).toNumber().def(0),
797
+
798
+ // Recipient details - use pre-extracted comma-separated strings
799
+ recipient_users: $.val(recipientUsers).def(''),
800
+ recipient_fields: $.val(recipientFields).def(''),
801
+ recipient_groups: $.val(recipientGroups).def(''),
802
+ exclude_delegates: $.val(recipientDetails?.get('excludeDelegates')).def(false),
803
+ subscribable: $.val(recipientDetails?.get('isSubscribableByAllUsers')).def(false),
804
+ send_self: $.val(recipientDetails?.get('sendToCreator')).def(false),
805
+ event_parm_1: $.val(recipientDetails?.get('eventParm1WithRecipient')).def(false),
806
+ event_parm_2: $.val(recipientDetails?.get('eventParm2WithRecipient')).def(false),
807
+
808
+ // Email content
809
+ content_type: $.val(emailContent?.get('contentType')).def('text/html'),
810
+ template: $.val(templateValue).def(''),
811
+ style: $.val(styleValue).def(''),
812
+ subject: $.val(emailContent?.get('subject')).def(''),
813
+ message: $.val(emailContent?.get('message')).def('').toCdata(),
814
+ message_html: $.val(emailContent?.get('messageHtml')).def('').toCdata(),
815
+ message_text: $.val(emailContent?.get('messageText')).def('').toCdata(),
816
+ sms_alternate: $.val(emailContent?.get('smsAlternate')).def('').toCdata(),
817
+ importance: $.val(emailContent?.get('importance')).def(''),
818
+ include_attachments: $.val(emailContent?.get('includeAttachments')).def(false),
819
+ omit_watermark: $.val(emailContent?.get('omitWatermark')).def(false),
820
+ from: $.val(emailContent?.get('from')).def(''),
821
+ reply_to: $.val(emailContent?.get('replyTo')).def(''),
822
+ push_message_only: $.val(emailContent?.get('pushMessageOnly')).def(false),
823
+ message_list: $.val(pushMessageList).def(''),
824
+ force_delivery: $.val(emailContent?.get('forceDelivery')).def(false),
825
+
826
+ // Digest
827
+ digestable: $.val(digest?.get('allow')).def(false),
828
+ default_digest: $.val(digest?.get('default')).def(false),
829
+ default_interval: $.val(defaultIntervalValue).def(''),
830
+ digest_type: $.val(digest?.get('type')).def('single'),
831
+ digest_template: $.val(digestTemplateValue).def(''),
832
+ digest_subject: $.val(digest?.get('subject')).def(''),
833
+ digest_html: $.val(digest?.get('html')).def('').toCdata(),
834
+ digest_text: $.val(digest?.get('text')).toCdata().def(''),
835
+ digest_separator_html: $.val(digestSeparatorHtml).toCdata(),
836
+ digest_separator_text: $.val(digestSeparatorText).toCdata(),
837
+ digest_from: $.val(digest?.get('from')).def(''),
838
+ digest_reply_to: $.val(digest?.get('replyTo')).def(''),
839
+ sys_version: $.val('2'),
840
+ })),
841
+ })
842
+
843
+ return {
844
+ success: true,
845
+ value: emailAction,
846
+ }
847
+ },
848
+ },
849
+ ],
850
+ })