@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
@@ -0,0 +1,319 @@
1
+ import { Plugin, CallExpressionShape, type Record, type Shape } from '@servicenow/sdk-build-core'
2
+ import { NowIdShape } from '../now-id-plugin'
3
+ import { createSdkDocEntry } from '../utils'
4
+
5
+ function convertToNumber(value: Shape, defaultValue: number = 0): number {
6
+ if (!value || value.isUndefined() || value.ifString()?.isEmpty()) {
7
+ return defaultValue
8
+ }
9
+ return value.toNumber()?.getValue() ?? defaultValue
10
+ }
11
+
12
+ export const ServicePortalPlugin = Plugin.create({
13
+ name: 'ServicePortalPlugin',
14
+ docs: [createSdkDocEntry('ServicePortal', ['sp_portal'])],
15
+ records: {
16
+ sp_portal: {
17
+ relationships: {
18
+ m2m_sp_portal_search_source: {
19
+ descendant: true,
20
+ via: 'sp_portal',
21
+ },
22
+ m2m_sp_portal_knowledge_base: {
23
+ descendant: true,
24
+ via: 'sp_portal',
25
+ },
26
+ m2m_sp_portal_catalog: {
27
+ descendant: true,
28
+ via: 'sp_portal',
29
+ },
30
+ m2m_sp_portal_taxonomy: {
31
+ descendant: true,
32
+ via: 'sp_portal',
33
+ },
34
+ },
35
+ toShape(record, { descendants }) {
36
+ // Handle search sources
37
+ const searchSources = descendants.query('m2m_sp_portal_search_source').map((m2m) => {
38
+ return {
39
+ searchSource: m2m.get('sp_search_source'),
40
+ order: convertToNumber(m2m.get('order'), 100),
41
+ }
42
+ })
43
+
44
+ // Handle knowledge bases
45
+ const knowledgeBases = descendants
46
+ .query('m2m_sp_portal_knowledge_base')
47
+ .filter((m2m) => !m2m.get('kb_knowledge_base')?.ifString()?.isEmpty())
48
+ .map((m2m) => {
49
+ return {
50
+ knowledgeBase: m2m.get('kb_knowledge_base'),
51
+ order: convertToNumber(m2m.get('order'), 100),
52
+ active: m2m.get('active')?.isUndefined()
53
+ ? undefined
54
+ : m2m.get('active')?.toBoolean()?.getValue(),
55
+ }
56
+ })
57
+
58
+ // Handle catalogs
59
+ const catalogs = descendants
60
+ .query('m2m_sp_portal_catalog')
61
+ .filter((m2m) => !m2m.get('sc_catalog')?.ifString()?.isEmpty())
62
+ .map((m2m) => {
63
+ return {
64
+ catalog: m2m.get('sc_catalog'),
65
+ order: convertToNumber(m2m.get('order'), 100),
66
+ active: m2m.get('active')?.isUndefined()
67
+ ? undefined
68
+ : m2m.get('active')?.toBoolean()?.getValue(),
69
+ }
70
+ })
71
+
72
+ // Handle taxonomies
73
+ const taxonomies = descendants.query('m2m_sp_portal_taxonomy').map((m2m) => {
74
+ return {
75
+ taxonomy: m2m.get('taxonomy'),
76
+ order: convertToNumber(m2m.get('order'), 100),
77
+ active: m2m.get('active')?.isUndefined()
78
+ ? undefined
79
+ : m2m.get('active')?.toBoolean()?.getValue(),
80
+ }
81
+ })
82
+
83
+ const portalRecord = new CallExpressionShape({
84
+ source: record,
85
+ callee: 'ServicePortal',
86
+ args: [
87
+ record.transform(({ $ }) => ({
88
+ $id: $.val(NowIdShape.from(record)),
89
+ title: $,
90
+ urlSuffix: $.from('url_suffix').def(''),
91
+ icon: $.from('icon').def(''),
92
+ logo: $.from('logo').def(''),
93
+ logoAltText: $.from('logo_alt_text').def(''),
94
+ quickStartConfig: $.from('quick_start_config').def(''),
95
+ embeddableMacroponents: $.from('embeddable_macroponents').def(''),
96
+ communicationChannels: $.from('communication_channels').def(''),
97
+ cssVariables: $.from('css_variables').def(''),
98
+ theme: $.from('theme').def(''),
99
+ darkTheme: $.from('dark_theme').def(''),
100
+ mainMenu: $.from('sp_rectangle_menu').def(''),
101
+ categoryHomePage: $.from('sc_category_page').def(''),
102
+ homePage: $.from('homepage').def(''),
103
+ searchResultsConfiguration: $.from('search_results_configuration').def(''),
104
+ searchApplication: $.from('search_application').def(''),
105
+ alternatePortal: $.from('alternate_portal').def(''),
106
+ catalogHomePage: $.from('sc_catalog_page').def(''),
107
+ loginPage: $.from('login_page').def(''),
108
+ knowledgeHomePage: $.from('kb_knowledge_page').def(''),
109
+ chatQueue: $.from('sp_chat_queue').def(''),
110
+ notFoundPage: $.from('notfound_page').def(''),
111
+ textIndexGroup: $.from('ts_index_group').def(''),
112
+ enableAiSearch: $.from('enable_ais').toBoolean().def(false),
113
+ enableCertificateBasedAuthentication: $.from('enable_certificate_based_authentication')
114
+ .toBoolean()
115
+ .def(false),
116
+ defaultPortal: $.from('default').toBoolean().def(false),
117
+ enableWebEmbeddables: $.from('enable_embeddables').toBoolean().def(false),
118
+ enableFavorites: $.from('enable_favorites').toBoolean().def(false),
119
+ inactive: $.from('inactive').toBoolean().def(false),
120
+ hidePortalName: $.from('hide_portal_name').toBoolean().def(false),
121
+ supportRightToLeftLanguages: $.from('rtl_enabled').toBoolean().def(false),
122
+ searchSources: $.val(searchSources).def([]),
123
+ knowledgeBases: $.val(knowledgeBases).def([]),
124
+ catalogs: $.val(catalogs).def([]),
125
+ taxonomies: $.val(taxonomies).def([]),
126
+ })),
127
+ ],
128
+ })
129
+
130
+ return {
131
+ success: true,
132
+ value: portalRecord,
133
+ }
134
+ },
135
+ },
136
+ m2m_sp_portal_search_source: {
137
+ coalesce: ['sp_portal', 'sp_search_source'],
138
+ },
139
+ m2m_sp_portal_knowledge_base: {
140
+ coalesce: ['sp_portal', 'kb_knowledge_base'],
141
+ },
142
+ m2m_sp_portal_catalog: {
143
+ coalesce: ['sp_portal', 'sc_catalog'],
144
+ },
145
+ m2m_sp_portal_taxonomy: {
146
+ coalesce: ['sp_portal', 'taxonomy'],
147
+ },
148
+ },
149
+ shapes: [
150
+ {
151
+ shape: CallExpressionShape,
152
+ fileTypes: ['fluent'],
153
+ async toRecord(callExpression, { factory, diagnostics }) {
154
+ if (callExpression.getCallee() !== 'ServicePortal') {
155
+ return { success: false }
156
+ }
157
+ const portalArgs = callExpression.getArgument(0).asObject()
158
+
159
+ const urlSuffix = portalArgs.get('urlSuffix').ifString()
160
+ if (urlSuffix) {
161
+ const raw = urlSuffix.getValue()
162
+ let normalized = raw.toLowerCase().replace(/[^a-zA-Z0-9_]/g, '_')
163
+ while (normalized.indexOf('__') !== -1) {
164
+ normalized = normalized.replace(/__/g, '_')
165
+ }
166
+ while (normalized !== '' && normalized.charAt(normalized.length - 1) === '_') {
167
+ normalized = normalized.substring(0, normalized.length - 1)
168
+ }
169
+ if (raw !== normalized) {
170
+ diagnostics.error(
171
+ urlSuffix.getOriginalNode(),
172
+ `Invalid url_suffix '${raw}'. Use lowercase alphanumeric and single underscores only (e.g. '${normalized}')`
173
+ )
174
+ }
175
+ }
176
+ // Extract first catalog and knowledgeBase from arrays to populate portal fields
177
+ const catalogsForM2M = portalArgs.get('catalogs')?.ifArray()?.getElements() ?? []
178
+ const knowledgeBasesForM2M = portalArgs.get('knowledgeBases')?.ifArray()?.getElements() ?? []
179
+ const firstCatalogFromArray =
180
+ catalogsForM2M.length > 0 ? catalogsForM2M[0]?.asObject()?.get('catalog') : undefined
181
+ const firstKnowledgeBaseFromArray =
182
+ knowledgeBasesForM2M.length > 0
183
+ ? knowledgeBasesForM2M[0]?.asObject()?.get('knowledgeBase')
184
+ : undefined
185
+
186
+ // Build portal properties with conditional catalog and knowledgeBase
187
+ const baseProperties = portalArgs.transform(({ $ }) => ({
188
+ title: $,
189
+ url_suffix: $.from('urlSuffix').def(''),
190
+ icon: $.from('icon'),
191
+ logo: $.from('logo'),
192
+ logo_alt_text: $.from('logoAltText'),
193
+ quick_start_config: $.from('quickStartConfig'),
194
+ embeddable_macroponents: $.from('embeddableMacroponents'),
195
+ communication_channels: $.from('communicationChannels'),
196
+ css_variables: $.from('cssVariables'),
197
+ theme: $.from('theme'),
198
+ dark_theme: $.from('darkTheme'),
199
+ sp_rectangle_menu: $.from('mainMenu'),
200
+ sc_category_page: $.from('categoryHomePage'),
201
+ homepage: $.from('homePage'),
202
+ search_results_configuration: $.from('searchResultsConfiguration'),
203
+ search_application: $.from('searchApplication'),
204
+ alternate_portal: $.from('alternatePortal'),
205
+ sc_catalog_page: $.from('catalogHomePage'),
206
+ sc_catalog: $.val(firstCatalogFromArray),
207
+ kb_knowledge_base: $.val(firstKnowledgeBaseFromArray),
208
+ login_page: $.from('loginPage'),
209
+ kb_knowledge_page: $.from('knowledgeHomePage'),
210
+ sp_chat_queue: $.from('chatQueue'),
211
+ notfound_page: $.from('notFoundPage'),
212
+ ts_index_group: $.from('textIndexGroup'),
213
+ enable_ais: $.from('enableAiSearch').def(false),
214
+ enable_certificate_based_authentication: $.from('enableCertificateBasedAuthentication').def(false),
215
+ default: $.from('defaultPortal').def(false),
216
+ enable_embeddables: $.from('enableWebEmbeddables').def(false),
217
+ enable_favorites: $.from('enableFavorites').def(false),
218
+ inactive: $.from('inactive').def(false),
219
+ hide_portal_name: $.from('hidePortalName').def(false),
220
+ rtl_enabled: $.from('supportRightToLeftLanguages').def(false),
221
+ }))
222
+
223
+ const portalRecord = await factory.createRecord({
224
+ source: callExpression,
225
+ table: 'sp_portal',
226
+ explicitId: portalArgs.get('$id'),
227
+ properties: baseProperties,
228
+ })
229
+
230
+ // Handle search sources
231
+ const searchSources = portalArgs.get('searchSources')?.ifArray()?.getElements() ?? []
232
+
233
+ const searchSourceRecords: Record[] = []
234
+ for (const source of searchSources) {
235
+ const sourceObj = source.asObject()
236
+ const searchSourceRecord = await factory.createRecord({
237
+ source: callExpression,
238
+ table: 'm2m_sp_portal_search_source',
239
+ properties: {
240
+ sp_portal: portalRecord,
241
+ sp_search_source: sourceObj.get('searchSource'),
242
+ order: convertToNumber(sourceObj.get('order'), 100),
243
+ },
244
+ })
245
+ searchSourceRecords.push(searchSourceRecord)
246
+ }
247
+
248
+ // Handle knowledge bases
249
+
250
+ const knowledgeBaseRecords: Record[] = []
251
+ for (const kb of knowledgeBasesForM2M) {
252
+ const kbObj = kb.asObject()
253
+ const knowledgeBaseRecord = await factory.createRecord({
254
+ source: callExpression,
255
+ table: 'm2m_sp_portal_knowledge_base',
256
+ properties: {
257
+ sp_portal: portalRecord,
258
+ kb_knowledge_base: kbObj.get('knowledgeBase'),
259
+ order: convertToNumber(kbObj.get('order'), 100),
260
+ active: kbObj.get('active')?.isUndefined()
261
+ ? true
262
+ : kbObj.get('active').toBoolean().getValue(),
263
+ },
264
+ })
265
+ knowledgeBaseRecords.push(knowledgeBaseRecord)
266
+ }
267
+
268
+ // Handle catalogs
269
+ const catalogRecords: Record[] = []
270
+ for (const cat of catalogsForM2M) {
271
+ const catObj = cat.asObject()
272
+ const catalogRecord = await factory.createRecord({
273
+ source: callExpression,
274
+ table: 'm2m_sp_portal_catalog',
275
+ properties: {
276
+ sp_portal: portalRecord,
277
+ sc_catalog: catObj.get('catalog'),
278
+ order: convertToNumber(catObj.get('order'), 100),
279
+ active: catObj.get('active')?.isUndefined()
280
+ ? true
281
+ : catObj.get('active').toBoolean().getValue(),
282
+ },
283
+ })
284
+ catalogRecords.push(catalogRecord)
285
+ }
286
+
287
+ // Handle taxonomies
288
+ const taxonomies = portalArgs.get('taxonomies')?.ifArray()?.getElements() ?? []
289
+ const taxonomyRecords: Record[] = []
290
+ for (const tax of taxonomies) {
291
+ const taxObj = tax.asObject()
292
+ const taxonomyRecord = await factory.createRecord({
293
+ source: callExpression,
294
+ table: 'm2m_sp_portal_taxonomy',
295
+ properties: {
296
+ sp_portal: portalRecord,
297
+ taxonomy: taxObj.get('taxonomy'),
298
+ order: convertToNumber(taxObj.get('order'), 100),
299
+ active: taxObj.get('active')?.isUndefined()
300
+ ? true
301
+ : taxObj.get('active').toBoolean().getValue(),
302
+ },
303
+ })
304
+ taxonomyRecords.push(taxonomyRecord)
305
+ }
306
+
307
+ return {
308
+ success: true,
309
+ value: portalRecord.with(
310
+ ...searchSourceRecords,
311
+ ...knowledgeBaseRecords,
312
+ ...catalogRecords,
313
+ ...taxonomyRecords
314
+ ),
315
+ }
316
+ },
317
+ },
318
+ ],
319
+ })
@@ -0,0 +1,135 @@
1
+ import { CallExpressionShape, Plugin } from '@servicenow/sdk-build-core'
2
+ import { NowIdShape } from '../now-id-plugin'
3
+ import { DEFAULT_ORDER, getIncludeRecords } from './utils'
4
+ import { createSdkDocEntry } from '../utils'
5
+
6
+ export const SPThemePlugin = Plugin.create({
7
+ name: 'SPThemePlugin',
8
+ docs: [createSdkDocEntry('SPTheme', ['sp_theme'])],
9
+ records: {
10
+ sp_theme: {
11
+ relationships: {
12
+ m2m_sp_theme_js_include: { via: 'sp_theme', descendant: true },
13
+ m2m_sp_theme_css_include: { via: 'sp_theme', descendant: true },
14
+ },
15
+ toShape(record, { descendants }) {
16
+ const cssIncludeRecords = descendants.query('m2m_sp_theme_css_include')
17
+ const cssIncludes = cssIncludeRecords
18
+ .map((m2m) => {
19
+ const orderValue = m2m.get('order')
20
+ const order =
21
+ (orderValue?.ifString()?.isEmpty() ? undefined : orderValue?.toNumber()?.getValue()) ??
22
+ DEFAULT_ORDER
23
+ return {
24
+ order,
25
+ include: m2m.get('sp_css_include'),
26
+ }
27
+ })
28
+ .sort((a, b) => a.order - b.order)
29
+
30
+ const jsIncludeRecords = descendants.query('m2m_sp_theme_js_include')
31
+ const jsIncludes = jsIncludeRecords
32
+ .map((m2m) => {
33
+ const orderValue = m2m.get('order')
34
+ const order =
35
+ (orderValue?.ifString()?.isEmpty() ? undefined : orderValue?.toNumber()?.getValue()) ??
36
+ DEFAULT_ORDER
37
+ return {
38
+ order,
39
+ include: m2m.get('sp_js_include'),
40
+ }
41
+ })
42
+ .sort((a, b) => a.order - b.order)
43
+
44
+ const themeOptions = record.transform(({ $ }) => ({
45
+ $id: $.val(NowIdShape.from(record)),
46
+ name: $,
47
+ customCss: $.from('css_variables').def(''),
48
+ header: $.def(''),
49
+ footer: $.def(''),
50
+ fixedHeader: $.from('navbar_fixed').toBoolean().def(true),
51
+ fixedFooter: $.from('footer_fixed').toBoolean().def(true),
52
+ turnOffScssCompilation: $.from('turn_off_scss_compilation').toBoolean().def(false),
53
+ matchingNextExperienceTheme: $.from('matching_now_experience_theme').def(''),
54
+ cssIncludes: $.val(cssIncludes.length > 0 ? cssIncludes : undefined),
55
+ jsIncludes: $.val(jsIncludes.length > 0 ? jsIncludes : undefined),
56
+ icon: $.def(''),
57
+ logo: $.def(''),
58
+ logoAltText: $.from('logo_alt_text').def(''),
59
+ }))
60
+
61
+ return {
62
+ success: true,
63
+ value: new CallExpressionShape({
64
+ source: record,
65
+ callee: 'SPTheme',
66
+ args: [themeOptions],
67
+ }),
68
+ }
69
+ },
70
+ },
71
+ m2m_sp_theme_css_include: {
72
+ coalesce: ['sp_css_include', 'sp_theme'],
73
+ },
74
+ m2m_sp_theme_js_include: {
75
+ coalesce: ['sp_js_include', 'sp_theme'],
76
+ },
77
+ },
78
+ shapes: [
79
+ {
80
+ shape: CallExpressionShape,
81
+ fileTypes: ['fluent'],
82
+ async toRecord(callExpression, { diagnostics, factory }) {
83
+ if (callExpression.getCallee() !== 'SPTheme') {
84
+ return { success: false }
85
+ }
86
+
87
+ const theme = callExpression.getArgument(0).asObject()
88
+
89
+ const themeRecord = await factory.createRecord({
90
+ source: callExpression,
91
+ table: 'sp_theme',
92
+ explicitId: theme.get('$id'),
93
+ properties: theme.transform(({ $ }) => ({
94
+ name: $,
95
+ css_variables: $.from('customCss').def(''),
96
+ header: $,
97
+ footer: $,
98
+ navbar_fixed: $.from('fixedHeader').def(true),
99
+ footer_fixed: $.from('fixedFooter').def(true),
100
+ turn_off_scss_compilation: $.from('turnOffScssCompilation').def(false),
101
+ matching_now_experience_theme: $.from('matchingNextExperienceTheme').def(''),
102
+ icon: $,
103
+ logo: $,
104
+ logo_alt_text: $.from('logoAltText').def(''),
105
+ })),
106
+ })
107
+
108
+ const cssIncludes = await getIncludeRecords(
109
+ theme.get('cssIncludes'),
110
+ themeRecord.getId(),
111
+ factory,
112
+ 'm2m_sp_theme_css_include',
113
+ 'sp_css_include',
114
+ 'sp_theme',
115
+ diagnostics
116
+ )
117
+
118
+ const jsIncludes = await getIncludeRecords(
119
+ theme.get('jsIncludes'),
120
+ themeRecord.getId(),
121
+ factory,
122
+ 'm2m_sp_theme_js_include',
123
+ 'sp_js_include',
124
+ 'sp_theme',
125
+ diagnostics
126
+ )
127
+
128
+ return {
129
+ success: true,
130
+ value: themeRecord.with(...cssIncludes, ...jsIncludes),
131
+ }
132
+ },
133
+ },
134
+ ],
135
+ })
@@ -0,0 +1,69 @@
1
+ import { NowIncludeShape } from '../now-include-plugin'
2
+ import { type Record, Shape, type RecordId, type Diagnostics, type Factory } from '@servicenow/sdk-build-core'
3
+
4
+ /**
5
+ * Creates a NowIncludeShape with a custom file path suffix and extension
6
+ */
7
+ export async function createIncludeShape(
8
+ record: Record,
9
+ content: string | Shape,
10
+ suffix: string,
11
+ extension: 'js' | 'html' | 'scss',
12
+ folderName: string
13
+ ): Promise<NowIncludeShape> {
14
+ const baseName = `sp_widget_${folderName}`
15
+ const includedText = content instanceof Shape ? content.toString().getValue() : content
16
+
17
+ return new NowIncludeShape({
18
+ source: record,
19
+ path: `./${baseName}/${suffix}.${extension}`,
20
+ includedText,
21
+ })
22
+ }
23
+
24
+ export const DEFAULT_ORDER = 100
25
+
26
+ export async function getIncludeRecords(
27
+ shape: Shape,
28
+ parentId: Shape,
29
+ factory: Factory,
30
+ m2mTable: string,
31
+ includeTable: string,
32
+ parentTable: string,
33
+ diagnostics: Diagnostics
34
+ ) {
35
+ return (
36
+ await Promise.all(
37
+ shape?.ifArray()?.map(async (include) => {
38
+ const includeProps = include.ifObject()?.get('include')
39
+
40
+ let includeId: string | RecordId
41
+ if (includeProps?.isString()) {
42
+ includeId = includeProps.toString().getValue()
43
+ } else if (includeProps?.isRecord()) {
44
+ includeId = includeProps.getId()
45
+ } else {
46
+ const necessaryPlugin = includeTable === 'sp_js_include' ? 'JsInclude' : 'CssInclude'
47
+ diagnostics.error(
48
+ includeProps!,
49
+ `include must contain a valid ${necessaryPlugin}, Record<'${includeTable}'> or sys_id'`
50
+ )
51
+ return undefined
52
+ }
53
+
54
+ const m2mProps = {
55
+ [includeTable]: includeId,
56
+ [parentTable]: parentId,
57
+ }
58
+ return await factory.createRecord({
59
+ source: shape,
60
+ table: m2mTable,
61
+ properties: {
62
+ order: include.ifObject()?.get('order').asNumber().getValue() || DEFAULT_ORDER,
63
+ ...m2mProps,
64
+ },
65
+ })
66
+ }) ?? []
67
+ )
68
+ ).filter((rec) => rec) as Record[]
69
+ }
@@ -3,9 +3,24 @@ import { noThrow, reverseObject } from '../utils'
3
3
  import { type SPTemplate, WidgetCategories } from '@servicenow/sdk-core/runtime/service-portal'
