@acorex/platform 21.0.0-next.3 → 21.0.0-next.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/fesm2022/acorex-platform-auth.mjs +295 -45
  2. package/fesm2022/acorex-platform-auth.mjs.map +1 -1
  3. package/fesm2022/{acorex-platform-common-common-settings.provider-zhqNP3xb.mjs → acorex-platform-common-common-settings.provider-G9XcXXOG.mjs} +60 -4
  4. package/fesm2022/acorex-platform-common-common-settings.provider-G9XcXXOG.mjs.map +1 -0
  5. package/fesm2022/acorex-platform-common.mjs +960 -319
  6. package/fesm2022/acorex-platform-common.mjs.map +1 -1
  7. package/fesm2022/acorex-platform-core.mjs +1352 -832
  8. package/fesm2022/acorex-platform-core.mjs.map +1 -1
  9. package/fesm2022/acorex-platform-domain.mjs +554 -826
  10. package/fesm2022/acorex-platform-domain.mjs.map +1 -1
  11. package/fesm2022/acorex-platform-layout-builder.mjs +530 -154
  12. package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
  13. package/fesm2022/acorex-platform-layout-components-binding-expression-editor-popup.component-CXEdvDTf.mjs +121 -0
  14. package/fesm2022/acorex-platform-layout-components-binding-expression-editor-popup.component-CXEdvDTf.mjs.map +1 -0
  15. package/fesm2022/acorex-platform-layout-components.mjs +5969 -2347
  16. package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
  17. package/fesm2022/acorex-platform-layout-designer.mjs +169 -154
  18. package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -1
  19. package/fesm2022/acorex-platform-layout-entity.mjs +15380 -9274
  20. package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
  21. package/fesm2022/acorex-platform-layout-views.mjs +393 -110
  22. package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
  23. package/fesm2022/acorex-platform-layout-widget-core.mjs +511 -450
  24. package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -1
  25. package/fesm2022/{acorex-platform-layout-widgets-button-widget-designer.component-C3VoBb_b.mjs → acorex-platform-layout-widgets-button-widget-designer.component-Dy7jF-oD.mjs} +10 -10
  26. package/fesm2022/acorex-platform-layout-widgets-button-widget-designer.component-Dy7jF-oD.mjs.map +1 -0
  27. package/fesm2022/{acorex-platform-layout-widgets-file-list-popup.component-CxrsI6Hn.mjs → acorex-platform-layout-widgets-file-list-popup.component-9uCkMxcc.mjs} +39 -16
  28. package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-9uCkMxcc.mjs.map +1 -0
  29. package/fesm2022/{acorex-platform-layout-widgets-image-preview.popup-V31OpYah.mjs → acorex-platform-layout-widgets-image-preview.popup-C_EPAvCU.mjs} +6 -7
  30. package/fesm2022/acorex-platform-layout-widgets-image-preview.popup-C_EPAvCU.mjs.map +1 -0
  31. package/fesm2022/{acorex-platform-layout-widgets-page-widget-designer.component-BtZMBxYp.mjs → acorex-platform-layout-widgets-page-widget-designer.component-D10yO28c.mjs} +12 -12
  32. package/fesm2022/acorex-platform-layout-widgets-page-widget-designer.component-D10yO28c.mjs.map +1 -0
  33. package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-BGQqY5Mw.mjs +111 -0
  34. package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-BGQqY5Mw.mjs.map +1 -0
  35. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-edit-popup.component-Ck7-wpT2.mjs → acorex-platform-layout-widgets-tabular-data-edit-popup.component-DmzNTYiS.mjs} +6 -6
  36. package/fesm2022/acorex-platform-layout-widgets-tabular-data-edit-popup.component-DmzNTYiS.mjs.map +1 -0
  37. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-view-popup.component-y8vjUiVs.mjs → acorex-platform-layout-widgets-tabular-data-view-popup.component-BNG_588B.mjs} +5 -5
  38. package/fesm2022/acorex-platform-layout-widgets-tabular-data-view-popup.component-BNG_588B.mjs.map +1 -0
  39. package/fesm2022/{acorex-platform-layout-widgets-text-block-widget-designer.component-Df1BFkSa.mjs → acorex-platform-layout-widgets-text-block-widget-designer.component-Vo4fWHtX.mjs} +6 -6
  40. package/fesm2022/acorex-platform-layout-widgets-text-block-widget-designer.component-Vo4fWHtX.mjs.map +1 -0
  41. package/fesm2022/acorex-platform-layout-widgets.mjs +7865 -4026
  42. package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
  43. package/fesm2022/acorex-platform-native.mjs +8 -7
  44. package/fesm2022/acorex-platform-native.mjs.map +1 -1
  45. package/fesm2022/acorex-platform-runtime.mjs +220 -169
  46. package/fesm2022/acorex-platform-runtime.mjs.map +1 -1
  47. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-Cvvr4HnL.mjs +160 -0
  48. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-Cvvr4HnL.mjs.map +1 -0
  49. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-TYoLN1Jq.mjs +120 -0
  50. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-TYoLN1Jq.mjs.map +1 -0
  51. package/fesm2022/{acorex-platform-themes-default-entity-master-single-view.component-eMBby9k4.mjs → acorex-platform-themes-default-entity-master-single-view.component-C2z5Lq9y.mjs} +18 -25
  52. package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-C2z5Lq9y.mjs.map +1 -0
  53. package/fesm2022/{acorex-platform-themes-default-error-401.component-cfREo88K.mjs → acorex-platform-themes-default-error-401.component-C7EYJzSr.mjs} +4 -4
  54. package/fesm2022/acorex-platform-themes-default-error-401.component-C7EYJzSr.mjs.map +1 -0
  55. package/fesm2022/{acorex-platform-themes-default-error-404.component-CdCV5ZoA.mjs → acorex-platform-themes-default-error-404.component-7MVLMwIa.mjs} +4 -4
  56. package/fesm2022/acorex-platform-themes-default-error-404.component-7MVLMwIa.mjs.map +1 -0
  57. package/fesm2022/acorex-platform-themes-default-error-offline.component-DR6G8gPC.mjs +19 -0
  58. package/fesm2022/acorex-platform-themes-default-error-offline.component-DR6G8gPC.mjs.map +1 -0
  59. package/fesm2022/acorex-platform-themes-default.mjs +1717 -66
  60. package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
  61. package/fesm2022/{acorex-platform-themes-shared-icon-chooser-column.component-C0EpfU2k.mjs → acorex-platform-themes-shared-icon-chooser-column.component-CqkWJYdv.mjs} +6 -6
  62. package/fesm2022/acorex-platform-themes-shared-icon-chooser-column.component-CqkWJYdv.mjs.map +1 -0
  63. package/fesm2022/{acorex-platform-themes-shared-icon-chooser-view.component-9W52W6Nu.mjs → acorex-platform-themes-shared-icon-chooser-view.component-BOTuLdWN.mjs} +6 -6
  64. package/fesm2022/acorex-platform-themes-shared-icon-chooser-view.component-BOTuLdWN.mjs.map +1 -0
  65. package/fesm2022/acorex-platform-themes-shared-settings.provider-DSs1o1M6.mjs.map +1 -1
  66. package/fesm2022/{acorex-platform-themes-shared-theme-color-chooser-column.component-DTnfRy5f.mjs → acorex-platform-themes-shared-theme-color-chooser-column.component-CHfrTtol.mjs} +11 -11
  67. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-CHfrTtol.mjs.map +1 -0
  68. package/fesm2022/{acorex-platform-themes-shared-theme-color-chooser-view.component-DY0JtT1v.mjs → acorex-platform-themes-shared-theme-color-chooser-view.component-BSmvnUVq.mjs} +9 -9
  69. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-BSmvnUVq.mjs.map +1 -0
  70. package/fesm2022/acorex-platform-themes-shared.mjs +563 -561
  71. package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
  72. package/fesm2022/acorex-platform-workflow.mjs +1735 -1750
  73. package/fesm2022/acorex-platform-workflow.mjs.map +1 -1
  74. package/fesm2022/acorex-platform.mjs.map +1 -1
  75. package/package.json +31 -31
  76. package/{auth/index.d.ts → types/acorex-platform-auth.d.ts} +247 -10
  77. package/{common/index.d.ts → types/acorex-platform-common.d.ts} +492 -31
  78. package/{core/index.d.ts → types/acorex-platform-core.d.ts} +606 -392
  79. package/{domain/index.d.ts → types/acorex-platform-domain.d.ts} +719 -413
  80. package/{layout/builder/index.d.ts → types/acorex-platform-layout-builder.d.ts} +128 -56
  81. package/types/acorex-platform-layout-components.d.ts +2927 -0
  82. package/{layout/designer/index.d.ts → types/acorex-platform-layout-designer.d.ts} +9 -3
  83. package/{layout/entity/index.d.ts → types/acorex-platform-layout-entity.d.ts} +1133 -237
  84. package/{layout/views/index.d.ts → types/acorex-platform-layout-views.d.ts} +90 -31
  85. package/{layout/widget-core/index.d.ts → types/acorex-platform-layout-widget-core.d.ts} +206 -102
  86. package/{layout/widgets/index.d.ts → types/acorex-platform-layout-widgets.d.ts} +942 -137
  87. package/{native/index.d.ts → types/acorex-platform-native.d.ts} +0 -7
  88. package/{runtime/index.d.ts → types/acorex-platform-runtime.d.ts} +237 -74
  89. package/{themes/default/index.d.ts → types/acorex-platform-themes-default.d.ts} +113 -5
  90. package/{themes/shared/index.d.ts → types/acorex-platform-themes-shared.d.ts} +1 -1
  91. package/types/acorex-platform-workflow.d.ts +1806 -0
  92. package/fesm2022/acorex-platform-common-common-settings.provider-zhqNP3xb.mjs.map +0 -1
  93. package/fesm2022/acorex-platform-layout-widgets-button-widget-designer.component-C3VoBb_b.mjs.map +0 -1
  94. package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-CxrsI6Hn.mjs.map +0 -1
  95. package/fesm2022/acorex-platform-layout-widgets-image-preview.popup-V31OpYah.mjs.map +0 -1
  96. package/fesm2022/acorex-platform-layout-widgets-page-widget-designer.component-BtZMBxYp.mjs.map +0 -1
  97. package/fesm2022/acorex-platform-layout-widgets-tabular-data-edit-popup.component-Ck7-wpT2.mjs.map +0 -1
  98. package/fesm2022/acorex-platform-layout-widgets-tabular-data-view-popup.component-y8vjUiVs.mjs.map +0 -1
  99. package/fesm2022/acorex-platform-layout-widgets-text-block-widget-designer.component-Df1BFkSa.mjs.map +0 -1
  100. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-VIGuU5M4.mjs +0 -157
  101. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-VIGuU5M4.mjs.map +0 -1
  102. package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-DyDa_hyd.mjs +0 -1542
  103. package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-DyDa_hyd.mjs.map +0 -1
  104. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-Ua3ZA5hk.mjs +0 -101
  105. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-Ua3ZA5hk.mjs.map +0 -1
  106. package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-eMBby9k4.mjs.map +0 -1
  107. package/fesm2022/acorex-platform-themes-default-error-401.component-cfREo88K.mjs.map +0 -1
  108. package/fesm2022/acorex-platform-themes-default-error-404.component-CdCV5ZoA.mjs.map +0 -1
  109. package/fesm2022/acorex-platform-themes-default-error-offline.component-E7SzBcAt.mjs +0 -19
  110. package/fesm2022/acorex-platform-themes-default-error-offline.component-E7SzBcAt.mjs.map +0 -1
  111. package/fesm2022/acorex-platform-themes-shared-icon-chooser-column.component-C0EpfU2k.mjs.map +0 -1
  112. package/fesm2022/acorex-platform-themes-shared-icon-chooser-view.component-9W52W6Nu.mjs.map +0 -1
  113. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-DTnfRy5f.mjs.map +0 -1
  114. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-DY0JtT1v.mjs.map +0 -1
  115. package/layout/components/index.d.ts +0 -1669
  116. package/workflow/index.d.ts +0 -2443
  117. /package/{index.d.ts → types/acorex-platform.d.ts} +0 -0
@@ -1,14 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, inject, InjectionToken, Optional, SkipSelf, Inject, NgModule } from '@angular/core';
2
+ import { Injectable, inject, InjectionToken, Optional, Inject, NgModule } from '@angular/core';
3
3
  import { Subject, filter } from 'rxjs';
4
4
  import { cloneDeep, get, set } from 'lodash-es';
5
- import { setSmart } from '@acorex/platform/core';
6
- import { AXPCommandService, AXPCommandRegistry, AXPQueryService, provideCommandSetups, AXP_COMMAND_SETUP } from '@acorex/platform/runtime';
7
- import { AXDialogService } from '@acorex/components/dialog';
8
- import { AXTranslationService } from '@acorex/core/translation';
9
- import { AXPLayoutBuilderService } from '@acorex/platform/layout/builder';
10
- import { AXToastService } from '@acorex/components/toast';
11
- import { Router } from '@angular/router';
5
+ import { setSmart, AXPExpressionEvaluatorService, AXPDataGenerator } from '@acorex/platform/core';
6
+ import { AXPCommandService } from '@acorex/platform/runtime';
12
7
 
13
8
  class AXPWorkflowError extends Error {
14
9
  constructor(message, inner = null) {
@@ -28,10 +23,10 @@ class AXPWorkflowEventService {
28
23
  get events$() {
29
24
  return this.eventSubject.asObservable();
30
25
  }
31
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowEventService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
32
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowEventService, providedIn: 'root' }); }
26
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowEventService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
27
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowEventService, providedIn: 'root' }); }
33
28
  }
34
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowEventService, decorators: [{
29
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowEventService, decorators: [{
35
30
  type: Injectable,
36
31
  args: [{
37
32
  providedIn: 'root',
@@ -121,10 +116,10 @@ class AXPWorkflowRegistryService {
121
116
  });
122
117
  }
123
118
  }
124
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
125
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowRegistryService, providedIn: 'root' }); }
119
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
120
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowRegistryService, providedIn: 'root' }); }
126
121
  }
127
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowRegistryService, decorators: [{
122
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowRegistryService, decorators: [{
128
123
  type: Injectable,
129
124
  args: [{
130
125
  providedIn: 'root',
@@ -163,17 +158,17 @@ class AXPWorkflowAction {
163
158
  dispatch(event) {
164
159
  this.eventService.dispatch(event);
165
160
  }
166
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowAction, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
167
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowAction }); }
161
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowAction, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
162
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowAction }); }
168
163
  }
169
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowAction, decorators: [{
164
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowAction, decorators: [{
170
165
  type: Injectable
171
166
  }] });
172
167
  class AXPWorkflowFunction {
173
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowFunction, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
174
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowFunction }); }
168
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowFunction, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
169
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowFunction }); }
175
170
  }
176
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowFunction, decorators: [{
171
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowFunction, decorators: [{
177
172
  type: Injectable
178
173
  }] });
179
174
  function createWorkFlowEvent(type) {
@@ -189,10 +184,10 @@ class AXPWorkflowDecideAction extends AXPWorkflowAction {
189
184
  async execute(context) {
190
185
  // its a fake action
191
186
  }
192
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowDecideAction, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
193
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowDecideAction, providedIn: 'root' }); }
187
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowDecideAction, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
188
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowDecideAction, providedIn: 'root' }); }
194
189
  }
195
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowDecideAction, decorators: [{
190
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowDecideAction, decorators: [{
196
191
  type: Injectable,
197
192
  args: [{
198
193
  providedIn: 'root',
@@ -378,10 +373,10 @@ class AXPWorkflowService {
378
373
  }
379
374
  return this.injector.get(functionType);
380
375
  }
381
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowService, deps: [{ token: AXPWorkflowRegistryService }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable }); }
382
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowService, providedIn: 'root' }); }
376
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowService, deps: [{ token: AXPWorkflowRegistryService }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable }); }
377
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowService, providedIn: 'root' }); }
383
378
  }
384
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowService, decorators: [{
379
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowService, decorators: [{
385
380
  type: Injectable,
386
381
  args: [{
387
382
  providedIn: 'root',
@@ -406,711 +401,769 @@ class AXPStartWorkflowAction extends AXPWorkflowAction {
406
401
  throw e;
407
402
  }
408
403
  }
409
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPStartWorkflowAction, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
410
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPStartWorkflowAction, providedIn: 'root' }); }
404
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPStartWorkflowAction, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
405
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPStartWorkflowAction, providedIn: 'root' }); }
411
406
  }
412
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPStartWorkflowAction, decorators: [{
407
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPStartWorkflowAction, decorators: [{
413
408
  type: Injectable,
414
409
  args: [{
415
410
  providedIn: 'root',
416
411
  }]
417
412
  }] });
418
413
 
414
+ // ============================================
415
+ // WORKFLOW INSTANCE v3.0.0 TYPES
416
+ // Based on Elsa Workflow Instance schema: https://elsaworkflows.io/schemas/workflow-instance/v3.0.0/schema.json
417
+ // Compatible with Elsa backend while using ACoreX naming conventions
418
+ // ============================================
419
+
420
+ // Note:
421
+ // Previous versions defined dedicated activity result/command types here
422
+ // (AXPActivityResult / AXPActivity) that wrapped output and outcome.
423
+ // Activities are now modeled directly as AXPCommand with outcome stored in
424
+ // AXPExecuteCommandResult.metadata.outcome.
425
+
426
+ const AXP_ACTIVITY_PROVIDER = new InjectionToken('AXP_ACTIVITY_PROVIDER', {
427
+ factory: () => [],
428
+ });
429
+ const AXP_ACTIVITY_CATEGORY_PROVIDER = new InjectionToken('AXP_ACTIVITY_CATEGORY_PROVIDER', {
430
+ factory: () => [],
431
+ });
432
+
433
+ //#region ---- Imports ----
434
+ //#endregion
419
435
  /**
420
- * Injection token for workflow definition loaders.
421
- * Multiple loaders can be provided (multi: true).
422
- */
423
- const AXP_WORKFLOW_DEFINITION_LOADER = new InjectionToken('AXP_WORKFLOW_DEFINITION_LOADER');
424
- /**
425
- * Resolver service for workflow definitions.
426
- * Aggregates all registered loaders and resolves workflow definitions.
436
+ * Optimized Activity Definition Service
437
+ *
438
+ * Manages activity definitions (metadata) for UI and tooling.
439
+ * Similar to AXPReportDefinitionService - only handles metadata, not execution.
440
+ *
441
+ * Performance optimizations:
442
+ * 1. Uses childrenCount to determine if category has children (no query needed)
443
+ * 2. Uses itemsCount to determine if category has activities (no query needed)
444
+ * 3. Aggressive caching prevents duplicate API calls
445
+ * 4. Single pending request per resource prevents race conditions
446
+ * 5. Lazy loading - only loads data when needed
427
447
  */
