@servicenow/sdk-build-plugins 4.4.0 → 4.5.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 (287) hide show
  1. package/dist/acl-plugin.js +54 -4
  2. package/dist/acl-plugin.js.map +1 -1
  3. package/dist/applicability-plugin.js +2 -0
  4. package/dist/applicability-plugin.js.map +1 -1
  5. package/dist/application-menu-plugin.js +2 -0
  6. package/dist/application-menu-plugin.js.map +1 -1
  7. package/dist/arrow-function-plugin.d.ts +6 -1
  8. package/dist/arrow-function-plugin.js +105 -12
  9. package/dist/arrow-function-plugin.js.map +1 -1
  10. package/dist/atf/test-plugin.js +2 -0
  11. package/dist/atf/test-plugin.js.map +1 -1
  12. package/dist/basic-syntax-plugin.js +20 -0
  13. package/dist/basic-syntax-plugin.js.map +1 -1
  14. package/dist/call-expression-plugin.js +1 -0
  15. package/dist/call-expression-plugin.js.map +1 -1
  16. package/dist/claims-plugin.js +1 -0
  17. package/dist/claims-plugin.js.map +1 -1
  18. package/dist/client-script-plugin.js +1 -0
  19. package/dist/client-script-plugin.js.map +1 -1
  20. package/dist/column-plugin.js +1 -0
  21. package/dist/column-plugin.js.map +1 -1
  22. package/dist/cross-scope-privilege-plugin.js +1 -0
  23. package/dist/cross-scope-privilege-plugin.js.map +1 -1
  24. package/dist/dashboard/dashboard-plugin.js +2 -0
  25. package/dist/dashboard/dashboard-plugin.js.map +1 -1
  26. package/dist/data-plugin.js +1 -0
  27. package/dist/data-plugin.js.map +1 -1
  28. package/dist/email-notification-plugin.js +9 -13
  29. package/dist/email-notification-plugin.js.map +1 -1
  30. package/dist/flow/constants/flow-plugin-constants.d.ts +1 -1
  31. package/dist/flow/constants/flow-plugin-constants.js +1 -1
  32. package/dist/flow/constants/flow-plugin-constants.js.map +1 -1
  33. package/dist/flow/flow-logic/flow-logic-plugin-helpers.d.ts +82 -2
  34. package/dist/flow/flow-logic/flow-logic-plugin-helpers.js +48 -40
  35. package/dist/flow/flow-logic/flow-logic-plugin-helpers.js.map +1 -1
  36. package/dist/flow/flow-logic/flow-logic-plugin.js +1 -0
  37. package/dist/flow/flow-logic/flow-logic-plugin.js.map +1 -1
  38. package/dist/flow/plugins/approval-rules-plugin.js +1 -0
  39. package/dist/flow/plugins/approval-rules-plugin.js.map +1 -1
  40. package/dist/flow/plugins/flow-action-definition-plugin.js +4 -2
  41. package/dist/flow/plugins/flow-action-definition-plugin.js.map +1 -1
  42. package/dist/flow/plugins/flow-data-pill-plugin.js +1 -0
  43. package/dist/flow/plugins/flow-data-pill-plugin.js.map +1 -1
  44. package/dist/flow/plugins/flow-definition-plugin.js +8 -3
  45. package/dist/flow/plugins/flow-definition-plugin.js.map +1 -1
  46. package/dist/flow/plugins/flow-diagnostics-plugin.js +1 -0
  47. package/dist/flow/plugins/flow-diagnostics-plugin.js.map +1 -1
  48. package/dist/flow/plugins/flow-instance-plugin.js +68 -12
  49. package/dist/flow/plugins/flow-instance-plugin.js.map +1 -1
  50. package/dist/flow/plugins/flow-trigger-instance-plugin.js +1 -0
  51. package/dist/flow/plugins/flow-trigger-instance-plugin.js.map +1 -1
  52. package/dist/flow/plugins/inline-script-plugin.js +1 -0
  53. package/dist/flow/plugins/inline-script-plugin.js.map +1 -1
  54. package/dist/flow/plugins/step-definition-plugin.js +3 -2
  55. package/dist/flow/plugins/step-definition-plugin.js.map +1 -1
  56. package/dist/flow/plugins/step-instance-plugin.js +1 -0
  57. package/dist/flow/plugins/step-instance-plugin.js.map +1 -1
  58. package/dist/flow/plugins/trigger-plugin.js +2 -0
  59. package/dist/flow/plugins/trigger-plugin.js.map +1 -1
  60. package/dist/flow/plugins/wfa-datapill-plugin.js +1 -0
  61. package/dist/flow/plugins/wfa-datapill-plugin.js.map +1 -1
  62. package/dist/flow/post-install.d.ts +2 -0
  63. package/dist/flow/post-install.js +58 -0
  64. package/dist/flow/post-install.js.map +1 -0
  65. package/dist/flow/utils/complex-objects.js +4 -2
  66. package/dist/flow/utils/complex-objects.js.map +1 -1
  67. package/dist/flow/utils/flow-constants.d.ts +24 -0
  68. package/dist/flow/utils/flow-constants.js +29 -2
  69. package/dist/flow/utils/flow-constants.js.map +1 -1
  70. package/dist/flow/utils/flow-to-xml.d.ts +3 -2
  71. package/dist/flow/utils/flow-to-xml.js +3 -4
  72. package/dist/flow/utils/flow-to-xml.js.map +1 -1
  73. package/dist/flow/utils/label-cache-processor.d.ts +5 -0
  74. package/dist/flow/utils/label-cache-processor.js +14 -2
  75. package/dist/flow/utils/label-cache-processor.js.map +1 -1
  76. package/dist/flow/utils/service-catalog.js +5 -1
  77. package/dist/flow/utils/service-catalog.js.map +1 -1
  78. package/dist/form-plugin.d.ts +2 -0
  79. package/dist/form-plugin.js +1134 -0
  80. package/dist/form-plugin.js.map +1 -0
  81. package/dist/html-import-plugin.js +1 -0
  82. package/dist/html-import-plugin.js.map +1 -1
  83. package/dist/import-sets-plugin.js +2 -0
  84. package/dist/import-sets-plugin.js.map +1 -1
  85. package/dist/index.d.ts +9 -0
  86. package/dist/index.js +13 -1
  87. package/dist/index.js.map +1 -1
  88. package/dist/instance-scan-plugin.d.ts +2 -0
  89. package/dist/instance-scan-plugin.js +298 -0
  90. package/dist/instance-scan-plugin.js.map +1 -0
  91. package/dist/json-plugin.js +1 -0
  92. package/dist/json-plugin.js.map +1 -1
  93. package/dist/list-plugin.js +1 -0
  94. package/dist/list-plugin.js.map +1 -1
  95. package/dist/now-attach-plugin.js +1 -0
  96. package/dist/now-attach-plugin.js.map +1 -1
  97. package/dist/now-config-plugin.js +659 -51
  98. package/dist/now-config-plugin.js.map +1 -1
  99. package/dist/now-id-plugin.js +1 -0
  100. package/dist/now-id-plugin.js.map +1 -1
  101. package/dist/now-include-plugin.js +1 -0
  102. package/dist/now-include-plugin.js.map +1 -1
  103. package/dist/now-ref-plugin.js +1 -0
  104. package/dist/now-ref-plugin.js.map +1 -1
  105. package/dist/now-unresolved-plugin.js +1 -0
  106. package/dist/now-unresolved-plugin.js.map +1 -1
  107. package/dist/package-json-plugin.js +1 -0
  108. package/dist/package-json-plugin.js.map +1 -1
  109. package/dist/property-plugin.js +3 -1
  110. package/dist/property-plugin.js.map +1 -1
  111. package/dist/record-plugin.d.ts +30 -0
  112. package/dist/record-plugin.js +37 -1
  113. package/dist/record-plugin.js.map +1 -1
  114. package/dist/repack/lint/Rules.d.ts +11 -2
  115. package/dist/repack/lint/Rules.js +160 -16
  116. package/dist/repack/lint/Rules.js.map +1 -1
  117. package/dist/repack/lint/index.d.ts +10 -5
  118. package/dist/repack/lint/index.js +76 -50
  119. package/dist/repack/lint/index.js.map +1 -1
  120. package/dist/rest-api-plugin.js +14 -0
  121. package/dist/rest-api-plugin.js.map +1 -1
  122. package/dist/role-plugin.js +1 -0
  123. package/dist/role-plugin.js.map +1 -1
  124. package/dist/schedule-script/index.d.ts +1 -0
  125. package/dist/schedule-script/index.js +18 -0
  126. package/dist/schedule-script/index.js.map +1 -0
  127. package/dist/schedule-script/scheduled-script-plugin.d.ts +2 -0
  128. package/dist/schedule-script/scheduled-script-plugin.js +551 -0
  129. package/dist/schedule-script/scheduled-script-plugin.js.map +1 -0
  130. package/dist/schedule-script/timeZoneConverter.d.ts +61 -0
  131. package/dist/schedule-script/timeZoneConverter.js +170 -0
  132. package/dist/schedule-script/timeZoneConverter.js.map +1 -0
  133. package/dist/script-action-plugin.js +2 -0
  134. package/dist/script-action-plugin.js.map +1 -1
  135. package/dist/script-include-plugin.js +2 -0
  136. package/dist/script-include-plugin.js.map +1 -1
  137. package/dist/server-module-plugin/index.js +13 -2
  138. package/dist/server-module-plugin/index.js.map +1 -1
  139. package/dist/service-catalog/catalog-clientscript-plugin.js +2 -0
  140. package/dist/service-catalog/catalog-clientscript-plugin.js.map +1 -1
  141. package/dist/service-catalog/catalog-item-plugin.js +2 -0
  142. package/dist/service-catalog/catalog-item-plugin.js.map +1 -1
  143. package/dist/service-catalog/catalog-ui-policy-plugin.js +2 -0
  144. package/dist/service-catalog/catalog-ui-policy-plugin.js.map +1 -1
  145. package/dist/service-catalog/sc-record-producer-plugin.js +2 -0
  146. package/dist/service-catalog/sc-record-producer-plugin.js.map +1 -1
  147. package/dist/service-catalog/service-catalog-diagnostics.d.ts +6 -0
  148. package/dist/service-catalog/service-catalog-diagnostics.js +20 -0
  149. package/dist/service-catalog/service-catalog-diagnostics.js.map +1 -1
  150. package/dist/service-catalog/shape-to-record.js +7 -2
  151. package/dist/service-catalog/shape-to-record.js.map +1 -1
  152. package/dist/service-catalog/variable-set-plugin.js +2 -0
  153. package/dist/service-catalog/variable-set-plugin.js.map +1 -1
  154. package/dist/service-portal/angular-provider-plugin.js +2 -0
  155. package/dist/service-portal/angular-provider-plugin.js.map +1 -1
  156. package/dist/service-portal/dependency-plugin.js +5 -31
  157. package/dist/service-portal/dependency-plugin.js.map +1 -1
  158. package/dist/service-portal/menu-plugin.d.ts +2 -0
  159. package/dist/service-portal/menu-plugin.js +353 -0
  160. package/dist/service-portal/menu-plugin.js.map +1 -0
  161. package/dist/service-portal/page-plugin.d.ts +2 -0
  162. package/dist/service-portal/page-plugin.js +702 -0
  163. package/dist/service-portal/page-plugin.js.map +1 -0
  164. package/dist/service-portal/portal-plugin.d.ts +2 -0
  165. package/dist/service-portal/portal-plugin.js +296 -0
  166. package/dist/service-portal/portal-plugin.js.map +1 -0
  167. package/dist/service-portal/theme-plugin.d.ts +2 -0
  168. package/dist/service-portal/theme-plugin.js +112 -0
  169. package/dist/service-portal/theme-plugin.js.map +1 -0
  170. package/dist/service-portal/utils.d.ts +8 -0
  171. package/dist/service-portal/utils.js +50 -0
  172. package/dist/service-portal/utils.js.map +1 -0
  173. package/dist/service-portal/widget-plugin.js +45 -8
  174. package/dist/service-portal/widget-plugin.js.map +1 -1
  175. package/dist/sla-plugin.js +2 -0
  176. package/dist/sla-plugin.js.map +1 -1
  177. package/dist/static-content-plugin.js +1 -0
  178. package/dist/static-content-plugin.js.map +1 -1
  179. package/dist/table-plugin.js +1 -0
  180. package/dist/table-plugin.js.map +1 -1
  181. package/dist/ui-action-plugin.js +2 -0
  182. package/dist/ui-action-plugin.js.map +1 -1
  183. package/dist/ui-page-plugin.js +33 -8
  184. package/dist/ui-page-plugin.js.map +1 -1
  185. package/dist/ui-policy-plugin.js +1 -0
  186. package/dist/ui-policy-plugin.js.map +1 -1
  187. package/dist/user-preference-plugin.js +2 -0
  188. package/dist/user-preference-plugin.js.map +1 -1
  189. package/dist/utils.d.ts +20 -2
  190. package/dist/utils.js +34 -3
  191. package/dist/utils.js.map +1 -1
  192. package/dist/ux-list-menu-config-plugin.js +2 -0
  193. package/dist/ux-list-menu-config-plugin.js.map +1 -1
  194. package/dist/view-plugin.js +1 -0
  195. package/dist/view-plugin.js.map +1 -1
  196. package/dist/workspace-plugin.js +2 -0
  197. package/dist/workspace-plugin.js.map +1 -1
  198. package/package.json +10 -11
  199. package/src/_types/eslint-community-eslint-utils.d.ts +15 -0
  200. package/src/acl-plugin.ts +97 -8
  201. package/src/applicability-plugin.ts +2 -0
  202. package/src/application-menu-plugin.ts +2 -0
  203. package/src/arrow-function-plugin.ts +128 -13
  204. package/src/atf/test-plugin.ts +2 -0
  205. package/src/basic-syntax-plugin.ts +21 -0
  206. package/src/call-expression-plugin.ts +1 -0
  207. package/src/claims-plugin.ts +1 -0
  208. package/src/client-script-plugin.ts +2 -1
  209. package/src/column-plugin.ts +1 -0
  210. package/src/cross-scope-privilege-plugin.ts +2 -1
  211. package/src/dashboard/dashboard-plugin.ts +2 -0
  212. package/src/data-plugin.ts +1 -0
  213. package/src/email-notification-plugin.ts +3 -23
  214. package/src/flow/constants/flow-plugin-constants.ts +1 -1
  215. package/src/flow/flow-logic/flow-logic-plugin-helpers.ts +47 -45
  216. package/src/flow/flow-logic/flow-logic-plugin.ts +1 -0
  217. package/src/flow/plugins/approval-rules-plugin.ts +1 -0
  218. package/src/flow/plugins/flow-action-definition-plugin.ts +4 -2
  219. package/src/flow/plugins/flow-data-pill-plugin.ts +1 -0
  220. package/src/flow/plugins/flow-definition-plugin.ts +10 -4
  221. package/src/flow/plugins/flow-diagnostics-plugin.ts +1 -0
  222. package/src/flow/plugins/flow-instance-plugin.ts +103 -14
  223. package/src/flow/plugins/flow-trigger-instance-plugin.ts +1 -0
  224. package/src/flow/plugins/inline-script-plugin.ts +1 -0
  225. package/src/flow/plugins/step-definition-plugin.ts +3 -2
  226. package/src/flow/plugins/step-instance-plugin.ts +1 -0
  227. package/src/flow/plugins/trigger-plugin.ts +2 -0
  228. package/src/flow/plugins/wfa-datapill-plugin.ts +1 -0
  229. package/src/flow/post-install.ts +92 -0
  230. package/src/flow/utils/complex-objects.ts +10 -2
  231. package/src/flow/utils/flow-constants.ts +30 -1
  232. package/src/flow/utils/flow-to-xml.ts +4 -4
  233. package/src/flow/utils/label-cache-processor.ts +14 -2
  234. package/src/flow/utils/service-catalog.ts +5 -2
  235. package/src/form-plugin.ts +1411 -0
  236. package/src/html-import-plugin.ts +1 -0
  237. package/src/import-sets-plugin.ts +2 -0
  238. package/src/index.ts +9 -0
  239. package/src/instance-scan-plugin.ts +318 -0
  240. package/src/json-plugin.ts +1 -0
  241. package/src/list-plugin.ts +2 -1
  242. package/src/now-attach-plugin.ts +1 -0
  243. package/src/now-config-plugin.ts +833 -53
  244. package/src/now-id-plugin.ts +1 -0
  245. package/src/now-include-plugin.ts +1 -0
  246. package/src/now-ref-plugin.ts +1 -0
  247. package/src/now-unresolved-plugin.ts +1 -0
  248. package/src/package-json-plugin.ts +1 -0
  249. package/src/property-plugin.ts +3 -1
  250. package/src/record-plugin.ts +42 -2
  251. package/src/repack/lint/Rules.ts +171 -22
  252. package/src/repack/lint/index.ts +80 -56
  253. package/src/rest-api-plugin.ts +21 -1
  254. package/src/role-plugin.ts +2 -1
  255. package/src/schedule-script/index.ts +1 -0
  256. package/src/schedule-script/scheduled-script-plugin.ts +679 -0
  257. package/src/schedule-script/timeZoneConverter.ts +188 -0
  258. package/src/script-action-plugin.ts +2 -0
  259. package/src/script-include-plugin.ts +2 -0
  260. package/src/server-module-plugin/index.ts +14 -2
  261. package/src/service-catalog/catalog-clientscript-plugin.ts +2 -0
  262. package/src/service-catalog/catalog-item-plugin.ts +2 -0
  263. package/src/service-catalog/catalog-ui-policy-plugin.ts +2 -0
  264. package/src/service-catalog/sc-record-producer-plugin.ts +2 -0
  265. package/src/service-catalog/service-catalog-diagnostics.ts +30 -0
  266. package/src/service-catalog/shape-to-record.ts +8 -2
  267. package/src/service-catalog/variable-set-plugin.ts +2 -0
  268. package/src/service-portal/angular-provider-plugin.ts +2 -0
  269. package/src/service-portal/dependency-plugin.ts +6 -53
  270. package/src/service-portal/menu-plugin.ts +435 -0
  271. package/src/service-portal/page-plugin.ts +830 -0
  272. package/src/service-portal/portal-plugin.ts +319 -0
  273. package/src/service-portal/theme-plugin.ts +135 -0
  274. package/src/service-portal/utils.ts +69 -0
  275. package/src/service-portal/widget-plugin.ts +79 -9
  276. package/src/sla-plugin.ts +2 -0
  277. package/src/static-content-plugin.ts +1 -0
  278. package/src/table-plugin.ts +2 -1
  279. package/src/ui-action-plugin.ts +2 -0
  280. package/src/ui-page-plugin.ts +34 -8
  281. package/src/ui-policy-plugin.ts +2 -1
  282. package/src/user-preference-plugin.ts +2 -0
  283. package/src/utils.ts +42 -2
  284. package/src/ux-list-menu-config-plugin.ts +2 -0
  285. package/src/view-plugin.ts +1 -0
  286. package/src/workspace-plugin.ts +2 -0
  287. package/src/_types/eslint-plugin-es-x.d.ts +0 -17
