@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
@@ -0,0 +1,333 @@
1
+ import { CallExpressionShape, Plugin, isSNScope, type Record as SdkRecord, Shape } from '@servicenow/sdk-build-core'
2
+ import { NowIdShape } from './now-id-plugin'
3
+
4
+ // Define table names as constants
5
+ const SYS_DATA_POLICY = 'sys_data_policy2'
6
+ const SYS_DATA_POLICY_RULE = 'sys_data_policy_rule'
7
+
8
+ /**
9
+ * Type for data policy rule configuration (internal plugin representation)
10
+ */
11
+ type DataPolicyRuleInternal = {
12
+ $id?: NowIdShape
13
+ mandatory?: boolean | 'ignore'
14
+ readOnly?: boolean | 'ignore'
15
+ table?: string
16
+ }
17
+
18
+ /**
19
+ * Most-restrictive-wins merge for a single boolean|'ignore' property.
20
+ * Priority: true > false > 'ignore' / undefined
21
+ */
22
+ const mergeRestrictive = (
23
+ existing: boolean | 'ignore' | undefined,
24
+ incoming: boolean | 'ignore' | undefined
25
+ ): boolean | 'ignore' | undefined => {
26
+ if (incoming === true || existing === true) {
27
+ return true
28
+ }
29
+ if (incoming === false || existing === false) {
30
+ return false
31
+ }
32
+ return incoming ?? existing
33
+ }
34
+
35
+ /**
36
+ * Converts ServiceNow string format ('true'/'false'/'leave alone') to boolean | 'ignore'
37
+ * Used when reading from database (toShape)
38
+ */
39
+ const stringToBoolean = (value?: string): boolean | 'ignore' | undefined => {
40
+ switch (value) {
41
+ case 'true':
42
+ return true
43
+ case 'false':
44
+ return false
45
+ case 'leave alone':
46
+ return 'ignore'
47
+ default:
48
+ return undefined
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Converts boolean | 'ignore' to ServiceNow string format ('true'/'false'/'leave alone')
54
+ * Used when writing to database (toRecord)
55
+ * Default: 'leave alone' - matches ServiceNow platform default for null/undefined values
56
+ */
57
+ const booleanToString = (value: Shape): string => {
58
+ if (value.isBoolean()) {
59
+ return value.getValue() ? 'true' : 'false'
60
+ }
61
+ // Default to 'leave alone' when value is 'ignore' or undefined
62
+ return 'leave alone'
63
+ }
64
+
65
+ /**
66
+ * Checks if a value is a valid rule property (boolean or 'ignore')
67
+ * Used for validation in toRecord
68
+ */
69
+ const isValidRuleValue = (value: Shape): boolean => {
70
+ return value.isBoolean() || (value.isString() && value.getValue() === 'ignore')
71
+ }
72
+
73
+ export const DataPolicyPlugin = Plugin.create({
74
+ name: 'DataPolicyPlugin',
75
+ records: {
76
+ [SYS_DATA_POLICY]: {
77
+ relationships: {
78
+ [SYS_DATA_POLICY_RULE]: {
79
+ via: 'sys_data_policy',
80
+ descendant: true,
81
+ },
82
+ },
83
+ toShape(record, { descendants, logger }) {
84
+ const rulesRecord: Record<string, DataPolicyRuleInternal> = {}
85
+ const parentTable = record.get('model_table')?.ifString()?.getValue()
86
+
87
+ descendants.query(SYS_DATA_POLICY_RULE).forEach((rule) => {
88
+ const fieldNameValue = rule.get('field')?.ifString()?.getValue()
89
+ if (!fieldNameValue) {
90
+ return // Skip rules without a field name
91
+ }
92
+
93
+ // Convert ServiceNow string format to boolean | 'ignore'
94
+ const mandatoryValue = rule.get('mandatory')?.ifString()?.getValue()
95
+ const readOnlyValue = rule.get('disabled')?.ifString()?.getValue()
96
+
97
+ const mandatory = stringToBoolean(mandatoryValue)
98
+ const readOnly = stringToBoolean(readOnlyValue)
99
+
100
+ if (rulesRecord[fieldNameValue]) {
101
+ // Multiple rule records for the same field — most restrictive wins.
102
+ // Priority: true > false > 'ignore' / undefined
103
+ // WARNING: Only the first rule's $id is kept in Fluent. The duplicate rule
104
+ // record (this one) will NOT be deleted from the instance on next deploy.
105
+ // Clean up orphan rule records manually on the instance.
106
+ logger.warn(
107
+ `Data Policy '${record.getId().getValue()}': field '${fieldNameValue}' has multiple rule records on the instance. ` +
108
+ `Merging values using most-restrictive-wins. The duplicate rule '${rule.getId().getValue()}' will NOT be deleted from the instance — remove it manually.`
109
+ )
110
+ const existing = rulesRecord[fieldNameValue]
111
+ const mergedMandatory = mergeRestrictive(existing.mandatory, mandatory)
112
+ const mergedReadOnly = mergeRestrictive(existing.readOnly, readOnly)
113
+
114
+ // Handle mutual exclusivity with priority rules:
115
+ // 1. mandatory=true beats readOnly=true → set mandatory=true, readOnly=false
116
+ // 2. readOnly=true beats mandatory=false → set readOnly=true, remove mandatory
117
+ // 3. mandatory=false beats readOnly=false → set mandatory=false, remove readOnly
118
+ let finalMandatory = mergedMandatory
119
+ let finalReadOnly = mergedReadOnly
120
+
121
+ if (mergedMandatory === true && mergedReadOnly === true) {
122
+ // Case 1: mandatory=true wins over readOnly=true
123
+ finalReadOnly = false
124
+ } else if (mergedReadOnly === true && mergedMandatory === false) {
125
+ // Case 2: readOnly=true wins over mandatory=false
126
+ finalMandatory = 'ignore'
127
+ } else if (mergedMandatory === false && mergedReadOnly === false) {
128
+ // Case 3: mandatory=false wins over readOnly=false
129
+ finalReadOnly = 'ignore'
130
+ }
131
+
132
+ if (finalMandatory !== undefined && finalMandatory !== 'ignore') {
133
+ existing.mandatory = finalMandatory
134
+ } else {
135
+ delete existing.mandatory
136
+ }
137
+
138
+ if (finalReadOnly !== undefined && finalReadOnly !== 'ignore') {
139
+ existing.readOnly = finalReadOnly
140
+ } else {
141
+ delete existing.readOnly
142
+ }
143
+ return
144
+ }
145
+
146
+ const ruleConfig: DataPolicyRuleInternal = {
147
+ $id: NowIdShape.from(rule),
148
+ }
149
+
150
+ // Only include properties with actual boolean values (not 'ignore')
151
+ // Omitting a property is equivalent to 'ignore' - cleaner generated code
152
+ if (mandatory !== undefined && mandatory !== 'ignore') {
153
+ ruleConfig.mandatory = mandatory
154
+ }
155
+ if (readOnly !== undefined && readOnly !== 'ignore') {
156
+ ruleConfig.readOnly = readOnly
157
+ }
158
+
159
+ // Add table field if it's different from parent policy's table
160
+ const table = rule.get('table')?.ifString()?.getValue()
161
+ if (table && table !== parentTable) {
162
+ ruleConfig.table = table
163
+ }
164
+
165
+ rulesRecord[fieldNameValue] = ruleConfig
166
+ })
167
+
168
+ return {
169
+ success: true,
170
+ value: new CallExpressionShape({
171
+ source: record,
172
+ callee: 'DataPolicy',
173
+ args: [
174
+ record.transform(({ $ }) => ({
175
+ $id: $.val(NowIdShape.from(record)),
176
+ table: $.from('model_table').def(''),
177
+ shortDescription: $.from('short_description').def(''),
178
+ active: $.toBoolean().def(true),
179
+ applyToImportSets: $.from('apply_import_set').toBoolean().def(true),
180
+ applyToSOAP: $.from('apply_soap').toBoolean().def(true),
181
+ conditions: $.def(''),
182
+ description: $.def(''),
183
+ useAsUiPolicyOnClient: $.from('enforce_ui').toBoolean().def(true),
184
+ inherit: $.toBoolean().def(false),
185
+ modelId: $.from('model_id').def(''),
186
+ reverseIfFalse: $.from('reverse_if_false').toBoolean().def(true),
187
+ rules: $.val(Object.keys(rulesRecord).length > 0 ? rulesRecord : undefined),
188
+ })),
189
+ ],
190
+ }),
191
+ }
192
+ },
193
+ },
194
+ [SYS_DATA_POLICY_RULE]: {},
195
+ },
196
+ shapes: [
197
+ {
198
+ shape: CallExpressionShape,
199
+ fileTypes: ['fluent'],
200
+ async toRecord(callExpression, { config, factory, diagnostics }) {
201
+ if (callExpression.getCallee() !== 'DataPolicy') {
202
+ return { success: false }
203
+ }
204
+
205
+ const arg = callExpression.getArgument(0).asObject()
206
+
207
+ // Validate table scope prefix for scoped apps
208
+ const tableValue = arg.get('table')
209
+ if (tableValue?.isString() && config?.scope) {
210
+ const tableName = tableValue.getValue()
211
+ const scopeName = config.scope
212
+ //Note: If scope is sn_ or global, this validation will not work as expected
213
+ if (!tableName.startsWith(`${scopeName}_`) && !isSNScope(scopeName) && scopeName !== 'global') {
214
+ diagnostics.error(
215
+ arg.get('table'),
216
+ `'table' property should start with scope prefix '${scopeName}_'. Data Policies in scoped apps can only be created for tables within the same scope.`
217
+ )
218
+ }
219
+ }
220
+
221
+ // Create the Data Policy record
222
+ const policyRecord = await factory.createRecord({
223
+ source: callExpression,
224
+ table: SYS_DATA_POLICY,
225
+ explicitId: arg.get('$id'),
226
+ properties: arg.transform(({ $ }) => ({
227
+ model_table: $.from('table').def(''),
228
+ short_description: $.from('shortDescription').def(''),
229
+ active: $.from('active').def(true),
230
+ apply_import_set: $.from('applyToImportSets').def(true),
231
+ apply_soap: $.from('applyToSOAP').def(true),
232
+ conditions: $.from('conditions').def(''),
233
+ description: $.from('description').def(''),
234
+ enforce_ui: $.from('useAsUiPolicyOnClient').def(true),
235
+ inherit: $.from('inherit').def(false),
236
+ model_id: $.from('modelId').def(''),
237
+ reverse_if_false: $.from('reverseIfFalse').def(true),
238
+ })),
239
+ })
240
+
241
+ // Process rules if they exist (Record structure)
242
+ const rules = arg.get('rules')
243
+ const ruleRecords: SdkRecord[] = []
244
+
245
+ if (!rules?.isObject()) {
246
+ return { success: true, value: policyRecord }
247
+ }
248
+
249
+ const ruleKeys = rules.keys()
250
+
251
+ // Process each field name as a key in the rules record
252
+ for (const fieldName of ruleKeys) {
253
+ const ruleConfig = rules.get(fieldName)
254
+ if (!ruleConfig?.isObject()) {
255
+ continue
256
+ }
257
+
258
+ // Get rule property values
259
+ let mandatoryValue = ruleConfig.get('mandatory')
260
+ let readOnlyValue = ruleConfig.get('readOnly')
261
+
262
+ const hasMandatoryProp = isValidRuleValue(mandatoryValue)
263
+ const hasReadOnlyProp = isValidRuleValue(readOnlyValue)
264
+
265
+ // If no valid properties, default both to 'ignore' instead of throwing error
266
+ // This creates a rule record with both fields set to 'leave alone' in the database
267
+ if (!hasMandatoryProp && !hasReadOnlyProp) {
268
+ mandatoryValue = Shape.from(ruleConfig, 'ignore')
269
+ readOnlyValue = Shape.from(ruleConfig, 'ignore')
270
+ }
271
+
272
+ // Validate mutual exclusivity: mandatory and readOnly cannot both be true
273
+ if (
274
+ mandatoryValue?.isBoolean() &&
275
+ mandatoryValue.getValue() === true &&
276
+ readOnlyValue?.isBoolean() &&
277
+ readOnlyValue.getValue() === true
278
+ ) {
279
+ diagnostics.error(
280
+ ruleConfig,
281
+ `Rule for field '${fieldName}': mandatory and readOnly cannot both be true - these are mutually exclusive`
282
+ )
283
+ continue
284
+ }
285
+
286
+ // Validate mandatory field is not set for tables outside the current scope
287
+ const ruleTableValue = ruleConfig.get('table')
288
+ if (
289
+ mandatoryValue?.isBoolean() &&
290
+ mandatoryValue.getValue() === true &&
291
+ ruleTableValue?.isString() &&
292
+ config?.scope
293
+ ) {
294
+ const ruleTableName = ruleTableValue.getValue()
295
+ const scopeName = config.scope
296
+ const policyTableName = arg.get('table')?.ifString()?.getValue() ?? ''
297
+
298
+ // Check if rule table is different from policy table and outside scope
299
+ if (
300
+ ruleTableName !== policyTableName &&
301
+ !ruleTableName.startsWith(`${scopeName}_`) &&
302
+ !isSNScope(scopeName) &&
303
+ scopeName !== 'global'
304
+ ) {
305
+ diagnostics.error(
306
+ ruleConfig.get('table') || ruleConfig,
307
+ `Rule for field '${fieldName}': cannot set mandatory=true for table '${ruleTableName}' which is outside the current scope '${scopeName}'. For tables in a different scope than the data policy record, you cannot make a field mandatory.`
308
+ )
309
+ continue
310
+ }
311
+ }
312
+
313
+ const ruleRecord = await factory.createRecord({
314
+ source: ruleConfig,
315
+ table: SYS_DATA_POLICY_RULE,
316
+ explicitId: ruleConfig.get('$id'),
317
+ properties: ruleConfig.transform(({ $ }) => ({
318
+ sys_data_policy: $.val(policyRecord.getId()),
319
+ table: $.from('table').def(arg.get('table')?.ifString()?.getValue() ?? ''),
320
+ field: $.val(fieldName),
321
+ mandatory: $.val(booleanToString(mandatoryValue)),
322
+ disabled: $.val(booleanToString(readOnlyValue)),
323
+ })),
324
+ })
325
+
326
+ ruleRecords.push(ruleRecord)
327
+ }
328
+
329
+ return { success: true, value: policyRecord.with(...ruleRecords) }
330
+ },
331
+ },
332
+ ],
333
+ })
@@ -1,6 +1,6 @@
1
1
  import { CallExpressionShape, Plugin, isGUID, Shape, type ObjectShape } from '@servicenow/sdk-build-core'
2
2
  import { NowIdShape } from './now-id-plugin'
3
- import { showGuidFieldDiagnostic, createSdkDocEntry } from './utils'
3
+ import { showGuidFieldDiagnostic } from './utils'
4
4
  import type { EmailNotification } from '@servicenow/sdk-core/runtime/notification'
5
5
 
6
6
  // Use index access types instead of exported utility types
@@ -55,7 +55,6 @@ const isValidEmail = (email: string): boolean => {
55
55
 
56
56
  export const EmailNotificationPlugin = Plugin.create({
57
57
  name: 'EmailNotificationPlugin',
58
- docs: [createSdkDocEntry('EmailNotification', ['sysevent_email_action'])],
59
58
  records: {
60
59
  sysevent_email_action: {
61
60
  toShape(record) {
@@ -555,7 +554,7 @@ export const EmailNotificationPlugin = Plugin.create({
555
554
  }
556
555
  }
557
556
 
558
- // Handle style field - can be string (GUID) or Record<'sys_email_style'>
557
+ // Handle style field - can be string (GUID) or Record<'sysevent_email_style'>
559
558
  let styleValue: string | undefined
560
559
  if (emailContent) {
561
560
  const styleArg = emailContent.get('style')
@@ -569,7 +568,12 @@ export const EmailNotificationPlugin = Plugin.create({
569
568
  if (isGUID(styleStr)) {
570
569
  styleValue = styleStr
571
570
  } else {
572
- showGuidFieldDiagnostic(styleArg, 'emailContent.style', 'sys_email_style', diagnostics)
571
+ showGuidFieldDiagnostic(
572
+ styleArg,
573
+ 'emailContent.style',
574
+ 'sysevent_email_style',
575
+ diagnostics
576
+ )
573
577
  }
574
578
  }
575
579
  }
@@ -13,6 +13,7 @@ export enum FLOW_LOGIC {
13
13
  TRY_CATCH = 'wfa.flowLogic.tryCatch',
14
14
  DO_IN_PARALLEL = 'wfa.flowLogic.doInParallel',
15
15
  SET_FLOW_VARIABLES = 'wfa.flowLogic.setFlowVariables',
16
+ APPEND_TO_FLOW_VARIABLES = 'wfa.flowLogic.appendToFlowVariables',
16
17
  ASSIGN_SUBFLOW_OUTPUTS = 'wfa.flowLogic.assignSubflowOutputs',
17
18
  }
18
19
 
@@ -101,7 +102,10 @@ export const FLOW_LOGIC_SYS_ID_MAP: { [K in FLOW_LOGIC]?: string } = {
101
102
  [FLOW_LOGIC.ENDFLOW]: 'd176605ea76103004f27b0d2187901c7',
102
103
  [FLOW_LOGIC.SKIP_ITERATION]: '48de1b5837063110ac85cdd734924b15',
103
104
  [FLOW_LOGIC.FOR_EACH]: '098e1dc5c3e232002841b63b12d3ae33',
105
+ [FLOW_LOGIC.TRY_CATCH]: '07223332de022010ec58424b73829721',
106
+ [FLOW_LOGIC.DO_IN_PARALLEL]: '5d31e5dedfe50300b61ecae83df26326',
104
107
  [FLOW_LOGIC.SET_FLOW_VARIABLES]: '4f787d1e0f9b0010ecf0cc52ff767ea0',
108
+ [FLOW_LOGIC.APPEND_TO_FLOW_VARIABLES]: '4784e82683031210d266f953d22bc0fd',
105
109
  [FLOW_LOGIC.ASSIGN_SUBFLOW_OUTPUTS]: 'df4e1945c3e232002841b63b12d3ae3e',
106
110
  }
107
111
 
@@ -118,3 +122,5 @@ export namespace FlowLogicSysId {
118
122
  }
119
123
 
120
124
  export const DO_IN_PARALLEL_BLOCK_SYS_ID = 'a1168de173311300b61e66476bf6a7d0'
125
+
126
+ export const TRY_CATCH_CATCH_SYS_ID = '35d60003e6022010a5e40cdd1254dd21'