428
- class AXPWorkflowDefinitionResolver {
448
+ class AXPActivityDefinitionService {
429
449
  constructor() {
430
- this.loaders = inject(AXP_WORKFLOW_DEFINITION_LOADER, { optional: true });
450
+ //#region ---- Providers & Caches ----
451
+ this.categoryProviders = inject(AXP_ACTIVITY_CATEGORY_PROVIDER, { optional: true }) || [];
452
+ this.activityProviders = inject(AXP_ACTIVITY_PROVIDER, { optional: true }) || [];
453
+ //#endregion
454
+ //#region ---- Cache Storage ----
455
+ /** Cache for categories by id - O(1) lookup */
456
+ this.categoriesById = new Map();
457
+ /** Cache for categories by parentId - O(1) lookup */
458
+ this.categoriesByParentId = new Map();
459
+ /** Cache for activity definitions by categoryId - O(1) lookup */
460
+ this.activitiesByCategory = new Map();
461
+ /** Cache for individual activity definitions by name - O(1) lookup */
462
+ this.activitiesByName = new Map();
463
+ /** Track which provider index owns each category (by category ID) */
464
+ this.categoryOwnership = new Map(); // Maps categoryId → provider index
465
+ /** Pending API requests to prevent duplicate calls */
466
+ this.pendingCategoriesRequests = new Map();
467
+ this.pendingActivitiesRequests = new Map();
468
+ this.pendingActivityRequests = new Map();
431
469
  }
470
+ //#endregion
471
+ //#region ---- Initialization ----
472
+ //#endregion
473
+ //#region ---- Public API: Categories ----
432
474
  /**
433
- * Get workflow definition by name (unique key).
434
- * Tries all registered loaders until one returns a definition.
435
- * @param name - The workflow name (unique key)
436
- * @returns Workflow definition or null if not found
475
+ * Get categories by parentId with aggressive caching
476
+ *
477
+ * Optimization: Returns cached result immediately if available,
478
+ * preventing unnecessary API calls during navigation
479
+ *
480
+ * @param parentId - Parent category ID (undefined = root categories)
481
+ * @returns Array of categories with count metadata (childrenCount, itemsCount)
437
482
  */
438
- async get(name) {
439
- const loaderArray = Array.isArray(this.loaders) ? this.loaders : this.loaders ? [this.loaders] : [];
440
- for (const loader of loaderArray) {
441
- try {
442
- const definition = await loader.get(name);
443
- if (definition) {
444
- return definition;
445
- }
483
+ async getCategories(parentId) {
484
+ // Fast path: Return cached result
485
+ if (this.categoriesByParentId.has(parentId)) {
486
+ return this.categoriesByParentId.get(parentId);
487
+ }
488
+ // Prevent duplicate requests: Return pending promise
489
+ if (this.pendingCategoriesRequests.has(parentId)) {
490
+ return this.pendingCategoriesRequests.get(parentId);
491
+ }
492
+ // ✅ Create single request and cache it
493
+ const requestPromise = this.loadCategoriesFromProviders(parentId);
494
+ this.pendingCategoriesRequests.set(parentId, requestPromise);
495
+ return requestPromise;
496
+ }
497
+ /**
498
+ * Get single category by ID with O(1) lookup
499
+ *
500
+ * Optimization: Uses Map for instant retrieval, falls back to
501
+ * searching cache, then providers if not found
502
+ */
503
+ async getCategoryById(categoryId) {
504
+ // ✅ Fast path: O(1) lookup in cache
505
+ if (this.categoriesById.has(categoryId)) {
506
+ return this.categoriesById.get(categoryId);
507
+ }
508
+ // ✅ Search in cached parent-child lists
509
+ for (const categories of this.categoriesByParentId.values()) {
510
+ const found = categories.find(cat => cat.id === categoryId);
511
+ if (found) {
512
+ this.categoriesById.set(categoryId, found);
513
+ return found;
446
514
  }
447
- catch (error) {
448
- console.warn(`[AXPWorkflowDefinitionResolver] Loader failed for ${name}:`, error);
515
+ }
516
+ // Load root categories if not loaded
517
+ if (!this.categoriesByParentId.has(undefined)) {
518
+ await this.getCategories();
519
+ if (this.categoriesById.has(categoryId)) {
520
+ return this.categoriesById.get(categoryId);
449
521
  }
450
522
  }
451
- return null;
523
+ // ✅ Breadth-first search through hierarchy
524
+ return this.searchCategoryInHierarchy(categoryId);
452
525
  }
453
526
  /**
454
- * Get all available workflow names from all loaders.
455
- * @returns Array of unique workflow names
527
+ * Get category path from root to specified category
528
+ *
529
+ * Optimization: Builds path using cached categories only
456
530
  */
457
- async getAllNames() {
458
- const loaderArray = Array.isArray(this.loaders) ? this.loaders : this.loaders ? [this.loaders] : [];
459
- const allNames = new Set();
460
- for (const loader of loaderArray) {
461
- if (loader.getAllNames) {
462
- try {
463
- const names = await loader.getAllNames();
464
- names.forEach((name) => allNames.add(name));
465
- }
466
- catch (error) {
467
- console.warn('[AXPWorkflowDefinitionResolver] Failed to get names from loader:', error);
468
- }
531
+ async getCategoriesPathById(categoryId) {
532
+ const path = [];
533
+ let currentCategoryId = categoryId;
534
+ while (currentCategoryId) {
535
+ const category = await this.getCategoryById(currentCategoryId);
536
+ if (!category) {
537
+ throw new Error(`Category '${currentCategoryId}' not found`);
469
538
  }
539
+ path.unshift(category);
540
+ currentCategoryId = category.parentId;
470
541
  }
471
- return Array.from(allNames);
472
- }
473
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowDefinitionResolver, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
474
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowDefinitionResolver, providedIn: 'root' }); }
475
- }
476
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowDefinitionResolver, decorators: [{
477
- type: Injectable,
478
- args: [{ providedIn: 'root' }]
479
- }] });
480
-
481
- /**
482
- * Registry service for workflow definitions.
483
- * Caches loaded definitions and provides change notifications.
484
- */
485
- class AXPWorkflowDefinitionRegistryService {
486
- constructor() {
487
- this.resolver = inject(AXPWorkflowDefinitionResolver);
488
- this.cache = new Map();
489
- this.onChanged = new Subject();
542
+ return path;
490
543
  }
544
+ //#endregion
545
+ //#region ---- Public API: Activity Definitions ----
491
546
  /**
492
- * Observable for workflow definition changes.
547
+ * Get activity definitions for a category with smart caching
548
+ *
549
+ * Optimization: Checks itemsCount before querying
550
+ * - If itemsCount = 0, returns empty array (no API call)
551
+ * - If itemsCount > 0, loads and caches activity definitions
552
+ * - Returns cached result on subsequent calls
553
+ *
554
+ * @param categoryId - Category ID to get activity definitions from
555
+ * @returns Array of activity definitions
493
556
  */
494
- get onChanged$() {
495
- return this.onChanged.asObservable();
557
+ async getActivitiesByCategoryId(categoryId) {
558
+ // ✅ Fast path: Return cached result
559
+ if (this.activitiesByCategory.has(categoryId)) {
560
+ return this.activitiesByCategory.get(categoryId);
561
+ }
562
+ // ✅ Smart optimization: Check itemsCount before querying
563
+ const category = await this.getCategoryById(categoryId);
564
+ if (category && category.itemsCount !== undefined && category.itemsCount === 0) {
565
+ // Category has no activities - cache empty array and skip API call
566
+ const emptyArray = [];
567
+ this.activitiesByCategory.set(categoryId, emptyArray);
568
+ return emptyArray;
569
+ }
570
+ // ✅ Prevent duplicate requests
571
+ if (this.pendingActivitiesRequests.has(categoryId)) {
572
+ return this.pendingActivitiesRequests.get(categoryId);
573
+ }
574
+ // ✅ Load from providers
575
+ const requestPromise = this.loadActivitiesFromProviders(categoryId);
576
+ this.pendingActivitiesRequests.set(categoryId, requestPromise);
577
+ return requestPromise;
496
578
  }
497
579
  /**
498
- * Get workflow definition by name (unique key).
499
- * Uses cache if available, otherwise loads from resolver.
500
- * @param name - The workflow name (unique key)
501
- * @returns Workflow definition or null if not found
580
+ * Get single activity definition by name with O(1) lookup
581
+ *
582
+ * Optimization: Uses Map for instant retrieval
583
+ *
584
+ * @param name - Activity name (unique identifier and command key)
585
+ * @returns Activity definition or undefined if not found
502
586
  */
503
- async get(name) {
504
- // Check cache first
505
- if (this.cache.has(name)) {
506
- return this.cache.get(name);
587
+ async getActivityByName(name) {
588
+ // Fast path: O(1) lookup in cache
589
+ if (this.activitiesByName.has(name)) {
590
+ return this.activitiesByName.get(name);
507
591
  }
508
- // Load from resolver
509
- const definition = await this.resolver.get(name);
510
- if (definition) {
511
- this.cache.set(name, definition);
512
- this.onChanged.next({ name, action: 'registered' });
592
+ // Prevent duplicate requests
593
+ if (this.pendingActivityRequests.has(name)) {
594
+ return this.pendingActivityRequests.get(name);
513
595
  }
514
- return definition;
596
+ // ✅ Load from providers
597
+ const requestPromise = this.loadActivityFromProviders(name);
598
+ this.pendingActivityRequests.set(name, requestPromise);
599
+ return requestPromise;
515
600
  }
516
601
  /**
517
- * Register a workflow definition in the cache.
518
- * @param definition - The workflow definition to register
602
+ * Get all activity definitions (flat list) by loading root categories and their activities.
603
+ * Used by activity selector UIs (e.g. automation command configurator).
519
604
  */
520
- register(definition) {
521
- this.cache.set(definition.name, definition);
522
- this.onChanged.next({ name: definition.name, action: 'registered' });
605
+ async getAllActivities() {
606
+ const categories = await this.getCategories(undefined);
607
+ const all = [];
608
+ for (const cat of categories) {
609
+ const activities = await this.getActivitiesByCategoryId(cat.id);
610
+ all.push(...activities);
611
+ }
612
+ return all;
523
613
  }
524
614
  /**
525
- * Update a workflow definition in the cache.
526
- * @param definition - The updated workflow definition
615
+ * Get category ID containing a specific activity definition
616
+ *
617
+ * Optimization: Searches cache first, loads on-demand if needed
527
618
  */
528
- update(definition) {
529
- if (this.cache.has(definition.name)) {
530
- this.cache.set(definition.name, definition);
531
- this.onChanged.next({ name: definition.name, action: 'updated' });
619
+ async getCategoryIdByActivityName(activityName) {
620
+ // Search in cached activity definitions
621
+ for (const [categoryId, definitions] of this.activitiesByCategory.entries()) {
622
+ if (definitions.some(def => def.name === activityName)) {
623
+ return categoryId;
624
+ }
532
625
  }
626
+ // ✅ Try loading the activity definition to find its category
627
+ const definition = await this.getActivityByName(activityName);
628
+ if (definition && definition.category) {
629
+ // Try to find category by name/id
630
+ const categories = await this.getCategories();
631
+ const found = categories.find(cat => cat.id === definition.category || cat.title === definition.category);
632
+ if (found) {
633
+ return found.id;
634
+ }
635
+ }
636
+ return undefined;
533
637
  }
534
638
  /**
535
- * Remove a workflow definition from the cache.
536
- * @param name - The workflow name to remove
639
+ * Get category path for an activity
537
640
  */
538
- remove(name) {
539
- if (this.cache.has(name)) {
540
- this.cache.delete(name);
541
- this.onChanged.next({ name, action: 'removed' });
641
+ async getCategoriesPathByActivityName(activityName) {
642
+ const categoryId = await this.getCategoryIdByActivityName(activityName);
643
+ if (!categoryId) {
644
+ throw new Error(`Activity '${activityName}' not found in any category`);
542
645
  }
646
+ return this.getCategoriesPathById(categoryId);
543
647
  }
648
+ //#endregion
649
+ //#region ---- Private: Data Loading ----
544
650
  /**
545
- * Clear all cached workflow definitions.
651
+ * Load categories from providers and cache results
652
+ *
653
+ * Optimization: Tracks provider ownership to avoid unnecessary API calls
654
+ * - For root (parentId = undefined): Query ALL providers
655
+ * - For children: Only query the provider that owns the parent
546
656
  */
547
- clear() {
548
- this.cache.clear();
657
+ async loadCategoriesFromProviders(parentId) {
658
+ try {
659
+ const resolvedProviders = await Promise.allSettled(this.categoryProviders);
660
+ const categories = [];
661
+ // Determine which provider(s) to query
662
+ const providerIndicesToQuery = parentId
663
+ ? this.getProviderIndexForCategory(parentId)
664
+ : null; // Root: query all providers
665
+ for (let i = 0; i < resolvedProviders.length; i++) {
666
+ const p = resolvedProviders[i];
667
+ // Skip if we have a specific provider index and this isn't it
668
+ if (providerIndicesToQuery !== null && !providerIndicesToQuery.includes(i)) {
669
+ continue;
670
+ }
671
+ if (p.status === 'fulfilled' && p.value && typeof p.value.getList === 'function') {
672
+ try {
673
+ const cats = await p.value.getList(parentId);
674
+ if (Array.isArray(cats) && cats.length > 0) {
675
+ categories.push(...cats);
676
+ // ✅ Track ownership: This provider INDEX owns these categories
677
+ cats.forEach(cat => this.categoryOwnership.set(cat.id, i));
678
+ }
679
+ }
680
+ catch {
681
+ // Continue on error - try other providers
682
+ }
683
+ }
684
+ }
685
+ // ✅ Cache results for fast subsequent access
686
+ this.categoriesByParentId.set(parentId, categories);
687
+ categories.forEach(cat => this.categoriesById.set(cat.id, cat));
688
+ return categories;
689
+ }
690
+ finally {
691
+ this.pendingCategoriesRequests.delete(parentId);
692
+ }
549
693
  }
550
694
  /**
551
- * Check if a workflow definition is cached.
552
- * @param definitionId - The workflow definition ID
553
- * @returns True if cached, false otherwise
695
+ * Get the provider index that owns a specific category
696
+ *
697
+ * @returns Array with provider index, or null if ownership unknown (query all)
554
698
  */
555
- has(definitionId) {
556
- return this.cache.has(definitionId);
699
+ getProviderIndexForCategory(categoryId) {
700
+ const ownerIndex = this.categoryOwnership.get(categoryId);
701
+ if (ownerIndex !== undefined) {
702
+ return [ownerIndex];
703
+ }
704
+ // Ownership unknown - will query all providers (fallback)
705
+ return null;
557
706
  }
558
707
  /**
559
- * Get all cached workflow definition IDs.
560
- * @returns Array of definition IDs (only those that have been loaded)
708
+ * Load activity definitions from providers and cache results
709
+ *
710
+ * Optimization: Only queries the provider that owns the category
711
+ * Uses provider INDEX to match category provider with activity provider
561
712
  */
562
- getAllIds() {
563
- return Array.from(this.cache.keys());
564
- }
565
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowDefinitionRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
566
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowDefinitionRegistryService, providedIn: 'root' }); }
567
- }
568
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowDefinitionRegistryService, decorators: [{
569
- type: Injectable,
570
- args: [{
571
- providedIn: 'root',
572
- }]
573
- }] });
574
-
575
- // ============================================
576
- // WORKFLOW INSTANCE v3.0.0 TYPES
577
- // Based on Elsa Workflow Instance schema: https://elsaworkflows.io/schemas/workflow-instance/v3.0.0/schema.json
578
- // Compatible with Elsa backend while using ACoreX naming conventions
579
- // ============================================
580
-
581
- /**
582
- * Base abstract class for activities.
583
- * Extend this to create custom activities.
584
- */
585
- class Activity {
586
- constructor(type, name) {
587
- this.type = type;
588
- this.name = name;
713
+ async loadActivitiesFromProviders(categoryId) {
714
+ try {
715
+ const resolvedProviders = await Promise.allSettled(this.activityProviders);
716
+ const definitions = [];
717
+ // Smart routing: Get provider INDEX that owns this category
718
+ const ownerIndex = this.categoryOwnership.get(categoryId);
719
+ const providerIndicesToQuery = ownerIndex !== undefined ? [ownerIndex] : null;
720
+ for (let i = 0; i < resolvedProviders.length; i++) {
721
+ const p = resolvedProviders[i];
722
+ // Skip if we have a specific provider index and this isn't it
723
+ if (providerIndicesToQuery !== null && !providerIndicesToQuery.includes(i)) {
724
+ continue;
725
+ }
726
+ if (p.status === 'fulfilled' && p.value && typeof p.value.getList === 'function') {
727
+ try {
728
+ const defs = await p.value.getList(categoryId);
729
+ if (Array.isArray(defs)) {
730
+ definitions.push(...defs);
731
+ }
732
+ }
733
+ catch {
734
+ // Continue on error - try other providers
735
+ }
736
+ }
737
+ }
738
+ // Cache results for fast subsequent access
739
+ this.activitiesByCategory.set(categoryId, definitions);
740
+ definitions.forEach(def => {
741
+ if (def.name) {
742
+ this.activitiesByName.set(def.name, def);
743
+ }
744
+ });
745
+ return definitions;
746
+ }
747
+ finally {
748
+ this.pendingActivitiesRequests.delete(categoryId);
749
+ }
589
750
  }
590
751
  /**
591
- * Helper method that returns Done outcome by default.
752
+ * Load single activity definition from providers and cache result
592
753
  */
593
- createResult(output, outcome = 'Done') {
594
- return {
595
- success: true,
596
- data: {
597
- output,
598
- outcomes: { [outcome]: true },
599
- },
600
- };
601
- }
602
- }
603
- /**
604
- * Activity registry for registering and creating activities.
605
- */
606
- class ActivityRegistry {
607
- constructor() {
608
- this.registry = new Map();
609
- this.descriptors = new Map();
754
+ async loadActivityFromProviders(name) {
755
+ try {
756
+ const resolvedProviders = await Promise.allSettled(this.activityProviders);
757
+ // Try providers first
758
+ for (const p of resolvedProviders) {
759
+ if (p.status === 'fulfilled' && p.value && typeof p.value.getById === 'function') {
760
+ try {
761
+ const result = await p.value.getById(name);
762
+ if (result) {
763
+ this.activitiesByName.set(name, result);
764
+ return result;
765
+ }
766
+ }
767
+ catch {
768
+ // Continue on error
769
+ }
770
+ }
771
+ }
772
+ // Fallback: Search in cached activity definitions
773
+ for (const definitions of this.activitiesByCategory.values()) {
774
+ const found = definitions.find(def => def.name === name);
775
+ if (found) {
776
+ this.activitiesByName.set(name, found);
777
+ return found;
778
+ }
779
+ }
780
+ return undefined;
781
+ }
782
+ finally {
783
+ this.pendingActivityRequests.delete(name);
784
+ }
610
785
  }
611
786
  /**
612
- * Register an activity type.
787
+ * Breadth-first search through category hierarchy
613
788
  */
614
- register(type, factory, descriptor) {
615
- this.registry.set(type, factory);
616
- this.descriptors.set(type, descriptor);
789
+ async searchCategoryInHierarchy(categoryId) {
790
+ const searchQueue = [undefined];
791
+ const searched = new Set();
792
+ while (searchQueue.length > 0) {
793
+ const parentId = searchQueue.shift();
794
+ if (searched.has(parentId))
795
+ continue;
796
+ searched.add(parentId);
797
+ const categories = await this.getCategories(parentId);
798
+ const found = categories.find(cat => cat.id === categoryId);
799
+ if (found) {
800
+ return found;
801
+ }
802
+ // ✅ Optimization: Only search children if childrenCount > 0
803
+ for (const category of categories) {
804
+ if (category.childrenCount > 0 && !searched.has(category.id)) {
805
+ searchQueue.push(category.id);
806
+ }
807
+ }
808
+ }
809
+ return undefined;
617
810
  }
811
+ //#endregion
812
+ //#region ---- Cache Management ----
618
813
  /**
619
- * Create an activity instance.
814
+ * Check if category has children (uses cached count)
620
815
  */
621
- create(type) {
622
- const factory = this.registry.get(type);
623
- if (!factory) {
624
- throw new Error(`Unknown activity type: ${type}`);
625
- }
626
- return factory();
816
+ categoryHasChildren(categoryId) {
817
+ const category = this.categoriesById.get(categoryId);
818
+ return category ? category.childrenCount > 0 : false;
627
819
  }
628
820
  /**
629
- * Get activity descriptor.
821
+ * Check if category has activities (uses cached count)
630
822
  */
631
- getDescriptor(type) {
632
- return this.descriptors.get(type);
823
+ categoryHasActivities(categoryId) {
824
+ const category = this.categoriesById.get(categoryId);
825
+ return category ? (category.itemsCount ?? 0) > 0 : false;
633
826
  }
634
827
  /**
635
- * Get all registered types.
828
+ * Clear all caches
636
829
  */
637
- getTypes() {
638
- return Array.from(this.registry.keys());
830
+ clearAllCache() {
831
+ this.categoriesById.clear();
832
+ this.categoriesByParentId.clear();
833
+ this.activitiesByCategory.clear();
834
+ this.activitiesByName.clear();
835
+ this.categoryOwnership.clear();
836
+ this.pendingCategoriesRequests.clear();
837
+ this.pendingActivitiesRequests.clear();
838
+ this.pendingActivityRequests.clear();
639
839
  }
640
840
  /**
641
- * Get all descriptors.
841
+ * Clear categories cache only
642
842
  */
643
- getAllDescriptors() {
644
- return Array.from(this.descriptors.values());
843
+ clearCategoriesCache() {
844
+ this.categoriesById.clear();
845
+ this.categoriesByParentId.clear();
846
+ this.categoryOwnership.clear();
847
+ this.pendingCategoriesRequests.clear();
645
848
  }
646
849
  /**
647
- * Get descriptors by category.
850
+ * Clear activities cache only
648
851
  */
649
- getDescriptorsByCategory(category) {
650
- return this.getAllDescriptors().filter(d => d.category === category);
651
- }
852
+ clearActivitiesCache() {
853
+ this.activitiesByCategory.clear();
854
+ this.activitiesByName.clear();
855
+ this.pendingActivitiesRequests.clear();
856
+ this.pendingActivityRequests.clear();
857
+ }
858
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPActivityDefinitionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
859
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPActivityDefinitionService, providedIn: 'root' }); }
652
860
  }
861
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPActivityDefinitionService, decorators: [{
862
+ type: Injectable,
863
+ args: [{
864
+ providedIn: 'root',
865
+ }]
866
+ }] });
653
867
 
654
868
  /**
655
- * Injection token for activity providers.
869
+ * Injection token for workflow engine.
870
+ * Default implementation is AXPWorkflowLocalEngine.
656
871
  */
657
- const AXP_ACTIVITY_PROVIDER = new InjectionToken('AXP_ACTIVITY_PROVIDER');
872
+ const AXP_WORKFLOW_ENGINE = new InjectionToken('AXP_WORKFLOW_ENGINE');
873
+
874
+ //#endregion
658
875
  /**
659
- * Activity Provider Service.
660
- * Collects all activity providers and manages activity registration.
876
+ * Workflow Expression Scope Service
877
+ *
878
+ * Shared service for building expression evaluation scope from workflow data.
879
+ * Used by both Local Engine and Mock Runtime to manage workflow data (inputs, variables, outputs).
880
+ *
881
+ * Responsibilities:
882
+ * - Build expression evaluation scope from workflow state
883
+ * - Provide context.eval() function for accessing workflow data
884
+ * - Manage workflow data structure (inputs, variables, outputs)
885
+ *
886
+ * This service does NOT:
887
+ * - Execute activities
888
+ * - Evaluate expressions (delegates to AXPExpressionEvaluatorService)
889
+ * - Manage workflow state (handled by runtime)
890
+ *
891
+ * @example
892
+ * ```typescript
893
+ * const scopeService = inject(WorkflowExpressionScopeService);
894
+ *
895
+ * // Build scope from workflow state
896
+ * const scope = scopeService.buildScope({
897
+ * inputs: state.input || {},
898
+ * variables: state.variables || {},
899
+ * outputs: activityOutputs
900
+ * });
901
+ *
902
+ * // Or build from state directly
903
+ * const scope = scopeService.buildScopeFromState(state, activityOutputs);
904
+ * ```
661
905
  */
662
- class AXPActivityProviderService {
663
- constructor() {
664
- this.activityDescriptors = new Map();
665
- this.categories = [];
666
- this.providers = [];
667
- this.categoryProviders = [];
668
- this.initialized = false;
669
- this.commandService = inject(AXPCommandService);
670
- this.commandRegistry = inject(AXPCommandRegistry);
671
- }
906
+ class WorkflowExpressionScopeService {
907
+ //#region ---- Private Helpers (dot-notation input normalization) ----
672
908
  /**
673
- * Register an activity provider.
909
+ * Collect dot-notation key-value pairs from nested objects so that e.g.
910
+ * { metadata: { "metadata.questionnaire.id": "x" } } becomes { "metadata.questionnaire.id": "x" } at root.
911
+ * Top-level non-dot keys are kept as-is.
674
912
  */
675
- registerProvider(provider) {
676
- this.providers.push(provider);
677
- if (this.initialized) {
678
- this.initializeProvider(provider);
913
+ flattenDotKeysFromTree(obj) {
914
+ if (obj === null || typeof obj !== 'object')
915
+ return obj;
916
+ const result = {};
917
+ for (const [key, value] of Object.entries(obj)) {
918
+ if (key.includes('.')) {
919
+ result[key] = value;
920
+ }
921
+ else if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
922
+ for (const [k, v] of Object.entries(value)) {
923
+ if (k.includes('.')) {
924
+ result[k] = v;
925
+ }
926
+ else {
927
+ result[key + '.' + k] = v;
928
+ }
929
+ }
930
+ }
931
+ else {
932
+ result[key] = value;
933
+ }
679
934
  }
935
+ return result;
680
936
  }
681
937
  /**
682
- * Register a category provider.
938
+ * Expand flat keys with dots into nested objects.
939
+ * e.g. { "metadata.questionnaire.id": "x" } -> { metadata: { questionnaire: { id: "x" } } }.
683
940
  */
684
- registerCategoryProvider(provider) {
685
- this.categoryProviders.push(provider);
686
- if (this.initialized) {
687
- this.initializeCategoryProvider(provider);
941
+ expandDotKeys(obj) {
942
+ if (obj === null || typeof obj !== 'object')
943
+ return obj;
944
+ const result = {};
945
+ const dotKeys = [];
946
+ const simpleKeys = [];
947
+ for (const [key, value] of Object.entries(obj)) {
948
+ if (key.includes('.')) {
949
+ dotKeys.push([key, value]);
950
+ }
951
+ else {
952
+ simpleKeys.push([key, value]);
953
+ }
688
954
  }
689
- }
690
- /**
691
- * Initialize all providers.
692
- */
693
- async initialize() {
694
- if (this.initialized)
695
- return;
696
- // Initialize category providers first
697
- for (const provider of this.categoryProviders) {
698
- await this.initializeCategoryProvider(provider);
955
+ for (const [key, value] of dotKeys) {
956
+ const parts = key.split('.');
957
+ let current = result;
958
+ for (let i = 0; i < parts.length - 1; i++) {
959
+ const part = parts[i];
960
+ if (!(part in current) || typeof current[part] !== 'object' || current[part] === null) {
961
+ current[part] = {};
962
+ }
963
+ current = current[part];
964
+ }
965
+ current[parts[parts.length - 1]] = value;
699
966
  }
700
- // Initialize activity providers
701
- for (const provider of this.providers) {
702
- await this.initializeProvider(provider);
967
+ for (const [key, value] of simpleKeys) {
968
+ result[key] = value;
703
969
  }
704
- this.initialized = true;
970
+ return result;
705
971
  }
706
972
  /**
707
- * Get activity descriptor by key.
973
+ * Normalize workflow input so that flat dot-notation keys (e.g. from form schema
974
+ * "metadata.questionnaire.id") become nested for expression access (inputs.metadata.questionnaire.id).
708
975
  */
709
- getDescriptor(key) {
710
- return this.activityDescriptors.get(key);
711
- }
712
- /**
713
- * Get all activity descriptors.
714
- */
715
- getAllDescriptors() {
716
- return Array.from(this.activityDescriptors.values());
717
- }
718
- /**
719
- * Get descriptors by category.
720
- */
721
- getDescriptorsByCategory(category) {
722
- return this.getAllDescriptors().filter(d => d.category === category);
976
+ normalizeInputs(input) {
977
+ if (!input || typeof input !== 'object')
978
+ return {};
979
+ const flattened = this.flattenDotKeysFromTree(input);
980
+ return this.expandDotKeys(flattened);
723
981
  }
982
+ //#endregion
983
+ //#region ---- Public Methods ----
724
984
  /**
725
- * Get all categories.
985
+ * Build expression evaluation scope for workflow activities.
986
+ *
987
+ * Provides workflow-specific data (inputs, variables, outputs) and context.eval() function.
988
+ * Other data (session, current user, etc.) are provided by expression evaluator scope providers.
989
+ *
990
+ * Scope includes:
991
+ * - inputs: Workflow input values (accessible as inputs.propertyName)
992
+ * - variables: Workflow state variables (accessible as variables.propertyName or vars.propertyName)
993
+ * - outputs: Previous activity outputs (accessible as outputs.activityId)
994
+ * - context.eval(path): Function to access nested properties from workflow data
995
+ *
996
+ * Expressions can use:
997
+ * - {{inputs.userName}} - Direct access to workflow input
998
+ * - {{variables.someVar}} or {{vars.someVar}} - Direct access to workflow variable
999
+ * - {{outputs.activityId.property}} - Direct access to previous activity output
1000
+ * - {{context.eval("inputs.userName")}} - Access via context.eval (supports nested paths)
1001
+ * - {{context.eval("variables.someVar")}} - Access variables via context.eval
1002
+ * - {{context.eval("outputs.activityId.property")}} - Access outputs via context.eval
1003
+ * - {{session.currentUser().name}} - Access current user via expression evaluator scope providers
1004
+ *
1005
+ * The context.eval() function provides a unified way to access all workflow data,
1006
+ * similar to how entity-detail-list uses context.eval() to access parent data.
1007
+ *
1008
+ * @param context - Workflow expression context containing inputs, variables, and outputs
1009
+ * @returns Expression evaluator scope with workflow data and context.eval() function
726
1010
  */
727
- getCategories() {
728
- return [...this.categories].sort((a, b) => (a.order || 999) - (b.order || 999));
1011
+ buildScope(context) {
1012
+ // Normalize inputs so flat dot-notation keys (e.g. metadata.questionnaire.id from forms) become nested for expressions
1013
+ const inputs = this.normalizeInputs(context.inputs || {});
1014
+ // Build merged workflow data object for context.eval()
1015
+ // This allows expressions like: context.eval("inputs.userName") or context.eval("variables.count")
1016
+ const workflowData = {
1017
+ inputs,
1018
+ variables: context.variables || {},
1019
+ vars: context.variables || {}, // Alias for convenience
1020
+ outputs: context.outputs || {},
1021
+ };
1022
+ // Build scope object with workflow-specific data and context.eval()
1023
+ // Note: AXPExpressionEvaluatorScope type expects { [namespace]: { [name]: Function } }
1024
+ // but evaluate() method actually accepts flat objects with values too
1025
+ // We'll use 'any' to allow both values and functions
1026
+ const scope = {
1027
+ // Direct access to workflow data
1028
+ inputs: workflowData.inputs,
1029
+ variables: workflowData.variables,
1030
+ vars: workflowData.vars,
1031
+ outputs: workflowData.outputs,
1032
+ // Context object with eval function (similar to entity-detail-list pattern)
1033
+ context: {
1034
+ eval: (path) => {
1035
+ // Use lodash get to access nested properties
1036
+ // Supports paths like: "inputs.userName", "variables.count", "outputs.activityId.property"
1037
+ return get(workflowData, path);
1038
+ },
1039
+ },
1040
+ };
1041
+ return scope;
729
1042
  }
730
1043
  /**
731
- * Create an activity instance by key using AXPCommandService.
1044
+ * Build expression evaluation scope from workflow instance state.
1045
+ *
1046
+ * Convenience method that extracts data from AXPWorkflowInstanceState.
1047
+ *
1048
+ * @param state - Workflow instance state
1049
+ * @param activityOutputs - Map of activity outputs (activityId -> output)
1050
+ * @returns Expression evaluator scope with workflow data and context.eval() function
732
1051
  */
733
- async createActivity(key) {
734
- try {
735
- // Check if command exists
736
- if (!this.commandService.exists(key)) {
737
- return null;
1052
+ buildScopeFromState(state, activityOutputs) {
1053
+ // Convert activity outputs to record format
1054
+ const outputs = {};
1055
+ if (activityOutputs) {
1056
+ if (activityOutputs instanceof Map) {
1057
+ activityOutputs.forEach((output, activityId) => {
1058
+ outputs[activityId] = output;
1059
+ });
738
1060
  }
739
- // Get command loader from registry
740
- const loader = this.commandRegistry.getLoader(key);
741
- if (!loader) {
742
- return null;
1061
+ else {
1062
+ Object.assign(outputs, activityOutputs);
743
1063
  }
744
- // Create command instance
745
- const command = await loader();
746
- return command;
747
1064
  }
748
- catch (error) {
749
- return null;
1065
+ // Convenience alias for the last activity output (if available).
1066
+ // This allows expressions like: {{outputs.last.someField}}
1067
+ if (outputs['last'] === undefined && state.lastActivityOutput !== undefined) {
1068
+ outputs['last'] = state.lastActivityOutput;
750
1069
  }
1070
+ return this.buildScope({
1071
+ inputs: this.normalizeInputs((state.input || {})),
1072
+ variables: state.variables || {},
1073
+ outputs: outputs,
1074
+ });
751
1075
  }
752
- // ============================================
753
- // PRIVATE METHODS
754
- // ============================================
755
- async initializeProvider(provider) {
756
- const context = {
757
- registerActivity: (config) => {
758
- this.activityDescriptors.set(config.key, config.descriptor);
759
- }
760
- };
761
- await provider.provide(context);
762
- }
763
- async initializeCategoryProvider(provider) {
764
- const context = {
765
- registerCategories: (categories) => {
766
- this.categories.push(...categories);
767
- }
768
- };
769
- await provider.provide(context);
770
- }
771
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPActivityProviderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
772
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPActivityProviderService, providedIn: 'root' }); }
1076
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: WorkflowExpressionScopeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1077
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: WorkflowExpressionScopeService, providedIn: 'root' }); }
773
1078
  }
774
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPActivityProviderService, decorators: [{
1079
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: WorkflowExpressionScopeService, decorators: [{
775
1080
  type: Injectable,
776
1081
  args: [{
777
1082
  providedIn: 'root'
778
1083
  }]
779
1084
  }] });
780
1085
 
1086
+ //#region ---- Constants ----
781
1087
  /**
782
- * Injection token for activity category providers.
1088
+ * Activity types handled internally by the workflow engine (e.g. mock backend).
1089
+ * When such an activity is not registered as Command on the client, we skip execution
1090
+ * without warning — the engine will run it when executing the workflow.
783
1091
  */
784
- const AXP_ACTIVITY_CATEGORY_PROVIDER = new InjectionToken('AXP_ACTIVITY_CATEGORY_PROVIDER');
785
- class AXPActivityCategoryProviderService {
786
- constructor(parent, providers = []) {
787
- this.parent = parent;
788
- this.providers = providers;
789
- this.categories = new Map();
790
- this.isInitialized = false;
791
- if (!parent) {
792
- this.initialize();
793
- }
794
- }
795
- async initialize() {
796
- if (this.isInitialized) {
797
- return;
798
- }
799
- for (const provider of this.providers) {
800
- await provider.provide({
801
- registerCategories: (categories) => {
802
- categories.forEach(cat => this.categories.set(cat.name, cat));
803
- }
804
- });
805
- }
806
- this.isInitialized = true;
807
- }
808
- getAllCategories() {
809
- return Array.from(this.categories.values()).sort((a, b) => (a.order || 0) - (b.order || 0));
810
- }
811
- getCategory(name) {
812
- return this.categories.get(name);
813
- }
814
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPActivityCategoryProviderService, deps: [{ token: AXPActivityCategoryProviderService, optional: true, skipSelf: true }, { token: AXP_ACTIVITY_CATEGORY_PROVIDER, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
815
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPActivityCategoryProviderService, providedIn: 'root' }); }
816
- }
817
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPActivityCategoryProviderService, decorators: [{
818
- type: Injectable,
819
- args: [{ providedIn: 'root' }]
820
- }], ctorParameters: () => [{ type: AXPActivityCategoryProviderService, decorators: [{
821
- type: Optional
822
- }, {
823
- type: SkipSelf
824
- }] }, { type: undefined, decorators: [{
825
- type: Optional
826
- }, {
827
- type: Inject,
828
- args: [AXP_ACTIVITY_CATEGORY_PROVIDER]
829
- }] }] });
830
-
1092
+ const ENGINE_BUILTIN_ACTIVITY_TYPES = new Set([
1093
+ 'workflow-activity:set-variable',
1094
+ 'workflow-activity:http-request',
1095
+ ]);
1096
+ //#endregion
831
1097
  /**
832
- * Abstract service for workflow execution operations.
833
- *
834
- * This service handles communication with backend for workflow execution.
1098
+ * Activity Executor Service
835
1099
  *
836
- * Implementation should be provided in connectivity layer:
837
- * - Mock implementation: @acorex/connectivity/mock
838
- * - API implementation: @acorex/connectivity/api
1100
+ * Service for executing workflow activities via CommandBus.
1101
+ * Automatically evaluates expressions in activity inputs before execution.
839
1102
  *
840
1103
  * @example
841
1104
  * ```typescript
842
- * // In connectivity/mock
843
- * @Injectable()
844
- * export class AXCWorkflowExecutionService implements AXPWorkflowExecutionService {
845
- * async startExecution(request: AXPStartWorkflowExecutionRequest): Promise<AXPStartWorkflowExecutionResponse> {
846
- * // Mock implementation
847
- * }
848
- * }
1105
+ * const executor = inject(ActivityExecutor);
849
1106
  *
850
- * // In connectivity/api
851
- * @Injectable()
852
- * export class AXCWorkflowExecutionService implements AXPWorkflowExecutionService {
853
- * constructor(private http: HttpClient) {}
854
- *
855
- * async startExecution(request: AXPStartWorkflowExecutionRequest): Promise<AXPStartWorkflowExecutionResponse> {
856
- * return firstValueFrom(
857
- * this.http.post<AXPStartWorkflowExecutionResponse>(
858
- * `${this.config.baseUrl}/api/workflows/${request.workflowId}/start`,
859
- * { input: request.input }
860
- * )
861
- * );
862
- * }
863
- * }
1107
+ * // Execute activity with task and workflow state (expressions will be evaluated)
1108
+ * const result = await executor.execute(task, workflowState, activityOutputs);
864
1109
  * ```
865
1110
  */
866
- class AXPWorkflowExecutionService {
867
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowExecutionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
868
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowExecutionService }); }
869
- }
870
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowExecutionService, decorators: [{
871
- type: Injectable
872
- }] });
873
-
874
- //#endregion
875
- /**
876
- * Production Workflow Coordinator.
877
- *
878
- * Separates frontend/backend execution:
879
- * - Frontend activities: Execute with AXPCommand in browser
880
- * - Backend activities: Execute via API calls to backend
881
- * - State caching: Caches workflow state in client for performance
882
- *
883
- */
884
- class WorkflowCoordinator {
1111
+ class ActivityExecutor {
885
1112
  constructor() {
886
1113
  //#region ---- Services & Dependencies ----
887
- this.workflowExecutionService = inject(AXPWorkflowExecutionService);
888
1114
  this.commandService = inject(AXPCommandService);
889
- //#endregion
890
- //#region ---- State Cache (Client-side for Performance) ----
891
- /**
892
- * Cache workflow states in memory for quick access.
893
- * Key: executionId
894
- * Value: AXPWorkflowExecutionState
895
- */
896
- this.stateCache = new Map();
1115
+ this.expressionEvaluator = inject(AXPExpressionEvaluatorService);
1116
+ this.expressionScopeService = inject(WorkflowExpressionScopeService);
897
1117
  }
898
1118
  //#endregion
899
1119
  //#region ---- Public Methods ----
900
1120
  /**
901
- * Start workflow execution in backend.
902
- *
903
- * Backend decides what to do: returns pendingTask or indicates completion.
904
- * Frontend only calls API - no business logic here.
905
- *
906
- * @param workflowId - Workflow ID
907
- * @param input - Initial input data
908
- * @returns Execution result with pendingTask (if any)
909
- *
910
- * @example
911
- * ```typescript
912
- * const result = await coordinator.startWorkflow('my-workflow', { userId: '123' });
913
- *
914
- * if (result.pendingTask) {
915
- * // Execute task if frontend, or wait for backend to complete
916
- * if (result.pendingTask.executionMode === 'frontend') {
917
- * await coordinator.executeTask(result.pendingTask);
918
- * await coordinator.completeTask(result.executionId, result.pendingTask, outcome, output);
919
- * }
920
- * }
921
- * ```
922
- */
923
- async startWorkflow(workflowId, input = {}) {
924
- try {
925
- const execution = await this.startWorkflowExecution(workflowId, input);
926
- const result = {
927
- success: true,
928
- output: execution.state.output,
929
- nextTask: execution.pendingTask || null,
930
- executionId: execution.executionId,
931
- state: execution.state
932
- };
933
- return result;
934
- }
935
- catch (error) {
936
- console.error('[WorkflowCoordinator] ❌ Error in startWorkflow', error);
937
- return {
938
- success: false,
939
- error: error.message || 'Failed to start workflow',
940
- nextTask: null
941
- };
942
- }
943
- }
944
- /**
945
- * Execute a frontend task using AXPCommand.
1121
+ * Execute a workflow activity with expression evaluation.
946
1122
  *
947
- * Only executes if task.executionMode is 'frontend' or 'both'.
948
- * Backend tasks are handled automatically by backend.
1123
+ * Evaluates expressions in activity inputs using workflow state,
1124
+ * then executes the activity via CommandBus.
949
1125
  *
950
- * @param task - Task to execute
1126
+ * @param task - Workflow task containing activity information
1127
+ * @param workflowState - Current workflow instance state (for expression evaluation)
1128
+ * @param activityOutputs - Map of previous activity outputs (for expression evaluation)
951
1129
  * @returns Execution result with output and outcome
952
1130
  */
953
- async executeTask(task) {
954
- // Only execute frontend tasks
955
- if (task.executionMode !== 'frontend' && task.executionMode !== 'both') {
956
- throw new Error(`Task '${task.activityId}' is not a frontend task. Backend handles it automatically.`);
957
- }
958
- return await this.executeFrontendActivity(task);
959
- }
960
- /**
961
- * Complete a task and get next task from backend.
962
- *
963
- * Sends task result to backend API.
964
- * Backend decides: next task, fail, or complete workflow.
965
- *
966
- * @param executionId - Execution ID
967
- * @param task - Completed task
968
- * @param outcome - Task outcome (e.g., 'Done', 'Confirmed', 'Cancelled')
969
- * @param output - Task output/result
970
- * @returns Next task from backend (if any)
971
- */
972
- async completeTask(executionId, task, outcome, output) {
1131
+ async execute(task, workflowState, activityOutputs) {
973
1132
  try {
974
- // Send result to backend - backend decides next step
975
- const response = await this.workflowExecutionService.resumeExecution({
976
- executionId,
977
- stepId: task.activityId,
978
- taskToken: task.taskToken,
979
- outcome,
980
- userInput: output
981
- });
982
- // Update cache
983
- if (response.state) {
984
- this.stateCache.set(executionId, response.state);
985
- }
986
- return {
987
- success: true,
988
- output: response.output,
989
- nextTask: response.nextTask || null,
990
- executionId,
991
- state: response.state
992
- };
993
- }
994
- catch (error) {
995
- return {
996
- success: false,
997
- error: error.message || 'Failed to complete task',
998
- nextTask: null
999
- };
1000
- }
1001
- }
1002
- /**
1003
- * Execute workflow by ID (backward compatibility).
1004
- *
1005
- * @deprecated Use startWorkflow + executeTask + completeTask pattern instead.
1006
- * This method is kept for backward compatibility but will be removed.
1007
- */
1008
- async executeWorkflowById(workflowId, input = {}) {
1009
- // Just start workflow - caller should handle task execution
1010
- return await this.startWorkflow(workflowId, input);
1011
- }
1012
- /**
1013
- * Resume a suspended workflow (e.g., after user interaction).
1014
- *
1015
- * Backend determines nextStep based on outcome and outcomeConnections.
1016
- * Client only provides executionId, stepId, outcome, and optional userInput.
1017
- *
1018
- * @param executionId - Workflow execution ID
1019
- * @param stepId - Step ID that was waiting for user input
1020
- * @param outcome - User action outcome (e.g., 'Confirmed', 'Cancelled', 'Submitted')
1021
- * @param userInput - Optional user input data
1022
- */
1023
- async resumeWorkflow(executionId, stepId, outcome, userInput, taskToken) {
1024
- try {
1025
- // Ensure taskToken is provided for secure resumption
1026
- if (!taskToken) {
1027
- throw new Error('Missing taskToken for resumeWorkflow');
1028
- }
1029
- // Backend handles everything: checks outcomeConnections and determines nextStep
1030
- const response = await this.workflowExecutionService.resumeExecution({
1031
- executionId,
1032
- stepId,
1033
- taskToken,
1034
- outcome,
1035
- userInput
1036
- });
1037
- // Update cache with state from backend
1038
- if (response.state) {
1039
- this.stateCache.set(executionId, response.state);
1040
- }
1041
- return {
1042
- success: true,
1043
- output: response.output,
1044
- nextTask: response.nextTask || null, // Backend determines this from outcomeConnections
1045
- executionId,
1046
- state: response.state
1047
- };
1048
- }
1049
- catch (error) {
1050
- return {
1051
- success: false,
1052
- error: error.message || 'Failed to resume workflow',
1053
- nextTask: null
1054
- };
1055
- }
1056
- }
1057
- /**
1058
- * Get workflow execution state (from cache or backend).
1059
- */
1060
- async getWorkflowState(executionId) {
1061
- // Check cache first
1062
- const cached = this.stateCache.get(executionId);
1063
- if (cached) {
1064
- // Cache is valid for 5 minutes
1065
- const cacheAge = Date.now() - cached.lastUpdated.getTime();
1066
- if (cacheAge < 5 * 60 * 1000) {
1067
- return cached;
1133
+ const activityName = task.activityType;
1134
+ // Evaluate inputs if workflow state is provided
1135
+ let evaluatedInputs = task.input || {};
1136
+ if (workflowState) {
1137
+ // Prefer explicit outputs, fallback to outputs stored in state
1138
+ const outputsForScope = activityOutputs ??
1139
+ workflowState.activityOutputs ??
1140
+ undefined;
1141
+ // Build expression scope from workflow state
1142
+ const scope = this.expressionScopeService.buildScopeFromState(workflowState, outputsForScope);
1143
+ // Evaluate all inputs recursively (handles nested objects and arrays)
1144
+ evaluatedInputs = await this.expressionEvaluator.evaluate(task.input || {}, scope);
1068
1145
  }
1069
- }
1070
- // Fetch from backend
1071
- try {
1072
- const state = await this.workflowExecutionService.getExecutionState({
1073
- executionId
1074
- });
1075
- // Update cache
1076
- this.stateCache.set(executionId, state);
1077
- return state;
1078
- }
1079
- catch {
1080
- return null;
1081
- }
1082
- }
1083
- //#endregion
1084
- //#region ---- Private Methods ----
1085
- /**
1086
- * Execute a frontend activity using CommandBus.
1087
- *
1088
- * Frontend activities are executed in the browser using AXPCommandService.
1089
- * Activities can also be executed in both frontend and backend (hybrid mode).
1090
- *
1091
- * @param task - Frontend task to execute
1092
- * @returns Execution result with output and outcome
1093
- */
1094
- async executeFrontendActivity(task) {
1095
- try {
1096
1146
  // Check if command exists
1097
- const commandExists = this.commandService.exists(task.activityType);
1147
+ const commandExists = this.commandService.exists(activityName);
1098
1148
  if (!commandExists) {
1099
- console.warn(`[WorkflowCoordinator] ⚠️ Frontend activity '${task.activityType}' is not registered. Skipping execution.`);
1149
+ if (!ENGINE_BUILTIN_ACTIVITY_TYPES.has(activityName)) {
1150
+ console.warn(`[ActivityExecutor] ⚠️ Activity '${activityName}' is not registered as Command. ` +
1151
+ `Skipping execution.`);
1152
+ }
1100
1153
  return {
1101
1154
  output: null,
1102
1155
  outcome: 'Done'
1103
1156
  };
1104
1157
  }
1105
- // Execute activity via CommandBus
1106
- // Activities registered as AXPCommand return {output, outcomes}
1107
- // 🎯 Flatten properties if nested (workflow-studio format)
1108
- let commandInput = task.input || task.config || {};
1158
+ // Flatten properties if nested (workflow-studio format)
1159
+ let commandInput = evaluatedInputs;
1109
1160
  if (commandInput['properties'] && typeof commandInput['properties'] === 'object') {
1110
1161
  // Flatten: {properties: {text: "..."}} -> {text: "..."}
1111
1162
  commandInput = { ...commandInput['properties'] };
1112
1163
  }
1113
- const result = await this.commandService.execute(task.activityType, commandInput);
1164
+ // Execute activity via CommandBus
1165
+ // Activities (AXPActivity) return {output, outcome}; legacy may return {output, outcomes}
1166
+ const result = await this.commandService.execute(activityName, commandInput);
1114
1167
  if (!result) {
1115
1168
  return {
1116
1169
  output: null,
@@ -1126,1285 +1179,1209 @@ class WorkflowCoordinator {
1126
1179
  };
1127
1180
  }
1128
1181
  const commandResult = result.data;
1129
- const outcomes = commandResult?.outcomes ?? {};
1182
+ // Prefer unified outcome in result.metadata; fall back to legacy data.outcome/outcomes.
1183
+ const metadataOutcome = result?.metadata?.['outcome'];
1130
1184
  let outcome = 'Done';
1131
- if (Object.keys(outcomes).length > 0) {
1132
- outcome = outcomes['Done'] ? 'Done' : Object.keys(outcomes)[0] || 'Done';
1185
+ if (typeof metadataOutcome === 'string' && metadataOutcome.length > 0) {
1186
+ outcome = metadataOutcome;
1133
1187
  }
1188
+ else if (typeof commandResult?.outcome === 'string' && commandResult.outcome.length > 0) {
1189
+ outcome = commandResult.outcome;
1190
+ }
1191
+ else {
1192
+ const outcomes = (commandResult?.outcomes ?? {});
1193
+ if (outcomes && typeof outcomes === 'object' && Object.keys(outcomes).length > 0) {
1194
+ outcome = outcomes['Done'] ? 'Done' : Object.keys(outcomes)[0] || 'Done';
1195
+ }
1196
+ }
1197
+ // Prefer output wrapper when present; otherwise treat data itself as output.
1198
+ const output = commandResult && typeof commandResult === 'object' && 'output' in commandResult
1199
+ ? commandResult.output
1200
+ : commandResult;
1134
1201
  return {
1135
- output: commandResult?.output ?? null,
1202
+ output: output ?? null,
1136
1203
  outcome,
1137
1204
  };
1138
1205
  }
1139
1206
  catch (error) {
1140
- console.error(`[WorkflowCoordinator] ❌ Error executing frontend activity '${task.activityType}':`, error);
1207
+ console.error(`[ActivityExecutor] ❌ Error evaluating expressions or executing activity:`, error);
1141
1208
  return {
1142
1209
  output: { error: error.message || 'Unknown error' },
1143
1210
  outcome: 'Failed'
1144
1211
  };
1145
1212
  }
1146
1213
  }
1147
- /**
1148
- * Start workflow execution in backend.
1149
- * Backend returns executionId, initial state, and first task to execute.
1150
- */
1151
- async startWorkflowExecution(workflowId, input) {
1152
- const response = await this.workflowExecutionService.startExecution({
1153
- workflowId,
1154
- input
1155
- });
1156
- // Cache state
1157
- this.stateCache.set(response.executionId, response.state);
1158
- return {
1159
- executionId: response.executionId,
1160
- state: response.state,
1161
- pendingTask: response.pendingTask
1162
- };
1163
- }
1164
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: WorkflowCoordinator, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1165
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: WorkflowCoordinator, providedIn: 'root' }); }
1214
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ActivityExecutor, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1215
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ActivityExecutor, providedIn: 'root' }); }
1166
1216
  }
1167
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: WorkflowCoordinator, decorators: [{
1217
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ActivityExecutor, decorators: [{
1168
1218
  type: Injectable,
1169
1219
  args: [{
1170
1220
  providedIn: 'root'
1171
1221
  }]
1172
1222
  }] });
1173
1223
 
1224
+ //#endregion
1174
1225
  /**
1175
- * Types and interfaces for Workflow Execution Service.
1176
- */
1177
-
1178
- // Production Workflow Coordinator (Frontend/Backend Separation)
1179
-
1180
- /**
1181
- * WriteLine Activity - Simple console logging activity.
1226
+ * Workflow Manager - Facade for workflow lifecycle orchestration.
1182
1227
  *
1183
- * Usage:
1184
- * ```typescript
1185
- * const activity = new WriteLine();
1186
- * await activity.execute({ text: 'Hello World' });
1187
- * ```
1188
- */
1189
- class WriteLine extends Activity {
1190
- constructor() {
1191
- super('WriteLine');
1192
- }
1193
- async execute(input) {
1194
- const text = input.text || '';
1195
- if (text !== undefined && text !== null) {
1196
- console.log(`[WriteLine] ${text}`);
1197
- }
1198
- return this.createResult(undefined, 'Done');
1199
- }
1200
- }
1201
-
1202
- var writeLine_activity = /*#__PURE__*/Object.freeze({
1203
- __proto__: null,
1204
- WriteLine: WriteLine
1205
- });
1206
-
1207
- /**
1208
- * Sequence Activity - Executes activities in sequential order.
1228
+ * This service is the ONLY interface the frontend uses to interact with workflows.
1229
+ * It follows Clean Architecture principles and does NOT contain execution or business logic.
1209
1230
  *
1210
- * Usage:
1211
- * ```typescript
1212
- * const sequence = new Sequence();
1213
- * sequence.activities = [activity1, activity2, activity3];
1214
- * await sequence.execute({});
1215
- * ```
1231
+ * Responsibilities:
1232
+ * - Orchestrate workflow lifecycle (start, execute, complete, resume)
1233
+ * - Delegate execution to ActivityExecutor
1234
+ * - Cache workflow state in memory
1235
+ * - Expose a stable API for UI
1236
+ *
1237
+ * Rules:
1238
+ * - No HTTP calls (delegates to AXPWorkflowEngine)
1239
+ * - No CommandBus / Command execution (delegates to ActivityExecutor)
1240
+ * - No workflow branching logic (backend decides)
1241
+ * - No business validation (backend validates)
1242
+ * - No backend assumptions (uses abstract runtime service)
1216
1243
  */
1217
- class Sequence extends Activity {
1244
+ class AXPWorkflowManager {
1218
1245
  constructor() {
1219
- super('Sequence');
1246
+ //#region ---- Services & Dependencies ----
1247
+ this.workflowEngine = inject(AXP_WORKFLOW_ENGINE);
1248
+ this.activityExecutor = inject(ActivityExecutor);
1249
+ //#endregion
1250
+ //#region ---- State Cache ----
1220
1251
  /**
1221
- * Activities to execute in sequence.
1252
+ * Cache workflow states in memory for quick access.
1253
+ * Key: instanceId
1254
+ * Value: AXPWorkflowInstanceState
1222
1255
  */
1223
- this.activities = [];
1256
+ this.stateCache = new Map();
1257
+ /**
1258
+ * Cache TTL in milliseconds (5 minutes).
1259
+ */
1260
+ this.CACHE_TTL = 5 * 60 * 1000;
1224
1261
  }
1225
- async execute(input) {
1226
- // Execute all activities in sequence
1227
- for (const activity of this.activities) {
1228
- const result = await activity.execute(input);
1229
- if (!result.success) {
1262
+ //#endregion
1263
+ //#region ---- Public Methods ----
1264
+ /**
1265
+ * Execute frontend activities interactively until reaching workflow-activity:human-task or completion.
1266
+ *
1267
+ * Interactive = show form/popup immediately (user sees and acts). Only workflow-activity:human-task
1268
+ * is not interactive (goes to task board). Other frontend activities (show-layout-popup, show-toast, etc.)
1269
+ * even with taskType human-task in definition are executed here.
1270
+ *
1271
+ * @param instanceId - Workflow instance ID
1272
+ * @param task - Current task to execute
1273
+ * @param state - Current workflow state
1274
+ * @param lastActivityOutput - Last activity output (for expression evaluation)
1275
+ * @returns Final result with nextTask (if workflow-activity:human-task) or completion status
1276
+ */
1277
+ async executeInteractiveFlow(instanceId, task, state, activityOutputs) {
1278
+ let currentTask = task;
1279
+ let currentState = state;
1280
+ let currentActivityOutputs = {
1281
+ ...(currentState.activityOutputs || {}),
1282
+ ...(activityOutputs || {}),
1283
+ };
1284
+ const maxIterations = 100; // Prevent infinite loops
1285
+ let iterationCount = 0;
1286
+ while (currentTask && iterationCount < maxIterations) {
1287
+ iterationCount++;
1288
+ // Interactive = frontend executionMode and NOT workflow-activity:human-task (that one goes to task board)
1289
+ const isInteractive = (currentTask.executionMode === 'frontend' || currentTask.executionMode === 'both') &&
1290
+ currentTask.activityType !== 'workflow-activity:human-task';
1291
+ if (isInteractive) {
1292
+ // Execute frontend activity
1293
+ const execResult = await this.activityExecutor.execute(currentTask, currentState, currentActivityOutputs);
1294
+ // Track outputs locally (backend should also persist and return them)
1295
+ currentActivityOutputs = {
1296
+ ...currentActivityOutputs,
1297
+ [currentTask.activityId]: execResult.output,
1298
+ };
1299
+ // Send result to backend
1300
+ const completeResponse = await this.workflowEngine.frontActivtyComplete({
1301
+ instanceId,
1302
+ activityNode: currentTask.activityId,
1303
+ output: execResult.output || {},
1304
+ outcome: execResult.outcome,
1305
+ });
1306
+ // Update state cache
1307
+ if (completeResponse.state) {
1308
+ const normalizedState = { ...completeResponse.state };
1309
+ if (normalizedState.lastUpdated && !(normalizedState.lastUpdated instanceof Date)) {
1310
+ normalizedState.lastUpdated = new Date(normalizedState.lastUpdated);
1311
+ }
1312
+ currentState = normalizedState;
1313
+ // Prefer outputs returned by backend; fallback to local cache
1314
+ currentActivityOutputs = {
1315
+ ...currentActivityOutputs,
1316
+ ...(normalizedState.activityOutputs || {}),
1317
+ };
1318
+ this.stateCache.set(instanceId, normalizedState);
1319
+ }
1320
+ // Backend decides: if no nextTask, workflow is completed
1321
+ if (!completeResponse.nextTask) {
1322
+ return {
1323
+ nextTask: null,
1324
+ state: currentState,
1325
+ output: completeResponse.output,
1326
+ };
1327
+ }
1328
+ // Backend decides: if nextTask is workflow-activity:human-task or not frontend, return it (task board or done)
1329
+ const nextInteractive = (completeResponse.nextTask.executionMode === 'frontend' || completeResponse.nextTask.executionMode === 'both') &&
1330
+ completeResponse.nextTask.activityType !== 'workflow-activity:human-task';
1331
+ if (!nextInteractive) {
1332
+ return {
1333
+ nextTask: completeResponse.nextTask,
1334
+ state: currentState,
1335
+ };
1336
+ }
1337
+ // Continue with next interactive frontend task
1338
+ currentTask = completeResponse.nextTask;
1339
+ }
1340
+ else {
1341
+ // Not interactive (e.g. workflow-activity:human-task) - return as-is for task board
1230
1342
  return {
1231
- success: false,
1232
- message: result.message,
1233
- data: {
1234
- output: undefined,
1235
- outcomes: {
1236
- Failed: true,
1237
- },
1238
- },
1343
+ nextTask: currentTask,
1344
+ state: currentState,
1239
1345
  };
1240
1346
  }
1241
1347
  }
1242
- return this.createResult(undefined, 'Done');
1348
+ // Max iterations reached
1349
+ if (iterationCount >= maxIterations) {
1350
+ console.warn(`[AXPWorkflowManager] ⚠️ Maximum iterations (${maxIterations}) reached`);
1351
+ }
1352
+ return {
1353
+ nextTask: currentTask,
1354
+ state: currentState,
1355
+ };
1243
1356
  }
1244
- }
1245
-
1246
- var sequence_activity = /*#__PURE__*/Object.freeze({
1247
- __proto__: null,
1248
- Sequence: Sequence
1249
- });
1250
-
1251
- /**
1252
- * Show Confirm Dialog Activity - Displays confirmation dialog to user.
1253
- *
1254
- * Has two outcomes:
1255
- * - 'Confirmed': User clicked confirm/yes
1256
- * - 'Cancelled': User clicked cancel/no
1257
- *
1258
- * Usage:
1259
- * ```typescript
1260
- * const dialog = new ShowConfirmDialog();
1261
- * await dialog.execute({
1262
- * title: 'Confirm Delete',
1263
- * message: 'Are you sure?',
1264
- * color: 'danger'
1265
- * });
1266
- * ```
1267
- */
1268
- class ShowConfirmDialog extends Activity {
1269
- //#endregion
1270
- constructor() {
1271
- super('ShowConfirmDialog');
1272
- //#region ---- Services & Dependencies ----
1273
- this.dialogService = inject(AXDialogService);
1274
- this.translationService = inject(AXTranslationService);
1275
- }
1276
- async execute(input) {
1277
- const { title = '', message = '', color = 'primary', defaultAction = 'cancel', align = 'horizontal', backdrop = false } = input;
1278
- // Translate title and message only if they start with '@' (translation key)
1279
- // Otherwise use the text as-is
1280
- const translatedTitle = title
1281
- ? (title.startsWith('@')
1282
- ? await this.translationService.translateAsync(title) || title
1283
- : title)
1284
- : '';
1285
- const translatedMessage = message
1286
- ? (message.startsWith('@')
1287
- ? await this.translationService.translateAsync(message) || message
1288
- : message)
1289
- : '';
1357
+ /**
1358
+ * Start a new workflow instance.
1359
+ *
1360
+ * Creates a new workflow instance in backend and returns instance ID.
1361
+ * Backend decides what to do: returns pendingTask or indicates completion.
1362
+ *
1363
+ * @param workflowId - Workflow ID to start
1364
+ * @param input - Initial input data (optional)
1365
+ * @returns Start result with instanceId, state, and nextTask
1366
+ *
1367
+ * @example
1368
+ * ```typescript
1369
+ * const result = await workflowManager.start('my-workflow', { userId: '123' });
1370
+ *
1371
+ * if (result.success && result.nextTask) {
1372
+ * // Execute task if frontend
1373
+ * if (result.nextTask.executionMode === 'frontend') {
1374
+ * const execResult = await workflowManager.execute(result.nextTask);
1375
+ * await workflowManager.complete(result.instanceId!, result.nextTask, execResult.outcome, execResult.output);
1376
+ * }
1377
+ * }
1378
+ * ```
1379
+ */
1380
+ async start(workflowId, input = {}) {
1290
1381
  try {
1291
- const confirmResult = await this.dialogService.confirm(translatedTitle, translatedMessage, color, align, backdrop, defaultAction);
1292
- const result = confirmResult.result;
1293
- const action = result ? 'confirm' : 'cancel';
1294
- return this.createResult({
1295
- result,
1296
- action
1297
- }, result ? 'Confirmed' : 'Cancelled');
1298
- }
1299
- catch (err) {
1300
- console.error('[ShowConfirmDialog] Error showing dialog:', err);
1382
+ const response = await this.workflowEngine.start({
1383
+ workflowId,
1384
+ input,
1385
+ });
1386
+ // Cache state (normalize Date)
1387
+ let startNormalizedState = { ...response.state };
1388
+ if (startNormalizedState.lastUpdated && !(startNormalizedState.lastUpdated instanceof Date)) {
1389
+ startNormalizedState.lastUpdated = new Date(startNormalizedState.lastUpdated);
1390
+ }
1391
+ this.stateCache.set(response.instanceId, startNormalizedState);
1392
+ // 🎯 Interactive flow: Execute frontend activities that are NOT workflow-activity:human-task (those go to task board)
1393
+ let finalNextTask = response.pendingTask || null;
1394
+ let finalOutput = startNormalizedState.output;
1395
+ const pendingTask = response.pendingTask;
1396
+ if (pendingTask &&
1397
+ (pendingTask.executionMode === 'frontend' || pendingTask.executionMode === 'both') &&
1398
+ pendingTask.activityType !== 'workflow-activity:human-task') {
1399
+ const interactiveResult = await this.executeInteractiveFlow(response.instanceId, pendingTask, startNormalizedState, response.activityOutputs || response.state.activityOutputs);
1400
+ finalNextTask = interactiveResult.nextTask;
1401
+ startNormalizedState = interactiveResult.state;
1402
+ if (interactiveResult.output !== undefined) {
1403
+ finalOutput = interactiveResult.output;
1404
+ }
1405
+ // Update cache with final state
1406
+ this.stateCache.set(response.instanceId, startNormalizedState);
1407
+ }
1408
+ // If backend returned null or non-executable task, return it as-is
1409
+ // Backend already decided workflow status (suspended, completed, etc.)
1301
1410
  return {
1302
- success: false,
1303
- message: {
1304
- text: err instanceof Error ? err.message : 'Failed to show confirm dialog',
1305
- },
1306
- data: {
1307
- output: {
1308
- result: false,
1309
- action: 'error',
1310
- },
1311
- outcomes: {
1312
- Cancelled: true,
1313
- },
1314
- },
1411
+ success: true,
1412
+ instanceId: response.instanceId,
1413
+ state: startNormalizedState,
1414
+ nextTask: finalNextTask,
1415
+ output: finalOutput,
1315
1416
  };
1316
1417
  }
1317
- }
1318
- }
1319
-
1320
- var showConfirmDialog_activity = /*#__PURE__*/Object.freeze({
1321
- __proto__: null,
1322
- ShowConfirmDialog: ShowConfirmDialog
1323
- });
1324
-
1325
- /**
1326
- * Show Alert Dialog Activity - Displays alert dialog to user.
1327
- *
1328
- * Has one outcome:
1329
- * - 'Done': User clicked OK
1330
- *
1331
- * Usage:
1332
- * ```typescript
1333
- * const dialog = new ShowAlertDialog();
1334
- * await dialog.execute({
1335
- * title: 'Alert',
1336
- * message: 'This is an alert',
1337
- * color: 'info'
1338
- * });
1339
- * ```
1340
- */
1341
- class ShowAlertDialog extends Activity {
1342
- //#endregion
1343
- constructor() {
1344
- super('ShowAlertDialog');
1345
- //#region ---- Services & Dependencies ----
1346
- this.dialogService = inject(AXDialogService);
1347
- this.translationService = inject(AXTranslationService);
1348
- }
1349
- async execute(input) {
1350
- const { title = '', message = '', color = 'primary' } = input;
1351
- // Translate title and message only if they start with '@' (translation key)
1352
- // Otherwise use the text as-is
1353
- const translatedTitle = title
1354
- ? (title.startsWith('@')
1355
- ? await this.translationService.translateAsync(title) || title
1356
- : title)
1357
- : '';
1358
- const translatedMessage = message
1359
- ? (message.startsWith('@')
1360
- ? await this.translationService.translateAsync(message) || message
1361
- : message)
1362
- : '';
1363
- try {
1364
- await this.dialogService.alert(translatedTitle, translatedMessage, color);
1365
- return this.createResult({
1366
- result: true,
1367
- action: 'ok'
1368
- }, 'Done');
1369
- }
1370
- catch (err) {
1371
- console.error('[ShowAlertDialog] Error showing dialog:', err);
1418
+ catch (error) {
1419
+ console.error('[AXPWorkflowManager] ❌ Error starting workflow:', error);
1372
1420
  return {
1373
1421
  success: false,
1374
- message: {
1375
- text: err instanceof Error ? err.message : 'Failed to show alert dialog',
1376
- },
1377
- data: {
1378
- output: {
1379
- result: false,
1380
- action: 'error',
1381
- },
1382
- outcomes: {
1383
- Failed: true,
1384
- },
1385
- },
1422
+ error: error.message || 'Failed to start workflow',
1386
1423
  };
1387
1424
  }
1388
1425
  }
1389
- }
1390
-
1391
- var showAlertDialog_activity = /*#__PURE__*/Object.freeze({
1392
- __proto__: null,
1393
- ShowAlertDialog: ShowAlertDialog
1394
- });
1395
-
1396
- /**
1397
- * Show Dialog Layout Builder Activity - Displays dialog using Layout Builder.
1398
- *
1399
- * This activity allows you to create custom dialogs using the Layout Builder API.
1400
- * It accepts JSON-serializable content (AXPWidgetNode) and actions configuration.
1401
- *
1402
- * The content can be created using Layout Builder and then converted to JSON:
1403
- * ```typescript
1404
- * const builder = layoutBuilderService.create();
1405
- * builder.flex(flex => {
1406
- * flex.setDirection('column')
1407
- * .formField('First Name', field => {
1408
- * field.path('firstName');
1409
- * field.textBox({ placeholder: 'Enter first name' });
1410
- * });
1411
- * });
1412
- * const contentNode = builder.build();
1413
- * ```
1414
- *
1415
- * Usage in Workflow:
1416
- * ```typescript
1417
- * const dialog = new ShowDialogLayoutBuilder();
1418
- * await dialog.execute({
1419
- * title: 'User Information',
1420
- * size: 'md',
1421
- * context: { firstName: '', lastName: '' },
1422
- * content: {
1423
- * type: 'flex-layout',
1424
- * mode: 'edit',
1425
- * options: {
1426
- * flexDirection: 'column',
1427
- * gap: '16px'
1428
- * },
1429
- * children: [
1430
- * {
1431
- * type: 'form-field',
1432
- * mode: 'edit',
1433
- * options: {
1434
- * label: 'First Name',
1435
- * showLabel: true
1436
- * },
1437
- * children: [{
1438
- * type: 'text-editor',
1439
- * path: 'firstName',
1440
- * options: {
1441
- * placeholder: 'Enter first name'
1442
- * }
1443
- * }]
1444
- * }
1445
- * ]
1446
- * },
1447
- * actions: {
1448
- * cancel: '@general:actions.cancel.title',
1449
- * submit: '@general:actions.submit.title',
1450
- * custom: [{
1451
- * title: 'Save Draft',
1452
- * icon: 'fa-save',
1453
- * color: 'secondary',
1454
- * command: { name: 'save-draft' }
1455
- * }]
1456
- * }
1457
- * });
1458
- * ```
1459
- *
1460
- * Usage in JSON Workflow Definition:
1461
- * ```json
1462
- * {
1463
- * "type": "ShowDialogLayoutBuilder",
1464
- * "properties": {
1465
- * "title": "User Information",
1466
- * "size": "md",
1467
- * "context": { "firstName": "", "lastName": "" },
1468
- * "content": {
1469
- * "type": "flex-layout",
1470
- * "mode": "edit",
1471
- * "options": {
1472
- * "flexDirection": "column",
1473
- * "gap": "16px"
1474
- * },
1475
- * "children": [
1476
- * {
1477
- * "type": "form-field",
1478
- * "mode": "edit",
1479
- * "options": {
1480
- * "label": "First Name",
1481
- * "showLabel": true
1482
- * },
1483
- * "children": [{
1484
- * "type": "text-editor",
1485
- * "path": "firstName",
1486
- * "options": {
1487
- * "placeholder": "Enter first name"
1488
- * }
1489
- * }]
1490
- * }
1491
- * ]
1492
- * },
1493
- * "actions": {
1494
- * "cancel": "@general:actions.cancel.title",
1495
- * "submit": "@general:actions.submit.title"
1496
- * }
1497
- * }
1498
- * }
1499
- * ```
1500
- */
1501
- class ShowDialogLayoutBuilder extends Activity {
1502
- //#endregion
1503
- constructor() {
1504
- super('ShowDialogLayoutBuilder');
1505
- //#region ---- Services & Dependencies ----
1506
- this.layoutBuilder = inject(AXPLayoutBuilderService);
1507
- }
1508
- async execute(input) {
1509
- const { title = '', size = 'md', context = {}, closeButton = false, message, content, actions } = input;
1426
+ /**
1427
+ * Resume a suspended workflow (e.g., after user interaction).
1428
+ *
1429
+ * Backend determines nextStep based on outcome and outcomeConnections.
1430
+ * Client only provides instanceId, stepId, outcome, and optional userInput.
1431
+ *
1432
+ * @param instanceId - Workflow instance ID
1433
+ * @param stepId - Step ID that was waiting for user input
1434
+ * @param outcome - User action outcome (e.g., 'Confirmed', 'Cancelled', 'Submitted')
1435
+ * @param userInput - Optional user input data
1436
+ * @param taskToken - Secure task token (required for secure resumption)
1437
+ * @returns Resume result with next task (if any)
1438
+ */
1439
+ async resume(instanceId, stepId, outcome, userInput, taskToken) {
1510
1440
  try {
1511
- // Create dialog using layout builder
1512
- const dialogRef = await this.layoutBuilder
1513
- .create()
1514
- .dialog(dialog => {
1515
- // Set basic dialog options
1516
- dialog
1517
- .setTitle(title)
1518
- .setSize(size)
1519
- .setCloseButton(closeButton)
1520
- .setContext(context);
1521
- // Set message if provided
1522
- if (message) {
1523
- dialog.setMessage(message);
1524
- }
1525
- // Set content if provided
1526
- if (content) {
1527
- // If content is already a flex-layout, use it directly
1528
- // Otherwise, wrap it in a flex container with column direction
1529
- if (content.type === 'flex-layout') {
1530
- // Set content layout directly (accessing internal state of DialogContainerBuilder)
1531
- dialog.contentLayout = content;
1532
- }
1533
- else {
1534
- // Wrap content in a flex container
1535
- const wrappedContent = {
1536
- type: 'flex-layout',
1537
- mode: 'edit',
1538
- options: {
1539
- flexDirection: 'column',
1540
- gap: '10px',
1541
- },
1542
- children: [content],
1543
- };
1544
- dialog.contentLayout = wrappedContent;
1545
- }
1546
- }
1547
- // Set actions if provided
1548
- if (actions) {
1549
- dialog.setActions(actionBuilder => {
1550
- if (actions.cancel) {
1551
- actionBuilder.cancel(actions.cancel);
1552
- }
1553
- if (actions.submit) {
1554
- actionBuilder.submit(actions.submit);
1555
- }
1556
- if (actions.custom) {
1557
- actions.custom.forEach(action => {
1558
- actionBuilder.custom(action);
1559
- });
1560
- }
1561
- });
1441
+ // Ensure taskToken is provided for secure resumption
1442
+ if (!taskToken) {
1443
+ throw new Error('Missing taskToken for resume operation');
1444
+ }
1445
+ // Backend handles everything: checks outcomeConnections and determines nextStep
1446
+ const response = await this.workflowEngine.resume({
1447
+ instanceId,
1448
+ stepId,
1449
+ taskToken,
1450
+ outcome,
1451
+ userInput,
1452
+ });
1453
+ // Update cache with state from backend
1454
+ let normalizedState = response.state ? { ...response.state } : undefined;
1455
+ if (normalizedState && normalizedState.lastUpdated && !(normalizedState.lastUpdated instanceof Date)) {
1456
+ normalizedState.lastUpdated = new Date(normalizedState.lastUpdated);
1457
+ }
1458
+ if (normalizedState) {
1459
+ this.stateCache.set(instanceId, normalizedState);
1460
+ }
1461
+ // 🎯 Interactive flow: Execute frontend activities that are NOT workflow-activity:human-task (those go to task board)
1462
+ let finalNextTask = response.nextTask || null;
1463
+ let finalOutput = response.output;
1464
+ const nextTask = response.nextTask;
1465
+ if (nextTask &&
1466
+ (nextTask.executionMode === 'frontend' || nextTask.executionMode === 'both') &&
1467
+ nextTask.activityType !== 'workflow-activity:human-task') {
1468
+ const interactiveResult = await this.executeInteractiveFlow(instanceId, nextTask, normalizedState, normalizedState?.activityOutputs);
1469
+ finalNextTask = interactiveResult.nextTask;
1470
+ normalizedState = interactiveResult.state;
1471
+ if (interactiveResult.output !== undefined) {
1472
+ finalOutput = interactiveResult.output;
1562
1473
  }
1563
- })
1564
- .show();
1565
- // Get user action and context
1566
- const action = dialogRef.action() || 'cancel';
1567
- const dialogContext = dialogRef.context();
1568
- // Determine outcomes
1569
- const cancelled = action === 'cancel';
1570
- const confirmed = action === 'submit' || (!cancelled && action !== 'error');
1571
- // Close dialog
1572
- dialogRef.close();
1573
- // Return result with appropriate outcome
1574
- const outcome = cancelled ? 'Cancelled' : confirmed ? 'Confirmed' : 'Done';
1575
- return this.createResult({
1576
- context: dialogContext,
1577
- action,
1578
- cancelled,
1579
- confirmed
1580
- }, outcome);
1581
- }
1582
- catch (err) {
1583
- console.error('[ShowDialogLayoutBuilder] Error showing dialog:', err);
1474
+ // Update cache with final state
1475
+ this.stateCache.set(instanceId, normalizedState);
1476
+ }
1477
+ // If backend returned null or non-executable task, return it as-is
1478
+ // Backend already decided workflow status (suspended, completed, etc.)
1479
+ return {
1480
+ success: true,
1481
+ instanceId,
1482
+ state: normalizedState || response.state,
1483
+ nextTask: finalNextTask,
1484
+ output: finalOutput,
1485
+ };
1486
+ }
1487
+ catch (error) {
1488
+ console.error('[AXPWorkflowManager] ❌ Error resuming workflow:', error);
1584
1489
  return {
1585
1490
  success: false,
1586
- message: {
1587
- text: err instanceof Error ? err.message : 'Failed to show dialog',
1588
- },
1589
- data: {
1590
- output: {
1591
- context: {},
1592
- action: 'error',
1593
- cancelled: true,
1594
- confirmed: false,
1595
- },
1596
- outcomes: {
1597
- Error: true,
1598
- },
1599
- },
1491
+ instanceId,
1492
+ error: error.message || 'Failed to resume workflow',
1600
1493
  };
1601
1494
  }
1602
1495
  }
1603
- }
1604
-
1605
- /**
1606
- * Show Toast Activity - Displays toast notification to user.
1607
- *
1608
- * Usage:
1609
- * ```typescript
1610
- * const toast = new ShowToast();
1611
- * await toast.execute({
1612
- * color: 'success',
1613
- * title: 'Success',
1614
- * message: 'Operation completed successfully!'
1615
- * });
1616
- * ```
1617
- */
1618
- class ShowToast extends Activity {
1619
- constructor() {
1620
- super('ShowToast');
1621
- this.toastService = inject(AXToastService);
1622
- this.translationService = inject(AXTranslationService);
1623
- }
1624
- async execute(input) {
1625
- const { color = 'primary', title = '', message = '', duration = 3000 } = input;
1626
- // Translate title and message only if they start with '@' (translation key)
1627
- // Otherwise use the text as-is
1628
- const translatedTitle = title
1629
- ? (title.startsWith('@')
1630
- ? await this.translationService.translateAsync(title) || title
1631
- : title)
1632
- : '';
1633
- const translatedMessage = message
1634
- ? (message.startsWith('@')
1635
- ? await this.translationService.translateAsync(message) || message
1636
- : message)
1637
- : '';
1496
+ /**
1497
+ * Get workflow instance state.
1498
+ *
1499
+ * Retrieves state from cache (if valid) or from backend.
1500
+ *
1501
+ * @param instanceId - Workflow instance ID
1502
+ * @returns Workflow instance state or null if not found
1503
+ */
1504
+ async getState(instanceId) {
1505
+ // Check cache first
1506
+ const cached = this.stateCache.get(instanceId);
1507
+ if (cached) {
1508
+ // Normalize lastUpdated to Date (for cache safety)
1509
+ const normalizedCached = { ...cached };
1510
+ if (normalizedCached.lastUpdated && !(normalizedCached.lastUpdated instanceof Date)) {
1511
+ normalizedCached.lastUpdated = new Date(normalizedCached.lastUpdated);
1512
+ }
1513
+ // Validate cache age
1514
+ const cacheAge = Date.now() - normalizedCached.lastUpdated.getTime();
1515
+ if (cacheAge < this.CACHE_TTL) {
1516
+ return normalizedCached;
1517
+ }
1518
+ }
1519
+ // Fetch from backend
1638
1520
  try {
1639
- await this.toastService.show({
1640
- color: color,
1641
- title: translatedTitle,
1642
- content: translatedMessage,
1643
- closeButton: true,
1644
- timeOut: duration,
1645
- timeOutProgress: true,
1521
+ const state = await this.workflowEngine.getState({
1522
+ instanceId,
1646
1523
  });
1647
- return this.createResult(undefined, 'Done');
1524
+ // Normalize lastUpdated to Date (for cache safety)
1525
+ const normalizedState = { ...state };
1526
+ if (normalizedState.lastUpdated && !(normalizedState.lastUpdated instanceof Date)) {
1527
+ normalizedState.lastUpdated = new Date(normalizedState.lastUpdated);
1528
+ }
1529
+ // Update cache
1530
+ this.stateCache.set(instanceId, normalizedState);
1531
+ return normalizedState;
1648
1532
  }
1649
- catch (err) {
1650
- console.error('[ShowToast] Error showing toast:', err);
1651
- return {
1652
- success: false,
1653
- message: {
1654
- text: err instanceof Error ? err.message : 'Failed to show toast',
1655
- },
1656
- data: {
1657
- output: undefined,
1658
- outcomes: {
1659
- Failed: true,
1660
- },
1661
- },
1662
- };
1533
+ catch (error) {
1534
+ console.error('[AXPWorkflowManager] Error getting workflow state:', error);
1535
+ return null;
1663
1536
  }
1664
1537
  }
1538
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowManager, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1539
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowManager, providedIn: 'root' }); }
1665
1540
  }
1541
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowManager, decorators: [{
1542
+ type: Injectable,
1543
+ args: [{
1544
+ providedIn: 'root',
1545
+ }]
1546
+ }] });
1666
1547
 
1667
- var showToast_activity = /*#__PURE__*/Object.freeze({
1668
- __proto__: null,
1669
- ShowToast: ShowToast
1548
+ const AXP_WORKFLOW_PROVIDER = new InjectionToken('AXP_WORKFLOW_PROVIDER', {
1549
+ factory: () => [],
1550
+ });
1551
+ const AXP_WORKFLOW_CATEGORY_PROVIDER = new InjectionToken('AXP_WORKFLOW_CATEGORY_PROVIDER', {
1552
+ factory: () => [],
1670
1553
  });
1671
1554
 
1555
+ //#endregion
1672
1556
  /**
1673
- * Navigate Activity - Navigates to different pages/routes.
1557
+ * Local engine implementation that manages workflow progression and state.
1674
1558
  *
1675
- * Usage:
1676
- * ```typescript
1677
- * const navigate = new Navigate();
1678
- * await navigate.execute({
1679
- * mode: 'route',
1680
- * route: '/users',
1681
- * params: { id: '123' }
1682
- * });
1683
- * ```
1559
+ * This engine:
1560
+ * - Returns frontend/both activities as pendingTask (does NOT execute them)
1561
+ * - Skips backend activities (does not error, continues execution)
1562
+ * - Maintains workflow state in memory
1563
+ * - Does not require backend API calls
1564
+ *
1565
+ * Execution of frontend tasks is handled by AXPWorkflowManager via ActivityExecutor.
1566
+ * This engine only manages workflow progression and state storage.
1567
+ *
1568
+ * This is the DEFAULT engine provider. Applications can override it with
1569
+ * an API-based engine implementation.
1684
1570
  */
1685
- class Navigate extends Activity {
1571
+ class AXPWorkflowLocalEngine {
1686
1572
  constructor() {
1687
- super('Navigate');
1688
- this.router = inject(Router);
1573
+ //#region ---- Services & Dependencies ----
1574
+ this.activityDefinitionService = inject(AXPActivityDefinitionService);
1575
+ this.workflowProviders = inject(AXP_WORKFLOW_PROVIDER, { optional: true }) || [];
1576
+ //#endregion
1577
+ //#region ---- Instance Storage ----
1578
+ /**
1579
+ * In-memory storage for workflow instances.
1580
+ * Key: instanceId
1581
+ * Value: LocalWorkflowState
1582
+ */
1583
+ this.instances = new Map();
1584
+ /**
1585
+ * Task token storage for secure resume operations.
1586
+ * Key: taskToken
1587
+ * Value: { instanceId, activityId }
1588
+ */
1589
+ this.taskTokens = new Map();
1689
1590
  }
1690
- async execute(input) {
1691
- const { mode = 'route', route = '', params = {}, queryParams = {}, entity = '', entityId = '', url = '' } = input;
1692
- try {
1693
- switch (mode) {
1694
- case 'route':
1695
- await this.router.navigate([route], { queryParams });
1696
- break;
1697
- case 'entity-details':
1698
- const detailsRoute = `/entities/${entity}/details/${entityId}`;
1699
- await this.router.navigate([detailsRoute], { queryParams });
1700
- break;
1701
- case 'entity-list':
1702
- const listRoute = `/entities/${entity}`;
1703
- await this.router.navigate([listRoute], { queryParams });
1704
- break;
1705
- case 'external':
1706
- if (url) {
1707
- window.open(url, '_blank');
1708
- }
1709
- break;
1710
- default:
1711
- console.error(`[Navigate] Unknown navigation mode: ${mode}`);
1712
- return {
1713
- success: false,
1714
- message: {
1715
- text: `Unknown navigation mode: ${mode}`,
1716
- },
1717
- data: {
1718
- output: undefined,
1719
- outcomes: {
1720
- Failed: true,
1721
- },
1722
- },
1723
- };
1724
- }
1725
- return this.createResult(undefined, 'Done');
1726
- }
1727
- catch (err) {
1728
- console.error('[Navigate] Error navigating:', err);
1729
- return {
1730
- success: false,
1731
- message: {
1732
- text: err instanceof Error ? err.message : 'Failed to navigate',
1733
- },
1734
- data: {
1735
- output: undefined,
1736
- outcomes: {
1737
- Failed: true,
1738
- },
1739
- },
1591
+ //#endregion
1592
+ //#region ---- Public Methods (AXPWorkflowEngine) ----
1593
+ /**
1594
+ * Start a new workflow instance.
1595
+ *
1596
+ * Creates an in-memory workflow instance and progresses it.
1597
+ * Frontend/both activities are returned as pendingTask for external execution.
1598
+ * Backend activities are skipped.
1599
+ */
1600
+ async start(request) {
1601
+ console.log(`[WorkflowLocalEngine] 🚀 Starting workflow: ${request.workflowId}`, request);
1602
+ // Generate instance ID
1603
+ const instanceId = AXPDataGenerator.uuid();
1604
+ const now = new Date();
1605
+ // Load workflow definition
1606
+ console.log(`[WorkflowLocalEngine] 📥 Loading workflow definition: ${request.workflowId}`);
1607
+ const definition = await this.getDefinition(request.workflowId);
1608
+ if (!definition) {
1609
+ console.error(`[WorkflowLocalEngine] ❌ Workflow definition not found: ${request.workflowId}`);
1610
+ throw new Error(`Workflow definition not found: ${request.workflowId}`);
1611
+ }
1612
+ console.log(`[WorkflowLocalEngine] Definition loaded:`, {
1613
+ name: definition.name,
1614
+ activitiesCount: definition.graph?.activities?.length || 0,
1615
+ connectionsCount: definition.graph?.connections?.length || 0,
1616
+ });
1617
+ // Initialize workflow state
1618
+ const state = {
1619
+ instanceId,
1620
+ workflowId: request.workflowId,
1621
+ status: 'running',
1622
+ variables: {},
1623
+ activityOutputs: {},
1624
+ lastActivityOutput: undefined,
1625
+ input: request.input || {},
1626
+ output: undefined,
1627
+ lastUpdated: now,
1628
+ };
1629
+ // Create local state
1630
+ const localState = {
1631
+ instanceId,
1632
+ workflowId: request.workflowId,
1633
+ definition,
1634
+ state,
1635
+ completedActivities: new Set(),
1636
+ activityResults: new Map(),
1637
+ };
1638
+ // Store instance
1639
+ this.instances.set(instanceId, localState);
1640
+ // Execute workflow steps
1641
+ console.log(`[WorkflowLocalEngine] ⚙️ Executing workflow steps...`);
1642
+ const pendingTask = await this.executeWorkflowSteps(localState);
1643
+ // Update state
1644
+ localState.state.lastUpdated = new Date();
1645
+ console.log(`[WorkflowLocalEngine] ✅ Workflow started:`, {
1646
+ instanceId,
1647
+ status: localState.state.status,
1648
+ hasPendingTask: !!pendingTask,
1649
+ pendingTaskType: pendingTask?.activityType,
1650
+ });
1651
+ return {
1652
+ instanceId,
1653
+ state: localState.state,
1654
+ pendingTask: pendingTask || null,
1655
+ activityOutputs: localState.state.activityOutputs,
1656
+ lastActivityOutput: localState.state.lastActivityOutput,
1657
+ };
1658
+ }
1659
+ /**
1660
+ * Resume a suspended workflow instance.
1661
+ *
1662
+ * Validates task token, applies externally executed result,
1663
+ * and continues progressing workflow steps.
1664
+ */
1665
+ async resume(request) {
1666
+ // Validate task token
1667
+ const tokenInfo = this.taskTokens.get(request.taskToken);
1668
+ if (!tokenInfo || tokenInfo.instanceId !== request.instanceId || tokenInfo.activityId !== request.stepId) {
1669
+ throw new Error('Invalid task token');
1670
+ }
1671
+ // Get instance
1672
+ const localState = this.instances.get(request.instanceId);
1673
+ if (!localState) {
1674
+ throw new Error(`Workflow instance not found: ${request.instanceId}`);
1675
+ }
1676
+ // Store activity result (from external execution)
1677
+ const outcome = request.outcome ?? 'Done';
1678
+ localState.activityResults.set(request.stepId, {
1679
+ output: request.userInput || {},
1680
+ outcome,
1681
+ });
1682
+ localState.completedActivities.add(request.stepId);
1683
+ // Store activity output for expression evaluation
1684
+ localState.state.activityOutputs = {
1685
+ ...(localState.state.activityOutputs || {}),
1686
+ [request.stepId]: request.userInput || {},
1687
+ };
1688
+ localState.state.lastActivityOutput = request.userInput || {};
1689
+ // Merge output/userInput into state variables
1690
+ if (request.userInput) {
1691
+ localState.state.variables = {
1692
+ ...localState.state.variables,
1693
+ ...(request.userInput || {}),
1740
1694
  };
1741
1695
  }
1696
+ // Mark activity as completed and continue progression
1697
+ // Continue progressing workflow steps (skipping backend activities)
1698
+ const nextTask = await this.executeWorkflowSteps(localState);
1699
+ // Update state
1700
+ localState.state.lastUpdated = new Date();
1701
+ // Determine final status
1702
+ if (!nextTask && localState.state.status === 'running') {
1703
+ localState.state.status = 'completed';
1704
+ localState.state.output = localState.state.variables;
1705
+ }
1706
+ return {
1707
+ output: request.userInput || {},
1708
+ outcomes: { [outcome]: true },
1709
+ state: localState.state,
1710
+ nextTask: nextTask || null,
1711
+ };
1742
1712
  }
1743
- }
1744
-
1745
- var navigate_activity = /*#__PURE__*/Object.freeze({
1746
- __proto__: null,
1747
- Navigate: Navigate
1748
- });
1749
-
1750
- /**
1751
- * Set Variable Activity - Sets a variable value in workflow context.
1752
- *
1753
- * Usage:
1754
- * ```typescript
1755
- * const setVar = new SetVariable();
1756
- * await setVar.execute({
1757
- * variableName: 'userId',
1758
- * value: '12345'
1759
- * });
1760
- * ```
1761
- */
1762
- class SetVariable extends Activity {
1763
- constructor() {
1764
- super('workflow-activity:set-variable', 'Set Variable');
1713
+ /**
1714
+ * Get current workflow instance state.
1715
+ */
1716
+ async getState(request) {
1717
+ const localState = this.instances.get(request.instanceId);
1718
+ if (!localState) {
1719
+ throw new Error(`Workflow instance not found: ${request.instanceId}`);
1720
+ }
1721
+ // Normalize lastUpdated to Date (for cache safety)
1722
+ const state = { ...localState.state };
1723
+ if (state.lastUpdated && !(state.lastUpdated instanceof Date)) {
1724
+ state.lastUpdated = new Date(state.lastUpdated);
1725
+ }
1726
+ return state;
1765
1727
  }
1766
- async execute(input) {
1767
- const { variableName, value } = input;
1768
- try {
1769
- // In a real implementation, this would set the variable in workflow context
1770
- // For now, we'll just log it
1771
- console.log(`[SetVariable] Setting ${variableName} = ${JSON.stringify(value)}`);
1772
- return this.createResult(undefined, 'Done');
1728
+ //#endregion
1729
+ //#region ---- Private Methods ----
1730
+ /**
1731
+ * Get workflow definition from available providers.
1732
+ */
1733
+ async getDefinition(workflowId) {
1734
+ // Try all providers in order
1735
+ const resolvedProviders = await Promise.allSettled(this.workflowProviders);
1736
+ for (const p of resolvedProviders) {
1737
+ if (p.status === 'fulfilled' && p.value) {
1738
+ const provider = p.value;
1739
+ // Check if provider has getByName method
1740
+ if (typeof provider.getByName === 'function') {
1741
+ try {
1742
+ const definition = await provider.getByName(workflowId);
1743
+ if (definition) {
1744
+ return definition;
1745
+ }
1746
+ }
1747
+ catch {
1748
+ // Continue on error - try other providers
1749
+ }
1750
+ }
1751
+ }
1773
1752
  }
1774
- catch (err) {
1775
- console.error('[SetVariable] Error setting variable:', err);
1776
- return {
1777
- success: false,
1778
- message: {
1779
- text: err instanceof Error ? err.message : 'Failed to set variable',
1780
- },
1781
- data: {
1782
- output: undefined,
1783
- outcomes: {
1784
- Failed: true,
1785
- },
1786
- },
1787
- };
1753
+ return null;
1754
+ }
1755
+ /**
1756
+ * Progress workflow steps starting from the current position.
1757
+ *
1758
+ * For frontend/both activities: returns task immediately (suspends workflow).
1759
+ * For backend activities: skips and continues.
1760
+ *
1761
+ * Returns the next pending task (if frontend activity found) or null (if completed).
1762
+ */
1763
+ async executeWorkflowSteps(localState) {
1764
+ const graph = localState.definition.graph;
1765
+ const activities = graph.activities || [];
1766
+ const connections = graph.connections || [];
1767
+ // Build activity map
1768
+ const activityMap = new Map();
1769
+ activities.forEach(activity => {
1770
+ activityMap.set(activity.id, activity);
1771
+ });
1772
+ // Build connection graph
1773
+ const outgoingConnections = new Map();
1774
+ const incomingConnections = new Map();
1775
+ connections.forEach(conn => {
1776
+ const sourceId = conn.source.activtyName;
1777
+ const targetId = conn.target.activtyName;
1778
+ if (!outgoingConnections.has(sourceId)) {
1779
+ outgoingConnections.set(sourceId, []);
1780
+ }
1781
+ outgoingConnections.get(sourceId).push(conn);
1782
+ if (!incomingConnections.has(targetId)) {
1783
+ incomingConnections.set(targetId, []);
1784
+ }
1785
+ incomingConnections.get(targetId).push(conn);
1786
+ });
1787
+ // Find starting activity (use startActivityId or no incoming connections, or first activity)
1788
+ let currentActivityId = localState.currentActivityId;
1789
+ if (!currentActivityId) {
1790
+ // Use startActivityId if available
1791
+ if (graph.startActivityId) {
1792
+ currentActivityId = graph.startActivityId;
1793
+ }
1794
+ else {
1795
+ // Find root activity (no incoming connections)
1796
+ for (const activity of activities) {
1797
+ if (!incomingConnections.has(activity.id)) {
1798
+ currentActivityId = activity.id;
1799
+ break;
1800
+ }
1801
+ }
1802
+ // If no root found, use first activity
1803
+ if (!currentActivityId && activities.length > 0) {
1804
+ currentActivityId = activities[0].id;
1805
+ }
1806
+ }
1788
1807
  }
1808
+ // Execute workflow steps
1809
+ while (currentActivityId) {
1810
+ const activity = activityMap.get(currentActivityId);
1811
+ if (!activity) {
1812
+ break;
1813
+ }
1814
+ // Skip if already completed
1815
+ if (localState.completedActivities.has(currentActivityId)) {
1816
+ // Move to next activity
1817
+ const nextId = this.getNextActivityId(currentActivityId, outgoingConnections, localState.activityResults);
1818
+ currentActivityId = nextId ?? undefined;
1819
+ continue;
1820
+ }
1821
+ // Get activity definition to retrieve executionMode and title
1822
+ console.log(`[WorkflowLocalEngine] 🔍 Getting activity definition for: ${activity.name}`);
1823
+ const activityDefinition = await this.activityDefinitionService.getActivityByName(activity.name);
1824
+ console.log(`[WorkflowLocalEngine] 📋 Activity definition:`, {
1825
+ name: activityDefinition?.name,
1826
+ type: activityDefinition?.type,
1827
+ executionMode: activityDefinition?.executionMode,
1828
+ title: activityDefinition?.title,
1829
+ found: !!activityDefinition,
1830
+ });
1831
+ const executionMode = activityDefinition?.executionMode || 'frontend';
1832
+ const activityTitle = activityDefinition?.title;
1833
+ // Handle backend activities: skip
1834
+ if (executionMode === 'backend') {
1835
+ console.log(`[WorkflowLocalEngine] ⏭️ Skipping backend activity: ${activity.name} (${activity.id})`);
1836
+ localState.completedActivities.add(currentActivityId);
1837
+ localState.activityResults.set(currentActivityId, {
1838
+ output: null,
1839
+ outcome: 'Done',
1840
+ });
1841
+ // Move to next activity
1842
+ const nextId = this.getNextActivityId(currentActivityId, outgoingConnections, localState.activityResults);
1843
+ currentActivityId = nextId ?? undefined;
1844
+ continue;
1845
+ }
1846
+ // Handle frontend/both activities: return as pendingTask (do NOT execute)
1847
+ if (executionMode === 'frontend' || executionMode === 'both') {
1848
+ // Create task for external execution
1849
+ // Note: Expression evaluation will be done by ActivityExecutor
1850
+ const task = {
1851
+ taskToken: AXPDataGenerator.uuid(),
1852
+ activityId: activity.id,
1853
+ activityType: activity.name,
1854
+ activityName: activityTitle || undefined,
1855
+ executionMode: executionMode,
1856
+ input: activity.inputs || {}, // Pass raw inputs - executor will evaluate
1857
+ };
1858
+ // Store task token for secure resume
1859
+ this.taskTokens.set(task.taskToken, {
1860
+ instanceId: localState.instanceId,
1861
+ activityId: activity.id,
1862
+ });
1863
+ // Update state to indicate suspension
1864
+ localState.currentActivityId = currentActivityId;
1865
+ localState.state.status = 'suspended';
1866
+ localState.state.currentStepId = currentActivityId;
1867
+ // Return task immediately - AXPWorkflowManager will execute it
1868
+ return task;
1869
+ }
1870
+ // Move to next activity
1871
+ const nextId = this.getNextActivityId(currentActivityId, outgoingConnections, localState.activityResults);
1872
+ currentActivityId = nextId ?? undefined;
1873
+ }
1874
+ // Workflow completed
1875
+ localState.state.status = 'completed';
1876
+ localState.state.output = localState.state.variables;
1877
+ localState.currentActivityId = undefined;
1878
+ return null;
1789
1879
  }
1790
- }
1791
-
1792
- var setVariable_activity = /*#__PURE__*/Object.freeze({
1793
- __proto__: null,
1794
- SetVariable: SetVariable
1795
- });
1796
-
1797
- /**
1798
- * Dispatch Event Activity - Dispatches an event to the system.
1799
- *
1800
- * Usage:
1801
- * ```typescript
1802
- * const dispatch = new DispatchEvent();
1803
- * await dispatch.execute({
1804
- * eventName: 'user-created',
1805
- * eventData: { userId: '123', name: 'John' }
1806
- * });
1807
- * ```
1808
- */
1809
- class DispatchEvent extends Activity {
1810
- constructor() {
1811
- super('DispatchEvent');
1880
+ /**
1881
+ * Get next activity ID based on connections and outcomes.
1882
+ */
1883
+ getNextActivityId(currentActivityId, outgoingConnections, activityResults) {
1884
+ const connections = outgoingConnections.get(currentActivityId) || [];
1885
+ if (connections.length === 0) {
1886
+ return null; // No outgoing connections - workflow ends
1887
+ }
1888
+ // Get current activity result
1889
+ const result = activityResults.get(currentActivityId);
1890
+ const outcome = result?.outcome || 'Done';
1891
+ // Find connection matching outcome
1892
+ // Outcome matching is typically done via port name (e.g., "Done", "Failed")
1893
+ for (const conn of connections) {
1894
+ const sourcePort = conn.source.port || 'Done';
1895
+ if (sourcePort === outcome) {
1896
+ return conn.target.activtyName;
1897
+ }
1898
+ }
1899
+ // If no matching outcome, use first connection (default path)
1900
+ if (connections.length > 0) {
1901
+ return connections[0].target.activtyName;
1902
+ }
1903
+ return null;
1812
1904
  }
1813
- async execute(input) {
1814
- const { eventName, eventData } = input;
1815
- try {
1816
- // In a real implementation, this would dispatch the event through an event bus
1817
- // For now, we'll just log it
1818
- console.log(`[DispatchEvent] Dispatching event: ${eventName}`, eventData);
1819
- return this.createResult(undefined, 'Done');
1905
+ async frontActivtyComplete(request) {
1906
+ const localState = this.instances.get(request.instanceId);
1907
+ if (!localState) {
1908
+ throw new Error(`Workflow instance not found: ${request.instanceId}`);
1820
1909
  }
1821
- catch (err) {
1822
- console.error('[DispatchEvent] Error dispatching event:', err);
1823
- return {
1824
- success: false,
1825
- message: {
1826
- text: err instanceof Error ? err.message : 'Failed to dispatch event',
1827
- },
1828
- data: {
1829
- output: undefined,
1830
- outcomes: {
1831
- Failed: true,
1832
- },
1833
- },
1834
- };
1910
+ // Store activity result
1911
+ const outcome = request.outcome ?? 'Done';
1912
+ localState.activityResults.set(request.activityNode, {
1913
+ output: request.output || {},
1914
+ outcome,
1915
+ });
1916
+ localState.completedActivities.add(request.activityNode);
1917
+ // Store outputs for expression evaluation
1918
+ localState.state.activityOutputs = {
1919
+ ...(localState.state.activityOutputs || {}),
1920
+ [request.activityNode]: request.output || {},
1921
+ };
1922
+ localState.state.lastActivityOutput = request.output || {};
1923
+ // Merge output into workflow variables
1924
+ localState.state.variables = {
1925
+ ...localState.state.variables,
1926
+ ...(request.output || {}),
1927
+ };
1928
+ // Continue workflow progression
1929
+ const nextTask = await this.executeWorkflowSteps(localState);
1930
+ // Update state timestamp
1931
+ localState.state.lastUpdated = new Date();
1932
+ // Determine final status
1933
+ if (!nextTask && localState.state.status === 'running') {
1934
+ localState.state.status = 'completed';
1935
+ localState.state.output = localState.state.variables;
1835
1936
  }
1937
+ return {
1938
+ output: request.output || {},
1939
+ outcomes: { [outcome]: true },
1940
+ nextTask: nextTask || null,
1941
+ state: localState.state,
1942
+ };
1836
1943
  }
1944
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowLocalEngine, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1945
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowLocalEngine }); }
1837
1946
  }
1947
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowLocalEngine, decorators: [{
1948
+ type: Injectable
1949
+ }] });
1838
1950
 
1839
- var dispatchEvent_activity = /*#__PURE__*/Object.freeze({
1840
- __proto__: null,
1841
- DispatchEvent: DispatchEvent
1842
- });
1951
+ // Workflow Runtime Services
1843
1952
 
1953
+ //#region ---- Imports ----
1954
+ //#endregion
1844
1955
  /**
1845
- * If Activity - Conditional execution based on a condition.
1956
+ * Optimized Workflow Definition Service
1846
1957
  *
1847
- * Usage:
1848
- * ```typescript
1849
- * const ifActivity = new If();
1850
- * ifActivity.condition = '{{user.isAdmin}}';
1851
- * ifActivity.thenActivities = [activity1, activity2];
1852
- * ifActivity.elseActivities = [activity3];
1853
- * ```
1958
+ * Manages workflow definitions (metadata) for UI and tooling.
1959
+ * Similar to AXPActivityDefinitionService - only handles metadata, not execution.
1960
+ *
1961
+ * Performance optimizations:
1962
+ * 1. Uses childrenCount to determine if category has children (no query needed)
1963
+ * 2. Uses itemsCount to determine if category has workflows (no query needed)
1964
+ * 3. Aggressive caching prevents duplicate API calls
1965
+ * 4. Single pending request per resource prevents race conditions
1966
+ * 5. Lazy loading - only loads data when needed
1854
1967
  */
1855
- class If extends Activity {
1968
+ class AXPWorkflowDefinitionService {
1856
1969
  constructor() {
1857
- super('If');
1970
+ //#region ---- Providers & Caches ----
1971
+ this.categoryProviders = inject(AXP_WORKFLOW_CATEGORY_PROVIDER, { optional: true }) || [];
1972
+ this.workflowProviders = inject(AXP_WORKFLOW_PROVIDER, { optional: true }) || [];
1973
+ //#endregion
1974
+ //#region ---- Cache Storage ----
1975
+ /** Cache for categories by id - O(1) lookup */
1976
+ this.categoriesById = new Map();
1977
+ /** Cache for categories by parentId - O(1) lookup */
1978
+ this.categoriesByParentId = new Map();
1979
+ /** Cache for workflow definitions by categoryId - O(1) lookup */
1980
+ this.workflowsByCategory = new Map();
1981
+ /** Cache for individual workflow definitions by name - O(1) lookup */
1982
+ this.workflowsByName = new Map();
1983
+ /** Track which provider index owns each category (by category ID) */
1984
+ this.categoryOwnership = new Map(); // Maps categoryId → provider index
1985
+ /** Pending API requests to prevent duplicate calls */
1986
+ this.pendingCategoriesRequests = new Map();
1987
+ this.pendingWorkflowsRequests = new Map();
1988
+ this.pendingWorkflowRequests = new Map();
1858
1989
  }
1859
- async execute(input) {
1860
- const { condition, thenActivities = [], elseActivities = [] } = input;
1861
- try {
1862
- // Evaluate condition (simplified - in real implementation, use expression evaluator)
1863
- const conditionResult = this.evaluateCondition(condition);
1864
- let result;
1865
- let activities;
1866
- if (conditionResult) {
1867
- activities = thenActivities;
1868
- result = { branch: 'then' };
1990
+ //#endregion
1991
+ //#region ---- Public API: Categories ----
1992
+ /**
1993
+ * Get categories by parentId with aggressive caching
1994
+ *
1995
+ * Optimization: Returns cached result immediately if available,
1996
+ * preventing unnecessary API calls during navigation
1997
+ *
1998
+ * @param parentId - Parent category ID (undefined = root categories)
1999
+ * @returns Array of categories with count metadata (childrenCount, itemsCount)
2000
+ */
2001
+ async getCategories(parentId) {
2002
+ // ✅ Fast path: Return cached result
2003
+ if (this.categoriesByParentId.has(parentId)) {
2004
+ return this.categoriesByParentId.get(parentId);
2005
+ }
2006
+ // ✅ Prevent duplicate requests: Return pending promise
2007
+ if (this.pendingCategoriesRequests.has(parentId)) {
2008
+ return this.pendingCategoriesRequests.get(parentId);
2009
+ }
2010
+ // ✅ Create single request and cache it
2011
+ const requestPromise = this.loadCategoriesFromProviders(parentId);
2012
+ this.pendingCategoriesRequests.set(parentId, requestPromise);
2013
+ return requestPromise;
2014
+ }
2015
+ /**
2016
+ * Get single category by ID with O(1) lookup
2017
+ *
2018
+ * Optimization: Uses Map for instant retrieval, falls back to
2019
+ * searching cache, then providers if not found
2020
+ */
2021
+ async getCategoryById(categoryId) {
2022
+ // ✅ Fast path: O(1) lookup in cache
2023
+ if (this.categoriesById.has(categoryId)) {
2024
+ return this.categoriesById.get(categoryId);
2025
+ }
2026
+ // ✅ Search in cached parent-child lists
2027
+ for (const categories of this.categoriesByParentId.values()) {
2028
+ const found = categories.find(cat => cat.id === categoryId);
2029
+ if (found) {
2030
+ this.categoriesById.set(categoryId, found);
2031
+ return found;
1869
2032
  }
1870
- else {
1871
- activities = elseActivities;
1872
- result = { branch: 'else' };
2033
+ }
2034
+ // Load root categories if not loaded
2035
+ if (!this.categoriesByParentId.has(undefined)) {
2036
+ await this.getCategories();
2037
+ if (this.categoriesById.has(categoryId)) {
2038
+ return this.categoriesById.get(categoryId);
1873
2039
  }
1874
- // Execute activities in the chosen branch
1875
- for (const activity of activities) {
1876
- const activityResult = await activity.execute(input);
1877
- if (!activityResult.success) {
1878
- return {
1879
- success: false,
1880
- message: activityResult.message,
1881
- data: {
1882
- output: { branch: conditionResult ? 'then' : 'else', failedActivity: activity },
1883
- outcomes: {
1884
- Failed: true,
1885
- },
1886
- },
1887
- };
1888
- }
2040
+ }
2041
+ // Breadth-first search through hierarchy
2042
+ return this.searchCategoryInHierarchy(categoryId);
2043
+ }
2044
+ /**
2045
+ * Get category path from root to specified category
2046
+ *
2047
+ * Optimization: Builds path using cached categories only
2048
+ */
2049
+ async getCategoriesPathById(categoryId) {
2050
+ const path = [];
2051
+ let currentCategoryId = categoryId;
2052
+ while (currentCategoryId) {
2053
+ const category = await this.getCategoryById(currentCategoryId);
2054
+ if (!category) {
2055
+ throw new Error(`Category '${currentCategoryId}' not found`);
1889
2056
  }
1890
- return this.createResult(result, conditionResult ? 'Then' : 'Else');
2057
+ path.unshift(category);
2058
+ currentCategoryId = category.parentId;
1891
2059
  }
1892
- catch (err) {
1893
- console.error('[If] Error evaluating condition:', err);
1894
- return {
1895
- success: false,
1896
- message: {
1897
- text: err instanceof Error ? err.message : 'Failed to evaluate condition',
1898
- },
1899
- data: {
1900
- output: { branch: 'error' },
1901
- outcomes: {
1902
- Failed: true,
1903
- },
1904
- },
1905
- };
2060
+ return path;
2061
+ }
2062
+ //#endregion
2063
+ //#region ---- Public API: Workflow Definitions ----
2064
+ /**
2065
+ * Get workflow definitions for a category with smart caching
2066
+ *
2067
+ * Optimization: Checks itemsCount before querying
2068
+ * - If itemsCount = 0, returns empty array (no API call)
2069
+ * - If itemsCount > 0, loads and caches workflow definitions
2070
+ * - Returns cached result on subsequent calls
2071
+ *
2072
+ * @param categoryId - Category ID to get workflow definitions from
2073
+ * @returns Array of workflow definitions
2074
+ */
2075
+ async getWorkflowsByCategoryId(categoryId) {
2076
+ // ✅ Fast path: Return cached result
2077
+ if (this.workflowsByCategory.has(categoryId)) {
2078
+ return this.workflowsByCategory.get(categoryId);
2079
+ }
2080
+ // ✅ Smart optimization: Check itemsCount before querying
2081
+ const category = await this.getCategoryById(categoryId);
2082
+ if (category && category.itemsCount !== undefined && category.itemsCount === 0) {
2083
+ // Category has no workflows - cache empty array and skip API call
2084
+ const emptyArray = [];
2085
+ this.workflowsByCategory.set(categoryId, emptyArray);
2086
+ return emptyArray;
2087
+ }
2088
+ // ✅ Prevent duplicate requests
2089
+ if (this.pendingWorkflowsRequests.has(categoryId)) {
2090
+ return this.pendingWorkflowsRequests.get(categoryId);
2091
+ }
2092
+ // ✅ Load from providers
2093
+ const requestPromise = this.loadWorkflowsFromProviders(categoryId);
2094
+ this.pendingWorkflowsRequests.set(categoryId, requestPromise);
2095
+ return requestPromise;
2096
+ }
2097
+ /**
2098
+ * Get single workflow definition by name with O(1) lookup
2099
+ *
2100
+ * Optimization: Uses Map for instant retrieval
2101
+ *
2102
+ * @param name - Workflow name (unique identifier)
2103
+ * @returns Workflow definition or undefined if not found
2104
+ */
2105
+ async getWorkflowByName(name) {
2106
+ // ✅ Fast path: O(1) lookup in cache
2107
+ if (this.workflowsByName.has(name)) {
2108
+ return this.workflowsByName.get(name);
2109
+ }
2110
+ // ✅ Prevent duplicate requests
2111
+ if (this.pendingWorkflowRequests.has(name)) {
2112
+ return this.pendingWorkflowRequests.get(name);
1906
2113
  }
2114
+ // ✅ Load from providers
2115
+ const requestPromise = this.loadWorkflowFromProviders(name);
2116
+ this.pendingWorkflowRequests.set(name, requestPromise);
2117
+ return requestPromise;
1907
2118
  }
1908
- evaluateCondition(condition) {
1909
- if (typeof condition === 'boolean') {
1910
- return condition;
2119
+ /**
2120
+ * Get category ID containing a specific workflow definition
2121
+ *
2122
+ * Optimization: Searches cache first, loads on-demand if needed
2123
+ */
2124
+ async getCategoryIdByWorkflowName(workflowName) {
2125
+ // ✅ Search in cached workflow definitions
2126
+ for (const [categoryId, definitions] of this.workflowsByCategory.entries()) {
2127
+ if (definitions.some(def => def.name === workflowName)) {
2128
+ return categoryId;
2129
+ }
1911
2130
  }
1912
- if (typeof condition === 'string') {
1913
- // Simple evaluation - in real implementation, use proper expression evaluator
1914
- return condition === 'true' || condition === '1' || condition === 'yes';
2131
+ // Try loading the workflow definition to find its category
2132
+ // Note: AXPWorkflowDefinition doesn't have category field
2133
+ // Category is managed separately through category providers
2134
+ // This method searches through cached categories
2135
+ const definition = await this.getWorkflowByName(workflowName);
2136
+ if (definition) {
2137
+ // Search through all categories to find which one contains this workflow
2138
+ const categories = await this.getCategories();
2139
+ for (const category of categories) {
2140
+ const workflows = await this.getWorkflowsByCategoryId(category.id);
2141
+ if (workflows.some(w => w.name === workflowName)) {
2142
+ return category.id;
2143
+ }
2144
+ }
1915
2145
  }
1916
- return false;
2146
+ return undefined;
1917
2147
  }
1918
- }
1919
-
1920
- var if_activity = /*#__PURE__*/Object.freeze({
1921
- __proto__: null,
1922
- If: If
1923
- });
1924
-
1925
- /**
1926
- * While Activity - Loop execution while condition is true.
1927
- *
1928
- * Usage:
1929
- * ```typescript
1930
- * const whileActivity = new While();
1931
- * whileActivity.condition = '{{counter < 10}}';
1932
- * whileActivity.activities = [incrementActivity, logActivity];
1933
- * ```
1934
- */
1935
- class While extends Activity {
1936
- constructor() {
1937
- super('While');
2148
+ /**
2149
+ * Get category path for a workflow
2150
+ */
2151
+ async getCategoriesPathByWorkflowName(workflowName) {
2152
+ const categoryId = await this.getCategoryIdByWorkflowName(workflowName);
2153
+ if (!categoryId) {
2154
+ throw new Error(`Workflow '${workflowName}' not found in any category`);
2155
+ }
2156
+ return this.getCategoriesPathById(categoryId);
1938
2157
  }
1939
- async execute(input) {
1940
- const { condition, activities = [], maxIterations = 1000 } = input;
2158
+ //#endregion
2159
+ //#region ---- Private: Data Loading ----
2160
+ /**
2161
+ * Load categories from providers and cache results
2162
+ *
2163
+ * Optimization: Tracks provider ownership to avoid unnecessary API calls
2164
+ * - For root (parentId = undefined): Query ALL providers
2165
+ * - For children: Only query the provider that owns the parent
2166
+ */
2167
+ async loadCategoriesFromProviders(parentId) {
1941
2168
  try {
1942
- let iteration = 0;
1943
- let conditionResult = this.evaluateCondition(condition);
1944
- while (conditionResult && iteration < maxIterations) {
1945
- // Execute activities in the loop
1946
- for (const activity of activities) {
1947
- const activityResult = await activity.execute(input);
1948
- if (!activityResult.success) {
1949
- return {
1950
- success: false,
1951
- message: activityResult.message,
1952
- data: {
1953
- output: {
1954
- iterations: iteration,
1955
- completed: false,
1956
- },
1957
- outcomes: {
1958
- Failed: true,
1959
- },
1960
- },
1961
- };
2169
+ const resolvedProviders = await Promise.allSettled(this.categoryProviders);
2170
+ const categories = [];
2171
+ // Determine which provider(s) to query
2172
+ const providerIndicesToQuery = parentId
2173
+ ? this.getProviderIndexForCategory(parentId)
2174
+ : null; // Root: query all providers
2175
+ for (let i = 0; i < resolvedProviders.length; i++) {
2176
+ const p = resolvedProviders[i];
2177
+ // Skip if we have a specific provider index and this isn't it
2178
+ if (providerIndicesToQuery !== null && !providerIndicesToQuery.includes(i)) {
2179
+ continue;
2180
+ }
2181
+ if (p.status === 'fulfilled' && p.value && typeof p.value.getList === 'function') {
2182
+ try {
2183
+ const cats = await p.value.getList(parentId);
2184
+ if (Array.isArray(cats) && cats.length > 0) {
2185
+ categories.push(...cats);
2186
+ // ✅ Track ownership: This provider INDEX owns these categories
2187
+ cats.forEach(cat => this.categoryOwnership.set(cat.id, i));
2188
+ }
2189
+ }
2190
+ catch {
2191
+ // Continue on error - try other providers
1962
2192
  }
1963
2193
  }
1964
- iteration++;
1965
- conditionResult = this.evaluateCondition(condition);
1966
2194
  }
1967
- const result = {
1968
- iterations: iteration,
1969
- completed: iteration < maxIterations
1970
- };
1971
- return this.createResult(result, 'Done');
2195
+ // Cache results for fast subsequent access
2196
+ this.categoriesByParentId.set(parentId, categories);
2197
+ categories.forEach(cat => this.categoriesById.set(cat.id, cat));
2198
+ return categories;
1972
2199
  }
1973
- catch (err) {
1974
- console.error('[While] Error in loop execution:', err);
1975
- return {
1976
- success: false,
1977
- message: {
1978
- text: err instanceof Error ? err.message : 'Failed during loop execution',
1979
- },
1980
- data: {
1981
- output: { iterations: 0, completed: false },
1982
- outcomes: {
1983
- Failed: true,
1984
- },
1985
- },
1986
- };
2200
+ finally {
2201
+ this.pendingCategoriesRequests.delete(parentId);
1987
2202
  }
1988
2203
  }
1989
- evaluateCondition(condition) {
1990
- if (typeof condition === 'boolean') {
1991
- return condition;
1992
- }
1993
- if (typeof condition === 'string') {
1994
- // Simple evaluation - in real implementation, use proper expression evaluator
1995
- return condition === 'true' || condition === '1' || condition === 'yes';
2204
+ /**
2205
+ * Get the provider index that owns a specific category
2206
+ *
2207
+ * @returns Array with provider index, or null if ownership unknown (query all)
2208
+ */
2209
+ getProviderIndexForCategory(categoryId) {
2210
+ const ownerIndex = this.categoryOwnership.get(categoryId);
2211
+ if (ownerIndex !== undefined) {
2212
+ return [ownerIndex];
1996
2213
  }
1997
- return false;
1998
- }
1999
- }
2000
-
2001
- var while_activity = /*#__PURE__*/Object.freeze({
2002
- __proto__: null,
2003
- While: While
2004
- });
2005
-
2006
- /**
2007
- * ForEach Activity - Iterates over a collection of items.
2008
- *
2009
- * Usage:
2010
- * ```typescript
2011
- * const forEach = new ForEach();
2012
- * await forEach.execute({
2013
- * items: ['item1', 'item2', 'item3'],
2014
- * activities: [processItemActivity]
2015
- * });
2016
- * ```
2017
- */
2018
- class ForEach extends Activity {
2019
- constructor() {
2020
- super('ForEach');
2214
+ // Ownership unknown - will query all providers (fallback)
2215
+ return null;
2021
2216
  }
2022
- async execute(input) {
2023
- const { items = [], activities = [], itemVariableName = 'currentItem', indexVariableName = 'currentIndex' } = input;
2217
+ /**
2218
+ * Load workflow definitions from providers and cache results
2219
+ *
2220
+ * Optimization: Only queries the provider that owns the category
2221
+ * Uses provider INDEX to match category provider with workflow provider
2222
+ */
2223
+ async loadWorkflowsFromProviders(categoryId) {
2024
2224
  try {
2025
- const results = [];
2026
- for (let index = 0; index < items.length; index++) {
2027
- const currentItem = items[index];
2028
- // Create context with current item and index
2029
- const itemContext = {
2030
- ...input,
2031
- [itemVariableName]: currentItem,
2032
- [indexVariableName]: index
2033
- };
2034
- // Execute activities for current item
2035
- for (const activity of activities) {
2036
- const activityResult = await activity.execute(itemContext);
2037
- if (!activityResult.success) {
2038
- return {
2039
- success: false,
2040
- message: activityResult.message,
2041
- data: {
2042
- output: {
2043
- totalItems: items.length,
2044
- processedItems: results.length,
2045
- results,
2046
- },
2047
- outcomes: {
2048
- Failed: true,
2049
- },
2050
- },
2051
- };
2225
+ const resolvedProviders = await Promise.allSettled(this.workflowProviders);
2226
+ const definitions = [];
2227
+ // Smart routing: Get provider INDEX that owns this category
2228
+ const ownerIndex = this.categoryOwnership.get(categoryId);
2229
+ const providerIndicesToQuery = ownerIndex !== undefined ? [ownerIndex] : null;
2230
+ for (let i = 0; i < resolvedProviders.length; i++) {
2231
+ const p = resolvedProviders[i];
2232
+ // Skip if we have a specific provider index and this isn't it
2233
+ if (providerIndicesToQuery !== null && !providerIndicesToQuery.includes(i)) {
2234
+ continue;
2235
+ }
2236
+ if (p.status === 'fulfilled' && p.value && typeof p.value.getList === 'function') {
2237
+ try {
2238
+ const defs = await p.value.getList(categoryId);
2239
+ if (Array.isArray(defs)) {
2240
+ definitions.push(...defs);
2241
+ }
2242
+ }
2243
+ catch {
2244
+ // Continue on error - try other providers
2052
2245
  }
2053
2246
  }
2054
- results.push({
2055
- item: currentItem,
2056
- index,
2057
- processed: true
2058
- });
2059
2247
  }
2060
- const result = {
2061
- totalItems: items.length,
2062
- processedItems: results.length,
2063
- results
2064
- };
2065
- return this.createResult(result, 'Done');
2248
+ // Cache results for fast subsequent access
2249
+ this.workflowsByCategory.set(categoryId, definitions);
2250
+ definitions.forEach(def => {
2251
+ if (def.name) {
2252
+ this.workflowsByName.set(def.name, def);
2253
+ }
2254
+ });
2255
+ return definitions;
2066
2256
  }
2067
- catch (err) {
2068
- console.error('[ForEach] Error in iteration:', err);
2069
- return {
2070
- success: false,
2071
- message: {
2072
- text: err instanceof Error ? err.message : 'Failed during iteration',
2073
- },
2074
- data: {
2075
- output: { totalItems: 0, processedItems: 0, results: [] },
2076
- outcomes: {
2077
- Failed: true,
2078
- },
2079
- },
2080
- };
2257
+ finally {
2258
+ this.pendingWorkflowsRequests.delete(categoryId);
2081
2259
  }
2082
2260
  }
2083
- }
2084
-
2085
- var forEach_activity = /*#__PURE__*/Object.freeze({
2086
- __proto__: null,
2087
- ForEach: ForEach
2088
- });
2089
-
2090
- /**
2091
- * Execute Command Activity - Executes a command through Command Bus.
2092
- *
2093
- * Usage:
2094
- * ```typescript
2095
- * const executeCmd = new ExecuteCommand();
2096
- * await executeCmd.execute({
2097
- * commandKey: 'UserManagement.CreateUser',
2098
- * input: { name: 'John', email: 'john@example.com' }
2099
- * });
2100
- * ```
2101
- */
2102
- class ExecuteCommand extends Activity {
2103
- constructor() {
2104
- super('workflow-activity:execute-command', 'Execute Command');
2105
- this.commandService = inject(AXPCommandService);
2106
- }
2107
- async execute(input) {
2108
- const { commandKey, input: commandInput = {} } = input;
2261
+ /**
2262
+ * Load single workflow definition from providers and cache result
2263
+ */
2264
+ async loadWorkflowFromProviders(name) {
2109
2265
  try {
2110
- // Check if command exists
2111
- if (!this.commandService.exists(commandKey)) {
2112
- console.warn(`[ExecuteCommand] Command '${commandKey}' is not registered. Simulating execution.`);
2113
- // Simulate command execution for unregistered commands
2114
- const result = {
2115
- commandKey,
2116
- success: true,
2117
- output: commandInput,
2118
- executedAt: new Date().toISOString(),
2119
- simulated: true
2120
- };
2121
- return this.createResult(result, 'Done');
2122
- }
2123
- // Execute command through Command Bus
2124
- const result = await this.commandService.execute(commandKey, commandInput);
2125
- if (!result) {
2126
- return {
2127
- success: false,
2128
- message: {
2129
- text: `Command '${commandKey}' returned no result`,
2130
- },
2131
- data: {
2132
- output: {
2133
- commandKey,
2134
- success: false,
2135
- executedAt: new Date().toISOString(),
2136
- },
2137
- outcomes: {
2138
- Failed: true,
2139
- },
2140
- },
2141
- };
2266
+ const resolvedProviders = await Promise.allSettled(this.workflowProviders);
2267
+ // Try providers first
2268
+ for (const p of resolvedProviders) {
2269
+ if (p.status === 'fulfilled' && p.value && typeof p.value.getByName === 'function') {
2270
+ try {
2271
+ const definition = await p.value.getByName(name);
2272
+ if (definition) {
2273
+ this.workflowsByName.set(name, definition);
2274
+ return definition;
2275
+ }
2276
+ }
2277
+ catch {
2278
+ // Continue on error
2279
+ }
2280
+ }
2142
2281
  }
2143
- if (!result.success) {
2144
- return {
2145
- success: false,
2146
- message: result.message,
2147
- data: {
2148
- output: {
2149
- commandKey,
2150
- success: false,
2151
- executedAt: new Date().toISOString(),
2152
- error: result.message?.text,
2153
- },
2154
- outcomes: {
2155
- Failed: true,
2156
- },
2157
- },
2158
- };
2282
+ // Fallback: Search in cached workflow definitions
2283
+ for (const definitions of this.workflowsByCategory.values()) {
2284
+ const found = definitions.find(def => def.name === name);
2285
+ if (found) {
2286
+ this.workflowsByName.set(name, found);
2287
+ return found;
2288
+ }
2159
2289
  }
2160
- return this.createResult({
2161
- commandKey,
2162
- success: true,
2163
- output: result.data,
2164
- executedAt: new Date().toISOString(),
2165
- }, 'Done');
2290
+ return undefined;
2166
2291
  }
2167
- catch (err) {
2168
- console.error('[ExecuteCommand] Error executing command:', err);
2169
- return {
2170
- success: false,
2171
- message: {
2172
- text: err instanceof Error ? err.message : 'Unknown error',
2173
- },
2174
- data: {
2175
- output: {
2176
- commandKey,
2177
- success: false,
2178
- error: err instanceof Error ? err.message : 'Unknown error',
2179
- },
2180
- outcomes: {
2181
- Failed: true,
2182
- },
2183
- },
2184
- };
2292
+ finally {
2293
+ this.pendingWorkflowRequests.delete(name);
2185
2294
  }
2186
2295
  }
2187
- }
2188
-
2189
- var executeCommand_activity = /*#__PURE__*/Object.freeze({
2190
- __proto__: null,
2191
- ExecuteCommand: ExecuteCommand
2192
- });
2193
-
2194
- /**
2195
- * Execute Query Activity - Executes a query through Query Bus.
2196
- *
2197
- * Usage:
2198
- * ```typescript
2199
- * const executeQuery = new ExecuteQuery();
2200
- * await executeQuery.execute({
2201
- * queryKey: 'UserManagement.GetUsers',
2202
- * input: { page: 1, pageSize: 10 }
2203
- * });
2204
- * ```
2205
- */
2206
- class ExecuteQuery extends Activity {
2207
- constructor() {
2208
- super('workflow-activity:execute-query', 'Execute Query');
2209
- this.queryService = inject(AXPQueryService);
2210
- }
2211
- async execute(input) {
2212
- const { queryKey, input: queryInput = {} } = input;
2213
- try {
2214
- // Check if query exists
2215
- if (!this.queryService.exists(queryKey)) {
2216
- console.warn(`[ExecuteQuery] Query '${queryKey}' is not registered. Simulating execution.`);
2217
- // Simulate query execution for unregistered queries
2218
- const result = {
2219
- queryKey,
2220
- success: true,
2221
- data: [],
2222
- totalCount: 0,
2223
- executedAt: new Date().toISOString(),
2224
- simulated: true
2225
- };
2226
- return this.createResult(result, 'Done');
2296
+ /**
2297
+ * Breadth-first search through category hierarchy
2298
+ */
2299
+ async searchCategoryInHierarchy(categoryId) {
2300
+ const searchQueue = [undefined];
2301
+ const searched = new Set();
2302
+ while (searchQueue.length > 0) {
2303
+ const parentId = searchQueue.shift();
2304
+ if (searched.has(parentId))
2305
+ continue;
2306
+ searched.add(parentId);
2307
+ const categories = await this.getCategories(parentId);
2308
+ const found = categories.find(cat => cat.id === categoryId);
2309
+ if (found) {
2310
+ return found;
2311
+ }
2312
+ // Optimization: Only search children if childrenCount > 0
2313
+ for (const category of categories) {
2314
+ if (category.childrenCount > 0 && !searched.has(category.id)) {
2315
+ searchQueue.push(category.id);
2316
+ }
2227
2317
  }
2228
- // Execute query through Query Bus
2229
- const result = await this.queryService.fetch(queryKey, queryInput);
2230
- return this.createResult({
2231
- queryKey,
2232
- success: true,
2233
- data: result,
2234
- totalCount: Array.isArray(result) ? result.length : (result?.totalCount || 0),
2235
- executedAt: new Date().toISOString()
2236
- }, 'Done');
2237
- }
2238
- catch (err) {
2239
- console.error('[ExecuteQuery] Error executing query:', err);
2240
- return {
2241
- success: false,
2242
- message: {
2243
- text: err instanceof Error ? err.message : 'Unknown error',
2244
- },
2245
- data: {
2246
- output: {
2247
- queryKey,
2248
- success: false,
2249
- error: err instanceof Error ? err.message : 'Unknown error',
2250
- },
2251
- outcomes: {
2252
- Failed: true,
2253
- },
2254
- },
2255
- };
2256
2318
  }
2319
+ return undefined;
2257
2320
  }
2258
- }
2259
-
2260
- var executeQuery_activity = /*#__PURE__*/Object.freeze({
2261
- __proto__: null,
2262
- ExecuteQuery: ExecuteQuery
2263
- });
2264
-
2265
- /**
2266
- * Start Activity - Marks the start point of a workflow.
2267
- *
2268
- * This is a visual marker activity that doesn't perform any actual work.
2269
- * It's used in workflow designers to clearly indicate where a workflow begins.
2270
- *
2271
- * Usage:
2272
- * ```typescript
2273
- * const start = new StartActivity();
2274
- * await start.execute({});
2275
- * ```
2276
- */
2277
- class StartActivity extends Activity {
2278
- constructor() {
2279
- super('StartActivity');
2321
+ //#endregion
2322
+ //#region ---- Cache Management ----
2323
+ /**
2324
+ * Check if category has children (uses cached count)
2325
+ */
2326
+ categoryHasChildren(categoryId) {
2327
+ const category = this.categoriesById.get(categoryId);
2328
+ return category ? category.childrenCount > 0 : false;
2280
2329
  }
2281
- async execute(input) {
2282
- // This activity is a visual marker only
2283
- // It immediately completes and allows workflow to proceed
2284
- console.log('[StartActivity] Workflow started');
2285
- return this.createResult(undefined, 'Done');
2330
+ /**
2331
+ * Check if category has workflows (uses cached count)
2332
+ */
2333
+ categoryHasWorkflows(categoryId) {
2334
+ const category = this.categoriesById.get(categoryId);
2335
+ return category ? (category.itemsCount ?? 0) > 0 : false;
2286
2336
  }
2287
- }
2288
-
2289
- var startActivity_activity = /*#__PURE__*/Object.freeze({
2290
- __proto__: null,
2291
- StartActivity: StartActivity
2292
- });
2293
-
2294
- /**
2295
- * End Activity - Marks the end point of a workflow.
2296
- *
2297
- * This is a visual marker activity that terminates the workflow execution.
2298
- * When this activity is reached, the workflow completes successfully.
2299
- *
2300
- * Usage:
2301
- * ```typescript
2302
- * const end = new EndActivity();
2303
- * await end.execute({});
2304
- * ```
2305
- */
2306
- class EndActivity extends Activity {
2307
- constructor() {
2308
- super('EndActivity');
2337
+ /**
2338
+ * Clear all caches
2339
+ */
2340
+ clearAllCache() {
2341
+ this.categoriesById.clear();
2342
+ this.categoriesByParentId.clear();
2343
+ this.workflowsByCategory.clear();
2344
+ this.workflowsByName.clear();
2345
+ this.categoryOwnership.clear();
2346
+ this.pendingCategoriesRequests.clear();
2347
+ this.pendingWorkflowsRequests.clear();
2348
+ this.pendingWorkflowRequests.clear();
2309
2349
  }
2310
- async execute(input) {
2311
- // This activity marks the end of workflow execution
2312
- console.log('[EndActivity] Workflow completed');
2313
- return this.createResult(undefined, 'Done');
2350
+ /**
2351
+ * Clear categories cache only
2352
+ */
2353
+ clearCategoriesCache() {
2354
+ this.categoriesById.clear();
2355
+ this.categoriesByParentId.clear();
2356
+ this.categoryOwnership.clear();
2357
+ this.pendingCategoriesRequests.clear();
2314
2358
  }
2359
+ /**
2360
+ * Clear workflows cache only
2361
+ */
2362
+ clearWorkflowsCache() {
2363
+ this.workflowsByCategory.clear();
2364
+ this.workflowsByName.clear();
2365
+ this.pendingWorkflowsRequests.clear();
2366
+ this.pendingWorkflowRequests.clear();
2367
+ }
2368
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowDefinitionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2369
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowDefinitionService, providedIn: 'root' }); }
2315
2370
  }
2371
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowDefinitionService, decorators: [{
2372
+ type: Injectable,
2373
+ args: [{
2374
+ providedIn: 'root',
2375
+ }]
2376
+ }] });
2316
2377
 
2317
- var endActivity_activity = /*#__PURE__*/Object.freeze({
2318
- __proto__: null,
2319
- EndActivity: EndActivity
2320
- });
2321
-
2322
- /**
2323
- * Command setups for all built-in workflow activities.
2324
- * Registers activities as AXPCommand instances.
2325
- */
2326
- const provideWorkflowActivityCommands = () => provideCommandSetups([
2327
- // Workflow Markers
2328
- {
2329
- key: 'workflow-activity:start',
2330
- command: () => Promise.resolve().then(function () { return startActivity_activity; }).then(m => m.StartActivity),
2331
- },
2332
- {
2333
- key: 'workflow-activity:end',
2334
- command: () => Promise.resolve().then(function () { return endActivity_activity; }).then(m => m.EndActivity),
2335
- },
2336
- // Console Activities
2337
- {
2338
- key: 'workflow-activity:write-line',
2339
- command: () => Promise.resolve().then(function () { return writeLine_activity; }).then(m => m.WriteLine),
2340
- },
2341
- // Control Flow Activities
2342
- {
2343
- key: 'workflow-activity:sequence',
2344
- command: () => Promise.resolve().then(function () { return sequence_activity; }).then(m => m.Sequence),
2345
- },
2346
- {
2347
- key: 'workflow-activity:if',
2348
- command: () => Promise.resolve().then(function () { return if_activity; }).then(m => m.If),
2349
- },
2350
- {
2351
- key: 'workflow-activity:while',
2352
- command: () => Promise.resolve().then(function () { return while_activity; }).then(m => m.While),
2353
- },
2354
- {
2355
- key: 'workflow-activity:for-each',
2356
- command: () => Promise.resolve().then(function () { return forEach_activity; }).then(m => m.ForEach),
2357
- },
2358
- // Dialog Activities
2359
- {
2360
- key: 'workflow-activity:show-confirm-dialog',
2361
- command: () => Promise.resolve().then(function () { return showConfirmDialog_activity; }).then(m => m.ShowConfirmDialog),
2362
- },
2363
- {
2364
- key: 'workflow-activity:show-alert-dialog',
2365
- command: () => Promise.resolve().then(function () { return showAlertDialog_activity; }).then(m => m.ShowAlertDialog),
2366
- },
2367
- // Notification Activities
2368
- {
2369
- key: 'workflow-activity:show-toast',
2370
- command: () => Promise.resolve().then(function () { return showToast_activity; }).then(m => m.ShowToast),
2371
- },
2372
- // Event Activities
2373
- {
2374
- key: 'workflow-activity:dispatch-event',
2375
- command: () => Promise.resolve().then(function () { return dispatchEvent_activity; }).then(m => m.DispatchEvent),
2376
- },
2377
- // Variable Activities
2378
- {
2379
- key: 'workflow-activity:set-variable',
2380
- command: () => Promise.resolve().then(function () { return setVariable_activity; }).then(m => m.SetVariable),
2381
- },
2382
- // Command & Query Activities
2383
- {
2384
- key: 'workflow-activity:execute-command',
2385
- command: () => Promise.resolve().then(function () { return executeCommand_activity; }).then(m => m.ExecuteCommand),
2386
- },
2387
- {
2388
- key: 'workflow-activity:execute-query',
2389
- command: () => Promise.resolve().then(function () { return executeQuery_activity; }).then(m => m.ExecuteQuery),
2390
- },
2391
- // Navigation Activities
2392
- {
2393
- key: 'workflow-activity:navigate',
2394
- command: () => Promise.resolve().then(function () { return navigate_activity; }).then(m => m.Navigate),
2395
- },
2396
- ]);
2397
-
2398
- // Built-in Activities
2399
-
2400
- // Workflow Definition Types (Storage/Database)
2378
+ // Workflow Instance Types (Storage/Database)
2401
2379
 
2402
2380
  class AXPWorkflowModule {
2403
2381
  static forRoot(config) {
2404
2382
  return {
2405
2383
  ngModule: AXPWorkflowModule,
2406
2384
  providers: [
2407
- provideWorkflowActivityCommands(),
2408
2385
  {
2409
2386
  provide: 'AXPWorkflowModuleFactory',
2410
2387
  useFactory: (registry) => () => {
@@ -2479,26 +2456,34 @@ class AXPWorkflowModule {
2479
2456
  * @ignore
2480
2457
  */
2481
2458
  constructor(instances) {
2482
- // Inject AXP_COMMAND_SETUP to trigger command registration factory
2483
- this._commandSetup = inject(AXP_COMMAND_SETUP, { optional: true });
2484
2459
  instances?.forEach((f) => {
2485
2460
  f();
2486
2461
  });
2487
2462
  }
2488
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowModule, deps: [{ token: 'AXPWorkflowModuleFactory', optional: true }], target: i0.ɵɵFactoryTarget.NgModule }); }
2489
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowModule }); }
2490
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowModule, providers: [
2491
- provideWorkflowActivityCommands(),
2463
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowModule, deps: [{ token: 'AXPWorkflowModuleFactory', optional: true }], target: i0.ɵɵFactoryTarget.NgModule }); }
2464
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowModule }); }
2465
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowModule, providers: [
2466
+ AXPWorkflowLocalEngine,
2467
+ {
2468
+ provide: AXP_WORKFLOW_ENGINE,
2469
+ useExisting: AXPWorkflowLocalEngine,
2470
+ },
2471
+ AXPWorkflowManager,
2492
2472
  ] }); }
2493
2473
  }
2494
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWorkflowModule, decorators: [{
2474
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowModule, decorators: [{
2495
2475
  type: NgModule,
2496
2476
  args: [{
2497
2477
  imports: [],
2498
2478
  exports: [],
2499
2479
  declarations: [],
2500
2480
  providers: [
2501
- provideWorkflowActivityCommands(),
2481
+ AXPWorkflowLocalEngine,
2482
+ {
2483
+ provide: AXP_WORKFLOW_ENGINE,
2484
+ useExisting: AXPWorkflowLocalEngine,
2485
+ },
2486
+ AXPWorkflowManager,
2502
2487
  ],
2503
2488
  }]
2504
2489
  }], ctorParameters: () => [{ type: undefined, decorators: [{
@@ -2512,5 +2497,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
2512
2497
  * Generated bundle index. Do not edit.
2513
2498
  */
2514
2499
 
2515
- export { AXPActivityCategoryProviderService, AXPActivityProviderService, AXPWorkflowAction, AXPWorkflowContext, AXPWorkflowDefinitionRegistryService, AXPWorkflowDefinitionResolver, AXPWorkflowError, AXPWorkflowEventService, AXPWorkflowExecutionService, AXPWorkflowFunction, AXPWorkflowModule, AXPWorkflowRegistryService, AXPWorkflowService, AXP_ACTIVITY_CATEGORY_PROVIDER, AXP_ACTIVITY_PROVIDER, AXP_WORKFLOW_DEFINITION_LOADER, Activity, ActivityRegistry, DispatchEvent, EndActivity, ExecuteCommand, ExecuteQuery, ForEach, If, Navigate, Sequence, SetVariable, ShowAlertDialog, ShowConfirmDialog, ShowDialogLayoutBuilder, ShowToast, StartActivity, While, WorkflowCoordinator, WriteLine, createWorkFlowEvent, ofType, provideWorkflowActivityCommands };
2500
+ export { AXPActivityDefinitionService, AXPWorkflowAction, AXPWorkflowContext, AXPWorkflowDefinitionService, AXPWorkflowError, AXPWorkflowEventService, AXPWorkflowFunction, AXPWorkflowLocalEngine, AXPWorkflowManager, AXPWorkflowModule, AXPWorkflowRegistryService, AXPWorkflowService, AXP_ACTIVITY_CATEGORY_PROVIDER, AXP_ACTIVITY_PROVIDER, AXP_WORKFLOW_CATEGORY_PROVIDER, AXP_WORKFLOW_ENGINE, AXP_WORKFLOW_PROVIDER, ActivityExecutor, WorkflowExpressionScopeService, createWorkFlowEvent, ofType };
2516
2501
  //# sourceMappingURL=acorex-platform-workflow.mjs.map