@servicenow/sdk-build-plugins 4.6.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 (45) 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 +96 -42
  8. package/dist/column-plugin.js.map +1 -1
  9. package/dist/now-config-plugin.js +1 -0
  10. package/dist/now-config-plugin.js.map +1 -1
  11. package/dist/record-plugin.js +3 -2
  12. package/dist/record-plugin.js.map +1 -1
  13. package/dist/script-include-plugin.js +4 -0
  14. package/dist/script-include-plugin.js.map +1 -1
  15. package/dist/service-catalog/catalog-clientscript-plugin.js +2 -2
  16. package/dist/service-catalog/catalog-clientscript-plugin.js.map +1 -1
  17. package/dist/service-catalog/catalog-ui-policy-plugin.js +2 -2
  18. package/dist/service-catalog/catalog-ui-policy-plugin.js.map +1 -1
  19. package/dist/service-catalog/service-catalog-base.d.ts +2 -2
  20. package/dist/service-catalog/service-catalog-base.js +2 -2
  21. package/dist/service-catalog/service-catalog-base.js.map +1 -1
  22. package/dist/service-catalog/utils.js +1 -1
  23. package/dist/service-catalog/utils.js.map +1 -1
  24. package/dist/table-plugin.js +201 -55
  25. package/dist/table-plugin.js.map +1 -1
  26. package/dist/ui-policy-plugin.js +28 -96
  27. package/dist/ui-policy-plugin.js.map +1 -1
  28. package/dist/utils.d.ts +5 -1
  29. package/dist/utils.js +41 -0
  30. package/dist/utils.js.map +1 -1
  31. package/package.json +4 -4
  32. package/src/acl-plugin.ts +3 -1
  33. package/src/atf/test-plugin.ts +6 -9
  34. package/src/basic-syntax-plugin.ts +11 -3
  35. package/src/column-plugin.ts +134 -67
  36. package/src/now-config-plugin.ts +1 -0
  37. package/src/record-plugin.ts +11 -3
  38. package/src/script-include-plugin.ts +8 -0
  39. package/src/service-catalog/catalog-clientscript-plugin.ts +2 -2
  40. package/src/service-catalog/catalog-ui-policy-plugin.ts +2 -2
  41. package/src/service-catalog/service-catalog-base.ts +2 -2
  42. package/src/service-catalog/utils.ts +1 -1
  43. package/src/table-plugin.ts +254 -77
  44. package/src/ui-policy-plugin.ts +32 -128
  45. package/src/utils.ts +52 -0
@@ -108,128 +108,35 @@ export const UiPolicyPlugin = Plugin.create({
108
108
  },
109
109
  },
