@servicenow/sdk-build-plugins 4.7.1 → 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 (87) 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-action-definition-plugin.js +38 -2
  29. package/dist/flow/plugins/flow-action-definition-plugin.js.map +1 -1
  30. package/dist/flow/plugins/flow-instance-plugin.js +1 -1
  31. package/dist/flow/plugins/flow-instance-plugin.js.map +1 -1
  32. package/dist/flow/plugins/step-instance-plugin.js +1 -1
  33. package/dist/flow/plugins/step-instance-plugin.js.map +1 -1
  34. package/dist/flow/utils/flow-constants.d.ts +7 -0
  35. package/dist/flow/utils/flow-constants.js +6 -1
  36. package/dist/flow/utils/flow-constants.js.map +1 -1
  37. package/dist/flow/utils/flow-shapes.d.ts +1 -1
  38. package/dist/form-plugin.js +35 -24
  39. package/dist/form-plugin.js.map +1 -1
  40. package/dist/index.d.ts +4 -0
  41. package/dist/index.js +4 -0
  42. package/dist/index.js.map +1 -1
  43. package/dist/now-attach-plugin.d.ts +1 -1
  44. package/dist/now-config-plugin.js +2 -1
  45. package/dist/now-config-plugin.js.map +1 -1
  46. package/dist/now-delete-plugin.d.ts +2 -0
  47. package/dist/now-delete-plugin.js +64 -0
  48. package/dist/now-delete-plugin.js.map +1 -0
  49. package/dist/record-plugin.d.ts +10 -0
  50. package/dist/record-plugin.js +15 -1
  51. package/dist/record-plugin.js.map +1 -1
  52. package/dist/repack/lint/Rules.js +17 -7
  53. package/dist/repack/lint/Rules.js.map +1 -1
  54. package/dist/rest-message-plugin.d.ts +2 -0
  55. package/dist/rest-message-plugin.js +331 -0
  56. package/dist/rest-message-plugin.js.map +1 -0
  57. package/dist/script-include-plugin.js +1 -1
  58. package/dist/script-include-plugin.js.map +1 -1
  59. package/dist/server-module-plugin/sbom-builder.js +17 -7
  60. package/dist/server-module-plugin/sbom-builder.js.map +1 -1
  61. package/dist/static-content-plugin.js +17 -7
  62. package/dist/static-content-plugin.js.map +1 -1
  63. package/dist/table-plugin.js +5 -2
  64. package/dist/table-plugin.js.map +1 -1
  65. package/package.json +6 -5
  66. package/src/alias/alias-plugin.ts +221 -0
  67. package/src/alias/alias-template-plugin.ts +271 -0
  68. package/src/alias/index.ts +3 -0
  69. package/src/alias/retry-policy-plugin.ts +138 -0
  70. package/src/arrow-function-plugin.ts +67 -23
  71. package/src/atf/test-plugin.ts +1 -1
  72. package/src/basic-syntax-plugin.ts +7 -7
  73. package/src/column/index.ts +7 -0
  74. package/src/dashboard/dashboard-plugin.ts +4 -0
  75. package/src/data-lookup-plugin.ts +191 -0
  76. package/src/flow/plugins/flow-action-definition-plugin.ts +49 -3
  77. package/src/flow/plugins/flow-instance-plugin.ts +2 -1
  78. package/src/flow/plugins/step-instance-plugin.ts +1 -1
  79. package/src/flow/utils/flow-constants.ts +8 -0
  80. package/src/form-plugin.ts +47 -26
  81. package/src/index.ts +4 -0
  82. package/src/now-config-plugin.ts +2 -1
  83. package/src/now-delete-plugin.ts +82 -0
  84. package/src/record-plugin.ts +17 -2
  85. package/src/rest-message-plugin.ts +391 -0
  86. package/src/script-include-plugin.ts +4 -1
  87. package/src/table-plugin.ts +7 -2
@@ -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
+ })
@@ -34,7 +34,7 @@ import {
34
34
  } from '../utils/flow-constants'
35
35
  import { STEP_PILL_TYPE_REGEX, stripPillType, collectPillTypes } from '../utils/flow-pill-utils'
36
36
  import { COLUMN_TYPE_TO_API } from '../../column/column-helper'
