@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,
|
|
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,
|
|
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,
|
|
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.
|
|
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
|
-
|
|
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 }); }
|