@open-mercato/core 0.6.5-develop.4516.1.88e6ab71a9 → 0.6.5-develop.4534.1.b459babe6d
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +2 -2
- package/dist/generated/entities/step_instance/index.js +2 -0
- package/dist/generated/entities/step_instance/index.js.map +2 -2
- package/dist/generated/entities/user_task/index.js +2 -0
- package/dist/generated/entities/user_task/index.js.map +2 -2
- package/dist/generated/entities/workflow_branch_instance/index.js +39 -0
- package/dist/generated/entities/workflow_branch_instance/index.js.map +7 -0
- package/dist/generated/entities/workflow_event/index.js +2 -0
- package/dist/generated/entities/workflow_event/index.js.map +2 -2
- package/dist/generated/entities/workflow_instance/index.js +2 -0
- package/dist/generated/entities/workflow_instance/index.js.map +2 -2
- package/dist/generated/entities.ids.generated.js +1 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +24 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/progress/api/jobs/[id]/route.js +7 -1
- package/dist/modules/progress/api/jobs/[id]/route.js.map +2 -2
- package/dist/modules/shipping_carriers/api/cancel/route.js +2 -2
- package/dist/modules/shipping_carriers/api/cancel/route.js.map +2 -2
- package/dist/modules/shipping_carriers/lib/status-sync.js +8 -1
- package/dist/modules/shipping_carriers/lib/status-sync.js.map +2 -2
- package/dist/modules/workflows/components/NodeEditDialog.js +3 -1
- package/dist/modules/workflows/components/NodeEditDialog.js.map +2 -2
- package/dist/modules/workflows/components/WorkflowGraphImpl.js +4 -2
- package/dist/modules/workflows/components/WorkflowGraphImpl.js.map +2 -2
- package/dist/modules/workflows/components/nodes/ParallelForkNode.js +49 -0
- package/dist/modules/workflows/components/nodes/ParallelForkNode.js.map +7 -0
- package/dist/modules/workflows/components/nodes/ParallelJoinNode.js +49 -0
- package/dist/modules/workflows/components/nodes/ParallelJoinNode.js.map +7 -0
- package/dist/modules/workflows/components/nodes/index.js +4 -0
- package/dist/modules/workflows/components/nodes/index.js.map +2 -2
- package/dist/modules/workflows/data/entities.js +81 -0
- package/dist/modules/workflows/data/entities.js.map +2 -2
- package/dist/modules/workflows/data/validators.js +146 -1
- package/dist/modules/workflows/data/validators.js.map +2 -2
- package/dist/modules/workflows/events.js +7 -1
- package/dist/modules/workflows/events.js.map +2 -2
- package/dist/modules/workflows/lib/activity-executor.js +4 -2
- package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
- package/dist/modules/workflows/lib/activity-queue-types.js.map +2 -2
- package/dist/modules/workflows/lib/event-logger.js +2 -0
- package/dist/modules/workflows/lib/event-logger.js.map +2 -2
- package/dist/modules/workflows/lib/execution-token.js +98 -0
- package/dist/modules/workflows/lib/execution-token.js.map +7 -0
- package/dist/modules/workflows/lib/node-type-icons.js +14 -5
- package/dist/modules/workflows/lib/node-type-icons.js.map +2 -2
- package/dist/modules/workflows/lib/parallel-handler.js +364 -0
- package/dist/modules/workflows/lib/parallel-handler.js.map +7 -0
- package/dist/modules/workflows/lib/signal-handler.js +63 -1
- package/dist/modules/workflows/lib/signal-handler.js.map +2 -2
- package/dist/modules/workflows/lib/step-handler.js +74 -30
- package/dist/modules/workflows/lib/step-handler.js.map +2 -2
- package/dist/modules/workflows/lib/task-handler.js +26 -0
- package/dist/modules/workflows/lib/task-handler.js.map +2 -2
- package/dist/modules/workflows/lib/timer-handler.js +26 -1
- package/dist/modules/workflows/lib/timer-handler.js.map +2 -2
- package/dist/modules/workflows/lib/transition-handler.js +33 -21
- package/dist/modules/workflows/lib/transition-handler.js.map +2 -2
- package/dist/modules/workflows/lib/workflow-executor.js +39 -1
- package/dist/modules/workflows/lib/workflow-executor.js.map +2 -2
- package/dist/modules/workflows/migrations/Migration20260602120000.js +24 -0
- package/dist/modules/workflows/migrations/Migration20260602120000.js.map +7 -0
- package/dist/modules/workflows/workers/workflow-activities.worker.js +8 -4
- package/dist/modules/workflows/workers/workflow-activities.worker.js.map +2 -2
- package/generated/entities/step_instance/index.ts +1 -0
- package/generated/entities/user_task/index.ts +1 -0
- package/generated/entities/workflow_branch_instance/index.ts +18 -0
- package/generated/entities/workflow_event/index.ts +1 -0
- package/generated/entities/workflow_instance/index.ts +1 -0
- package/generated/entities.ids.generated.ts +1 -0
- package/generated/entity-fields-registry.ts +24 -0
- package/package.json +7 -7
- package/src/modules/progress/api/jobs/[id]/route.ts +7 -0
- package/src/modules/shipping_carriers/api/cancel/route.ts +2 -2
- package/src/modules/shipping_carriers/lib/status-sync.ts +19 -0
- package/src/modules/workflows/components/NodeEditDialog.tsx +2 -0
- package/src/modules/workflows/components/WorkflowGraphImpl.tsx +3 -1
- package/src/modules/workflows/components/nodes/ParallelForkNode.tsx +66 -0
- package/src/modules/workflows/components/nodes/ParallelJoinNode.tsx +66 -0
- package/src/modules/workflows/components/nodes/index.ts +6 -0
- package/src/modules/workflows/data/entities.ts +109 -0
- package/src/modules/workflows/data/validators.ts +223 -0
- package/src/modules/workflows/events.ts +7 -0
- package/src/modules/workflows/i18n/de.json +12 -0
- package/src/modules/workflows/i18n/en.json +12 -0
- package/src/modules/workflows/i18n/es.json +12 -0
- package/src/modules/workflows/i18n/pl.json +12 -0
- package/src/modules/workflows/lib/activity-executor.ts +8 -2
- package/src/modules/workflows/lib/activity-queue-types.ts +3 -0
- package/src/modules/workflows/lib/event-logger.ts +3 -0
- package/src/modules/workflows/lib/execution-token.ts +166 -0
- package/src/modules/workflows/lib/node-type-icons.ts +11 -2
- package/src/modules/workflows/lib/parallel-handler.ts +575 -0
- package/src/modules/workflows/lib/signal-handler.ts +72 -1
- package/src/modules/workflows/lib/step-handler.ts +94 -34
- package/src/modules/workflows/lib/task-handler.ts +32 -0
- package/src/modules/workflows/lib/timer-handler.ts +30 -1
- package/src/modules/workflows/lib/transition-handler.ts +56 -24
- package/src/modules/workflows/lib/workflow-executor.ts +53 -1
- package/src/modules/workflows/migrations/.snapshot-open-mercato.json +263 -0
- package/src/modules/workflows/migrations/Migration20260602120000.ts +25 -0
- package/src/modules/workflows/workers/workflow-activities.worker.ts +9 -4
|
@@ -14,7 +14,7 @@ class StepExecutionError extends Error {
|
|
|
14
14
|
this.name = "StepExecutionError";
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
-
async function enterStep(em, instance, stepId, context) {
|
|
17
|
+
async function enterStep(em, instance, stepId, context, branch) {
|
|
18
18
|
const definition = await em.findOne(WorkflowDefinition, {
|
|
19
19
|
id: instance.definitionId
|
|
20
20
|
});
|
|
@@ -36,6 +36,7 @@ async function enterStep(em, instance, stepId, context) {
|
|
|
36
36
|
const now = /* @__PURE__ */ new Date();
|
|
37
37
|
const stepInstance = em.create(StepInstance, {
|
|
38
38
|
workflowInstanceId: instance.id,
|
|
39
|
+
branchInstanceId: branch ? branch.id : null,
|
|
39
40
|
stepId: stepDef.stepId,
|
|
40
41
|
stepName: stepDef.stepName,
|
|
41
42
|
stepType: stepDef.stepType,
|
|
@@ -52,6 +53,7 @@ async function enterStep(em, instance, stepId, context) {
|
|
|
52
53
|
await logStepEvent(em, {
|
|
53
54
|
workflowInstanceId: instance.id,
|
|
54
55
|
stepInstanceId: stepInstance.id,
|
|
56
|
+
...branch ? { branchInstanceId: branch.id } : {},
|
|
55
57
|
eventType: "STEP_ENTERED",
|
|
56
58
|
eventData: {
|
|
57
59
|
stepId: stepDef.stepId,
|
|
@@ -90,9 +92,9 @@ async function exitStep(em, stepInstance, outputData) {
|
|
|
90
92
|
organizationId: stepInstance.organizationId
|
|
91
93
|
});
|
|
92
94
|
}
|
|
93
|
-
async function executeStep(em, instance, stepId, context, container) {
|
|
95
|
+
async function executeStep(em, instance, stepId, context, container, branch) {
|
|
94
96
|
try {
|
|
95
|
-
const stepInstance = await enterStep(em, instance, stepId, context);
|
|
97
|
+
const stepInstance = await enterStep(em, instance, stepId, context, branch);
|
|
96
98
|
const definition = await em.findOne(WorkflowDefinition, {
|
|
97
99
|
id: instance.definitionId
|
|
98
100
|
});
|
|
@@ -117,7 +119,8 @@ async function executeStep(em, instance, stepId, context, container) {
|
|
|
117
119
|
stepInstance,
|
|
118
120
|
stepDef,
|
|
119
121
|
context,
|
|
120
|
-
container
|
|
122
|
+
container,
|
|
123
|
+
branch
|
|
121
124
|
);
|
|
122
125
|
if (result.status === "COMPLETED") {
|
|
123
126
|
await exitStep(em, stepInstance, result.outputData);
|
|
@@ -158,7 +161,7 @@ async function executeStep(em, instance, stepId, context, container) {
|
|
|
158
161
|
};
|
|
159
162
|
}
|
|
160
163
|
}
|
|
161
|
-
async function executeStepByType(em, instance, stepInstance, stepDef, context, container) {
|
|
164
|
+
async function executeStepByType(em, instance, stepInstance, stepDef, context, container, branch) {
|
|
162
165
|
const stepType = stepDef.stepType;
|
|
163
166
|
switch (stepType) {
|
|
164
167
|
case "START":
|
|
@@ -166,9 +169,9 @@ async function executeStepByType(em, instance, stepInstance, stepDef, context, c
|
|
|
166
169
|
case "END":
|
|
167
170
|
return handleEndStep(stepDef, context);
|
|
168
171
|
case "AUTOMATED":
|
|
169
|
-
return await handleAutomatedStep(em, instance, stepInstance, stepDef, context, container);
|
|
172
|
+
return await handleAutomatedStep(em, instance, stepInstance, stepDef, context, container, branch);
|
|
170
173
|
case "USER_TASK":
|
|
171
|
-
return await handleUserTaskStep(em, instance, stepInstance, stepDef, context);
|
|
174
|
+
return await handleUserTaskStep(em, instance, stepInstance, stepDef, context, branch);
|
|
172
175
|
case "SUB_WORKFLOW":
|
|
173
176
|
if (!container) {
|
|
174
177
|
throw new StepExecutionError(
|
|
@@ -179,16 +182,31 @@ async function executeStepByType(em, instance, stepInstance, stepDef, context, c
|
|
|
179
182
|
}
|
|
180
183
|
return await handleSubWorkflowStep(em, container, instance, stepInstance, stepDef, context);
|
|
181
184
|
case "WAIT_FOR_SIGNAL":
|
|
182
|
-
return await handleWaitForSignalStep(em, instance, stepInstance, stepDef, context);
|
|
185
|
+
return await handleWaitForSignalStep(em, instance, stepInstance, stepDef, context, branch);
|
|
183
186
|
case "WAIT_FOR_TIMER":
|
|
184
|
-
return await handleWaitForTimerStep(em, instance, stepInstance, stepDef, context);
|
|
185
|
-
case "PARALLEL_FORK":
|
|
187
|
+
return await handleWaitForTimerStep(em, instance, stepInstance, stepDef, context, branch);
|
|
188
|
+
case "PARALLEL_FORK": {
|
|
189
|
+
if (branch) {
|
|
190
|
+
throw new StepExecutionError(
|
|
191
|
+
"Nested PARALLEL_FORK is not supported",
|
|
192
|
+
"NESTED_FORK_NOT_SUPPORTED",
|
|
193
|
+
{ stepType, stepId: stepDef.stepId }
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
const definition = await em.findOne(WorkflowDefinition, { id: instance.definitionId });
|
|
197
|
+
if (!definition) {
|
|
198
|
+
throw new StepExecutionError(
|
|
199
|
+
`Workflow definition not found: ${instance.definitionId}`,
|
|
200
|
+
"DEFINITION_NOT_FOUND",
|
|
201
|
+
{ definitionId: instance.definitionId }
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
const { openFork } = await import("./parallel-handler.js");
|
|
205
|
+
await openFork(em, instance, definition, stepDef);
|
|
206
|
+
return { status: "WAITING", waitReason: "FORK", outputData: { stepType: "PARALLEL_FORK", forkStepId: stepDef.stepId } };
|
|
207
|
+
}
|
|
186
208
|
case "PARALLEL_JOIN":
|
|
187
|
-
|
|
188
|
-
`Step type not yet implemented: ${stepType}`,
|
|
189
|
-
"STEP_TYPE_NOT_IMPLEMENTED",
|
|
190
|
-
{ stepType }
|
|
191
|
-
);
|
|
209
|
+
return { status: "COMPLETED", outputData: { stepType: "PARALLEL_JOIN", timestamp: (/* @__PURE__ */ new Date()).toISOString() } };
|
|
192
210
|
default:
|
|
193
211
|
throw new StepExecutionError(
|
|
194
212
|
`Unknown step type: ${stepType}`,
|
|
@@ -216,7 +234,7 @@ function handleEndStep(stepDef, context) {
|
|
|
216
234
|
}
|
|
217
235
|
};
|
|
218
236
|
}
|
|
219
|
-
async function handleAutomatedStep(em, instance, stepInstance, stepDef, context, container) {
|
|
237
|
+
async function handleAutomatedStep(em, instance, stepInstance, stepDef, context, container, branch) {
|
|
220
238
|
const activities = stepDef.activities || [];
|
|
221
239
|
if (activities.length === 0) {
|
|
222
240
|
return {
|
|
@@ -238,9 +256,15 @@ async function handleAutomatedStep(em, instance, stepInstance, stepDef, context,
|
|
|
238
256
|
});
|
|
239
257
|
const pendingActivities = results.filter((r) => r.async && !r.success);
|
|
240
258
|
if (pendingActivities.length > 0) {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
259
|
+
const now = /* @__PURE__ */ new Date();
|
|
260
|
+
if (branch) {
|
|
261
|
+
branch.status = "WAITING_FOR_ACTIVITIES";
|
|
262
|
+
branch.updatedAt = now;
|
|
263
|
+
} else {
|
|
264
|
+
instance.status = "WAITING_FOR_ACTIVITIES";
|
|
265
|
+
instance.pausedAt = now;
|
|
266
|
+
instance.updatedAt = now;
|
|
267
|
+
}
|
|
244
268
|
await em.flush();
|
|
245
269
|
return {
|
|
246
270
|
status: "WAITING",
|
|
@@ -293,7 +317,7 @@ async function handleAutomatedStep(em, instance, stepInstance, stepDef, context,
|
|
|
293
317
|
};
|
|
294
318
|
}
|
|
295
319
|
}
|
|
296
|
-
async function handleUserTaskStep(em, instance, stepInstance, stepDef, context) {
|
|
320
|
+
async function handleUserTaskStep(em, instance, stepInstance, stepDef, context, branch) {
|
|
297
321
|
const userTaskConfig = stepDef.userTaskConfig || {};
|
|
298
322
|
let assignedTo = userTaskConfig.assignedTo || null;
|
|
299
323
|
let assignedToRoles = userTaskConfig.assignedToRoles || null;
|
|
@@ -305,6 +329,7 @@ async function handleUserTaskStep(em, instance, stepInstance, stepDef, context)
|
|
|
305
329
|
const userTask = em.create(UserTask, {
|
|
306
330
|
workflowInstanceId: instance.id,
|
|
307
331
|
stepInstanceId: stepInstance.id,
|
|
332
|
+
branchInstanceId: branch ? branch.id : null,
|
|
308
333
|
taskName: stepDef.stepName,
|
|
309
334
|
description: stepDef.description || null,
|
|
310
335
|
status: "PENDING",
|
|
@@ -322,6 +347,7 @@ async function handleUserTaskStep(em, instance, stepInstance, stepDef, context)
|
|
|
322
347
|
await logStepEvent(em, {
|
|
323
348
|
workflowInstanceId: instance.id,
|
|
324
349
|
stepInstanceId: stepInstance.id,
|
|
350
|
+
...branch ? { branchInstanceId: branch.id } : {},
|
|
325
351
|
eventType: "USER_TASK_CREATED",
|
|
326
352
|
eventData: {
|
|
327
353
|
userTaskId: userTask.id,
|
|
@@ -332,8 +358,13 @@ async function handleUserTaskStep(em, instance, stepInstance, stepDef, context)
|
|
|
332
358
|
tenantId: instance.tenantId,
|
|
333
359
|
organizationId: instance.organizationId
|
|
334
360
|
});
|
|
335
|
-
|
|
336
|
-
|
|
361
|
+
if (branch) {
|
|
362
|
+
branch.status = "PAUSED";
|
|
363
|
+
branch.updatedAt = now;
|
|
364
|
+
} else {
|
|
365
|
+
instance.status = "PAUSED";
|
|
366
|
+
instance.updatedAt = now;
|
|
367
|
+
}
|
|
337
368
|
await em.flush();
|
|
338
369
|
return {
|
|
339
370
|
status: "WAITING",
|
|
@@ -448,7 +479,7 @@ async function handleSubWorkflowStep(em, container, instance, stepInstance, step
|
|
|
448
479
|
};
|
|
449
480
|
}
|
|
450
481
|
}
|
|
451
|
-
async function handleWaitForSignalStep(em, instance, stepInstance, stepDef, context) {
|
|
482
|
+
async function handleWaitForSignalStep(em, instance, stepInstance, stepDef, context, branch) {
|
|
452
483
|
const signalConfig = stepDef.signalConfig || {};
|
|
453
484
|
const signalName = signalConfig.signalName || stepDef.stepId;
|
|
454
485
|
const timeout = signalConfig.timeout ? parseDuration(signalConfig.timeout) : null;
|
|
@@ -456,6 +487,7 @@ async function handleWaitForSignalStep(em, instance, stepInstance, stepDef, cont
|
|
|
456
487
|
await logStepEvent(em, {
|
|
457
488
|
workflowInstanceId: instance.id,
|
|
458
489
|
stepInstanceId: stepInstance.id,
|
|
490
|
+
...branch ? { branchInstanceId: branch.id } : {},
|
|
459
491
|
eventType: "SIGNAL_AWAITING",
|
|
460
492
|
eventData: {
|
|
461
493
|
signalName,
|
|
@@ -466,9 +498,14 @@ async function handleWaitForSignalStep(em, instance, stepInstance, stepDef, cont
|
|
|
466
498
|
tenantId: instance.tenantId,
|
|
467
499
|
organizationId: instance.organizationId
|
|
468
500
|
});
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
501
|
+
if (branch) {
|
|
502
|
+
branch.status = "PAUSED";
|
|
503
|
+
branch.updatedAt = now;
|
|
504
|
+
} else {
|
|
505
|
+
instance.status = "PAUSED";
|
|
506
|
+
instance.pausedAt = now;
|
|
507
|
+
instance.updatedAt = now;
|
|
508
|
+
}
|
|
472
509
|
await em.flush();
|
|
473
510
|
return {
|
|
474
511
|
status: "WAITING",
|
|
@@ -480,7 +517,7 @@ async function handleWaitForSignalStep(em, instance, stepInstance, stepDef, cont
|
|
|
480
517
|
}
|
|
481
518
|
};
|
|
482
519
|
}
|
|
483
|
-
async function handleWaitForTimerStep(em, instance, stepInstance, stepDef, context) {
|
|
520
|
+
async function handleWaitForTimerStep(em, instance, stepInstance, stepDef, context, branch) {
|
|
484
521
|
const timerConfig = stepDef.config || stepDef.timerConfig || {};
|
|
485
522
|
const duration = timerConfig.duration;
|
|
486
523
|
const until = timerConfig.until;
|
|
@@ -524,6 +561,7 @@ async function handleWaitForTimerStep(em, instance, stepInstance, stepDef, conte
|
|
|
524
561
|
const jobId = await enqueueTimerJob({
|
|
525
562
|
workflowInstanceId: instance.id,
|
|
526
563
|
stepInstanceId: stepInstance.id,
|
|
564
|
+
branchInstanceId: branch ? branch.id : void 0,
|
|
527
565
|
tenantId: instance.tenantId,
|
|
528
566
|
organizationId: instance.organizationId,
|
|
529
567
|
userId: context.userId,
|
|
@@ -533,6 +571,7 @@ async function handleWaitForTimerStep(em, instance, stepInstance, stepDef, conte
|
|
|
533
571
|
await logWorkflowEvent(em, {
|
|
534
572
|
workflowInstanceId: instance.id,
|
|
535
573
|
stepInstanceId: stepInstance.id,
|
|
574
|
+
...branch ? { branchInstanceId: branch.id } : {},
|
|
536
575
|
eventType: "TIMER_AWAITING",
|
|
537
576
|
eventData: {
|
|
538
577
|
fireAt: fireAt.toISOString(),
|
|
@@ -544,9 +583,14 @@ async function handleWaitForTimerStep(em, instance, stepInstance, stepDef, conte
|
|
|
544
583
|
tenantId: instance.tenantId,
|
|
545
584
|
organizationId: instance.organizationId
|
|
546
585
|
});
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
586
|
+
if (branch) {
|
|
587
|
+
branch.status = "PAUSED";
|
|
588
|
+
branch.updatedAt = now;
|
|
589
|
+
} else {
|
|
590
|
+
instance.status = "PAUSED";
|
|
591
|
+
instance.pausedAt = now;
|
|
592
|
+
instance.updatedAt = now;
|
|
593
|
+
}
|
|
550
594
|
await em.flush();
|
|
551
595
|
return {
|
|
552
596
|
status: "WAITING",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/workflows/lib/step-handler.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Workflows Module - Step Handler Service\n *\n * Handles individual workflow step execution:\n * - Creating step instances when entering a step\n * - Executing step logic based on step type (START, END, AUTOMATED, USER_TASK)\n * - Completing step instances when exiting\n *\n * Functional API (no classes) following Open Mercato conventions.\n */\n\nimport { EntityManager } from '@mikro-orm/core'\nimport {\n WorkflowInstance,\n WorkflowDefinition,\n StepInstance,\n UserTask,\n WorkflowEvent,\n type StepInstanceStatus,\n type WorkflowStepType,\n} from '../data/entities'\nimport { parseDuration } from './duration'\nimport { logWorkflowEvent } from './event-logger'\n\n// ============================================================================\n// Types and Interfaces\n// ============================================================================\n\nexport interface StepExecutionContext {\n workflowContext: Record<string, any>\n userId?: string\n triggerData?: any\n}\n\nexport interface StepExecutionResult {\n status: 'COMPLETED' | 'WAITING' | 'FAILED'\n outputData?: any\n nextSteps?: string[] // For parallel forks (Phase 7)\n waitReason?: 'USER_TASK' | 'SIGNAL' | 'TIMER'\n error?: string\n}\n\nexport class StepExecutionError extends Error {\n constructor(\n message: string,\n public code: string,\n public details?: any\n ) {\n super(message)\n this.name = 'StepExecutionError'\n }\n}\n\n// ============================================================================\n// Main Step Execution Functions\n// ============================================================================\n\n/**\n * Enter a workflow step - create step instance and mark as ACTIVE\n *\n * @param em - Entity manager\n * @param instance - Workflow instance\n * @param stepId - Step ID to enter\n * @param context - Execution context\n * @returns Created step instance\n */\nexport async function enterStep(\n em: EntityManager,\n instance: WorkflowInstance,\n stepId: string,\n context: StepExecutionContext\n): Promise<StepInstance> {\n // Load workflow definition to get step details\n const definition = await em.findOne(WorkflowDefinition, {\n id: instance.definitionId,\n })\n\n if (!definition) {\n throw new StepExecutionError(\n `Workflow definition not found: ${instance.definitionId}`,\n 'DEFINITION_NOT_FOUND',\n { definitionId: instance.definitionId }\n )\n }\n\n // Find step in definition\n const stepDef = definition.definition.steps.find((s: any) => s.stepId === stepId)\n if (!stepDef) {\n throw new StepExecutionError(\n `Step not found in workflow definition: ${stepId}`,\n 'STEP_NOT_FOUND',\n { workflowId: definition.workflowId, stepId }\n )\n }\n\n const now = new Date()\n\n // Create step instance\n const stepInstance = em.create(StepInstance, {\n workflowInstanceId: instance.id,\n stepId: stepDef.stepId,\n stepName: stepDef.stepName,\n stepType: stepDef.stepType,\n status: 'ACTIVE',\n inputData: context.triggerData || null,\n enteredAt: now,\n retryCount: 0,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n createdAt: now,\n updatedAt: now,\n })\n\n await em.persist(stepInstance).flush()\n\n // Log STEP_ENTERED event\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n eventType: 'STEP_ENTERED',\n eventData: {\n stepId: stepDef.stepId,\n stepName: stepDef.stepName,\n stepType: stepDef.stepType,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return stepInstance\n}\n\n/**\n * Exit a workflow step - mark as completed and record timing\n *\n * @param em - Entity manager\n * @param stepInstance - Step instance to exit\n * @param outputData - Optional output data from step execution\n */\nexport async function exitStep(\n em: EntityManager,\n stepInstance: StepInstance,\n outputData?: any\n): Promise<void> {\n const now = new Date()\n\n // Calculate execution time if we have enteredAt\n let executionTimeMs: number | null = null\n if (stepInstance.enteredAt) {\n executionTimeMs = now.getTime() - stepInstance.enteredAt.getTime()\n }\n\n // Update step instance\n stepInstance.status = 'COMPLETED'\n stepInstance.outputData = outputData || null\n stepInstance.exitedAt = now\n stepInstance.executionTimeMs = executionTimeMs\n stepInstance.updatedAt = now\n\n await em.flush()\n\n // Log STEP_EXITED event\n await logStepEvent(em, {\n workflowInstanceId: stepInstance.workflowInstanceId,\n stepInstanceId: stepInstance.id,\n eventType: 'STEP_EXITED',\n eventData: {\n stepId: stepInstance.stepId,\n status: 'COMPLETED',\n executionTimeMs,\n hasOutput: !!outputData,\n },\n tenantId: stepInstance.tenantId,\n organizationId: stepInstance.organizationId,\n })\n}\n\n/**\n * Execute a workflow step based on its type\n *\n * Main entry point for step execution. Handles:\n * - START: Immediate completion\n * - END: Workflow completion\n * - AUTOMATED: Activity execution (MVP: immediate completion)\n * - USER_TASK: Create user task and wait\n * - SUB_WORKFLOW: Invoke child workflow\n *\n * @param em - Entity manager\n * @param instance - Workflow instance\n * @param stepId - Step ID to execute\n * @param context - Execution context\n * @param container - DI container (required for SUB_WORKFLOW steps)\n * @returns Execution result with status and output\n */\nexport async function executeStep(\n em: EntityManager,\n instance: WorkflowInstance,\n stepId: string,\n context: StepExecutionContext,\n container?: any\n): Promise<StepExecutionResult> {\n try {\n // Enter the step (create step instance)\n const stepInstance = await enterStep(em, instance, stepId, context)\n\n // Load workflow definition to get step configuration\n const definition = await em.findOne(WorkflowDefinition, {\n id: instance.definitionId,\n })\n\n if (!definition) {\n throw new StepExecutionError(\n `Workflow definition not found: ${instance.definitionId}`,\n 'DEFINITION_NOT_FOUND',\n { definitionId: instance.definitionId }\n )\n }\n\n const stepDef = definition.definition.steps.find((s: any) => s.stepId === stepId)\n if (!stepDef) {\n throw new StepExecutionError(\n `Step not found: ${stepId}`,\n 'STEP_NOT_FOUND',\n { stepId }\n )\n }\n\n // Execute based on step type\n const result = await executeStepByType(\n em,\n instance,\n stepInstance,\n stepDef,\n context,\n container\n )\n\n // If step completed, exit it\n if (result.status === 'COMPLETED') {\n await exitStep(em, stepInstance, result.outputData)\n }\n\n return result\n } catch (error) {\n // Handle step execution errors\n const errorMessage = error instanceof Error ? error.message : String(error)\n\n // Try to mark step as failed if we have a step instance\n try {\n const failedStepInstance = await em.findOne(StepInstance, {\n workflowInstanceId: instance.id,\n stepId,\n status: 'ACTIVE',\n })\n\n if (failedStepInstance) {\n failedStepInstance.status = 'FAILED'\n failedStepInstance.errorData = {\n error: errorMessage,\n details: error instanceof StepExecutionError ? error.details : undefined,\n }\n failedStepInstance.exitedAt = new Date()\n failedStepInstance.updatedAt = new Date()\n await em.flush()\n\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: failedStepInstance.id,\n eventType: 'STEP_FAILED',\n eventData: { error: errorMessage },\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n }\n } catch (updateError) {\n // Swallow update errors to preserve original error\n console.error('Failed to update step instance with error:', updateError)\n }\n\n return {\n status: 'FAILED',\n error: errorMessage,\n }\n }\n}\n\n// ============================================================================\n// Step Type Handlers\n// ============================================================================\n\n/**\n * Execute step based on its type\n *\n * @param em - Entity manager\n * @param instance - Workflow instance\n * @param stepInstance - Step instance\n * @param stepDef - Step definition from workflow\n * @param context - Execution context\n * @returns Execution result\n */\nasync function executeStepByType(\n em: EntityManager,\n instance: WorkflowInstance,\n stepInstance: StepInstance,\n stepDef: any,\n context: StepExecutionContext,\n container?: any\n): Promise<StepExecutionResult> {\n const stepType: WorkflowStepType = stepDef.stepType\n\n switch (stepType) {\n case 'START':\n return handleStartStep(stepDef, context)\n\n case 'END':\n return handleEndStep(stepDef, context)\n\n case 'AUTOMATED':\n return await handleAutomatedStep(em, instance, stepInstance, stepDef, context, container)\n\n case 'USER_TASK':\n return await handleUserTaskStep(em, instance, stepInstance, stepDef, context)\n\n case 'SUB_WORKFLOW':\n if (!container) {\n throw new StepExecutionError(\n 'Container required for SUB_WORKFLOW execution',\n 'CONTAINER_REQUIRED',\n { stepType }\n )\n }\n return await handleSubWorkflowStep(em, container, instance, stepInstance, stepDef, context)\n\n case 'WAIT_FOR_SIGNAL':\n return await handleWaitForSignalStep(em, instance, stepInstance, stepDef, context)\n\n case 'WAIT_FOR_TIMER':\n return await handleWaitForTimerStep(em, instance, stepInstance, stepDef, context)\n\n case 'PARALLEL_FORK':\n case 'PARALLEL_JOIN':\n // These will be implemented in later phases\n throw new StepExecutionError(\n `Step type not yet implemented: ${stepType}`,\n 'STEP_TYPE_NOT_IMPLEMENTED',\n { stepType }\n )\n\n default:\n throw new StepExecutionError(\n `Unknown step type: ${stepType}`,\n 'UNKNOWN_STEP_TYPE',\n { stepType }\n )\n }\n}\n\n/**\n * Handle START step - no-op, immediately complete\n */\nfunction handleStartStep(\n stepDef: any,\n context: StepExecutionContext\n): StepExecutionResult {\n return {\n status: 'COMPLETED',\n outputData: {\n stepType: 'START',\n timestamp: new Date().toISOString(),\n },\n }\n}\n\n/**\n * Handle END step - mark as complete\n */\nfunction handleEndStep(\n stepDef: any,\n context: StepExecutionContext\n): StepExecutionResult {\n return {\n status: 'COMPLETED',\n outputData: {\n stepType: 'END',\n timestamp: new Date().toISOString(),\n finalContext: context.workflowContext,\n },\n }\n}\n\n/**\n * Handle AUTOMATED step - execute activities\n *\n * Executes activities defined in step configuration.\n * Supports both sync and async activities.\n */\nasync function handleAutomatedStep(\n em: EntityManager,\n instance: WorkflowInstance,\n stepInstance: StepInstance,\n stepDef: any,\n context: StepExecutionContext,\n container?: any\n): Promise<StepExecutionResult> {\n // Extract activities from step definition\n const activities = stepDef.activities || []\n\n if (activities.length === 0) {\n // No activities defined - immediate completion (legacy behavior)\n return {\n status: 'COMPLETED',\n outputData: {\n stepType: 'AUTOMATED',\n timestamp: new Date().toISOString(),\n },\n }\n }\n\n // Import activity executor\n const { executeActivities } = await import('./activity-executor')\n\n try {\n // Execute activities with proper context\n const results = await executeActivities(em, container, activities, {\n workflowInstance: instance,\n workflowContext: context.workflowContext,\n stepContext: { stepId: stepDef.stepId, stepName: stepDef.stepName },\n stepInstanceId: stepInstance.id,\n userId: context.userId,\n })\n\n // Check if there are pending async activities\n const pendingActivities = results.filter(r => r.async && !r.success)\n if (pendingActivities.length > 0) {\n // Workflow should pause and wait for async activities\n instance.status = 'WAITING_FOR_ACTIVITIES'\n instance.pausedAt = new Date()\n instance.updatedAt = new Date()\n await em.flush()\n\n return {\n status: 'WAITING',\n waitReason: 'SIGNAL', // Reuse SIGNAL wait reason (will be resumed by activity completion)\n outputData: {\n pendingActivities: pendingActivities.map(r => ({\n activityId: r.activityId,\n activityName: r.activityName,\n jobId: r.jobId,\n })),\n },\n }\n }\n\n // Check for failures in sync activities\n const failures = results.filter(r => !r.success && !r.async)\n if (failures.length > 0) {\n const errorMessages = failures.map(f => `${f.activityName || f.activityId}: ${f.error}`).join('; ')\n return {\n status: 'FAILED',\n error: `${failures.length} activity(ies) failed: ${errorMessages}`,\n outputData: {\n failures: failures.map(f => ({\n activityId: f.activityId,\n activityName: f.activityName,\n error: f.error,\n retryCount: f.retryCount,\n })),\n },\n }\n }\n\n // All activities completed successfully\n const activityOutputs = results.reduce((acc, r) => {\n if (r.output) {\n acc[r.activityId] = r.output\n }\n return acc\n }, {} as Record<string, any>)\n\n return {\n status: 'COMPLETED',\n outputData: {\n stepType: 'AUTOMATED',\n timestamp: new Date().toISOString(),\n activityResults: activityOutputs,\n activityCount: results.length,\n },\n }\n } catch (error: any) {\n return {\n status: 'FAILED',\n error: `Activity execution failed: ${error.message}`,\n }\n }\n}\n\n/**\n * Handle USER_TASK step - create user task and enter waiting state\n *\n * Creates a UserTask entity and returns WAITING status.\n * The workflow will pause until the task is completed by a user.\n */\nasync function handleUserTaskStep(\n em: EntityManager,\n instance: WorkflowInstance,\n stepInstance: StepInstance,\n stepDef: any,\n context: StepExecutionContext\n): Promise<StepExecutionResult> {\n const userTaskConfig = stepDef.userTaskConfig || {}\n\n // Handle assignedTo - if it's an array, treat it as roles\n let assignedTo = userTaskConfig.assignedTo || null\n let assignedToRoles = userTaskConfig.assignedToRoles || null\n\n if (Array.isArray(assignedTo)) {\n assignedToRoles = assignedTo\n assignedTo = null\n }\n\n // Create user task\n const now = new Date()\n const userTask = em.create(UserTask, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n taskName: stepDef.stepName,\n description: stepDef.description || null,\n status: 'PENDING',\n formSchema: userTaskConfig.formSchema || null,\n formData: null,\n assignedTo: assignedTo,\n assignedToRoles: assignedToRoles,\n dueDate: userTaskConfig.slaDuration ? calculateDueDate(userTaskConfig.slaDuration) : null,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n createdAt: now,\n updatedAt: now,\n })\n\n await em.persist(userTask).flush()\n\n // Log USER_TASK_CREATED event\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n eventType: 'USER_TASK_CREATED',\n eventData: {\n userTaskId: userTask.id,\n taskName: userTask.taskName,\n assignedTo: userTask.assignedTo,\n assignedToRoles: userTask.assignedToRoles,\n },\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n // Pause workflow execution - workflow waits for user task completion\n instance.status = 'PAUSED'\n instance.updatedAt = now\n await em.flush()\n\n return {\n status: 'WAITING',\n waitReason: 'USER_TASK',\n outputData: {\n userTaskId: userTask.id,\n },\n }\n}\n\n/**\n * Handle SUB_WORKFLOW step - invoke another workflow and wait for completion\n *\n * Creates a child workflow instance with mapped input data,\n * executes it synchronously, and returns mapped output data.\n */\nasync function handleSubWorkflowStep(\n em: EntityManager,\n container: any,\n instance: WorkflowInstance,\n stepInstance: StepInstance,\n stepDef: any,\n context: StepExecutionContext\n): Promise<StepExecutionResult> {\n const { subWorkflowId, inputMapping, outputMapping, version } = stepDef.config || {}\n\n if (!subWorkflowId) {\n return {\n status: 'FAILED',\n error: 'Sub-workflow ID not specified in step configuration'\n }\n }\n\n // Map input data from parent context to child context\n const childContext = mapInputData(instance.context, inputMapping || {})\n\n // Import workflow executor functions\n const { startWorkflow, executeWorkflow } = await import('./workflow-executor')\n\n try {\n // Start child workflow with parent metadata\n const childInstance = await startWorkflow(em, {\n workflowId: subWorkflowId,\n version,\n initialContext: childContext,\n correlationKey: instance.correlationKey || undefined,\n metadata: {\n ...instance.metadata,\n labels: {\n ...instance.metadata?.labels,\n parentInstanceId: instance.id,\n parentStepId: stepDef.stepId,\n parentStepInstanceId: stepInstance.id,\n },\n },\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n // Log sub-workflow invocation event\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n eventType: 'SUB_WORKFLOW_STARTED',\n eventData: {\n childInstanceId: childInstance.id,\n subWorkflowId,\n version,\n inputData: childContext,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n // Execute child workflow synchronously\n const result = await executeWorkflow(em, container, childInstance.id, {\n userId: context.userId,\n })\n\n // Handle child workflow result\n if (result.status === 'COMPLETED') {\n // Map output data from child context to parent context\n const outputData = mapOutputData(result.context, outputMapping || {})\n\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n eventType: 'SUB_WORKFLOW_COMPLETED',\n eventData: {\n childInstanceId: childInstance.id,\n outputData,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return {\n status: 'COMPLETED',\n outputData,\n }\n } else if (result.status === 'FAILED') {\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n eventType: 'SUB_WORKFLOW_FAILED',\n eventData: {\n childInstanceId: childInstance.id,\n error: result.errors?.join(', '),\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return {\n status: 'FAILED',\n error: `Sub-workflow failed: ${result.errors?.join(', ')}`,\n }\n } else {\n // WAITING, PAUSED, etc. - For synchronous execution, treat as error\n return {\n status: 'FAILED',\n error: `Sub-workflow ended in unexpected state: ${result.status}`,\n }\n }\n } catch (error: any) {\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n eventType: 'SUB_WORKFLOW_FAILED',\n eventData: {\n subWorkflowId,\n error: error.message,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return {\n status: 'FAILED',\n error: `Sub-workflow execution failed: ${error.message}`,\n }\n }\n}\n\n/**\n * Handle WAIT_FOR_SIGNAL step - pause workflow until signal received\n *\n * Creates a waiting state and pauses the workflow until an external signal\n * with the matching signal name is received. The signal payload will be merged\n * into the workflow context when received.\n */\nasync function handleWaitForSignalStep(\n em: EntityManager,\n instance: WorkflowInstance,\n stepInstance: StepInstance,\n stepDef: any,\n context: StepExecutionContext\n): Promise<StepExecutionResult> {\n const signalConfig = stepDef.signalConfig || {}\n const signalName = signalConfig.signalName || stepDef.stepId\n const timeout = signalConfig.timeout ? parseDuration(signalConfig.timeout) : null\n\n const now = new Date()\n\n // Log signal awaiting event\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n eventType: 'SIGNAL_AWAITING',\n eventData: {\n signalName,\n timeout,\n description: stepDef.description,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n // Pause workflow execution\n instance.status = 'PAUSED'\n instance.pausedAt = now\n instance.updatedAt = now\n await em.flush()\n\n // Return WAITING status to halt executor\n return {\n status: 'WAITING',\n waitReason: 'SIGNAL',\n outputData: {\n signalName,\n timeout,\n awaitingSince: now,\n },\n }\n}\n\n/**\n * Handle WAIT_FOR_TIMER step - pause workflow until a timer fires.\n *\n * Reads `duration` (relative, e.g. \"PT5M\") or `until` (ISO 8601 datetime) from\n * `stepDef.config` (preferred \u2014 matches StepsEditor) or `stepDef.timerConfig`.\n * Enqueues a delayed timer job on the workflow-activities queue; when the job\n * is processed by the activity worker, it calls `timerHandler.fireTimer` to\n * resume the workflow.\n */\nasync function handleWaitForTimerStep(\n em: EntityManager,\n instance: WorkflowInstance,\n stepInstance: StepInstance,\n stepDef: any,\n context: StepExecutionContext\n): Promise<StepExecutionResult> {\n const timerConfig = stepDef.config || stepDef.timerConfig || {}\n const duration: string | undefined = timerConfig.duration\n const until: string | undefined = timerConfig.until\n\n if (!duration && !until) {\n throw new StepExecutionError(\n 'WAIT_FOR_TIMER requires either \"duration\" (e.g., \"PT5M\") or \"until\" (ISO 8601 datetime)',\n 'TIMER_CONFIG_MISSING',\n { stepId: stepDef.stepId }\n )\n }\n\n let fireAtMs: number\n if (until) {\n const targetDate = new Date(until)\n if (isNaN(targetDate.getTime())) {\n throw new StepExecutionError(\n `WAIT_FOR_TIMER invalid \"until\" datetime: ${until}`,\n 'TIMER_CONFIG_INVALID',\n { until }\n )\n }\n fireAtMs = targetDate.getTime()\n } else {\n fireAtMs = Date.now() + parseDuration(duration as string)\n }\n\n const delayMs = fireAtMs - Date.now()\n const fireAt = new Date(fireAtMs)\n\n // Immediate-fire path: skip the queue round-trip if the timer is in the past\n if (delayMs <= 0) {\n return {\n status: 'COMPLETED',\n outputData: {\n stepType: 'WAIT_FOR_TIMER',\n timerFiredImmediately: true,\n fireAt,\n duration,\n until,\n },\n }\n }\n\n const now = new Date()\n\n // Enqueue delayed timer job via the shared activity queue.\n // Imported here to avoid a top-level cycle between step-handler and activity-executor.\n const { enqueueTimerJob } = await import('./activity-executor')\n const jobId = await enqueueTimerJob({\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n userId: context.userId,\n fireAt: fireAt.toISOString(),\n delayMs,\n })\n\n await logWorkflowEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n eventType: 'TIMER_AWAITING',\n eventData: {\n fireAt: fireAt.toISOString(),\n duration: duration || null,\n until: until || null,\n jobId,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n instance.status = 'PAUSED'\n instance.pausedAt = now\n instance.updatedAt = now\n await em.flush()\n\n return {\n status: 'WAITING',\n waitReason: 'TIMER',\n outputData: {\n fireAt,\n duration,\n until,\n jobId,\n },\n }\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n// parseDuration is imported from ./duration\n\n/**\n * Log step-related event to event sourcing table\n */\nasync function logStepEvent(\n em: EntityManager,\n event: {\n workflowInstanceId: string\n stepInstanceId: string\n eventType: string\n eventData: any\n userId?: string\n tenantId: string\n organizationId: string\n }\n): Promise<WorkflowEvent> {\n const workflowEvent = em.create(WorkflowEvent, {\n ...event,\n occurredAt: new Date(),\n })\n\n await em.persist(workflowEvent).flush()\n return workflowEvent\n}\n\n/**\n * Calculate due date from ISO 8601 duration string\n *\n * @param duration - ISO 8601 duration (e.g., \"P1D\" for 1 day)\n * @returns Due date\n */\nfunction calculateDueDate(duration: string): Date {\n // Simple implementation for MVP\n // Supports: P1D (1 day), P1H (1 hour), P1W (1 week)\n const now = new Date()\n\n const daysMatch = duration.match(/P(\\d+)D/)\n if (daysMatch) {\n const days = parseInt(daysMatch[1], 10)\n return new Date(now.getTime() + days * 24 * 60 * 60 * 1000)\n }\n\n const hoursMatch = duration.match(/PT(\\d+)H/)\n if (hoursMatch) {\n const hours = parseInt(hoursMatch[1], 10)\n return new Date(now.getTime() + hours * 60 * 60 * 1000)\n }\n\n const weeksMatch = duration.match(/P(\\d+)W/)\n if (weeksMatch) {\n const weeks = parseInt(weeksMatch[1], 10)\n return new Date(now.getTime() + weeks * 7 * 24 * 60 * 60 * 1000)\n }\n\n // Default: 1 day\n return new Date(now.getTime() + 24 * 60 * 60 * 1000)\n}\n\n/**\n * Get nested value from object using dot notation\n *\n * @param obj - Source object\n * @param path - Dot-notation path (e.g., \"user.email\")\n * @returns Value at path or undefined\n */\nfunction getNestedValue(obj: any, path: string): any {\n return path.split('.').reduce((current, key) => current?.[key], obj)\n}\n\n/**\n * Set nested value in object using dot notation\n *\n * @param obj - Target object\n * @param path - Dot-notation path (e.g., \"user.email\")\n * @param value - Value to set\n */\nfunction setNestedValue(obj: any, path: string, value: any): void {\n const keys = path.split('.')\n const lastKey = keys.pop()!\n const target = keys.reduce((current, key) => {\n if (!(key in current)) current[key] = {}\n return current[key]\n }, obj)\n target[lastKey] = value\n}\n\n/**\n * Map data from source context using mapping configuration\n *\n * @param sourceContext - Source data object\n * @param mapping - Mapping configuration (targetKey -> sourcePath)\n * @returns Mapped data object\n */\nfunction mapInputData(\n sourceContext: Record<string, any>,\n mapping: Record<string, string>\n): Record<string, any> {\n const result: Record<string, any> = {}\n\n for (const [targetKey, sourcePath] of Object.entries(mapping)) {\n const value = getNestedValue(sourceContext, sourcePath)\n if (value !== undefined) {\n setNestedValue(result, targetKey, value)\n }\n }\n\n // If no mapping provided, pass entire context\n return Object.keys(result).length > 0 ? result : sourceContext\n}\n\n/**\n * Map output data from child context back to parent\n *\n * @param childContext - Child workflow context\n * @param mapping - Mapping configuration (targetKey -> sourcePath)\n * @returns Mapped output data\n */\nfunction mapOutputData(\n childContext: Record<string, any>,\n mapping: Record<string, string>\n): Record<string, any> {\n const result: Record<string, any> = {}\n\n for (const [targetKey, sourcePath] of Object.entries(mapping)) {\n const value = getNestedValue(childContext, sourcePath)\n if (value !== undefined) {\n setNestedValue(result, targetKey, value)\n }\n }\n\n // If no mapping provided, pass entire child context\n return Object.keys(result).length > 0 ? result : childContext\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * Workflows Module - Step Handler Service\n *\n * Handles individual workflow step execution:\n * - Creating step instances when entering a step\n * - Executing step logic based on step type (START, END, AUTOMATED, USER_TASK)\n * - Completing step instances when exiting\n *\n * Functional API (no classes) following Open Mercato conventions.\n */\n\nimport { EntityManager } from '@mikro-orm/core'\nimport {\n WorkflowInstance,\n WorkflowBranchInstance,\n WorkflowDefinition,\n StepInstance,\n UserTask,\n WorkflowEvent,\n type StepInstanceStatus,\n type WorkflowStepType,\n} from '../data/entities'\nimport { parseDuration } from './duration'\nimport { logWorkflowEvent } from './event-logger'\n\n// ============================================================================\n// Types and Interfaces\n// ============================================================================\n\nexport interface StepExecutionContext {\n workflowContext: Record<string, any>\n userId?: string\n triggerData?: any\n}\n\nexport interface StepExecutionResult {\n status: 'COMPLETED' | 'WAITING' | 'FAILED'\n outputData?: any\n nextSteps?: string[] // For parallel forks (Phase 7)\n waitReason?: 'USER_TASK' | 'SIGNAL' | 'TIMER' | 'FORK'\n error?: string\n}\n\nexport class StepExecutionError extends Error {\n constructor(\n message: string,\n public code: string,\n public details?: any\n ) {\n super(message)\n this.name = 'StepExecutionError'\n }\n}\n\n// ============================================================================\n// Main Step Execution Functions\n// ============================================================================\n\n/**\n * Enter a workflow step - create step instance and mark as ACTIVE\n *\n * @param em - Entity manager\n * @param instance - Workflow instance\n * @param stepId - Step ID to enter\n * @param context - Execution context\n * @returns Created step instance\n */\nexport async function enterStep(\n em: EntityManager,\n instance: WorkflowInstance,\n stepId: string,\n context: StepExecutionContext,\n branch?: WorkflowBranchInstance | null\n): Promise<StepInstance> {\n // Load workflow definition to get step details\n const definition = await em.findOne(WorkflowDefinition, {\n id: instance.definitionId,\n })\n\n if (!definition) {\n throw new StepExecutionError(\n `Workflow definition not found: ${instance.definitionId}`,\n 'DEFINITION_NOT_FOUND',\n { definitionId: instance.definitionId }\n )\n }\n\n // Find step in definition\n const stepDef = definition.definition.steps.find((s: any) => s.stepId === stepId)\n if (!stepDef) {\n throw new StepExecutionError(\n `Step not found in workflow definition: ${stepId}`,\n 'STEP_NOT_FOUND',\n { workflowId: definition.workflowId, stepId }\n )\n }\n\n const now = new Date()\n\n // Create step instance\n const stepInstance = em.create(StepInstance, {\n workflowInstanceId: instance.id,\n branchInstanceId: branch ? branch.id : null,\n stepId: stepDef.stepId,\n stepName: stepDef.stepName,\n stepType: stepDef.stepType,\n status: 'ACTIVE',\n inputData: context.triggerData || null,\n enteredAt: now,\n retryCount: 0,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n createdAt: now,\n updatedAt: now,\n })\n\n await em.persist(stepInstance).flush()\n\n // Log STEP_ENTERED event\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n ...(branch ? { branchInstanceId: branch.id } : {}),\n eventType: 'STEP_ENTERED',\n eventData: {\n stepId: stepDef.stepId,\n stepName: stepDef.stepName,\n stepType: stepDef.stepType,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return stepInstance\n}\n\n/**\n * Exit a workflow step - mark as completed and record timing\n *\n * @param em - Entity manager\n * @param stepInstance - Step instance to exit\n * @param outputData - Optional output data from step execution\n */\nexport async function exitStep(\n em: EntityManager,\n stepInstance: StepInstance,\n outputData?: any\n): Promise<void> {\n const now = new Date()\n\n // Calculate execution time if we have enteredAt\n let executionTimeMs: number | null = null\n if (stepInstance.enteredAt) {\n executionTimeMs = now.getTime() - stepInstance.enteredAt.getTime()\n }\n\n // Update step instance\n stepInstance.status = 'COMPLETED'\n stepInstance.outputData = outputData || null\n stepInstance.exitedAt = now\n stepInstance.executionTimeMs = executionTimeMs\n stepInstance.updatedAt = now\n\n await em.flush()\n\n // Log STEP_EXITED event\n await logStepEvent(em, {\n workflowInstanceId: stepInstance.workflowInstanceId,\n stepInstanceId: stepInstance.id,\n eventType: 'STEP_EXITED',\n eventData: {\n stepId: stepInstance.stepId,\n status: 'COMPLETED',\n executionTimeMs,\n hasOutput: !!outputData,\n },\n tenantId: stepInstance.tenantId,\n organizationId: stepInstance.organizationId,\n })\n}\n\n/**\n * Execute a workflow step based on its type\n *\n * Main entry point for step execution. Handles:\n * - START: Immediate completion\n * - END: Workflow completion\n * - AUTOMATED: Activity execution (MVP: immediate completion)\n * - USER_TASK: Create user task and wait\n * - SUB_WORKFLOW: Invoke child workflow\n *\n * @param em - Entity manager\n * @param instance - Workflow instance\n * @param stepId - Step ID to execute\n * @param context - Execution context\n * @param container - DI container (required for SUB_WORKFLOW steps)\n * @returns Execution result with status and output\n */\nexport async function executeStep(\n em: EntityManager,\n instance: WorkflowInstance,\n stepId: string,\n context: StepExecutionContext,\n container?: any,\n branch?: WorkflowBranchInstance | null\n): Promise<StepExecutionResult> {\n try {\n // Enter the step (create step instance)\n const stepInstance = await enterStep(em, instance, stepId, context, branch)\n\n // Load workflow definition to get step configuration\n const definition = await em.findOne(WorkflowDefinition, {\n id: instance.definitionId,\n })\n\n if (!definition) {\n throw new StepExecutionError(\n `Workflow definition not found: ${instance.definitionId}`,\n 'DEFINITION_NOT_FOUND',\n { definitionId: instance.definitionId }\n )\n }\n\n const stepDef = definition.definition.steps.find((s: any) => s.stepId === stepId)\n if (!stepDef) {\n throw new StepExecutionError(\n `Step not found: ${stepId}`,\n 'STEP_NOT_FOUND',\n { stepId }\n )\n }\n\n // Execute based on step type\n const result = await executeStepByType(\n em,\n instance,\n stepInstance,\n stepDef,\n context,\n container,\n branch\n )\n\n // If step completed, exit it\n if (result.status === 'COMPLETED') {\n await exitStep(em, stepInstance, result.outputData)\n }\n\n return result\n } catch (error) {\n // Handle step execution errors\n const errorMessage = error instanceof Error ? error.message : String(error)\n\n // Try to mark step as failed if we have a step instance\n try {\n const failedStepInstance = await em.findOne(StepInstance, {\n workflowInstanceId: instance.id,\n stepId,\n status: 'ACTIVE',\n })\n\n if (failedStepInstance) {\n failedStepInstance.status = 'FAILED'\n failedStepInstance.errorData = {\n error: errorMessage,\n details: error instanceof StepExecutionError ? error.details : undefined,\n }\n failedStepInstance.exitedAt = new Date()\n failedStepInstance.updatedAt = new Date()\n await em.flush()\n\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: failedStepInstance.id,\n eventType: 'STEP_FAILED',\n eventData: { error: errorMessage },\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n }\n } catch (updateError) {\n // Swallow update errors to preserve original error\n console.error('Failed to update step instance with error:', updateError)\n }\n\n return {\n status: 'FAILED',\n error: errorMessage,\n }\n }\n}\n\n// ============================================================================\n// Step Type Handlers\n// ============================================================================\n\n/**\n * Execute step based on its type\n *\n * @param em - Entity manager\n * @param instance - Workflow instance\n * @param stepInstance - Step instance\n * @param stepDef - Step definition from workflow\n * @param context - Execution context\n * @returns Execution result\n */\nasync function executeStepByType(\n em: EntityManager,\n instance: WorkflowInstance,\n stepInstance: StepInstance,\n stepDef: any,\n context: StepExecutionContext,\n container?: any,\n branch?: WorkflowBranchInstance | null\n): Promise<StepExecutionResult> {\n const stepType: WorkflowStepType = stepDef.stepType\n\n switch (stepType) {\n case 'START':\n return handleStartStep(stepDef, context)\n\n case 'END':\n return handleEndStep(stepDef, context)\n\n case 'AUTOMATED':\n return await handleAutomatedStep(em, instance, stepInstance, stepDef, context, container, branch)\n\n case 'USER_TASK':\n return await handleUserTaskStep(em, instance, stepInstance, stepDef, context, branch)\n\n case 'SUB_WORKFLOW':\n if (!container) {\n throw new StepExecutionError(\n 'Container required for SUB_WORKFLOW execution',\n 'CONTAINER_REQUIRED',\n { stepType }\n )\n }\n return await handleSubWorkflowStep(em, container, instance, stepInstance, stepDef, context)\n\n case 'WAIT_FOR_SIGNAL':\n return await handleWaitForSignalStep(em, instance, stepInstance, stepDef, context, branch)\n\n case 'WAIT_FOR_TIMER':\n return await handleWaitForTimerStep(em, instance, stepInstance, stepDef, context, branch)\n\n case 'PARALLEL_FORK': {\n // Entering a fork opens branch tokens and parks the root token in the\n // FORKED state; the interleaved loop in the executor drives the branches.\n if (branch) {\n // Nested forks are rejected by definition validation; fail closed.\n throw new StepExecutionError(\n 'Nested PARALLEL_FORK is not supported',\n 'NESTED_FORK_NOT_SUPPORTED',\n { stepType, stepId: stepDef.stepId }\n )\n }\n const definition = await em.findOne(WorkflowDefinition, { id: instance.definitionId })\n if (!definition) {\n throw new StepExecutionError(\n `Workflow definition not found: ${instance.definitionId}`,\n 'DEFINITION_NOT_FOUND',\n { definitionId: instance.definitionId }\n )\n }\n const { openFork } = await import('./parallel-handler')\n await openFork(em, instance, definition, stepDef)\n return { status: 'WAITING', waitReason: 'FORK', outputData: { stepType: 'PARALLEL_FORK', forkStepId: stepDef.stepId } }\n }\n\n case 'PARALLEL_JOIN':\n // The join is a synchronization point handled by the parallel loop\n // (branches are marked COMPLETED on arrival; the loop fires the join).\n // Executing the step itself is a no-op.\n return { status: 'COMPLETED', outputData: { stepType: 'PARALLEL_JOIN', timestamp: new Date().toISOString() } }\n\n default:\n throw new StepExecutionError(\n `Unknown step type: ${stepType}`,\n 'UNKNOWN_STEP_TYPE',\n { stepType }\n )\n }\n}\n\n/**\n * Handle START step - no-op, immediately complete\n */\nfunction handleStartStep(\n stepDef: any,\n context: StepExecutionContext\n): StepExecutionResult {\n return {\n status: 'COMPLETED',\n outputData: {\n stepType: 'START',\n timestamp: new Date().toISOString(),\n },\n }\n}\n\n/**\n * Handle END step - mark as complete\n */\nfunction handleEndStep(\n stepDef: any,\n context: StepExecutionContext\n): StepExecutionResult {\n return {\n status: 'COMPLETED',\n outputData: {\n stepType: 'END',\n timestamp: new Date().toISOString(),\n finalContext: context.workflowContext,\n },\n }\n}\n\n/**\n * Handle AUTOMATED step - execute activities\n *\n * Executes activities defined in step configuration.\n * Supports both sync and async activities.\n */\nasync function handleAutomatedStep(\n em: EntityManager,\n instance: WorkflowInstance,\n stepInstance: StepInstance,\n stepDef: any,\n context: StepExecutionContext,\n container?: any,\n branch?: WorkflowBranchInstance | null\n): Promise<StepExecutionResult> {\n // Extract activities from step definition\n const activities = stepDef.activities || []\n\n if (activities.length === 0) {\n // No activities defined - immediate completion (legacy behavior)\n return {\n status: 'COMPLETED',\n outputData: {\n stepType: 'AUTOMATED',\n timestamp: new Date().toISOString(),\n },\n }\n }\n\n // Import activity executor\n const { executeActivities } = await import('./activity-executor')\n\n try {\n // Execute activities with proper context\n const results = await executeActivities(em, container, activities, {\n workflowInstance: instance,\n workflowContext: context.workflowContext,\n stepContext: { stepId: stepDef.stepId, stepName: stepDef.stepName },\n stepInstanceId: stepInstance.id,\n userId: context.userId,\n })\n\n // Check if there are pending async activities\n const pendingActivities = results.filter(r => r.async && !r.success)\n if (pendingActivities.length > 0) {\n // Workflow should pause and wait for async activities\n const now = new Date()\n if (branch) {\n branch.status = 'WAITING_FOR_ACTIVITIES'\n branch.updatedAt = now\n } else {\n instance.status = 'WAITING_FOR_ACTIVITIES'\n instance.pausedAt = now\n instance.updatedAt = now\n }\n await em.flush()\n\n return {\n status: 'WAITING',\n waitReason: 'SIGNAL', // Reuse SIGNAL wait reason (will be resumed by activity completion)\n outputData: {\n pendingActivities: pendingActivities.map(r => ({\n activityId: r.activityId,\n activityName: r.activityName,\n jobId: r.jobId,\n })),\n },\n }\n }\n\n // Check for failures in sync activities\n const failures = results.filter(r => !r.success && !r.async)\n if (failures.length > 0) {\n const errorMessages = failures.map(f => `${f.activityName || f.activityId}: ${f.error}`).join('; ')\n return {\n status: 'FAILED',\n error: `${failures.length} activity(ies) failed: ${errorMessages}`,\n outputData: {\n failures: failures.map(f => ({\n activityId: f.activityId,\n activityName: f.activityName,\n error: f.error,\n retryCount: f.retryCount,\n })),\n },\n }\n }\n\n // All activities completed successfully\n const activityOutputs = results.reduce((acc, r) => {\n if (r.output) {\n acc[r.activityId] = r.output\n }\n return acc\n }, {} as Record<string, any>)\n\n return {\n status: 'COMPLETED',\n outputData: {\n stepType: 'AUTOMATED',\n timestamp: new Date().toISOString(),\n activityResults: activityOutputs,\n activityCount: results.length,\n },\n }\n } catch (error: any) {\n return {\n status: 'FAILED',\n error: `Activity execution failed: ${error.message}`,\n }\n }\n}\n\n/**\n * Handle USER_TASK step - create user task and enter waiting state\n *\n * Creates a UserTask entity and returns WAITING status.\n * The workflow will pause until the task is completed by a user.\n */\nasync function handleUserTaskStep(\n em: EntityManager,\n instance: WorkflowInstance,\n stepInstance: StepInstance,\n stepDef: any,\n context: StepExecutionContext,\n branch?: WorkflowBranchInstance | null\n): Promise<StepExecutionResult> {\n const userTaskConfig = stepDef.userTaskConfig || {}\n\n // Handle assignedTo - if it's an array, treat it as roles\n let assignedTo = userTaskConfig.assignedTo || null\n let assignedToRoles = userTaskConfig.assignedToRoles || null\n\n if (Array.isArray(assignedTo)) {\n assignedToRoles = assignedTo\n assignedTo = null\n }\n\n // Create user task\n const now = new Date()\n const userTask = em.create(UserTask, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n branchInstanceId: branch ? branch.id : null,\n taskName: stepDef.stepName,\n description: stepDef.description || null,\n status: 'PENDING',\n formSchema: userTaskConfig.formSchema || null,\n formData: null,\n assignedTo: assignedTo,\n assignedToRoles: assignedToRoles,\n dueDate: userTaskConfig.slaDuration ? calculateDueDate(userTaskConfig.slaDuration) : null,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n createdAt: now,\n updatedAt: now,\n })\n\n await em.persist(userTask).flush()\n\n // Log USER_TASK_CREATED event\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n ...(branch ? { branchInstanceId: branch.id } : {}),\n eventType: 'USER_TASK_CREATED',\n eventData: {\n userTaskId: userTask.id,\n taskName: userTask.taskName,\n assignedTo: userTask.assignedTo,\n assignedToRoles: userTask.assignedToRoles,\n },\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n // Pause execution - waits for user task completion. For a branch, only the\n // branch pauses; sibling branches keep running.\n if (branch) {\n branch.status = 'PAUSED'\n branch.updatedAt = now\n } else {\n instance.status = 'PAUSED'\n instance.updatedAt = now\n }\n await em.flush()\n\n return {\n status: 'WAITING',\n waitReason: 'USER_TASK',\n outputData: {\n userTaskId: userTask.id,\n },\n }\n}\n\n/**\n * Handle SUB_WORKFLOW step - invoke another workflow and wait for completion\n *\n * Creates a child workflow instance with mapped input data,\n * executes it synchronously, and returns mapped output data.\n */\nasync function handleSubWorkflowStep(\n em: EntityManager,\n container: any,\n instance: WorkflowInstance,\n stepInstance: StepInstance,\n stepDef: any,\n context: StepExecutionContext\n): Promise<StepExecutionResult> {\n const { subWorkflowId, inputMapping, outputMapping, version } = stepDef.config || {}\n\n if (!subWorkflowId) {\n return {\n status: 'FAILED',\n error: 'Sub-workflow ID not specified in step configuration'\n }\n }\n\n // Map input data from parent context to child context\n const childContext = mapInputData(instance.context, inputMapping || {})\n\n // Import workflow executor functions\n const { startWorkflow, executeWorkflow } = await import('./workflow-executor')\n\n try {\n // Start child workflow with parent metadata\n const childInstance = await startWorkflow(em, {\n workflowId: subWorkflowId,\n version,\n initialContext: childContext,\n correlationKey: instance.correlationKey || undefined,\n metadata: {\n ...instance.metadata,\n labels: {\n ...instance.metadata?.labels,\n parentInstanceId: instance.id,\n parentStepId: stepDef.stepId,\n parentStepInstanceId: stepInstance.id,\n },\n },\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n // Log sub-workflow invocation event\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n eventType: 'SUB_WORKFLOW_STARTED',\n eventData: {\n childInstanceId: childInstance.id,\n subWorkflowId,\n version,\n inputData: childContext,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n // Execute child workflow synchronously\n const result = await executeWorkflow(em, container, childInstance.id, {\n userId: context.userId,\n })\n\n // Handle child workflow result\n if (result.status === 'COMPLETED') {\n // Map output data from child context to parent context\n const outputData = mapOutputData(result.context, outputMapping || {})\n\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n eventType: 'SUB_WORKFLOW_COMPLETED',\n eventData: {\n childInstanceId: childInstance.id,\n outputData,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return {\n status: 'COMPLETED',\n outputData,\n }\n } else if (result.status === 'FAILED') {\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n eventType: 'SUB_WORKFLOW_FAILED',\n eventData: {\n childInstanceId: childInstance.id,\n error: result.errors?.join(', '),\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return {\n status: 'FAILED',\n error: `Sub-workflow failed: ${result.errors?.join(', ')}`,\n }\n } else {\n // WAITING, PAUSED, etc. - For synchronous execution, treat as error\n return {\n status: 'FAILED',\n error: `Sub-workflow ended in unexpected state: ${result.status}`,\n }\n }\n } catch (error: any) {\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n eventType: 'SUB_WORKFLOW_FAILED',\n eventData: {\n subWorkflowId,\n error: error.message,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return {\n status: 'FAILED',\n error: `Sub-workflow execution failed: ${error.message}`,\n }\n }\n}\n\n/**\n * Handle WAIT_FOR_SIGNAL step - pause workflow until signal received\n *\n * Creates a waiting state and pauses the workflow until an external signal\n * with the matching signal name is received. The signal payload will be merged\n * into the workflow context when received.\n */\nasync function handleWaitForSignalStep(\n em: EntityManager,\n instance: WorkflowInstance,\n stepInstance: StepInstance,\n stepDef: any,\n context: StepExecutionContext,\n branch?: WorkflowBranchInstance | null\n): Promise<StepExecutionResult> {\n const signalConfig = stepDef.signalConfig || {}\n const signalName = signalConfig.signalName || stepDef.stepId\n const timeout = signalConfig.timeout ? parseDuration(signalConfig.timeout) : null\n\n const now = new Date()\n\n // Log signal awaiting event\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n ...(branch ? { branchInstanceId: branch.id } : {}),\n eventType: 'SIGNAL_AWAITING',\n eventData: {\n signalName,\n timeout,\n description: stepDef.description,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n // Pause execution (branch-scoped when running inside a parallel branch)\n if (branch) {\n branch.status = 'PAUSED'\n branch.updatedAt = now\n } else {\n instance.status = 'PAUSED'\n instance.pausedAt = now\n instance.updatedAt = now\n }\n await em.flush()\n\n // Return WAITING status to halt executor\n return {\n status: 'WAITING',\n waitReason: 'SIGNAL',\n outputData: {\n signalName,\n timeout,\n awaitingSince: now,\n },\n }\n}\n\n/**\n * Handle WAIT_FOR_TIMER step - pause workflow until a timer fires.\n *\n * Reads `duration` (relative, e.g. \"PT5M\") or `until` (ISO 8601 datetime) from\n * `stepDef.config` (preferred \u2014 matches StepsEditor) or `stepDef.timerConfig`.\n * Enqueues a delayed timer job on the workflow-activities queue; when the job\n * is processed by the activity worker, it calls `timerHandler.fireTimer` to\n * resume the workflow.\n */\nasync function handleWaitForTimerStep(\n em: EntityManager,\n instance: WorkflowInstance,\n stepInstance: StepInstance,\n stepDef: any,\n context: StepExecutionContext,\n branch?: WorkflowBranchInstance | null\n): Promise<StepExecutionResult> {\n const timerConfig = stepDef.config || stepDef.timerConfig || {}\n const duration: string | undefined = timerConfig.duration\n const until: string | undefined = timerConfig.until\n\n if (!duration && !until) {\n throw new StepExecutionError(\n 'WAIT_FOR_TIMER requires either \"duration\" (e.g., \"PT5M\") or \"until\" (ISO 8601 datetime)',\n 'TIMER_CONFIG_MISSING',\n { stepId: stepDef.stepId }\n )\n }\n\n let fireAtMs: number\n if (until) {\n const targetDate = new Date(until)\n if (isNaN(targetDate.getTime())) {\n throw new StepExecutionError(\n `WAIT_FOR_TIMER invalid \"until\" datetime: ${until}`,\n 'TIMER_CONFIG_INVALID',\n { until }\n )\n }\n fireAtMs = targetDate.getTime()\n } else {\n fireAtMs = Date.now() + parseDuration(duration as string)\n }\n\n const delayMs = fireAtMs - Date.now()\n const fireAt = new Date(fireAtMs)\n\n // Immediate-fire path: skip the queue round-trip if the timer is in the past\n if (delayMs <= 0) {\n return {\n status: 'COMPLETED',\n outputData: {\n stepType: 'WAIT_FOR_TIMER',\n timerFiredImmediately: true,\n fireAt,\n duration,\n until,\n },\n }\n }\n\n const now = new Date()\n\n // Enqueue delayed timer job via the shared activity queue.\n // Imported here to avoid a top-level cycle between step-handler and activity-executor.\n const { enqueueTimerJob } = await import('./activity-executor')\n const jobId = await enqueueTimerJob({\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n branchInstanceId: branch ? branch.id : undefined,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n userId: context.userId,\n fireAt: fireAt.toISOString(),\n delayMs,\n })\n\n await logWorkflowEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n ...(branch ? { branchInstanceId: branch.id } : {}),\n eventType: 'TIMER_AWAITING',\n eventData: {\n fireAt: fireAt.toISOString(),\n duration: duration || null,\n until: until || null,\n jobId,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n if (branch) {\n branch.status = 'PAUSED'\n branch.updatedAt = now\n } else {\n instance.status = 'PAUSED'\n instance.pausedAt = now\n instance.updatedAt = now\n }\n await em.flush()\n\n return {\n status: 'WAITING',\n waitReason: 'TIMER',\n outputData: {\n fireAt,\n duration,\n until,\n jobId,\n },\n }\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n// parseDuration is imported from ./duration\n\n/**\n * Log step-related event to event sourcing table\n */\nasync function logStepEvent(\n em: EntityManager,\n event: {\n workflowInstanceId: string\n stepInstanceId: string\n branchInstanceId?: string | null\n eventType: string\n eventData: any\n userId?: string\n tenantId: string\n organizationId: string\n }\n): Promise<WorkflowEvent> {\n const workflowEvent = em.create(WorkflowEvent, {\n ...event,\n occurredAt: new Date(),\n })\n\n await em.persist(workflowEvent).flush()\n return workflowEvent\n}\n\n/**\n * Calculate due date from ISO 8601 duration string\n *\n * @param duration - ISO 8601 duration (e.g., \"P1D\" for 1 day)\n * @returns Due date\n */\nfunction calculateDueDate(duration: string): Date {\n // Simple implementation for MVP\n // Supports: P1D (1 day), P1H (1 hour), P1W (1 week)\n const now = new Date()\n\n const daysMatch = duration.match(/P(\\d+)D/)\n if (daysMatch) {\n const days = parseInt(daysMatch[1], 10)\n return new Date(now.getTime() + days * 24 * 60 * 60 * 1000)\n }\n\n const hoursMatch = duration.match(/PT(\\d+)H/)\n if (hoursMatch) {\n const hours = parseInt(hoursMatch[1], 10)\n return new Date(now.getTime() + hours * 60 * 60 * 1000)\n }\n\n const weeksMatch = duration.match(/P(\\d+)W/)\n if (weeksMatch) {\n const weeks = parseInt(weeksMatch[1], 10)\n return new Date(now.getTime() + weeks * 7 * 24 * 60 * 60 * 1000)\n }\n\n // Default: 1 day\n return new Date(now.getTime() + 24 * 60 * 60 * 1000)\n}\n\n/**\n * Get nested value from object using dot notation\n *\n * @param obj - Source object\n * @param path - Dot-notation path (e.g., \"user.email\")\n * @returns Value at path or undefined\n */\nfunction getNestedValue(obj: any, path: string): any {\n return path.split('.').reduce((current, key) => current?.[key], obj)\n}\n\n/**\n * Set nested value in object using dot notation\n *\n * @param obj - Target object\n * @param path - Dot-notation path (e.g., \"user.email\")\n * @param value - Value to set\n */\nfunction setNestedValue(obj: any, path: string, value: any): void {\n const keys = path.split('.')\n const lastKey = keys.pop()!\n const target = keys.reduce((current, key) => {\n if (!(key in current)) current[key] = {}\n return current[key]\n }, obj)\n target[lastKey] = value\n}\n\n/**\n * Map data from source context using mapping configuration\n *\n * @param sourceContext - Source data object\n * @param mapping - Mapping configuration (targetKey -> sourcePath)\n * @returns Mapped data object\n */\nfunction mapInputData(\n sourceContext: Record<string, any>,\n mapping: Record<string, string>\n): Record<string, any> {\n const result: Record<string, any> = {}\n\n for (const [targetKey, sourcePath] of Object.entries(mapping)) {\n const value = getNestedValue(sourceContext, sourcePath)\n if (value !== undefined) {\n setNestedValue(result, targetKey, value)\n }\n }\n\n // If no mapping provided, pass entire context\n return Object.keys(result).length > 0 ? result : sourceContext\n}\n\n/**\n * Map output data from child context back to parent\n *\n * @param childContext - Child workflow context\n * @param mapping - Mapping configuration (targetKey -> sourcePath)\n * @returns Mapped output data\n */\nfunction mapOutputData(\n childContext: Record<string, any>,\n mapping: Record<string, string>\n): Record<string, any> {\n const result: Record<string, any> = {}\n\n for (const [targetKey, sourcePath] of Object.entries(mapping)) {\n const value = getNestedValue(childContext, sourcePath)\n if (value !== undefined) {\n setNestedValue(result, targetKey, value)\n }\n }\n\n // If no mapping provided, pass entire child context\n return Object.keys(result).length > 0 ? result : childContext\n}\n"],
|
|
5
|
+
"mappings": "AAYA;AAAA,EAGE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,SAAS,qBAAqB;AAC9B,SAAS,wBAAwB;AAoB1B,MAAM,2BAA2B,MAAM;AAAA,EAC5C,YACE,SACO,MACA,SACP;AACA,UAAM,OAAO;AAHN;AACA;AAGP,SAAK,OAAO;AAAA,EACd;AACF;AAeA,eAAsB,UACpB,IACA,UACA,QACA,SACA,QACuB;AAEvB,QAAM,aAAa,MAAM,GAAG,QAAQ,oBAAoB;AAAA,IACtD,IAAI,SAAS;AAAA,EACf,CAAC;AAED,MAAI,CAAC,YAAY;AACf,UAAM,IAAI;AAAA,MACR,kCAAkC,SAAS,YAAY;AAAA,MACvD;AAAA,MACA,EAAE,cAAc,SAAS,aAAa;AAAA,IACxC;AAAA,EACF;AAGA,QAAM,UAAU,WAAW,WAAW,MAAM,KAAK,CAAC,MAAW,EAAE,WAAW,MAAM;AAChF,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR,0CAA0C,MAAM;AAAA,MAChD;AAAA,MACA,EAAE,YAAY,WAAW,YAAY,OAAO;AAAA,IAC9C;AAAA,EACF;AAEA,QAAM,MAAM,oBAAI,KAAK;AAGrB,QAAM,eAAe,GAAG,OAAO,cAAc;AAAA,IAC3C,oBAAoB,SAAS;AAAA,IAC7B,kBAAkB,SAAS,OAAO,KAAK;AAAA,IACvC,QAAQ,QAAQ;AAAA,IAChB,UAAU,QAAQ;AAAA,IAClB,UAAU,QAAQ;AAAA,IAClB,QAAQ;AAAA,IACR,WAAW,QAAQ,eAAe;AAAA,IAClC,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,UAAU,SAAS;AAAA,IACnB,gBAAgB,SAAS;AAAA,IACzB,WAAW;AAAA,IACX,WAAW;AAAA,EACb,CAAC;AAED,QAAM,GAAG,QAAQ,YAAY,EAAE,MAAM;AAGrC,QAAM,aAAa,IAAI;AAAA,IACrB,oBAAoB,SAAS;AAAA,IAC7B,gBAAgB,aAAa;AAAA,IAC7B,GAAI,SAAS,EAAE,kBAAkB,OAAO,GAAG,IAAI,CAAC;AAAA,IAChD,WAAW;AAAA,IACX,WAAW;AAAA,MACT,QAAQ,QAAQ;AAAA,MAChB,UAAU,QAAQ;AAAA,MAClB,UAAU,QAAQ;AAAA,IACpB;AAAA,IACA,QAAQ,QAAQ;AAAA,IAChB,UAAU,SAAS;AAAA,IACnB,gBAAgB,SAAS;AAAA,EAC3B,CAAC;AAED,SAAO;AACT;AASA,eAAsB,SACpB,IACA,cACA,YACe;AACf,QAAM,MAAM,oBAAI,KAAK;AAGrB,MAAI,kBAAiC;AACrC,MAAI,aAAa,WAAW;AAC1B,sBAAkB,IAAI,QAAQ,IAAI,aAAa,UAAU,QAAQ;AAAA,EACnE;AAGA,eAAa,SAAS;AACtB,eAAa,aAAa,cAAc;AACxC,eAAa,WAAW;AACxB,eAAa,kBAAkB;AAC/B,eAAa,YAAY;AAEzB,QAAM,GAAG,MAAM;AAGf,QAAM,aAAa,IAAI;AAAA,IACrB,oBAAoB,aAAa;AAAA,IACjC,gBAAgB,aAAa;AAAA,IAC7B,WAAW;AAAA,IACX,WAAW;AAAA,MACT,QAAQ,aAAa;AAAA,MACrB,QAAQ;AAAA,MACR;AAAA,MACA,WAAW,CAAC,CAAC;AAAA,IACf;AAAA,IACA,UAAU,aAAa;AAAA,IACvB,gBAAgB,aAAa;AAAA,EAC/B,CAAC;AACH;AAmBA,eAAsB,YACpB,IACA,UACA,QACA,SACA,WACA,QAC8B;AAC9B,MAAI;AAEF,UAAM,eAAe,MAAM,UAAU,IAAI,UAAU,QAAQ,SAAS,MAAM;AAG1E,UAAM,aAAa,MAAM,GAAG,QAAQ,oBAAoB;AAAA,MACtD,IAAI,SAAS;AAAA,IACf,CAAC;AAED,QAAI,CAAC,YAAY;AACf,YAAM,IAAI;AAAA,QACR,kCAAkC,SAAS,YAAY;AAAA,QACvD;AAAA,QACA,EAAE,cAAc,SAAS,aAAa;AAAA,MACxC;AAAA,IACF;AAEA,UAAM,UAAU,WAAW,WAAW,MAAM,KAAK,CAAC,MAAW,EAAE,WAAW,MAAM;AAChF,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI;AAAA,QACR,mBAAmB,MAAM;AAAA,QACzB;AAAA,QACA,EAAE,OAAO;AAAA,MACX;AAAA,IACF;AAGA,UAAM,SAAS,MAAM;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAGA,QAAI,OAAO,WAAW,aAAa;AACjC,YAAM,SAAS,IAAI,cAAc,OAAO,UAAU;AAAA,IACpD;AAEA,WAAO;AAAA,EACT,SAAS,OAAO;AAEd,UAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAG1E,QAAI;AACF,YAAM,qBAAqB,MAAM,GAAG,QAAQ,cAAc;AAAA,QACxD,oBAAoB,SAAS;AAAA,QAC7B;AAAA,QACA,QAAQ;AAAA,MACV,CAAC;AAED,UAAI,oBAAoB;AACtB,2BAAmB,SAAS;AAC5B,2BAAmB,YAAY;AAAA,UAC7B,OAAO;AAAA,UACP,SAAS,iBAAiB,qBAAqB,MAAM,UAAU;AAAA,QACjE;AACA,2BAAmB,WAAW,oBAAI,KAAK;AACvC,2BAAmB,YAAY,oBAAI,KAAK;AACxC,cAAM,GAAG,MAAM;AAEf,cAAM,aAAa,IAAI;AAAA,UACrB,oBAAoB,SAAS;AAAA,UAC7B,gBAAgB,mBAAmB;AAAA,UACnC,WAAW;AAAA,UACX,WAAW,EAAE,OAAO,aAAa;AAAA,UACjC,UAAU,SAAS;AAAA,UACnB,gBAAgB,SAAS;AAAA,QAC3B,CAAC;AAAA,MACH;AAAA,IACF,SAAS,aAAa;AAEpB,cAAQ,MAAM,8CAA8C,WAAW;AAAA,IACzE;AAEA,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AACF;AAgBA,eAAe,kBACb,IACA,UACA,cACA,SACA,SACA,WACA,QAC8B;AAC9B,QAAM,WAA6B,QAAQ;AAE3C,UAAQ,UAAU;AAAA,IAChB,KAAK;AACH,aAAO,gBAAgB,SAAS,OAAO;AAAA,IAEzC,KAAK;AACH,aAAO,cAAc,SAAS,OAAO;AAAA,IAEvC,KAAK;AACH,aAAO,MAAM,oBAAoB,IAAI,UAAU,cAAc,SAAS,SAAS,WAAW,MAAM;AAAA,IAElG,KAAK;AACH,aAAO,MAAM,mBAAmB,IAAI,UAAU,cAAc,SAAS,SAAS,MAAM;AAAA,IAEtF,KAAK;AACH,UAAI,CAAC,WAAW;AACd,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,UACA,EAAE,SAAS;AAAA,QACb;AAAA,MACF;AACA,aAAO,MAAM,sBAAsB,IAAI,WAAW,UAAU,cAAc,SAAS,OAAO;AAAA,IAE5F,KAAK;AACH,aAAO,MAAM,wBAAwB,IAAI,UAAU,cAAc,SAAS,SAAS,MAAM;AAAA,IAE3F,KAAK;AACH,aAAO,MAAM,uBAAuB,IAAI,UAAU,cAAc,SAAS,SAAS,MAAM;AAAA,IAE1F,KAAK,iBAAiB;AAGpB,UAAI,QAAQ;AAEV,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,UACA,EAAE,UAAU,QAAQ,QAAQ,OAAO;AAAA,QACrC;AAAA,MACF;AACA,YAAM,aAAa,MAAM,GAAG,QAAQ,oBAAoB,EAAE,IAAI,SAAS,aAAa,CAAC;AACrF,UAAI,CAAC,YAAY;AACf,cAAM,IAAI;AAAA,UACR,kCAAkC,SAAS,YAAY;AAAA,UACvD;AAAA,UACA,EAAE,cAAc,SAAS,aAAa;AAAA,QACxC;AAAA,MACF;AACA,YAAM,EAAE,SAAS,IAAI,MAAM,OAAO,oBAAoB;AACtD,YAAM,SAAS,IAAI,UAAU,YAAY,OAAO;AAChD,aAAO,EAAE,QAAQ,WAAW,YAAY,QAAQ,YAAY,EAAE,UAAU,iBAAiB,YAAY,QAAQ,OAAO,EAAE;AAAA,IACxH;AAAA,IAEA,KAAK;AAIH,aAAO,EAAE,QAAQ,aAAa,YAAY,EAAE,UAAU,iBAAiB,YAAW,oBAAI,KAAK,GAAE,YAAY,EAAE,EAAE;AAAA,IAE/G;AACE,YAAM,IAAI;AAAA,QACR,sBAAsB,QAAQ;AAAA,QAC9B;AAAA,QACA,EAAE,SAAS;AAAA,MACb;AAAA,EACJ;AACF;AAKA,SAAS,gBACP,SACA,SACqB;AACrB,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,YAAY;AAAA,MACV,UAAU;AAAA,MACV,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IACpC;AAAA,EACF;AACF;AAKA,SAAS,cACP,SACA,SACqB;AACrB,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,YAAY;AAAA,MACV,UAAU;AAAA,MACV,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,cAAc,QAAQ;AAAA,IACxB;AAAA,EACF;AACF;AAQA,eAAe,oBACb,IACA,UACA,cACA,SACA,SACA,WACA,QAC8B;AAE9B,QAAM,aAAa,QAAQ,cAAc,CAAC;AAE1C,MAAI,WAAW,WAAW,GAAG;AAE3B,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,YAAY;AAAA,QACV,UAAU;AAAA,QACV,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MACpC;AAAA,IACF;AAAA,EACF;AAGA,QAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,qBAAqB;AAEhE,MAAI;AAEF,UAAM,UAAU,MAAM,kBAAkB,IAAI,WAAW,YAAY;AAAA,MACjE,kBAAkB;AAAA,MAClB,iBAAiB,QAAQ;AAAA,MACzB,aAAa,EAAE,QAAQ,QAAQ,QAAQ,UAAU,QAAQ,SAAS;AAAA,MAClE,gBAAgB,aAAa;AAAA,MAC7B,QAAQ,QAAQ;AAAA,IAClB,CAAC;AAGD,UAAM,oBAAoB,QAAQ,OAAO,OAAK,EAAE,SAAS,CAAC,EAAE,OAAO;AACnE,QAAI,kBAAkB,SAAS,GAAG;AAEhC,YAAM,MAAM,oBAAI,KAAK;AACrB,UAAI,QAAQ;AACV,eAAO,SAAS;AAChB,eAAO,YAAY;AAAA,MACrB,OAAO;AACL,iBAAS,SAAS;AAClB,iBAAS,WAAW;AACpB,iBAAS,YAAY;AAAA,MACvB;AACA,YAAM,GAAG,MAAM;AAEf,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,YAAY;AAAA;AAAA,QACZ,YAAY;AAAA,UACV,mBAAmB,kBAAkB,IAAI,QAAM;AAAA,YAC7C,YAAY,EAAE;AAAA,YACd,cAAc,EAAE;AAAA,YAChB,OAAO,EAAE;AAAA,UACX,EAAE;AAAA,QACJ;AAAA,MACF;AAAA,IACF;AAGA,UAAM,WAAW,QAAQ,OAAO,OAAK,CAAC,EAAE,WAAW,CAAC,EAAE,KAAK;AAC3D,QAAI,SAAS,SAAS,GAAG;AACvB,YAAM,gBAAgB,SAAS,IAAI,OAAK,GAAG,EAAE,gBAAgB,EAAE,UAAU,KAAK,EAAE,KAAK,EAAE,EAAE,KAAK,IAAI;AAClG,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,OAAO,GAAG,SAAS,MAAM,0BAA0B,aAAa;AAAA,QAChE,YAAY;AAAA,UACV,UAAU,SAAS,IAAI,QAAM;AAAA,YAC3B,YAAY,EAAE;AAAA,YACd,cAAc,EAAE;AAAA,YAChB,OAAO,EAAE;AAAA,YACT,YAAY,EAAE;AAAA,UAChB,EAAE;AAAA,QACJ;AAAA,MACF;AAAA,IACF;AAGA,UAAM,kBAAkB,QAAQ,OAAO,CAAC,KAAK,MAAM;AACjD,UAAI,EAAE,QAAQ;AACZ,YAAI,EAAE,UAAU,IAAI,EAAE;AAAA,MACxB;AACA,aAAO;AAAA,IACT,GAAG,CAAC,CAAwB;AAE5B,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,YAAY;AAAA,QACV,UAAU;AAAA,QACV,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,QAClC,iBAAiB;AAAA,QACjB,eAAe,QAAQ;AAAA,MACzB;AAAA,IACF;AAAA,EACF,SAAS,OAAY;AACnB,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,OAAO,8BAA8B,MAAM,OAAO;AAAA,IACpD;AAAA,EACF;AACF;AAQA,eAAe,mBACb,IACA,UACA,cACA,SACA,SACA,QAC8B;AAC9B,QAAM,iBAAiB,QAAQ,kBAAkB,CAAC;AAGlD,MAAI,aAAa,eAAe,cAAc;AAC9C,MAAI,kBAAkB,eAAe,mBAAmB;AAExD,MAAI,MAAM,QAAQ,UAAU,GAAG;AAC7B,sBAAkB;AAClB,iBAAa;AAAA,EACf;AAGA,QAAM,MAAM,oBAAI,KAAK;AACrB,QAAM,WAAW,GAAG,OAAO,UAAU;AAAA,IACnC,oBAAoB,SAAS;AAAA,IAC7B,gBAAgB,aAAa;AAAA,IAC7B,kBAAkB,SAAS,OAAO,KAAK;AAAA,IACvC,UAAU,QAAQ;AAAA,IAClB,aAAa,QAAQ,eAAe;AAAA,IACpC,QAAQ;AAAA,IACR,YAAY,eAAe,cAAc;AAAA,IACzC,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA,SAAS,eAAe,cAAc,iBAAiB,eAAe,WAAW,IAAI;AAAA,IACrF,UAAU,SAAS;AAAA,IACnB,gBAAgB,SAAS;AAAA,IACzB,WAAW;AAAA,IACX,WAAW;AAAA,EACb,CAAC;AAED,QAAM,GAAG,QAAQ,QAAQ,EAAE,MAAM;AAGjC,QAAM,aAAa,IAAI;AAAA,IACrB,oBAAoB,SAAS;AAAA,IAC7B,gBAAgB,aAAa;AAAA,IAC7B,GAAI,SAAS,EAAE,kBAAkB,OAAO,GAAG,IAAI,CAAC;AAAA,IAChD,WAAW;AAAA,IACX,WAAW;AAAA,MACT,YAAY,SAAS;AAAA,MACrB,UAAU,SAAS;AAAA,MACnB,YAAY,SAAS;AAAA,MACrB,iBAAiB,SAAS;AAAA,IAC5B;AAAA,IACA,UAAU,SAAS;AAAA,IACnB,gBAAgB,SAAS;AAAA,EAC3B,CAAC;AAID,MAAI,QAAQ;AACV,WAAO,SAAS;AAChB,WAAO,YAAY;AAAA,EACrB,OAAO;AACL,aAAS,SAAS;AAClB,aAAS,YAAY;AAAA,EACvB;AACA,QAAM,GAAG,MAAM;AAEf,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,YAAY;AAAA,MACV,YAAY,SAAS;AAAA,IACvB;AAAA,EACF;AACF;AAQA,eAAe,sBACb,IACA,WACA,UACA,cACA,SACA,SAC8B;AAC9B,QAAM,EAAE,eAAe,cAAc,eAAe,QAAQ,IAAI,QAAQ,UAAU,CAAC;AAEnF,MAAI,CAAC,eAAe;AAClB,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AAGA,QAAM,eAAe,aAAa,SAAS,SAAS,gBAAgB,CAAC,CAAC;AAGtE,QAAM,EAAE,eAAe,gBAAgB,IAAI,MAAM,OAAO,qBAAqB;AAE7E,MAAI;AAEF,UAAM,gBAAgB,MAAM,cAAc,IAAI;AAAA,MAC5C,YAAY;AAAA,MACZ;AAAA,MACA,gBAAgB;AAAA,MAChB,gBAAgB,SAAS,kBAAkB;AAAA,MAC3C,UAAU;AAAA,QACR,GAAG,SAAS;AAAA,QACZ,QAAQ;AAAA,UACN,GAAG,SAAS,UAAU;AAAA,UACtB,kBAAkB,SAAS;AAAA,UAC3B,cAAc,QAAQ;AAAA,UACtB,sBAAsB,aAAa;AAAA,QACrC;AAAA,MACF;AAAA,MACA,UAAU,SAAS;AAAA,MACnB,gBAAgB,SAAS;AAAA,IAC3B,CAAC;AAGD,UAAM,aAAa,IAAI;AAAA,MACrB,oBAAoB,SAAS;AAAA,MAC7B,gBAAgB,aAAa;AAAA,MAC7B,WAAW;AAAA,MACX,WAAW;AAAA,QACT,iBAAiB,cAAc;AAAA,QAC/B;AAAA,QACA;AAAA,QACA,WAAW;AAAA,MACb;AAAA,MACA,QAAQ,QAAQ;AAAA,MAChB,UAAU,SAAS;AAAA,MACnB,gBAAgB,SAAS;AAAA,IAC3B,CAAC;AAGD,UAAM,SAAS,MAAM,gBAAgB,IAAI,WAAW,cAAc,IAAI;AAAA,MACpE,QAAQ,QAAQ;AAAA,IAClB,CAAC;AAGD,QAAI,OAAO,WAAW,aAAa;AAEjC,YAAM,aAAa,cAAc,OAAO,SAAS,iBAAiB,CAAC,CAAC;AAEpE,YAAM,aAAa,IAAI;AAAA,QACrB,oBAAoB,SAAS;AAAA,QAC7B,gBAAgB,aAAa;AAAA,QAC7B,WAAW;AAAA,QACX,WAAW;AAAA,UACT,iBAAiB,cAAc;AAAA,UAC/B;AAAA,QACF;AAAA,QACA,QAAQ,QAAQ;AAAA,QAChB,UAAU,SAAS;AAAA,QACnB,gBAAgB,SAAS;AAAA,MAC3B,CAAC;AAED,aAAO;AAAA,QACL,QAAQ;AAAA,QACR;AAAA,MACF;AAAA,IACF,WAAW,OAAO,WAAW,UAAU;AACrC,YAAM,aAAa,IAAI;AAAA,QACrB,oBAAoB,SAAS;AAAA,QAC7B,gBAAgB,aAAa;AAAA,QAC7B,WAAW;AAAA,QACX,WAAW;AAAA,UACT,iBAAiB,cAAc;AAAA,UAC/B,OAAO,OAAO,QAAQ,KAAK,IAAI;AAAA,QACjC;AAAA,QACA,QAAQ,QAAQ;AAAA,QAChB,UAAU,SAAS;AAAA,QACnB,gBAAgB,SAAS;AAAA,MAC3B,CAAC;AAED,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,OAAO,wBAAwB,OAAO,QAAQ,KAAK,IAAI,CAAC;AAAA,MAC1D;AAAA,IACF,OAAO;AAEL,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,OAAO,2CAA2C,OAAO,MAAM;AAAA,MACjE;AAAA,IACF;AAAA,EACF,SAAS,OAAY;AACnB,UAAM,aAAa,IAAI;AAAA,MACrB,oBAAoB,SAAS;AAAA,MAC7B,gBAAgB,aAAa;AAAA,MAC7B,WAAW;AAAA,MACX,WAAW;AAAA,QACT;AAAA,QACA,OAAO,MAAM;AAAA,MACf;AAAA,MACA,QAAQ,QAAQ;AAAA,MAChB,UAAU,SAAS;AAAA,MACnB,gBAAgB,SAAS;AAAA,IAC3B,CAAC;AAED,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,OAAO,kCAAkC,MAAM,OAAO;AAAA,IACxD;AAAA,EACF;AACF;AASA,eAAe,wBACb,IACA,UACA,cACA,SACA,SACA,QAC8B;AAC9B,QAAM,eAAe,QAAQ,gBAAgB,CAAC;AAC9C,QAAM,aAAa,aAAa,cAAc,QAAQ;AACtD,QAAM,UAAU,aAAa,UAAU,cAAc,aAAa,OAAO,IAAI;AAE7E,QAAM,MAAM,oBAAI,KAAK;AAGrB,QAAM,aAAa,IAAI;AAAA,IACrB,oBAAoB,SAAS;AAAA,IAC7B,gBAAgB,aAAa;AAAA,IAC7B,GAAI,SAAS,EAAE,kBAAkB,OAAO,GAAG,IAAI,CAAC;AAAA,IAChD,WAAW;AAAA,IACX,WAAW;AAAA,MACT;AAAA,MACA;AAAA,MACA,aAAa,QAAQ;AAAA,IACvB;AAAA,IACA,QAAQ,QAAQ;AAAA,IAChB,UAAU,SAAS;AAAA,IACnB,gBAAgB,SAAS;AAAA,EAC3B,CAAC;AAGD,MAAI,QAAQ;AACV,WAAO,SAAS;AAChB,WAAO,YAAY;AAAA,EACrB,OAAO;AACL,aAAS,SAAS;AAClB,aAAS,WAAW;AACpB,aAAS,YAAY;AAAA,EACvB;AACA,QAAM,GAAG,MAAM;AAGf,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,YAAY;AAAA,MACV;AAAA,MACA;AAAA,MACA,eAAe;AAAA,IACjB;AAAA,EACF;AACF;AAWA,eAAe,uBACb,IACA,UACA,cACA,SACA,SACA,QAC8B;AAC9B,QAAM,cAAc,QAAQ,UAAU,QAAQ,eAAe,CAAC;AAC9D,QAAM,WAA+B,YAAY;AACjD,QAAM,QAA4B,YAAY;AAE9C,MAAI,CAAC,YAAY,CAAC,OAAO;AACvB,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,EAAE,QAAQ,QAAQ,OAAO;AAAA,IAC3B;AAAA,EACF;AAEA,MAAI;AACJ,MAAI,OAAO;AACT,UAAM,aAAa,IAAI,KAAK,KAAK;AACjC,QAAI,MAAM,WAAW,QAAQ,CAAC,GAAG;AAC/B,YAAM,IAAI;AAAA,QACR,4CAA4C,KAAK;AAAA,QACjD;AAAA,QACA,EAAE,MAAM;AAAA,MACV;AAAA,IACF;AACA,eAAW,WAAW,QAAQ;AAAA,EAChC,OAAO;AACL,eAAW,KAAK,IAAI,IAAI,cAAc,QAAkB;AAAA,EAC1D;AAEA,QAAM,UAAU,WAAW,KAAK,IAAI;AACpC,QAAM,SAAS,IAAI,KAAK,QAAQ;AAGhC,MAAI,WAAW,GAAG;AAChB,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,YAAY;AAAA,QACV,UAAU;AAAA,QACV,uBAAuB;AAAA,QACvB;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,MAAM,oBAAI,KAAK;AAIrB,QAAM,EAAE,gBAAgB,IAAI,MAAM,OAAO,qBAAqB;AAC9D,QAAM,QAAQ,MAAM,gBAAgB;AAAA,IAClC,oBAAoB,SAAS;AAAA,IAC7B,gBAAgB,aAAa;AAAA,IAC7B,kBAAkB,SAAS,OAAO,KAAK;AAAA,IACvC,UAAU,SAAS;AAAA,IACnB,gBAAgB,SAAS;AAAA,IACzB,QAAQ,QAAQ;AAAA,IAChB,QAAQ,OAAO,YAAY;AAAA,IAC3B;AAAA,EACF,CAAC;AAED,QAAM,iBAAiB,IAAI;AAAA,IACzB,oBAAoB,SAAS;AAAA,IAC7B,gBAAgB,aAAa;AAAA,IAC7B,GAAI,SAAS,EAAE,kBAAkB,OAAO,GAAG,IAAI,CAAC;AAAA,IAChD,WAAW;AAAA,IACX,WAAW;AAAA,MACT,QAAQ,OAAO,YAAY;AAAA,MAC3B,UAAU,YAAY;AAAA,MACtB,OAAO,SAAS;AAAA,MAChB;AAAA,IACF;AAAA,IACA,QAAQ,QAAQ;AAAA,IAChB,UAAU,SAAS;AAAA,IACnB,gBAAgB,SAAS;AAAA,EAC3B,CAAC;AAED,MAAI,QAAQ;AACV,WAAO,SAAS;AAChB,WAAO,YAAY;AAAA,EACrB,OAAO;AACL,aAAS,SAAS;AAClB,aAAS,WAAW;AACpB,aAAS,YAAY;AAAA,EACvB;AACA,QAAM,GAAG,MAAM;AAEf,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,YAAY;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAWA,eAAe,aACb,IACA,OAUwB;AACxB,QAAM,gBAAgB,GAAG,OAAO,eAAe;AAAA,IAC7C,GAAG;AAAA,IACH,YAAY,oBAAI,KAAK;AAAA,EACvB,CAAC;AAED,QAAM,GAAG,QAAQ,aAAa,EAAE,MAAM;AACtC,SAAO;AACT;AAQA,SAAS,iBAAiB,UAAwB;AAGhD,QAAM,MAAM,oBAAI,KAAK;AAErB,QAAM,YAAY,SAAS,MAAM,SAAS;AAC1C,MAAI,WAAW;AACb,UAAM,OAAO,SAAS,UAAU,CAAC,GAAG,EAAE;AACtC,WAAO,IAAI,KAAK,IAAI,QAAQ,IAAI,OAAO,KAAK,KAAK,KAAK,GAAI;AAAA,EAC5D;AAEA,QAAM,aAAa,SAAS,MAAM,UAAU;AAC5C,MAAI,YAAY;AACd,UAAM,QAAQ,SAAS,WAAW,CAAC,GAAG,EAAE;AACxC,WAAO,IAAI,KAAK,IAAI,QAAQ,IAAI,QAAQ,KAAK,KAAK,GAAI;AAAA,EACxD;AAEA,QAAM,aAAa,SAAS,MAAM,SAAS;AAC3C,MAAI,YAAY;AACd,UAAM,QAAQ,SAAS,WAAW,CAAC,GAAG,EAAE;AACxC,WAAO,IAAI,KAAK,IAAI,QAAQ,IAAI,QAAQ,IAAI,KAAK,KAAK,KAAK,GAAI;AAAA,EACjE;AAGA,SAAO,IAAI,KAAK,IAAI,QAAQ,IAAI,KAAK,KAAK,KAAK,GAAI;AACrD;AASA,SAAS,eAAe,KAAU,MAAmB;AACnD,SAAO,KAAK,MAAM,GAAG,EAAE,OAAO,CAAC,SAAS,QAAQ,UAAU,GAAG,GAAG,GAAG;AACrE;AASA,SAAS,eAAe,KAAU,MAAc,OAAkB;AAChE,QAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,QAAM,UAAU,KAAK,IAAI;AACzB,QAAM,SAAS,KAAK,OAAO,CAAC,SAAS,QAAQ;AAC3C,QAAI,EAAE,OAAO,SAAU,SAAQ,GAAG,IAAI,CAAC;AACvC,WAAO,QAAQ,GAAG;AAAA,EACpB,GAAG,GAAG;AACN,SAAO,OAAO,IAAI;AACpB;AASA,SAAS,aACP,eACA,SACqB;AACrB,QAAM,SAA8B,CAAC;AAErC,aAAW,CAAC,WAAW,UAAU,KAAK,OAAO,QAAQ,OAAO,GAAG;AAC7D,UAAM,QAAQ,eAAe,eAAe,UAAU;AACtD,QAAI,UAAU,QAAW;AACvB,qBAAe,QAAQ,WAAW,KAAK;AAAA,IACzC;AAAA,EACF;AAGA,SAAO,OAAO,KAAK,MAAM,EAAE,SAAS,IAAI,SAAS;AACnD;AASA,SAAS,cACP,cACA,SACqB;AACrB,QAAM,SAA8B,CAAC;AAErC,aAAW,CAAC,WAAW,UAAU,KAAK,OAAO,QAAQ,OAAO,GAAG;AAC7D,UAAM,QAAQ,eAAe,cAAc,UAAU;AACrD,QAAI,UAAU,QAAW;AACvB,qBAAe,QAAQ,WAAW,KAAK;AAAA,IACzC;AAAA,EACF;AAGA,SAAO,OAAO,KAAK,MAAM,EAAE,SAAS,IAAI,SAAS;AACnD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -56,6 +56,32 @@ async function completeUserTask(em, container, options) {
|
|
|
56
56
|
{ workflowInstanceId: task.workflowInstanceId }
|
|
57
57
|
);
|
|
58
58
|
}
|
|
59
|
+
if (task.branchInstanceId) {
|
|
60
|
+
await logWorkflowEvent(em, {
|
|
61
|
+
workflowInstanceId: instance.id,
|
|
62
|
+
stepInstanceId: task.stepInstanceId,
|
|
63
|
+
branchInstanceId: task.branchInstanceId,
|
|
64
|
+
eventType: "USER_TASK_COMPLETED",
|
|
65
|
+
eventData: { taskId: task.id, taskName: task.taskName, completedBy: userId, formData },
|
|
66
|
+
userId,
|
|
67
|
+
tenantId: instance.tenantId,
|
|
68
|
+
organizationId: instance.organizationId
|
|
69
|
+
});
|
|
70
|
+
const { resumeBranch } = await import("./parallel-handler.js");
|
|
71
|
+
const resumed = await resumeBranch(em, {
|
|
72
|
+
instanceId: instance.id,
|
|
73
|
+
branchInstanceId: task.branchInstanceId,
|
|
74
|
+
tenantId: instance.tenantId,
|
|
75
|
+
organizationId: instance.organizationId,
|
|
76
|
+
contextMerge: formData,
|
|
77
|
+
exitStepInstanceId: task.stepInstanceId,
|
|
78
|
+
exitOutput: { userTaskId: task.id, formData }
|
|
79
|
+
});
|
|
80
|
+
if (resumed) {
|
|
81
|
+
await executeWorkflow(em, container, instance.id, { userId });
|
|
82
|
+
}
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
59
85
|
instance.context = {
|
|
60
86
|
...instance.context,
|
|
61
87
|
...formData
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/workflows/lib/task-handler.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Workflows Module - User Task Handler Service\n *\n * Handles user task lifecycle operations:\n * - Completing user tasks\n * - Claiming tasks from role queues\n * - Reassigning tasks\n * - Escalating overdue tasks\n *\n * Functional API (no classes) following Open Mercato conventions.\n */\n\nimport { EntityManager } from '@mikro-orm/core'\nimport type { AwilixContainer } from 'awilix'\nimport {\n UserTask,\n WorkflowInstance,\n WorkflowEvent,\n StepInstance,\n WorkflowDefinition,\n} from '../data/entities'\nimport { executeWorkflow } from './workflow-executor'\nimport * as stepHandler from './step-handler'\nimport * as transitionHandler from './transition-handler'\n\n// ============================================================================\n// Types and Interfaces\n// ============================================================================\n\nexport interface CompleteUserTaskOptions {\n taskId: string\n formData: Record<string, any>\n userId: string\n comments?: string\n}\n\nexport class UserTaskError extends Error {\n constructor(\n message: string,\n public code: string,\n public details?: any\n ) {\n super(message)\n this.name = 'UserTaskError'\n }\n}\n\n// ============================================================================\n// User Task Completion\n// ============================================================================\n\n/**\n * Complete a user task and resume workflow execution\n *\n * This function:\n * 1. Validates the task exists and can be completed\n * 2. Updates the task with form data and completion info\n * 3. Merges form data into workflow context\n * 4. Logs completion event\n * 5. Resumes workflow execution\n *\n * @param em - Entity manager\n * @param container - DI container for workflow execution\n * @param options - Task completion options\n * @throws UserTaskError if task not found or validation fails\n */\nexport async function completeUserTask(\n em: EntityManager,\n container: AwilixContainer,\n options: CompleteUserTaskOptions\n): Promise<void> {\n const { taskId, formData, userId, comments } = options\n\n // Fetch task\n const task = await em.findOne(UserTask, {\n id: taskId,\n status: { $in: ['PENDING', 'IN_PROGRESS'] },\n })\n\n if (!task) {\n throw new UserTaskError(\n 'Task not found or already completed',\n 'TASK_NOT_FOUND',\n { taskId }\n )\n }\n\n // Validate form data against schema (simple validation for MVP)\n // In Phase 7, we'll add comprehensive JSON Schema validation\n if (task.formSchema) {\n try {\n validateFormData(formData, task.formSchema)\n } catch (error) {\n throw new UserTaskError(\n error instanceof Error ? error.message : 'Form validation failed',\n 'FORM_VALIDATION_FAILED',\n { taskId, formSchema: task.formSchema, formData }\n )\n }\n }\n\n // Update task\n const now = new Date()\n task.status = 'COMPLETED'\n task.formData = formData\n task.completedBy = userId\n task.completedAt = now\n task.comments = comments || null\n task.updatedAt = now\n\n await em.flush()\n\n // Fetch workflow instance\n const instance = await em.findOne(WorkflowInstance, task.workflowInstanceId)\n if (!instance) {\n throw new UserTaskError(\n 'Workflow instance not found',\n 'INSTANCE_NOT_FOUND',\n { workflowInstanceId: task.workflowInstanceId }\n )\n }\n\n // Merge form data into workflow context\n instance.context = {\n ...instance.context,\n ...formData,\n }\n instance.updatedAt = now\n\n // Log USER_TASK_COMPLETED event\n await logWorkflowEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: task.stepInstanceId,\n eventType: 'USER_TASK_COMPLETED',\n eventData: {\n taskId: task.id,\n taskName: task.taskName,\n completedBy: userId,\n formData,\n },\n userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n // Mark the step instance as completed\n const stepInstance = await em.findOne(StepInstance, {\n id: task.stepInstanceId,\n status: 'ACTIVE',\n })\n\n if (stepInstance) {\n await stepHandler.exitStep(em, stepInstance, { userTaskId: task.id, formData })\n }\n\n // Find the next automatic transition from the current step\n const currentStepId = instance.currentStepId\n\n // Load workflow definition to find transitions\n const definition = await em.findOne(WorkflowDefinition, {\n id: instance.definitionId,\n })\n\n if (!definition) {\n throw new UserTaskError(\n 'Workflow definition not found',\n 'DEFINITION_NOT_FOUND',\n { definitionId: instance.definitionId }\n )\n }\n\n // Find automatic transitions from current step\n const autoTransitions = (definition.definition.transitions || []).filter(\n (t: any) => t.fromStepId === currentStepId && t.trigger === 'auto'\n )\n\n if (autoTransitions.length === 0) {\n // No automatic transitions, workflow stays paused at current step\n return\n }\n\n // Find valid transitions using transition handler\n const transitionContext = {\n workflowContext: instance.context,\n userId,\n }\n\n const validTransitions = await transitionHandler.findValidTransitions(\n em,\n instance,\n currentStepId,\n transitionContext\n )\n\n const firstValidTransition = validTransitions.find(t => t.isValid)\n\n if (!firstValidTransition || !firstValidTransition.transition) {\n // Resume workflow execution anyway, maybe conditions will be met later\n instance.status = 'RUNNING'\n await em.flush()\n return\n }\n\n // Execute the transition to move to next step\n\n const transitionResult = await transitionHandler.executeTransition(\n em,\n container,\n instance,\n currentStepId,\n firstValidTransition.transition.toStepId,\n transitionContext\n )\n\n if (!transitionResult.success) {\n console.error(`[TaskHandler] Transition failed:`, transitionResult.error)\n // Don't throw, just leave workflow in current state\n return\n }\n\n // Now continue workflow execution from the new step\n await executeWorkflow(em, container, instance.id, { userId })\n}\n\n/**\n * Claim a user task from a role queue\n *\n * Allows a user to claim a task that's assigned to their role(s).\n * Prevents race conditions by checking task status.\n *\n * @param em - Entity manager\n * @param taskId - Task ID to claim\n * @param userId - User claiming the task\n * @throws UserTaskError if task cannot be claimed\n */\nexport async function claimUserTask(\n em: EntityManager,\n taskId: string,\n userId: string\n): Promise<void> {\n const task = await em.findOne(UserTask, {\n id: taskId,\n status: 'PENDING',\n })\n\n if (!task) {\n throw new UserTaskError(\n 'Task not found or already claimed',\n 'TASK_NOT_FOUND',\n { taskId }\n )\n }\n\n if (task.assignedTo) {\n throw new UserTaskError(\n 'Task is already assigned to a specific user',\n 'TASK_ALREADY_ASSIGNED',\n { taskId, assignedTo: task.assignedTo }\n )\n }\n\n if (!task.assignedToRoles || task.assignedToRoles.length === 0) {\n throw new UserTaskError(\n 'Task is not assigned to any roles',\n 'TASK_NOT_ROLE_ASSIGNED',\n { taskId }\n )\n }\n\n // Update task\n const now = new Date()\n task.claimedBy = userId\n task.claimedAt = now\n task.status = 'IN_PROGRESS'\n task.updatedAt = now\n\n await em.flush()\n\n // Log event\n const instance = await em.findOne(WorkflowInstance, task.workflowInstanceId)\n if (instance) {\n await logWorkflowEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: task.stepInstanceId,\n eventType: 'USER_TASK_STARTED',\n eventData: {\n taskId: task.id,\n taskName: task.taskName,\n claimedBy: userId,\n },\n userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n }\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Log workflow event to event sourcing table\n */\nasync function logWorkflowEvent(\n em: EntityManager,\n event: {\n workflowInstanceId: string\n stepInstanceId: string | null\n eventType: string\n eventData: any\n userId?: string\n tenantId: string\n organizationId: string\n }\n): Promise<WorkflowEvent> {\n const workflowEvent = em.create(WorkflowEvent, {\n ...event,\n occurredAt: new Date(),\n })\n\n await em.persist(workflowEvent).flush()\n return workflowEvent\n}\n\n/**\n * Validate form data against JSON schema (basic validation for MVP)\n *\n * In Phase 7, we'll implement comprehensive JSON Schema validation.\n * For MVP, we do basic type checking.\n *\n * @param formData - User-provided form data\n * @param formSchema - JSON schema defining expected structure\n * @throws Error if validation fails\n */\nfunction validateFormData(\n formData: Record<string, any>,\n formSchema: any\n): void {\n // For MVP: Basic validation - just check required fields exist\n if (!formSchema || !formSchema.properties) {\n return // No schema to validate against\n }\n\n const requiredFields = formSchema.required || []\n\n for (const field of requiredFields) {\n if (!(field in formData) || formData[field] === null || formData[field] === undefined) {\n throw new Error(`Required field missing: ${field}`)\n }\n }\n\n // Additional type validation can be added in Phase 7\n // For now, this basic validation is sufficient\n}\n"],
|
|
5
|
-
"mappings": "AAcA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,uBAAuB;AAChC,YAAY,iBAAiB;AAC7B,YAAY,uBAAuB;AAa5B,MAAM,sBAAsB,MAAM;AAAA,EACvC,YACE,SACO,MACA,SACP;AACA,UAAM,OAAO;AAHN;AACA;AAGP,SAAK,OAAO;AAAA,EACd;AACF;AAqBA,eAAsB,iBACpB,IACA,WACA,SACe;AACf,QAAM,EAAE,QAAQ,UAAU,QAAQ,SAAS,IAAI;AAG/C,QAAM,OAAO,MAAM,GAAG,QAAQ,UAAU;AAAA,IACtC,IAAI;AAAA,IACJ,QAAQ,EAAE,KAAK,CAAC,WAAW,aAAa,EAAE;AAAA,EAC5C,CAAC;AAED,MAAI,CAAC,MAAM;AACT,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,EAAE,OAAO;AAAA,IACX;AAAA,EACF;AAIA,MAAI,KAAK,YAAY;AACnB,QAAI;AACF,uBAAiB,UAAU,KAAK,UAAU;AAAA,IAC5C,SAAS,OAAO;AACd,YAAM,IAAI;AAAA,QACR,iBAAiB,QAAQ,MAAM,UAAU;AAAA,QACzC;AAAA,QACA,EAAE,QAAQ,YAAY,KAAK,YAAY,SAAS;AAAA,MAClD;AAAA,IACF;AAAA,EACF;AAGA,QAAM,MAAM,oBAAI,KAAK;AACrB,OAAK,SAAS;AACd,OAAK,WAAW;AAChB,OAAK,cAAc;AACnB,OAAK,cAAc;AACnB,OAAK,WAAW,YAAY;AAC5B,OAAK,YAAY;AAEjB,QAAM,GAAG,MAAM;AAGf,QAAM,WAAW,MAAM,GAAG,QAAQ,kBAAkB,KAAK,kBAAkB;AAC3E,MAAI,CAAC,UAAU;AACb,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,EAAE,oBAAoB,KAAK,mBAAmB;AAAA,IAChD;AAAA,EACF;AAGA,WAAS,UAAU;AAAA,IACjB,GAAG,SAAS;AAAA,IACZ,GAAG;AAAA,EACL;AACA,WAAS,YAAY;AAGrB,QAAM,iBAAiB,IAAI;AAAA,IACzB,oBAAoB,SAAS;AAAA,IAC7B,gBAAgB,KAAK;AAAA,IACrB,WAAW;AAAA,IACX,WAAW;AAAA,MACT,QAAQ,KAAK;AAAA,MACb,UAAU,KAAK;AAAA,MACf,aAAa;AAAA,MACb;AAAA,IACF;AAAA,IACA;AAAA,IACA,UAAU,SAAS;AAAA,IACnB,gBAAgB,SAAS;AAAA,EAC3B,CAAC;AAGD,QAAM,eAAe,MAAM,GAAG,QAAQ,cAAc;AAAA,IAClD,IAAI,KAAK;AAAA,IACT,QAAQ;AAAA,EACV,CAAC;AAED,MAAI,cAAc;AAChB,UAAM,YAAY,SAAS,IAAI,cAAc,EAAE,YAAY,KAAK,IAAI,SAAS,CAAC;AAAA,EAChF;AAGA,QAAM,gBAAgB,SAAS;AAG/B,QAAM,aAAa,MAAM,GAAG,QAAQ,oBAAoB;AAAA,IACtD,IAAI,SAAS;AAAA,EACf,CAAC;AAED,MAAI,CAAC,YAAY;AACf,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,EAAE,cAAc,SAAS,aAAa;AAAA,IACxC;AAAA,EACF;AAGA,QAAM,mBAAmB,WAAW,WAAW,eAAe,CAAC,GAAG;AAAA,IAChE,CAAC,MAAW,EAAE,eAAe,iBAAiB,EAAE,YAAY;AAAA,EAC9D;AAEA,MAAI,gBAAgB,WAAW,GAAG;AAEhC;AAAA,EACF;AAGA,QAAM,oBAAoB;AAAA,IACxB,iBAAiB,SAAS;AAAA,IAC1B;AAAA,EACF;AAEA,QAAM,mBAAmB,MAAM,kBAAkB;AAAA,IAC/C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,QAAM,uBAAuB,iBAAiB,KAAK,OAAK,EAAE,OAAO;AAEjE,MAAI,CAAC,wBAAwB,CAAC,qBAAqB,YAAY;AAE7D,aAAS,SAAS;AAClB,UAAM,GAAG,MAAM;AACf;AAAA,EACF;AAIA,QAAM,mBAAmB,MAAM,kBAAkB;AAAA,IAC/C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,qBAAqB,WAAW;AAAA,IAChC;AAAA,EACF;AAEA,MAAI,CAAC,iBAAiB,SAAS;AAC7B,YAAQ,MAAM,oCAAoC,iBAAiB,KAAK;AAExE;AAAA,EACF;AAGA,QAAM,gBAAgB,IAAI,WAAW,SAAS,IAAI,EAAE,OAAO,CAAC;AAC9D;AAaA,eAAsB,cACpB,IACA,QACA,QACe;AACf,QAAM,OAAO,MAAM,GAAG,QAAQ,UAAU;AAAA,IACtC,IAAI;AAAA,IACJ,QAAQ;AAAA,EACV,CAAC;AAED,MAAI,CAAC,MAAM;AACT,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,EAAE,OAAO;AAAA,IACX;AAAA,EACF;AAEA,MAAI,KAAK,YAAY;AACnB,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,EAAE,QAAQ,YAAY,KAAK,WAAW;AAAA,IACxC;AAAA,EACF;AAEA,MAAI,CAAC,KAAK,mBAAmB,KAAK,gBAAgB,WAAW,GAAG;AAC9D,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,EAAE,OAAO;AAAA,IACX;AAAA,EACF;AAGA,QAAM,MAAM,oBAAI,KAAK;AACrB,OAAK,YAAY;AACjB,OAAK,YAAY;AACjB,OAAK,SAAS;AACd,OAAK,YAAY;AAEjB,QAAM,GAAG,MAAM;AAGf,QAAM,WAAW,MAAM,GAAG,QAAQ,kBAAkB,KAAK,kBAAkB;AAC3E,MAAI,UAAU;AACZ,UAAM,iBAAiB,IAAI;AAAA,MACzB,oBAAoB,SAAS;AAAA,MAC7B,gBAAgB,KAAK;AAAA,MACrB,WAAW;AAAA,MACX,WAAW;AAAA,QACT,QAAQ,KAAK;AAAA,QACb,UAAU,KAAK;AAAA,QACf,WAAW;AAAA,MACb;AAAA,MACA;AAAA,MACA,UAAU,SAAS;AAAA,MACnB,gBAAgB,SAAS;AAAA,IAC3B,CAAC;AAAA,EACH;AACF;AASA,eAAe,iBACb,IACA,
|
|
4
|
+
"sourcesContent": ["/**\n * Workflows Module - User Task Handler Service\n *\n * Handles user task lifecycle operations:\n * - Completing user tasks\n * - Claiming tasks from role queues\n * - Reassigning tasks\n * - Escalating overdue tasks\n *\n * Functional API (no classes) following Open Mercato conventions.\n */\n\nimport { EntityManager } from '@mikro-orm/core'\nimport type { AwilixContainer } from 'awilix'\nimport {\n UserTask,\n WorkflowInstance,\n WorkflowEvent,\n StepInstance,\n WorkflowDefinition,\n} from '../data/entities'\nimport { executeWorkflow } from './workflow-executor'\nimport * as stepHandler from './step-handler'\nimport * as transitionHandler from './transition-handler'\n\n// ============================================================================\n// Types and Interfaces\n// ============================================================================\n\nexport interface CompleteUserTaskOptions {\n taskId: string\n formData: Record<string, any>\n userId: string\n comments?: string\n}\n\nexport class UserTaskError extends Error {\n constructor(\n message: string,\n public code: string,\n public details?: any\n ) {\n super(message)\n this.name = 'UserTaskError'\n }\n}\n\n// ============================================================================\n// User Task Completion\n// ============================================================================\n\n/**\n * Complete a user task and resume workflow execution\n *\n * This function:\n * 1. Validates the task exists and can be completed\n * 2. Updates the task with form data and completion info\n * 3. Merges form data into workflow context\n * 4. Logs completion event\n * 5. Resumes workflow execution\n *\n * @param em - Entity manager\n * @param container - DI container for workflow execution\n * @param options - Task completion options\n * @throws UserTaskError if task not found or validation fails\n */\nexport async function completeUserTask(\n em: EntityManager,\n container: AwilixContainer,\n options: CompleteUserTaskOptions\n): Promise<void> {\n const { taskId, formData, userId, comments } = options\n\n // Fetch task\n const task = await em.findOne(UserTask, {\n id: taskId,\n status: { $in: ['PENDING', 'IN_PROGRESS'] },\n })\n\n if (!task) {\n throw new UserTaskError(\n 'Task not found or already completed',\n 'TASK_NOT_FOUND',\n { taskId }\n )\n }\n\n // Validate form data against schema (simple validation for MVP)\n // In Phase 7, we'll add comprehensive JSON Schema validation\n if (task.formSchema) {\n try {\n validateFormData(formData, task.formSchema)\n } catch (error) {\n throw new UserTaskError(\n error instanceof Error ? error.message : 'Form validation failed',\n 'FORM_VALIDATION_FAILED',\n { taskId, formSchema: task.formSchema, formData }\n )\n }\n }\n\n // Update task\n const now = new Date()\n task.status = 'COMPLETED'\n task.formData = formData\n task.completedBy = userId\n task.completedAt = now\n task.comments = comments || null\n task.updatedAt = now\n\n await em.flush()\n\n // Fetch workflow instance\n const instance = await em.findOne(WorkflowInstance, task.workflowInstanceId)\n if (!instance) {\n throw new UserTaskError(\n 'Workflow instance not found',\n 'INSTANCE_NOT_FOUND',\n { workflowInstanceId: task.workflowInstanceId }\n )\n }\n\n // Branch-scoped completion: when the task belongs to a parallel branch,\n // merge form data into the branch namespace and resume just that branch.\n if (task.branchInstanceId) {\n await logWorkflowEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: task.stepInstanceId,\n branchInstanceId: task.branchInstanceId,\n eventType: 'USER_TASK_COMPLETED',\n eventData: { taskId: task.id, taskName: task.taskName, completedBy: userId, formData },\n userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n const { resumeBranch } = await import('./parallel-handler')\n const resumed = await resumeBranch(em, {\n instanceId: instance.id,\n branchInstanceId: task.branchInstanceId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n contextMerge: formData,\n exitStepInstanceId: task.stepInstanceId,\n exitOutput: { userTaskId: task.id, formData },\n })\n\n if (resumed) {\n await executeWorkflow(em, container, instance.id, { userId })\n }\n return\n }\n\n // Merge form data into workflow context\n instance.context = {\n ...instance.context,\n ...formData,\n }\n instance.updatedAt = now\n\n // Log USER_TASK_COMPLETED event\n await logWorkflowEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: task.stepInstanceId,\n eventType: 'USER_TASK_COMPLETED',\n eventData: {\n taskId: task.id,\n taskName: task.taskName,\n completedBy: userId,\n formData,\n },\n userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n // Mark the step instance as completed\n const stepInstance = await em.findOne(StepInstance, {\n id: task.stepInstanceId,\n status: 'ACTIVE',\n })\n\n if (stepInstance) {\n await stepHandler.exitStep(em, stepInstance, { userTaskId: task.id, formData })\n }\n\n // Find the next automatic transition from the current step\n const currentStepId = instance.currentStepId\n\n // Load workflow definition to find transitions\n const definition = await em.findOne(WorkflowDefinition, {\n id: instance.definitionId,\n })\n\n if (!definition) {\n throw new UserTaskError(\n 'Workflow definition not found',\n 'DEFINITION_NOT_FOUND',\n { definitionId: instance.definitionId }\n )\n }\n\n // Find automatic transitions from current step\n const autoTransitions = (definition.definition.transitions || []).filter(\n (t: any) => t.fromStepId === currentStepId && t.trigger === 'auto'\n )\n\n if (autoTransitions.length === 0) {\n // No automatic transitions, workflow stays paused at current step\n return\n }\n\n // Find valid transitions using transition handler\n const transitionContext = {\n workflowContext: instance.context,\n userId,\n }\n\n const validTransitions = await transitionHandler.findValidTransitions(\n em,\n instance,\n currentStepId,\n transitionContext\n )\n\n const firstValidTransition = validTransitions.find(t => t.isValid)\n\n if (!firstValidTransition || !firstValidTransition.transition) {\n // Resume workflow execution anyway, maybe conditions will be met later\n instance.status = 'RUNNING'\n await em.flush()\n return\n }\n\n // Execute the transition to move to next step\n\n const transitionResult = await transitionHandler.executeTransition(\n em,\n container,\n instance,\n currentStepId,\n firstValidTransition.transition.toStepId,\n transitionContext\n )\n\n if (!transitionResult.success) {\n console.error(`[TaskHandler] Transition failed:`, transitionResult.error)\n // Don't throw, just leave workflow in current state\n return\n }\n\n // Now continue workflow execution from the new step\n await executeWorkflow(em, container, instance.id, { userId })\n}\n\n/**\n * Claim a user task from a role queue\n *\n * Allows a user to claim a task that's assigned to their role(s).\n * Prevents race conditions by checking task status.\n *\n * @param em - Entity manager\n * @param taskId - Task ID to claim\n * @param userId - User claiming the task\n * @throws UserTaskError if task cannot be claimed\n */\nexport async function claimUserTask(\n em: EntityManager,\n taskId: string,\n userId: string\n): Promise<void> {\n const task = await em.findOne(UserTask, {\n id: taskId,\n status: 'PENDING',\n })\n\n if (!task) {\n throw new UserTaskError(\n 'Task not found or already claimed',\n 'TASK_NOT_FOUND',\n { taskId }\n )\n }\n\n if (task.assignedTo) {\n throw new UserTaskError(\n 'Task is already assigned to a specific user',\n 'TASK_ALREADY_ASSIGNED',\n { taskId, assignedTo: task.assignedTo }\n )\n }\n\n if (!task.assignedToRoles || task.assignedToRoles.length === 0) {\n throw new UserTaskError(\n 'Task is not assigned to any roles',\n 'TASK_NOT_ROLE_ASSIGNED',\n { taskId }\n )\n }\n\n // Update task\n const now = new Date()\n task.claimedBy = userId\n task.claimedAt = now\n task.status = 'IN_PROGRESS'\n task.updatedAt = now\n\n await em.flush()\n\n // Log event\n const instance = await em.findOne(WorkflowInstance, task.workflowInstanceId)\n if (instance) {\n await logWorkflowEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: task.stepInstanceId,\n eventType: 'USER_TASK_STARTED',\n eventData: {\n taskId: task.id,\n taskName: task.taskName,\n claimedBy: userId,\n },\n userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n }\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Log workflow event to event sourcing table\n */\nasync function logWorkflowEvent(\n em: EntityManager,\n event: {\n workflowInstanceId: string\n stepInstanceId: string | null\n branchInstanceId?: string | null\n eventType: string\n eventData: any\n userId?: string\n tenantId: string\n organizationId: string\n }\n): Promise<WorkflowEvent> {\n const workflowEvent = em.create(WorkflowEvent, {\n ...event,\n occurredAt: new Date(),\n })\n\n await em.persist(workflowEvent).flush()\n return workflowEvent\n}\n\n/**\n * Validate form data against JSON schema (basic validation for MVP)\n *\n * In Phase 7, we'll implement comprehensive JSON Schema validation.\n * For MVP, we do basic type checking.\n *\n * @param formData - User-provided form data\n * @param formSchema - JSON schema defining expected structure\n * @throws Error if validation fails\n */\nfunction validateFormData(\n formData: Record<string, any>,\n formSchema: any\n): void {\n // For MVP: Basic validation - just check required fields exist\n if (!formSchema || !formSchema.properties) {\n return // No schema to validate against\n }\n\n const requiredFields = formSchema.required || []\n\n for (const field of requiredFields) {\n if (!(field in formData) || formData[field] === null || formData[field] === undefined) {\n throw new Error(`Required field missing: ${field}`)\n }\n }\n\n // Additional type validation can be added in Phase 7\n // For now, this basic validation is sufficient\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -9,11 +9,36 @@ class TimerError extends Error {
|
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
async function fireTimer(em, container, options) {
|
|
12
|
-
const { instanceId, stepInstanceId, userId, tenantId, organizationId } = options;
|
|
12
|
+
const { instanceId, stepInstanceId, branchInstanceId, userId, tenantId, organizationId } = options;
|
|
13
13
|
const eventLogger = container.resolve("eventLogger");
|
|
14
14
|
const stepHandler = container.resolve("stepHandler");
|
|
15
15
|
const transitionHandler = container.resolve("transitionHandler");
|
|
16
16
|
const workflowExecutor = container.resolve("workflowExecutor");
|
|
17
|
+
if (branchInstanceId) {
|
|
18
|
+
await eventLogger.logWorkflowEvent(em, {
|
|
19
|
+
workflowInstanceId: instanceId,
|
|
20
|
+
stepInstanceId,
|
|
21
|
+
branchInstanceId,
|
|
22
|
+
eventType: "TIMER_FIRED",
|
|
23
|
+
eventData: { firedAt: (/* @__PURE__ */ new Date()).toISOString(), branch: true },
|
|
24
|
+
userId,
|
|
25
|
+
tenantId,
|
|
26
|
+
organizationId
|
|
27
|
+
});
|
|
28
|
+
const { resumeBranch } = await import("./parallel-handler.js");
|
|
29
|
+
const resumed = await resumeBranch(em, {
|
|
30
|
+
instanceId,
|
|
31
|
+
branchInstanceId,
|
|
32
|
+
tenantId,
|
|
33
|
+
organizationId,
|
|
34
|
+
exitStepInstanceId: stepInstanceId,
|
|
35
|
+
exitOutput: { firedAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
36
|
+
});
|
|
37
|
+
if (resumed) {
|
|
38
|
+
await workflowExecutor.executeWorkflow(em, container, instanceId, { userId });
|
|
39
|
+
}
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
17
42
|
const instance = await findOneWithDecryption(
|
|
18
43
|
em,
|
|
19
44
|
WorkflowInstance,
|