@servicenow/sdk-build-plugins 4.7.2 → 4.8.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 (81) hide show
  1. package/dist/alias/alias-plugin.d.ts +2 -0
  2. package/dist/alias/alias-plugin.js +183 -0
  3. package/dist/alias/alias-plugin.js.map +1 -0
  4. package/dist/alias/alias-template-plugin.d.ts +2 -0
  5. package/dist/alias/alias-template-plugin.js +232 -0
  6. package/dist/alias/alias-template-plugin.js.map +1 -0
  7. package/dist/alias/index.d.ts +3 -0
  8. package/dist/alias/index.js +20 -0
  9. package/dist/alias/index.js.map +1 -0
  10. package/dist/alias/retry-policy-plugin.d.ts +2 -0
  11. package/dist/alias/retry-policy-plugin.js +119 -0
  12. package/dist/alias/retry-policy-plugin.js.map +1 -0
  13. package/dist/arrow-function-plugin.d.ts +1 -0
  14. package/dist/arrow-function-plugin.js +60 -21
  15. package/dist/arrow-function-plugin.js.map +1 -1
  16. package/dist/atf/test-plugin.js +1 -1
  17. package/dist/atf/test-plugin.js.map +1 -1
  18. package/dist/basic-syntax-plugin.js +7 -7
  19. package/dist/basic-syntax-plugin.js.map +1 -1
  20. package/dist/column/index.d.ts +2 -0
  21. package/dist/column/index.js +13 -0
  22. package/dist/column/index.js.map +1 -0
  23. package/dist/dashboard/dashboard-plugin.js +4 -0
  24. package/dist/dashboard/dashboard-plugin.js.map +1 -1
  25. package/dist/data-lookup-plugin.d.ts +2 -0
  26. package/dist/data-lookup-plugin.js +159 -0
  27. package/dist/data-lookup-plugin.js.map +1 -0
  28. package/dist/flow/plugins/flow-instance-plugin.js +1 -1
  29. package/dist/flow/plugins/flow-instance-plugin.js.map +1 -1
  30. package/dist/flow/plugins/step-instance-plugin.js +1 -1
  31. package/dist/flow/plugins/step-instance-plugin.js.map +1 -1
  32. package/dist/flow/utils/flow-constants.d.ts +7 -0
  33. package/dist/flow/utils/flow-constants.js +6 -1
  34. package/dist/flow/utils/flow-constants.js.map +1 -1
  35. package/dist/flow/utils/flow-shapes.d.ts +1 -1
  36. package/dist/form-plugin.js +35 -24
  37. package/dist/form-plugin.js.map +1 -1
  38. package/dist/index.d.ts +4 -0
  39. package/dist/index.js +4 -0
  40. package/dist/index.js.map +1 -1
  41. package/dist/now-attach-plugin.d.ts +1 -1
  42. package/dist/now-config-plugin.js +2 -1
  43. package/dist/now-config-plugin.js.map +1 -1
  44. package/dist/now-delete-plugin.d.ts +2 -0
  45. package/dist/now-delete-plugin.js +64 -0
  46. package/dist/now-delete-plugin.js.map +1 -0
  47. package/dist/record-plugin.d.ts +10 -0
  48. package/dist/record-plugin.js +15 -1
  49. package/dist/record-plugin.js.map +1 -1
  50. package/dist/repack/lint/Rules.js +17 -7
  51. package/dist/repack/lint/Rules.js.map +1 -1
  52. package/dist/rest-message-plugin.d.ts +2 -0
  53. package/dist/rest-message-plugin.js +331 -0
  54. package/dist/rest-message-plugin.js.map +1 -0
  55. package/dist/script-include-plugin.js +1 -1
  56. package/dist/script-include-plugin.js.map +1 -1
  57. package/dist/server-module-plugin/sbom-builder.js +17 -7
  58. package/dist/server-module-plugin/sbom-builder.js.map +1 -1
  59. package/dist/static-content-plugin.js +17 -7
  60. package/dist/static-content-plugin.js.map +1 -1
  61. package/package.json +6 -5
  62. package/src/alias/alias-plugin.ts +221 -0
  63. package/src/alias/alias-template-plugin.ts +271 -0
  64. package/src/alias/index.ts +3 -0
  65. package/src/alias/retry-policy-plugin.ts +138 -0
  66. package/src/arrow-function-plugin.ts +67 -23
  67. package/src/atf/test-plugin.ts +1 -1
  68. package/src/basic-syntax-plugin.ts +7 -7
  69. package/src/column/index.ts +7 -0
  70. package/src/dashboard/dashboard-plugin.ts +4 -0
  71. package/src/data-lookup-plugin.ts +191 -0
  72. package/src/flow/plugins/flow-instance-plugin.ts +2 -1
  73. package/src/flow/plugins/step-instance-plugin.ts +1 -1
  74. package/src/flow/utils/flow-constants.ts +8 -0
  75. package/src/form-plugin.ts +47 -26
  76. package/src/index.ts +4 -0
  77. package/src/now-config-plugin.ts +2 -1
  78. package/src/now-delete-plugin.ts +82 -0
  79. package/src/record-plugin.ts +17 -2
  80. package/src/rest-message-plugin.ts +391 -0
  81. package/src/script-include-plugin.ts +4 -1
