@acorex/platform 20.7.4 → 20.7.5

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.
@@ -966,6 +966,11 @@ class WorkflowExpressionScopeService {
966
966
  Object.assign(outputs, activityOutputs);
967
967
  }
968
968
  }
969
+ // Convenience alias for the last activity output (if available).
970
+ // This allows expressions like: {{outputs.last.someField}}
971
+ if (outputs['last'] === undefined && state.lastActivityOutput !== undefined) {
972
+ outputs['last'] = state.lastActivityOutput;
973
+ }
969
974
  return this.buildScope({
970
975
  inputs: state.input || {},
971
976
  variables: state.variables || {},
@@ -1020,11 +1025,16 @@ class ActivityExecutor {
1020
1025
  async execute(task, workflowState, activityOutputs) {
1021
1026
  try {
1022
1027
  const activityName = task.activityType;
1028
+ debugger;
1023
1029
  // Evaluate inputs if workflow state is provided
1024
1030
  let evaluatedInputs = task.input || {};
1025
1031
  if (workflowState) {
1032
+ // Prefer explicit outputs, fallback to outputs stored in state
1033
+ const outputsForScope = activityOutputs ??
1034
+ workflowState.activityOutputs ??
1035
+ undefined;
1026
1036
  // Build expression scope from workflow state
1027
- const scope = this.expressionScopeService.buildScopeFromState(workflowState, activityOutputs);
1037
+ const scope = this.expressionScopeService.buildScopeFromState(workflowState, outputsForScope);
1028
1038
  // Evaluate all inputs recursively (handles nested objects and arrays)
1029
1039
  evaluatedInputs = await this.expressionEvaluator.evaluate(task.input || {}, scope);
1030
1040
  }
@@ -1147,11 +1157,15 @@ class AXPWorkflowManager {
1147
1157
  * @param lastActivityOutput - Last activity output (for expression evaluation)
1148
1158
  * @returns Final result with nextTask (if human-task) or completion status
1149
1159
  */
1150
- async executeInteractiveFlow(instanceId, task, state, lastActivityOutput) {
1160
+ async executeInteractiveFlow(instanceId, task, state, activityOutputs) {
1151
1161
  // Backend decides what to execute - frontend only executes ui-activity with frontend/both executionMode
1152
1162
  // If backend returns a task that's not executable here, return it as-is
1153
1163
  let currentTask = task;
1154
1164
  let currentState = state;
1165
+ let currentActivityOutputs = {
1166
+ ...(currentState.activityOutputs || {}),
1167
+ ...(activityOutputs || {}),
1168
+ };
1155
1169
  const maxIterations = 100; // Prevent infinite loops
1156
1170
  let iterationCount = 0;
1157
1171
  while (currentTask && iterationCount < maxIterations) {
@@ -1161,7 +1175,12 @@ class AXPWorkflowManager {
1161
1175
  if (currentTask.taskType === 'ui-activity' &&
1162
1176
  (currentTask.executionMode === 'frontend' || currentTask.executionMode === 'both')) {
1163
1177
  // Execute frontend activity
1164
- const execResult = await this.activityExecutor.execute(currentTask, currentState, lastActivityOutput);
1178
+ const execResult = await this.activityExecutor.execute(currentTask, currentState, currentActivityOutputs);
1179
+ // Track outputs locally (backend should also persist and return them)
1180
+ currentActivityOutputs = {
1181
+ ...currentActivityOutputs,
1182
+ [currentTask.activityId]: execResult.output,
1183
+ };
1165
1184
  // Send result to backend
1166
1185
  const completeResponse = await this.workflowEngine.frontActivtyComplete({
1167
1186
  instanceId,
@@ -1176,6 +1195,11 @@ class AXPWorkflowManager {
1176
1195
  normalizedState.lastUpdated = new Date(normalizedState.lastUpdated);
1177
1196
  }
1178
1197
  currentState = normalizedState;
1198
+ // Prefer outputs returned by backend; fallback to local cache
1199
+ currentActivityOutputs = {
1200
+ ...currentActivityOutputs,
1201
+ ...(normalizedState.activityOutputs || {}),
1202
+ };
1179
1203
  this.stateCache.set(instanceId, normalizedState);
1180
1204
  }
1181
1205
  // Backend decides: if no nextTask, workflow is completed
@@ -1197,7 +1221,6 @@ class AXPWorkflowManager {
1197
1221
  }
1198
1222
  // Continue with next executable frontend task
1199
1223
  currentTask = completeResponse.nextTask;
1200
- lastActivityOutput = execResult.output;
1201
1224
  }
1202
1225
  else {
1203
1226
  // Not a frontend ui-activity - return as-is
@@ -1259,7 +1282,7 @@ class AXPWorkflowManager {
1259
1282
  if (response.pendingTask &&
1260
1283
  response.pendingTask.taskType === 'ui-activity' &&
1261
1284
  (response.pendingTask.executionMode === 'frontend' || response.pendingTask.executionMode === 'both')) {
1262
- const interactiveResult = await this.executeInteractiveFlow(response.instanceId, response.pendingTask, startNormalizedState, response.lastActivityOutput);
1285
+ const interactiveResult = await this.executeInteractiveFlow(response.instanceId, response.pendingTask, startNormalizedState, response.activityOutputs || response.state.activityOutputs);
1263
1286
  finalNextTask = interactiveResult.nextTask;
1264
1287
  startNormalizedState = interactiveResult.state;
1265
1288
  if (interactiveResult.output !== undefined) {
@@ -1329,7 +1352,7 @@ class AXPWorkflowManager {
1329
1352
  if (response.nextTask &&
1330
1353
  response.nextTask.taskType === 'ui-activity' &&
1331
1354
  (response.nextTask.executionMode === 'frontend' || response.nextTask.executionMode === 'both')) {
1332
- const interactiveResult = await this.executeInteractiveFlow(instanceId, response.nextTask, normalizedState);
1355
+ const interactiveResult = await this.executeInteractiveFlow(instanceId, response.nextTask, normalizedState, normalizedState?.activityOutputs);
1333
1356
  finalNextTask = interactiveResult.nextTask;
1334
1357
  normalizedState = interactiveResult.state;
1335
1358
  if (interactiveResult.output !== undefined) {
@@ -1484,6 +1507,8 @@ class AXPWorkflowLocalEngine {
1484
1507
  workflowId: request.workflowId,
1485
1508
  status: 'running',
1486
1509
  variables: {},
1510
+ activityOutputs: {},
1511
+ lastActivityOutput: undefined,
1487
1512
  input: request.input || {},
1488
1513
  output: undefined,
1489
1514
  lastUpdated: now,
@@ -1514,6 +1539,8 @@ class AXPWorkflowLocalEngine {
1514
1539
  instanceId,
1515
1540
  state: localState.state,
1516
1541
  pendingTask: pendingTask || null,
1542
+ activityOutputs: localState.state.activityOutputs,
1543
+ lastActivityOutput: localState.state.lastActivityOutput,
1517
1544
  };
1518
1545
  }
1519
1546
  /**
@@ -1539,6 +1566,12 @@ class AXPWorkflowLocalEngine {
1539
1566
  outcome: request.outcome,
1540
1567
  });
1541
1568
  localState.completedActivities.add(request.stepId);
1569
+ // Store activity output for expression evaluation
1570
+ localState.state.activityOutputs = {
1571
+ ...(localState.state.activityOutputs || {}),
1572
+ [request.stepId]: request.userInput || {},
1573
+ };
1574
+ localState.state.lastActivityOutput = request.userInput || {};
1542
1575
  // Merge output/userInput into state variables
1543
1576
  if (request.userInput) {
1544
1577
  localState.state.variables = {
@@ -1757,7 +1790,42 @@ class AXPWorkflowLocalEngine {
1757
1790
  return null;
1758
1791
  }
1759
1792
  async frontActivtyComplete(request) {
1760
- return {};
1793
+ const localState = this.instances.get(request.instanceId);
1794
+ if (!localState) {
1795
+ throw new Error(`Workflow instance not found: ${request.instanceId}`);
1796
+ }
1797
+ // Store activity result
1798
+ localState.activityResults.set(request.activityNode, {
1799
+ output: request.output || {},
1800
+ outcome: request.outcome,
1801
+ });
1802
+ localState.completedActivities.add(request.activityNode);
1803
+ // Store outputs for expression evaluation
1804
+ localState.state.activityOutputs = {
1805
+ ...(localState.state.activityOutputs || {}),
1806
+ [request.activityNode]: request.output || {},
1807
+ };
1808
+ localState.state.lastActivityOutput = request.output || {};
1809
+ // Merge output into workflow variables
1810
+ localState.state.variables = {
1811
+ ...localState.state.variables,
1812
+ ...(request.output || {}),
1813
+ };
1814
+ // Continue workflow progression
1815
+ const nextTask = await this.executeWorkflowSteps(localState);
1816
+ // Update state timestamp
1817
+ localState.state.lastUpdated = new Date();
1818
+ // Determine final status
1819
+ if (!nextTask && localState.state.status === 'running') {
1820
+ localState.state.status = 'completed';
1821
+ localState.state.output = localState.state.variables;
1822
+ }
1823
+ return {
1824
+ output: request.output || {},
1825
+ outcomes: { [request.outcome]: true },
1826
+ nextTask: nextTask || null,
1827
+ state: localState.state,
1828
+ };
1761
1829
  }
1762
1830
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPWorkflowLocalEngine, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1763
1831
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPWorkflowLocalEngine }); }