@open-mercato/core 0.8.1-develop.7296.1.2111d779db → 0.8.1-develop.7297.1.67605e74f9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/modules/workflows/lib/activity-executor.js +87 -20
- package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
- package/dist/modules/workflows/lib/activity-types.js +4 -4
- package/dist/modules/workflows/lib/activity-types.js.map +2 -2
- package/dist/modules/workflows/lib/activity-worker-handler.js +16 -11
- package/dist/modules/workflows/lib/activity-worker-handler.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/workflows/AGENTS.md +4 -4
- package/src/modules/workflows/lib/activity-executor.ts +125 -28
- package/src/modules/workflows/lib/activity-types.ts +8 -4
- package/src/modules/workflows/lib/activity-worker-handler.ts +16 -12
|
@@ -460,11 +460,12 @@ async function executeActivityByType(em, container, activity, context, signal) {
|
|
|
460
460
|
signal
|
|
461
461
|
});
|
|
462
462
|
}
|
|
463
|
-
async function executeSendEmail(config, context, container) {
|
|
463
|
+
async function executeSendEmail(config, context, container, signal) {
|
|
464
464
|
const { to, subject, template, templateData, body } = config;
|
|
465
465
|
if (!to || !subject) {
|
|
466
466
|
throw new Error('SEND_EMAIL requires "to" and "subject" fields');
|
|
467
467
|
}
|
|
468
|
+
throwIfAborted(signal);
|
|
468
469
|
logger.info("Send email activity invoked", { component: "SEND_EMAIL", subject });
|
|
469
470
|
let emailService;
|
|
470
471
|
try {
|
|
@@ -473,19 +474,26 @@ async function executeSendEmail(config, context, container) {
|
|
|
473
474
|
emailService = void 0;
|
|
474
475
|
}
|
|
475
476
|
if (emailService && typeof emailService.send === "function") {
|
|
476
|
-
await
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
477
|
+
await raceAbortable(
|
|
478
|
+
Promise.resolve(
|
|
479
|
+
emailService.send({
|
|
480
|
+
to,
|
|
481
|
+
subject,
|
|
482
|
+
template,
|
|
483
|
+
templateData,
|
|
484
|
+
body,
|
|
485
|
+
signal
|
|
486
|
+
})
|
|
487
|
+
),
|
|
488
|
+
signal
|
|
489
|
+
);
|
|
490
|
+
throwIfAborted(signal);
|
|
483
491
|
return { sent: true, to, subject, via: "emailService" };
|
|
484
492
|
}
|
|
485
493
|
logger.warn("SEND_EMAIL has no registered email service; email was not sent", { component: "SEND_EMAIL", subject });
|
|
486
494
|
return { sent: false, simulated: true, to, subject, via: "console", reason: "no-email-service" };
|
|
487
495
|
}
|
|
488
|
-
async function executeEmitEvent(config, context, container) {
|
|
496
|
+
async function executeEmitEvent(config, context, container, signal) {
|
|
489
497
|
const { eventName, payload } = config;
|
|
490
498
|
if (!eventName) {
|
|
491
499
|
throw new Error('EMIT_EVENT requires "eventName" field');
|
|
@@ -499,6 +507,7 @@ async function executeEmitEvent(config, context, container) {
|
|
|
499
507
|
`EMIT_EVENT payload contains unresolved template variables for: ${unresolvedPayloadKeys.join(", ")}. Check that the workflow context provides these keys.`
|
|
500
508
|
);
|
|
501
509
|
}
|
|
510
|
+
throwIfAborted(signal);
|
|
502
511
|
const eventBus = container.resolve("eventBus");
|
|
503
512
|
if (!eventBus || typeof eventBus.emitEvent !== "function") {
|
|
504
513
|
throw new Error("Event bus not available in container");
|
|
@@ -512,10 +521,16 @@ async function executeEmitEvent(config, context, container) {
|
|
|
512
521
|
organizationId: context.workflowInstance.organizationId
|
|
513
522
|
}
|
|
514
523
|
};
|
|
515
|
-
await
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
524
|
+
await raceAbortable(
|
|
525
|
+
Promise.resolve(
|
|
526
|
+
eventBus.emitEvent(eventName, enrichedPayload, {
|
|
527
|
+
tenantId: context.workflowInstance.tenantId,
|
|
528
|
+
organizationId: context.workflowInstance.organizationId
|
|
529
|
+
})
|
|
530
|
+
),
|
|
531
|
+
signal
|
|
532
|
+
);
|
|
533
|
+
throwIfAborted(signal);
|
|
519
534
|
return { emitted: true, eventName, payload: enrichedPayload };
|
|
520
535
|
}
|
|
521
536
|
const UNRESOLVED_TEMPLATE_PATTERN = /\{\{[^}]+\}\}/;
|
|
@@ -533,7 +548,7 @@ function findUnresolvedTemplateKeys(value, path = "") {
|
|
|
533
548
|
}
|
|
534
549
|
return [];
|
|
535
550
|
}
|
|
536
|
-
async function executeUpdateEntity(em, config, context, container) {
|
|
551
|
+
async function executeUpdateEntity(em, config, context, container, signal) {
|
|
537
552
|
const { commandId, input, statusDictionary } = config;
|
|
538
553
|
if (!commandId) {
|
|
539
554
|
throw new Error('UPDATE_ENTITY requires "commandId" field (e.g., "sales.documents.update")');
|
|
@@ -546,6 +561,7 @@ async function executeUpdateEntity(em, config, context, container) {
|
|
|
546
561
|
container,
|
|
547
562
|
context.workflowInstance.tenantId
|
|
548
563
|
);
|
|
564
|
+
throwIfAborted(signal);
|
|
549
565
|
if (!isWorkflowCommandEnabled(workflowSafeCommand, commandPolicy)) {
|
|
550
566
|
throw new Error("UPDATE_ENTITY command is not enabled for this tenant");
|
|
551
567
|
}
|
|
@@ -563,6 +579,7 @@ async function executeUpdateEntity(em, config, context, container) {
|
|
|
563
579
|
context.workflowInstance.tenantId,
|
|
564
580
|
context.workflowInstance.organizationId
|
|
565
581
|
);
|
|
582
|
+
throwIfAborted(signal);
|
|
566
583
|
if (!authorized) {
|
|
567
584
|
throw new Error("UPDATE_ENTITY command is not authorized");
|
|
568
585
|
}
|
|
@@ -585,6 +602,7 @@ async function executeUpdateEntity(em, config, context, container) {
|
|
|
585
602
|
context.workflowInstance.tenantId,
|
|
586
603
|
context.workflowInstance.organizationId
|
|
587
604
|
);
|
|
605
|
+
throwIfAborted(signal);
|
|
588
606
|
if (statusEntryId) {
|
|
589
607
|
finalInput.statusEntryId = statusEntryId;
|
|
590
608
|
}
|
|
@@ -602,10 +620,17 @@ async function executeUpdateEntity(em, config, context, container) {
|
|
|
602
620
|
selectedOrganizationId: context.workflowInstance.organizationId,
|
|
603
621
|
organizationIds: context.workflowInstance.organizationId ? [context.workflowInstance.organizationId] : null
|
|
604
622
|
};
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
623
|
+
throwIfAborted(signal);
|
|
624
|
+
const { result, logEntry } = await raceAbortable(
|
|
625
|
+
Promise.resolve(
|
|
626
|
+
commandBus.execute(commandId, {
|
|
627
|
+
input: finalInput,
|
|
628
|
+
ctx
|
|
629
|
+
})
|
|
630
|
+
),
|
|
631
|
+
signal
|
|
632
|
+
);
|
|
633
|
+
throwIfAborted(signal);
|
|
609
634
|
return {
|
|
610
635
|
executed: true,
|
|
611
636
|
commandId,
|
|
@@ -705,18 +730,23 @@ async function executeCallWebhook(config, context, deps = {}) {
|
|
|
705
730
|
result
|
|
706
731
|
};
|
|
707
732
|
}
|
|
708
|
-
async function executeFunction(config, context, container) {
|
|
733
|
+
async function executeFunction(config, context, container, signal) {
|
|
709
734
|
const { functionName, args = {} } = config;
|
|
710
735
|
if (!functionName) {
|
|
711
736
|
throw new Error('EXECUTE_FUNCTION requires "functionName" field');
|
|
712
737
|
}
|
|
738
|
+
throwIfAborted(signal);
|
|
713
739
|
const fnKey = `workflowFunction:${functionName}`;
|
|
714
740
|
try {
|
|
715
741
|
const fn = container.resolve(fnKey);
|
|
716
742
|
if (typeof fn !== "function") {
|
|
717
743
|
throw new Error(`Registered workflow function "${functionName}" is not a function`);
|
|
718
744
|
}
|
|
719
|
-
const result = await
|
|
745
|
+
const result = await raceAbortable(
|
|
746
|
+
Promise.resolve(fn(args, context, signal)),
|
|
747
|
+
signal
|
|
748
|
+
);
|
|
749
|
+
throwIfAborted(signal);
|
|
720
750
|
return { executed: true, functionName, result };
|
|
721
751
|
} catch (error) {
|
|
722
752
|
if (error instanceof Error && error.message.includes("not registered")) {
|
|
@@ -1168,6 +1198,43 @@ async function executeWithTimeout(executor, timeoutMs) {
|
|
|
1168
1198
|
clearTimeout(timeoutId);
|
|
1169
1199
|
}
|
|
1170
1200
|
}
|
|
1201
|
+
function createActivityAbortError(signal) {
|
|
1202
|
+
if (signal?.reason instanceof Error) return signal.reason;
|
|
1203
|
+
try {
|
|
1204
|
+
return new DOMException("The operation was aborted", "AbortError");
|
|
1205
|
+
} catch {
|
|
1206
|
+
const error = new Error("The operation was aborted");
|
|
1207
|
+
error.name = "AbortError";
|
|
1208
|
+
return error;
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
function throwIfAborted(signal) {
|
|
1212
|
+
if (signal?.aborted) {
|
|
1213
|
+
throw createActivityAbortError(signal);
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
function raceAbortable(promise, signal) {
|
|
1217
|
+
if (!signal) return promise;
|
|
1218
|
+
if (signal.aborted) {
|
|
1219
|
+
return Promise.reject(createActivityAbortError(signal));
|
|
1220
|
+
}
|
|
1221
|
+
return new Promise((resolve, reject) => {
|
|
1222
|
+
const onAbort = () => {
|
|
1223
|
+
reject(createActivityAbortError(signal));
|
|
1224
|
+
};
|
|
1225
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1226
|
+
promise.then(
|
|
1227
|
+
(value) => {
|
|
1228
|
+
signal.removeEventListener("abort", onAbort);
|
|
1229
|
+
resolve(value);
|
|
1230
|
+
},
|
|
1231
|
+
(error) => {
|
|
1232
|
+
signal.removeEventListener("abort", onAbort);
|
|
1233
|
+
reject(error);
|
|
1234
|
+
}
|
|
1235
|
+
);
|
|
1236
|
+
});
|
|
1237
|
+
}
|
|
1171
1238
|
bindActivityExecutor({
|
|
1172
1239
|
executeSendEmail,
|
|
1173
1240
|
executeEmitEvent,
|