@@ -71,6 +71,10 @@ export class ArrowFunctionShape extends Shape {
71
71
  return this.returnValue
72
72
  }
73
73
 
74
+ isImplicitReturn(): boolean {
75
+ return this.implicitReturn
76
+ }
77
+
74
78
  override getCode(): string {
75
79
  const params = `(${this.getParameters()
76
80
  .map((p) => p.getCode())
@@ -192,48 +196,88 @@ export const ArrowFunctionPlugin = Plugin.create({
192
196
  {
193
197
  shape: ArrowFunctionShape,
194
198
  async commit(shape, target, { commit }) {
195
- const arrowFunction = ts.Node.isArrowFunction(target)
199
+ const originalTargetWasArrow = ts.Node.isArrowFunction(target)
200
+ const arrowTarget = originalTargetWasArrow
196
201
  ? target
197
202
  : target.replaceWithText('() => {}').asKindOrThrow(ts.SyntaxKind.ArrowFunction)
198
203
 
199
- const nodeStatements = arrowFunction.getStatements()
200
204
  const shapeStatements = shape.getStatements()
201
205
  const returnValue = shape.getReturnValue()
206
+ const body = arrowTarget.getBody()
202
207
 
203
- if (ts.Node.isArrowFunction(target)) {
204
- const body = target.getBody()
205
- if (!ts.Node.isBlock(body) && shapeStatements.length === 0 && returnValue) {
206
- // Already inline form - commit directly to the expression
208
+ // Preserve concise arrows when the shape is still an implicit return.
209
+ if (
210
+ originalTargetWasArrow &&
211
+ !ts.Node.isBlock(body) &&
212
+ shapeStatements.length === 0 &&
213
+ returnValue &&
214
+ shape.isImplicitReturn()
215
+ ) {
216
+ if (returnValue.is(ObjectShape)) {
217
+ body.replaceWithText(`(${returnValue.getCode()})`)
218
+ } else {
207
219
  await commit(returnValue, body)
208
- return { success: true }
209
220
  }
221
+ return { success: true }
222
+ }
223
+
224
+ if (!ts.Node.isBlock(body)) {
225
+ // ts-morph cannot grow expression-body arrows like `() => ({})` into
226
+ // block bodies with statement APIs, so convert the body to an empty block
227
+ // first and then continue through the normal commit flow below.
228
+ body.replaceWithText('{}')
210
229
  }
211
230
 
212
- const totalExpected = shapeStatements.length + (returnValue ? 1 : 0)
213
- const excess = nodeStatements.length - totalExpected
231
+ const getNonReturnStatements = () =>
232
+ arrowTarget.getStatements().filter((statement) => !ts.Node.isReturnStatement(statement))
233
+
234
+ const existingStatements = getNonReturnStatements()
235
+ const excess = existingStatements.length - shapeStatements.length
214
236
  for (let i = 1; i <= excess; i++) {
215
- arrowFunction.removeStatement(nodeStatements.length - i)
237
+ existingStatements[existingStatements.length - i]?.remove()
216
238
  }
217
239
 
218
240
  for (const [i, statement] of shapeStatements.entries()) {
219
- const targetStatement = nodeStatements[i] ?? arrowFunction.addStatements('undefined')[0]!
220
- await commit(statement, targetStatement)
241
+ const currentStatements = getNonReturnStatements()
242
+ const targetStatement = currentStatements[i]
243
+ if (targetStatement) {
244
+ await commit(statement, targetStatement)
245
+ } else {
246
+ // Insert before `return` so return-only bodies can grow safely.
247
+ const returnIndex = arrowTarget
248
+ .getStatements()
249
+ .findIndex((node) => ts.Node.isReturnStatement(node))
250
+ const inserted =
251
+ returnIndex >= 0
252
+ ? arrowTarget.insertStatements(returnIndex, statement.getCode())[0]!
253
+ : arrowTarget.addStatements(statement.getCode())[0]!
254
+ await commit(statement, inserted)
255
+ }
221
256
  }
222
257
 
223
- if (returnValue) {
224
- const returnIndex = shapeStatements.length
225
- const targetReturn =
226
- nodeStatements[returnIndex] ?? arrowFunction.addStatements('return undefined')[0]!
227
- if (ts.Node.isReturnStatement(targetReturn)) {
228
- const expr = targetReturn.getExpression()
229
- if (expr) {
230
- await commit(returnValue, expr)
231
- }
258
+ let existingReturn = arrowTarget
259
+ .getStatements()
260
+ .find((statement) => ts.Node.isReturnStatement(statement))
261
+ if (!returnValue) {
262
+ existingReturn?.remove()
263
+ return { success: true }
264
+ }
265
+
266
+ if (!existingReturn) {
267
+ const inserted = arrowTarget.addStatements(`return ${returnValue.getCode()}`)[0]!
268
+ existingReturn = ts.Node.isReturnStatement(inserted)
269
+ ? inserted
270
+ : arrowTarget.getStatements().find((statement) => ts.Node.isReturnStatement(statement))
271
+ }
272
+
273
+ if (existingReturn) {
274
+ const expr = existingReturn.getExpression()
275
+ if (expr) {
276
+ await commit(returnValue, expr)
232
277
  } else {
233
- targetReturn.replaceWithText(`return ${returnValue.getCode()}`)
278
+ existingReturn.replaceWithText(`return ${returnValue.getCode()}`)
234
279
  }
235
280
  }
236
-
237
281
  return { success: true }
238
282
  },
239
283
  },
@@ -242,7 +242,7 @@ export const TestPlugin = Plugin.create({
242
242
  },
243
243
  },
244
244
  sys_element_mapping: {
245
- coalesce: ['field', 'id'],
245
+ coalesce: ['field', 'table', 'id'],
246
246
  },
247
247
  },
248
248
  shapes: [
@@ -85,13 +85,13 @@ export const BasicSyntaxPlugin = Plugin.create({
85
85
  const conditionalDir = NowConfig.getConditionalDirectory(config, source.path)
86
86
  const taxonomySubdir = tableName && taxonomy.mapping[tableName] ? taxonomy.mapping[tableName] : ''
87
87
 
88
- const dir = hostedDir
89
- ? pathModule.join(generatedDir, hostedDir)
90
- : conditionalDir
91
- ? pathModule.join(generatedDir, conditionalDir)
92
- : taxonomySubdir
93
- ? pathModule.join(generatedDir, taxonomySubdir)
94
- : generatedDir
88
+ // The hosted/conditional plugin prefix (e.g. `if/com.<plugin>`) is orthogonal to
89
+ // the taxonomy folder (e.g. `data/table`). Compose them so records keep their
90
+ // taxonomy organization even when they live under a conditional/hosted plugin
91
+ // directory, instead of the plugin prefix overriding the taxonomy (DEF0844422).
92
+ // `pathModule.join` drops empty segments, so any of these may be absent.
93
+ const prefixDir = hostedDir ?? conditionalDir ?? ''
94
+ const dir = pathModule.join(generatedDir, prefixDir, taxonomySubdir)
95
95
 
96
96
  let name = pathModule.basename(source.path).replace(RegExp(`${pathModule.extname(source.path)}$`), '')
97
97
  if (!name) {
@@ -0,0 +1,7 @@
1
+ export {
2
+ addFieldsToColumn,
3
+ COLUMN_API_TO_TYPE,
4
+ COLUMN_TYPE_TO_API,
5
+ getDefaultMaxLength,
6
+ } from './column-helper'
7
+ export { getChoiceRecords, getDocumentationRecords, getLabelForDefaultLanguage } from './column-to-record'
@@ -211,6 +211,8 @@ export const DashboardPlugin = Plugin.create({
211
211
  $id: $.val(NowIdShape.from(record)),
212
212
  name: $,
213
213
  active: $.toBoolean().def(true),
214
+ description: $.def(''),
215
+ certified: $.toBoolean().def(false),
214
216
  tabs: $.val(tabs).def([]),
215
217
  visibilities: $.val(visibilities).def([]),
216
218
  permissions: $.val(permissions).def([]),
@@ -272,6 +274,8 @@ export const DashboardPlugin = Plugin.create({
272
274
  properties: arg.transform(({ $ }) => ({
273
275
  name: $,
274
276
  active: $.def(true),
277
+ description: $.def(''),
278
+ certified: $.def(false),
275
279
  })),
276
280
  })
277
281
 
@@ -0,0 +1,191 @@
1
+ import { CallExpressionShape, Plugin, isSNScope } from '@servicenow/sdk-build-core'
2
+ import { NowIdShape } from './now-id-plugin'
3
+
4
+ const DL_DEFINITION = 'dl_definition'
5
+ const DL_DEFINITION_REL_MATCH = 'dl_definition_rel_match'
6
+ const DL_DEFINITION_REL_SET = 'dl_definition_rel_set'
7
+
8
+ export const DataLookupPlugin = Plugin.create({
9
+ name: 'DataLookupPlugin',
10
+
11
+ records: {
12
+ [DL_DEFINITION]: {
13
+ relationships: {
14
+ [DL_DEFINITION_REL_MATCH]: {
15
+ via: 'dl_definition',
16
+ descendant: true,
17
+ },
18
+ [DL_DEFINITION_REL_SET]: {
19
+ via: 'dl_definition',
20
+ descendant: true,
21
+ },
22
+ },
23
+
24
+ async toShape(record, { descendants }) {
25
+ const matchRecords = descendants
26
+ .query(DL_DEFINITION_REL_MATCH)
27
+ .filter((r) => r.get('dl_definition').equals(record.getId()))
28
+
29
+ const setRecords = descendants
30
+ .query(DL_DEFINITION_REL_SET)
31
+ .filter((r) => r.get('dl_definition').equals(record.getId()))
32
+
33
+ const matchRules = matchRecords.map((r) =>
34
+ r.transform(({ $ }) => ({
35
+ $id: $.val(NowIdShape.from(r)),
36
+ sourceField: $.from('source_table_field'),
37
+ matcherField: $.from('matcher_table_field'),
38
+ exactMatch: $.from('exact_match').toBoolean().def(false),
39
+ }))
40
+ )
41
+
42
+ const setRules = setRecords.map((r) =>
43
+ r.transform(({ $ }) => ({
44
+ $id: $.val(NowIdShape.from(r)),
45
+ targetField: $.from('source_table_field'),
46
+ matcherField: $.from('matcher_table_field'),
47
+ alwaysReplace: $.from('always_replace').toBoolean().def(false),
48
+ }))
49
+ )
50
+
51
+ return {
52
+ success: true,
53
+ value: new CallExpressionShape({
54
+ source: record,
55
+ callee: 'DataLookup',
56
+ args: [
57
+ record.transform(({ $ }) => ({
58
+ $id: $.val(NowIdShape.from(record)),
59
+ name: $,
60
+ sourceTable: $.from('source_table'),
61
+ matcherTable: $.from('matcher_table'),
62
+ active: $.from('active').toBoolean().def(true),
63
+ runOnInsert: $.from('run_on_insert').toBoolean().def(true),
64
+ runOnUpdate: $.from('run_on_update').toBoolean().def(false),
65
+ runOnFormChange: $.from('run_on_form_change').toBoolean().def(true),
66
+ matchRules: $.val(matchRules.length ? matchRules : undefined),
67
+ setRules: $.val(setRules.length ? setRules : undefined),
68
+ protectionPolicy: $.from('sys_policy').def(''),
69
+ })),
70
+ ],
71
+ }),
72
+ }
73
+ },
74
+ },
75
+
76
+ [DL_DEFINITION_REL_MATCH]: {
77
+ // Child records are pulled by the parent dl_definition toShape via descendants.query(),
78
+ // so they must not be transformed independently.
79
+ toShape() {
80
+ return { success: false }
81
+ },
82
+ },
83
+
84
+ [DL_DEFINITION_REL_SET]: {
85
+ // Same as dl_definition_rel_match — rendered inline by the parent's toShape.
86
+ toShape() {
87
+ return { success: false }
88
+ },
89
+ },
90
+ },
91
+
92
+ shapes: [
93
+ {
94
+ shape: CallExpressionShape,
95
+ fileTypes: ['fluent'],
96
+ async toRecord(callExpression, { factory, diagnostics, config }) {
97
+ if (callExpression.getCallee() !== 'DataLookup') {
98
+ return { success: false }
99
+ }
100
+
101
+ const arg = callExpression.getArgument(0).asObject()
102
+
103
+ const name = arg.get('name').ifString()?.getValue()
104
+ if (name && name.length > 40) {
105
+ diagnostics.error(
106
+ arg.get('name'),
107
+ `Data Lookup Definition name must be 40 characters or fewer (got ${name.length}).`
108
+ )
109
+ }
110
+
111
+ const sourceTable = arg.get('sourceTable').ifString()?.getValue() ?? ''
112
+ const matcherTable = arg.get('matcherTable').ifString()?.getValue() ?? ''
113
+
114
+ const scope = config.scope
115
+ if (sourceTable && !sourceTable.startsWith(`${scope}_`) && !isSNScope(scope) && scope !== 'global') {
116
+ diagnostics.error(
117
+ arg.get('sourceTable'),
118
+ `sourceTable '${sourceTable}' must be in the same scope as the data lookup definition. Expected a table starting with '${scope}_'.`
119
+ )
120
+ }
121
+ if (matcherTable && !matcherTable.startsWith(`${scope}_`) && !isSNScope(scope) && scope !== 'global') {
122
+ diagnostics.error(
123
+ arg.get('matcherTable'),
124
+ `matcherTable '${matcherTable}' must be in the same scope as the data lookup definition. Expected a table starting with '${scope}_'.`
125
+ )
126
+ }
127
+
128
+ const mainRecord = await factory.createRecord({
129
+ source: callExpression,
130
+ table: DL_DEFINITION,
131
+ explicitId: arg.get('$id'),
132
+ properties: arg.transform(({ $ }) => ({
133
+ name: $,
134
+ source_table: $.from('sourceTable'),
135
+ matcher_table: $.from('matcherTable'),
136
+ active: $.from('active').def(true),
137
+ run_on_insert: $.from('runOnInsert').def(true),
138
+ run_on_update: $.from('runOnUpdate').def(false),
139
+ run_on_form_change: $.from('runOnFormChange').def(true),
140
+ sys_policy: $.from('protectionPolicy'),
141
+ })),
142
+ })
143
+
144
+ const matchElements = arg.get('matchRules').ifArray()?.getElements() ?? []
145
+ const matchRecords = await Promise.all(
146
+ matchElements.map(async (el) => {
147
+ const rule = el.asObject()
148
+ return factory.createRecord({
149
+ source: el,
150
+ table: DL_DEFINITION_REL_MATCH,
151
+ explicitId: rule.get('$id'),
152
+ properties: rule.transform(({ $ }) => ({
153
+ dl_definition: $.val(mainRecord.getId()),
154
+ source_table_field: $.from('sourceField'),
155
+ matcher_table_field: $.from('matcherField'),
156
+ exact_match: $.from('exactMatch').def(false),
157
+ source_table: $.val(sourceTable),
158
+ matcher_table: $.val(matcherTable),
159
+ })),
160
+ })
161
+ })
162
+ )
163
+
164
+ const setElements = arg.get('setRules').ifArray()?.getElements() ?? []
165
+ const setRecords = await Promise.all(
166
+ setElements.map(async (el) => {
167
+ const rule = el.asObject()
168
+ return factory.createRecord({
169
+ source: el,
170
+ table: DL_DEFINITION_REL_SET,
171
+ explicitId: rule.get('$id'),
172
+ properties: rule.transform(({ $ }) => ({
173
+ dl_definition: $.val(mainRecord.getId()),
174
+ source_table_field: $.from('targetField'),
175
+ matcher_table_field: $.from('matcherField'),
176
+ always_replace: $.from('alwaysReplace').def(false),
177
+ source_table: $.val(sourceTable),
178
+ matcher_table: $.val(matcherTable),
179
+ })),
180
+ })
181
+ })
182
+ )
183
+
184
+ return {
185
+ success: true,
186
+ value: mainRecord.with(...matchRecords, ...setRecords),
187
+ }
188
+ },
189
+ },
190
+ ],
191
+ })
@@ -68,6 +68,7 @@ import {
68
68
  UNSUPPORTED_DATA_TYPES,
69
69
  UTC_TIMEZONE_VALUE,
70
70
  ELEMENT_MAPPING_FIELD_ALIASES,
71
+ ELEMENT_MAPPING_FIELD_ALIASES_REVERSE,
71
72
  } from '../utils/flow-constants'
72
73
 
73
74
  import type { ApprovalDueDateType, ApprovalRulesType } from '@servicenow/sdk-core/runtime/flow'
@@ -958,7 +959,7 @@ function processInputValue(
958
959
 
959
960
  // Return standard input object
960
961
  return {
961
- name: inputName,
962
+ name: ELEMENT_MAPPING_FIELD_ALIASES_REVERSE[inputName] ?? inputName,
962
963
  value: finalValue,
963
964
  displayValue: displayValues && displayValues.has(inputName) ? displayValues.get(inputName) : finalValue,
964
965
  scriptActive: false,
@@ -372,7 +372,7 @@ export const StepInstancePlugin = Plugin.create({
372
372
  coalesce: ['name'],
373
373
  },
374
374
  sys_element_mapping: {
375
- coalesce: ['field', 'id'],
375
+ coalesce: ['field', 'table', 'id'],
376
376
  },
377
377
  sys_hub_step_instance: {
378
378
  relationships: {
@@ -545,6 +545,14 @@ export const ELEMENT_MAPPING_FIELD_ALIASES: { [platformName: string]: string } =
545
545
  __snc_dont_fail_on_error: 'dont_fail_flow_on_error',
546
546
  }
547
547
 
548
+ /**
549
+ * Reverse of ELEMENT_MAPPING_FIELD_ALIASES — maps Fluent API names back to their
550
+ * platform-internal names for serialization into sys_hub_action_instance_v2.
551
+ */
552
+ export const ELEMENT_MAPPING_FIELD_ALIASES_REVERSE: { [fluentName: string]: string } = Object.fromEntries(
553
+ Object.entries(ELEMENT_MAPPING_FIELD_ALIASES).map(([platform, fluent]) => [fluent, platform])
554
+ )
555
+
548
556
  export enum ChoiceDropdown {
549
557
  DROPDOWN_WITH_NONE = 1,
550
558
  DROPDOWN_WITHOUT_NONE = 3,
@@ -268,50 +268,51 @@ function getOptionalString(record: Record, field: string, defaultValue = ''): st
268
268
  * The name is needed as an attribute on the <view> XML element so the parser
269
269
  * can create a RecordId with proper coalesce keys when re-reading the XML.
270
270
  */
271
- function resolveView(view: ReturnType<Record['get']>, database: Database): { sysId: string; name: string } {
271
+ function resolveView(
272
+ view: ReturnType<Record['get']>,
273
+ database: Database
274
+ ): { sysId: string; name: string; displayValue: string } {
272
275
  if (view.isRecordId() || view.isRecord()) {
273
276
  const viewId = view.isRecordId() ? view : view.getId()
274
277
  const sysId = viewId.getValue()
275
- // Try to resolve the view record to get its name
278
+ // Try to resolve the view record to get its name and title
276
279
  const viewRecord = database.resolve(viewId)
277
280
  if (viewRecord) {
278
281
  const name = viewRecord.get('name').ifString()?.getValue() ?? ''
279
- return { sysId, name }
282
+ const displayValue = viewRecord.get('title').ifString()?.getValue() ?? name
283
+ return { sysId, name, displayValue }
280
284
  }
281
285
  // Fall back to primary key (view name) if available
282
286
  if (view.isRecordId() && view.asRecordId().hasPrimaryKey()) {
283
287
  const pk = view.asRecordId().getPrimaryKey()
284
288
  if (pk && !isGUID(pk)) {
285
- return { sysId, name: pk }
289
+ return { sysId, name: pk, displayValue: pk }
286
290
  }
287
291
  }
288
- return { sysId, name: '' }
292
+ return { sysId, name: '', displayValue: '' }
289
293
  }
290
294
 
291
295
  const viewValue = view.getValue()
292
296
  if (viewValue === 'NULL' || viewValue === DEFAULT_VIEW) {
293
- return { sysId: DEFAULT_VIEW, name: DEFAULT_VIEW }
297
+ return { sysId: DEFAULT_VIEW, name: DEFAULT_VIEW, displayValue: DEFAULT_VIEW }
294
298
  }
295
299
 
296
300
  // View is a string - try to resolve to record ID if it exists in database
297
301
  const viewNameStr = view.ifString()?.getValue() ?? ''
298
302
  if (!viewNameStr) {
299
- return { sysId: '', name: '' }
303
+ return { sysId: '', name: '', displayValue: '' }
300
304
  }
301
305
 
302
306
  const viewRecord = database.query('sys_ui_view').find((v) => v.get('name').ifString()?.getValue() === viewNameStr)
303
307
  if (viewRecord) {
304
- return { sysId: viewRecord.getId().getValue(), name: viewNameStr }
308
+ const displayValue = viewRecord.get('title').ifString()?.getValue() ?? viewNameStr
309
+ return { sysId: viewRecord.getId().getValue(), name: viewNameStr, displayValue }
310
+ }
311
+ return {
312
+ sysId: viewNameStr,
313
+ name: isGUID(viewNameStr) ? '' : viewNameStr,
314
+ displayValue: isGUID(viewNameStr) ? '' : viewNameStr,
305
315
  }
306
- return { sysId: viewNameStr, name: isGUID(viewNameStr) ? '' : viewNameStr }
307
- }
308
-
309
- /**
310
- * Resolves a view value to its string representation (sys_id or name)
311
- * Handles RecordId, Record, string values, and database lookups
312
- */
313
- function resolveViewName(view: ReturnType<Record['get']>, database: Database): string {
314
- return resolveView(view, database).sysId
315
316
  }
316
317
 
317
318
  type FormElement =
@@ -377,7 +378,11 @@ export const FormPlugin = Plugin.create({
377
378
  const sectionName = section.get('name').asString().getValue()
378
379
  const caption = getOptionalString(section, 'caption')
379
380
  const sysDomain = getOptionalString(section, 'sys_domain', 'global')
380
- const { sysId: viewName, name: viewDisplayName } = resolveView(section.get('view'), database)
381
+ const {
382
+ sysId: viewSysId,
383
+ name: viewName,
384
+ displayValue: viewDisplayValue,
385
+ } = resolveView(section.get('view'), database)
381
386
 
382
387
  const xml = create().ele('record_update')
383
388
  const root = xml.ele('sys_ui_section', {
@@ -385,7 +390,7 @@ export const FormPlugin = Plugin.create({
385
390
  section_id: section.getId().getValue(),
386
391
  sys_domain: sysDomain,
387
392
  table: sectionName,
388
- view: viewName,
393
+ view: viewSysId === DEFAULT_VIEW ? '' : viewName,
389
394
  })
390
395
 
391
396
  // Add all sys_ui_annotation records for this section (excluding deleted ones)
@@ -427,7 +432,7 @@ export const FormPlugin = Plugin.create({
427
432
  display_value: caption,
428
433
  name: sectionName,
429
434
  sys_domain: sysDomain,
430
- view: viewName,
435
+ view: viewSysId,
431
436
  })
432
437
  .txt(section.getId().getValue())
433
438
  child.ele('sys_user')
@@ -446,7 +451,13 @@ export const FormPlugin = Plugin.create({
446
451
  sectionChild.ele('sys_user')
447
452
  sectionChild.ele('title').txt(String(section.get('title').ifBoolean()?.getValue() ?? false))
448
453
  sectionChild.ele('view_name')
449
- sectionChild.ele('view', viewDisplayName ? { name: viewDisplayName } : {}).txt(viewName)
454
+ const isDefaultView = viewSysId === DEFAULT_VIEW
455
+ const sectionViewAttrs = isDefaultView
456
+ ? { name: 'NULL', display_value: DEFAULT_VIEW }
457
+ : viewName
458
+ ? { name: viewName, display_value: viewDisplayValue || viewName }
459
+ : {}
460
+ sectionChild.ele('view', sectionViewAttrs).txt(viewSysId)
450
461
 
451
462
  return {
452
463
  success: true,
@@ -702,7 +713,11 @@ export const FormPlugin = Plugin.create({
702
713
 
703
714
  const formName = form.get('name').asString().getValue()
704
715
  const formSysDomain = getOptionalString(form, 'sys_domain', 'global')
705
- const { sysId: formViewName, name: formViewDisplayName } = resolveView(form.get('view'), database)
716
+ const {
717
+ sysId: formViewSysId,
718
+ name: formViewName,
719
+ displayValue: formViewDisplayValue,
720
+ } = resolveView(form.get('view'), database)
706
721
 
707
722
  const xml = create().ele('record_update')
708
723
  const root = xml.ele('sys_ui_form_sections', {
@@ -727,7 +742,7 @@ export const FormPlugin = Plugin.create({
727
742
  display_value: formName,
728
743
  name: formName,
729
744
  sys_domain: formSysDomain,
730
- view: formViewName,
745
+ view: formViewSysId,
731
746
  })
732
747
  .txt(form.getId().getValue())
733
748
 
@@ -737,14 +752,14 @@ export const FormPlugin = Plugin.create({
737
752
 
738
753
  if (section) {
739
754
  const sectionCaption = getOptionalString(section, 'caption')
740
- const sectionViewName = resolveViewName(section.get('view'), database)
755
+ const { sysId: sectionViewSysId } = resolveView(section.get('view'), database)
741
756
  child
742
757
  .ele('sys_ui_section', {
743
758
  caption: sectionCaption,
744
759
  display_value: sectionCaption,
745
760
  name: formName,
746
761
  sys_domain: getOptionalString(section, 'sys_domain', 'global'),
747
- view: sectionViewName,
762
+ view: sectionViewSysId,
748
763
  })
749
764
  .txt(section.getId().getValue())
750
765
  }
@@ -757,7 +772,13 @@ export const FormPlugin = Plugin.create({
757
772
  formChild.ele('sys_id').txt(form.getId().getValue())
758
773
  formChild.ele('sys_scope', { display_value: config.scope }).txt(config.scopeId)
759
774
  formChild.ele('sys_user').txt(getOptionalString(form, 'sys_user'))
760
- formChild.ele('view', formViewDisplayName ? { name: formViewDisplayName } : {}).txt(formViewName)
775
+ const isDefaultFormView = formViewSysId === DEFAULT_VIEW
776
+ const formViewAttrs = isDefaultFormView
777
+ ? { name: 'NULL', display_value: DEFAULT_VIEW }
778
+ : formViewName
779
+ ? { name: formViewName, display_value: formViewDisplayValue || formViewName }
780
+ : {}
781
+ formChild.ele('view', formViewAttrs).txt(formViewSysId)
761
782
  formChild.ele('view_name')
762
783
 
763
784
  return {
package/src/index.ts CHANGED
@@ -6,6 +6,7 @@ export * from './cross-scope-privilege-plugin'
6
6
  export * from './record-plugin'
7
7
  export * from './now-id-plugin'
8
8
  export * from './now-ref-plugin'
9
+ export * from './now-delete-plugin'
9
10
  export * from './now-unresolved-plugin'
10
11
  export * from './property-plugin'
11
12
  export * from './role-plugin'
@@ -21,6 +22,7 @@ export * from './now-include-plugin'
21
22
  export * from './table-plugin'
22
23
  export * from './column-plugin'
23
24
  export * from './data-plugin'
25
+ export * from './data-lookup-plugin'
24
26
  export * from './ui-page-plugin'
25
27
  export * from './list-plugin'
26
28
  export * from './ui-action-plugin'
@@ -62,6 +64,7 @@ export * from './now-attach-plugin'
62
64
  export * from './sla-plugin'
63
65
  export * from './email-notification-plugin'
64
66
  export * from './inbound-email-action-plugin'
67
+ export * from './rest-message-plugin'
65
68
 
66
69
  export * from './service-catalog'
67
70
  export * from './ux-list-menu-config-plugin'
@@ -69,6 +72,7 @@ export * from './workspace-plugin'
69
72
  export * from './dashboard/dashboard-plugin'
70
73
  export * from './applicability-plugin'
71
74
  export * from './instance-scan-plugin'
75
+ export * from './alias'
72
76
  // non-plugins
73
77
  export * from './atf/step-configs'
74
78
  export { REPACK_OUTPUT_DIR, checkModuleExists, resolveModule } from './repack'
@@ -787,6 +787,7 @@ export const NowConfigPlugin = Plugin.create({
787
787
  packageResolverVersion: $.from('package_resolver_version').def(
788
788
  config.scope === 'global' ? MODULE_RESOLUTION.V2 : MODULE_RESOLUTION.V1
789
789
  ),
790
+ sysCode: $.from('sys_code').def(''),
790
791
  })),
791
792
  }),
792
793
  }
@@ -936,7 +937,7 @@ export const NowConfigPlugin = Plugin.create({
936
937
  package_resolver_version: $.from('packageResolverVersion').def(
937
938
  scope === 'global' ? MODULE_RESOLUTION.V2 : MODULE_RESOLUTION.V1
938
939
  ),
939
- sys_code: $.from('scope'),
940
+ sys_code: $.from('sysCode'),
940
941
  })),
941
942
  })
942
943