@@ -6,8 +6,345 @@ const json_plugin_1 = require("./json-plugin");
6
6
  class NowConfigShape extends sdk_build_core_1.ObjectShape {
7
7
  }
8
8
  exports.NowConfigShape = NowConfigShape;
9
+ // ============================================================================
10
+ // Application Runtime Policy Helper Functions
11
+ // ============================================================================
12
+ /**
13
+ * Maps Fluent mode values to ServiceNow XML state values for performance policy
14
+ */
15
+ const PERFORMANCE_MODE_MAP = {
16
+ disabled: 'disabled',
17
+ enforced: 'enforced',
18
+ logOnly: 'log_only',
19
+ };
20
+ /**
21
+ * Reverse mapping: ServiceNow XML state values to Fluent mode values
22
+ */
23
+ const PERFORMANCE_MODE_REVERSE_MAP = {
24
+ disabled: 'disabled',
25
+ enforced: 'enforced',
26
+ log_only: 'logOnly',
27
+ };
28
+ /**
29
+ * Maps applicationRuntimePolicy to performance policy mode
30
+ */
31
+ const ARP_TO_PERFORMANCE_POLICY_MODE_MAP = {
32
+ none: 'disabled',
33
+ tracking: 'log_only',
34
+ enforcing: 'enforced',
35
+ };
36
+ /**
37
+ * Maps Application Runtime Policy values to required Runtime Access Tracking values
38
+ * - none: RAT can be anything (no restriction)
39
+ * - tracking: RAT must be permissive
40
+ * - enforcing: RAT must be enforcing
41
+ */
42
+ const ARP_TO_RAT_MAP = {
43
+ none: null, // No restriction - RAT can be any value
44
+ tracking: 'permissive',
45
+ enforcing: 'enforcing',
46
+ };
47
+ // ============================================================================
48
+ // Network Policy Validation Functions
49
+ // ============================================================================
50
+ /**
51
+ * Validates host field format and content
52
+ * Uses URL API for parsing with manual wildcard validation (URL can't handle wildcards)
53
+ * Matches platform business rule validation logic
54
+ */
55
+ function validateHost(host, policyType, diagnostics, source) {
56
+ if (!host || !host.trim()) {
57
+ return true;
58
+ }
59
+ const hostValue = host.trim();
60
+ // Use URL API for scheme/port parsing — replace wildcards with placeholder since URL can't handle them
61
+ const parseableUrl = hostValue.replace(/\*/g, 'wildcard-placeholder');
62
+ let url;
63
+ try {
64
+ url = new URL(parseableUrl);
65
+ }
66
+ catch {
67
+ // URL throws for invalid ports (>65535) among other reasons
68
+ // Check if the host has an out-of-range port and diagnose it
69
+ const portMatch = hostValue.match(/:(\d+)\s*$/);
70
+ if (portMatch) {
71
+ const port = parseInt(portMatch[1], 10);
72
+ if (isNaN(port) || port < 1 || port > 65535) {
73
+ diagnostics.error(source, `Invalid port "${portMatch[1]}". Must be 1-65535.`);
74
+ return false;
75
+ }
76
+ }
77
+ // Zod regex handles basic format validation; if URL can't parse for other reasons, skip
78
+ return true;
79
+ }
80
+ const scheme = url.protocol.slice(0, -1); // Remove trailing ':'
81
+ // Validate scheme based on policy type
82
+ // Platform uses system properties to configure allowed schemes,
83
+ // but we use hardcoded defaults here for build-time validation
84
+ const allowedSchemes = getValidSchemes(policyType);
85
+ if (!allowedSchemes.includes(scheme)) {
86
+ diagnostics.error(source, `Invalid URL scheme "${scheme}" for policy type "${policyType}". Allowed schemes: ${allowedSchemes.join(', ')}`);
87
+ return false;
88
+ }
89
+ // Validate wildcards on original hostname (not the placeholder)
90
+ const originalHostname = url.hostname.replace(/wildcard-placeholder/g, '*');
91
+ if (!validateWildcards(originalHostname, diagnostics, source)) {
92
+ return false;
93
+ }
94
+ // Validate port range using URL's parsed port
95
+ if (url.port) {
96
+ const port = parseInt(url.port, 10);
97
+ if (port < 1 || port > 65535) {
98
+ diagnostics.error(source, `Invalid port "${url.port}". Must be 1-65535.`);
99
+ return false;
100
+ }
101
+ }
102
+ return true;
103
+ }
104
+ /**
105
+ * Gets valid URL schemes based on policy type
106
+ * Matches platform business rule defaults for glide.arp.csp.allowed.url.schemes
107
+ * and glide.arp.server.outbound.allowed.url.schemes
108
+ */
109
+ function getValidSchemes(policyType) {
110
+ // CSP script-src only allows HTTPS
111
+ if (policyType === 'csp_script_src') {
112
+ return ['https'];
113
+ }
114
+ // CSP connect-src allows HTTPS and WSS
115
+ if (policyType === 'csp_connect_src') {
116
+ return ['https', 'wss'];
117
+ }
118
+ // Server outbound allows all four protocols
119
+ if (policyType === 'now_outbound') {
120
+ return ['http', 'https', 'ws', 'wss'];
121
+ }
122
+ // For inbound policies, host should be empty, but if provided, use https
123
+ return ['https'];
124
+ }
125
+ /**
126
+ * Validates wildcard patterns in hostname
127
+ */
128
+ function validateWildcards(hostPart, diagnostics, source) {
129
+ if (hostPart === '*') {
130
+ diagnostics.error(source, 'Single wildcard "*" not allowed. Use "*.example.com" format.');
131
+ return false;
132
+ }
133
+ if (hostPart.indexOf('*') === -1) {
134
+ return true;
135
+ }
136
+ const parts = hostPart.split('.');
137
+ let wildcardCount = 0;
138
+ for (let i = 0; i < parts.length; i++) {
139
+ const part = parts[i];
140
+ if (part === '*') {
141
+ wildcardCount++;
142
+ if (i !== 0 || wildcardCount > 1) {
143
+ diagnostics.error(source, `Wildcard must be first segment only. Invalid: "${hostPart}"`);
144
+ return false;
145
+ }
146
+ }
147
+ else if (part.indexOf('*') !== -1) {
148
+ diagnostics.error(source, `Partial wildcards not allowed: "${part}"`);
149
+ return false;
150
+ }
151
+ }
152
+ return true;
153
+ }
154
+ /**
155
+ * Validates path field format
156
+ * Each path in the array must start with single forward slash
157
+ */
158
+ function validatePath(path, diagnostics, source) {
159
+ if (!path || path.length === 0) {
160
+ return true;
161
+ }
162
+ for (const singlePath of path) {
163
+ const trimmedPath = singlePath.trim();
164
+ if (!trimmedPath.startsWith('/') || trimmedPath.startsWith('//')) {
165
+ diagnostics.error(source, `Path must start with single "/": "${trimmedPath}"`);
166
+ return false;
167
+ }
168
+ }
169
+ return true;
170
+ }
171
+ /**
172
+ * Validates network policy fields for cross-field and business rules
173
+ */
174
+ function validateNetworkPolicy(policy, diagnostics, source) {
175
+ let isValid = true;
176
+ const policyType = typeof policy.policyType === 'string' ? policy.policyType : '';
177
+ const host = typeof policy.host === 'string' ? policy.host : undefined;
178
+ const path = Array.isArray(policy.path) ? policy.path : undefined;
179
+ if (host && !validateHost(host, policyType, diagnostics, source)) {
180
+ isValid = false;
181
+ }
182
+ if (path && !validatePath(path, diagnostics, source)) {
183
+ isValid = false;
184
+ }
185
+ return isValid;
186
+ }
187
+ /**
188
+ * Creates network policy records from config
189
+ */
190
+ async function createNetworkPolicyRecords(networkPolicies, factory, source, diagnostics) {
191
+ const records = [];
192
+ for (const policy of networkPolicies) {
193
+ const policyObj = policy.asObject();
194
+ // Validate policy fields (complex validations that can't be done in schema)
195
+ const policyData = {
196
+ policyType: policyObj.get('policyType')?.getValue(),
197
+ host: policyObj.get('host')?.getValue(),
198
+ path: policyObj.get('path')?.getValue(),
199
+ };
200
+ validateNetworkPolicy(policyData, diagnostics, policy);
201
+ const record = await factory.createRecord({
202
+ source: source,
203
+ table: 'sys_arp_network_policy',
204
+ explicitId: policyObj.get('$id'),
205
+ properties: policyObj.transform(({ $ }) => ({
206
+ active: $.def(true),
207
+ policy_type: $.from('policyType'),
208
+ status: $,
209
+ host: $,
210
+ scheme: $,
211
+ // Join path array with newlines for XML storage
212
+ path: $.from('path').map((pathShape) => pathShape
213
+ .ifArray()
214
+ ?.getElements()
215
+ .map((e) => e.getValue())
216
+ .join('\n') ?? ''),
217
+ resource: $,
218
+ short_description: $.from('shortDescription'),
219
+ })),
220
+ });
221
+ records.push(record);
222
+ }
223
+ return records;
224
+ }
225
+ /**
226
+ * Creates wildcard policy record from config
227
+ */
228
+ async function createWildcardPolicyRecord(wildcardPolicy, factory, source, scopeId, diagnostics) {
229
+ // Extract nested pillar objects
230
+ const networkPillar = wildcardPolicy.get('network')?.ifDefined()?.asObject();
231
+ const scriptingPillar = wildcardPolicy.get('scripting')?.ifDefined()?.asObject();
232
+ const arlPillar = wildcardPolicy.get('arl')?.ifDefined()?.asObject();
233
+ // Validate pillar active/wildcard consistency
234
+ const networkActive = networkPillar?.get('active')?.toBoolean().getValue() ?? false;
235
+ const networkWildcardShape = networkPillar?.get('networkWildcard')?.ifArray();
236
+ const networkWildcard = networkWildcardShape?.getElements();
237
+ if (!networkActive && networkWildcard && networkWildcard.length > 0) {
238
+ diagnostics.warn(networkWildcardShape, 'networkWildcard values will be ignored when network.active is false');
239
+ }
240
+ if (networkActive && (!networkWildcard || networkWildcard.length === 0)) {
241
+ diagnostics.hint(networkPillar.get('active'), 'Network pillar is active but has no networkWildcard selections. Consider adding networkWildcard values or setting active to false.');
242
+ }
243
+ const scriptingActive = scriptingPillar?.get('active')?.toBoolean().getValue() ?? false;
244
+ const scriptingWildcardShape = scriptingPillar?.get('scriptingWildcard')?.ifArray();
245
+ const scriptingWildcard = scriptingWildcardShape?.getElements();
246
+ if (!scriptingActive && scriptingWildcard && scriptingWildcard.length > 0) {
247
+ diagnostics.warn(scriptingWildcardShape, 'scriptingWildcard values will be ignored when scripting.active is false');
248
+ }
249
+ if (scriptingActive && (!scriptingWildcard || scriptingWildcard.length === 0)) {
250
+ diagnostics.hint(scriptingPillar.get('active'), 'Scripting pillar is active but has no scriptingWildcard selections. Consider adding scriptingWildcard values or setting active to false.');
251
+ }
252
+ const arlActive = arlPillar?.get('active')?.toBoolean().getValue() ?? false;
253
+ const arlWildcardShape = arlPillar?.get('arlWildcard')?.ifArray();
254
+ const arlWildcard = arlWildcardShape?.getElements();
255
+ if (!arlActive && arlWildcard && arlWildcard.length > 0) {
256
+ diagnostics.warn(arlWildcardShape, 'arlWildcard values will be ignored when arl.active is false');
257
+ }
258
+ if (arlActive && (!arlWildcard || arlWildcard.length === 0)) {
259
+ diagnostics.hint(arlPillar.get('active'), 'ARL pillar is active but has no arlWildcard selections. Consider adding arlWildcard values or setting active to false.');
260
+ }
261
+ return await factory.createRecord({
262
+ source: source,
263
+ table: 'sys_arp_segment_policy',
264
+ explicitId: wildcardPolicy.get('$id'),
265
+ properties: wildcardPolicy.transform(({ $ }) => ({
266
+ sys_scope: $.val(scopeId),
267
+ active: $.def(false),
268
+ short_description: $.from('shortDescription').def(''),
269
+ arp_record: $.from('record').toBoolean().def(false),
270
+ // Network pillar
271
+ arp_network: $.val(networkPillar?.get('active')?.toBoolean().getValue() ?? false),
272
+ arp_network_wildcard: $.val(networkPillar
273
+ ?.get('networkWildcard')
274
+ ?.ifArray()
275
+ ?.getElements()
276
+ .map((e) => e.getValue())
277
+ .join(',') ?? ''),
278
+ // Scripting pillar
279
+ arp_script: $.val(scriptingPillar?.get('active')?.toBoolean().getValue() ?? false),
280
+ arp_script_wildcard: $.val(scriptingPillar
281
+ ?.get('scriptingWildcard')
282
+ ?.ifArray()
283
+ ?.getElements()
284
+ .map((e) => e.getValue())
285
+ .join(',') ?? ''),
286
+ // ARL pillar
287
+ arp_arl: $.val(arlPillar?.get('active')?.toBoolean().getValue() ?? false),
288
+ arp_arl_wildcard: $.val(arlPillar
289
+ ?.get('arlWildcard')
290
+ ?.ifArray()
291
+ ?.getElements()
292
+ .map((e) => e.getValue())
293
+ .join(',') ?? ''),
294
+ })),
295
+ });
296
+ }
297
+ /**
298
+ * Creates performance policy record from config
299
+ * Mode is auto-derived from applicationRuntimePolicy if not explicitly set
300
+ */
301
+ async function createPerformancePolicyRecord(performancePolicy, factory, source, scopeId, applicationRuntimePolicy, diagnostics) {
302
+ // Derive state from applicationRuntimePolicy
303
+ const derivedMode = ARP_TO_PERFORMANCE_POLICY_MODE_MAP[applicationRuntimePolicy] ?? 'log_only';
304
+ // Check if user explicitly set mode and it differs from auto-derived value
305
+ const explicitModeShape = performancePolicy.get('mode').ifDefined();
306
+ if (explicitModeShape) {
307
+ const explicitMode = explicitModeShape.toString().getValue();
308
+ const explicitModeInXml = PERFORMANCE_MODE_MAP[explicitMode];
309
+ if (explicitModeInXml && explicitModeInXml !== derivedMode) {
310
+ const derivedModeInFluent = Object.entries(PERFORMANCE_MODE_MAP).find(([_, xml]) => xml === derivedMode)?.[0];
311
+ diagnostics.warn(explicitModeShape, `Performance policy mode '${explicitMode}' differs from auto-derived mode '${derivedModeInFluent}' based on applicationRuntimePolicy='${applicationRuntimePolicy}'. The explicit mode will be used.`);
312
+ }
313
+ }
314
+ return await factory.createRecord({
315
+ source: source,
316
+ table: 'sys_app_resource_limit_template',
317
+ explicitId: performancePolicy.get('$id'),
318
+ properties: performancePolicy.transform(({ $ }) => ({
319
+ template_name: $.from('name'),
320
+ // Auto-derive appCondition to query current scope
321
+ app_condition: $.val(`sys_id=${scopeId}`),
322
+ scheduled_job_limit: $.from('scheduledJobLimit').def(20),
323
+ event_handler_limit: $.from('eventHandlerLimit').def(20),
324
+ api_transaction_limit: $.from('apiTransactionLimit').def(30),
325
+ interactive_transaction_limit: $.from('interactiveTransactionLimit').def(30),
326
+ // State is auto-derived from applicationRuntimePolicy unless explicitly overridden
327
+ state: $.from('mode')
328
+ .map((v) => {
329
+ const explicitMode = v.getValue();
330
+ const mappedMode = PERFORMANCE_MODE_MAP[explicitMode];
331
+ if (explicitMode && !mappedMode) {
332
+ // Invalid mode value - should have been caught by schema validation
333
+ // but add a diagnostic warning just in case
334
+ diagnostics.warn(v, `Invalid mode value '${explicitMode}'. Valid values are: disabled, enforced, logOnly. Using auto-derived mode instead.`);
335
+ }
336
+ return mappedMode ?? derivedMode;
337
+ })
338
+ .def(derivedMode),
339
+ enable_auto_gen_limits: $.val(true), // Always enabled, hidden from user
340
+ order: $.val(100), // Default order, hidden from user
341
+ })),
342
+ });
343
+ }
344
+ // ============================================================================
9
345
  exports.NowConfigPlugin = sdk_build_core_1.Plugin.create({
10
346
  name: 'NowConfigPlugin',
347
+ docs: [],
11
348
  files: [
12
349
  {
13
350
  matcher: /\Wnow\.config\.json$/,
@@ -16,15 +353,221 @@ exports.NowConfigPlugin = sdk_build_core_1.Plugin.create({
16
353
  ],
17
354
  records: {
18
355
  sys_app: {
19
- toShape(record, { packageJson, config }) {
356
+ relationships: {
357
+ sys_arp_network_policy: {
358
+ via: 'sys_scope',
359
+ descendant: true,
360
+ },
361
+ sys_arp_segment_policy: {
362
+ via: 'sys_scope',
363
+ descendant: true,
364
+ },
365
+ sys_app_resource_limit_template: {
366
+ via: 'sys_scope',
367
+ descendant: true,
368
+ },
369
+ },
370
+ toShape(record, { packageJson, config, database }) {
20
371
  const { name: packageName } = packageJson;
372
+ // Query policy records directly from database
373
+ // During transform, the database only contains records from the current scope's XML
374
+ const networkPolicyRecords = database.query('sys_arp_network_policy');
375
+ const segmentPolicyRecords = database.query('sys_arp_segment_policy');
376
+ const resourceLimitRecords = database.query('sys_app_resource_limit_template');
377
+ // Transform network policies (exclude default values)
378
+ const networkPolicies = networkPolicyRecords.map((policyRecord) => {
379
+ const policyType = policyRecord.get('policy_type').toString().getValue();
380
+ const result = {
381
+ $id: policyRecord.getId().getValue(),
382
+ policyType: policyType,
383
+ };
384
+ // Only include active if not default (true)
385
+ const active = policyRecord.get('active').toBoolean().getValue();
386
+ if (active !== true) {
387
+ result['active'] = active;
388
+ }
389
+ // Status is required - always include it (default to 'requested' if missing for backwards compatibility)
390
+ const status = policyRecord.get('status').toString().getValue() || 'requested';
391
+ result['status'] = status;
392
+ const host = policyRecord.get('host').ifDefined()?.toString().getValue();
393
+ if (host) {
394
+ result['host'] = host;
395
+ }
396
+ const scheme = policyRecord.get('scheme').ifDefined()?.toString().getValue();
397
+ if (scheme) {
398
+ result['scheme'] = scheme;
399
+ }
400
+ const pathVal = policyRecord.get('path').ifDefined()?.toString().getValue();
401
+ if (pathVal) {
402
+ // Split path string by newlines and clean each path
403
+ // ServiceNow stores multi-line fields with CRLF (\r\n) which get XML-encoded as \n&#13;
404
+ // The fast-xml-parser doesn't decode numeric character references, so we strip them manually
405
+ result['path'] = pathVal
406
+ .split('\n')
407
+ .map((p) => p.replace(/&#13;/g, '').trim())
408
+ .filter((p) => p);
409
+ }
410
+ const resource = policyRecord.get('resource').ifDefined()?.toString().getValue();
411
+ if (resource) {
412
+ result['resource'] = resource;
413
+ }
414
+ const shortDesc = policyRecord.get('short_description').ifDefined()?.toString().getValue();
415
+ if (shortDesc) {
416
+ result['shortDescription'] = shortDesc;
417
+ }
418
+ return result;
419
+ });
420
+ // Transform wildcard/segment policy (only one per scope, exclude default values)
421
+ let wildcardPolicy;
422
+ const segmentRecord = segmentPolicyRecords[0];
423
+ if (segmentRecord) {
424
+ wildcardPolicy = {
425
+ $id: segmentRecord.getId().getValue(),
426
+ };
427
+ // Only include active if not default (false)
428
+ const active = segmentRecord.get('active').toBoolean().getValue();
429
+ if (active !== false) {
430
+ wildcardPolicy['active'] = active;
431
+ }
432
+ const shortDesc = segmentRecord.get('short_description').ifDefined()?.toString().getValue();
433
+ if (shortDesc) {
434
+ wildcardPolicy['shortDescription'] = shortDesc;
435
+ }
436
+ // Network pillar
437
+ const networkActive = segmentRecord.get('arp_network').ifDefined()?.toBoolean().getValue();
438
+ const networkWildcardStr = segmentRecord
439
+ .get('arp_network_wildcard')
440
+ .ifDefined()
441
+ ?.toString()
442
+ .getValue();
443
+ const networkWildcardArr = networkWildcardStr
444
+ ? networkWildcardStr.split(',').filter((s) => s.trim())
445
+ : [];
446
+ if (networkActive !== undefined || networkWildcardArr.length > 0) {
447
+ const networkPillar = {};
448
+ // Only include active if not default (false)
449
+ if (networkActive !== false) {
450
+ networkPillar['active'] = networkActive ?? false;
451
+ }
452
+ if (networkWildcardArr.length > 0) {
453
+ networkPillar['networkWildcard'] = networkWildcardArr;
454
+ }
455
+ if (Object.keys(networkPillar).length > 0) {
456
+ wildcardPolicy['network'] = networkPillar;
457
+ }
458
+ }
459
+ // Scripting pillar
460
+ const scriptingActive = segmentRecord.get('arp_script').ifDefined()?.toBoolean().getValue();
461
+ const scriptWildcardStr = segmentRecord
462
+ .get('arp_script_wildcard')
463
+ .ifDefined()
464
+ ?.toString()
465
+ .getValue();
466
+ const scriptWildcardArr = scriptWildcardStr
467
+ ? scriptWildcardStr.split(',').filter((s) => s.trim())
468
+ : [];
469
+ if (scriptingActive !== undefined || scriptWildcardArr.length > 0) {
470
+ const scriptingPillar = {};
471
+ // Only include active if not default (false)
472
+ if (scriptingActive !== false) {
473
+ scriptingPillar['active'] = scriptingActive ?? false;
474
+ }
475
+ if (scriptWildcardArr.length > 0) {
476
+ scriptingPillar['scriptingWildcard'] = scriptWildcardArr;
477
+ }
478
+ if (Object.keys(scriptingPillar).length > 0) {
479
+ wildcardPolicy['scripting'] = scriptingPillar;
480
+ }
481
+ }
482
+ // ARL pillar
483
+ const arlActive = segmentRecord.get('arp_arl').ifDefined()?.toBoolean().getValue();
484
+ const arlWildcardStr = segmentRecord.get('arp_arl_wildcard').ifDefined()?.toString().getValue();
485
+ const arlWildcardArr = arlWildcardStr
486
+ ? arlWildcardStr.split(',').filter((s) => s.trim())
487
+ : [];
488
+ if (arlActive !== undefined || arlWildcardArr.length > 0) {
489
+ const arlPillar = {};
490
+ // Only include active if not default (false)
491
+ if (arlActive !== false) {
492
+ arlPillar['active'] = arlActive ?? false;
493
+ }
494
+ if (arlWildcardArr.length > 0) {
495
+ arlPillar['arlWildcard'] = arlWildcardArr;
496
+ }
497
+ if (Object.keys(arlPillar).length > 0) {
498
+ wildcardPolicy['arl'] = arlPillar;
499
+ }
500
+ }
501
+ // Record flag - only include if not default (false)
502
+ const arpRecord = segmentRecord.get('arp_record').ifDefined()?.toBoolean().getValue();
503
+ if (arpRecord !== undefined && arpRecord !== false) {
504
+ wildcardPolicy['record'] = arpRecord;
505
+ }
506
+ // If wildcardPolicy is empty, set to undefined
507
+ if (Object.keys(wildcardPolicy).length === 0) {
508
+ wildcardPolicy = undefined;
509
+ }
510
+ }
511
+ // Transform performance/resource limit policy (only one per scope, exclude default values)
512
+ let performancePolicy;
513
+ const limitRecord = resourceLimitRecords[0];
514
+ if (limitRecord) {
515
+ performancePolicy = {
516
+ $id: limitRecord.getId().getValue(),
517
+ name: limitRecord.get('template_name').toString().getValue(),
518
+ };
519
+ // Only include quota limits if not default values
520
+ const scheduledJobLimit = limitRecord.get('scheduled_job_limit').ifDefined()?.toNumber().getValue();
521
+ if (scheduledJobLimit !== undefined && scheduledJobLimit !== 20) {
522
+ performancePolicy['scheduledJobLimit'] = scheduledJobLimit;
523
+ }
524
+ const eventHandlerLimit = limitRecord.get('event_handler_limit').ifDefined()?.toNumber().getValue();
525
+ if (eventHandlerLimit !== undefined && eventHandlerLimit !== 20) {
526
+ performancePolicy['eventHandlerLimit'] = eventHandlerLimit;
527
+ }
528
+ const apiTransactionLimit = limitRecord
529
+ .get('api_transaction_limit')
530
+ .ifDefined()
531
+ ?.toNumber()
532
+ .getValue();
533
+ if (apiTransactionLimit !== undefined && apiTransactionLimit !== 30) {
534
+ performancePolicy['apiTransactionLimit'] = apiTransactionLimit;
535
+ }
536
+ const interactiveTransactionLimit = limitRecord
537
+ .get('interactive_transaction_limit')
538
+ .ifDefined()
539
+ ?.toNumber()
540
+ .getValue();
541
+ if (interactiveTransactionLimit !== undefined && interactiveTransactionLimit !== 30) {
542
+ performancePolicy['interactiveTransactionLimit'] = interactiveTransactionLimit;
543
+ }
544
+ // Only include mode if it differs from auto-derived value based on applicationRuntimePolicy
545
+ // This ensures round-trip consistency with toRecord behavior
546
+ const arpValue = record.get('application_runtime_policy').ifDefined()?.toString().getValue() || 'none';
547
+ const autoDerivedMode = ARP_TO_PERFORMANCE_POLICY_MODE_MAP[arpValue] ?? 'log_only';
548
+ const state = limitRecord.get('state').ifDefined()?.toString().getValue();
549
+ if (state && state !== autoDerivedMode) {
550
+ const mode = PERFORMANCE_MODE_REVERSE_MAP[state] ?? state;
551
+ performancePolicy['mode'] = mode;
552
+ }
553
+ }
21
554
  return {
22
555
  success: true,
23
556
  value: new NowConfigShape({
24
557
  source: record,
25
558
  properties: record.transform(({ $ }) => ({
26
- name: $.toString().def(packageName),
559
+ // Required fields
560
+ scope: $.from('scope').toString(),
561
+ scopeId: $.val(record.getId().getValue()),
562
+ name: $.from('name').toString().def(packageName),
27
563
  active: $.toBoolean().def(true),
564
+ // Include ARP configuration
565
+ applicationRuntimePolicy: $.from('application_runtime_policy').def('none'),
566
+ // Policy records are always included in config regardless of ARP mode
567
+ // The platform will honor or ignore them based on applicationRuntimePolicy value
568
+ ...(networkPolicies.length > 0 ? { networkPolicies: $.val(networkPolicies) } : {}),
569
+ ...(wildcardPolicy ? { wildcardPolicy: $.val(wildcardPolicy) } : {}),
570
+ ...(performancePolicy ? { performancePolicy: $.val(performancePolicy) } : {}),
28
571
  accessControls: $.from('scoped_administration', 'restrict_table_access', 'can_edit_in_studio', 'runtime_access_tracking', 'private', 'trackable', 'uninstall_blocked', 'hide_on_ui', 'user_role')
29
572
  .def({
30
573
  scopedAdministration: false,
@@ -83,6 +626,27 @@ exports.NowConfigPlugin = sdk_build_core_1.Plugin.create({
83
626
  };
84
627
  },
85
628
  },
629
+ // Policy table configurations for transform (XML to Fluent)
630
+ // These records are merged into now.config.json via sys_app.toShape descendants query
631
+ // toNoOpShape prevents RecordPlugin from creating separate files for these descendants
632
+ sys_arp_network_policy: {
633
+ coalesce: ['policy_type', 'host', 'scheme', 'path', 'resource'],
634
+ toShape(record) {
635
+ return { success: true, value: sdk_build_core_1.Shape.noOp(record) };
636
+ },
637
+ },
638
+ sys_arp_segment_policy: {
639
+ coalesce: ['sys_scope'],
640
+ toShape(record) {
641
+ return { success: true, value: sdk_build_core_1.Shape.noOp(record) };
642
+ },
643
+ },
644
+ sys_app_resource_limit_template: {
645
+ coalesce: ['template_name'],
646
+ toShape(record) {
647
+ return { success: true, value: sdk_build_core_1.Shape.noOp(record) };
648
+ },
649
+ },
86
650
  },
87
651
  shapes: [
88
652
  {
@@ -107,57 +671,101 @@ exports.NowConfigPlugin = sdk_build_core_1.Plugin.create({
107
671
  }
108
672
  const accessControls = config.get('accessControls').ifDefined()?.asObject();
109
673
  const licensing = config.get('licensing').ifDefined()?.asObject();
674
+ // Application Runtime Policy configuration
675
+ const applicationRuntimePolicy = config.get('applicationRuntimePolicy').ifString()?.getValue() ?? 'none';
676
+ const networkPoliciesShape = config.get('networkPolicies')?.ifArray()?.getElements() ?? [];
677
+ const wildcardPolicyShape = config.get('wildcardPolicy')?.ifDefined()?.asObject();
678
+ const performancePolicyShape = config.get('performancePolicy')?.ifDefined()?.asObject();
679
+ const hasPolicyDefinitions = networkPoliciesShape.length > 0 || wildcardPolicyShape || performancePolicyShape;
680
+ // Warn if ARP is 'none' but policies are defined
681
+ if (applicationRuntimePolicy === 'none' && hasPolicyDefinitions) {
682
+ diagnostics.warn(config, `Application Runtime Policy is set to 'none'. Policy records will be created but the ServiceNow platform will not enforce them. Set applicationRuntimePolicy to 'tracking' or 'enforcing' to enable policy enforcement.`);
683
+ }
684
+ // Create sys_app record
685
+ const sysAppRecord = await factory.createRecord({
686
+ source: config,
687
+ table: 'sys_app',
688
+ properties: config.transform(({ $ }) => ({
689
+ active: $.toBoolean().def(true),
690
+ // Application Runtime Policy field - only include if not 'none' (default)
691
+ ...(applicationRuntimePolicy && applicationRuntimePolicy !== 'none'
692
+ ? { application_runtime_policy: $.val(applicationRuntimePolicy) }
693
+ : {}),
694
+ scoped_administration: $.val(accessControls?.get('scopedAdministration'))
695
+ .toBoolean()
696
+ .def(false),
697
+ can_edit_in_studio: $.val(accessControls?.get('canEditInStudio')).toBoolean().def(true),
698
+ js_level: $.from('jsLevel').toString().def('es_latest'),
699
+ restrict_table_access: $.val(accessControls?.get('restrictTableAccess')).toBoolean().def(false),
700
+ runtime_access_tracking: $.map(() => {
701
+ const ratShape = accessControls?.get('runtimeAccessTracking');
702
+ const userProvidedRat = ratShape?.getValue();
703
+ // ARP may require a specific RAT value
704
+ const requiredRat = ARP_TO_RAT_MAP[applicationRuntimePolicy];
705
+ // Error only when user *explicitly* set a conflicting value — not when it was omitted
706
+ if (userProvidedRat && requiredRat && userProvidedRat !== requiredRat) {
707
+ diagnostics.error(ratShape || config.get('accessControls'), `Runtime Access Tracking is set to '${userProvidedRat}' but Application Runtime Policy '${applicationRuntimePolicy}' requires '${requiredRat}'. ` +
708
+ `Set runtimeAccessTracking to '${requiredRat}' to match the Application Runtime Policy.`);
709
+ }
710
+ // Hint when ARP auto-derives RAT and user also set it explicitly (even if not conflicting)
711
+ if (userProvidedRat && requiredRat && userProvidedRat === requiredRat) {
712
+ diagnostics.hint(ratShape || config.get('accessControls'), `runtimeAccessTracking is auto-derived from applicationRuntimePolicy='${applicationRuntimePolicy}'. ` +
713
+ `The explicit value '${userProvidedRat}' can be removed.`);
714
+ }
715
+ // Auto-derive from ARP when it specifies a required value; otherwise fall back to
716
+ // the user-provided value or the platform default of 'permissive'.
717
+ const rat = requiredRat ?? userProvidedRat ?? 'permissive';
718
+ // Convert 'none' to undefined for XML (empty field).
719
+ // Round-trip safe: toShape maps empty string back to 'none' (line 796-798)
720
+ if (rat === 'none') {
721
+ return undefined;
722
+ }
723
+ return rat;
724
+ }),
725
+ licensable: $.val(licensing?.get('licensable')).toBoolean().def(true),
726
+ enforce_license: $.val(licensing?.get('enforceLicense')).toString().def('log'),
727
+ license_model: $.val(licensing?.get('licenseModel')).toString().def('none'),
728
+ menu: $.toString().def(''),
729
+ user_role: $.val(accessControls?.get('userRole')).toString().def(''),
730
+ short_description: $.from('description').toString().def(''),
731
+ logo: $.toString().def(''),
732
+ guided_setup_guid: $.from('guidedSetupGuid').toString().def(''),
733
+ subscription_entitlement: $.val(licensing?.get('subscriptionEntitlement')).toString().def(''),
734
+ private: $.val(accessControls?.get('private')).toBoolean().def(false),
735
+ trackable: $.val(accessControls?.get('trackable')).toBoolean().def(true),
736
+ uninstall_blocked: $.val(accessControls?.get('uninstallBlocked')).toBoolean().def(false),
737
+ hide_on_ui: $.val(accessControls?.get('hideOnUI')).toBoolean().def(false),
738
+ installed_as_dependency: $.from('installedAsDependency').toBoolean().def(false),
739
+ license_category: $.val(licensing?.get('licenseCategory')).toString().def('none'),
740
+ scope: $.val(scope),
741
+ package_json: $.val(packageJsonPath),
742
+ name: $.val(name),
743
+ source: $.val(scope),
744
+ sys_id: $.from('scopeId'),
745
+ version: $.val(version),
746
+ package_resolver_version: $.from('packageResolverVersion').def(scope === 'global' ? sdk_build_core_1.MODULE_RESOLUTION.V2 : sdk_build_core_1.MODULE_RESOLUTION.V1),
747
+ })),
748
+ });
749
+ const policyRecords = [];
750
+ const scopeId = config.get('scopeId').asString().getValue();
751
+ // Create network policy records
752
+ if (networkPoliciesShape.length > 0) {
753
+ const networkRecords = await createNetworkPolicyRecords(networkPoliciesShape, factory, config, diagnostics);
754
+ policyRecords.push(...networkRecords);
755
+ }
756
+ // Create wildcard policy record
757
+ if (wildcardPolicyShape) {
758
+ const wildcardRecord = await createWildcardPolicyRecord(wildcardPolicyShape, factory, config, scopeId, diagnostics);
759
+ policyRecords.push(wildcardRecord);
760
+ }
761
+ // Create performance policy record
762
+ if (performancePolicyShape) {
763
+ const performanceRecord = await createPerformancePolicyRecord(performancePolicyShape, factory, config, scopeId, applicationRuntimePolicy, diagnostics);
764
+ policyRecords.push(performanceRecord);
765
+ }
110
766
  return {
111
767
  success: true,
112
- value: await factory.createRecord({
113
- source: config,
114
- table: 'sys_app',
115
- properties: config.transform(({ $ }) => ({
116
- active: $.toBoolean().def(true),
117
- scoped_administration: $.val(accessControls?.get('scopedAdministration'))
118
- .toBoolean()
119
- .def(false),
120
- can_edit_in_studio: $.val(accessControls?.get('canEditInStudio')).toBoolean().def(true),
121
- js_level: $.from('jsLevel').toString().def('es_latest'),
122
- restrict_table_access: $.val(accessControls?.get('restrictTableAccess'))
123
- .toBoolean()
124
- .def(false),
125
- runtime_access_tracking: $.map(() => {
126
- const rac = accessControls?.get('runtimeAccessTracking').getValue();
127
- if (!rac) {
128
- return 'permissive';
129
- }
130
- else if (rac === 'none') {
131
- return undefined;
132
- }
133
- return rac;
134
- }),
135
- licensable: $.val(licensing?.get('licensable')).toBoolean().def(true),
136
- enforce_license: $.val(licensing?.get('enforceLicense')).toString().def('log'),
137
- license_model: $.val(licensing?.get('licenseModel')).toString().def('none'),
138
- menu: $.toString().def(''),
139
- user_role: $.val(accessControls?.get('userRole')).toString().def(''),
140
- short_description: $.from('description').toString().def(''),
141
- logo: $.toString().def(''),
142
- guided_setup_guid: $.from('guidedSetupGuid').toString().def(''),
143
- subscription_entitlement: $.val(licensing?.get('subscriptionEntitlement'))
144
- .toString()
145
- .def(''),
146
- private: $.val(accessControls?.get('private')).toBoolean().def(false),
147
- trackable: $.val(accessControls?.get('trackable')).toBoolean().def(true),
148
- uninstall_blocked: $.val(accessControls?.get('uninstallBlocked')).toBoolean().def(false),
149
- hide_on_ui: $.val(accessControls?.get('hideOnUI')).toBoolean().def(false),
150
- installed_as_dependency: $.from('installedAsDependency').toBoolean().def(false),
151
- license_category: $.val(licensing?.get('licenseCategory')).toString().def('none'),
152
- scope: $.val(scope),
153
- package_json: $.val(packageJsonPath),
154
- name: $.val(name),
155
- source: $.val(scope),
156
- sys_id: $.from('scopeId'),
157
- version: $.val(version),
158
- package_resolver_version: $.from('packageResolverVersion').def(scope === 'global' ? sdk_build_core_1.MODULE_RESOLUTION.V2 : sdk_build_core_1.MODULE_RESOLUTION.V1),
159
- })),
160
- }),
768
+ value: sysAppRecord.with(...policyRecords),
161
769
  };
162
770
  },
163
771
  },