37
- import { getAttributeValue } from '../utils/schema-to-flow-object'
37
+ import { buildComplexObjectFromSchema, parseAttributesToObject } from '../utils/schema-to-flow-object'
38
38
  import { normalizeInputValue } from './flow-instance-plugin'
39
39
  import { ApprovalRulesShape, ApprovalDueDateShape } from '../utils/flow-shapes'
40
40
  import { ArrowFunctionShape } from '../../arrow-function-plugin'
@@ -1387,6 +1387,11 @@ const actionDefRelationships = {
1387
1387
  via: { name: 'name', element: 'element' },
1388
1388
  descendant: true,
1389
1389
  },
1390
+ sys_complex_object: {
1391
+ via: [complexObjectMatchesIoRecord],
1392
+ descendant: true,
1393
+ inverse: true,
1394
+ },
1390
1395
  },
1391
1396
  },
1392
1397
  sys_hub_input_scripts: {
@@ -1610,6 +1615,13 @@ export const ActionDefinitionPlugin = Plugin.create({
1610
1615
  sys_hub_step_ext_output: {
1611
1616
  via: 'model',
1612
1617
  descendant: true,
1618
+ relationships: {
1619
+ sys_complex_object: {
1620
+ via: [complexObjectMatchesIoRecord],
1621
+ descendant: true,
1622
+ inverse: true,
1623
+ },
1624
+ },
1613
1625
  },
1614
1626
  sys_translated_text: {
1615
1627
  via: 'documentkey',
@@ -1871,8 +1883,42 @@ export const ActionDefinitionPlugin = Plugin.create({
1871
1883
  const mandatory = extOutput.get('mandatory')?.ifString()?.getValue() === 'true'
1872
1884
  const internalType = extOutput.get('internal_type')?.ifString()?.getValue() ?? 'string'
1873
1885
  // Check attributes for uiType to handle cases where internal_type is generic
1874
- const attributes = extOutput.get('attributes')?.ifString()?.getValue() ?? ''
1875
- const uiType = getAttributeValue(attributes, 'uiType') ?? internalType
1886
+ const attributesStr = extOutput.get('attributes')?.ifString()?.getValue() ?? ''
1887
+ const parsedAttributes = parseAttributesToObject(attributesStr)
1888
+ const uiType = parsedAttributes.uiType ?? internalType
1889
+
1890
+ // Handle FlowObject/FlowArray complex types via co_type_name
1891
+ const maybeCoTypeName = parsedAttributes.co_type_name
1892
+ if (maybeCoTypeName) {
1893
+ const coRecord = descendants.query('sys_complex_object', { name: maybeCoTypeName })
1894
+ let serializedContent = coRecord[0]
1895
+ ?.get('serialized_content')
1896
+ ?.ifString()
1897
+ ?.getValue()
1898
+ let complexObjectSysId: string | undefined = coRecord[0]?.getId().getValue()
1899
+ if (!serializedContent) {
1900
+ const builtIn = builtInComplexObjects[maybeCoTypeName]
1901
+ serializedContent = builtIn?.data.serialized_content
1902
+ complexObjectSysId = undefined
1903
+ }
1904
+ if (serializedContent) {
1905
+ const varSysId = extOutput.getId().getValue()
1906
+ const isDeterministic = maybeCoTypeName === `FD${varSysId}`
1907
+ const schema = JSON.parse(serializedContent)
1908
+ outputVariablesObj[elementName] = buildComplexObjectFromSchema(
1909
+ extOutput,
1910
+ schema,
1911
+ parsedAttributes,
1912
+ isDeterministic ? undefined : complexObjectSysId,
1913
+ isDeterministic ? undefined : maybeCoTypeName
1914
+ )
1915
+ continue
1916
+ }
1917
+ logger.warn(
1918
+ `Unable to find sys_complex_object with co_type_name '${maybeCoTypeName}' for step output '${elementName}'; falling back to StringColumn`
1919
+ )
1920
+ }
1921
+
1876
1922
  const columnApiName = COLUMN_TYPE_TO_API[uiType] ?? 'StringColumn'
1877
1923
  const columnProps: Props = { label }
1878
1924
  if (mandatory) {
@@ -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,