@servicenow/sdk-build-plugins 4.5.0 → 4.6.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 (175) hide show
  1. package/dist/acl-plugin.js +3 -1
  2. package/dist/acl-plugin.js.map +1 -1
  3. package/dist/atf/test-plugin.js +6 -8
  4. package/dist/atf/test-plugin.js.map +1 -1
  5. package/dist/basic-syntax-plugin.js +10 -3
  6. package/dist/basic-syntax-plugin.js.map +1 -1
  7. package/dist/column-plugin.js +99 -49
  8. package/dist/column-plugin.js.map +1 -1
  9. package/dist/flow/flow-logic/flow-logic-diagnostics.js +5 -5
  10. package/dist/flow/flow-logic/flow-logic-diagnostics.js.map +1 -1
  11. package/dist/flow/plugins/flow-action-definition-plugin.js +1229 -54
  12. package/dist/flow/plugins/flow-action-definition-plugin.js.map +1 -1
  13. package/dist/flow/plugins/flow-data-pill-plugin.js +5 -2
  14. package/dist/flow/plugins/flow-data-pill-plugin.js.map +1 -1
  15. package/dist/flow/plugins/flow-definition-plugin.js +16 -42
  16. package/dist/flow/plugins/flow-definition-plugin.js.map +1 -1
  17. package/dist/flow/plugins/flow-diagnostics-plugin.d.ts +2 -2
  18. package/dist/flow/plugins/flow-diagnostics-plugin.js +2 -2
  19. package/dist/flow/plugins/flow-instance-plugin.js +68 -22
  20. package/dist/flow/plugins/flow-instance-plugin.js.map +1 -1
  21. package/dist/flow/plugins/step-definition-plugin.js +2 -1
  22. package/dist/flow/plugins/step-definition-plugin.js.map +1 -1
  23. package/dist/flow/plugins/step-instance-plugin.d.ts +9 -1
  24. package/dist/flow/plugins/step-instance-plugin.js +649 -136
  25. package/dist/flow/plugins/step-instance-plugin.js.map +1 -1
  26. package/dist/flow/plugins/wfa-datapill-plugin.js +20 -5
  27. package/dist/flow/plugins/wfa-datapill-plugin.js.map +1 -1
  28. package/dist/flow/post-install.js +1 -0
  29. package/dist/flow/post-install.js.map +1 -1
  30. package/dist/flow/utils/complex-object-resolver.js +4 -1
  31. package/dist/flow/utils/complex-object-resolver.js.map +1 -1
  32. package/dist/flow/utils/complex-objects.js +1 -1
  33. package/dist/flow/utils/complex-objects.js.map +1 -1
  34. package/dist/flow/utils/flow-constants.d.ts +66 -2
  35. package/dist/flow/utils/flow-constants.js +402 -6
  36. package/dist/flow/utils/flow-constants.js.map +1 -1
  37. package/dist/flow/utils/flow-io-to-record.d.ts +1 -1
  38. package/dist/flow/utils/flow-io-to-record.js +37 -16
  39. package/dist/flow/utils/flow-io-to-record.js.map +1 -1
  40. package/dist/flow/utils/flow-shapes.js +4 -0
  41. package/dist/flow/utils/flow-shapes.js.map +1 -1
  42. package/dist/flow/utils/label-cache-parser.d.ts +9 -2
  43. package/dist/flow/utils/label-cache-parser.js +32 -4
  44. package/dist/flow/utils/label-cache-parser.js.map +1 -1
  45. package/dist/flow/utils/pill-shape-helpers.d.ts +15 -0
  46. package/dist/flow/utils/pill-shape-helpers.js +35 -0
  47. package/dist/flow/utils/pill-shape-helpers.js.map +1 -0
  48. package/dist/flow/utils/pill-string-parser.js +1 -0
  49. package/dist/flow/utils/pill-string-parser.js.map +1 -1
  50. package/dist/flow/utils/schema-to-flow-object.d.ts +6 -1
  51. package/dist/flow/utils/schema-to-flow-object.js +131 -15
  52. package/dist/flow/utils/schema-to-flow-object.js.map +1 -1
  53. package/dist/flow/utils/utils.d.ts +1 -0
  54. package/dist/flow/utils/utils.js +6 -1
  55. package/dist/flow/utils/utils.js.map +1 -1
  56. package/dist/form-plugin.js +7 -9
  57. package/dist/form-plugin.js.map +1 -1
  58. package/dist/inbound-email-action-plugin.d.ts +10 -0
  59. package/dist/inbound-email-action-plugin.js +128 -0
  60. package/dist/inbound-email-action-plugin.js.map +1 -0
  61. package/dist/index.d.ts +4 -0
  62. package/dist/index.js +4 -0
  63. package/dist/index.js.map +1 -1
  64. package/dist/instance-scan-plugin.js +0 -5
  65. package/dist/instance-scan-plugin.js.map +1 -1
  66. package/dist/now-config-plugin.js +1 -0
  67. package/dist/now-config-plugin.js.map +1 -1
  68. package/dist/property-plugin.js +1 -1
  69. package/dist/property-plugin.js.map +1 -1
  70. package/dist/record-plugin.d.ts +7 -0
  71. package/dist/record-plugin.js +13 -4
  72. package/dist/record-plugin.js.map +1 -1
  73. package/dist/rest-api-plugin.js +8 -1
  74. package/dist/rest-api-plugin.js.map +1 -1
  75. package/dist/schedule-script/scheduled-script-plugin.js +8 -3
  76. package/dist/schedule-script/scheduled-script-plugin.js.map +1 -1
  77. package/dist/script-include-plugin.js +4 -0
  78. package/dist/script-include-plugin.js.map +1 -1
  79. package/dist/service-catalog/catalog-clientscript-plugin.js +2 -2
  80. package/dist/service-catalog/catalog-clientscript-plugin.js.map +1 -1
  81. package/dist/service-catalog/catalog-ui-policy-plugin.js +2 -2
  82. package/dist/service-catalog/catalog-ui-policy-plugin.js.map +1 -1
  83. package/dist/service-catalog/service-catalog-base.d.ts +20 -20
  84. package/dist/service-catalog/service-catalog-base.js +24 -24
  85. package/dist/service-catalog/service-catalog-base.js.map +1 -1
  86. package/dist/service-catalog/utils.js +1 -1
  87. package/dist/service-catalog/utils.js.map +1 -1
  88. package/dist/service-portal/header-footer-plugin.d.ts +2 -0
  89. package/dist/service-portal/header-footer-plugin.js +50 -0
  90. package/dist/service-portal/header-footer-plugin.js.map +1 -0
  91. package/dist/service-portal/menu-plugin.js +3 -22
  92. package/dist/service-portal/menu-plugin.js.map +1 -1
  93. package/dist/service-portal/page-plugin.js +3 -24
  94. package/dist/service-portal/page-plugin.js.map +1 -1
  95. package/dist/service-portal/page-route-map-plugin.d.ts +2 -0
  96. package/dist/service-portal/page-route-map-plugin.js +114 -0
  97. package/dist/service-portal/page-route-map-plugin.js.map +1 -0
  98. package/dist/service-portal/portal-plugin.js +21 -8
  99. package/dist/service-portal/portal-plugin.js.map +1 -1
  100. package/dist/service-portal/utils.d.ts +40 -2
  101. package/dist/service-portal/utils.js +283 -2
  102. package/dist/service-portal/utils.js.map +1 -1
  103. package/dist/service-portal/widget-plugin.js +9 -218
  104. package/dist/service-portal/widget-plugin.js.map +1 -1
  105. package/dist/static-content-plugin.js +4 -0
  106. package/dist/static-content-plugin.js.map +1 -1
  107. package/dist/table-plugin.js +377 -67
  108. package/dist/table-plugin.js.map +1 -1
  109. package/dist/ui-action-plugin.js +1 -4
  110. package/dist/ui-action-plugin.js.map +1 -1
  111. package/dist/ui-page-plugin.js +68 -13
  112. package/dist/ui-page-plugin.js.map +1 -1
  113. package/dist/ui-policy-plugin.js +28 -96
  114. package/dist/ui-policy-plugin.js.map +1 -1
  115. package/dist/utils.d.ts +5 -1
  116. package/dist/utils.js +41 -0
  117. package/dist/utils.js.map +1 -1
  118. package/dist/view-plugin.js +8 -3
  119. package/dist/view-plugin.js.map +1 -1
  120. package/dist/workspace-plugin.js +39 -36
  121. package/dist/workspace-plugin.js.map +1 -1
  122. package/package.json +5 -4
  123. package/src/acl-plugin.ts +3 -1
  124. package/src/atf/test-plugin.ts +6 -9
  125. package/src/basic-syntax-plugin.ts +11 -3
  126. package/src/column-plugin.ts +137 -75
  127. package/src/flow/flow-logic/flow-logic-diagnostics.ts +5 -6
  128. package/src/flow/plugins/flow-action-definition-plugin.ts +1581 -61
  129. package/src/flow/plugins/flow-data-pill-plugin.ts +5 -2
  130. package/src/flow/plugins/flow-definition-plugin.ts +12 -47
  131. package/src/flow/plugins/flow-diagnostics-plugin.ts +2 -2
  132. package/src/flow/plugins/flow-instance-plugin.ts +98 -22
  133. package/src/flow/plugins/step-definition-plugin.ts +2 -1
  134. package/src/flow/plugins/step-instance-plugin.ts +772 -156
  135. package/src/flow/plugins/wfa-datapill-plugin.ts +25 -5
  136. package/src/flow/post-install.ts +1 -0
  137. package/src/flow/utils/complex-object-resolver.ts +4 -1
  138. package/src/flow/utils/complex-objects.ts +1 -1
  139. package/src/flow/utils/flow-constants.ts +421 -5
  140. package/src/flow/utils/flow-io-to-record.ts +43 -17
  141. package/src/flow/utils/flow-shapes.ts +4 -0
  142. package/src/flow/utils/label-cache-parser.ts +33 -4
  143. package/src/flow/utils/pill-shape-helpers.ts +42 -0
  144. package/src/flow/utils/pill-string-parser.ts +1 -0
  145. package/src/flow/utils/schema-to-flow-object.ts +183 -15
  146. package/src/flow/utils/utils.ts +12 -1
  147. package/src/form-plugin.ts +1 -3
  148. package/src/inbound-email-action-plugin.ts +145 -0
  149. package/src/index.ts +4 -0
  150. package/src/instance-scan-plugin.ts +0 -5
  151. package/src/now-config-plugin.ts +1 -0
  152. package/src/property-plugin.ts +4 -1
  153. package/src/record-plugin.ts +25 -7
  154. package/src/rest-api-plugin.ts +7 -1
  155. package/src/schedule-script/scheduled-script-plugin.ts +14 -3
  156. package/src/script-include-plugin.ts +8 -0
  157. package/src/service-catalog/catalog-clientscript-plugin.ts +2 -2
  158. package/src/service-catalog/catalog-ui-policy-plugin.ts +2 -2
  159. package/src/service-catalog/service-catalog-base.ts +24 -24
  160. package/src/service-catalog/utils.ts +1 -1
  161. package/src/service-portal/header-footer-plugin.ts +57 -0
  162. package/src/service-portal/menu-plugin.ts +1 -23
  163. package/src/service-portal/page-plugin.ts +3 -28
  164. package/src/service-portal/page-route-map-plugin.ts +124 -0
  165. package/src/service-portal/portal-plugin.ts +33 -10
  166. package/src/service-portal/utils.ts +404 -3
  167. package/src/service-portal/widget-plugin.ts +14 -290
  168. package/src/static-content-plugin.ts +3 -0
  169. package/src/table-plugin.ts +466 -99
  170. package/src/ui-action-plugin.ts +1 -8
  171. package/src/ui-page-plugin.ts +76 -13
  172. package/src/ui-policy-plugin.ts +32 -128
  173. package/src/utils.ts +52 -0
  174. package/src/view-plugin.ts +10 -4
  175. package/src/workspace-plugin.ts +43 -43