110
110
  toShape(record, { descendants }) {
111
- const actions = descendants
112
- .query(SYS_UI_POLICY_ACTION)
113
- .map((action) => {
114
- const fieldValue = action.get('field')?.ifString()?.getValue()
115
- if (!fieldValue) {
116
- return null // Skip actions without a field
117
- }
118
-
119
- const actionObj: {
120
- field: string
121
- visible?: boolean | 'ignore'
122
- readOnly?: boolean | 'ignore'
123
- mandatory?: boolean | 'ignore'
124
- cleared?: boolean
125
- table?: string
126
- value?: string
127
- fieldMessage?: string
128
- fieldMessageType?: string
129
- valueAction?: string
130
- } = {
131
- field: fieldValue,
132
- }
133
-
134
- // Convert ServiceNow string format to boolean | 'ignore'
135
- const visible = stringToBoolean(action.get('visible')?.ifString()?.getValue())
136
- const disabled = stringToBoolean(action.get('disabled')?.ifString()?.getValue())
137
- const mandatory = stringToBoolean(action.get('mandatory')?.ifString()?.getValue())
138
-
139
- // Only include visible if it's not 'ignore'
140
- if (visible !== undefined && visible !== 'ignore') {
141
- actionObj.visible = visible
142
- }
143
-
144
- // Convert 'disabled' to 'readOnly'
145
- // Only include 'readOnly' if it's not 'ignore'
146
- if (disabled !== undefined && disabled !== 'ignore') {
147
- actionObj.readOnly = disabled
148
- }
149
-
150
- // Only include mandatory if it's not 'ignore'
151
- if (mandatory !== undefined && mandatory !== 'ignore') {
152
- actionObj.mandatory = mandatory
153
- }
154
-
155
- const cleared = action.get('cleared')?.ifBoolean()?.getValue()
156
- if (cleared) {
157
- actionObj.cleared = cleared
158
- }
159
-
160
- // Add new optional fields - only if they have meaningful values
161
- const table = action.get('table')?.ifString()?.getValue()
162
- const parentTable = record.get('table')?.ifString()?.getValue()
163
- // Only include table if it's different from the parent policy's table
164
- if (table && table !== parentTable) {
165
- actionObj.table = table
166
- }
167
-
168
- const value = action.get('value')?.ifString()?.getValue()
169
- if (value) {
170
- actionObj.value = value
171
- }
172
-
173
- const fieldMessage = action.get('field_message')?.ifString()?.getValue()
174
- if (fieldMessage) {
175
- actionObj.fieldMessage = fieldMessage
176
- }
177
-
178
- const fieldMessageType = action.get('field_message_type')?.ifString()?.getValue()
179
- // Only include if it's not the default value 'none'
180
- if (fieldMessageType && fieldMessageType !== 'none') {
181
- actionObj.fieldMessageType = fieldMessageType
182
- }
183
-
184
- const valueAction = action.get('value_action')?.ifString()?.getValue()
185
- // Only include if it's not the default value 'ignore'
186
- if (valueAction && valueAction !== 'ignore') {
187
- actionObj.valueAction = valueAction
188
- }
189
-
190
- // Skip actions that have no meaningful properties (all are 'ignore')
191
- if (Object.keys(actionObj).length === 1) {
192
- return null // Only 'field' property exists, skip this action
193
- }
111
+ const actions = descendants.query(SYS_UI_POLICY_ACTION).map((action) => {
112
+ return action.transform(({ $ }) => ({
113
+ field: $,
114
+ visible: $.map((v) => stringToBoolean(v?.asString()?.getValue())).def('ignore'),
115
+ readOnly: $.from('disabled')
116
+ .map((v) => stringToBoolean(v?.asString()?.getValue()))
117
+ .def('ignore'),
118
+ mandatory: $.map((v) => stringToBoolean(v?.asString()?.getValue())).def('ignore'),
119
+ cleared: $.toBoolean().def(false),
120
+ table: $.def(''),
121
+ value: $.def(''),
122
+ fieldMessage: $.from('field_message').def(''),
123
+ fieldMessageType: $.from('field_message_type').def('none'),
124
+ valueAction: $.from('value_action').def('ignore'),
125
+ }))
126
+ })
194
127
 
195
- return actionObj
196
- })
197
- .filter((action) => action !== null) // Remove null actions
198
-
199
- // Process related list actions
200
- const relatedListActions = descendants
201
- .query(SYS_UI_POLICY_RL_ACTION)
202
- .map((rlAction) => {
203
- const rlActionObj: {
204
- list?: string
205
- visible?: boolean | 'ignore'
206
- } = {}
207
-
208
- const listValue = rlAction.get('list')?.ifString()?.getValue()
209
- if (listValue) {
210
- // Strip REL: prefix if present (transform ServiceNow → Fluent)
211
- // Users write plain GUIDs or table.field format in Fluent code
212
- if (listValue.startsWith('REL:')) {
213
- rlActionObj.list = listValue.substring(4) // Remove 'REL:' prefix
214
- } else {
215
- rlActionObj.list = listValue
128
+ const relatedListActions = descendants.query(SYS_UI_POLICY_RL_ACTION).map((rlAction) =>
129
+ rlAction.transform(({ $ }) => ({
130
+ list: $.map((v) => {
131
+ if (v?.ifString()?.ifDefined()) {
132
+ const listVal = v.asString().getValue()
133
+ return listVal.startsWith('REL:') ? listVal.substring(4) : listVal
216
134
  }
217
- }
218
-
219
- // Convert ServiceNow string format to boolean | 'ignore'
220
- const visible = stringToBoolean(rlAction.get('visible')?.ifString()?.getValue())
221
- if (visible !== undefined && visible !== 'ignore') {
222
- rlActionObj.visible = visible
223
- }
224
-
225
- // Skip if no meaningful properties (only $id exists)
226
- if (Object.keys(rlActionObj).length === 0) {
227
- return null
228
- }
229
-
230
- return rlActionObj
231
- })
232
- .filter((rlAction) => rlAction !== null) // Remove null actions
135
+ return ''
136
+ }).def(''),
137
+ visible: $.map((v) => stringToBoolean(v?.asString()?.getValue())).def('ignore'),
138
+ }))
139
+ )
233
140
 
234
141
  return {
235
142
  success: true,
@@ -278,7 +185,7 @@ export const UiPolicyPlugin = Plugin.create({
278
185
  order: $.from('order').toNumber().def(100),
279
186
  setValues: $.from('set_values').def(''),
280
187
  view: $.from('view').def(''),
281
- actions: $.val(actions.length > 0 ? actions : undefined),
188
+ actions: $.val(actions).def([]),
282
189
  relatedListActions: $.val(
283
190
  relatedListActions.length > 0 ? relatedListActions : undefined
284
191
  ),
@@ -401,11 +308,10 @@ export const UiPolicyPlugin = Plugin.create({
401
308
  const hasClearedProp = action.get('cleared')?.isBoolean() || false
402
309
 
403
310
  if (!hasVisibleProp && !hasReadOnlyProp && !hasMandatoryProp && !hasClearedProp) {
404
- diagnostics.error(
311
+ diagnostics.hint(
405
312
  action,
406
- `Action at index ${i} must specify at least one of: visible, readOnly, mandatory, or cleared`
313
+ `Action at index ${i} has no effect — consider specifying at least one of: visible, readOnly, mandatory, or cleared`
407
314
  )
408
- continue
409
315
  }
410
316
 
411
317
  const actionRecord = await factory.createRecord({
@@ -475,14 +381,12 @@ export const UiPolicyPlugin = Plugin.create({
475
381
  }
476
382
  }
477
383
 
478
- // At least one property must be specified (visible or list)
479
384
  const hasVisibleProp = isValidActionValue(rlAction.get('visible'))
480
385
  if (!hasVisibleProp && !listValue) {
481
- diagnostics.error(
386
+ diagnostics.hint(
482
387
  rlAction,
483
- `Related list action at index ${i} must specify at least one of: list or visible`
388
+ `Related list action at index ${i} has no effect — consider specifying at least one of: list or visible`
484
389
  )
485
- continue
486
390
  }
487
391
 
488
392
  const rlActionRecord = await factory.createRecord({
package/src/utils.ts CHANGED
@@ -6,7 +6,10 @@ import {
6
6
  type Compiler,
7
7
  type PluginApiDoc,
8
8
  type Record as FluentRecord,
9
+ type OutputFile,
10
+ type Transform,
9
11
  } from '@servicenow/sdk-build-core'
12
+ import { create } from 'xmlbuilder2'
10
13
 
11
14
  export function toReference(shape: Shape) {
12
15
  return shape.ifRecord()?.getId() ?? shape.ifString()?.getValue() ?? ''
@@ -130,6 +133,55 @@ export const showGuidFieldDiagnostic = (
130
133
  }
131
134
  }
132
135
 
136
+ export async function generateChoiceSetFile(
137
+ choiceSet: FluentRecord,
138
+ choices: FluentRecord[],
139
+ config: { scope: string; scopeId: string },
140
+ transform: Transform
141
+ ): Promise<OutputFile> {
142
+ const tableName = choiceSet.get('name').asString().getValue()
143
+ const elementName = choiceSet.get('element').asString().getValue()
144
+ const xml = create().ele('record_update')
145
+ const root = xml.ele('sys_choice_set', { table: tableName, field: elementName })
146
+
147
+ choices.forEach((choice) => {
148
+ const child = root.ele('sys_choice', { action: choice.getAction() })
149
+ child.ele('sys_id').txt(choice.getId().getValue())
150
+ child.ele('name').txt(tableName)
151
+ child.ele('element').txt(elementName)
152
+
153
+ for (const prop of [
154
+ 'label',
155
+ 'value',
156
+ 'sequence',
157
+ 'dependent_value',
158
+ 'hint',
159
+ 'inactive',
160
+ 'inactive_on_update',
161
+ 'language',
162
+ ]) {
163
+ choice
164
+ .get(prop)
165
+ .ifDefined()
166
+ ?.toString()
167
+ .pipe((p) => child.ele(prop).txt(p.getValue()))
168
+ }
169
+ })
170
+
171
+ const child = root.ele('sys_choice_set', { action: choiceSet.getAction() })
172
+ child.ele('sys_id').txt(choiceSet.getId().getValue())
173
+ child.ele('sys_scope', { display_value: config.scope }).txt(config.scopeId)
174
+ child.ele('name').txt(tableName)
175
+ child.ele('element').txt(elementName)
176
+
177
+ return {
178
+ source: choiceSet,
179
+ name: `${await transform.getUpdateName(choiceSet)}.xml`,
180
+ category: choiceSet.getInstallCategory(),
181
+ content: xml.end({ prettyPrint: true }),
182
+ }
183
+ }
184
+
133
185
  /**
134
186
  * Creates a documentation entry for first-party SDK plugins.
135
187
  *