@open-mercato/core 0.10.1-develop.7708.1.5a5c248637 → 0.10.1-develop.7709.1.046124599c
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 +78 -51
- package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
- package/dist/modules/workflows/lib/activity-registry.js.map +2 -2
- package/dist/modules/workflows/lib/activity-types.js +4 -1
- package/dist/modules/workflows/lib/activity-types.js.map +2 -2
- package/dist/modules/workflows/lib/activity-worker-handler.js +1 -1
- package/dist/modules/workflows/lib/activity-worker-handler.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/workflows/lib/activity-executor.ts +89 -58
- package/src/modules/workflows/lib/activity-registry.ts +1 -0
- package/src/modules/workflows/lib/activity-types.ts +4 -1
- package/src/modules/workflows/lib/activity-worker-handler.ts +2 -2
|
@@ -170,7 +170,7 @@ async function enqueueActivity(em, activity, context) {
|
|
|
170
170
|
workflowContext,
|
|
171
171
|
stepContext,
|
|
172
172
|
retryPolicy: activity.retryPolicy,
|
|
173
|
-
timeoutMs: activity
|
|
173
|
+
timeoutMs: resolveActivityTimeoutMs(activity),
|
|
174
174
|
tenantId: workflowInstance.tenantId,
|
|
175
175
|
organizationId: workflowInstance.organizationId,
|
|
176
176
|
userId: context.userId
|
|
@@ -458,7 +458,8 @@ async function executeActivityByType(em, container, activity, context, signal) {
|
|
|
458
458
|
return await entry.execute(interpolatedConfig, context, {
|
|
459
459
|
em,
|
|
460
460
|
container,
|
|
461
|
-
signal
|
|
461
|
+
signal,
|
|
462
|
+
timeoutMs: resolveActivityTimeoutMs(activity)
|
|
462
463
|
});
|
|
463
464
|
}
|
|
464
465
|
async function executeSendEmail(config, context, container, signal) {
|
|
@@ -669,6 +670,34 @@ async function resolveDictionaryEntryId(em, dictionaryKey, value, tenantId, orga
|
|
|
669
670
|
return null;
|
|
670
671
|
}
|
|
671
672
|
}
|
|
673
|
+
const WEBHOOK_TIMEOUT_MS = 3e4;
|
|
674
|
+
const WEBHOOK_MAX_RESPONSE_BYTES = 1024 * 1024;
|
|
675
|
+
async function readWebhookResponse(response, signal) {
|
|
676
|
+
const declaredLength = Number(response.headers.get("content-length"));
|
|
677
|
+
if (declaredLength > WEBHOOK_MAX_RESPONSE_BYTES) {
|
|
678
|
+
throw new Error(`[internal] CALL_WEBHOOK response exceeds ${WEBHOOK_MAX_RESPONSE_BYTES} bytes`);
|
|
679
|
+
}
|
|
680
|
+
if (!response.body) return "";
|
|
681
|
+
const reader = response.body.getReader();
|
|
682
|
+
const chunks = [];
|
|
683
|
+
let totalBytes = 0;
|
|
684
|
+
try {
|
|
685
|
+
for (; ; ) {
|
|
686
|
+
throwIfAborted(signal);
|
|
687
|
+
const { done, value } = await raceAbortable(reader.read(), signal);
|
|
688
|
+
if (done) break;
|
|
689
|
+
totalBytes += value.byteLength;
|
|
690
|
+
if (totalBytes > WEBHOOK_MAX_RESPONSE_BYTES) {
|
|
691
|
+
throw new Error(`[internal] CALL_WEBHOOK response exceeds ${WEBHOOK_MAX_RESPONSE_BYTES} bytes`);
|
|
692
|
+
}
|
|
693
|
+
chunks.push(value);
|
|
694
|
+
}
|
|
695
|
+
return new TextDecoder().decode(Buffer.concat(chunks, totalBytes));
|
|
696
|
+
} finally {
|
|
697
|
+
void reader.cancel().catch(() => void 0);
|
|
698
|
+
reader.releaseLock();
|
|
699
|
+
}
|
|
700
|
+
}
|
|
672
701
|
async function executeCallWebhook(config, context, deps = {}) {
|
|
673
702
|
const parsed = callWebhookConfigSchema.safeParse(config);
|
|
674
703
|
if (!parsed.success) {
|
|
@@ -678,58 +707,56 @@ async function executeCallWebhook(config, context, deps = {}) {
|
|
|
678
707
|
const { url, method, headers: rawHeaders, body } = parsed.data;
|
|
679
708
|
const headers = rawHeaders ?? {};
|
|
680
709
|
const allowPrivate = deps.allowPrivate ?? isAllowPrivateWorkflowWebhookUrlsEnabled();
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
710
|
+
return executeWithTimeout(async (deadlineSignal) => {
|
|
711
|
+
const signal = deps.signal ? AbortSignal.any([deadlineSignal, deps.signal]) : deadlineSignal;
|
|
712
|
+
throwIfAborted(signal);
|
|
713
|
+
let response;
|
|
714
|
+
try {
|
|
715
|
+
response = await raceAbortable(safeOutboundFetch(
|
|
716
|
+
url,
|
|
717
|
+
{
|
|
718
|
+
method,
|
|
719
|
+
headers: {
|
|
720
|
+
"Content-Type": "application/json",
|
|
721
|
+
...headers
|
|
722
|
+
},
|
|
723
|
+
body: body !== void 0 && body !== null ? JSON.stringify(body) : void 0,
|
|
724
|
+
redirect: "manual",
|
|
725
|
+
signal
|
|
690
726
|
},
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
727
|
+
{
|
|
728
|
+
subject: "Workflow webhook URL",
|
|
729
|
+
allowPrivate,
|
|
730
|
+
lookupHost: deps.lookupHost,
|
|
731
|
+
fetchImpl: deps.fetchImpl
|
|
732
|
+
}
|
|
733
|
+
), signal);
|
|
734
|
+
if (response.status >= 300 && response.status < 400) {
|
|
735
|
+
const location = response.headers.get("location");
|
|
736
|
+
throw new Error(
|
|
737
|
+
`CALL_WEBHOOK refused to follow redirect ${response.status} to ${location ?? "(no Location header)"}`
|
|
738
|
+
);
|
|
700
739
|
}
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
740
|
+
const text = await readWebhookResponse(response, signal);
|
|
741
|
+
const contentType = response.headers.get("content-type");
|
|
742
|
+
const result = contentType?.includes("application/json") ? JSON.parse(text) : text;
|
|
743
|
+
if (!response.ok) {
|
|
744
|
+
throw new Error(
|
|
745
|
+
`Webhook request failed with status ${response.status}: ${JSON.stringify(result)}`
|
|
746
|
+
);
|
|
747
|
+
}
|
|
748
|
+
return { status: response.status, statusText: response.statusText, result };
|
|
749
|
+
} catch (error) {
|
|
750
|
+
if (error instanceof UnsafeOutboundUrlError) {
|
|
751
|
+
throw new Error(
|
|
752
|
+
`CALL_WEBHOOK rejected unsafe URL (reason=${error.reason}): ${error.message}`
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
throw error;
|
|
756
|
+
} finally {
|
|
757
|
+
void response?.body?.cancel().catch(() => void 0);
|
|
707
758
|
}
|
|
708
|
-
|
|
709
|
-
}
|
|
710
|
-
if (response.status >= 300 && response.status < 400) {
|
|
711
|
-
const location = response.headers.get("location");
|
|
712
|
-
throw new Error(
|
|
713
|
-
`CALL_WEBHOOK refused to follow redirect ${response.status} to ${location ?? "(no Location header)"}`
|
|
714
|
-
);
|
|
715
|
-
}
|
|
716
|
-
let result;
|
|
717
|
-
const contentType = response.headers.get("content-type");
|
|
718
|
-
if (contentType && contentType.includes("application/json")) {
|
|
719
|
-
result = await response.json();
|
|
720
|
-
} else {
|
|
721
|
-
result = await response.text();
|
|
722
|
-
}
|
|
723
|
-
if (!response.ok) {
|
|
724
|
-
throw new Error(
|
|
725
|
-
`Webhook request failed with status ${response.status}: ${JSON.stringify(result)}`
|
|
726
|
-
);
|
|
727
|
-
}
|
|
728
|
-
return {
|
|
729
|
-
status: response.status,
|
|
730
|
-
statusText: response.statusText,
|
|
731
|
-
result
|
|
732
|
-
};
|
|
759
|
+
}, deps.timeoutMs && deps.timeoutMs > 0 ? deps.timeoutMs : WEBHOOK_TIMEOUT_MS);
|
|
733
760
|
}
|
|
734
761
|
async function executeFunction(config, context, container, signal) {
|
|
735
762
|
const { functionName, args = {} } = config;
|