@servicenow/sdk-build-plugins 4.2.0 → 4.4.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.
- package/dist/acl-plugin.js +11 -0
- package/dist/acl-plugin.js.map +1 -1
- package/dist/applicability-plugin.d.ts +2 -0
- package/dist/applicability-plugin.js +72 -0
- package/dist/applicability-plugin.js.map +1 -0
- package/dist/atf/test-plugin.js +5 -2
- package/dist/atf/test-plugin.js.map +1 -1
- package/dist/basic-syntax-plugin.js +7 -1
- package/dist/basic-syntax-plugin.js.map +1 -1
- package/dist/business-rule-plugin.js +1 -0
- package/dist/business-rule-plugin.js.map +1 -1
- package/dist/call-expression-plugin.js +1 -107
- package/dist/call-expression-plugin.js.map +1 -1
- package/dist/column/column-to-record.d.ts +10 -3
- package/dist/column/column-to-record.js +44 -7
- package/dist/column/column-to-record.js.map +1 -1
- package/dist/column-plugin.d.ts +3 -1
- package/dist/column-plugin.js +12 -12
- package/dist/column-plugin.js.map +1 -1
- package/dist/dashboard/dashboard-component-property-defaults.d.ts +152 -0
- package/dist/dashboard/dashboard-component-property-defaults.js +264 -0
- package/dist/dashboard/dashboard-component-property-defaults.js.map +1 -0
- package/dist/dashboard/dashboard-component-resolver.d.ts +13 -0
- package/dist/dashboard/dashboard-component-resolver.js +69 -0
- package/dist/dashboard/dashboard-component-resolver.js.map +1 -0
- package/dist/dashboard/dashboard-plugin.d.ts +12 -0
- package/dist/dashboard/dashboard-plugin.js +397 -0
- package/dist/dashboard/dashboard-plugin.js.map +1 -0
- package/dist/data-plugin.d.ts +3 -0
- package/dist/data-plugin.js +61 -113
- package/dist/data-plugin.js.map +1 -1
- package/dist/email-notification-plugin.d.ts +2 -0
- package/dist/email-notification-plugin.js +541 -0
- package/dist/email-notification-plugin.js.map +1 -0
- package/dist/flow/constants/flow-plugin-constants.d.ts +58 -0
- package/dist/flow/constants/flow-plugin-constants.js +70 -0
- package/dist/flow/constants/flow-plugin-constants.js.map +1 -0
- package/dist/flow/flow-logic/flow-logic-constants.d.ts +38 -0
- package/dist/flow/flow-logic/flow-logic-constants.js +118 -0
- package/dist/flow/flow-logic/flow-logic-constants.js.map +1 -0
- package/dist/flow/flow-logic/flow-logic-diagnostics.d.ts +19 -0
- package/dist/flow/flow-logic/flow-logic-diagnostics.js +503 -0
- package/dist/flow/flow-logic/flow-logic-diagnostics.js.map +1 -0
- package/dist/flow/flow-logic/flow-logic-plugin-helpers.d.ts +62 -0
- package/dist/flow/flow-logic/flow-logic-plugin-helpers.js +2092 -0
- package/dist/flow/flow-logic/flow-logic-plugin-helpers.js.map +1 -0
- package/dist/flow/flow-logic/flow-logic-plugin.d.ts +52 -0
- package/dist/flow/flow-logic/flow-logic-plugin.js +283 -0
- package/dist/flow/flow-logic/flow-logic-plugin.js.map +1 -0
- package/dist/flow/flow-logic/flow-logic-shapes.d.ts +104 -0
- package/dist/flow/flow-logic/flow-logic-shapes.js +201 -0
- package/dist/flow/flow-logic/flow-logic-shapes.js.map +1 -0
- package/dist/flow/plugins/approval-rules-plugin.d.ts +2 -0
- package/dist/flow/plugins/approval-rules-plugin.js +49 -0
- package/dist/flow/plugins/approval-rules-plugin.js.map +1 -0
- package/dist/flow/plugins/flow-action-definition-plugin.d.ts +2 -0
- package/dist/flow/plugins/flow-action-definition-plugin.js +286 -0
- package/dist/flow/plugins/flow-action-definition-plugin.js.map +1 -0
- package/dist/flow/plugins/flow-data-pill-plugin.d.ts +9 -0
- package/dist/flow/plugins/flow-data-pill-plugin.js +212 -0
- package/dist/flow/plugins/flow-data-pill-plugin.js.map +1 -0
- package/dist/flow/plugins/flow-definition-plugin.d.ts +2 -0
- package/dist/flow/plugins/flow-definition-plugin.js +1668 -0
- package/dist/flow/plugins/flow-definition-plugin.js.map +1 -0
- package/dist/flow/plugins/flow-diagnostics-plugin.d.ts +26 -0
- package/dist/flow/plugins/flow-diagnostics-plugin.js +217 -0
- package/dist/flow/plugins/flow-diagnostics-plugin.js.map +1 -0
- package/dist/flow/plugins/flow-instance-plugin.d.ts +12 -0
- package/dist/flow/plugins/flow-instance-plugin.js +1205 -0
- package/dist/flow/plugins/flow-instance-plugin.js.map +1 -0
- package/dist/flow/plugins/flow-trigger-instance-plugin.d.ts +2 -0
- package/dist/flow/plugins/flow-trigger-instance-plugin.js +338 -0
- package/dist/flow/plugins/flow-trigger-instance-plugin.js.map +1 -0
- package/dist/flow/plugins/inline-script-plugin.d.ts +39 -0
- package/dist/flow/plugins/inline-script-plugin.js +80 -0
- package/dist/flow/plugins/inline-script-plugin.js.map +1 -0
- package/dist/flow/plugins/step-definition-plugin.d.ts +5 -0
- package/dist/flow/plugins/step-definition-plugin.js +71 -0
- package/dist/flow/plugins/step-definition-plugin.js.map +1 -0
- package/dist/flow/plugins/step-instance-plugin.d.ts +31 -0
- package/dist/flow/plugins/step-instance-plugin.js +339 -0
- package/dist/flow/plugins/step-instance-plugin.js.map +1 -0
- package/dist/flow/plugins/trigger-plugin.d.ts +2 -0
- package/dist/flow/plugins/trigger-plugin.js +96 -0
- package/dist/flow/plugins/trigger-plugin.js.map +1 -0
- package/dist/flow/plugins/wfa-datapill-plugin.d.ts +15 -0
- package/dist/flow/plugins/wfa-datapill-plugin.js +178 -0
- package/dist/flow/plugins/wfa-datapill-plugin.js.map +1 -0
- package/dist/flow/utils/approval-rules-processor.d.ts +13 -0
- package/dist/flow/utils/approval-rules-processor.js +267 -0
- package/dist/flow/utils/approval-rules-processor.js.map +1 -0
- package/dist/flow/utils/built-in-complex-objects.d.ts +19 -0
- package/dist/flow/utils/built-in-complex-objects.js +62 -0
- package/dist/flow/utils/built-in-complex-objects.js.map +1 -0
- package/dist/flow/utils/complex-object-resolver.d.ts +8 -0
- package/dist/flow/utils/complex-object-resolver.js +614 -0
- package/dist/flow/utils/complex-object-resolver.js.map +1 -0
- package/dist/flow/utils/complex-objects.d.ts +36 -0
- package/dist/flow/utils/complex-objects.js +481 -0
- package/dist/flow/utils/complex-objects.js.map +1 -0
- package/dist/flow/utils/data-pill-shapes.d.ts +58 -0
- package/dist/flow/utils/data-pill-shapes.js +135 -0
- package/dist/flow/utils/data-pill-shapes.js.map +1 -0
- package/dist/flow/utils/datapill-transformer.d.ts +110 -0
- package/dist/flow/utils/datapill-transformer.js +503 -0
- package/dist/flow/utils/datapill-transformer.js.map +1 -0
- package/dist/flow/utils/flow-constants.d.ts +72 -0
- package/dist/flow/utils/flow-constants.js +230 -0
- package/dist/flow/utils/flow-constants.js.map +1 -0
- package/dist/flow/utils/flow-io-to-record.d.ts +44 -0
- package/dist/flow/utils/flow-io-to-record.js +409 -0
- package/dist/flow/utils/flow-io-to-record.js.map +1 -0
- package/dist/flow/utils/flow-shapes.d.ts +161 -0
- package/dist/flow/utils/flow-shapes.js +255 -0
- package/dist/flow/utils/flow-shapes.js.map +1 -0
- package/dist/flow/utils/flow-to-xml.d.ts +16 -0
- package/dist/flow/utils/flow-to-xml.js +237 -0
- package/dist/flow/utils/flow-to-xml.js.map +1 -0
- package/dist/flow/utils/flow-variable-processor.d.ts +51 -0
- package/dist/flow/utils/flow-variable-processor.js +69 -0
- package/dist/flow/utils/flow-variable-processor.js.map +1 -0
- package/dist/flow/utils/label-cache-parser.d.ts +7 -0
- package/dist/flow/utils/label-cache-parser.js +24 -0
- package/dist/flow/utils/label-cache-parser.js.map +1 -0
- package/dist/flow/utils/label-cache-processor.d.ts +119 -0
- package/dist/flow/utils/label-cache-processor.js +719 -0
- package/dist/flow/utils/label-cache-processor.js.map +1 -0
- package/dist/flow/utils/pill-string-parser.d.ts +88 -0
- package/dist/flow/utils/pill-string-parser.js +306 -0
- package/dist/flow/utils/pill-string-parser.js.map +1 -0
- package/dist/flow/utils/schema-to-flow-object.d.ts +22 -0
- package/dist/flow/utils/schema-to-flow-object.js +318 -0
- package/dist/flow/utils/schema-to-flow-object.js.map +1 -0
- package/dist/flow/utils/service-catalog.d.ts +47 -0
- package/dist/flow/utils/service-catalog.js +137 -0
- package/dist/flow/utils/service-catalog.js.map +1 -0
- package/dist/flow/utils/utils.d.ts +117 -0
- package/dist/flow/utils/utils.js +345 -0
- package/dist/flow/utils/utils.js.map +1 -0
- package/dist/index.d.ts +20 -1
- package/dist/index.js +21 -1
- package/dist/index.js.map +1 -1
- package/dist/list-plugin.js +1 -1
- package/dist/list-plugin.js.map +1 -1
- package/dist/now-attach-plugin.d.ts +1 -0
- package/dist/now-attach-plugin.js +10 -10
- package/dist/now-attach-plugin.js.map +1 -1
- package/dist/now-ref-plugin.js +1 -1
- package/dist/now-ref-plugin.js.map +1 -1
- package/dist/record-plugin.d.ts +29 -0
- package/dist/record-plugin.js +66 -7
- package/dist/record-plugin.js.map +1 -1
- package/dist/repack/index.d.ts +2 -0
- package/dist/repack/index.js +8 -0
- package/dist/repack/index.js.map +1 -1
- package/dist/rest-api-plugin.js +54 -44
- package/dist/rest-api-plugin.js.map +1 -1
- package/dist/server-module-plugin/index.d.ts +10 -0
- package/dist/server-module-plugin/index.js +83 -59
- package/dist/server-module-plugin/index.js.map +1 -1
- package/dist/service-catalog/catalog-clientscript-plugin.d.ts +2 -0
- package/dist/service-catalog/catalog-clientscript-plugin.js +117 -0
- package/dist/service-catalog/catalog-clientscript-plugin.js.map +1 -0
- package/dist/service-catalog/catalog-item-plugin.d.ts +2 -0
- package/dist/service-catalog/catalog-item-plugin.js +115 -0
- package/dist/service-catalog/catalog-item-plugin.js.map +1 -0
- package/dist/service-catalog/catalog-ui-policy-plugin.d.ts +2 -0
- package/dist/service-catalog/catalog-ui-policy-plugin.js +266 -0
- package/dist/service-catalog/catalog-ui-policy-plugin.js.map +1 -0
- package/dist/service-catalog/index.d.ts +5 -0
- package/dist/service-catalog/index.js +22 -0
- package/dist/service-catalog/index.js.map +1 -0
- package/dist/service-catalog/record-to-shape.d.ts +6 -0
- package/dist/service-catalog/record-to-shape.js +93 -0
- package/dist/service-catalog/record-to-shape.js.map +1 -0
- package/dist/service-catalog/sc-record-producer-plugin.d.ts +2 -0
- package/dist/service-catalog/sc-record-producer-plugin.js +140 -0
- package/dist/service-catalog/sc-record-producer-plugin.js.map +1 -0
- package/dist/service-catalog/service-catalog-base.d.ts +311 -0
- package/dist/service-catalog/service-catalog-base.js +542 -0
- package/dist/service-catalog/service-catalog-base.js.map +1 -0
- package/dist/service-catalog/service-catalog-diagnostics.d.ts +45 -0
- package/dist/service-catalog/service-catalog-diagnostics.js +172 -0
- package/dist/service-catalog/service-catalog-diagnostics.js.map +1 -0
- package/dist/service-catalog/shape-to-record.d.ts +8 -0
- package/dist/service-catalog/shape-to-record.js +235 -0
- package/dist/service-catalog/shape-to-record.js.map +1 -0
- package/dist/service-catalog/utils.d.ts +323 -0
- package/dist/service-catalog/utils.js +1216 -0
- package/dist/service-catalog/utils.js.map +1 -0
- package/dist/service-catalog/variable-helper.d.ts +43 -0
- package/dist/service-catalog/variable-helper.js +92 -0
- package/dist/service-catalog/variable-helper.js.map +1 -0
- package/dist/service-catalog/variable-set-plugin.d.ts +2 -0
- package/dist/service-catalog/variable-set-plugin.js +175 -0
- package/dist/service-catalog/variable-set-plugin.js.map +1 -0
- package/dist/service-catalog/variables-transform.d.ts +139 -0
- package/dist/service-catalog/variables-transform.js +403 -0
- package/dist/service-catalog/variables-transform.js.map +1 -0
- package/dist/sla/sla-validators.d.ts +61 -0
- package/dist/sla/sla-validators.js +224 -0
- package/dist/sla/sla-validators.js.map +1 -0
- package/dist/sla-plugin.d.ts +5 -0
- package/dist/sla-plugin.js +280 -0
- package/dist/sla-plugin.js.map +1 -0
- package/dist/static-content-plugin.js +25 -2
- package/dist/static-content-plugin.js.map +1 -1
- package/dist/table-plugin.js +32 -15
- package/dist/table-plugin.js.map +1 -1
- package/dist/ui-page-plugin.js +832 -19
- package/dist/ui-page-plugin.js.map +1 -1
- package/dist/ui-policy-plugin.js +5 -7
- package/dist/ui-policy-plugin.js.map +1 -1
- package/dist/utils.d.ts +10 -1
- package/dist/utils.js +16 -0
- package/dist/utils.js.map +1 -1
- package/dist/ux-list-menu-config-plugin.d.ts +2 -0
- package/dist/ux-list-menu-config-plugin.js +292 -0
- package/dist/ux-list-menu-config-plugin.js.map +1 -0
- package/dist/workspace-plugin/chrome-tab.d.ts +2 -0
- package/dist/workspace-plugin/chrome-tab.js +46 -0
- package/dist/workspace-plugin/chrome-tab.js.map +1 -0
- package/dist/workspace-plugin/constants.d.ts +52 -0
- package/dist/workspace-plugin/constants.js +56 -0
- package/dist/workspace-plugin/constants.js.map +1 -0
- package/dist/workspace-plugin/fluent-utils.d.ts +9 -0
- package/dist/workspace-plugin/fluent-utils.js +60 -0
- package/dist/workspace-plugin/fluent-utils.js.map +1 -0
- package/dist/workspace-plugin/page.d.ts +8 -0
- package/dist/workspace-plugin/page.js +108 -0
- package/dist/workspace-plugin/page.js.map +1 -0
- package/dist/workspace-plugin/screen.d.ts +1 -0
- package/dist/workspace-plugin/screen.js +38 -0
- package/dist/workspace-plugin/screen.js.map +1 -0
- package/dist/workspace-plugin/templates/index.d.ts +10 -0
- package/dist/workspace-plugin/templates/index.js +20 -0
- package/dist/workspace-plugin/templates/index.js.map +1 -0
- package/dist/workspace-plugin/templates/record-page-composition.d.ts +1 -0
- package/dist/workspace-plugin/templates/record-page-composition.js +4043 -0
- package/dist/workspace-plugin/templates/record-page-composition.js.map +1 -0
- package/dist/workspace-plugin/templates/record-page-data.d.ts +1 -0
- package/dist/workspace-plugin/templates/record-page-data.js +527 -0
- package/dist/workspace-plugin/templates/record-page-data.js.map +1 -0
- package/dist/workspace-plugin/templates/record-page-interalEventMappings.d.ts +1 -0
- package/dist/workspace-plugin/templates/record-page-interalEventMappings.js +39 -0
- package/dist/workspace-plugin/templates/record-page-interalEventMappings.js.map +1 -0
- package/dist/workspace-plugin/templates/record-page-layoutModel.d.ts +1 -0
- package/dist/workspace-plugin/templates/record-page-layoutModel.js +55 -0
- package/dist/workspace-plugin/templates/record-page-layoutModel.js.map +1 -0
- package/dist/workspace-plugin/templates/record-page-properties.d.ts +1 -0
- package/dist/workspace-plugin/templates/record-page-properties.js +135 -0
- package/dist/workspace-plugin/templates/record-page-properties.js.map +1 -0
- package/dist/workspace-plugin/templates/record-page.d.ts +3 -0
- package/dist/workspace-plugin/templates/record-page.js +8 -0
- package/dist/workspace-plugin/templates/record-page.js.map +1 -0
- package/dist/workspace-plugin.d.ts +2 -0
- package/dist/workspace-plugin.js +453 -0
- package/dist/workspace-plugin.js.map +1 -0
- package/package.json +10 -12
- package/src/acl-plugin.ts +16 -1
- package/src/applicability-plugin.ts +82 -0
- package/src/atf/test-plugin.ts +6 -3
- package/src/basic-syntax-plugin.ts +10 -1
- package/src/business-rule-plugin.ts +2 -1
- package/src/call-expression-plugin.ts +2 -130
- package/src/column/column-to-record.ts +54 -8
- package/src/column-plugin.ts +29 -13
- package/src/dashboard/dashboard-component-property-defaults.ts +277 -0
- package/src/dashboard/dashboard-component-resolver.ts +69 -0
- package/src/dashboard/dashboard-plugin.ts +450 -0
- package/src/data-plugin.ts +67 -139
- package/src/email-notification-plugin.ts +850 -0
- package/src/flow/constants/flow-plugin-constants.ts +79 -0
- package/src/flow/flow-logic/flow-logic-constants.ts +120 -0
- package/src/flow/flow-logic/flow-logic-diagnostics.ts +591 -0
- package/src/flow/flow-logic/flow-logic-plugin-helpers.ts +2550 -0
- package/src/flow/flow-logic/flow-logic-plugin.ts +337 -0
- package/src/flow/flow-logic/flow-logic-shapes.ts +215 -0
- package/src/flow/plugins/approval-rules-plugin.ts +48 -0
- package/src/flow/plugins/flow-action-definition-plugin.ts +295 -0
- package/src/flow/plugins/flow-data-pill-plugin.ts +258 -0
- package/src/flow/plugins/flow-definition-plugin.ts +2173 -0
- package/src/flow/plugins/flow-diagnostics-plugin.ts +280 -0
- package/src/flow/plugins/flow-instance-plugin.ts +1499 -0
- package/src/flow/plugins/flow-trigger-instance-plugin.ts +444 -0
- package/src/flow/plugins/inline-script-plugin.ts +83 -0
- package/src/flow/plugins/step-definition-plugin.ts +67 -0
- package/src/flow/plugins/step-instance-plugin.ts +431 -0
- package/src/flow/plugins/trigger-plugin.ts +95 -0
- package/src/flow/plugins/wfa-datapill-plugin.ts +213 -0
- package/src/flow/utils/approval-rules-processor.ts +298 -0
- package/src/flow/utils/built-in-complex-objects.ts +81 -0
- package/src/flow/utils/complex-object-resolver.ts +875 -0
- package/src/flow/utils/complex-objects.ts +656 -0
- package/src/flow/utils/data-pill-shapes.ts +165 -0
- package/src/flow/utils/datapill-transformer.ts +632 -0
- package/src/flow/utils/flow-constants.ts +285 -0
- package/src/flow/utils/flow-io-to-record.ts +533 -0
- package/src/flow/utils/flow-shapes.ts +296 -0
- package/src/flow/utils/flow-to-xml.ts +318 -0
- package/src/flow/utils/flow-variable-processor.ts +100 -0
- package/src/flow/utils/label-cache-parser.ts +37 -0
- package/src/flow/utils/label-cache-processor.ts +870 -0
- package/src/flow/utils/pill-string-parser.ts +375 -0
- package/src/flow/utils/schema-to-flow-object.ts +385 -0
- package/src/flow/utils/service-catalog.ts +174 -0
- package/src/flow/utils/utils.ts +395 -0
- package/src/index.ts +20 -1
- package/src/list-plugin.ts +1 -1
- package/src/now-attach-plugin.ts +14 -11
- package/src/now-ref-plugin.ts +1 -1
- package/src/record-plugin.ts +76 -11
- package/src/repack/index.ts +14 -0
- package/src/rest-api-plugin.ts +62 -50
- package/src/server-module-plugin/index.ts +112 -86
- package/src/service-catalog/catalog-clientscript-plugin.ts +140 -0
- package/src/service-catalog/catalog-item-plugin.ts +162 -0
- package/src/service-catalog/catalog-ui-policy-plugin.ts +324 -0
- package/src/service-catalog/index.ts +5 -0
- package/src/service-catalog/record-to-shape.ts +109 -0
- package/src/service-catalog/sc-record-producer-plugin.ts +201 -0
- package/src/service-catalog/service-catalog-base.ts +600 -0
- package/src/service-catalog/service-catalog-diagnostics.ts +254 -0
- package/src/service-catalog/shape-to-record.ts +279 -0
- package/src/service-catalog/utils.ts +1455 -0
- package/src/service-catalog/variable-helper.ts +135 -0
- package/src/service-catalog/variable-set-plugin.ts +197 -0
- package/src/service-catalog/variables-transform.ts +438 -0
- package/src/sla/sla-validators.ts +331 -0
- package/src/sla-plugin.ts +358 -0
- package/src/static-content-plugin.ts +25 -2
- package/src/table-plugin.ts +49 -16
- package/src/ui-page-plugin.ts +1063 -20
- package/src/ui-policy-plugin.ts +5 -9
- package/src/utils.ts +24 -1
- package/src/ux-list-menu-config-plugin.ts +312 -0
- package/src/workspace-plugin/chrome-tab.ts +44 -0
- package/src/workspace-plugin/constants.ts +53 -0
- package/src/workspace-plugin/fluent-utils.ts +60 -0
- package/src/workspace-plugin/page.ts +139 -0
- package/src/workspace-plugin/screen.ts +34 -0
- package/src/workspace-plugin/templates/index.ts +17 -0
- package/src/workspace-plugin/templates/record-page-composition.ts +4051 -0
- package/src/workspace-plugin/templates/record-page-data.ts +523 -0
- package/src/workspace-plugin/templates/record-page-interalEventMappings.ts +35 -0
- package/src/workspace-plugin/templates/record-page-layoutModel.ts +51 -0
- package/src/workspace-plugin/templates/record-page-properties.ts +131 -0
- package/src/workspace-plugin/templates/record-page.ts +6 -0
- package/src/workspace-plugin.ts +574 -0
|
@@ -0,0 +1,2092 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createEmptyFlowLogicValues = createEmptyFlowLogicValues;
|
|
4
|
+
exports.childrenContainDatapills = childrenContainDatapills;
|
|
5
|
+
exports.isConditionalLogicShape = isConditionalLogicShape;
|
|
6
|
+
exports.isForEachShape = isForEachShape;
|
|
7
|
+
exports.isGoBackToShape = isGoBackToShape;
|
|
8
|
+
exports.isTryCatchShape = isTryCatchShape;
|
|
9
|
+
exports.isDoInParallelShape = isDoInParallelShape;
|
|
10
|
+
exports.isSetFlowVariablesShape = isSetFlowVariablesShape;
|
|
11
|
+
exports.isAssignSubflowOutputsShape = isAssignSubflowOutputsShape;
|
|
12
|
+
exports.isDataPillShape = isDataPillShape;
|
|
13
|
+
exports.getFlowLogicInstanceRecord = getFlowLogicInstanceRecord;
|
|
14
|
+
exports.getFlowLogicArguments = getFlowLogicArguments;
|
|
15
|
+
const sdk_build_core_1 = require("@servicenow/sdk-build-core");
|
|
16
|
+
const data_pill_shapes_1 = require("../utils/data-pill-shapes");
|
|
17
|
+
const zlib_1 = require("zlib");
|
|
18
|
+
const flow_instance_plugin_1 = require("../plugins/flow-instance-plugin");
|
|
19
|
+
const inline_script_plugin_1 = require("../plugins/inline-script-plugin");
|
|
20
|
+
const flow_shapes_1 = require("../utils/flow-shapes");
|
|
21
|
+
const flow_logic_constants_1 = require("./flow-logic-constants");
|
|
22
|
+
const utils_1 = require("../utils/utils");
|
|
23
|
+
const arrow_function_plugin_1 = require("../../arrow-function-plugin");
|
|
24
|
+
const now_id_plugin_1 = require("../../now-id-plugin");
|
|
25
|
+
const flow_logic_diagnostics_1 = require("./flow-logic-diagnostics");
|
|
26
|
+
const flow_data_pill_plugin_1 = require("../plugins/flow-data-pill-plugin");
|
|
27
|
+
async function buildFlowLogicInstanceRecord(expr, factory, values) {
|
|
28
|
+
const partialRecord = await factory.createRecord({
|
|
29
|
+
source: expr,
|
|
30
|
+
table: 'sys_hub_flow_logic_instance_v2',
|
|
31
|
+
explicitId: expr.getSysId(),
|
|
32
|
+
properties: {
|
|
33
|
+
comment: expr.getAnnotation()?.getValue() ?? '',
|
|
34
|
+
logic_definition: flow_logic_constants_1.FlowLogicSysId.getLogicId(expr.getCallee()),
|
|
35
|
+
values: values,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
return partialRecord.merge({ ui_id: (0, utils_1.sysIdToUuid)(partialRecord.getId().getValue()) });
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Creates an empty FlowLogicValues object with all fields initialized to empty arrays
|
|
42
|
+
* @returns Empty FlowLogicValues structure
|
|
43
|
+
*/
|
|
44
|
+
function createEmptyFlowLogicValues() {
|
|
45
|
+
return {
|
|
46
|
+
inputs: [],
|
|
47
|
+
outputsToAssign: [],
|
|
48
|
+
variables: [],
|
|
49
|
+
decisionTableInputs: [],
|
|
50
|
+
dynamicInputs: [],
|
|
51
|
+
workflowInputs: [],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* FlowLogicValueProcessor handles bidirectional conversion between Fluent TypeScript
|
|
56
|
+
* flow logic calls and ServiceNow XML flow records.
|
|
57
|
+
*
|
|
58
|
+
* ## Architecture
|
|
59
|
+
*
|
|
60
|
+
* **Forward Flow (Fluent → XML):**
|
|
61
|
+
* - `prepare()` converts TypeScript flow logic calls to ServiceNow XML format
|
|
62
|
+
* - Extracts configuration properties and creates structured input data
|
|
63
|
+
* - Data gets gzipped and stored in sys_hub_flow_logic_instance_v2 records
|
|
64
|
+
*
|
|
65
|
+
* **Reverse Flow (XML → Fluent):**
|
|
66
|
+
* - `parse()` converts ServiceNow XML records back to TypeScript
|
|
67
|
+
* - Unzips and parses stored JSON data
|
|
68
|
+
* - Reconstructs original configuration objects for shape generation
|
|
69
|
+
*
|
|
70
|
+
* ## Data Structure
|
|
71
|
+
*
|
|
72
|
+
* All flow logic data is stored as FlowLogicValues containing an array of
|
|
73
|
+
* FlowLogicValueInput objects with name/value pairs that represent the
|
|
74
|
+
* original TypeScript configuration properties.
|
|
75
|
+
*/
|
|
76
|
+
var FlowLogicValueProcessor;
|
|
77
|
+
(function (FlowLogicValueProcessor) {
|
|
78
|
+
/**
|
|
79
|
+
* Generic factory function for creating flow logic entry objects.
|
|
80
|
+
* All entry types (AssignSubflowOutput, SetFlowVariablesInput, FlowLogicValueInput)
|
|
81
|
+
* share the same structure, differing only in displayValue and type name.
|
|
82
|
+
*
|
|
83
|
+
* @param name - Entry name
|
|
84
|
+
* @param value - Entry value (may contain datapills like "{{trigger.name}}")
|
|
85
|
+
* @param children - Optional children array for complex objects (FlowObject/FlowArray)
|
|
86
|
+
* @param displayValue - Display value shown in ServiceNow UI (defaults to value)
|
|
87
|
+
* @returns Entry object with the specified type
|
|
88
|
+
*/
|
|
89
|
+
function createEntry(name, value, children, displayValue = value) {
|
|
90
|
+
return {
|
|
91
|
+
name,
|
|
92
|
+
value,
|
|
93
|
+
displayValue,
|
|
94
|
+
children: children || [],
|
|
95
|
+
parameter: {},
|
|
96
|
+
scriptActive: false,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function createInput(name, value) {
|
|
100
|
+
return createEntry(name, value);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Creates an AssignSubflowOutput entry with full display value.
|
|
104
|
+
* Used when generating XML from Fluent code for assignSubflowOutputs.
|
|
105
|
+
*
|
|
106
|
+
* @param name - Output name
|
|
107
|
+
* @param value - Output value (may contain datapills like "{{trigger.name}}")
|
|
108
|
+
* @param children - Optional children array for complex objects (FlowObject/FlowArray)
|
|
109
|
+
* @returns AssignSubflowOutput with displayValue set to the value
|
|
110
|
+
*/
|
|
111
|
+
function createOutput(name, value, children) {
|
|
112
|
+
return createEntry(name, value, children);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Creates a SetFlowVariablesInput entry for the 'variables' array with full display value.
|
|
116
|
+
* Used when generating XML from Fluent code.
|
|
117
|
+
*
|
|
118
|
+
* @param name - Variable name
|
|
119
|
+
* @param value - Variable value (may contain datapills like "{{trigger.name}}")
|
|
120
|
+
* @param children - Optional children array for complex objects (FlowObject/FlowArray)
|
|
121
|
+
* @returns SetFlowVariablesInput with displayValue set to the value
|
|
122
|
+
*/
|
|
123
|
+
function createVariable(name, value, children) {
|
|
124
|
+
return createEntry(name, value, children);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Creates a SetFlowVariablesInput entry for the 'inputs' array with empty display value.
|
|
128
|
+
* Used when generating XML from Fluent code for the inputs array (required for backward compatibility).
|
|
129
|
+
*
|
|
130
|
+
* @param name - Variable name
|
|
131
|
+
* @param value - Variable value
|
|
132
|
+
* @param children - Optional children array for complex objects (FlowObject/FlowArray)
|
|
133
|
+
* @returns SetFlowVariablesInput with empty displayValue
|
|
134
|
+
*/
|
|
135
|
+
function createVariableInput(name, value, children) {
|
|
136
|
+
return createEntry(name, value, children, '');
|
|
137
|
+
}
|
|
138
|
+
const INPUT_VALUE_PREPARERS = {
|
|
139
|
+
[flow_logic_constants_1.FLOW_LOGIC.ELSE]: () => [],
|
|
140
|
+
[flow_logic_constants_1.FLOW_LOGIC.GOBACKTO]: (data) => prepareGoBackTo(data.isString() ? data.asString() : data.asRecord()),
|
|
141
|
+
[flow_logic_constants_1.FLOW_LOGIC.IF]: (data) => prepareIfElse(data.asObject()),
|
|
142
|
+
[flow_logic_constants_1.FLOW_LOGIC.ELSEIF]: (data) => prepareIfElse(data.asObject()),
|
|
143
|
+
[flow_logic_constants_1.FLOW_LOGIC.WAIT_FOR_A_DURATION]: (data) => prepareWaitForADuration(data.asObject()),
|
|
144
|
+
[flow_logic_constants_1.FLOW_LOGIC.EXITLOOP]: () => [],
|
|
145
|
+
[flow_logic_constants_1.FLOW_LOGIC.ENDFLOW]: () => [],
|
|
146
|
+
[flow_logic_constants_1.FLOW_LOGIC.SKIP_ITERATION]: () => [],
|
|
147
|
+
[flow_logic_constants_1.FLOW_LOGIC.FOR_EACH]: (data) => prepareForEach(data.asString()),
|
|
148
|
+
[flow_logic_constants_1.FLOW_LOGIC.DO_IN_PARALLEL]: () => [],
|
|
149
|
+
[flow_logic_constants_1.FLOW_LOGIC.TRY_CATCH]: () => [],
|
|
150
|
+
[flow_logic_constants_1.FLOW_LOGIC.SET_FLOW_VARIABLES]: (data) => {
|
|
151
|
+
return prepareSetFlowVariables(data.asObject(), '');
|
|
152
|
+
},
|
|
153
|
+
[flow_logic_constants_1.FLOW_LOGIC.ASSIGN_SUBFLOW_OUTPUTS]: (data) => {
|
|
154
|
+
return prepareAssignSubflowOutputs(data.asObject());
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
/**
|
|
158
|
+
* Converts Fluent TypeScript flow logic calls into ServiceNow XML format.
|
|
159
|
+
*
|
|
160
|
+
* **Process:**
|
|
161
|
+
* 1. Takes the flow logic type (e.g., 'IF') and configuration data
|
|
162
|
+
* 2. Uses the appropriate VALUE_PREPARER function
|
|
163
|
+
* 3. Extracts relevant properties (condition, label, etc.)
|
|
164
|
+
* 4. Creates FlowLogicValueInput objects with name/value pairs
|
|
165
|
+
* 5. Returns FlowLogicValues structure that gets gzipped into XML
|
|
166
|
+
*
|
|
167
|
+
* **Example:**
|
|
168
|
+
* ```typescript
|
|
169
|
+
* // Input: If({ condition: "user.role === 'admin'", label: "Admin Check" })
|
|
170
|
+
* // Output: { inputs: [
|
|
171
|
+
* // { name: 'condition_name', value: 'Admin Check' },
|
|
172
|
+
* // { name: 'condition', value: "user.role === 'admin'" }
|
|
173
|
+
* // ]}
|
|
174
|
+
* ```
|
|
175
|
+
*
|
|
176
|
+
* @param logicType - The flow logic type key (e.g., 'IF', 'ELSEIF', 'GOBACKTO')
|
|
177
|
+
* @param data - Configuration data from the Fluent TypeScript call
|
|
178
|
+
* @param args - Optional arguments including childNameMetadata for FlowArray fields
|
|
179
|
+
* @returns FlowLogicValues structure ready for XML serialization
|
|
180
|
+
*/
|
|
181
|
+
function prepare(logicType, data, args) {
|
|
182
|
+
const preparer = INPUT_VALUE_PREPARERS[logicType];
|
|
183
|
+
if (!preparer) {
|
|
184
|
+
throw new Error(`Unsupported flow logic type: ${logicType}`);
|
|
185
|
+
}
|
|
186
|
+
const preparedData = preparer(data, args);
|
|
187
|
+
return {
|
|
188
|
+
outputsToAssign: logicType === flow_logic_constants_1.FLOW_LOGIC.ASSIGN_SUBFLOW_OUTPUTS ? preparedData : [],
|
|
189
|
+
variables: logicType === flow_logic_constants_1.FLOW_LOGIC.SET_FLOW_VARIABLES
|
|
190
|
+
? prepareSetFlowVariables(data.asObject(), 'variables')
|
|
191
|
+
: [],
|
|
192
|
+
decisionTableInputs: [],
|
|
193
|
+
dynamicInputs: [],
|
|
194
|
+
workflowInputs: [],
|
|
195
|
+
inputs: logicType === flow_logic_constants_1.FLOW_LOGIC.ASSIGN_SUBFLOW_OUTPUTS ? [] : preparedData,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
FlowLogicValueProcessor.prepare = prepare;
|
|
199
|
+
function prepareIfElse(config) {
|
|
200
|
+
const inputs = [];
|
|
201
|
+
// After datapill resolution, values are already resolved strings, so use getValue() directly
|
|
202
|
+
const conditionValue = config.get('condition')?.getValue();
|
|
203
|
+
const conditionString = typeof conditionValue === 'string' ? conditionValue : String(conditionValue ?? '');
|
|
204
|
+
const labelValue = config.get('label')?.getValue();
|
|
205
|
+
const label = labelValue && typeof labelValue === 'string' ? labelValue : '';
|
|
206
|
+
if (!conditionString) {
|
|
207
|
+
return [];
|
|
208
|
+
}
|
|
209
|
+
if (label) {
|
|
210
|
+
inputs.push(createInput('condition_name', label));
|
|
211
|
+
}
|
|
212
|
+
inputs.push(createInput('condition', conditionString));
|
|
213
|
+
return inputs;
|
|
214
|
+
}
|
|
215
|
+
function prepareGoBackTo(step) {
|
|
216
|
+
const stepId = step.isRecord() ? step.get('ui_id').asString().getValue() : step.asString().getValue();
|
|
217
|
+
return [createInput('go_back_to_step', stepId)];
|
|
218
|
+
}
|
|
219
|
+
function prepareForEach(config) {
|
|
220
|
+
// ForEach needs to extract the items array from the first argument
|
|
221
|
+
// The structure is: ForEach(items, config, body)
|
|
222
|
+
// For now, we create a basic items input descriptor based on the XML structure we found
|
|
223
|
+
const inputs = [];
|
|
224
|
+
// Create the items input descriptor based on the XML structure we found
|
|
225
|
+
const itemsInput = {
|
|
226
|
+
name: 'items',
|
|
227
|
+
value: config.getValue(),
|
|
228
|
+
displayValue: config.getValue(),
|
|
229
|
+
};
|
|
230
|
+
inputs.push(itemsInput);
|
|
231
|
+
return inputs;
|
|
232
|
+
}
|
|
233
|
+
function prepareWaitForADuration(config) {
|
|
234
|
+
const inputDescriptors = JSON.parse(JSON.stringify(flow_logic_constants_1.WAIT_FOR_A_DURATION_INPUT_DESCRIPTORS));
|
|
235
|
+
// Helper to safely get config value as string (after datapill resolution, values are already resolved)
|
|
236
|
+
const getConfigValue = (key) => {
|
|
237
|
+
const value = config.get(key)?.getValue();
|
|
238
|
+
return value !== undefined && value !== null ? String(value) : '';
|
|
239
|
+
};
|
|
240
|
+
// Map the instanceProps to the correct fields in the template
|
|
241
|
+
// durationType: explicit_duration | relative_duration | percentage_duration
|
|
242
|
+
const durationType = getConfigValue('durationType');
|
|
243
|
+
// Duration field is optional (not required for percentage_duration)
|
|
244
|
+
const durationField = config.get('duration');
|
|
245
|
+
const duration = durationField && !durationField.isUndefined()
|
|
246
|
+
? new sdk_build_core_1.DurationShape({
|
|
247
|
+
source: config.getOriginalNode(),
|
|
248
|
+
value: durationField.getValue(),
|
|
249
|
+
})
|
|
250
|
+
: durationField;
|
|
251
|
+
const schedule = getConfigValue('schedule');
|
|
252
|
+
const durationTypeDisplayMap = {
|
|
253
|
+
explicit_duration: 'Explicit duration',
|
|
254
|
+
relative_duration: 'Relative duration',
|
|
255
|
+
percentage_duration: 'Percentage duration',
|
|
256
|
+
};
|
|
257
|
+
const updateDescriptor = (index, value, displayValue, parameterType) => {
|
|
258
|
+
if (inputDescriptors[index]) {
|
|
259
|
+
inputDescriptors[index].value = value;
|
|
260
|
+
inputDescriptors[index].displayValue = displayValue ?? value;
|
|
261
|
+
// Add parameter.type to preserve type information for round-trip transformation
|
|
262
|
+
if (parameterType) {
|
|
263
|
+
inputDescriptors[index].parameter = { type: parameterType };
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
updateDescriptor(0, durationType, durationTypeDisplayMap[durationType]);
|
|
268
|
+
const durationValue = duration?.toString()?.getValue() || '';
|
|
269
|
+
// Include 'glide_duration' type so XML → Fluent transform knows to create DurationShape
|
|
270
|
+
updateDescriptor(3, durationValue, durationValue, 'glide_duration');
|
|
271
|
+
updateDescriptor(6, schedule);
|
|
272
|
+
if (durationType === 'relative_duration') {
|
|
273
|
+
updateDescriptor(4, getConfigValue('relativeOperator'));
|
|
274
|
+
updateDescriptor(5, getConfigValue('relativeDatetime'));
|
|
275
|
+
}
|
|
276
|
+
if (durationType === 'percentage_duration') {
|
|
277
|
+
updateDescriptor(2, getConfigValue('percentage'));
|
|
278
|
+
updateDescriptor(1, getConfigValue('percentageDatetime'));
|
|
279
|
+
}
|
|
280
|
+
return inputDescriptors;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Checks if a value is a serialized inline script object.
|
|
284
|
+
* Inline script objects have the shape: { scriptActive: true, script: {...}, name, value, ... }
|
|
285
|
+
*
|
|
286
|
+
* @param value - The value to check
|
|
287
|
+
* @returns true if value is an inline script object, false otherwise
|
|
288
|
+
*/
|
|
289
|
+
function isSerializedInlineScript(value) {
|
|
290
|
+
return (typeof value === 'object' &&
|
|
291
|
+
value !== null &&
|
|
292
|
+
!Array.isArray(value) &&
|
|
293
|
+
value.scriptActive === true);
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Builds a flattened children array from a nested object or array.
|
|
297
|
+
* Used for the 'children' field in SetFlowVariablesInput for complex objects (FlowObject/FlowArray).
|
|
298
|
+
*
|
|
299
|
+
* @param value - The object or array value
|
|
300
|
+
* @param childName - Optional childName for FlowArray elements (e.g., 'tag', 'color')
|
|
301
|
+
* @returns Array of child entries with name, value, displayValue, and scriptActive fields
|
|
302
|
+
*/
|
|
303
|
+
function buildChildrenArray(value, childName) {
|
|
304
|
+
const children = [];
|
|
305
|
+
if (Array.isArray(value)) {
|
|
306
|
+
// FlowArray: Create a child entry for each array element
|
|
307
|
+
// Use childName if provided (matches UI behavior), otherwise empty string (backward compatibility)
|
|
308
|
+
const elementName = childName || '';
|
|
309
|
+
for (const element of value) {
|
|
310
|
+
if (typeof element === 'object' && element !== null) {
|
|
311
|
+
// Nested object in array
|
|
312
|
+
children.push({
|
|
313
|
+
name: elementName,
|
|
314
|
+
value: '',
|
|
315
|
+
displayValue: '',
|
|
316
|
+
children: buildChildrenArray(element),
|
|
317
|
+
scriptActive: false,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
// Primitive in array
|
|
322
|
+
const elementStr = element === null || element === undefined || element === '' ? '' : String(element);
|
|
323
|
+
children.push({
|
|
324
|
+
name: elementName,
|
|
325
|
+
value: elementStr,
|
|
326
|
+
displayValue: elementStr,
|
|
327
|
+
scriptActive: false,
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
else if (typeof value === 'object' && value !== null) {
|
|
333
|
+
// FlowObject: Create a child entry for each field
|
|
334
|
+
for (const [fieldName, fieldValue] of Object.entries(value)) {
|
|
335
|
+
if (typeof fieldValue === 'object' && fieldValue !== null && !Array.isArray(fieldValue)) {
|
|
336
|
+
// Nested object
|
|
337
|
+
children.push({
|
|
338
|
+
name: fieldName,
|
|
339
|
+
value: '',
|
|
340
|
+
displayValue: '',
|
|
341
|
+
children: buildChildrenArray(fieldValue),
|
|
342
|
+
scriptActive: false,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
else if (Array.isArray(fieldValue)) {
|
|
346
|
+
// Nested array - Note: nested arrays don't have childName metadata yet
|
|
347
|
+
children.push({
|
|
348
|
+
name: fieldName,
|
|
349
|
+
value: '',
|
|
350
|
+
displayValue: '',
|
|
351
|
+
children: buildChildrenArray(fieldValue),
|
|
352
|
+
scriptActive: false,
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
// Primitive field
|
|
357
|
+
const fieldStr = fieldValue === null || fieldValue === undefined || fieldValue === '' ? '' : String(fieldValue);
|
|
358
|
+
children.push({
|
|
359
|
+
name: fieldName,
|
|
360
|
+
value: fieldStr,
|
|
361
|
+
displayValue: fieldStr,
|
|
362
|
+
scriptActive: false,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return children;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Generic helper to prepare entries from ObjectShape for XML serialization.
|
|
371
|
+
* Extracts key-value pairs and applies a factory function to create typed entries.
|
|
372
|
+
*
|
|
373
|
+
* Handles inline script objects (with scriptActive: true) by returning them as-is.
|
|
374
|
+
* Also handles complex objects (FlowObject/FlowArray) by building children arrays.
|
|
375
|
+
*
|
|
376
|
+
* @param values - ObjectShape containing name-value pairs from Fluent code
|
|
377
|
+
* @param factory - Factory function to create entry objects from key-value pairs
|
|
378
|
+
* @param childNameMetadata - Optional map of field name → childName for FlowArray fields
|
|
379
|
+
* @param useDottedNames - If true, use "fieldName.childName" format for array elements (assignSubflowOutputs)
|
|
380
|
+
* @returns Array of entries ready for XML serialization
|
|
381
|
+
*/
|
|
382
|
+
function prepareEntries(values, factory) {
|
|
383
|
+
if (!values || values.isUndefined()) {
|
|
384
|
+
return [];
|
|
385
|
+
}
|
|
386
|
+
const results = [];
|
|
387
|
+
const entries = values.entries({ resolve: false });
|
|
388
|
+
for (const [key, shape] of entries) {
|
|
389
|
+
// Check if the shape is an ObjectShape (for FlowObject assignments)
|
|
390
|
+
if (shape.is(sdk_build_core_1.ObjectShape)) {
|
|
391
|
+
const serializedObj = serializeShapeToValue(shape);
|
|
392
|
+
// Check if the serialized value is an inline script (has scriptActive: true)
|
|
393
|
+
if (isSerializedInlineScript(serializedObj)) {
|
|
394
|
+
// Inline scripts are already in the correct format - return as-is
|
|
395
|
+
results.push(serializedObj);
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
// Regular FlowObject - children with correct names will be added by resolveComplexInput in postProcess
|
|
399
|
+
const children = buildChildrenArray(serializedObj);
|
|
400
|
+
results.push(factory(key, JSON.stringify(serializedObj), children));
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
// Check if the shape is an ArrayShape (for FlowArray assignments)
|
|
404
|
+
if (shape.is(sdk_build_core_1.ArrayShape)) {
|
|
405
|
+
const arrShape = shape.as(sdk_build_core_1.ArrayShape);
|
|
406
|
+
const serializedArr = [];
|
|
407
|
+
for (const elem of arrShape.getElements()) {
|
|
408
|
+
serializedArr.push(serializeShapeToValue(elem));
|
|
409
|
+
}
|
|
410
|
+
// Children with correct names will be added by resolveComplexInput in postProcess
|
|
411
|
+
const children = buildChildrenArray(serializedArr);
|
|
412
|
+
results.push(factory(key, JSON.stringify(serializedArr), children));
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
// For all other shapes, get their value
|
|
416
|
+
const value = shape.getValue();
|
|
417
|
+
// Check if value is a serialized inline script object
|
|
418
|
+
if (isSerializedInlineScript(value)) {
|
|
419
|
+
// Inline scripts are already in the correct format - return as-is
|
|
420
|
+
results.push(value);
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
// Check if value is a plain object or array (shouldn't happen but just in case)
|
|
424
|
+
if (typeof value === 'object' && value !== null) {
|
|
425
|
+
const children = buildChildrenArray(value);
|
|
426
|
+
results.push(factory(key, JSON.stringify(value), children));
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
// Primitive values (string, number, boolean, datapills)
|
|
430
|
+
results.push(factory(key, String(value)));
|
|
431
|
+
}
|
|
432
|
+
return results;
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Recursively serializes a Shape to a plain JavaScript value, preserving datapills as pill strings.
|
|
436
|
+
* @param shape - The shape to serialize
|
|
437
|
+
* @returns Plain JavaScript value with datapills as strings like "{{trigger.name}}"
|
|
438
|
+
*/
|
|
439
|
+
function serializeShapeToValue(shape) {
|
|
440
|
+
if (shape.is(sdk_build_core_1.ObjectShape)) {
|
|
441
|
+
const obj = {};
|
|
442
|
+
for (const [key, valueShape] of shape.as(sdk_build_core_1.ObjectShape).entries({ resolve: false })) {
|
|
443
|
+
obj[key] = serializeShapeToValue(valueShape);
|
|
444
|
+
}
|
|
445
|
+
return obj;
|
|
446
|
+
}
|
|
447
|
+
if (shape.is(sdk_build_core_1.ArrayShape)) {
|
|
448
|
+
const arr = [];
|
|
449
|
+
for (const elem of shape.as(sdk_build_core_1.ArrayShape).getElements()) {
|
|
450
|
+
arr.push(serializeShapeToValue(elem));
|
|
451
|
+
}
|
|
452
|
+
return arr;
|
|
453
|
+
}
|
|
454
|
+
// Handle TemplateExpressionShape (template strings with datapills)
|
|
455
|
+
if (shape.is(sdk_build_core_1.TemplateExpressionShape)) {
|
|
456
|
+
// TemplateExpressionShape should have been converted to PillTemplateShape by DataPillPlugin
|
|
457
|
+
// If it's a PillTemplateShape, get its value
|
|
458
|
+
if (shape.is(data_pill_shapes_1.PillTemplateShape)) {
|
|
459
|
+
return shape.as(data_pill_shapes_1.PillTemplateShape).getValue();
|
|
460
|
+
}
|
|
461
|
+
// Otherwise, get the regular value
|
|
462
|
+
return shape.getValue();
|
|
463
|
+
}
|
|
464
|
+
// Handle PillShape (wfa.dataPill() is converted to PillShape by WfaDataPillPlugin)
|
|
465
|
+
if (shape.is(data_pill_shapes_1.PillShape)) {
|
|
466
|
+
return shape.as(data_pill_shapes_1.PillShape).getValue();
|
|
467
|
+
}
|
|
468
|
+
// For all other shapes (primitives, etc.), get the value
|
|
469
|
+
return shape.getValue();
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Prepares SetFlowVariables data for XML serialization.
|
|
473
|
+
* Converts an ObjectShape containing variable assignments into an array of SetFlowVariablesInput entries.
|
|
474
|
+
*
|
|
475
|
+
* The `type` parameter determines which array format to use:
|
|
476
|
+
* - 'variables': Creates entries with full displayValue (for the 'variables' array in XML)
|
|
477
|
+
* - default: Creates entries with empty displayValue (for the 'inputs' array in XML)
|
|
478
|
+
*
|
|
479
|
+
* This dual behavior is required because ServiceNow stores SetFlowVariables data in both
|
|
480
|
+
* the 'variables' array and the 'inputs' array for backward compatibility.
|
|
481
|
+
*
|
|
482
|
+
* @param values - ObjectShape containing variable name-value pairs from Fluent code
|
|
483
|
+
* @param type - Optional type flag ('variables' for variables array, default for inputs array)
|
|
484
|
+
* @returns Array of SetFlowVariablesInput entries ready for XML serialization
|
|
485
|
+
*
|
|
486
|
+
* @example
|
|
487
|
+
* // Fluent: { user: params.trigger.name, count: 42 }
|
|
488
|
+
* // Variables array: [{ name: 'user', value: '{{trigger.name}}', displayValue: '{{trigger.name}}' }, ...]
|
|
489
|
+
* // Inputs array: [{ name: 'user', value: '{{trigger.name}}', displayValue: '' }, ...]
|
|
490
|
+
*/
|
|
491
|
+
function prepareSetFlowVariables(values, type = '') {
|
|
492
|
+
return prepareEntries(values, (key, value, children) => {
|
|
493
|
+
// Choose appropriate creator based on type flag
|
|
494
|
+
if (type === 'variables') {
|
|
495
|
+
return createVariable(key, value, children);
|
|
496
|
+
}
|
|
497
|
+
else {
|
|
498
|
+
return createVariableInput(key, value, children);
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Prepares AssignSubflowOutputs data for XML serialization.
|
|
504
|
+
* Converts an ObjectShape containing output assignments into an array of AssignSubflowOutput entries.
|
|
505
|
+
*
|
|
506
|
+
* @param values - ObjectShape containing output name-value pairs from Fluent code
|
|
507
|
+
* @returns Array of AssignSubflowOutput entries ready for XML serialization
|
|
508
|
+
*
|
|
509
|
+
* @example
|
|
510
|
+
* // Fluent: { result: params.action.output, status: 'complete' }
|
|
511
|
+
* // XML: [{ name: 'result', value: '{{action.output}}', displayValue: '{{action.output}}', scriptActive: false }, ...]
|
|
512
|
+
*
|
|
513
|
+
* Note: childName for FlowArray fields is now set automatically by resolveComplexInput in postProcess functions
|
|
514
|
+
*/
|
|
515
|
+
function prepareAssignSubflowOutputs(values) {
|
|
516
|
+
return prepareEntries(values, createOutput);
|
|
517
|
+
}
|
|
518
|
+
const VALUE_PARSERS = {
|
|
519
|
+
[flow_logic_constants_1.FLOW_LOGIC.GOBACKTO]: parseGoBackTo,
|
|
520
|
+
[flow_logic_constants_1.FLOW_LOGIC.IF]: parseIfElse,
|
|
521
|
+
[flow_logic_constants_1.FLOW_LOGIC.ELSEIF]: parseIfElse,
|
|
522
|
+
[flow_logic_constants_1.FLOW_LOGIC.ELSE]: parseIfElse,
|
|
523
|
+
[flow_logic_constants_1.FLOW_LOGIC.WAIT_FOR_A_DURATION]: () => ({}),
|
|
524
|
+
[flow_logic_constants_1.FLOW_LOGIC.EXITLOOP]: () => ({}),
|
|
525
|
+
[flow_logic_constants_1.FLOW_LOGIC.ENDFLOW]: () => ({}),
|
|
526
|
+
[flow_logic_constants_1.FLOW_LOGIC.SKIP_ITERATION]: () => ({}),
|
|
527
|
+
[flow_logic_constants_1.FLOW_LOGIC.FOR_EACH]: parseForEach,
|
|
528
|
+
[flow_logic_constants_1.FLOW_LOGIC.DO_IN_PARALLEL]: () => ({}),
|
|
529
|
+
[flow_logic_constants_1.FLOW_LOGIC.TRY_CATCH]: () => ({}),
|
|
530
|
+
[flow_logic_constants_1.FLOW_LOGIC.SET_FLOW_VARIABLES]: () => ({}),
|
|
531
|
+
[flow_logic_constants_1.FLOW_LOGIC.ASSIGN_SUBFLOW_OUTPUTS]: () => ({}),
|
|
532
|
+
};
|
|
533
|
+
/**
|
|
534
|
+
* Converts ServiceNow XML flow logic records back to Fluent TypeScript.
|
|
535
|
+
*
|
|
536
|
+
* **Process:**
|
|
537
|
+
* 1. Takes gzipped JSON string from XML record
|
|
538
|
+
* 2. Unzips and parses the FlowLogicValues structure
|
|
539
|
+
* 3. Uses the appropriate VALUE_PARSER function
|
|
540
|
+
* 4. Reconstructs the original configuration object
|
|
541
|
+
* 5. Returns structured data for shape generation
|
|
542
|
+
*
|
|
543
|
+
* **Example:**
|
|
544
|
+
* ```typescript
|
|
545
|
+
* // Input: gzipped JSON with inputs array
|
|
546
|
+
* // Output: { condition: "user.role === 'admin'", label: "Admin Check" /**
|
|
547
|
+
* Parses flow logic values from gzipped JSON string
|
|
548
|
+
* @param logicType - The flow logic type key (e.g., 'IF', 'ELSEIF', 'GOBACKTO')
|
|
549
|
+
* @param gZippedString - Base64 encoded gzipped JSON from XML record
|
|
550
|
+
* @param typeMap - Optional map of variable/output names to their internal types for proper type conversion
|
|
551
|
+
* @param labelCacheMap - Optional map of datapill names to their types from the label cache
|
|
552
|
+
* @returns Parsed configuration object for TypeScript shape generation
|
|
553
|
+
*/
|
|
554
|
+
function parse(logicType, gZippedString, typeMap, labelCacheMap) {
|
|
555
|
+
const valuesJson = parseGzippedJson(gZippedString);
|
|
556
|
+
// Special handling for SET_FLOW_VARIABLES - data is in variables array
|
|
557
|
+
if (logicType === flow_logic_constants_1.FLOW_LOGIC.SET_FLOW_VARIABLES) {
|
|
558
|
+
if (valuesJson?.variables?.length) {
|
|
559
|
+
return parseEntriesFromArray(valuesJson.variables, typeMap, labelCacheMap);
|
|
560
|
+
}
|
|
561
|
+
return {};
|
|
562
|
+
}
|
|
563
|
+
// Special handling for ASSIGN_SUBFLOW_OUTPUTS - data is in outputsToAssign array
|
|
564
|
+
if (logicType === flow_logic_constants_1.FLOW_LOGIC.ASSIGN_SUBFLOW_OUTPUTS) {
|
|
565
|
+
if (valuesJson?.outputsToAssign?.length) {
|
|
566
|
+
return parseEntriesFromArray(valuesJson.outputsToAssign, typeMap, labelCacheMap);
|
|
567
|
+
}
|
|
568
|
+
return {};
|
|
569
|
+
}
|
|
570
|
+
if (!valuesJson?.inputs?.length) {
|
|
571
|
+
if (logicType === flow_logic_constants_1.FLOW_LOGIC.GOBACKTO) {
|
|
572
|
+
throw new Error('Malformed/unsupported input payload in GoBackTo Instance');
|
|
573
|
+
}
|
|
574
|
+
return {};
|
|
575
|
+
}
|
|
576
|
+
const parser = VALUE_PARSERS[logicType];
|
|
577
|
+
if (!parser) {
|
|
578
|
+
throw new Error(`Unsupported flow logic type for parsing: ${logicType}`);
|
|
579
|
+
}
|
|
580
|
+
return parser(valuesJson.inputs);
|
|
581
|
+
}
|
|
582
|
+
FlowLogicValueProcessor.parse = parse;
|
|
583
|
+
function parseGzippedJson(gZippedString) {
|
|
584
|
+
try {
|
|
585
|
+
const unzipped = (0, zlib_1.gunzipSync)(Buffer.from(gZippedString, 'base64')).toString();
|
|
586
|
+
return JSON.parse(unzipped);
|
|
587
|
+
}
|
|
588
|
+
catch {
|
|
589
|
+
return null;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
function parseIfElse(inputs) {
|
|
593
|
+
const result = {};
|
|
594
|
+
for (const input of inputs) {
|
|
595
|
+
if (input.name === 'condition' && input.value) {
|
|
596
|
+
result.condition = input.value;
|
|
597
|
+
}
|
|
598
|
+
else if (input.name === 'condition_name' && input.value) {
|
|
599
|
+
result.label = input.value;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
return result;
|
|
603
|
+
}
|
|
604
|
+
function parseGoBackTo(inputs) {
|
|
605
|
+
for (const input of inputs) {
|
|
606
|
+
if (input.name === 'go_back_to_step' && input.value) {
|
|
607
|
+
return input.value;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
throw new Error('Malformed/unsupported input payload in GoBackTo Instance');
|
|
611
|
+
}
|
|
612
|
+
function parseForEach(inputs) {
|
|
613
|
+
const result = {};
|
|
614
|
+
for (const input of inputs) {
|
|
615
|
+
if (input.name === 'items' && input.value) {
|
|
616
|
+
result.items = input.value;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
return result;
|
|
620
|
+
}
|
|
621
|
+
// Constants for type handling
|
|
622
|
+
const ARRAY_TYPE_PREFIX = 'array.';
|
|
623
|
+
const ARRAY_TYPE_PREFIX_LENGTH = ARRAY_TYPE_PREFIX.length;
|
|
624
|
+
const EXCLUDED_TYPE_ANNOTATIONS = new Set(['string', 'glide_date_time']);
|
|
625
|
+
/**
|
|
626
|
+
* Extracts the base name from a dotted path (e.g., "project.name" -> "name")
|
|
627
|
+
* For assignSubflowOutputs, names are prefixed with parent (e.g., "project.name", "project.tasks")
|
|
628
|
+
* We need to strip the prefix to get the actual field name
|
|
629
|
+
* @param name - Full path name
|
|
630
|
+
* @returns Base name after the last dot
|
|
631
|
+
*/
|
|
632
|
+
function getBaseName(name) {
|
|
633
|
+
if (!name) {
|
|
634
|
+
return '';
|
|
635
|
+
}
|
|
636
|
+
const lastDot = name.lastIndexOf('.');
|
|
637
|
+
return lastDot >= 0 ? name.substring(lastDot + 1) : name;
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Extracts element type from array type string
|
|
641
|
+
* @param type - Type string like "array.integer" or "string"
|
|
642
|
+
* @returns Element type or undefined
|
|
643
|
+
*/
|
|
644
|
+
function extractArrayElementType(type) {
|
|
645
|
+
if (!type || !type.startsWith(ARRAY_TYPE_PREFIX)) {
|
|
646
|
+
return undefined;
|
|
647
|
+
}
|
|
648
|
+
return type.substring(ARRAY_TYPE_PREFIX_LENGTH);
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Strip type annotation from datapill strings.
|
|
652
|
+
* ServiceNow stores datapills with type annotations like {{trigger.name|string}}.
|
|
653
|
+
* We need to remove the |type part before creating PropertyAccessShape.
|
|
654
|
+
*
|
|
655
|
+
* @param value - The value to clean (may be datapill string or regular value)
|
|
656
|
+
* @returns Cleaned value with type annotation removed
|
|
657
|
+
*/
|
|
658
|
+
function stripDatapillTypeAnnotation(value) {
|
|
659
|
+
// Check if this is a datapill string (starts with {{ and ends with }})
|
|
660
|
+
if (value.startsWith('{{') && value.endsWith('}}')) {
|
|
661
|
+
// Extract content between {{ and }}
|
|
662
|
+
const content = value.slice(2, -2);
|
|
663
|
+
// Split by | and take only the first part (the path without type annotation)
|
|
664
|
+
const pathOnly = content.split('|')[0];
|
|
665
|
+
// Return cleaned datapill
|
|
666
|
+
return `{{${pathOnly}}}`;
|
|
667
|
+
}
|
|
668
|
+
// Not a datapill, return as-is
|
|
669
|
+
return value;
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Adds type annotation to a datapill if needed based on label cache
|
|
673
|
+
* @param pillName - Datapill name without braces
|
|
674
|
+
* @param labelCacheMap - Map of datapill names to types
|
|
675
|
+
* @returns Datapill with annotation if applicable
|
|
676
|
+
*/
|
|
677
|
+
function addTypeAnnotationIfNeeded(pillName, labelCacheMap) {
|
|
678
|
+
if (!labelCacheMap) {
|
|
679
|
+
return `{{${pillName}}}`;
|
|
680
|
+
}
|
|
681
|
+
const cachedType = labelCacheMap.get(pillName.trim());
|
|
682
|
+
if (cachedType && !EXCLUDED_TYPE_ANNOTATIONS.has(cachedType)) {
|
|
683
|
+
return `{{${pillName}|${cachedType}}}`;
|
|
684
|
+
}
|
|
685
|
+
return `{{${pillName}}}`;
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* Processes a pure datapill value (starts and ends with {{}})
|
|
689
|
+
* @param value - Pure datapill string
|
|
690
|
+
* @param labelCacheMap - Map of datapill names to types
|
|
691
|
+
* @returns Processed datapill with type annotation if needed
|
|
692
|
+
*/
|
|
693
|
+
function processPureDatapill(value, labelCacheMap) {
|
|
694
|
+
const strippedValue = stripDatapillTypeAnnotation(value);
|
|
695
|
+
const pillName = strippedValue.slice(2, -2); // Remove {{ and }}
|
|
696
|
+
return addTypeAnnotationIfNeeded(pillName, labelCacheMap);
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Processes a template expression containing datapills mixed with text
|
|
700
|
+
* @param value - Template expression string
|
|
701
|
+
* @param labelCacheMap - Map of datapill names to types
|
|
702
|
+
* @returns Processed template with type annotations added to datapills
|
|
703
|
+
*/
|
|
704
|
+
function processTemplateExpression(value, labelCacheMap) {
|
|
705
|
+
if (!labelCacheMap) {
|
|
706
|
+
return value;
|
|
707
|
+
}
|
|
708
|
+
const datapillRegex = /\{\{([^}|]+)(?:\|[^}]+)?\}\}/g;
|
|
709
|
+
return value.replace(datapillRegex, (_match, pillName) => addTypeAnnotationIfNeeded(pillName, labelCacheMap));
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Converts a primitive value based on its type
|
|
713
|
+
* @param value - String value to convert
|
|
714
|
+
* @param uiType - Type information
|
|
715
|
+
* @returns Converted value
|
|
716
|
+
*/
|
|
717
|
+
function convertPrimitiveValue(value, uiType) {
|
|
718
|
+
// Preserve empty strings - don't convert them to 0 or false
|
|
719
|
+
if (value === '') {
|
|
720
|
+
return value;
|
|
721
|
+
}
|
|
722
|
+
if (uiType === 'integer') {
|
|
723
|
+
const num = Number(value);
|
|
724
|
+
return Number.isNaN(num) ? value : num;
|
|
725
|
+
}
|
|
726
|
+
if (uiType === 'boolean') {
|
|
727
|
+
if (value === 'true' || value === '1') {
|
|
728
|
+
return true;
|
|
729
|
+
}
|
|
730
|
+
if (value === 'false' || value === '0') {
|
|
731
|
+
return false;
|
|
732
|
+
}
|
|
733
|
+
return value;
|
|
734
|
+
}
|
|
735
|
+
return stripDatapillTypeAnnotation(value);
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* Recursively applies type conversion to an object and its nested objects
|
|
739
|
+
* @param obj - Object to apply type conversion to
|
|
740
|
+
* @param typeMap - Map of field names to their types
|
|
741
|
+
* @returns Object with type-converted values
|
|
742
|
+
*/
|
|
743
|
+
function applyTypeConversionToObject(obj, typeMap) {
|
|
744
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
745
|
+
return obj;
|
|
746
|
+
}
|
|
747
|
+
if (Array.isArray(obj)) {
|
|
748
|
+
return obj;
|
|
749
|
+
}
|
|
750
|
+
const result = {};
|
|
751
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
752
|
+
const fieldType = typeMap.get(key);
|
|
753
|
+
if (typeof value === 'string' && fieldType) {
|
|
754
|
+
result[key] = convertPrimitiveValue(value, fieldType);
|
|
755
|
+
}
|
|
756
|
+
else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
757
|
+
// Recursively apply to nested objects
|
|
758
|
+
result[key] = applyTypeConversionToObject(value, typeMap);
|
|
759
|
+
}
|
|
760
|
+
else {
|
|
761
|
+
result[key] = value;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
return result;
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* Converts a string value to the appropriate type based on uiType
|
|
768
|
+
* @param value - String value to convert
|
|
769
|
+
* @param uiType - Type information from parameter (e.g., 'integer', 'boolean', 'string')
|
|
770
|
+
* @param labelCacheMap - Optional map of datapill names to their types from the label cache
|
|
771
|
+
* @returns Properly typed value
|
|
772
|
+
*/
|
|
773
|
+
function convertChildValueToType(value, uiType, labelCacheMap) {
|
|
774
|
+
// Check if value contains datapills (either pure datapill or template expression)
|
|
775
|
+
if (value.includes('{{') && value.includes('}}')) {
|
|
776
|
+
if (value.startsWith('{{') && value.endsWith('}}')) {
|
|
777
|
+
// Pure datapill
|
|
778
|
+
return processPureDatapill(value, labelCacheMap);
|
|
779
|
+
}
|
|
780
|
+
// Template expression with datapills
|
|
781
|
+
return processTemplateExpression(value, labelCacheMap);
|
|
782
|
+
}
|
|
783
|
+
// Non-datapill value - convert based on type
|
|
784
|
+
return convertPrimitiveValue(value, uiType);
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* Extracts the element type from a parent entry for primitive arrays.
|
|
788
|
+
* Parses the complexObjectSchema from the parent's value to get child_type.
|
|
789
|
+
*
|
|
790
|
+
* @param parentEntry - Parent entry containing the schema information
|
|
791
|
+
* @returns Element type (e.g., 'integer', 'boolean', 'string') or undefined
|
|
792
|
+
*/
|
|
793
|
+
function getArrayElementType(parentEntry) {
|
|
794
|
+
if (!parentEntry.value || typeof parentEntry.value !== 'string') {
|
|
795
|
+
return undefined;
|
|
796
|
+
}
|
|
797
|
+
try {
|
|
798
|
+
const parsed = JSON.parse(parentEntry.value);
|
|
799
|
+
if (parsed.complexObjectSchema) {
|
|
800
|
+
// Find the $COCollectionField.$type_facets
|
|
801
|
+
for (const schemaKey in parsed.complexObjectSchema) {
|
|
802
|
+
const schema = parsed.complexObjectSchema[schemaKey];
|
|
803
|
+
if (schema && schema.$COCollectionField && schema['$COCollectionField.$type_facets']) {
|
|
804
|
+
const typeFacets = schema['$COCollectionField.$type_facets'];
|
|
805
|
+
if (typeFacets.SimpleMapFacet) {
|
|
806
|
+
const facetData = JSON.parse(typeFacets.SimpleMapFacet);
|
|
807
|
+
return facetData.child_type;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
catch {
|
|
814
|
+
// Ignore parsing errors
|
|
815
|
+
}
|
|
816
|
+
return undefined;
|
|
817
|
+
}
|
|
818
|
+
/**
|
|
819
|
+
* Extracts type schema from a parent entry's complexObjectSchema.
|
|
820
|
+
* The schema contains nested field type information that can be used for type conversion.
|
|
821
|
+
*
|
|
822
|
+
* @param parentEntry - Parent entry containing the schema in its value field
|
|
823
|
+
* @returns Map of field paths to their types (e.g., 'timeout' -> 'integer')
|
|
824
|
+
*/
|
|
825
|
+
function extractNestedTypeSchema(parentEntry) {
|
|
826
|
+
const typeMap = new Map();
|
|
827
|
+
if (!parentEntry.value || typeof parentEntry.value !== 'string') {
|
|
828
|
+
return undefined;
|
|
829
|
+
}
|
|
830
|
+
try {
|
|
831
|
+
const parsed = JSON.parse(parentEntry.value);
|
|
832
|
+
const complexObjectSchema = parsed.complexObjectSchema;
|
|
833
|
+
if (!complexObjectSchema) {
|
|
834
|
+
return undefined;
|
|
835
|
+
}
|
|
836
|
+
// Iterate through schema keys (e.g., "FlowDesigner:FD8fc76f46892b4980af21eb5f7b164ef2")
|
|
837
|
+
for (const schemaKey in complexObjectSchema) {
|
|
838
|
+
const schema = complexObjectSchema[schemaKey];
|
|
839
|
+
if (!schema || typeof schema !== 'object') {
|
|
840
|
+
continue;
|
|
841
|
+
}
|
|
842
|
+
// Extract field types from the schema
|
|
843
|
+
for (const fieldName in schema) {
|
|
844
|
+
if (fieldName.endsWith('.$field_facets') || fieldName.endsWith('.$type_facets')) {
|
|
845
|
+
continue;
|
|
846
|
+
}
|
|
847
|
+
const fieldValue = schema[fieldName];
|
|
848
|
+
// Check if this is a nested object with its own fields
|
|
849
|
+
if (typeof fieldValue === 'object' && fieldValue !== null) {
|
|
850
|
+
// Nested object - recursively extract types
|
|
851
|
+
for (const nestedFieldName in fieldValue) {
|
|
852
|
+
if (nestedFieldName.endsWith('.$field_facets')) {
|
|
853
|
+
continue;
|
|
854
|
+
}
|
|
855
|
+
const nestedType = fieldValue[nestedFieldName];
|
|
856
|
+
if (typeof nestedType === 'string') {
|
|
857
|
+
// Map nested field names (e.g., 'timeout' under 'settings')
|
|
858
|
+
typeMap.set(nestedFieldName, nestedType.toLowerCase());
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
else if (typeof fieldValue === 'string') {
|
|
863
|
+
// Direct type (e.g., "Integer", "Boolean", "String")
|
|
864
|
+
typeMap.set(fieldName, fieldValue.toLowerCase());
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
return typeMap.size > 0 ? typeMap : undefined;
|
|
869
|
+
}
|
|
870
|
+
catch {
|
|
871
|
+
// Ignore parsing errors
|
|
872
|
+
return undefined;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* Recursively parses children array to reconstruct nested objects and arrays
|
|
877
|
+
* @param children - Array of child entries with name, value, and optional nested children
|
|
878
|
+
* @param elementType - Type hint for array elements (e.g., 'string', 'integer')
|
|
879
|
+
* @param nestedTypeMap - Optional map of field names to types for nested FlowObjects
|
|
880
|
+
* @param complexObjectData - Optional complex object data for merging with children
|
|
881
|
+
* @param labelCacheMap - Optional map of datapill names to their types from the label cache
|
|
882
|
+
* @returns Reconstructed nested object or array
|
|
883
|
+
*/
|
|
884
|
+
function parseChildrenArray(children, elementType, nestedTypeMap, complexObjectData, labelCacheMap) {
|
|
885
|
+
if (!Array.isArray(children) || children.length === 0) {
|
|
886
|
+
return undefined;
|
|
887
|
+
}
|
|
888
|
+
// Check if this is an array or object:
|
|
889
|
+
// - Array (SDK): all children have empty names
|
|
890
|
+
// - Array (UI): all children have the SAME non-empty name (childName repeated)
|
|
891
|
+
// - Object: children have different names (field names)
|
|
892
|
+
const childObj = children[0];
|
|
893
|
+
const firstBaseName = getBaseName(childObj.name);
|
|
894
|
+
// Check if all children have the same base name (indicating FlowArray with repeated childName)
|
|
895
|
+
const allSameName = children.length > 1 &&
|
|
896
|
+
children.every((c) => {
|
|
897
|
+
const entry = c;
|
|
898
|
+
return getBaseName(entry.name) === firstBaseName;
|
|
899
|
+
});
|
|
900
|
+
// Check if the base name is empty (array elements without explicit names)
|
|
901
|
+
const hasEmptyBaseName = !firstBaseName || firstBaseName === '';
|
|
902
|
+
const isArray = hasEmptyBaseName || allSameName;
|
|
903
|
+
if (isArray) {
|
|
904
|
+
// FlowArray: reconstruct array of elements
|
|
905
|
+
const result = [];
|
|
906
|
+
for (const child of children) {
|
|
907
|
+
const childEntry = child;
|
|
908
|
+
if (childEntry.children && childEntry.children.length > 0) {
|
|
909
|
+
// Nested object or array - get corresponding complexObject data for this element
|
|
910
|
+
let elementComplexData;
|
|
911
|
+
if (Array.isArray(complexObjectData) && result.length < complexObjectData.length) {
|
|
912
|
+
elementComplexData = complexObjectData[result.length];
|
|
913
|
+
}
|
|
914
|
+
result.push(parseChildrenArray(childEntry.children, undefined, undefined, elementComplexData, labelCacheMap));
|
|
915
|
+
}
|
|
916
|
+
else if (childEntry.value !== undefined && childEntry.value !== null && childEntry.value !== '') {
|
|
917
|
+
// Primitive value - convert to proper type
|
|
918
|
+
// Use parameter type if available, otherwise use passed elementType
|
|
919
|
+
const uiType = childEntry.parameter?.type || elementType;
|
|
920
|
+
const convertedValue = typeof childEntry.value === 'string'
|
|
921
|
+
? convertChildValueToType(childEntry.value, uiType, labelCacheMap)
|
|
922
|
+
: childEntry.value;
|
|
923
|
+
result.push(convertedValue);
|
|
924
|
+
}
|
|
925
|
+
else if (Array.isArray(complexObjectData) && result.length < complexObjectData.length) {
|
|
926
|
+
// Value is empty/null, try to get from complexObject
|
|
927
|
+
result.push(complexObjectData[result.length]);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
return result;
|
|
931
|
+
}
|
|
932
|
+
else {
|
|
933
|
+
// FlowObject: reconstruct object with named fields
|
|
934
|
+
const result = {};
|
|
935
|
+
for (const child of children) {
|
|
936
|
+
const childEntry = child;
|
|
937
|
+
if (!childEntry.name) {
|
|
938
|
+
continue;
|
|
939
|
+
}
|
|
940
|
+
// Strip parent prefix from field name (e.g., "project.name" -> "name")
|
|
941
|
+
// This is needed for assignSubflowOutputs where names are prefixed with parent
|
|
942
|
+
const fieldName = getBaseName(childEntry.name);
|
|
943
|
+
if (childEntry.children && childEntry.children.length > 0) {
|
|
944
|
+
// Nested object or array
|
|
945
|
+
// Extract element type from parameter.type if this is an array (e.g., "array.integer" -> "integer")
|
|
946
|
+
const childElementType = extractArrayElementType(childEntry.parameter?.type);
|
|
947
|
+
// Extract nested complex object data for this field
|
|
948
|
+
let nestedComplexData;
|
|
949
|
+
if (complexObjectData &&
|
|
950
|
+
typeof complexObjectData === 'object' &&
|
|
951
|
+
!Array.isArray(complexObjectData)) {
|
|
952
|
+
const objData = complexObjectData;
|
|
953
|
+
nestedComplexData = objData[fieldName];
|
|
954
|
+
}
|
|
955
|
+
// Extract type schema for this nested child
|
|
956
|
+
// Try to get schema from the child entry itself, or fall back to parent's type map
|
|
957
|
+
const childTypeMap = extractNestedTypeSchema(childEntry) || nestedTypeMap;
|
|
958
|
+
result[fieldName] = parseChildrenArray(childEntry.children, childElementType, childTypeMap, nestedComplexData, labelCacheMap);
|
|
959
|
+
}
|
|
960
|
+
else if (childEntry.value !== undefined && childEntry.value !== null && childEntry.value !== '') {
|
|
961
|
+
// Primitive value - convert to proper type
|
|
962
|
+
// Try parameter type first, then nested type map, then no type
|
|
963
|
+
const uiType = childEntry.parameter?.type || nestedTypeMap?.get(childEntry.name);
|
|
964
|
+
const convertedValue = typeof childEntry.value === 'string'
|
|
965
|
+
? convertChildValueToType(childEntry.value, uiType, labelCacheMap)
|
|
966
|
+
: childEntry.value;
|
|
967
|
+
result[fieldName] = convertedValue;
|
|
968
|
+
}
|
|
969
|
+
else if (complexObjectData &&
|
|
970
|
+
typeof complexObjectData === 'object' &&
|
|
971
|
+
!Array.isArray(complexObjectData)) {
|
|
972
|
+
// No value in children, try to get from complexObject
|
|
973
|
+
const objData = complexObjectData;
|
|
974
|
+
if (fieldName in objData) {
|
|
975
|
+
const value = objData[fieldName];
|
|
976
|
+
// Apply type conversion if we have type information
|
|
977
|
+
const uiType = nestedTypeMap?.get(childEntry.name) || nestedTypeMap?.get(fieldName);
|
|
978
|
+
if (typeof value === 'string' && uiType) {
|
|
979
|
+
result[fieldName] = convertPrimitiveValue(value, uiType);
|
|
980
|
+
}
|
|
981
|
+
else {
|
|
982
|
+
result[fieldName] = value;
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
else if (childEntry.parameter?.type && childEntry.parameter.type.startsWith('array')) {
|
|
987
|
+
// Array field with no children or value - initialize as empty array
|
|
988
|
+
result[fieldName] = [];
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
// Merge any fields from complexObject that aren't in children
|
|
992
|
+
// This handles primitive arrays that are only in complexObject
|
|
993
|
+
if (complexObjectData && typeof complexObjectData === 'object' && !Array.isArray(complexObjectData)) {
|
|
994
|
+
const objData = complexObjectData;
|
|
995
|
+
for (const key of Object.keys(objData)) {
|
|
996
|
+
if (!(key in result)) {
|
|
997
|
+
result[key] = objData[key];
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
return result;
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
/**
|
|
1005
|
+
* Extracts plain values from ServiceNow's $cv wrapped format.
|
|
1006
|
+
*/
|
|
1007
|
+
function extractComplexObjectValues(complexObject) {
|
|
1008
|
+
if (typeof complexObject !== 'object' || complexObject === null) {
|
|
1009
|
+
return complexObject;
|
|
1010
|
+
}
|
|
1011
|
+
const obj = complexObject;
|
|
1012
|
+
if (obj.$cv && '$v' in obj.$cv) {
|
|
1013
|
+
return obj.$cv.$v;
|
|
1014
|
+
}
|
|
1015
|
+
if (obj.$COCollectionField) {
|
|
1016
|
+
return extractComplexObjectValues(obj.$COCollectionField);
|
|
1017
|
+
}
|
|
1018
|
+
if (Array.isArray(complexObject)) {
|
|
1019
|
+
return complexObject.map((item) => extractComplexObjectValues(item));
|
|
1020
|
+
}
|
|
1021
|
+
const result = {};
|
|
1022
|
+
for (const [key, value] of Object.entries(complexObject)) {
|
|
1023
|
+
if (key === 'name$' || key === '$COCollectionField') {
|
|
1024
|
+
continue;
|
|
1025
|
+
}
|
|
1026
|
+
result[key] = extractComplexObjectValues(value);
|
|
1027
|
+
}
|
|
1028
|
+
return result;
|
|
1029
|
+
}
|
|
1030
|
+
/**
|
|
1031
|
+
* Reconstructs FlowObject/FlowArray from complexObjectSchema with mapped datapills.
|
|
1032
|
+
* This handles the UI format where children array is empty but datapills are stored in facets.
|
|
1033
|
+
*/
|
|
1034
|
+
function reconstructFromMappedFacets(schema, complexObject) {
|
|
1035
|
+
if (typeof schema !== 'object' || schema === null) {
|
|
1036
|
+
return null;
|
|
1037
|
+
}
|
|
1038
|
+
const schemaObj = schema;
|
|
1039
|
+
const typeKey = Object.keys(schemaObj).find((k) => k.startsWith('FlowDesigner:'));
|
|
1040
|
+
if (!typeKey) {
|
|
1041
|
+
return null;
|
|
1042
|
+
}
|
|
1043
|
+
const template = schemaObj[typeKey];
|
|
1044
|
+
if (typeof template !== 'object' || template === null) {
|
|
1045
|
+
return null;
|
|
1046
|
+
}
|
|
1047
|
+
const templateObj = template;
|
|
1048
|
+
const literalValues = extractComplexObjectValues(complexObject);
|
|
1049
|
+
const mappings = extractMappingsFromFacets(templateObj);
|
|
1050
|
+
if (mappings.size === 0) {
|
|
1051
|
+
return literalValues;
|
|
1052
|
+
}
|
|
1053
|
+
return applyMappingsToValues(literalValues, mappings);
|
|
1054
|
+
}
|
|
1055
|
+
/**
|
|
1056
|
+
* Extracts datapill mappings from field facets in the schema template.
|
|
1057
|
+
* Recursively searches through nested objects and arrays to find all $field_facets.
|
|
1058
|
+
*/
|
|
1059
|
+
function extractMappingsFromFacets(template) {
|
|
1060
|
+
const mappings = new Map();
|
|
1061
|
+
function extractFromObject(obj) {
|
|
1062
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
1063
|
+
return;
|
|
1064
|
+
}
|
|
1065
|
+
if (Array.isArray(obj)) {
|
|
1066
|
+
for (const item of obj) {
|
|
1067
|
+
extractFromObject(item);
|
|
1068
|
+
}
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
const objMap = obj;
|
|
1072
|
+
for (const key of Object.keys(objMap)) {
|
|
1073
|
+
if (key.endsWith('.$field_facets')) {
|
|
1074
|
+
const facets = objMap[key];
|
|
1075
|
+
if (typeof facets !== 'object' || facets === null) {
|
|
1076
|
+
continue;
|
|
1077
|
+
}
|
|
1078
|
+
const facetsObj = facets;
|
|
1079
|
+
const simpleMapFacetStr = facetsObj.SimpleMapFacet;
|
|
1080
|
+
if (typeof simpleMapFacetStr === 'string') {
|
|
1081
|
+
try {
|
|
1082
|
+
const facetObj = JSON.parse(simpleMapFacetStr);
|
|
1083
|
+
if (facetObj.mapped) {
|
|
1084
|
+
const mappedObj = JSON.parse(facetObj.mapped);
|
|
1085
|
+
for (const [path, datapill] of Object.entries(mappedObj)) {
|
|
1086
|
+
mappings.set(path, stripDatapillTypeAnnotation(datapill));
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
catch {
|
|
1091
|
+
// Skip invalid facets
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
else {
|
|
1096
|
+
// Recursively search nested objects
|
|
1097
|
+
extractFromObject(objMap[key]);
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
extractFromObject(template);
|
|
1102
|
+
return mappings;
|
|
1103
|
+
}
|
|
1104
|
+
/**
|
|
1105
|
+
* Applies datapill mappings to literal values by setting values at dotted paths.
|
|
1106
|
+
*/
|
|
1107
|
+
function applyMappingsToValues(values, mappings) {
|
|
1108
|
+
const result = JSON.parse(JSON.stringify(values));
|
|
1109
|
+
for (const [path, datapill] of mappings.entries()) {
|
|
1110
|
+
setValueAtPath(result, path, datapill);
|
|
1111
|
+
}
|
|
1112
|
+
return result;
|
|
1113
|
+
}
|
|
1114
|
+
/**
|
|
1115
|
+
* Sets a value at a dotted path in an object/array structure.
|
|
1116
|
+
* Handles array indices like "tasks[0].dueDate".
|
|
1117
|
+
*/
|
|
1118
|
+
function setValueAtPath(obj, path, value) {
|
|
1119
|
+
const parts = path.split(/\.|\[|\]/).filter((p) => p !== '');
|
|
1120
|
+
let current = obj;
|
|
1121
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
1122
|
+
const part = parts[i];
|
|
1123
|
+
if (!part) {
|
|
1124
|
+
continue;
|
|
1125
|
+
}
|
|
1126
|
+
const nextPart = parts[i + 1];
|
|
1127
|
+
if (nextPart && /^\d+$/.test(nextPart)) {
|
|
1128
|
+
if (Array.isArray(current)) {
|
|
1129
|
+
const idx = Number.parseInt(part, 10);
|
|
1130
|
+
if (!current[idx]) {
|
|
1131
|
+
current[idx] = {};
|
|
1132
|
+
}
|
|
1133
|
+
current = current[idx];
|
|
1134
|
+
}
|
|
1135
|
+
else {
|
|
1136
|
+
if (!Array.isArray(current[part])) {
|
|
1137
|
+
current[part] = [];
|
|
1138
|
+
}
|
|
1139
|
+
current = current[part];
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
else if (/^\d+$/.test(part)) {
|
|
1143
|
+
const idx = Number.parseInt(part, 10);
|
|
1144
|
+
if (Array.isArray(current)) {
|
|
1145
|
+
if (!current[idx]) {
|
|
1146
|
+
current[idx] = {};
|
|
1147
|
+
}
|
|
1148
|
+
current = current[idx];
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
else {
|
|
1152
|
+
if (Array.isArray(current)) {
|
|
1153
|
+
const idx = Number.parseInt(part, 10);
|
|
1154
|
+
if (!current[idx]) {
|
|
1155
|
+
current[idx] = {};
|
|
1156
|
+
}
|
|
1157
|
+
current = current[idx];
|
|
1158
|
+
}
|
|
1159
|
+
else {
|
|
1160
|
+
if (typeof current[part] !== 'object' || current[part] === null) {
|
|
1161
|
+
current[part] = {};
|
|
1162
|
+
}
|
|
1163
|
+
current = current[part];
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
const lastPart = parts[parts.length - 1];
|
|
1168
|
+
if (lastPart) {
|
|
1169
|
+
if (/^\d+$/.test(lastPart)) {
|
|
1170
|
+
const idx = Number.parseInt(lastPart, 10);
|
|
1171
|
+
if (Array.isArray(current)) {
|
|
1172
|
+
current[idx] = value;
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
else {
|
|
1176
|
+
if (!Array.isArray(current)) {
|
|
1177
|
+
current[lastPart] = value;
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
/**
|
|
1183
|
+
* Generic helper to parse an array of entries with name-value pairs into an object.
|
|
1184
|
+
* Converts string values to appropriate types using type information from the schema.
|
|
1185
|
+
*
|
|
1186
|
+
* **Inline Script Support:**
|
|
1187
|
+
* Detects entries with `scriptActive: true` and creates FDInlineScriptCallShape objects
|
|
1188
|
+
* that represent `wfa.inlineScript('...')` calls in the generated Fluent code.
|
|
1189
|
+
*
|
|
1190
|
+
* **FlowObject/FlowArray Support:**
|
|
1191
|
+
* Detects entries with non-empty `children` arrays and recursively parses them back
|
|
1192
|
+
* into nested objects or arrays, reversing the buildChildrenArray operation.
|
|
1193
|
+
*
|
|
1194
|
+
* @param entries - Array of entries with name, value, and optional scriptActive/script properties
|
|
1195
|
+
* @param typeMap - Optional map of names to their internal types for proper type conversion
|
|
1196
|
+
* @param record - Optional source Record for creating FDInlineScriptCallShape with proper source
|
|
1197
|
+
* @returns Object with names as keys and properly typed values or FDInlineScriptCallShape for inline scripts
|
|
1198
|
+
*/
|
|
1199
|
+
function parseEntriesFromArray(entries, typeMap, labelCacheMap) {
|
|
1200
|
+
const result = {};
|
|
1201
|
+
for (const entry of entries) {
|
|
1202
|
+
if (!entry.name) {
|
|
1203
|
+
continue;
|
|
1204
|
+
}
|
|
1205
|
+
// Check if this is an inline script (matches flow-instance-plugin.ts pattern)
|
|
1206
|
+
// Return script metadata that buildVariableOrOutputArguments will recognize
|
|
1207
|
+
if (entry.scriptActive === true && entry.script) {
|
|
1208
|
+
const scriptData = entry.script[entry.name];
|
|
1209
|
+
if (scriptData?.script) {
|
|
1210
|
+
// Return metadata object (not a Shape) so it can be processed later
|
|
1211
|
+
result[entry.name] = {
|
|
1212
|
+
scriptActive: true,
|
|
1213
|
+
scriptContent: scriptData.script,
|
|
1214
|
+
};
|
|
1215
|
+
continue;
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
// Check if this entry has children (FlowObject/FlowArray)
|
|
1219
|
+
if (entry.children && Array.isArray(entry.children) && entry.children.length > 0) {
|
|
1220
|
+
// Special case: empty arrays are represented as a single child with empty string value
|
|
1221
|
+
// Empty arrays have: no name, empty string value, and undefined children (not empty array)
|
|
1222
|
+
// Arrays with objects (like [{item: ''}]) have children with names and/or children arrays
|
|
1223
|
+
const firstChild = entry.children[0];
|
|
1224
|
+
const isEmptyArray = entry.children.length === 1 &&
|
|
1225
|
+
firstChild.value === '' &&
|
|
1226
|
+
!firstChild.name && // Empty arrays have no name on the child element
|
|
1227
|
+
firstChild.children === undefined; // Empty arrays have undefined children, not empty array
|
|
1228
|
+
if (isEmptyArray) {
|
|
1229
|
+
result[entry.name] = [];
|
|
1230
|
+
continue;
|
|
1231
|
+
}
|
|
1232
|
+
// If parent value is a datapill (pure or template expression), use it directly and ignore children
|
|
1233
|
+
// This handles the case where a datapill is assigned to a FlowObject variable
|
|
1234
|
+
// Check for datapills: starts with {{ (pure datapill) or contains {{ but not starting with { (template expression)
|
|
1235
|
+
const isDatapillValue = entry.value &&
|
|
1236
|
+
typeof entry.value === 'string' &&
|
|
1237
|
+
(entry.value.startsWith('{{') ||
|
|
1238
|
+
(entry.value.includes('{{') && entry.value.includes('}}') && !entry.value.startsWith('{')));
|
|
1239
|
+
if (isDatapillValue) {
|
|
1240
|
+
const uiType = typeMap?.get(entry.name);
|
|
1241
|
+
const convertedValue = convertChildValueToType(entry.value, uiType, labelCacheMap);
|
|
1242
|
+
result[entry.name] = convertedValue;
|
|
1243
|
+
continue;
|
|
1244
|
+
}
|
|
1245
|
+
// Try to extract actual values from complexObject field if available
|
|
1246
|
+
if (entry.value && typeof entry.value === 'string' && !childrenContainDatapills(entry.children)) {
|
|
1247
|
+
try {
|
|
1248
|
+
const parsed = JSON.parse(entry.value);
|
|
1249
|
+
if (parsed.complexObject) {
|
|
1250
|
+
const extractedValue = extractComplexObjectValues(parsed.complexObject);
|
|
1251
|
+
// Apply type conversion using schema information
|
|
1252
|
+
const nestedTypeMap = extractNestedTypeSchema(entry);
|
|
1253
|
+
if (nestedTypeMap &&
|
|
1254
|
+
typeof extractedValue === 'object' &&
|
|
1255
|
+
extractedValue !== null &&
|
|
1256
|
+
!Array.isArray(extractedValue)) {
|
|
1257
|
+
// For FlowObject, apply type conversion to each field (including nested objects)
|
|
1258
|
+
result[entry.name] = applyTypeConversionToObject(extractedValue, nestedTypeMap);
|
|
1259
|
+
}
|
|
1260
|
+
else {
|
|
1261
|
+
result[entry.name] = extractedValue;
|
|
1262
|
+
}
|
|
1263
|
+
continue;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
catch {
|
|
1267
|
+
// Fall through to children parsing
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
// Fallback: Parse structure from children array
|
|
1271
|
+
// Extract complexObject data if available for merging with children
|
|
1272
|
+
let complexObjData;
|
|
1273
|
+
if (entry.value && typeof entry.value === 'string') {
|
|
1274
|
+
try {
|
|
1275
|
+
const parsed = JSON.parse(entry.value);
|
|
1276
|
+
if (parsed.complexObject) {
|
|
1277
|
+
complexObjData = extractComplexObjectValues(parsed.complexObject);
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
catch {
|
|
1281
|
+
// Ignore parse errors
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
const elementType = getArrayElementType(entry);
|
|
1285
|
+
const nestedTypeMap = extractNestedTypeSchema(entry);
|
|
1286
|
+
const parsedValue = parseChildrenArray(entry.children, elementType, nestedTypeMap, complexObjData, labelCacheMap);
|
|
1287
|
+
result[entry.name] = parsedValue;
|
|
1288
|
+
continue;
|
|
1289
|
+
}
|
|
1290
|
+
// Handle UI format: children empty but complexObjectSchema has mapped datapills in facets
|
|
1291
|
+
if (entry.value && typeof entry.value === 'string') {
|
|
1292
|
+
try {
|
|
1293
|
+
const parsed = JSON.parse(entry.value);
|
|
1294
|
+
if (parsed.complexObjectSchema && parsed.complexObject) {
|
|
1295
|
+
// Extract datapills from mapped facets and merge with literal values
|
|
1296
|
+
const reconstructed = reconstructFromMappedFacets(parsed.complexObjectSchema, parsed.complexObject);
|
|
1297
|
+
if (reconstructed !== null) {
|
|
1298
|
+
result[entry.name] = reconstructed;
|
|
1299
|
+
continue;
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
catch {
|
|
1304
|
+
// Fall through to regular value handling
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
// Handle regular values (non-inline-script)
|
|
1308
|
+
if (entry.value !== undefined && entry.value !== null) {
|
|
1309
|
+
// Get the type for this entry from the schema
|
|
1310
|
+
const uiType = typeMap?.get(entry.name);
|
|
1311
|
+
// Strip type annotation from datapill strings before normalization
|
|
1312
|
+
// ServiceNow stores datapills with type annotations like {{trigger.name|string}}
|
|
1313
|
+
const valueStr = String(entry.value);
|
|
1314
|
+
const cleanedValue = stripDatapillTypeAnnotation(valueStr);
|
|
1315
|
+
// Use normalizeInputValue to handle type conversion
|
|
1316
|
+
// This converts:
|
|
1317
|
+
// - "true"/"false" → boolean (when uiType is 'boolean')
|
|
1318
|
+
// - "1"/"0" → true/false (when uiType is 'boolean')
|
|
1319
|
+
// - "123" → number
|
|
1320
|
+
// - JSON strings → parsed objects
|
|
1321
|
+
// - Datapills like "{{trigger.name}}" → kept as string (for later conversion)
|
|
1322
|
+
const normalizedValue = (0, flow_instance_plugin_1.normalizeInputValue)(cleanedValue, uiType);
|
|
1323
|
+
result[entry.name] = normalizedValue;
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
return result;
|
|
1327
|
+
}
|
|
1328
|
+
})(FlowLogicValueProcessor || (FlowLogicValueProcessor = {}));
|
|
1329
|
+
/**
|
|
1330
|
+
* Helper function to check if children array contains any datapill values.
|
|
1331
|
+
* Datapills are indicated by {{...}} syntax in the value field.
|
|
1332
|
+
*
|
|
1333
|
+
* @param children - The children array to check
|
|
1334
|
+
* @returns true if any child has a datapill value
|
|
1335
|
+
*/
|
|
1336
|
+
function childrenContainDatapills(children) {
|
|
1337
|
+
for (const child of children) {
|
|
1338
|
+
if (typeof child === 'object' && child !== null) {
|
|
1339
|
+
const childObj = child;
|
|
1340
|
+
// Check if this child has a datapill value (pure datapill or template expression)
|
|
1341
|
+
if (childObj.value && typeof childObj.value === 'string') {
|
|
1342
|
+
// Check for pure datapill or template expression containing datapills
|
|
1343
|
+
if (childObj.value.includes('{{') && childObj.value.includes('}}')) {
|
|
1344
|
+
return true;
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
// Recursively check nested children
|
|
1348
|
+
if (childObj.children && Array.isArray(childObj.children) && childObj.children.length > 0) {
|
|
1349
|
+
if (childrenContainDatapills(childObj.children)) {
|
|
1350
|
+
return true;
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
return false;
|
|
1356
|
+
}
|
|
1357
|
+
// Type guard functions for each shape type
|
|
1358
|
+
function isConditionalLogicShape(expr) {
|
|
1359
|
+
return [flow_logic_constants_1.FLOW_LOGIC.IF, flow_logic_constants_1.FLOW_LOGIC.ELSEIF, flow_logic_constants_1.FLOW_LOGIC.ELSE].includes(expr.getCallee());
|
|
1360
|
+
}
|
|
1361
|
+
function isForEachShape(expr) {
|
|
1362
|
+
return expr.getCallee() === flow_logic_constants_1.FLOW_LOGIC.FOR_EACH;
|
|
1363
|
+
}
|
|
1364
|
+
function isGoBackToShape(expr) {
|
|
1365
|
+
return expr.getCallee() === flow_logic_constants_1.FLOW_LOGIC.GOBACKTO;
|
|
1366
|
+
}
|
|
1367
|
+
function isTryCatchShape(expr) {
|
|
1368
|
+
return expr.getCallee() === flow_logic_constants_1.FLOW_LOGIC.TRY_CATCH;
|
|
1369
|
+
}
|
|
1370
|
+
function isDoInParallelShape(expr) {
|
|
1371
|
+
return expr.getCallee() === flow_logic_constants_1.FLOW_LOGIC.DO_IN_PARALLEL;
|
|
1372
|
+
}
|
|
1373
|
+
function isSetFlowVariablesShape(expr) {
|
|
1374
|
+
return expr.getCallee() === flow_logic_constants_1.FLOW_LOGIC.SET_FLOW_VARIABLES;
|
|
1375
|
+
}
|
|
1376
|
+
function isAssignSubflowOutputsShape(expr) {
|
|
1377
|
+
return expr.getCallee() === flow_logic_constants_1.FLOW_LOGIC.ASSIGN_SUBFLOW_OUTPUTS;
|
|
1378
|
+
}
|
|
1379
|
+
/**
|
|
1380
|
+
* Checks if a shape is a datapill shape (PropertyAccessShape, TemplateExpressionShape, IdentifierShape, PillShape, or PillTemplateShape)
|
|
1381
|
+
*/
|
|
1382
|
+
function isDataPillShape(shape) {
|
|
1383
|
+
return (shape instanceof sdk_build_core_1.PropertyAccessShape ||
|
|
1384
|
+
shape instanceof sdk_build_core_1.TemplateExpressionShape ||
|
|
1385
|
+
shape instanceof sdk_build_core_1.IdentifierShape ||
|
|
1386
|
+
shape instanceof data_pill_shapes_1.PillShape ||
|
|
1387
|
+
shape instanceof data_pill_shapes_1.PillTemplateShape);
|
|
1388
|
+
}
|
|
1389
|
+
/**
|
|
1390
|
+
* Recursively resolves datapills in nested values (for FlowObjects/FlowArrays).
|
|
1391
|
+
*
|
|
1392
|
+
* @param value - The value to resolve datapills in
|
|
1393
|
+
* @param transform - Transform context for shape conversion
|
|
1394
|
+
* @returns Resolved value with datapills replaced
|
|
1395
|
+
*/
|
|
1396
|
+
async function resolveNestedDataPills(value, transform) {
|
|
1397
|
+
// Handle datapill shapes
|
|
1398
|
+
if (value instanceof sdk_build_core_1.Shape && isDataPillShape(value)) {
|
|
1399
|
+
return await (0, utils_1.resolveDataPillShape)(value, transform);
|
|
1400
|
+
}
|
|
1401
|
+
// Handle ObjectShape - need to resolve its entries
|
|
1402
|
+
if (value instanceof sdk_build_core_1.Shape && (value.isObject() || value.is(sdk_build_core_1.DurationShape))) {
|
|
1403
|
+
const objectShape = value.isObject()
|
|
1404
|
+
? value.asObject()
|
|
1405
|
+
: sdk_build_core_1.Shape.from(value.getSource(), value.as(sdk_build_core_1.DurationShape).getDuration()).asObject();
|
|
1406
|
+
const resolvedObj = {};
|
|
1407
|
+
const entries = objectShape.entries({ resolve: false });
|
|
1408
|
+
for (const [key, shape] of entries) {
|
|
1409
|
+
resolvedObj[key] = await resolveNestedDataPills(shape, transform);
|
|
1410
|
+
}
|
|
1411
|
+
return resolvedObj;
|
|
1412
|
+
}
|
|
1413
|
+
// Handle ArrayShape - need to resolve its elements
|
|
1414
|
+
if (value instanceof sdk_build_core_1.Shape && value.isArray()) {
|
|
1415
|
+
const arrayShape = value.asArray();
|
|
1416
|
+
const resolvedArray = [];
|
|
1417
|
+
// IMPORTANT: Use getElements(false) to prevent premature resolution of CallExpressionShapes
|
|
1418
|
+
const elements = arrayShape.getElements(false);
|
|
1419
|
+
for (const element of elements) {
|
|
1420
|
+
if (element === undefined) {
|
|
1421
|
+
continue;
|
|
1422
|
+
}
|
|
1423
|
+
resolvedArray.push(await resolveNestedDataPills(element, transform));
|
|
1424
|
+
}
|
|
1425
|
+
return resolvedArray;
|
|
1426
|
+
}
|
|
1427
|
+
// Handle CallExpressionShape for wfa.dataPill() - convert to pill string
|
|
1428
|
+
if (value instanceof sdk_build_core_1.Shape && value.is(sdk_build_core_1.CallExpressionShape)) {
|
|
1429
|
+
const callExpr = value.as(sdk_build_core_1.CallExpressionShape);
|
|
1430
|
+
if (callExpr.getCallee() === 'wfa.dataPill') {
|
|
1431
|
+
// The first argument is a PropertyAccessShape (e.g., params.inputs.inputString)
|
|
1432
|
+
// The second argument is the type hint (e.g., 'string', 'integer')
|
|
1433
|
+
const firstArg = callExpr.getArgument(0);
|
|
1434
|
+
const secondArg = callExpr.getArgument(1);
|
|
1435
|
+
if (firstArg && firstArg.is(sdk_build_core_1.PropertyAccessShape)) {
|
|
1436
|
+
// Extract the pill components from PropertyAccessShape
|
|
1437
|
+
const propAccess = firstArg.as(sdk_build_core_1.PropertyAccessShape);
|
|
1438
|
+
const elements = propAccess.getElements();
|
|
1439
|
+
if (elements.length >= 2) {
|
|
1440
|
+
const propertyNames = [];
|
|
1441
|
+
elements.forEach((element) => {
|
|
1442
|
+
if (element.is(sdk_build_core_1.IdentifierShape)) {
|
|
1443
|
+
propertyNames.push(element.as(sdk_build_core_1.IdentifierShape).getName());
|
|
1444
|
+
}
|
|
1445
|
+
});
|
|
1446
|
+
// Use the same logic as DataPillPlugin to determine root identifier and path
|
|
1447
|
+
const { rootIdentifier, pathStr } = (0, flow_data_pill_plugin_1.determineRootIdentifierAndPath)(propertyNames,
|
|
1448
|
+
// We don't have diagnostics here, casting through unknown to satisfy the type
|
|
1449
|
+
// The function will return null values if this object is used
|
|
1450
|
+
{}, propAccess);
|
|
1451
|
+
// Get type hint if provided
|
|
1452
|
+
let typePart = '';
|
|
1453
|
+
if (secondArg && secondArg.isString()) {
|
|
1454
|
+
const typeHint = secondArg.asString().getValue();
|
|
1455
|
+
typePart = `|${typeHint}`;
|
|
1456
|
+
}
|
|
1457
|
+
// Return pill format: {{rootIdentifier.path|type}}
|
|
1458
|
+
if (rootIdentifier) {
|
|
1459
|
+
return pathStr
|
|
1460
|
+
? `{{${rootIdentifier}.${pathStr}${typePart}}}`
|
|
1461
|
+
: `{{${rootIdentifier}${typePart}}}`;
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
// If already converted to PillShape or try normal resolution
|
|
1466
|
+
if (firstArg && isDataPillShape(firstArg)) {
|
|
1467
|
+
return await (0, utils_1.resolveDataPillShape)(firstArg, transform);
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
// Extract value from other Shape types
|
|
1472
|
+
if (value instanceof sdk_build_core_1.Shape) {
|
|
1473
|
+
value = value.getValue();
|
|
1474
|
+
}
|
|
1475
|
+
// Recursively handle arrays
|
|
1476
|
+
if (Array.isArray(value)) {
|
|
1477
|
+
const resolvedArray = [];
|
|
1478
|
+
for (const element of value) {
|
|
1479
|
+
// Skip undefined elements
|
|
1480
|
+
if (element === undefined) {
|
|
1481
|
+
continue;
|
|
1482
|
+
}
|
|
1483
|
+
resolvedArray.push(await resolveNestedDataPills(element, transform));
|
|
1484
|
+
}
|
|
1485
|
+
return resolvedArray;
|
|
1486
|
+
}
|
|
1487
|
+
// Recursively handle objects
|
|
1488
|
+
if (typeof value === 'object' && value !== null) {
|
|
1489
|
+
const resolvedObj = {};
|
|
1490
|
+
for (const [key, val] of Object.entries(value)) {
|
|
1491
|
+
// Skip undefined values
|
|
1492
|
+
if (val === undefined) {
|
|
1493
|
+
continue;
|
|
1494
|
+
}
|
|
1495
|
+
resolvedObj[key] = await resolveNestedDataPills(val, transform);
|
|
1496
|
+
}
|
|
1497
|
+
return resolvedObj;
|
|
1498
|
+
}
|
|
1499
|
+
// Return primitive values as-is
|
|
1500
|
+
return value;
|
|
1501
|
+
}
|
|
1502
|
+
/**
|
|
1503
|
+
* Processes inline scripts in flow logic values (setFlowVariables, assignSubflowOutputs).
|
|
1504
|
+
* Follows the same pattern as checkAndResolveInlineScripts() in flow-instance-plugin.ts.
|
|
1505
|
+
*
|
|
1506
|
+
* @param config - The ObjectShape containing config properties
|
|
1507
|
+
* @returns Array of [key, value] tuples where inline scripts are serialized
|
|
1508
|
+
*/
|
|
1509
|
+
function processInlineScriptsInFlowLogic(config) {
|
|
1510
|
+
const entries = config.entries({ resolve: false });
|
|
1511
|
+
const results = [];
|
|
1512
|
+
for (const [key, shape] of entries) {
|
|
1513
|
+
// Handle wfa.inlineScript() calls
|
|
1514
|
+
if (shape instanceof inline_script_plugin_1.FDInlineScriptCallShape) {
|
|
1515
|
+
const scriptContent = shape.getScriptContent();
|
|
1516
|
+
// Wrap in TemplateExpressionShape for proper serialization
|
|
1517
|
+
const templateShape = new sdk_build_core_1.TemplateExpressionShape({
|
|
1518
|
+
source: shape,
|
|
1519
|
+
literalText: scriptContent,
|
|
1520
|
+
});
|
|
1521
|
+
// Create InlineScriptShape for Flow Designer serialization
|
|
1522
|
+
const inlineScript = new flow_shapes_1.InlineScriptShape({
|
|
1523
|
+
source: templateShape,
|
|
1524
|
+
scriptContent,
|
|
1525
|
+
});
|
|
1526
|
+
// Convert to Flow Designer JSON format
|
|
1527
|
+
const scriptValue = inlineScript.toFlowDesignerJson(key);
|
|
1528
|
+
results.push([key, scriptValue]);
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
return results;
|
|
1532
|
+
}
|
|
1533
|
+
/**
|
|
1534
|
+
* Resolves datapills and inline scripts in flow logic config properties.
|
|
1535
|
+
* Similar to prepareActionInstanceValueJson() in flow-instance-plugin.ts.
|
|
1536
|
+
*
|
|
1537
|
+
* @param config - The ObjectShape containing config properties
|
|
1538
|
+
* @param transform - Transform context for shape conversion
|
|
1539
|
+
* @param diagnostics - Diagnostics for reporting errors
|
|
1540
|
+
* @returns ObjectShape with resolved datapill and inline script values
|
|
1541
|
+
*/
|
|
1542
|
+
async function resolveConfigDataPills(config, transform, diagnostics) {
|
|
1543
|
+
const entries = config.entries({ resolve: false });
|
|
1544
|
+
const dataPillProps = {};
|
|
1545
|
+
// Step 1: Resolve datapills (including nested datapills in FlowObject/FlowArray values)
|
|
1546
|
+
for (const [key, shape] of entries) {
|
|
1547
|
+
// Validate: annotation field must not contain datapills or inline scripts
|
|
1548
|
+
if (key === 'annotation' && (isDataPillShape(shape) || shape instanceof inline_script_plugin_1.FDInlineScriptCallShape)) {
|
|
1549
|
+
diagnostics.error(shape, 'Datapills and inline scripts are not allowed in the annotation field. Please use a static string value.');
|
|
1550
|
+
// Still extract the value to continue processing
|
|
1551
|
+
dataPillProps[key] = shape.getValue();
|
|
1552
|
+
continue;
|
|
1553
|
+
}
|
|
1554
|
+
if (isDataPillShape(shape)) {
|
|
1555
|
+
dataPillProps[key] = await (0, utils_1.resolveDataPillShape)(shape, transform);
|
|
1556
|
+
}
|
|
1557
|
+
else {
|
|
1558
|
+
// Recursively resolve nested datapills in FlowObject/FlowArray values
|
|
1559
|
+
dataPillProps[key] = await resolveNestedDataPills(shape, transform);
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
// Step 2: Process inline scripts (wfa.inlineScript() calls)
|
|
1563
|
+
// This serializes inline scripts into Flow Designer JSON format with scriptActive: true
|
|
1564
|
+
const inlineScriptResults = processInlineScriptsInFlowLogic(config);
|
|
1565
|
+
const inlineScriptMap = new Map(inlineScriptResults);
|
|
1566
|
+
// Step 3: Replace raw inline script strings with their serialized JSON objects
|
|
1567
|
+
// For fields with inline scripts: replace the raw string from Step 1 with the properly
|
|
1568
|
+
// serialized Flow Designer JSON object from Step 2. All other fields remain unchanged.
|
|
1569
|
+
const mergedProps = {};
|
|
1570
|
+
for (const [key, value] of Object.entries(dataPillProps)) {
|
|
1571
|
+
mergedProps[key] = inlineScriptMap.has(key) ? inlineScriptMap.get(key) : value;
|
|
1572
|
+
}
|
|
1573
|
+
return sdk_build_core_1.Shape.from(config.getSource(), mergedProps).asObject();
|
|
1574
|
+
}
|
|
1575
|
+
/**
|
|
1576
|
+
* Extracts the appropriate data from different flow logic shape types.
|
|
1577
|
+
* Resolves datapills in config properties for If, ElseIf, Else, ForEach, and WaitForADuration.
|
|
1578
|
+
*/
|
|
1579
|
+
async function extractDataFromShape(expr, transform, diagnostics) {
|
|
1580
|
+
// Use a more elegant approach with early returns for each shape type
|
|
1581
|
+
if (isGoBackToShape(expr)) {
|
|
1582
|
+
return expr.getStep();
|
|
1583
|
+
}
|
|
1584
|
+
if (isSetFlowVariablesShape(expr)) {
|
|
1585
|
+
const variablesToSet = expr.getVariablesToSet();
|
|
1586
|
+
return await resolveConfigDataPills(variablesToSet, transform, diagnostics);
|
|
1587
|
+
}
|
|
1588
|
+
if (isAssignSubflowOutputsShape(expr)) {
|
|
1589
|
+
const assignedSubflowOutputs = expr.getAssignedSubflowOutputs();
|
|
1590
|
+
return await resolveConfigDataPills(assignedSubflowOutputs, transform, diagnostics);
|
|
1591
|
+
}
|
|
1592
|
+
if (isForEachShape(expr)) {
|
|
1593
|
+
// Check if argument 0 (items) is a PropertyAccessShape before processing
|
|
1594
|
+
const itemsArg = expr.getArgument(0, false);
|
|
1595
|
+
if (itemsArg?.is(sdk_build_core_1.PropertyAccessShape)) {
|
|
1596
|
+
const items = expr.getItems();
|
|
1597
|
+
const itemString = await (0, utils_1.resolveDataPillShape)(items, transform);
|
|
1598
|
+
return sdk_build_core_1.Shape.from(expr, itemString).asString();
|
|
1599
|
+
}
|
|
1600
|
+
// If not a PropertyAccessShape (e.g., StringLiteralShape when no data pill assigned),
|
|
1601
|
+
// return empty string instead of falling through to default case (which returns ObjectShape)
|
|
1602
|
+
return sdk_build_core_1.Shape.from(expr, '').asString();
|
|
1603
|
+
}
|
|
1604
|
+
// For conditional logic (If/ElseIf/Else) and WAIT_FOR_A_DURATION, resolve datapills in config
|
|
1605
|
+
const logicType = expr.getCallee();
|
|
1606
|
+
if (isConditionalLogicShape(expr) || logicType === flow_logic_constants_1.FLOW_LOGIC.WAIT_FOR_A_DURATION) {
|
|
1607
|
+
const config = expr.getConfig();
|
|
1608
|
+
return await resolveConfigDataPills(config, transform, diagnostics);
|
|
1609
|
+
}
|
|
1610
|
+
// Default case: extract config as-is for other shape types
|
|
1611
|
+
return expr.getConfig();
|
|
1612
|
+
}
|
|
1613
|
+
/**
|
|
1614
|
+
* Main function to process flow logic instances and build records
|
|
1615
|
+
*/
|
|
1616
|
+
async function getFlowLogicInstanceRecord(expr, context) {
|
|
1617
|
+
const { diagnostics, transform, factory } = context;
|
|
1618
|
+
const logicType = expr.getCallee();
|
|
1619
|
+
// Validate flow logic context
|
|
1620
|
+
const diagnosticError = (0, flow_logic_diagnostics_1.validateFlowLogic)(expr, diagnostics);
|
|
1621
|
+
if (diagnosticError) {
|
|
1622
|
+
diagnostics.error(expr, diagnosticError);
|
|
1623
|
+
return { success: false };
|
|
1624
|
+
}
|
|
1625
|
+
// Extract data based on shape type using type-specific extractors
|
|
1626
|
+
const data = await extractDataFromShape(expr, transform, diagnostics);
|
|
1627
|
+
// Note: childName for arrays is now handled in resolveComplexInput within postProcess functions
|
|
1628
|
+
const values = FlowLogicValueProcessor.prepare(logicType, data);
|
|
1629
|
+
let instanceRecord = await buildFlowLogicInstanceRecord(expr, factory, values);
|
|
1630
|
+
if (isAssignSubflowOutputsShape(expr)) {
|
|
1631
|
+
instanceRecord = instanceRecord.merge({
|
|
1632
|
+
outputs_assigned: expr.getAssignedSubflowOutputsAsString(),
|
|
1633
|
+
});
|
|
1634
|
+
}
|
|
1635
|
+
if (isSetFlowVariablesShape(expr)) {
|
|
1636
|
+
instanceRecord = instanceRecord.merge({
|
|
1637
|
+
flow_variables_assigned: expr.getVariablesToSetAsString(),
|
|
1638
|
+
});
|
|
1639
|
+
}
|
|
1640
|
+
instanceRecord = instanceRecord.merge({
|
|
1641
|
+
values: values,
|
|
1642
|
+
});
|
|
1643
|
+
// Handle sibling connections for conditional logic types
|
|
1644
|
+
if (requiresSiblingValidation(logicType)) {
|
|
1645
|
+
const siblingResult = await handleSiblingConnection(expr, transform, diagnostics);
|
|
1646
|
+
if (!siblingResult.success) {
|
|
1647
|
+
return siblingResult;
|
|
1648
|
+
}
|
|
1649
|
+
if (siblingResult.value.connectedTo) {
|
|
1650
|
+
instanceRecord = instanceRecord.merge({
|
|
1651
|
+
connected_to: siblingResult.value.connectedTo,
|
|
1652
|
+
values: values,
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
return { success: true, value: instanceRecord };
|
|
1657
|
+
}
|
|
1658
|
+
function requiresSiblingValidation(logicType) {
|
|
1659
|
+
return [flow_logic_constants_1.FLOW_LOGIC.IF, flow_logic_constants_1.FLOW_LOGIC.ELSEIF, flow_logic_constants_1.FLOW_LOGIC.ELSE].includes(logicType);
|
|
1660
|
+
}
|
|
1661
|
+
async function handleSiblingConnection(expr, transform, diagnostics) {
|
|
1662
|
+
const sibling = expr
|
|
1663
|
+
.getOriginalNode()
|
|
1664
|
+
.getParent()
|
|
1665
|
+
?.getPreviousSibling((node) => !sdk_build_core_1.ts.Node.isCommentStatement(node))
|
|
1666
|
+
?.asKind(sdk_build_core_1.ts.SyntaxKind.ExpressionStatement)
|
|
1667
|
+
?.getExpression()
|
|
1668
|
+
.asKind(sdk_build_core_1.ts.SyntaxKind.CallExpression);
|
|
1669
|
+
const siblingError = (0, flow_logic_diagnostics_1.validateSibling)(expr, sibling);
|
|
1670
|
+
if (siblingError) {
|
|
1671
|
+
diagnostics.error(expr, siblingError);
|
|
1672
|
+
return { success: false };
|
|
1673
|
+
}
|
|
1674
|
+
const logicType = expr.getCallee();
|
|
1675
|
+
if (sibling && (logicType === flow_logic_constants_1.FLOW_LOGIC.ELSE || logicType === flow_logic_constants_1.FLOW_LOGIC.ELSEIF)) {
|
|
1676
|
+
const siblingRecord = await transform.toRecord(sibling);
|
|
1677
|
+
if (siblingRecord.success) {
|
|
1678
|
+
return { success: true, value: { connectedTo: siblingRecord.value.get('ui_id').asString().getValue() } };
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
return { success: true, value: {} };
|
|
1682
|
+
}
|
|
1683
|
+
const FLOW_LOGIC_ARGUMENT_BUILDERS = {
|
|
1684
|
+
[flow_logic_constants_1.FLOW_LOGIC.GOBACKTO]: (record, _, database) => {
|
|
1685
|
+
const stepId = FlowLogicValueProcessor.parse(flow_logic_constants_1.FLOW_LOGIC.GOBACKTO, record.get('values').asString().getValue());
|
|
1686
|
+
const stepRecord = database.get('sys_hub_action_instance_v2', { ui_id: stepId, flow: record.get('flow').getValue() }) ||
|
|
1687
|
+
database.get('sys_hub_sub_flow_instance_v2', { ui_id: stepId, flow: record.get('flow').getValue() }) ||
|
|
1688
|
+
database.get('sys_hub_flow_logic_instance_v2', { ui_id: stepId, flow: record.get('flow').getValue() });
|
|
1689
|
+
if (!stepRecord) {
|
|
1690
|
+
throw new Error(`Step with id ${stepId} not found`);
|
|
1691
|
+
}
|
|
1692
|
+
// Target step validation is now handled during FlowLogicInstanceShape.toRecord phase
|
|
1693
|
+
// No validation needed here anymore
|
|
1694
|
+
const firstArg = new sdk_build_core_1.ObjectShape({
|
|
1695
|
+
source: record,
|
|
1696
|
+
properties: {
|
|
1697
|
+
annotation: record.get('comment').asString().getValue(),
|
|
1698
|
+
$id: now_id_plugin_1.NowIdShape.from(record),
|
|
1699
|
+
},
|
|
1700
|
+
});
|
|
1701
|
+
const source = stepRecord.getSource();
|
|
1702
|
+
if (source instanceof sdk_build_core_1.CallExpressionShape) {
|
|
1703
|
+
const identifier = stepRecord
|
|
1704
|
+
.getOriginalNode()
|
|
1705
|
+
?.getFirstAncestorByKind(sdk_build_core_1.ts.SyntaxKind.VariableDeclaration)
|
|
1706
|
+
?.getNameNode();
|
|
1707
|
+
if (identifier) {
|
|
1708
|
+
return [
|
|
1709
|
+
firstArg,
|
|
1710
|
+
new sdk_build_core_1.IdentifierShape({
|
|
1711
|
+
source: record,
|
|
1712
|
+
name: identifier.getText(),
|
|
1713
|
+
}),
|
|
1714
|
+
];
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
return [firstArg, sdk_build_core_1.Shape.from(record, stepId)];
|
|
1718
|
+
},
|
|
1719
|
+
[flow_logic_constants_1.FLOW_LOGIC.IF]: buildConditionalArguments,
|
|
1720
|
+
[flow_logic_constants_1.FLOW_LOGIC.ELSEIF]: buildConditionalArguments,
|
|
1721
|
+
[flow_logic_constants_1.FLOW_LOGIC.ELSE]: buildConditionalArguments,
|
|
1722
|
+
[flow_logic_constants_1.FLOW_LOGIC.ENDFLOW]: buildBasicArguments,
|
|
1723
|
+
[flow_logic_constants_1.FLOW_LOGIC.EXITLOOP]: buildBasicArguments,
|
|
1724
|
+
[flow_logic_constants_1.FLOW_LOGIC.SKIP_ITERATION]: buildBasicArguments,
|
|
1725
|
+
[flow_logic_constants_1.FLOW_LOGIC.WAIT_FOR_A_DURATION]: buildWaitForADurationArguments,
|
|
1726
|
+
[flow_logic_constants_1.FLOW_LOGIC.FOR_EACH]: buildForEachArguments,
|
|
1727
|
+
[flow_logic_constants_1.FLOW_LOGIC.DO_IN_PARALLEL]: buildBasicArguments,
|
|
1728
|
+
[flow_logic_constants_1.FLOW_LOGIC.TRY_CATCH]: buildBasicArguments,
|
|
1729
|
+
[flow_logic_constants_1.FLOW_LOGIC.SET_FLOW_VARIABLES]: buildSetFlowVariablesArguments,
|
|
1730
|
+
[flow_logic_constants_1.FLOW_LOGIC.ASSIGN_SUBFLOW_OUTPUTS]: buildAssignSubflowOutputsArguments,
|
|
1731
|
+
};
|
|
1732
|
+
/**
|
|
1733
|
+
* Generic helper to build arguments for flow logic operations that follow the pattern:
|
|
1734
|
+
* (config, schema, values)
|
|
1735
|
+
*
|
|
1736
|
+
* This pattern is used by SetFlowVariables and AssignSubflowOutputs.
|
|
1737
|
+
*
|
|
1738
|
+
* @param record - ServiceNow sys_hub_flow_logic_instance_v2 record
|
|
1739
|
+
* @param database - Database to query for type information
|
|
1740
|
+
* @param tableName - Table name to query for schema records (e.g., 'sys_hub_flow_variable', 'sys_hub_flow_output')
|
|
1741
|
+
* @param flowLogicType - Flow logic type for parsing (e.g., FLOW_LOGIC.SET_FLOW_VARIABLES)
|
|
1742
|
+
* @param schemaRef - Schema reference string (e.g., '{{flowVariables}}', '{{outputs}}')
|
|
1743
|
+
* @returns Array of three Shape arguments
|
|
1744
|
+
*/
|
|
1745
|
+
function buildVariableOrOutputArguments(record, database, tableName, flowLogicType, schemaRef) {
|
|
1746
|
+
const comment = record.get('comment').asString().getValue();
|
|
1747
|
+
const valuesString = record.get('values').asString().getValue();
|
|
1748
|
+
const flowId = record.get('flow').asString().getValue();
|
|
1749
|
+
// Get flow definition and extract type information from schema
|
|
1750
|
+
const typeMap = new Map();
|
|
1751
|
+
let labelCacheMap;
|
|
1752
|
+
const flowRecord = database.get('sys_hub_flow', flowId);
|
|
1753
|
+
if (flowRecord) {
|
|
1754
|
+
// Get the schema records that belong to this flow
|
|
1755
|
+
const schemaRecords = database.query(tableName).filter((v) => v.get('model').getValue() === flowId);
|
|
1756
|
+
for (const schemaRecord of schemaRecords) {
|
|
1757
|
+
const name = schemaRecord.get('element').asString().getValue();
|
|
1758
|
+
const internalType = schemaRecord.get('internal_type').asString().getValue();
|
|
1759
|
+
typeMap.set(name, internalType);
|
|
1760
|
+
}
|
|
1761
|
+
// Extract label cache from flow record for datapill type preservation
|
|
1762
|
+
try {
|
|
1763
|
+
const labelCacheValue = flowRecord.get('label_cache')?.getValue();
|
|
1764
|
+
if (labelCacheValue && typeof labelCacheValue === 'string') {
|
|
1765
|
+
// Parse label cache JSON and create name->type map
|
|
1766
|
+
const labelCacheEntries = JSON.parse(labelCacheValue);
|
|
1767
|
+
labelCacheMap = new Map();
|
|
1768
|
+
for (const entry of labelCacheEntries) {
|
|
1769
|
+
if (entry.name && entry.type) {
|
|
1770
|
+
labelCacheMap.set(entry.name, entry.type);
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
catch {
|
|
1776
|
+
// If label cache extraction fails, continue without it
|
|
1777
|
+
labelCacheMap = undefined;
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
// Parse the values from the gzipped string with type conversion
|
|
1781
|
+
// Returns object with properly typed values, inline script markers, or datapill strings
|
|
1782
|
+
const parsedData = FlowLogicValueProcessor.parse(flowLogicType, valuesString, typeMap, labelCacheMap);
|
|
1783
|
+
// Build the config object (first argument)
|
|
1784
|
+
const configArg = new sdk_build_core_1.ObjectShape({
|
|
1785
|
+
source: record,
|
|
1786
|
+
properties: {
|
|
1787
|
+
...(comment ? { annotation: comment } : {}),
|
|
1788
|
+
$id: now_id_plugin_1.NowIdShape.from(record),
|
|
1789
|
+
},
|
|
1790
|
+
});
|
|
1791
|
+
// Second argument: schema reference ({{flowVariables}} or {{outputs}})
|
|
1792
|
+
// Keep as string - will be transformed to PropertyAccessShape by datapill transformer
|
|
1793
|
+
// The transformer will use the actual parameter name from the arrow function context
|
|
1794
|
+
const schemaArg = sdk_build_core_1.Shape.from(record, schemaRef);
|
|
1795
|
+
// Build the values object (third argument)
|
|
1796
|
+
// Process inline scripts and create FDInlineScriptCallShape (matching action plugin pattern)
|
|
1797
|
+
const valueProperties = {};
|
|
1798
|
+
for (const [key, value] of Object.entries(parsedData)) {
|
|
1799
|
+
// Check for inline script (has scriptActive and scriptContent from XML)
|
|
1800
|
+
if (value && typeof value === 'object' && 'scriptActive' in value && 'scriptContent' in value) {
|
|
1801
|
+
// Create FDInlineScriptCallShape for inline scripts (same as actions)
|
|
1802
|
+
const script = value;
|
|
1803
|
+
valueProperties[key] = new inline_script_plugin_1.FDInlineScriptCallShape({
|
|
1804
|
+
source: record,
|
|
1805
|
+
scriptContent: script.scriptContent,
|
|
1806
|
+
});
|
|
1807
|
+
}
|
|
1808
|
+
else {
|
|
1809
|
+
// Primitive value or string - convert using Shape.from()
|
|
1810
|
+
valueProperties[key] = sdk_build_core_1.Shape.from(record, value);
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
const valuesArg = new sdk_build_core_1.ObjectShape({
|
|
1814
|
+
source: record,
|
|
1815
|
+
properties: valueProperties,
|
|
1816
|
+
});
|
|
1817
|
+
return [configArg, schemaArg, valuesArg];
|
|
1818
|
+
}
|
|
1819
|
+
/**
|
|
1820
|
+
* Builds arguments for SetFlowVariables when converting XML to Fluent.
|
|
1821
|
+
* Creates the three arguments required by the SetFlowVariables API:
|
|
1822
|
+
* 1. Config object with $id and optional annotation
|
|
1823
|
+
* 2. Schema reference (placeholder for params.flowVariables)
|
|
1824
|
+
* 3. Values object with variable assignments
|
|
1825
|
+
*
|
|
1826
|
+
* **Type Conversion Pipeline:**
|
|
1827
|
+
* XML (gzipped) → parse() → normalizeInputValue(with type) → typed values → Shape.from() → Shapes
|
|
1828
|
+
*
|
|
1829
|
+
* **Example:**
|
|
1830
|
+
* ```typescript
|
|
1831
|
+
* // XML: { variables: [{ name: "count", value: "123" }, { name: "active", value: "1" }] }
|
|
1832
|
+
* // Result: SetFlowVariables({ $id: ... }, params.flowVariables, { count: 123, active: true })
|
|
1833
|
+
* ```
|
|
1834
|
+
*
|
|
1835
|
+
* @param record - ServiceNow sys_hub_flow_logic_instance_v2 record
|
|
1836
|
+
* @param _logicBodyShapes - Unused parameter (required by FlowLogicArgumentBuilder interface)
|
|
1837
|
+
* @param database - Database to query for flow variable schemas
|
|
1838
|
+
* @returns Array of three Shape arguments for SetFlowVariables call
|
|
1839
|
+
*/
|
|
1840
|
+
function buildSetFlowVariablesArguments(record, _logicBodyShapes, database) {
|
|
1841
|
+
return buildVariableOrOutputArguments(record, database, 'sys_hub_flow_variable', flow_logic_constants_1.FLOW_LOGIC.SET_FLOW_VARIABLES, '{{flowVariables}}');
|
|
1842
|
+
}
|
|
1843
|
+
/**
|
|
1844
|
+
* Builds arguments for AssignSubflowOutputs when converting XML to Fluent.
|
|
1845
|
+
* Creates the three arguments required by the AssignSubflowOutputs API:
|
|
1846
|
+
* 1. Config object with $id and optional annotation
|
|
1847
|
+
* 2. Schema reference (placeholder for params.outputs or subflow outputs schema)
|
|
1848
|
+
* 3. Values object with output assignments
|
|
1849
|
+
*
|
|
1850
|
+
* **Type Conversion Pipeline:**
|
|
1851
|
+
* XML (gzipped) → parse() → normalizeInputValue(with type) → typed values → Shape.from() → Shapes
|
|
1852
|
+
*
|
|
1853
|
+
* **Example:**
|
|
1854
|
+
* ```typescript
|
|
1855
|
+
* // XML: { outputsToAssign: [{ name: "result", value: "success" }, { name: "isComplete", value: "1" }] }
|
|
1856
|
+
* // Result: AssignSubflowOutputs({ $id: ... }, params.outputs, { result: "success", isComplete: true })
|
|
1857
|
+
* ```
|
|
1858
|
+
*
|
|
1859
|
+
* @param record - ServiceNow sys_hub_flow_logic_instance_v2 record
|
|
1860
|
+
* @param _logicBodyShapes - Unused parameter (required by FlowLogicArgumentBuilder interface)
|
|
1861
|
+
* @param database - Database to query for subflow output schemas
|
|
1862
|
+
* @returns Array of three Shape arguments for AssignSubflowOutputs call
|
|
1863
|
+
*/
|
|
1864
|
+
function buildAssignSubflowOutputsArguments(record, _logicBodyShapes, database) {
|
|
1865
|
+
return buildVariableOrOutputArguments(record, database, 'sys_hub_flow_output', flow_logic_constants_1.FLOW_LOGIC.ASSIGN_SUBFLOW_OUTPUTS, '{{outputs}}');
|
|
1866
|
+
}
|
|
1867
|
+
function buildBasicArguments(record) {
|
|
1868
|
+
return [
|
|
1869
|
+
new sdk_build_core_1.ObjectShape({
|
|
1870
|
+
source: record,
|
|
1871
|
+
properties: {
|
|
1872
|
+
annotation: record.get('comment').asString().getValue(),
|
|
1873
|
+
$id: now_id_plugin_1.NowIdShape.from(record),
|
|
1874
|
+
},
|
|
1875
|
+
}),
|
|
1876
|
+
];
|
|
1877
|
+
}
|
|
1878
|
+
function buildWaitForADurationArguments(record, _logicBodyShapes, database) {
|
|
1879
|
+
const zippedInputs = record.get('values').asString().getValue();
|
|
1880
|
+
const logicDefinitionId = record.get('logic_definition').asString().getValue();
|
|
1881
|
+
const logicDefinition = database.get('sys_hub_flow_logic_definition', logicDefinitionId);
|
|
1882
|
+
const definitionInputs = logicDefinition
|
|
1883
|
+
?.flat()
|
|
1884
|
+
.filter((v) => v.is(sdk_build_core_1.Record) && v.getTable() === 'sys_hub_flow_logic_input');
|
|
1885
|
+
const inputsShape = buildInputsShapeFromZipped({
|
|
1886
|
+
zippedInputs,
|
|
1887
|
+
definitionInputs,
|
|
1888
|
+
record,
|
|
1889
|
+
});
|
|
1890
|
+
const comment = record.get('comment').asString().getValue();
|
|
1891
|
+
const uuid = record.get('ui_id').asString().getValue();
|
|
1892
|
+
// Get properties from inputsShape (which is now an ObjectShape)
|
|
1893
|
+
// Use entries({ resolve: false }) to preserve shape types (DurationShape, etc.)
|
|
1894
|
+
const inputProperties = inputsShape ? Object.fromEntries(inputsShape.entries({ resolve: false })) : {};
|
|
1895
|
+
const mergedProperties = {
|
|
1896
|
+
...(comment ? { annotation: comment } : {}),
|
|
1897
|
+
$id: now_id_plugin_1.NowIdShape.from(record),
|
|
1898
|
+
...(uuid ? { uuid } : {}),
|
|
1899
|
+
...inputProperties,
|
|
1900
|
+
};
|
|
1901
|
+
const finalArg = new sdk_build_core_1.ObjectShape({
|
|
1902
|
+
source: record,
|
|
1903
|
+
properties: mergedProperties,
|
|
1904
|
+
});
|
|
1905
|
+
return [finalArg];
|
|
1906
|
+
}
|
|
1907
|
+
function buildConditionalArguments(record, logicBodyShapes) {
|
|
1908
|
+
const logicName = flow_logic_constants_1.FlowLogicSysId.getLogicName(record.get('logic_definition').asString().getValue());
|
|
1909
|
+
const valuesJson = FlowLogicValueProcessor.parse(logicName, record.get('values').asString().getValue());
|
|
1910
|
+
const properties = {
|
|
1911
|
+
...valuesJson,
|
|
1912
|
+
annotation: record.get('comment').asString().getValue(),
|
|
1913
|
+
$id: now_id_plugin_1.NowIdShape.from(record),
|
|
1914
|
+
};
|
|
1915
|
+
const firstArg = new sdk_build_core_1.ObjectShape({ source: record, properties });
|
|
1916
|
+
return [
|
|
1917
|
+
firstArg,
|
|
1918
|
+
new arrow_function_plugin_1.ArrowFunctionShape({
|
|
1919
|
+
source: record,
|
|
1920
|
+
parameters: [],
|
|
1921
|
+
statements: logicBodyShapes,
|
|
1922
|
+
}),
|
|
1923
|
+
];
|
|
1924
|
+
}
|
|
1925
|
+
/**
|
|
1926
|
+
* Builds arguments for ForEach flow logic from XML record.
|
|
1927
|
+
* Converts XML structure back to Fluent ForEach syntax.
|
|
1928
|
+
*
|
|
1929
|
+
* @param record - The sys_hub_flow_logic_instance_v2 record
|
|
1930
|
+
* @param logicBodyShapes - The child shapes that form the loop body
|
|
1931
|
+
* @returns Array of shapes: [items, config, body]
|
|
1932
|
+
*
|
|
1933
|
+
* @example
|
|
1934
|
+
* // XML with values: { items: "{{trigger.array_field}}" }
|
|
1935
|
+
* // Converts to: ForEach(params.trigger.array_field, { $id, annotation }, (item) => {...})
|
|
1936
|
+
*/
|
|
1937
|
+
/**
|
|
1938
|
+
* Build arguments for forEach flow logic with intelligent parameter name preservation.
|
|
1939
|
+
*
|
|
1940
|
+
* PARAMETER NAMING STRATEGY (Priority Order):
|
|
1941
|
+
*
|
|
1942
|
+
* 1. **Original Source Extraction** (Round-Trip Preservation):
|
|
1943
|
+
* - For Fluent-authored flows, extracts parameter name from original AST
|
|
1944
|
+
* - Preserves user-chosen names: (dept), (category), (item_0), etc.
|
|
1945
|
+
* - Enables lossless round-trips: Fluent → XML → Fluent
|
|
1946
|
+
*
|
|
1947
|
+
* 2. **Order-Based Naming** (UI-Authored Flows):
|
|
1948
|
+
* - For UI-authored flows (no original source), generates item_${order}
|
|
1949
|
+
* - Creates predictable names: item_0, item_1, item_2
|
|
1950
|
+
* - Disambiguates nested forEach loops clearly
|
|
1951
|
+
*
|
|
1952
|
+
* 3. **Default Fallback** (Edge Cases):
|
|
1953
|
+
* - Falls back to 'item' if neither strategy succeeds
|
|
1954
|
+
*
|
|
1955
|
+
* @example Round-Trip Preservation
|
|
1956
|
+
* // Original Fluent code:
|
|
1957
|
+
* forEach(users, {...}, (dept) => {
|
|
1958
|
+
* forEach(categories, {...}, (category) => {
|
|
1959
|
+
* action(..., { msg: `${dept.name} - ${category.value}` })
|
|
1960
|
+
* })
|
|
1961
|
+
* })
|
|
1962
|
+
*
|
|
1963
|
+
* // After deploy + pull: PRESERVED!
|
|
1964
|
+
* forEach(users, {...}, (dept) => { // ✅ 'dept' preserved
|
|
1965
|
+
* forEach(categories, {...}, (category) => { // ✅ 'category' preserved
|
|
1966
|
+
* action(..., { msg: `${dept.name} - ${category.value}` })
|
|
1967
|
+
* })
|
|
1968
|
+
* })
|
|
1969
|
+
*
|
|
1970
|
+
* @example UI-Authored Flow
|
|
1971
|
+
* // Flow created in UI, pulled to Fluent:
|
|
1972
|
+
* forEach(users, {...}, (item_0) => { // Generated from order=0
|
|
1973
|
+
* forEach(categories, {...}, (item_3) => { // Generated from order=3
|
|
1974
|
+
* action(..., { msg: `${item_0.name} - ${item_3.value}` })
|
|
1975
|
+
* })
|
|
1976
|
+
* })
|
|
1977
|
+
*
|
|
1978
|
+
* @param record - The forEach flow logic instance record
|
|
1979
|
+
* @param logicBodyShapes - The shapes within the forEach body
|
|
1980
|
+
* @returns Array of shapes [items, config, body] for forEach arguments
|
|
1981
|
+
*/
|
|
1982
|
+
/**
|
|
1983
|
+
* Extracts the forEach parameter name from the original source (for round-trip preservation).
|
|
1984
|
+
* Falls back to order-based naming for UI-authored flows, or 'item' as final fallback.
|
|
1985
|
+
*
|
|
1986
|
+
* @param record - The forEach flow logic record
|
|
1987
|
+
* @returns The parameter name to use in the arrow function
|
|
1988
|
+
*/
|
|
1989
|
+
function extractForEachParameterName(record) {
|
|
1990
|
+
try {
|
|
1991
|
+
const originalSource = record.getOriginalSource();
|
|
1992
|
+
// Check if we have a valid ts.Node
|
|
1993
|
+
if (!sdk_build_core_1.ts.Node.isNode(originalSource)) {
|
|
1994
|
+
return getFallbackParameterName(record);
|
|
1995
|
+
}
|
|
1996
|
+
// For forEach: wfa.flowLogic.forEach(items, config, (param) => {...})
|
|
1997
|
+
// Third argument (index 2) is the arrow function
|
|
1998
|
+
const arrowFn = originalSource.getArguments()[2]?.asKind(sdk_build_core_1.ts.SyntaxKind.ArrowFunction);
|
|
1999
|
+
if (!arrowFn) {
|
|
2000
|
+
return getFallbackParameterName(record);
|
|
2001
|
+
}
|
|
2002
|
+
const params = arrowFn.getParameters();
|
|
2003
|
+
const paramName = params[0]?.getName();
|
|
2004
|
+
return paramName || getFallbackParameterName(record);
|
|
2005
|
+
}
|
|
2006
|
+
catch {
|
|
2007
|
+
// Failed to extract parameter name - using fallback
|
|
2008
|
+
return getFallbackParameterName(record);
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
/**
|
|
2012
|
+
* Gets the fallback parameter name based on order or default.
|
|
2013
|
+
*/
|
|
2014
|
+
function getFallbackParameterName(record) {
|
|
2015
|
+
const order = record.get('order')?.getValue();
|
|
2016
|
+
return order ? `item_${order}` : 'item';
|
|
2017
|
+
}
|
|
2018
|
+
function buildForEachArguments(record, logicBodyShapes) {
|
|
2019
|
+
// 1. Parse values field to extract items data pill string
|
|
2020
|
+
const logicName = flow_logic_constants_1.FlowLogicSysId.getLogicName(record.get('logic_definition').asString().getValue());
|
|
2021
|
+
const valuesJson = FlowLogicValueProcessor.parse(logicName, record.get('values').asString().getValue());
|
|
2022
|
+
// 2. Return items as raw string - will be converted by processInstanceForDatapills
|
|
2023
|
+
// with the correct parameter name extracted from the flow
|
|
2024
|
+
const itemsString = valuesJson.items || '';
|
|
2025
|
+
const itemsShape = sdk_build_core_1.Shape.from(record, itemsString);
|
|
2026
|
+
// 3. Build config ObjectShape
|
|
2027
|
+
const configShape = new sdk_build_core_1.ObjectShape({
|
|
2028
|
+
source: record,
|
|
2029
|
+
properties: {
|
|
2030
|
+
annotation: record.get('comment').asString().getValue(),
|
|
2031
|
+
$id: now_id_plugin_1.NowIdShape.from(record),
|
|
2032
|
+
},
|
|
2033
|
+
});
|
|
2034
|
+
// 4. Build body ArrowFunctionShape with parameter name extraction
|
|
2035
|
+
// Strategy for parameter naming (in priority order):
|
|
2036
|
+
// 1. Extract from original source (for round-trip preservation) - e.g., user wrote (item_0), (dept), etc.
|
|
2037
|
+
// 2. Use order-based naming for UI-authored flows - e.g., item_0, item_1, item_2
|
|
2038
|
+
// 3. Fall back to 'item' if order is unavailable
|
|
2039
|
+
const parameterName = extractForEachParameterName(record);
|
|
2040
|
+
const bodyShape = new arrow_function_plugin_1.ArrowFunctionShape({
|
|
2041
|
+
source: record,
|
|
2042
|
+
parameters: [new sdk_build_core_1.IdentifierShape({ source: record, name: parameterName })],
|
|
2043
|
+
statements: logicBodyShapes,
|
|
2044
|
+
});
|
|
2045
|
+
// 5. Return in correct order: [items, config, body]
|
|
2046
|
+
return [itemsShape, configShape, bodyShape];
|
|
2047
|
+
}
|
|
2048
|
+
function buildInputsShapeFromZipped({ zippedInputs, definitionInputs, record, }) {
|
|
2049
|
+
if (!zippedInputs) {
|
|
2050
|
+
return undefined;
|
|
2051
|
+
}
|
|
2052
|
+
// Type definition includes parameter field which contains type information from XML
|
|
2053
|
+
let inputsJson;
|
|
2054
|
+
try {
|
|
2055
|
+
const inputsBuffer = (0, zlib_1.gunzipSync)(Buffer.from(zippedInputs, 'base64'));
|
|
2056
|
+
inputsJson = JSON.parse(inputsBuffer.toString('utf8'));
|
|
2057
|
+
}
|
|
2058
|
+
catch {
|
|
2059
|
+
// Failed to parse zipped inputs - returning undefined
|
|
2060
|
+
return undefined;
|
|
2061
|
+
}
|
|
2062
|
+
const inputs = {};
|
|
2063
|
+
if (inputsJson.inputs) {
|
|
2064
|
+
const durationTypeInput = inputsJson.inputs.find((i) => i.name === 'duration_type');
|
|
2065
|
+
const durationType = (durationTypeInput ? (0, flow_instance_plugin_1.normalizeInputValue)(durationTypeInput.value) : '');
|
|
2066
|
+
const validProps = flow_logic_constants_1.DURATION_TYPE_PROPS[durationType];
|
|
2067
|
+
if (validProps) {
|
|
2068
|
+
for (const input of inputsJson.inputs) {
|
|
2069
|
+
const mappedName = flow_logic_constants_1.NAME_MAP[input.name] ?? input.name;
|
|
2070
|
+
if (validProps.has(mappedName)) {
|
|
2071
|
+
const defInput = definitionInputs?.find((def) => def.get('element').getValue() === input.name);
|
|
2072
|
+
const uiType = defInput?.get('ui_type')?.asString()?.getValue();
|
|
2073
|
+
// Use uiType or fallback to parameter.type (consistent with flow-instance-plugin)
|
|
2074
|
+
const effectiveType = uiType ?? input.parameter?.type;
|
|
2075
|
+
const value = (0, flow_instance_plugin_1.normalizeInputValue)(input.value, effectiveType, record);
|
|
2076
|
+
if (value !== '') {
|
|
2077
|
+
inputs[mappedName] = value;
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
return sdk_build_core_1.Shape.from(record, inputs).asObject();
|
|
2084
|
+
}
|
|
2085
|
+
function getFlowLogicArguments(logicName, record, logicBodyShapes, database) {
|
|
2086
|
+
const builder = FLOW_LOGIC_ARGUMENT_BUILDERS[logicName];
|
|
2087
|
+
if (!builder) {
|
|
2088
|
+
throw new Error(`Unsupported flow logic type for argument building: ${logicName}`);
|
|
2089
|
+
}
|
|
2090
|
+
return builder(record, logicBodyShapes, database);
|
|
2091
|
+
}
|
|
2092
|
+
//# sourceMappingURL=flow-logic-plugin-helpers.js.map
|