@servicenow/sdk-build-plugins 4.5.0 → 4.6.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 (144) hide show
  1. package/dist/column-plugin.js +3 -7
  2. package/dist/column-plugin.js.map +1 -1
  3. package/dist/flow/flow-logic/flow-logic-diagnostics.js +5 -5
  4. package/dist/flow/flow-logic/flow-logic-diagnostics.js.map +1 -1
  5. package/dist/flow/plugins/flow-action-definition-plugin.js +1229 -54
  6. package/dist/flow/plugins/flow-action-definition-plugin.js.map +1 -1
  7. package/dist/flow/plugins/flow-data-pill-plugin.js +5 -2
  8. package/dist/flow/plugins/flow-data-pill-plugin.js.map +1 -1
  9. package/dist/flow/plugins/flow-definition-plugin.js +16 -42
  10. package/dist/flow/plugins/flow-definition-plugin.js.map +1 -1
  11. package/dist/flow/plugins/flow-diagnostics-plugin.d.ts +2 -2
  12. package/dist/flow/plugins/flow-diagnostics-plugin.js +2 -2
  13. package/dist/flow/plugins/flow-instance-plugin.js +68 -22
  14. package/dist/flow/plugins/flow-instance-plugin.js.map +1 -1
  15. package/dist/flow/plugins/step-definition-plugin.js +2 -1
  16. package/dist/flow/plugins/step-definition-plugin.js.map +1 -1
  17. package/dist/flow/plugins/step-instance-plugin.d.ts +9 -1
  18. package/dist/flow/plugins/step-instance-plugin.js +649 -136
  19. package/dist/flow/plugins/step-instance-plugin.js.map +1 -1
  20. package/dist/flow/plugins/wfa-datapill-plugin.js +20 -5
  21. package/dist/flow/plugins/wfa-datapill-plugin.js.map +1 -1
  22. package/dist/flow/post-install.js +1 -0
  23. package/dist/flow/post-install.js.map +1 -1
  24. package/dist/flow/utils/complex-object-resolver.js +4 -1
  25. package/dist/flow/utils/complex-object-resolver.js.map +1 -1
  26. package/dist/flow/utils/complex-objects.js +1 -1
  27. package/dist/flow/utils/complex-objects.js.map +1 -1
  28. package/dist/flow/utils/flow-constants.d.ts +66 -2
  29. package/dist/flow/utils/flow-constants.js +402 -6
  30. package/dist/flow/utils/flow-constants.js.map +1 -1
  31. package/dist/flow/utils/flow-io-to-record.d.ts +1 -1
  32. package/dist/flow/utils/flow-io-to-record.js +37 -16
  33. package/dist/flow/utils/flow-io-to-record.js.map +1 -1
  34. package/dist/flow/utils/flow-shapes.js +4 -0
  35. package/dist/flow/utils/flow-shapes.js.map +1 -1
  36. package/dist/flow/utils/label-cache-parser.d.ts +9 -2
  37. package/dist/flow/utils/label-cache-parser.js +32 -4
  38. package/dist/flow/utils/label-cache-parser.js.map +1 -1
  39. package/dist/flow/utils/pill-shape-helpers.d.ts +15 -0
  40. package/dist/flow/utils/pill-shape-helpers.js +35 -0
  41. package/dist/flow/utils/pill-shape-helpers.js.map +1 -0
  42. package/dist/flow/utils/pill-string-parser.js +1 -0
  43. package/dist/flow/utils/pill-string-parser.js.map +1 -1
  44. package/dist/flow/utils/schema-to-flow-object.d.ts +6 -1
  45. package/dist/flow/utils/schema-to-flow-object.js +131 -15
  46. package/dist/flow/utils/schema-to-flow-object.js.map +1 -1
  47. package/dist/flow/utils/utils.d.ts +1 -0
  48. package/dist/flow/utils/utils.js +6 -1
  49. package/dist/flow/utils/utils.js.map +1 -1
  50. package/dist/form-plugin.js +7 -9
  51. package/dist/form-plugin.js.map +1 -1
  52. package/dist/inbound-email-action-plugin.d.ts +10 -0
  53. package/dist/inbound-email-action-plugin.js +128 -0
  54. package/dist/inbound-email-action-plugin.js.map +1 -0
  55. package/dist/index.d.ts +4 -0
  56. package/dist/index.js +4 -0
  57. package/dist/index.js.map +1 -1
  58. package/dist/instance-scan-plugin.js +0 -5
  59. package/dist/instance-scan-plugin.js.map +1 -1
  60. package/dist/property-plugin.js +1 -1
  61. package/dist/property-plugin.js.map +1 -1
  62. package/dist/record-plugin.d.ts +7 -0
  63. package/dist/record-plugin.js +10 -2
  64. package/dist/record-plugin.js.map +1 -1
  65. package/dist/rest-api-plugin.js +8 -1
  66. package/dist/rest-api-plugin.js.map +1 -1
  67. package/dist/schedule-script/scheduled-script-plugin.js +8 -3
  68. package/dist/schedule-script/scheduled-script-plugin.js.map +1 -1
  69. package/dist/service-catalog/service-catalog-base.d.ts +18 -18
  70. package/dist/service-catalog/service-catalog-base.js +22 -22
  71. package/dist/service-catalog/service-catalog-base.js.map +1 -1
  72. package/dist/service-portal/header-footer-plugin.d.ts +2 -0
  73. package/dist/service-portal/header-footer-plugin.js +50 -0
  74. package/dist/service-portal/header-footer-plugin.js.map +1 -0
  75. package/dist/service-portal/menu-plugin.js +3 -22
  76. package/dist/service-portal/menu-plugin.js.map +1 -1
  77. package/dist/service-portal/page-plugin.js +3 -24
  78. package/dist/service-portal/page-plugin.js.map +1 -1
  79. package/dist/service-portal/page-route-map-plugin.d.ts +2 -0
  80. package/dist/service-portal/page-route-map-plugin.js +114 -0
  81. package/dist/service-portal/page-route-map-plugin.js.map +1 -0
  82. package/dist/service-portal/portal-plugin.js +21 -8
  83. package/dist/service-portal/portal-plugin.js.map +1 -1
  84. package/dist/service-portal/utils.d.ts +40 -2
  85. package/dist/service-portal/utils.js +283 -2
  86. package/dist/service-portal/utils.js.map +1 -1
  87. package/dist/service-portal/widget-plugin.js +9 -218
  88. package/dist/service-portal/widget-plugin.js.map +1 -1
  89. package/dist/static-content-plugin.js +4 -0
  90. package/dist/static-content-plugin.js.map +1 -1
  91. package/dist/table-plugin.js +190 -26
  92. package/dist/table-plugin.js.map +1 -1
  93. package/dist/ui-action-plugin.js +1 -4
  94. package/dist/ui-action-plugin.js.map +1 -1
  95. package/dist/ui-page-plugin.js +68 -13
  96. package/dist/ui-page-plugin.js.map +1 -1
  97. package/dist/view-plugin.js +8 -3
  98. package/dist/view-plugin.js.map +1 -1
  99. package/dist/workspace-plugin.js +39 -36
  100. package/dist/workspace-plugin.js.map +1 -1
  101. package/package.json +5 -4
  102. package/src/column-plugin.ts +3 -8
  103. package/src/flow/flow-logic/flow-logic-diagnostics.ts +5 -6
  104. package/src/flow/plugins/flow-action-definition-plugin.ts +1581 -61
  105. package/src/flow/plugins/flow-data-pill-plugin.ts +5 -2
  106. package/src/flow/plugins/flow-definition-plugin.ts +12 -47
  107. package/src/flow/plugins/flow-diagnostics-plugin.ts +2 -2
  108. package/src/flow/plugins/flow-instance-plugin.ts +98 -22
  109. package/src/flow/plugins/step-definition-plugin.ts +2 -1
  110. package/src/flow/plugins/step-instance-plugin.ts +772 -156
  111. package/src/flow/plugins/wfa-datapill-plugin.ts +25 -5
  112. package/src/flow/post-install.ts +1 -0
  113. package/src/flow/utils/complex-object-resolver.ts +4 -1
  114. package/src/flow/utils/complex-objects.ts +1 -1
  115. package/src/flow/utils/flow-constants.ts +421 -5
  116. package/src/flow/utils/flow-io-to-record.ts +43 -17
  117. package/src/flow/utils/flow-shapes.ts +4 -0
  118. package/src/flow/utils/label-cache-parser.ts +33 -4
  119. package/src/flow/utils/pill-shape-helpers.ts +42 -0
  120. package/src/flow/utils/pill-string-parser.ts +1 -0
  121. package/src/flow/utils/schema-to-flow-object.ts +183 -15
  122. package/src/flow/utils/utils.ts +12 -1
  123. package/src/form-plugin.ts +1 -3
  124. package/src/inbound-email-action-plugin.ts +145 -0
  125. package/src/index.ts +4 -0
  126. package/src/instance-scan-plugin.ts +0 -5
  127. package/src/property-plugin.ts +4 -1
  128. package/src/record-plugin.ts +14 -4
  129. package/src/rest-api-plugin.ts +7 -1
  130. package/src/schedule-script/scheduled-script-plugin.ts +14 -3
  131. package/src/service-catalog/service-catalog-base.ts +22 -22
  132. package/src/service-portal/header-footer-plugin.ts +57 -0
  133. package/src/service-portal/menu-plugin.ts +1 -23
  134. package/src/service-portal/page-plugin.ts +3 -28
  135. package/src/service-portal/page-route-map-plugin.ts +124 -0
  136. package/src/service-portal/portal-plugin.ts +33 -10
  137. package/src/service-portal/utils.ts +404 -3
  138. package/src/service-portal/widget-plugin.ts +14 -290
  139. package/src/static-content-plugin.ts +3 -0
  140. package/src/table-plugin.ts +226 -36
  141. package/src/ui-action-plugin.ts +1 -8
  142. package/src/ui-page-plugin.ts +76 -13
  143. package/src/view-plugin.ts +10 -4
  144. package/src/workspace-plugin.ts +43 -43