@@ -1,4 +1,11 @@
1
- import { CallExpressionShape, Database, Plugin, unloadBuilder, type Record } from '@servicenow/sdk-build-core'
1
+ import {
2
+ CallExpressionShape,
3
+ Database,
4
+ NowConfig,
5
+ Plugin,
6
+ unloadBuilder,
7
+ type Record,
8
+ } from '@servicenow/sdk-build-core'
2
9
  import { NowIdShape } from './now-id-plugin'
3
10
  import { createSdkDocEntry } from './utils'
4
11
  import {
@@ -16,6 +23,7 @@ import {
16
23
  import {
17
24
  ServicePortal,
18
25
  SPWidget,
26
+ SPHeaderFooter,
19
27
  SPAngularProvider,
20
28
  SPWidgetDependency,
21
29
  CssInclude,
@@ -23,6 +31,7 @@ import {
23
31
  SPPage,
24
32
  SPTheme,
25
33
  SPMenu,
34
+ SPPageRouteMap,
26
35
  } from '@servicenow/sdk-core/runtime/service-portal'
27
36
  import {
28
37
  CatalogItem,
@@ -33,11 +42,12 @@ import {
33
42
  } from '@servicenow/sdk-core/runtime/service-catalog'
34
43
  import { ClientScript } from '@servicenow/sdk-core/runtime/clientscript'
35
44
  import { ScriptAction, ScriptInclude, ScheduledScript } from '@servicenow/sdk-core/runtime/sys'
36
- import { List } from '@servicenow/sdk-core/runtime/ui'
45
+ import { List, Form } from '@servicenow/sdk-core/runtime/ui'
37
46
  import { Table } from '@servicenow/sdk-core/runtime/db'
38
47
  import { RestApi } from '@servicenow/sdk-core/runtime/rest'
39
- import { EmailNotification } from '@servicenow/sdk-core/runtime/notification'
48
+ import { EmailNotification, InboundEmailAction } from '@servicenow/sdk-core/runtime/notification'
40
49
  import { UiAction, UiPage, UiPolicy } from '@servicenow/sdk-core/runtime/ui'
50
+ import { ActionStepDefinition } from '@servicenow/sdk-core/runtime/flow'
41
51
  import { Applicability, UxListMenuConfig, Workspace } from '@servicenow/sdk-core/runtime/uxf'
42
52
  import { Dashboard } from '@servicenow/sdk-core/runtime/dashboard'
43
53
  import { ColumnTypeCheck, LinterCheck, ScriptOnlyCheck, TableCheck } from '@servicenow/sdk-core/runtime/instancescan'
@@ -113,9 +123,10 @@ export const RecordPlugin = Plugin.create({
113
123
  }
114
124
  },
115
125
  async toFile(record, { config, database, transform }) {
126
+ const { scope, scopeId } = NowConfig.getRecordScope(config, record.getOriginalFilePath())
116
127
  const recordBuilder = unloadBuilder({
117
- scope: config.scope,
118
- scopeId: config.scopeId,
128
+ scope,
129
+ scopeId,
119
130
  table: record.getTable(),
120
131
  })
121
132
  const updateName = await transform.getUpdateName(record)
@@ -223,6 +234,10 @@ export const TableOwnership = {
223
234
  sys_script: BusinessRule.name,
224
235
  sys_script_client: ClientScript.name,
225
236
  sys_scope_privilege: CrossScopePrivilege.name,
237
+ sys_ui_form: Form.name,
238
+ sys_ui_section: Form.name,
239
+ sys_ui_form_section: Form.name,
240
+ sys_ui_element: Form.name,
226
241
  sys_ui_list_element: List.name,
227
242
  sys_ui_list: List.name,
228
243
  sys_properties: Property.name,
@@ -230,6 +245,7 @@ export const TableOwnership = {
230
245
  sys_user_role_contains: Role.name,
231
246
  sys_script_include: ScriptInclude.name,
232
247
  sp_widget: SPWidget.name,
248
+ sp_header_footer: SPHeaderFooter.name,
233
249
  m2m_sp_widget_dependency: SPWidget.name,
234
250
  m2m_sp_ng_pro_sp_widget: SPWidget.name,
235
251
  sp_portal: ServicePortal.name,
@@ -248,6 +264,7 @@ export const TableOwnership = {
248
264
  m2m_sp_ng_pro_sp_ng_pro: SPAngularProvider.name,
249
265
  sp_instance_menu: SPMenu.name,
250
266
  sp_rectangle_menu_item: SPMenu.name,
267
+ sp_page_route_map: SPPageRouteMap.name,
251
268
  sys_atf_test: Test.name,
252
269
  sys_ws_header_map: RestApi.name,
253
270
  sys_ws_query_parameter_map: RestApi.name,
@@ -267,6 +284,7 @@ export const TableOwnership = {
267
284
  sysevent_script_action: ScriptAction.name,
268
285
  sysauto_script: ScheduledScript.name,
269
286
  sysevent_email_action: EmailNotification.name,
287
+ sysevent_in_email_action: InboundEmailAction.name,
270
288
  sys_db_object: Table.name,
271
289
  sys_dictionary: Table.name,
272
290
  sys_hub_flow: 'Flow',
@@ -274,10 +292,10 @@ export const TableOwnership = {
274
292
  sys_hub_action_instance_v2: 'wfa.action',
275
293
  sys_hub_sub_flow_instance_v2: 'wfa.subflow',
276
294
  sys_hub_flow_logic_instance_v2: 'wfa.flowLogic',
277
- sys_flow_step_definition: 'ActionStepDefinition',
295
+ sys_flow_step_definition: ActionStepDefinition.name,
278
296
  sys_decision: 'DecisionTablePlugin',
279
297
  sys_hub_action_type_definition: 'Action',
280
- sys_hub_step_instance: 'wfa.action_step',
298
+ sys_hub_step_instance: 'wfa.actionStep',
281
299
  sp_theme: SPTheme.name,
282
300
  m2m_sp_theme_css_include: SPTheme.name,
283
301
  m2m_sp_theme_js_include: SPTheme.name,
@@ -183,6 +183,7 @@ async function generateRouteAttributeRecords(
183
183
  installCategory: restDef.getInstallCategory(),
184
184
  })
185
185
 
186
+ const isShared = attrMap.has(attrRecord.getId().getValue())
186
187
  checkForDuplicateRecords(attrMap, attrRecord, attr.asObject(), diagnostics)
187
188
 
188
189
  const attributeMappingRecord = await factory.createRecord({
@@ -195,7 +196,12 @@ async function generateRouteAttributeRecords(
195
196
  installCategory: restDef.getInstallCategory(),
196
197
  })
197
198
 
198
- records.push(attrRecord.with(attributeMappingRecord))
199
+ if (isShared) {
200
+ // The attrRecord was already emitted by a previous route — only emit the mapping
201
+ records.push(attributeMappingRecord)
202
+ } else {
203
+ records.push(attrRecord.with(attributeMappingRecord))
204
+ }
199
205
  }
200
206
 
201
207
  return records
@@ -9,6 +9,7 @@ import {
9
9
  } from '@servicenow/sdk-build-core'
10
10
  import { NowIdShape } from '../now-id-plugin'
11
11
  import { NowIncludeShape } from '../now-include-plugin'
12
+ import { ModuleFunctionShape } from '../server-module-plugin'
12
13
  import { createSdkDocEntry, toReference } from '../utils'
13
14
  import { dateTimeFieldToXML, convertXMLToDateTime, formatTimeDataToDateTime, formatToUTC } from './timeZoneConverter'
14
15
 
@@ -444,13 +445,21 @@ export const ScheduledScriptPlugin = Plugin.create({
444
445
  {
445
446
  shape: CallExpressionShape,
446
447
  fileTypes: ['fluent'],
447
- async toRecord(callExpression, { factory, diagnostics }) {
448
+ async toRecord(callExpression, { factory, diagnostics, config }) {
448
449
  if (callExpression.getCallee() !== 'ScheduledScript') {
449
450
  return { success: false }
450
451
  }
451
452
 
452
453
  const args = callExpression.getArgument(0).asObject()
453
454
 
455
+ // Check if script is unresolved module
456
+ if (args.get('script').isUnresolved()) {
457
+ diagnostics.error(
458
+ args.get('script').getOriginalNode(),
459
+ `Unable to resolve the script reference, ensure the imported module is within the ${config.serverModulesDir} directory.`
460
+ )
461
+ }
462
+
454
463
  // Validate executionInterval is defined when frequency is 'periodically'
455
464
  const frequency = args.get('frequency').ifString()?.getValue()
456
465
  if (frequency === 'periodically' && args.get('executionInterval').isUndefined()) {
@@ -667,8 +676,10 @@ export const ScheduledScriptPlugin = Plugin.create({
667
676
  // Protection policy
668
677
  sys_policy: $.from('protectionPolicy'),
669
678
 
670
- // Script
671
- script: $.toCdata(),
679
+ // Script - handle both module functions and strings
680
+ script: $.map(
681
+ (v) => v.if(ModuleFunctionShape)?.toString((n) => `${n}({{PARAMS}})`, []) ?? v
682
+ ).toCdata(),
672
683
  })),
673
684
  })
674
685
 
@@ -61,6 +61,14 @@ export const ScriptIncludePlugin = Plugin.create({
61
61
  diagnostics.error(name.getOriginalNode(), `name must be a valid JavaScript identifier`)
62
62
  }
63
63
 
64
+ const callerAccess = prop.get('callerAccess')
65
+ if (callerAccess.isDefined() && prop.get('accessibleFrom').ifString()?.getValue() !== 'public') {
66
+ diagnostics.hint(
67
+ callerAccess.getOriginalNode(),
68
+ `'callerAccess' is only effective when 'accessibleFrom' is set to 'public'. It is ignored by the platform otherwise.`
69
+ )
70
+ }
71
+
64
72
  const apiName = prop.get('apiName')
65
73
 
66
74
  if (apiName.isDefined()) {
@@ -58,7 +58,7 @@ export const CatalogClientScriptPlugin = Plugin.create({
58
58
  .map((v) =>
59
59
  v.ifString()?.isEmpty() ? undefined : getUITypeFromId(v.toNumber()?.getValue())
60
60
  )
61
- .def('desktop'),
61
+ .def('all'),
62
62
  catalogItem: $.val(catalogItemReference).def(''),
63
63
  variableName: $.val(propertyAccess).def(''),
64
64
  variableSet: $.val(variableSetReference).def(''),
@@ -130,7 +130,7 @@ export const CatalogClientScriptPlugin = Plugin.create({
130
130
  isolate_script: $.from('isolateScript').def(true),
131
131
  ui_type: $.from('uiType')
132
132
  .map((v) => (v.isString() && !v.isEmpty() ? getUITypeId(v.getValue()) : undefined))
133
- .def(0),
133
+ .def(10),
134
134
  va_supported: $.from('vaSupported').toBoolean().def(false),
135
135
  published_ref: $.from('publishedRef').def(''),
136
136
  })),
@@ -166,7 +166,7 @@ export const CatalogUiPolicyPlugin = Plugin.create({
166
166
  .map((v) =>
167
167
  v.ifString()?.isEmpty() ? undefined : getUITypeFromId(v.toNumber()?.getValue())
168
168
  )
169
- .def('desktop'),
169
+ .def('all'),
170
170
  actions: $.val(actions.length > 0 ? actions : []).def([]),
171
171
  })),
172
172
  ],
@@ -239,7 +239,7 @@ export const CatalogUiPolicyPlugin = Plugin.create({
239
239
  published_ref: $.from('publishedRef').def(''),
240
240
  ui_type: $.from('runScriptsInUiType')
241
241
  .map((v) => (v.isString() && !v.isEmpty() ? getUITypeId(v.getValue()) : undefined))
242
- .def(0),
242
+ .def(10),
243
243
  table: $.def(''),
244
244
  va_supported: $.from('vaSupported').def(true),
245
245
  view: $.def(''),
@@ -255,6 +255,18 @@ export function transformCatalogBaseFieldsToShape(record: Record, $: ShapeTransf
255
255
  visibleBundle: $.from('visible_bundle').toBoolean().def(true),
256
256
  visibleGuide: $.from('visible_guide').toBoolean().def(true),
257
257
  visibleStandalone: $.from('visible_standalone').toBoolean().def(true),
258
+ hideAddToCart: $.from('no_cart_v2').toBoolean().def(false),
259
+ hideAddToWishList: $.from('no_wishlist_v2').toBoolean().def(false),
260
+ hideDeliveryTime: $.from('no_delivery_time_v2').toBoolean().def(false),
261
+ hideQuantitySelector: $.from('no_quantity_v2').toBoolean().def(false),
262
+ hideSP: $.from('hide_sp').toBoolean().def(false),
263
+ image: $.def(''),
264
+ model: $.def(''),
265
+ mobilePictureType: $.from('mobile_picture_type')
266
+ .map((v: Shape) => getMobilePictureTypeFromDb(v.toString().getValue()))
267
+ .def('desktopPicture'),
268
+ makeItemNonConversational: $.from('make_item_non_conversational').toBoolean().def(false),
269
+ noSearch: $.from('no_search').toBoolean().def(false),
258
270
  }
259
271
  }
260
272
 
@@ -327,6 +339,18 @@ export function transformCatalogItemBaseFieldsToRecord($: ShapeTransform) {
327
339
  visible_bundle: $.from('visibleBundle').def(true),
328
340
  visible_guide: $.from('visibleGuide').def(true),
329
341
  visible_standalone: $.from('visibleStandalone').def(true),
342
+ no_cart_v2: $.from('hideAddToCart').def(false),
343
+ no_wishlist_v2: $.from('hideAddToWishList').def(false),
344
+ no_delivery_time_v2: $.from('hideDeliveryTime').def(false),
345
+ no_quantity_v2: $.from('hideQuantitySelector').def(false),
346
+ hide_sp: $.from('hideSP').def(false),
347
+ image: $.def(''),
348
+ model: $.def(''),
349
+ mobile_picture_type: $.from('mobilePictureType')
350
+ .map((v: Shape) => getMobilePictureTypeToDb(v.toString().getValue()))
351
+ .def('use_desktop_picture'),
352
+ make_item_non_conversational: $.from('makeItemNonConversational').def(false),
353
+ no_search: $.from('noSearch').def(false),
330
354
  }
331
355
  }
332
356
 
@@ -375,9 +399,6 @@ export function transformCatalogItemSpecificFieldsToShape(record: Record, $: Sha
375
399
  requestMethod: $.from('request_method')
376
400
  .map((v: Shape) => (v.ifString()?.isEmpty() ? undefined : v.getValue()))
377
401
  .def('order'),
378
- hideAddToCart: $.from('no_cart_v2').toBoolean().def(false),
379
- hideQuantitySelector: $.from('no_quantity_v2').toBoolean().def(false),
380
- hideDeliveryTime: $.from('no_delivery_time_v2').toBoolean().def(false),
381
402
  customCart: $.from('custom_cart').def(''),
382
403
  recurringFrequency: $.from('recurring_frequency').def(''),
383
404
 
@@ -387,17 +408,8 @@ export function transformCatalogItemSpecificFieldsToShape(record: Record, $: Sha
387
408
  noOrderNow: $.from('no_order_now').toBoolean().def(false),
388
409
  noProceedCheckout: $.from('no_proceed_checkout').toBoolean().def(false),
389
410
  noQuantity: $.from('no_quantity').toBoolean().def(false),
390
- noSearch: $.from('no_search').toBoolean().def(false),
391
- hideAddToWishList: $.from('no_wishlist_v2').toBoolean().def(false),
392
411
 
393
412
  // Additional fields
394
- hideSP: $.from('hide_sp').toBoolean().def(false),
395
- image: $.def(''),
396
- makeItemNonConversational: $.from('make_item_non_conversational').toBoolean().def(false),
397
- mobilePictureType: $.from('mobile_picture_type')
398
- .map((v: Shape) => getMobilePictureTypeFromDb(v.toString().getValue()))
399
- .def('desktopPicture'),
400
- model: $.def(''),
401
413
  vendor: $.def(''),
402
414
  location: $.def(''),
403
415
  }
@@ -448,9 +460,6 @@ export function transformCatalogItemSpecificFieldsToRecord(arg: ObjectShape, $:
448
460
  request_method: $.from('requestMethod')
449
461
  .map((v: Shape) => (v.ifString()?.getValue() === 'order' ? '' : v.getValue()))
450
462
  .def(''),
451
- no_cart_v2: $.from('hideAddToCart').def(false),
452
- no_quantity_v2: $.from('hideQuantitySelector').def(false),
453
- no_delivery_time_v2: $.from('hideDeliveryTime').def(false),
454
463
  custom_cart: $.from('customCart').def(''),
455
464
  recurring_frequency: $.from('recurringFrequency').def(''),
456
465
 
@@ -460,17 +469,8 @@ export function transformCatalogItemSpecificFieldsToRecord(arg: ObjectShape, $:
460
469
  no_order_now: $.from('noOrderNow').def(false),
461
470
  no_proceed_checkout: $.from('noProceedCheckout').def(false),
462
471
  no_quantity: $.from('noQuantity').def(false),
463
- no_search: $.from('noSearch').def(false),
464
- no_wishlist_v2: $.from('hideAddToWishList').def(false),
465
472
 
466
473
  // Additional
467
- hide_sp: $.from('hideSP').def(false),
468
- image: $.def(''),
469
- make_item_non_conversational: $.from('makeItemNonConversational').def(false),
470
- mobile_picture_type: $.from('mobilePictureType')
471
- .map((v: Shape) => getMobilePictureTypeToDb(v.toString().getValue()))
472
- .def('use_desktop_picture'),
473
- model: $.def(''),
474
474
  vendor: $.def(''),
475
475
  location: $.def(''),
476
476
  }
@@ -318,7 +318,7 @@ export function getUITypeId(value: string): number {
318
318
  case 'all':
319
319
  return 10
320
320
  default:
321
- return 0 // Default to desktop
321
+ return 10 // Default to all
322
322
  }
323
323
  }
324
324
 
@@ -0,0 +1,57 @@
1
+ import { CallExpressionShape, Plugin } from '@servicenow/sdk-build-core'
2
+ import { createSdkDocEntry } from '../utils'
3
+ import { createWidgetToRecord, createWidgetToShape } from './utils'
4
+ import {
5
+ getDefaultClientScript,
6
+ SP_WIDGET_DEFAULT_HTML_TEMPLATE,
7
+ SP_WIDGET_DEFAULT_LINK_SCRIPT,
8
+ SP_WIDGET_DEFAULT_SERVER_SCRIPT,
9
+ } from './widget-plugin'
10
+
11
+ export const SPHeaderFooterPlugin = Plugin.create({
12
+ name: 'SPHeaderFooterPlugin',
13
+ docs: [createSdkDocEntry('SPHeaderFooter', ['sp_header_footer'])],
14
+ records: {
15
+ sp_header_footer: {
16
+ relationships: {
17
+ m2m_sp_widget_dependency: { via: 'sp_widget', descendant: true },
18
+ m2m_sp_ng_pro_sp_widget: { via: 'sp_widget', descendant: true },
19
+ sp_ng_template: { via: 'sp_widget', descendant: true },
20
+ },
21
+ async toShape(record, { descendants }) {
22
+ return createWidgetToShape(record, descendants, 'SPHeaderFooter', 'sp_header_footer', true)
23
+ },
24
+ },
25
+ m2m_sp_widget_dependency: {
26
+ coalesce: ['sp_widget', 'sp_dependency'],
27
+ },
28
+ m2m_sp_ng_pro_sp_widget: {
29
+ coalesce: ['sp_widget', 'sp_angular_provider'],
30
+ },
31
+ },
32
+ shapes: [
33
+ {
34
+ shape: CallExpressionShape,
35
+ fileTypes: ['fluent'],
36
+ async toRecord(callExpression, { diagnostics, factory, config }) {
37
+ if (callExpression.getCallee() !== 'SPHeaderFooter') {
38
+ return { success: false }
39
+ }
40
+
41
+ return createWidgetToRecord(
42
+ callExpression,
43
+ { diagnostics, factory, config },
44
+ {
45
+ callee: 'SPHeaderFooter',
46
+ table: 'sp_header_footer',
47
+ includeStaticField: true,
48
+ getDefaultClientScript,
49
+ defaultServerScript: SP_WIDGET_DEFAULT_SERVER_SCRIPT,
50
+ defaultLinkScript: SP_WIDGET_DEFAULT_LINK_SCRIPT,
51
+ defaultHtmlTemplate: SP_WIDGET_DEFAULT_HTML_TEMPLATE,
52
+ }
53
+ )
54
+ },
55
+ },
56
+ ],
57
+ })
@@ -10,6 +10,7 @@ import {
10
10
  import { NowIdShape } from '../now-id-plugin'
11
11
  import { NowIncludeShape } from '../now-include-plugin'
12
12
  import { toReference, getFieldAsNumber, noThrow, createSdkDocEntry } from '../utils'
13
+ import { getRolesString } from './utils'
13
14
 
14
15
  const default_placeholder_dimensions = `{
15
16
  "mobile": {
@@ -73,29 +74,6 @@ const defaultValues = {
73
74
  },
74
75
  }
75
76
 
76
- function getRolesString(rolesShape: Shape, diagnostics: Diagnostics): string {
77
- const roles = rolesShape
78
- ?.ifArray()
79
- ?.map((role) => {
80
- if (role.isString()) {
81
- return role.getValue()
82
- }
83
- if (role.isRecord()) {
84
- const name = role.get('name')
85
- if (name?.isString()) {
86
- return name.getValue()
87
- }
88
- return role.getId()
89
- }
90
-
91
- diagnostics.error(role, `roles must be strings or role records`)
92
- return undefined
93
- })
94
- .filter((r) => r) as string[] | undefined
95
-
96
- return roles?.toString() ?? ''
97
- }
98
-
99
77
  const addProperty = (obj: { [key: string]: unknown }, key: string, value: unknown) => {
100
78
  if (
101
79
  value === undefined ||
@@ -6,11 +6,11 @@ import {
6
6
  type Record,
7
7
  type RecordId,
8
8
  type Shape,
9
- type ArrayShape,
10
9
  type ObjectShape,
11
10
  } from '@servicenow/sdk-build-core'
12
11
  import isEmpty from 'lodash/isEmpty'
13
12
  import { toReference, getFieldAsNumber, createSdkDocEntry } from '../utils'
13
+ import { getRolesString } from './utils'
14
14
 
15
15
  type Dict = { [key: string]: unknown }
16
16
 
@@ -292,29 +292,6 @@ function getInstanceObject(instance: Record): object | undefined {
292
292
  return instanceObject
293
293
  }
294
294
 
295
- /**
296
- * Converts an ArrayShape of roles to a comma-separated string.
297
- * Handles both role record objects and string values, removes duplicates and empty values.
298
- * @param roles - ArrayShape containing role records or strings
299
- * @returns Comma-separated string of unique role names or undefined if no roles
300
- */
301
- function getRolesString(roles: ArrayShape | undefined): string | undefined {
302
- if (!roles) {
303
- return
304
- }
305
- const uniqueRoles = [
306
- ...new Set(
307
- roles.getElements().map((role) => {
308
- const roleName = role.isRecord()
309
- ? role.get('name')?.ifString()?.getValue()
310
- : role.ifString()?.getValue()
311
- return (roleName ?? '').trim()
312
- })
313
- ),
314
- ]
315
- return uniqueRoles.filter((role) => role !== '').join(',')
316
- }
317
-
318
295
  /**
319
296
  * Recursively processes nested rows within a column
320
297
  * @param columnRows - Rows that belong to the column
@@ -548,8 +525,7 @@ async function createInstanceRecords(instancesArray: Shape[], columnId: RecordId
548
525
  const instance = instanceShape.asObject()
549
526
 
550
527
  // Process roles if they exist as an array
551
- const rolesArray = instance.get('roles').ifArray()
552
- const rolesString = getRolesString(rolesArray)
528
+ const rolesString = getRolesString(instance.get('roles'))
553
529
 
554
530
  const instanceRecord = await factory.createRecord({
555
531
  source: instanceShape,
@@ -749,8 +725,7 @@ export const SPPagePlugin = Plugin.create({
749
725
  }
750
726
 
751
727
  // Process roles if they exist as an array
752
- const rolesArray = page.get('roles').ifArray()
753
- const rolesString = getRolesString(rolesArray)
728
+ const rolesString = getRolesString(page.get('roles'))
754
729
 
755
730
  const pageIdShape = page.get('pageId')
756
731
  const pageId = pageIdShape.asString().getValue()
@@ -0,0 +1,124 @@
1
+ import { CallExpressionShape, Plugin, type Shape } from '@servicenow/sdk-build-core'
2
+ import { NowIdShape } from '../now-id-plugin'
3
+ import { toReference, createSdkDocEntry } from '../utils'
4
+ import { getRolesString } from './utils'
5
+
6
+ function getPortalsString(portalsShape: Shape): string {
7
+ const portals = portalsShape
8
+ .ifArray()
9
+ ?.getElements()
10
+ .map((p) => {
11
+ const ref = toReference(p)
12
+ return typeof ref === 'string' ? ref : ref.getValue()
13
+ })
14
+ .filter((id) => id !== '')
15
+
16
+ if (!portals || portals.length === 0) {
17
+ return ''
18
+ }
19
+ return [...new Set(portals)].join(',')
20
+ }
21
+
22
+ export const SPPageRouteMapPlugin = Plugin.create({
23
+ name: 'SPPageRouteMapPlugin',
24
+ docs: [createSdkDocEntry('SPPageRouteMap', ['sp_page_route_map'])],
25
+ records: {
26
+ sp_page_route_map: {
27
+ coalesce: ['route_from_page', 'portals'],
28
+ toShape(record) {
29
+ const portalsStr = record.get('portals').ifString()?.getValue() ?? ''
30
+ const portalsArray = portalsStr
31
+ ? portalsStr
32
+ .split(',')
33
+ .map((p) => p.trim())
34
+ .filter((p) => p !== '')
35
+ : []
36
+
37
+ const rolesStr = record.get('roles').ifString()?.getValue() ?? ''
38
+ const rolesArray = rolesStr
39
+ ? rolesStr
40
+ .split(',')
41
+ .map((r) => r.trim())
42
+ .filter((r) => r !== '')
43
+ : []
44
+
45
+ return {
46
+ success: true,
47
+ value: new CallExpressionShape({
48
+ source: record,
49
+ callee: 'SPPageRouteMap',
50
+ args: [
51
+ record.transform(({ $ }) => {
52
+ const config: { [key: string]: typeof $ | undefined } = {
53
+ $id: $.val(NowIdShape.from(record)),
54
+ routeFromPage: $.from('route_from_page'),
55
+ routeToPage: $.from('route_to_page'),
56
+ shortDescription: $.from('short_description').def(''),
57
+ active: $.from('active').toBoolean().def(true),
58
+ order: $.from('order').toNumber().def(10),
59
+ }
60
+ if (portalsArray.length > 0) {
61
+ config['portals'] = $.val(portalsArray)
62
+ }
63
+ if (rolesArray.length > 0) {
64
+ config['roles'] = $.val(rolesArray)
65
+ }
66
+ return config
67
+ }),
68
+ ],
69
+ }),
70
+ }
71
+ },
72
+ },
73
+ },
74
+ shapes: [
75
+ {
76
+ shape: CallExpressionShape,
77
+ fileTypes: ['fluent'],
78
+ async toRecord(callExpression, { factory, diagnostics }) {
79
+ if (callExpression.getCallee() !== 'SPPageRouteMap') {
80
+ return { success: false }
81
+ }
82
+
83
+ const args = callExpression.getArgument(0).asObject()
84
+
85
+ const routeFromShape = args.get('routeFromPage')
86
+ const routeToShape = args.get('routeToPage')
87
+ const routeFromRef = toReference(routeFromShape)
88
+ const routeToRef = toReference(routeToShape)
89
+ const routeFromStr = typeof routeFromRef === 'string' ? routeFromRef : routeFromRef.getValue()
90
+ const routeToStr = typeof routeToRef === 'string' ? routeToRef : routeToRef.getValue()
91
+
92
+ if (routeFromStr !== '' && routeToStr !== '' && routeFromStr === routeToStr) {
93
+ diagnostics.error(
94
+ routeToShape.getOriginalNode(),
95
+ `Invalid SPPageRouteMap: "routeToPage" must be different from "routeFromPage".`
96
+ )
97
+ }
98
+
99
+ const portalsString = getPortalsString(args.get('portals'))
100
+ const rolesString = getRolesString(args.get('roles'), diagnostics)
101
+
102
+ const routeMapRecord = await factory.createRecord({
103
+ source: callExpression,
104
+ table: 'sp_page_route_map',
105
+ explicitId: args.get('$id'),
106
+ properties: args.transform(({ $ }) => ({
107
+ route_from_page: $.from('routeFromPage').map(toReference).def(''),
108
+ route_to_page: $.from('routeToPage').map(toReference).def(''),
109
+ short_description: $.from('shortDescription').def(''),
110
+ portals: $.val(portalsString).def(''),
111
+ roles: $.val(rolesString).def(''),
112
+ active: $.def(true),
113
+ order: $.def(10),
114
+ })),
115
+ })
116
+
117
+ return {
118
+ success: true,
119
+ value: routeMapRecord,
120
+ }
121
+ },
122
+ },
123
+ ],
124
+ })