@solidxai/core 0.1.13-beta.2 → 0.1.13-beta.4
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/commands/seed.command.d.ts +2 -0
- package/dist/commands/seed.command.d.ts.map +1 -1
- package/dist/commands/seed.command.js +21 -0
- package/dist/commands/seed.command.js.map +1 -1
- package/dist/entities/saved-filters.entity.js +3 -3
- package/dist/entities/saved-filters.entity.js.map +1 -1
- package/dist/entities/workflow-trigger-execution.entity.js +1 -1
- package/dist/entities/workflow-trigger-execution.entity.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/interfaces.d.ts +1 -0
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/interfaces.js.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.js +44 -27
- package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +80 -67
- package/dist/services/workflow/nodes/execution-fail.node.d.ts +6 -0
- package/dist/services/workflow/nodes/execution-fail.node.d.ts.map +1 -0
- package/dist/services/workflow/nodes/execution-fail.node.js +81 -0
- package/dist/services/workflow/nodes/execution-fail.node.js.map +1 -0
- package/dist/services/workflow/nodes/for-each.node.d.ts +2 -0
- package/dist/services/workflow/nodes/for-each.node.d.ts.map +1 -1
- package/dist/services/workflow/nodes/for-each.node.js +114 -5
- package/dist/services/workflow/nodes/for-each.node.js.map +1 -1
- package/dist/services/workflow/nodes/if.node.d.ts.map +1 -1
- package/dist/services/workflow/nodes/if.node.js +7 -0
- package/dist/services/workflow/nodes/if.node.js.map +1 -1
- package/dist/services/workflow/nodes/log-write.node.d.ts.map +1 -1
- package/dist/services/workflow/nodes/log-write.node.js +2 -0
- package/dist/services/workflow/nodes/log-write.node.js.map +1 -1
- package/dist/services/workflow/nodes/loop-until.node.d.ts +12 -0
- package/dist/services/workflow/nodes/loop-until.node.d.ts.map +1 -0
- package/dist/services/workflow/nodes/loop-until.node.js +281 -0
- package/dist/services/workflow/nodes/loop-until.node.js.map +1 -0
- package/dist/services/workflow/nodes/parallel.node.d.ts.map +1 -1
- package/dist/services/workflow/nodes/parallel.node.js +7 -0
- package/dist/services/workflow/nodes/parallel.node.js.map +1 -1
- package/dist/services/workflow/nodes/sequential.node.d.ts.map +1 -1
- package/dist/services/workflow/nodes/sequential.node.js +7 -0
- package/dist/services/workflow/nodes/sequential.node.js.map +1 -1
- package/dist/services/workflow/nodes/switch.node.d.ts.map +1 -1
- package/dist/services/workflow/nodes/switch.node.js +7 -0
- package/dist/services/workflow/nodes/switch.node.js.map +1 -1
- package/dist/services/workflow/workflow-definition-validator.service.d.ts.map +1 -1
- package/dist/services/workflow/workflow-definition-validator.service.js +5 -2
- package/dist/services/workflow/workflow-definition-validator.service.js.map +1 -1
- package/dist/services/workflow/workflow-expression.service.d.ts.map +1 -1
- package/dist/services/workflow/workflow-expression.service.js +5 -0
- package/dist/services/workflow/workflow-expression.service.js.map +1 -1
- package/dist/services/workflow/workflow-node-registry.service.d.ts +2 -1
- package/dist/services/workflow/workflow-node-registry.service.d.ts.map +1 -1
- package/dist/services/workflow/workflow-node-registry.service.js +7 -0
- package/dist/services/workflow/workflow-node-registry.service.js.map +1 -1
- package/dist/services/workflow/workflow-runtime.service.d.ts +8 -0
- package/dist/services/workflow/workflow-runtime.service.d.ts.map +1 -1
- package/dist/services/workflow/workflow-runtime.service.js +197 -32
- package/dist/services/workflow/workflow-runtime.service.js.map +1 -1
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +4 -0
- package/dist/solid-core.module.js.map +1 -1
- package/dist/types/workflow-dsl.types.d.ts +14 -0
- package/dist/types/workflow-dsl.types.d.ts.map +1 -1
- package/dist/types/workflow-dsl.types.js.map +1 -1
- package/dist-tests/api/authenticate.spec.js +119 -0
- package/dist-tests/api/authenticate.spec.js.map +1 -0
- package/dist-tests/api/crud-service.findOne.cityMaster.spec.js +97 -0
- package/dist-tests/api/crud-service.findOne.cityMaster.spec.js.map +1 -0
- package/dist-tests/api/ping.spec.js +21 -0
- package/dist-tests/api/ping.spec.js.map +1 -0
- package/dist-tests/helpers/auth.js +41 -0
- package/dist-tests/helpers/auth.js.map +1 -0
- package/dist-tests/helpers/env.js +11 -0
- package/dist-tests/helpers/env.js.map +1 -0
- package/docs/agent-builder/notebook-lm-prompt-for-agent-builder.md +136 -0
- package/docs/agent-builder/team-ready-note-agent-builder.md +305 -0
- package/docs/agent-hub-grooming.md +301 -0
- package/docs/dashboards/AGENTIC_DASHBOARD_IMPLEMENTATION_PLAN.md +438 -0
- package/docs/dashboards/dashboard-curl-smoke-tests.txt +146 -0
- package/docs/dashboards/delete-legacy-dashboard-metadata.sql +172 -0
- package/docs/datasource-introspection-ddl-analysis.md +326 -0
- package/docs/datasource-introspection-implementation-plan.md +306 -0
- package/docs/grouping-enhancements.md +89 -0
- package/docs/java-spring/README.md +3 -0
- package/docs/java-spring/solid-core-module-deep-dive-report.md +1317 -0
- package/docs/module-package-import-handoff.md +691 -0
- package/docs/seed-changes.md +65 -0
- package/docs/test-data-workflow.md +200 -0
- package/docs/type-declaration-import-issue.md +24 -0
- package/docs/workflow/business-automation-example-notes.md +309 -0
- package/docs/workflow/control-flow-node-addition-sop.md +324 -0
- package/docs/workflow/data-engineering-pipeline-example-notes.md +330 -0
- package/docs/workflow/foreach-example-notes.md +187 -0
- package/docs/workflow/hello-world-example-notes.md +271 -0
- package/docs/workflow/kestra-concepts-plugins-blueprints.md +315 -0
- package/docs/workflow/loop-until-example-notes.md +198 -0
- package/docs/workflow/microservices-and-apis-example-notes.md +264 -0
- package/docs/workflow/samples.md +25 -0
- package/docs/workflow/what-is-kestra.md +79 -0
- package/docs/workflow/workflow-core-module-handoff-summary.md +191 -0
- package/docs/workflow/workflow-module-brd.md +185 -0
- package/docs/workflow/workflow-module-domain-model.md +252 -0
- package/docs/workflow/workflow-module-metadata-dsl.md +302 -0
- package/docs/workflow/workflow-module-technical-spec.md +293 -0
- package/docs/workflow/workflow-node-type-contracts.md +635 -0
- package/docs/workflow/workflow-node-ui-metadata-contract.md +1262 -0
- package/docs/workflow/workflow-solidx-model-field-spec.md +284 -0
- package/package.json +1 -1
- package/src/commands/seed.command.ts +19 -0
- package/src/entities/saved-filters.entity.ts +3 -3
- package/src/entities/workflow-trigger-execution.entity.ts +1 -1
- package/src/index.ts +2 -0
- package/src/interfaces.ts +1 -0
- package/src/seeders/module-metadata-seeder.service.ts +50 -36
- package/src/seeders/seed-data/solid-core-metadata.json +80 -67
- package/src/services/workflow/nodes/execution-fail.node.ts +82 -0
- package/src/services/workflow/nodes/for-each.node.ts +140 -6
- package/src/services/workflow/nodes/if.node.ts +7 -0
- package/src/services/workflow/nodes/log-write.node.ts +2 -0
- package/src/services/workflow/nodes/loop-until.node.ts +343 -0
- package/src/services/workflow/nodes/parallel.node.ts +7 -0
- package/src/services/workflow/nodes/sequential.node.ts +7 -0
- package/src/services/workflow/nodes/switch.node.ts +7 -0
- package/src/services/workflow/workflow-definition-validator.service.ts +5 -2
- package/src/services/workflow/workflow-expression.service.ts +5 -0
- package/src/services/workflow/workflow-node-registry.service.ts +11 -0
- package/src/services/workflow/workflow-runtime.service.ts +274 -39
- package/src/solid-core.module.ts +4 -0
- package/src/types/workflow-dsl.types.ts +43 -0
- package/.claude/settings.local.json +0 -16
- package/CLAUDE.md +0 -26
- package/src/services/1.js +0 -6
|
@@ -8681,16 +8681,42 @@
|
|
|
8681
8681
|
{
|
|
8682
8682
|
"displayName": "Workflow Definitions",
|
|
8683
8683
|
"name": "workflowDefinition-list-action",
|
|
8684
|
-
"type": "
|
|
8684
|
+
"type": "solid",
|
|
8685
8685
|
"domain": "",
|
|
8686
8686
|
"context": "",
|
|
8687
|
-
"customComponent": "
|
|
8687
|
+
"customComponent": "",
|
|
8688
8688
|
"customIsModal": true,
|
|
8689
8689
|
"serverEndpoint": "",
|
|
8690
8690
|
"viewUserKey": "workflowDefinition-list-view",
|
|
8691
8691
|
"moduleUserKey": "solid-core",
|
|
8692
8692
|
"modelUserKey": "workflowDefinition"
|
|
8693
8693
|
},
|
|
8694
|
+
{
|
|
8695
|
+
"displayName": "New Workflow Definition",
|
|
8696
|
+
"name": "workflowDefinition-create-action",
|
|
8697
|
+
"type": "custom",
|
|
8698
|
+
"domain": "",
|
|
8699
|
+
"context": "{}",
|
|
8700
|
+
"customComponent": "/admin/core/solid-core/workflow-definition/editor/new",
|
|
8701
|
+
"customIsModal": false,
|
|
8702
|
+
"serverEndpoint": "",
|
|
8703
|
+
"viewUserKey": "workflowDefinition-list-view",
|
|
8704
|
+
"moduleUserKey": "solid-core",
|
|
8705
|
+
"modelUserKey": "workflowDefinition"
|
|
8706
|
+
},
|
|
8707
|
+
{
|
|
8708
|
+
"displayName": "Workflow Definition Editor",
|
|
8709
|
+
"name": "workflowDefinition-editor-action",
|
|
8710
|
+
"type": "custom",
|
|
8711
|
+
"domain": "",
|
|
8712
|
+
"context": "{}",
|
|
8713
|
+
"customComponent": "/admin/core/solid-core/workflow-definition/editor",
|
|
8714
|
+
"customIsModal": false,
|
|
8715
|
+
"serverEndpoint": "",
|
|
8716
|
+
"viewUserKey": "workflowDefinition-list-view",
|
|
8717
|
+
"moduleUserKey": "solid-core",
|
|
8718
|
+
"modelUserKey": "workflowDefinition"
|
|
8719
|
+
},
|
|
8694
8720
|
{
|
|
8695
8721
|
"displayName": "Workflow Execution List Action",
|
|
8696
8722
|
"name": "workflowExecution-list-action",
|
|
@@ -17260,97 +17286,71 @@
|
|
|
17260
17286
|
],
|
|
17261
17287
|
"enableGlobalSearch": true,
|
|
17262
17288
|
"create": true,
|
|
17289
|
+
"createAction": "workflowDefinition-create-action",
|
|
17263
17290
|
"edit": true,
|
|
17291
|
+
"editAction": "workflowDefinition-editor-action",
|
|
17264
17292
|
"delete": true,
|
|
17265
|
-
"
|
|
17266
|
-
|
|
17267
|
-
"attrs": {
|
|
17268
|
-
"label": "Open Workflow Editor",
|
|
17269
|
-
"action": "openWorkflowDefinitionEditorHeaderAction",
|
|
17270
|
-
"actionInContextMenu": false,
|
|
17271
|
-
"openInPopup": false,
|
|
17272
|
-
"icon": "si-sparkles"
|
|
17273
|
-
}
|
|
17274
|
-
}
|
|
17275
|
-
],
|
|
17276
|
-
"rowButtons": [
|
|
17277
|
-
{
|
|
17278
|
-
"attrs": {
|
|
17279
|
-
"className": "p-button-text",
|
|
17280
|
-
"icon": "si-pencil",
|
|
17281
|
-
"label": "Open Editor",
|
|
17282
|
-
"action": "openWorkflowDefinitionEditorRowAction",
|
|
17283
|
-
"openInPopup": false,
|
|
17284
|
-
"actionInContextMenu": true
|
|
17285
|
-
}
|
|
17286
|
-
}
|
|
17287
|
-
]
|
|
17293
|
+
"showRowActionsAsIconOnly": true,
|
|
17294
|
+
"showRowActionsInContextMenu": false
|
|
17288
17295
|
},
|
|
17289
17296
|
"children": [
|
|
17290
17297
|
{
|
|
17291
17298
|
"type": "field",
|
|
17292
17299
|
"attrs": {
|
|
17293
|
-
"name": "
|
|
17294
|
-
|
|
17295
|
-
|
|
17296
|
-
|
|
17297
|
-
|
|
17298
|
-
"attrs": {
|
|
17299
|
-
"name": "key"
|
|
17300
|
-
}
|
|
17301
|
-
},
|
|
17302
|
-
{
|
|
17303
|
-
"type": "field",
|
|
17304
|
-
"attrs": {
|
|
17305
|
-
"name": "moduleMetadata"
|
|
17306
|
-
}
|
|
17307
|
-
},
|
|
17308
|
-
{
|
|
17309
|
-
"type": "field",
|
|
17310
|
-
"attrs": {
|
|
17311
|
-
"name": "displayName"
|
|
17312
|
-
}
|
|
17313
|
-
},
|
|
17314
|
-
{
|
|
17315
|
-
"type": "field",
|
|
17316
|
-
"attrs": {
|
|
17317
|
-
"name": "namespace"
|
|
17318
|
-
}
|
|
17319
|
-
},
|
|
17320
|
-
{
|
|
17321
|
-
"type": "field",
|
|
17322
|
-
"attrs": {
|
|
17323
|
-
"name": "description"
|
|
17300
|
+
"name": "key",
|
|
17301
|
+
"className": "font-bold",
|
|
17302
|
+
"sortable": true,
|
|
17303
|
+
"isSearchable": true,
|
|
17304
|
+
"truncateAfter": 28
|
|
17324
17305
|
}
|
|
17325
17306
|
},
|
|
17326
17307
|
{
|
|
17327
17308
|
"type": "field",
|
|
17328
17309
|
"attrs": {
|
|
17329
|
-
"name": "
|
|
17310
|
+
"name": "displayName",
|
|
17311
|
+
"className": "font-bold",
|
|
17312
|
+
"sortable": true,
|
|
17313
|
+
"isSearchable": true,
|
|
17314
|
+
"truncateAfter": 32
|
|
17330
17315
|
}
|
|
17331
17316
|
},
|
|
17332
17317
|
{
|
|
17333
17318
|
"type": "field",
|
|
17334
17319
|
"attrs": {
|
|
17335
|
-
"name": "
|
|
17320
|
+
"name": "namespace",
|
|
17321
|
+
"sortable": true,
|
|
17322
|
+
"isSearchable": true,
|
|
17323
|
+
"truncateAfter": 28
|
|
17336
17324
|
}
|
|
17337
17325
|
},
|
|
17338
17326
|
{
|
|
17339
17327
|
"type": "field",
|
|
17340
17328
|
"attrs": {
|
|
17341
|
-
"name": "
|
|
17329
|
+
"name": "description",
|
|
17330
|
+
"isSearchable": true,
|
|
17331
|
+
"truncateAfter": 56
|
|
17342
17332
|
}
|
|
17343
17333
|
},
|
|
17344
17334
|
{
|
|
17345
17335
|
"type": "field",
|
|
17346
17336
|
"attrs": {
|
|
17347
|
-
"name": "
|
|
17337
|
+
"name": "status",
|
|
17338
|
+
"sortable": true,
|
|
17339
|
+
"viewWidget": "PillWidget",
|
|
17340
|
+
"pillColorMap": {
|
|
17341
|
+
"draft": "#e5e7eb",
|
|
17342
|
+
"active": "#dcfce7",
|
|
17343
|
+
"inactive": "#fef3c7",
|
|
17344
|
+
"archived": "#ede9fe",
|
|
17345
|
+
"default": "#e5e7eb"
|
|
17346
|
+
}
|
|
17348
17347
|
}
|
|
17349
17348
|
},
|
|
17350
17349
|
{
|
|
17351
17350
|
"type": "field",
|
|
17352
17351
|
"attrs": {
|
|
17353
|
-
"name": "
|
|
17352
|
+
"name": "definitionVersion",
|
|
17353
|
+
"sortable": true
|
|
17354
17354
|
}
|
|
17355
17355
|
}
|
|
17356
17356
|
]
|
|
@@ -17518,7 +17518,7 @@
|
|
|
17518
17518
|
50
|
|
17519
17519
|
],
|
|
17520
17520
|
"enableGlobalSearch": true,
|
|
17521
|
-
"create":
|
|
17521
|
+
"create": false,
|
|
17522
17522
|
"edit": true,
|
|
17523
17523
|
"editAction": "workflowExecution-detail-action",
|
|
17524
17524
|
"delete": true,
|
|
@@ -17539,8 +17539,10 @@
|
|
|
17539
17539
|
"type": "field",
|
|
17540
17540
|
"attrs": {
|
|
17541
17541
|
"name": "executionIdentifier",
|
|
17542
|
+
"className": "font-bold",
|
|
17542
17543
|
"sortable": true,
|
|
17543
|
-
"isSearchable": true
|
|
17544
|
+
"isSearchable": true,
|
|
17545
|
+
"truncateAfter": 28
|
|
17544
17546
|
}
|
|
17545
17547
|
},
|
|
17546
17548
|
{
|
|
@@ -17549,7 +17551,8 @@
|
|
|
17549
17551
|
"name": "workflowDefinition",
|
|
17550
17552
|
"sortable": true,
|
|
17551
17553
|
"isSearchable": true,
|
|
17552
|
-
"searchField": "workflowDefinition.displayName"
|
|
17554
|
+
"searchField": "workflowDefinition.displayName",
|
|
17555
|
+
"truncateAfter": 30
|
|
17553
17556
|
}
|
|
17554
17557
|
},
|
|
17555
17558
|
{
|
|
@@ -17558,7 +17561,8 @@
|
|
|
17558
17561
|
"name": "workflowDisplayName",
|
|
17559
17562
|
"className": "font-bold",
|
|
17560
17563
|
"sortable": true,
|
|
17561
|
-
"isSearchable": true
|
|
17564
|
+
"isSearchable": true,
|
|
17565
|
+
"truncateAfter": 32
|
|
17562
17566
|
}
|
|
17563
17567
|
},
|
|
17564
17568
|
{
|
|
@@ -17583,7 +17587,15 @@
|
|
|
17583
17587
|
"attrs": {
|
|
17584
17588
|
"name": "triggerType",
|
|
17585
17589
|
"sortable": true,
|
|
17586
|
-
"isSearchable": true
|
|
17590
|
+
"isSearchable": true,
|
|
17591
|
+
"viewWidget": "PillWidget",
|
|
17592
|
+
"pillColorMap": {
|
|
17593
|
+
"manual": "#ede9fe",
|
|
17594
|
+
"schedule": "#e0f2fe",
|
|
17595
|
+
"webhook": "#ccfbf1",
|
|
17596
|
+
"api": "#dbeafe",
|
|
17597
|
+
"default": "#e5e7eb"
|
|
17598
|
+
}
|
|
17587
17599
|
}
|
|
17588
17600
|
},
|
|
17589
17601
|
{
|
|
@@ -17611,7 +17623,8 @@
|
|
|
17611
17623
|
"type": "field",
|
|
17612
17624
|
"attrs": {
|
|
17613
17625
|
"name": "errorSummary",
|
|
17614
|
-
"isSearchable": true
|
|
17626
|
+
"isSearchable": true,
|
|
17627
|
+
"truncateAfter": 56
|
|
17615
17628
|
}
|
|
17616
17629
|
}
|
|
17617
17630
|
]
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { BadRequestException } from '@nestjs/common';
|
|
2
|
+
import { WorkflowNodeProvider } from '../../../decorators/workflow-node-provider.decorator';
|
|
3
|
+
import { WorkflowNodeHandler } from '../../../interfaces/workflow-node-handler.interface';
|
|
4
|
+
import {
|
|
5
|
+
WorkflowNodeExecutionContext,
|
|
6
|
+
WorkflowNodeHandlerResult,
|
|
7
|
+
} from '../../../types/workflow-dsl.types';
|
|
8
|
+
|
|
9
|
+
@WorkflowNodeProvider({
|
|
10
|
+
type: 'execution.fail',
|
|
11
|
+
kind: 'task',
|
|
12
|
+
version: '1.0.0',
|
|
13
|
+
category: 'core',
|
|
14
|
+
subcategory: 'execution',
|
|
15
|
+
label: 'Fail Execution',
|
|
16
|
+
description: 'Fails intentionally with a configured message.',
|
|
17
|
+
tags: ['failure', 'error', 'testing'],
|
|
18
|
+
aliases: ['fail'],
|
|
19
|
+
configSchema: {
|
|
20
|
+
type: 'object',
|
|
21
|
+
properties: {
|
|
22
|
+
message: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
default: 'Execution failed intentionally.',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
authoring: {
|
|
29
|
+
defaultConfiguration: {
|
|
30
|
+
message: 'Execution failed intentionally.',
|
|
31
|
+
},
|
|
32
|
+
configurationFields: [
|
|
33
|
+
{
|
|
34
|
+
key: 'message',
|
|
35
|
+
label: 'Message',
|
|
36
|
+
description: 'Failure message. Expressions are allowed.',
|
|
37
|
+
valueType: 'string',
|
|
38
|
+
expressionAllowed: true,
|
|
39
|
+
group: 'General',
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
supportsExpressions: true,
|
|
43
|
+
supportsDisableToggle: true,
|
|
44
|
+
supportsName: true,
|
|
45
|
+
supportsDescription: true,
|
|
46
|
+
searchableText: ['fail', 'failure', 'error', 'testing'],
|
|
47
|
+
},
|
|
48
|
+
runtime: {
|
|
49
|
+
emitsLogs: false,
|
|
50
|
+
emitsArtifacts: false,
|
|
51
|
+
deterministicOutputs: true,
|
|
52
|
+
executionMode: 'task',
|
|
53
|
+
successStatuses: [],
|
|
54
|
+
},
|
|
55
|
+
documentation: {
|
|
56
|
+
summary:
|
|
57
|
+
'Use this node in reference workflows and tests to intentionally exercise failure paths and error handlers.',
|
|
58
|
+
},
|
|
59
|
+
ui: {
|
|
60
|
+
icon: 'si-x-circle',
|
|
61
|
+
iconColor: '#dc2626',
|
|
62
|
+
iconBackgroundColor: '#fee2e2',
|
|
63
|
+
iconBorderColor: '#fecaca',
|
|
64
|
+
modalSize: 'lg',
|
|
65
|
+
layoutHints: {
|
|
66
|
+
groupOrder: ['General'],
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
export class ExecutionFailNode implements WorkflowNodeHandler {
|
|
71
|
+
async execute(
|
|
72
|
+
context: WorkflowNodeExecutionContext,
|
|
73
|
+
): Promise<WorkflowNodeHandlerResult> {
|
|
74
|
+
const configuration = context.expression.interpolate(
|
|
75
|
+
context.node.configuration ?? {},
|
|
76
|
+
context,
|
|
77
|
+
);
|
|
78
|
+
throw new BadRequestException(
|
|
79
|
+
configuration.message ?? 'Execution failed intentionally.',
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -16,6 +16,8 @@ import {
|
|
|
16
16
|
authoring: {
|
|
17
17
|
defaultConfiguration: {
|
|
18
18
|
items: '',
|
|
19
|
+
concurrencyLimit: 1,
|
|
20
|
+
aggregateOutputs: false,
|
|
19
21
|
},
|
|
20
22
|
configurationFields: [
|
|
21
23
|
{
|
|
@@ -32,6 +34,35 @@ import {
|
|
|
32
34
|
},
|
|
33
35
|
},
|
|
34
36
|
},
|
|
37
|
+
{
|
|
38
|
+
key: 'concurrencyLimit',
|
|
39
|
+
label: 'Concurrency Limit',
|
|
40
|
+
description:
|
|
41
|
+
'Maximum number of loop iterations to run at once. Use 1 for sequential execution and 0 for unlimited concurrency.',
|
|
42
|
+
valueType: 'number',
|
|
43
|
+
defaultValue: 1,
|
|
44
|
+
expressionAllowed: true,
|
|
45
|
+
group: 'General',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
key: 'aggregateOutputs',
|
|
49
|
+
label: 'Aggregate Outputs',
|
|
50
|
+
description:
|
|
51
|
+
'Adds per-iteration child outputs to this forEach node output for downstream expressions.',
|
|
52
|
+
valueType: 'boolean',
|
|
53
|
+
defaultValue: false,
|
|
54
|
+
expressionAllowed: true,
|
|
55
|
+
group: 'General',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
key: 'outputKey',
|
|
59
|
+
label: 'Output Key',
|
|
60
|
+
description:
|
|
61
|
+
'Optional expression used as the per-iteration key when aggregate outputs is enabled.',
|
|
62
|
+
valueType: 'string',
|
|
63
|
+
expressionAllowed: true,
|
|
64
|
+
group: 'General',
|
|
65
|
+
},
|
|
35
66
|
],
|
|
36
67
|
childSlots: [
|
|
37
68
|
{
|
|
@@ -42,6 +73,13 @@ import {
|
|
|
42
73
|
layout: 'sequential',
|
|
43
74
|
required: true,
|
|
44
75
|
},
|
|
76
|
+
{
|
|
77
|
+
key: 'errors',
|
|
78
|
+
label: 'Errors',
|
|
79
|
+
description: 'Nodes that run sequentially if this loop fails.',
|
|
80
|
+
kind: 'sequence',
|
|
81
|
+
layout: 'sequential',
|
|
82
|
+
},
|
|
45
83
|
],
|
|
46
84
|
supportsDescription: true,
|
|
47
85
|
supportsName: true,
|
|
@@ -66,32 +104,128 @@ export class ForEachNode implements WorkflowNodeHandler {
|
|
|
66
104
|
): Promise<WorkflowNodeHandlerResult> {
|
|
67
105
|
const configuration = context.node.configuration ?? {};
|
|
68
106
|
const items = context.expression.interpolate(configuration.items ?? [], context);
|
|
107
|
+
const concurrencyLimit = this.resolveConcurrencyLimit(
|
|
108
|
+
context.expression.interpolate(configuration.concurrencyLimit ?? 1, context),
|
|
109
|
+
);
|
|
110
|
+
const aggregateOutputs = Boolean(
|
|
111
|
+
context.expression.interpolate(configuration.aggregateOutputs ?? false, context),
|
|
112
|
+
);
|
|
69
113
|
|
|
70
114
|
if (!Array.isArray(items)) {
|
|
71
115
|
throw new BadRequestException('forEach requires configuration.items array.');
|
|
72
116
|
}
|
|
73
117
|
|
|
74
118
|
const childNodes = context.node.tasks ?? [];
|
|
119
|
+
const iterationsByIndex: Record<number, any> = {};
|
|
120
|
+
const iterationsByKey: Record<string, any> = {};
|
|
121
|
+
const shouldAggregateByKey =
|
|
122
|
+
aggregateOutputs && configuration.outputKey !== undefined && configuration.outputKey !== null;
|
|
75
123
|
|
|
76
|
-
|
|
124
|
+
await this.runIterations(items, concurrencyLimit, async (item, index) => {
|
|
77
125
|
const inheritedOutputs = context.outputs ?? {};
|
|
78
126
|
const iterationOutputs = { ...inheritedOutputs };
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
item
|
|
127
|
+
const parentLoops = context.loops ?? [];
|
|
128
|
+
const currentLoop = {
|
|
129
|
+
item,
|
|
130
|
+
index,
|
|
131
|
+
nodeId: context.node.id,
|
|
132
|
+
stepExecutionKey: context.stepExecution.stepExecutionKey,
|
|
133
|
+
};
|
|
134
|
+
const loopContext = {
|
|
135
|
+
item,
|
|
82
136
|
index,
|
|
137
|
+
parent: parentLoops[parentLoops.length - 1],
|
|
138
|
+
parents: [...parentLoops].reverse(),
|
|
139
|
+
loops: [...parentLoops, currentLoop],
|
|
140
|
+
};
|
|
141
|
+
const outputKey = shouldAggregateByKey
|
|
142
|
+
? String(context.expression.interpolate(configuration.outputKey, {
|
|
143
|
+
...context,
|
|
144
|
+
...loopContext,
|
|
145
|
+
outputs: iterationOutputs,
|
|
146
|
+
}))
|
|
147
|
+
: undefined;
|
|
148
|
+
|
|
149
|
+
const childOutputs = await context.runNodes(childNodes, {
|
|
150
|
+
...loopContext,
|
|
83
151
|
outputs: iterationOutputs,
|
|
84
152
|
});
|
|
85
|
-
|
|
153
|
+
|
|
154
|
+
if (aggregateOutputs) {
|
|
155
|
+
const iterationResult = {
|
|
156
|
+
item,
|
|
157
|
+
index,
|
|
158
|
+
outputs: childOutputs,
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
iterationsByIndex[index] = iterationResult;
|
|
162
|
+
if (outputKey !== undefined) {
|
|
163
|
+
iterationsByKey[outputKey] = iterationResult;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
});
|
|
86
167
|
|
|
87
168
|
return {
|
|
88
169
|
output: {
|
|
89
170
|
count: items.length,
|
|
90
171
|
childNodeIds: childNodes.map((childNode) => childNode.id),
|
|
172
|
+
concurrencyLimit,
|
|
91
173
|
// Keep the control-node output intentionally small. Per-iteration
|
|
92
174
|
// outputs are already stored on the child workflowStepExecution rows.
|
|
93
|
-
iterationOutputsStoredOnSteps:
|
|
175
|
+
iterationOutputsStoredOnSteps: !aggregateOutputs,
|
|
176
|
+
...(aggregateOutputs
|
|
177
|
+
? {
|
|
178
|
+
iterations: {
|
|
179
|
+
byIndex: iterationsByIndex,
|
|
180
|
+
...(shouldAggregateByKey
|
|
181
|
+
? {
|
|
182
|
+
byKey: iterationsByKey,
|
|
183
|
+
}
|
|
184
|
+
: {}),
|
|
185
|
+
},
|
|
186
|
+
}
|
|
187
|
+
: {}),
|
|
94
188
|
},
|
|
95
189
|
};
|
|
96
190
|
}
|
|
191
|
+
|
|
192
|
+
private resolveConcurrencyLimit(value: any): number {
|
|
193
|
+
const parsed = Number(value === '' || value === null || value === undefined ? 1 : value);
|
|
194
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
195
|
+
throw new BadRequestException(
|
|
196
|
+
'forEach configuration.concurrencyLimit must be 0 or a positive number.',
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return Math.floor(parsed);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private async runIterations<T>(
|
|
204
|
+
items: T[],
|
|
205
|
+
concurrencyLimit: number,
|
|
206
|
+
handler: (item: T, index: number) => Promise<void>,
|
|
207
|
+
): Promise<void> {
|
|
208
|
+
if (concurrencyLimit === 1 || items.length <= 1) {
|
|
209
|
+
for (let index = 0; index < items.length; index++) {
|
|
210
|
+
await handler(items[index], index);
|
|
211
|
+
}
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const limit = concurrencyLimit === 0 ? items.length : concurrencyLimit;
|
|
216
|
+
let nextIndex = 0;
|
|
217
|
+
|
|
218
|
+
const workers = Array.from(
|
|
219
|
+
{ length: Math.min(limit, items.length) },
|
|
220
|
+
async () => {
|
|
221
|
+
while (nextIndex < items.length) {
|
|
222
|
+
const index = nextIndex;
|
|
223
|
+
nextIndex += 1;
|
|
224
|
+
await handler(items[index], index);
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
await Promise.all(workers);
|
|
230
|
+
}
|
|
97
231
|
}
|
|
@@ -42,6 +42,13 @@ import {
|
|
|
42
42
|
description: 'Nodes that run when the condition evaluates to false.',
|
|
43
43
|
kind: 'sequence',
|
|
44
44
|
},
|
|
45
|
+
{
|
|
46
|
+
key: 'errors',
|
|
47
|
+
label: 'Errors',
|
|
48
|
+
description: 'Nodes that run sequentially if the selected branch fails.',
|
|
49
|
+
kind: 'sequence',
|
|
50
|
+
layout: 'sequential',
|
|
51
|
+
},
|
|
45
52
|
],
|
|
46
53
|
supportsDescription: true,
|
|
47
54
|
supportsName: true,
|
|
@@ -162,6 +162,7 @@ import {
|
|
|
162
162
|
description: 'The resolved log level.',
|
|
163
163
|
valueType: 'string',
|
|
164
164
|
path: 'level',
|
|
165
|
+
includeInRuntimeContext: false,
|
|
165
166
|
},
|
|
166
167
|
{
|
|
167
168
|
key: 'message',
|
|
@@ -169,6 +170,7 @@ import {
|
|
|
169
170
|
description: 'The rendered log message.',
|
|
170
171
|
valueType: 'string',
|
|
171
172
|
path: 'message',
|
|
173
|
+
includeInRuntimeContext: false,
|
|
172
174
|
},
|
|
173
175
|
],
|
|
174
176
|
supportsExpressions: true,
|