@@ -91,10 +91,13 @@ export function determineRootIdentifierAndPath(
91
91
  let pathStr = ''
92
92
 
93
93
  switch (propertyNames[1]) {
94
- case 'inputs':
95
- rootIdentifier = 'subflow' // <-- Hardcoded: But need to handle if this input is in action/subflow body when we do action body impl.
94
+ case 'inputs': {
95
+ // Detect if inside Action() body emit {{action.xxx}}, otherwise {{subflow.xxx}}
96
+ const actionAncestor = findAncestorByCalleeName(propertyAccessShape.getOriginalNode(), 'Action')
97
+ rootIdentifier = actionAncestor ? 'action' : 'subflow'
96
98
  pathStr = propertyNames.slice(2).join('.')
97
99
  break
100
+ }
98
101
  case 'flowVariables':
99
102
  // For params.flowVariables (schema reference), use 'flowVariables' as identifier with empty path
100
103
  // For params.flowVariables.someVar (property access), use 'flow_variable' as identifier
@@ -17,7 +17,6 @@ import {
17
17
  type Diagnostics,
18
18
  ts,
19
19
  deleteMultipleDiff,
20
- StringLiteralShape,
21
20
  type Logger,
22
21
  } from '@servicenow/sdk-build-core'
23
22
  import { gzipSync } from 'node:zlib'
@@ -55,6 +54,7 @@ import {
55
54
  createPropertyAccessFromPath,
56
55
  } from '../utils/pill-string-parser'
57
56
  import { isDataPill } from '../utils/complex-object-resolver'
57
+ import { wrapWithDataPillCall, extractDataPillNames } from '../utils/pill-shape-helpers'
58
58
  import { ArrayShape } from '@servicenow/sdk-build-core'
59
59
  import { processFlowInputs, processFlowOutputs, processFlowVariables } from '../utils/flow-variable-processor'
60
60
  import { buildUuidToIdentifierMap, processInstanceForDatapills, uuidToRecordMap } from '../utils/datapill-transformer'
@@ -211,7 +211,7 @@ import { ApprovalDueDateShape, ApprovalRulesShape } from '../utils/flow-shapes'
211
211
 
212
212
  /**
213
213
  * Traverse ancestors to find the topmost ArrowFunction and extract its parameter name
214
- * This finds the ArrowFunction that's an argument to Flow/SubflowDefinition, ignoring nested
214
+ * This finds the ArrowFunction that's an argument to Flow/Subflow, ignoring nested
215
215
  * ArrowFunctions from control flow APIs like If, ElseIf, ForEach.
216
216
  *
217
217
  * @param shape - The shape to start traversing from
@@ -446,45 +446,6 @@ function processShapeForDatapills(
446
446
  * @param dataType - The data type string (default: 'string')
447
447
  * @returns CallExpressionShape wrapping the expression with wfa.dataPill()
448
448
  */
449
- function wrapWithDataPillCall(
450
- expression: PropertyAccessShape | IdentifierShape,
451
- source: Record,
452
- dataType = 'string'
453
- ): CallExpressionShape {
454
- return new CallExpressionShape({
455
- source,
456
- callee: 'wfa.dataPill',
457
- args: [expression, new StringLiteralShape({ source, literalText: dataType })],
458
- })
459
- }
460
-
461
- /**
462
- * Extract all datapill names from a string value containing datapill patterns
463
- * Handles both UUID-based pills ({{uuid.property}}) and semantic pills ({{prefix.path}})
464
- *
465
- * @param stringValue - String that may contain datapill patterns
466
- * @returns Array of datapill names (e.g., ["trigger.table_name", "uuid.record.number"])
467
- */
468
- function extractDataPillNames(stringValue: string): string[] {
469
- const dataPillNames: string[] = []
470
-
471
- // Match all datapill patterns: {{...}}
472
- const pillPattern = /\{\{([^}]+)\}\}/g
473
- const matches = Array.from(stringValue.matchAll(pillPattern))
474
-
475
- for (const match of matches) {
476
- if (match[1]) {
477
- // Extract the content inside {{}} and split by | to get just the name part
478
- // (some pills may have type info like {{pill.name|string}})
479
- const pillContent = match[1].split('|')[0]
480
- if (pillContent) {
481
- dataPillNames.push(pillContent)
482
- }
483
- }
484
- }
485
-
486
- return dataPillNames
487
- }
488
449
 
489
450
  /**
490
451
  * Wrap all PropertyAccessShape expressions in a TemplateExpressionShape with wfa.dataPill()
@@ -615,16 +576,17 @@ function extractAllPills(
615
576
  const pipeIndex = content.indexOf('|')
616
577
  const cleanContent = pipeIndex !== -1 ? content.substring(0, pipeIndex) : content
617
578
 
618
- // Try UUID pattern first (more specific)
619
- // UUID format: 8-4-4-4-12 hex digits followed by dot and property path
579
+ // Match UUID-based pills: both bare UUID (action/subflow outputs) and step[UUID] (action step outputs)
580
+ // Bare: {{uuid.property}} → e.g., action/subflow instance output
581
+ // Step: {{step[uuid].property}} → e.g., action step output
620
582
  const uuidMatch = cleanContent.match(
621
- /^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.([a-zA-Z_][a-zA-Z0-9_.]*[a-zA-Z0-9_])$/
583
+ /^(?:step\[([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\]|([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}))\.([a-zA-Z_][a-zA-Z0-9_.]*[a-zA-Z0-9_])$/
622
584
  )
623
585
 
624
586
  if (uuidMatch) {
625
587
  // This is a UUID-based pill
626
- const uuid = uuidMatch[1]
627
- const property = uuidMatch[2]
588
+ const uuid = uuidMatch[1] ?? uuidMatch[2]
589
+ const property = uuidMatch[3]
628
590
 
629
591
  if (!uuid || !property) {
630
592
  continue
@@ -633,7 +595,10 @@ function extractAllPills(
633
595
  const identifier = uuidToIdentifierMap.get(uuid)
634
596
 
635
597
  if (identifier) {
636
- // PRESERVE EXISTING LOGIC: Apply forEach parameter handling
598
+ // PRESERVE EXISTING LOGIC: Apply forEach parameter handling.
599
+ // Note: With the consolidated regex, this is now called for step[UUID] pills too
600
+ // (previously only bare UUID pills hit this code). This is harmless since action
601
+ // step CIDs are never forEach parameters, so isForEachParam will always be false.
637
602
  const isForEachParam = isForEachParameter(identifier, uuid)
638
603
  let expression: PropertyAccessShape | IdentifierShape
639
604
 
@@ -90,8 +90,8 @@ function isInsideFlowContext(node: ts.Node): boolean {
90
90
  * - wfa.flowLogic.goBackTo
91
91
  * - wfa.flowLogic.tryCatch
92
92
  * - wfa.flowLogic.doInParallel
93
- * - FlowObject (must be within Flow/Subflow/ActionDefinition/SubflowDefinition/TriggerDefinition)
94
- * - FlowArray (must be within Flow/Subflow/ActionDefinition/SubflowDefinition/TriggerDefinition)
93
+ * - FlowObject (must be within Flow/Subflow/Action/TriggerDefinition)
94
+ * - FlowArray (must be within Flow/Subflow/Action/TriggerDefinition)
95
95
  */
96
96
  export const FlowDiagnosticsPlugin = Plugin.create({
97
97
  name: 'FlowDiagnosticsPlugin',
@@ -166,10 +166,13 @@ export function normalizeInputValue(value: string, uiType?: string, source?: Sou
166
166
  // Create a StringShape from the value and use ApprovalRulesShape.from() to parse it
167
167
  return ApprovalRulesShape.from(source, Shape.from(source, value).asString())
168
168
  } else if (uiType === APPROVAL_DUE_DATE_DATA_TYPE_VALUE) {
169
- // Check if source is a Record before accessing action_type
169
+ // Check if source is a Record before accessing action_type or step_type.
170
+ // Flow context uses action_type; action definition context uses step_type.
170
171
  if (source instanceof Record) {
171
172
  const actionType = source.get(ACTION_TYPE_KEY_NAME)?.ifDefined()?.asString()?.getValue()
172
- if (APPROVAL_DUE_DATE_INPUT_FIELD_ACTIONS.includes(actionType as string)) {
173
+ const stepType = source.get('step_type')?.ifDefined()?.asString()?.getValue()
174
+ const typeId = actionType ?? stepType
175
+ if (typeId && APPROVAL_DUE_DATE_INPUT_FIELD_ACTIONS.includes(typeId)) {
173
176
  // Create a StringShape from the value and use ApprovalDueDateShape.from() to parse it
174
177
  return ApprovalDueDateShape.from(source, Shape.from(source, value).asString())
175
178
  }
@@ -277,10 +280,6 @@ function buildInstanceToShape({
277
280
  }
278
281
 
279
282
  const coreActionIdentifier = getCoreActionIdentifier(sysId)
280
- if (callee === ACTION_INSTANCE_API_NAME && !coreActionIdentifier) {
281
- logger.warn(`Custom actions are not supported, action = ${sysId}`)
282
- return { success: false }
283
- }
284
283
 
285
284
  const action = database
286
285
  .query('sys_hub_action_type_definition')
@@ -327,13 +326,12 @@ function buildInstanceToShape({
327
326
  if (inputsShape === undefined) {
328
327
  return { success: false as const }
329
328
  }
330
- const waitForCompletion =
331
- record.get('wait_for_completion')?.getValue() === true ||
332
- record.get('wait_for_completion')?.getValue() === 'true'
333
- if (waitForCompletion) {
329
+ const waitForCompletionValue = record.get('wait_for_completion')?.getValue()
330
+ const isWaitForCompletionFalse = waitForCompletionValue === false || waitForCompletionValue === 'false'
331
+ if (isWaitForCompletionFalse) {
334
332
  inputsShape = inputsShape
335
- ? inputsShape.merge({ waitForCompletion })
336
- : Shape.from(record, { waitForCompletion }).asObject()
333
+ ? inputsShape.merge({ waitForCompletion: false })
334
+ : Shape.from(record, { waitForCompletion: false }).asObject()
337
335
  }
338
336
 
339
337
  const configArg = new ObjectShape({
@@ -533,6 +531,7 @@ export const FlowInstancePlugin = Plugin.create({
533
531
  factory,
534
532
  source: callExpression,
535
533
  transform,
534
+ diagnostics,
536
535
  })
537
536
  }
538
537
 
@@ -728,6 +727,7 @@ async function buildActionInstance({
728
727
  const values = actionDef
729
728
  ? await prepareActionInstanceValueJson(inputs, actionDef, transform, logger, diagnostics)
730
729
  : undefined
730
+
731
731
  const actionDefRecord = actionDef instanceof Record ? actionDef : undefined
732
732
  const instanceProps = inputs.transform(({ $ }) => ({
733
733
  active: $.val(true),
@@ -767,6 +767,7 @@ async function buildSubflowInstance({
767
767
  uuid,
768
768
  source,
769
769
  transform,
770
+ diagnostics,
770
771
  }: {
771
772
  subflowDef: Record | StringShape | undefined
772
773
  inputs: ObjectShape
@@ -776,8 +777,11 @@ async function buildSubflowInstance({
776
777
  uuid: string | undefined
777
778
  source: Source
778
779
  transform: Transform
780
+ diagnostics: Diagnostics
779
781
  }): Promise<Record | undefined> {
780
- const values = subflowDef ? await prepareSubflowInstanceValueJson(inputs, subflowDef, transform) : undefined
782
+ const values = subflowDef
783
+ ? await prepareSubflowInstanceValueJson(inputs, subflowDef, transform, diagnostics)
784
+ : undefined
781
785
 
782
786
  const subflowDefRecord = subflowDef instanceof Record ? subflowDef : undefined
783
787
  const instanceProps = inputs.transform(({ $ }) => ({
@@ -789,7 +793,7 @@ async function buildSubflowInstance({
789
793
  generation_source: $.val(''),
790
794
  subflow: $.val(subflowDefRecord?.getId()?.getValue() || subflowDef?.getValue() || ''),
791
795
  subflow_inputs: $.val(values),
792
- wait_for_completion: $.from('waitForCompletion').def(false),
796
+ wait_for_completion: $.from('waitForCompletion').def(true),
793
797
  sys_class_name: $.val('sys_hub_sub_flow_instance_v2'),
794
798
  parent_ui_id: $.def(''),
795
799
  }))
@@ -1052,6 +1056,61 @@ function processDefaultOrHiddenInput(
1052
1056
  return undefined
1053
1057
  }
1054
1058
 
1059
+ /**
1060
+ * Validates mandatory fields on raw shapes before datapill or inline script resolution. Only `StringShape`
1061
+ * values are validated for emptiness. Runtime-evaluated shapes (data pills, template expressions, inline
1062
+ * scripts, identifiers) are exempt because their values cannot be determined at build time.
1063
+ *
1064
+ * @param definition - The action or subflow definition record containing input definitions.
1065
+ * @param instanceInputs - The raw ObjectShape of user-provided input values before resolution.
1066
+ * @param diagnostics - Diagnostics instance for reporting validation errors.
1067
+ * @param inputTable - The table name for input definitions (e.g., 'sys_hub_action_input' or 'sys_hub_flow_input').
1068
+ * @param definitionName - Optional name of the action or subflow for more specific error messages.
1069
+ * @returns The filtered input definition records for reuse, or undefined if no definition.
1070
+ */
1071
+ function validateMandatoryFields(
1072
+ definition: Record | undefined,
1073
+ instanceInputs: ObjectShape,
1074
+ diagnostics: Diagnostics,
1075
+ inputTable: string,
1076
+ definitionName?: string
1077
+ ): Record[] | undefined {
1078
+ const inputDefinitions = definition
1079
+ ? definition.flat().filter((record) => record.getTable() === inputTable)
1080
+ : undefined
1081
+
1082
+ if (inputDefinitions) {
1083
+ const rawEntries = new Map(instanceInputs.entries({ resolve: false }))
1084
+
1085
+ for (const inputDefinition of inputDefinitions) {
1086
+ const inputName = inputDefinition.get('element')?.asString()?.getValue() ?? ''
1087
+ if (!inputName) {
1088
+ continue
1089
+ }
1090
+
1091
+ const isMandatory = inputDefinition.get('mandatory')?.ifBoolean()?.getValue() ?? false
1092
+ if (!isMandatory) {
1093
+ continue
1094
+ }
1095
+
1096
+ const rawShape = rawEntries.get(inputName)
1097
+ if (!rawShape) {
1098
+ continue
1099
+ }
1100
+
1101
+ if (rawShape.isString() && rawShape.isEmpty()) {
1102
+ const context = definitionName ? ` in action '${definitionName}'` : ''
1103
+ diagnostics.error(
1104
+ rawShape,
1105
+ `Mandatory field '${inputName}'${context} cannot be an empty string. Please provide a non-empty value.`
1106
+ )
1107
+ }
1108
+ }
1109
+ }
1110
+
1111
+ return inputDefinitions
1112
+ }
1113
+
1055
1114
  /**
1056
1115
  * Resolve a catalog item record from a Shape (direct Record, TemplateExpressionShape, or IdentifierShape).
1057
1116
  */
@@ -1237,14 +1296,24 @@ async function prepareActionInstanceValueJson(
1237
1296
  }
1238
1297
  const isActionDefString = actionDef instanceof StringShape
1239
1298
 
1299
+ // Resolve action name early for use in validation messages and special handling
1300
+ const actionSysId = actionDefRecord?.getId()?.getValue()
1301
+ const actionName = typeof actionSysId === 'string' ? CORE_ACTIONS_SYS_ID_NAME_MAP[actionSysId] : undefined
1302
+
1303
+ const definitionInputs = validateMandatoryFields(
1304
+ actionDefRecord,
1305
+ instanceInputs,
1306
+ diagnostics,
1307
+ 'sys_hub_action_input',
1308
+ actionName
1309
+ )
1310
+
1240
1311
  // Special handling for getCatalogVariables action
1241
1312
  let overrideValues = new Map<string, string>()
1242
1313
  let displayValues = new Map<string, string>()
1243
1314
  let unsupportedFieldsForAction: string[] | undefined
1244
1315
 
1245
1316
  if (actionDefRecord) {
1246
- const actionSysId = actionDefRecord.getId().getValue()
1247
- const actionName = typeof actionSysId === 'string' ? CORE_ACTIONS_SYS_ID_NAME_MAP[actionSysId] : undefined
1248
1317
  unsupportedFieldsForAction = actionName ? INLINE_SCRIPT_UNSUPPORTED_FIELDS[actionName] : undefined
1249
1318
 
1250
1319
  // Check if this is the getCatalogVariables action or createCatalogTask action
@@ -1287,9 +1356,8 @@ async function prepareActionInstanceValueJson(
1287
1356
 
1288
1357
  // When isActionDefString is true, there's no action definition in fluent
1289
1358
  // We expect arbitrary inputs and should process all provided inputs directly
1290
- if (actionDefRecord) {
1291
- // When action definition record is present, process only definition inputs
1292
- const definitionInputs = actionDefRecord.flat().filter((v) => v.getTable() === 'sys_hub_action_input')
1359
+ if (actionDefRecord && definitionInputs) {
1360
+ // When action definition record is present, process only the definition inputs.
1293
1361
  const providedInputNames = new Set(objShape.keys())
1294
1362
 
1295
1363
  for (const inputDef of definitionInputs) {
@@ -1351,8 +1419,15 @@ async function prepareActionInstanceValueJson(
1351
1419
  async function prepareSubflowInstanceValueJson(
1352
1420
  instanceInputs: ObjectShape,
1353
1421
  subflowDef: Record | StringShape,
1354
- transform: Transform
1422
+ transform: Transform,
1423
+ diagnostics: Diagnostics
1355
1424
  ) {
1425
+ const subflowDefRecord = subflowDef.isRecord() ? subflowDef.as(Record) : undefined
1426
+ const subflowName = subflowDefRecord?.get('name')?.ifString()?.getValue()
1427
+
1428
+ // Validate mandatory fields on raw shapes before any resolution.
1429
+ validateMandatoryFields(subflowDefRecord, instanceInputs, diagnostics, 'sys_hub_flow_input', subflowName)
1430
+
1356
1431
  // Check for datapills and resolve them
1357
1432
  const dataPillResults = await checkAndResolveDataPills(instanceInputs, transform)
1358
1433
 
@@ -1386,8 +1461,9 @@ async function prepareSubflowInstanceValueJson(
1386
1461
  value: primitiveValue,
1387
1462
  internalType: undefined,
1388
1463
  }
1389
- if (subflowDef.isRecord()) {
1390
- resolvedValue = resolveComplexInput(key, primitiveValue, subflowDef.as(Record), 'sys_hub_flow_input')
1464
+
1465
+ if (subflowDefRecord) {
1466
+ resolvedValue = resolveComplexInput(key, primitiveValue, subflowDefRecord, 'sys_hub_flow_input')
1391
1467
  }
1392
1468
 
1393
1469
  const result: globalThis.Record<string, unknown> = {
@@ -2,12 +2,13 @@ import { CallExpressionShape, Plugin } from '@servicenow/sdk-build-core'
2
2
  import { buildVariableShapes } from '../utils/flow-io-to-record'
3
3
  import { NowIdShape } from '../../now-id-plugin'
4
4
  import { generateXML } from '../utils/flow-to-xml'
5
+ import { createSdkDocEntry } from '../../utils'
5
6
  /**
6
7
  * Plugin for processing StepDefinition calls
7
8
  */
8
9
  export const StepDefinitionPlugin = Plugin.create({
9
10
  name: 'StepDefinitionPlugin',
10
- docs: [],
11
+ docs: [createSdkDocEntry('ActionStepDefinition', ['sys_flow_step_definition'])],
11
12
  records: {
12
13
  sys_flow_step_definition: {
13
14
  relationships: {