4
4
  import { NowIdShape } from '../now-id-plugin'
5
5
  import { NowIncludeShape } from '../now-include-plugin'
6
+ import { createSdkDocEntry } from '../utils'
7
+ import { createIncludeShape } from './utils'
8
+
9
+ function convertOptionSchemaKeys(optionSchema: unknown, convertKey: (key: string) => string): unknown {
10
+ if (!Array.isArray(optionSchema)) {
11
+ return optionSchema
12
+ }
13
+ return optionSchema.map((option: Record<string, unknown>) =>
14
+ Object.fromEntries(Object.entries(option).map(([k, v]) => [convertKey(k), v]))
15
+ )
16
+ }
17
+
18
+ const toCamelCase = (key: string) => key.replace(/_([a-z])/g, (_, c: string) => c.toUpperCase())
19
+ const toSnakeCase = (key: string) => key.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`)
6
20
 
7
21
  export const SPWidgetPlugin = Plugin.create({
8
22
  name: 'SPWidgetPlugin',
23
+ docs: [createSdkDocEntry('SPWidget', ['sp_widget'])],
9
24
  records: {
10
25
  sp_widget: {
11
26
  relationships: {
@@ -13,7 +28,10 @@ export const SPWidgetPlugin = Plugin.create({
13
28
  m2m_sp_ng_pro_sp_widget: { via: 'sp_widget', descendant: true },
14
29
  sp_ng_template: { via: 'sp_widget', descendant: true },
15
30
  },
16
- toShape(record, { descendants }) {
31
+ async toShape(record, { descendants }) {
32
+ const folderName =
33
+ record.get('id')?.asString()?.getValue()?.replace(/[\s-]/g, '_') ||
34
+ record.get('name')?.asString()?.getValue()?.toLowerCase().replace(/[\s-]/g, '_')
17
35
  const dependencies = descendants.query('m2m_sp_widget_dependency')
18
36
  const angularProviders = descendants.query('m2m_sp_ng_pro_sp_widget')
19
37
  const templates = descendants.query('sp_ng_template').map((template) => {
@@ -25,7 +43,29 @@ export const SPWidgetPlugin = Plugin.create({
25
43
  })
26
44
 
27
45
  const category = reverseObject(WidgetCategories)[record.get('category').ifString()?.getValue() || '']
28
-
46
+ const serverScript = await createIncludeShape(
47
+ record,
48
+ record.get('script'),
49
+ 'server_script',
50
+ 'js',
51
+ folderName
52
+ )
53
+ const clientScript = await createIncludeShape(
54
+ record,
55
+ record.get('client_script'),
56
+ 'client_script',
57
+ 'js',
58
+ folderName
59
+ )
60
+ const css = await createIncludeShape(record, record.get('css'), 'style', 'scss', folderName)
61
+ const htmlTemplate = await createIncludeShape(
62
+ record,
63
+ record.get('template'),
64
+ 'template',
65
+ 'html',
66
+ folderName
67
+ )
68
+ const linkScript = await createIncludeShape(record, record.get('link'), 'link-script', 'js', folderName)
29
69
  return {
30
70
  success: true,
31
71
  value: new CallExpressionShape({
@@ -36,11 +76,11 @@ export const SPWidgetPlugin = Plugin.create({
36
76
  $id: $.val(NowIdShape.from(record)),
37
77
  name: $,
38
78
  category: $.val(category).def('custom'),
39
- clientScript: $.from('client_script').def(''),
40
- serverScript: $.from('script').def(''),
79
+ clientScript: $.val(clientScript).def(''),
80
+ serverScript: $.val(serverScript).def(''),
41
81
  controllerAs: $.from('controller_as').def('c'),
42
- htmlTemplate: $.from('template').def(''),
43
- customCss: $.from('css').def(''),
82
+ htmlTemplate: $.val(htmlTemplate).def(''),
83
+ customCss: $.val(css).def(''),
44
84
  dataTable: $.from('data_table').def('sp_instance'),
45
85
  demoData: $.from('demo_data')
46
86
  .map((d) => {
@@ -61,7 +101,7 @@ export const SPWidgetPlugin = Plugin.create({
61
101
  hasPreview: $.from('has_preview').toBoolean().def(false),
62
102
  id: $.def(''),
63
103
  internal: $.toBoolean().def(false),
64
- linkScript: $.from('link').def(''),
104
+ linkScript: $.val(linkScript).def(''),
65
105
  roles: $.map((role) => {
66
106
  const roleStr = role.ifString()?.getValue()
67
107
  return roleStr && roleStr.split(',')
@@ -71,7 +111,8 @@ export const SPWidgetPlugin = Plugin.create({
71
111
  .map((v) => {
72
112
  const json = v.ifString()?.getValue()
73
113
  const parsed = noThrow(() => json && JSON.parse(json))
74
- return parsed instanceof Error ? json : parsed
114
+ const result = parsed instanceof Error ? json : parsed
115
+ return convertOptionSchemaKeys(result, toCamelCase)
75
116
  })
76
117
  .def(''),
77
118
  public: $.toBoolean().def(false),
@@ -133,6 +174,34 @@ export const SPWidgetPlugin = Plugin.create({
133
174
  )
134
175
  }
135
176
 
177
+ const clientScript = widget.get('clientScript')
178
+ const clientScriptValue =
179
+ clientScript instanceof NowIncludeShape
180
+ ? clientScript.getValue()
181
+ : clientScript.ifString()?.getValue()
182
+ if (clientScriptValue && clientScriptValue.trim().length > 0) {
183
+ const clientScriptPattern =
184
+ /^(function|api\.controller\s?=\s?function)\s?([$a-z_][$0-9a-z_]*)?\s?\(.*\)\s?\n?{/i
185
+ if (!clientScriptPattern.test(clientScriptValue.trim())) {
186
+ diagnostics.error(
187
+ clientScript.getOriginalNode(),
188
+ `Client controller must contain a JavaScript function. Example: api.controller = function($scope) { ... }`
189
+ )
190
+ }
191
+ }
192
+
193
+ const htmlTemplate = widget.get('htmlTemplate')
194
+ const htmlTemplateValue =
195
+ htmlTemplate instanceof NowIncludeShape
196
+ ? htmlTemplate.getValue()
197
+ : htmlTemplate.ifString()?.getValue()
198
+ if (htmlTemplateValue && htmlTemplateValue.length > 0 && htmlTemplateValue.indexOf('href="#"') > 0) {
199
+ diagnostics.error(
200
+ htmlTemplate.getOriginalNode(),
201
+ `Do not use href="#" in the Service Portal, use href="javascript:void(0)" instead`
202
+ )
203
+ }
204
+
136
205
  const roles = widget
137
206
  .get('roles')
138
207
  .ifArray()
@@ -200,7 +269,8 @@ export const SPWidgetPlugin = Plugin.create({
200
269
  option_schema: $.from('optionSchema')
201
270
  .map((v) => {
202
271
  const json = v.getValue()
203
- return json && JSON.stringify(json)
272
+ const converted = convertOptionSchemaKeys(json, toSnakeCase)
273
+ return converted && JSON.stringify(converted)
204
274
  })
205
275
  .def(''),
206
276
  public: $.def(false),