@open-mercato/core 0.6.8-develop.6908.1.4792c7717e → 0.6.8-develop.6910.1.560e304de2
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 +1 -1
- package/dist/modules/workflows/components/NodeEditDialog.js +3 -2
- package/dist/modules/workflows/components/NodeEditDialog.js.map +2 -2
- package/dist/modules/workflows/components/TransitionsEditor.js +8 -4
- package/dist/modules/workflows/components/TransitionsEditor.js.map +2 -2
- package/dist/modules/workflows/components/fields/ActivityArrayEditor.js +8 -4
- package/dist/modules/workflows/components/fields/ActivityArrayEditor.js.map +2 -2
- package/dist/modules/workflows/lib/activity-executor.js +1 -14
- package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
- package/dist/modules/workflows/lib/activityTimeoutFields.js +28 -0
- package/dist/modules/workflows/lib/activityTimeoutFields.js.map +7 -0
- package/dist/modules/workflows/lib/duration.js +20 -1
- package/dist/modules/workflows/lib/duration.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/workflows/components/NodeEditDialog.tsx +3 -2
- package/src/modules/workflows/components/TransitionsEditor.tsx +11 -4
- package/src/modules/workflows/components/fields/ActivityArrayEditor.tsx +9 -4
- package/src/modules/workflows/lib/activity-executor.ts +7 -27
- package/src/modules/workflows/lib/activityTimeoutFields.ts +75 -0
- package/src/modules/workflows/lib/duration.ts +37 -0
|
@@ -26,6 +26,7 @@ import { callWebhookConfigSchema } from '../data/validators'
|
|
|
26
26
|
import { WorkflowActivityJob, WORKFLOW_ACTIVITIES_QUEUE_NAME } from './activity-queue-types'
|
|
27
27
|
import { logWorkflowEvent } from './event-logger'
|
|
28
28
|
import { parseDuration } from './duration'
|
|
29
|
+
import { resolveActivityTimeoutMs } from './activityTimeoutFields'
|
|
29
30
|
import { getWorkflowSafeCommand } from './workflow-safe-commands'
|
|
30
31
|
|
|
31
32
|
export { isPrivateUrl } from '@open-mercato/shared/lib/network'
|
|
@@ -114,33 +115,7 @@ export interface ActivityDefinition {
|
|
|
114
115
|
compensate?: boolean // Flag to execute compensation on failure
|
|
115
116
|
}
|
|
116
117
|
|
|
117
|
-
|
|
118
|
-
* Effective timeout for an activity, in milliseconds.
|
|
119
|
-
*
|
|
120
|
-
* The editor and this executor both speak `timeoutMs`, but the definition
|
|
121
|
-
* schema historically accepted only an ISO 8601 `timeout` string — so stored
|
|
122
|
-
* definitions can carry either. Prefer `timeoutMs`; fall back to parsing
|
|
123
|
-
* `timeout`, ignoring a malformed value rather than throwing mid-execution
|
|
124
|
-
* (an unparseable timeout must not fail an activity that would otherwise
|
|
125
|
-
* succeed). Returns undefined when no usable timeout is configured (#4424).
|
|
126
|
-
*/
|
|
127
|
-
export function resolveActivityTimeoutMs(activity: {
|
|
128
|
-
timeoutMs?: number
|
|
129
|
-
timeout?: string
|
|
130
|
-
}): number | undefined {
|
|
131
|
-
if (typeof activity.timeoutMs === 'number' && activity.timeoutMs > 0) {
|
|
132
|
-
return activity.timeoutMs
|
|
133
|
-
}
|
|
134
|
-
if (typeof activity.timeout === 'string' && activity.timeout.trim().length > 0) {
|
|
135
|
-
try {
|
|
136
|
-
const parsed = parseDuration(activity.timeout.trim())
|
|
137
|
-
if (Number.isFinite(parsed) && parsed > 0) return parsed
|
|
138
|
-
} catch {
|
|
139
|
-
return undefined
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return undefined
|
|
143
|
-
}
|
|
118
|
+
export { resolveActivityTimeoutMs }
|
|
144
119
|
|
|
145
120
|
export interface RetryPolicy {
|
|
146
121
|
maxAttempts: number
|
|
@@ -1494,6 +1469,11 @@ function sleep(ms: number): Promise<void> {
|
|
|
1494
1469
|
|
|
1495
1470
|
/**
|
|
1496
1471
|
* Execute a promise with timeout
|
|
1472
|
+
*
|
|
1473
|
+
* Only CALL_API and CALL_WEBHOOK honour the abort signal today — they forward
|
|
1474
|
+
* it to `fetch`. SEND_EMAIL, EMIT_EVENT, UPDATE_ENTITY and EXECUTE_FUNCTION
|
|
1475
|
+
* still run to completion after the timeout has been recorded. Tracked in
|
|
1476
|
+
* #5148.
|
|
1497
1477
|
*/
|
|
1498
1478
|
async function executeWithTimeout<T>(
|
|
1499
1479
|
executor: (signal: AbortSignal) => Promise<T>,
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { toTimeoutMs } from './duration'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The two timeout fields an activity definition can carry.
|
|
5
|
+
*
|
|
6
|
+
* `timeoutMs` is canonical and is what `resolveActivityTimeoutMs` prefers;
|
|
7
|
+
* `timeout` is the deprecated alias that stored definitions and the CrudForm
|
|
8
|
+
* activity editor still write. A timeout input must own both fields — read
|
|
9
|
+
* them merged and write both on every edit — otherwise the box shows the
|
|
10
|
+
* executor's effective timeout while changing only half of the pair, and the
|
|
11
|
+
* user's edit is silently discarded.
|
|
12
|
+
*/
|
|
13
|
+
export type ActivityTimeoutFields = {
|
|
14
|
+
timeout?: string
|
|
15
|
+
timeoutMs?: number
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Effective timeout for an activity, in milliseconds.
|
|
20
|
+
*
|
|
21
|
+
* The editors and the executor both speak `timeoutMs`, but the definition
|
|
22
|
+
* schema historically accepted only a `timeout` string — so stored definitions
|
|
23
|
+
* can carry either. Prefer `timeoutMs`; fall back to `toTimeoutMs`, which
|
|
24
|
+
* reads both a duration string ("PT30S", "5m") and a plain millisecond string
|
|
25
|
+
* ("30000") — the CrudForm activity editor writes the latter, and its own
|
|
26
|
+
* placeholder tells the user to. A malformed value is ignored rather than
|
|
27
|
+
* thrown mid-execution (an unparseable timeout must not fail an activity that
|
|
28
|
+
* would otherwise succeed). Returns undefined when no usable timeout is
|
|
29
|
+
* configured (#4424).
|
|
30
|
+
*
|
|
31
|
+
* `activity-executor` re-exports this so its import path stays stable; it
|
|
32
|
+
* lives here so the editors can assert the round-trip without pulling in the
|
|
33
|
+
* server-only executor.
|
|
34
|
+
*/
|
|
35
|
+
export function resolveActivityTimeoutMs(activity: ActivityTimeoutFields): number | undefined {
|
|
36
|
+
if (typeof activity.timeoutMs === 'number' && activity.timeoutMs > 0) {
|
|
37
|
+
return activity.timeoutMs
|
|
38
|
+
}
|
|
39
|
+
return toTimeoutMs(activity.timeout)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Text shown by a timeout input that accepts duration strings as well as
|
|
44
|
+
* milliseconds (the CrudForm activity editor, whose placeholder reads
|
|
45
|
+
* "PT30S or 30000").
|
|
46
|
+
*/
|
|
47
|
+
export function durationTimeoutInputValue(activity: ActivityTimeoutFields): string {
|
|
48
|
+
if (activity.timeout) return activity.timeout
|
|
49
|
+
return activity.timeoutMs != null ? String(activity.timeoutMs) : ''
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Value shown by a millisecond-only timeout input (the two visual editors). */
|
|
53
|
+
export function millisecondTimeoutInputValue(activity: ActivityTimeoutFields): number | '' {
|
|
54
|
+
return activity.timeoutMs ?? toTimeoutMs(activity.timeout) ?? ''
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Fields to merge into an activity after a duration-accepting input changed.
|
|
59
|
+
*
|
|
60
|
+
* The raw text stays in the deprecated alias so a partially typed duration
|
|
61
|
+
* survives the re-render, and `timeoutMs` carries whatever the executor can
|
|
62
|
+
* actually use — undefined while the text is incomplete or unusable.
|
|
63
|
+
*/
|
|
64
|
+
export function durationTimeoutPatch(raw: string): ActivityTimeoutFields {
|
|
65
|
+
return { timeout: raw || undefined, timeoutMs: toTimeoutMs(raw) }
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Fields to merge into an activity after a millisecond-only input changed.
|
|
70
|
+
* The deprecated alias is dropped, so clearing the box clears the timeout and
|
|
71
|
+
* stored definitions migrate off `timeout` as they are edited.
|
|
72
|
+
*/
|
|
73
|
+
export function millisecondTimeoutPatch(raw: string): ActivityTimeoutFields {
|
|
74
|
+
return { timeout: undefined, timeoutMs: toTimeoutMs(raw) }
|
|
75
|
+
}
|
|
@@ -49,3 +49,40 @@ export function parseDuration(duration: string): number {
|
|
|
49
49
|
|
|
50
50
|
throw new Error(`Invalid duration format: ${duration}`)
|
|
51
51
|
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Normalize a timeout value to milliseconds
|
|
55
|
+
*
|
|
56
|
+
* Accepts every shape the activity editors and stored definitions carry:
|
|
57
|
+
* - a millisecond number (30000)
|
|
58
|
+
* - a millisecond string ("30000") — what the CrudForm activity editor writes,
|
|
59
|
+
* and what its own placeholder ("PT30S or 30000") tells the user to type
|
|
60
|
+
* - a duration string ("PT30S", "5m")
|
|
61
|
+
*
|
|
62
|
+
* Returns undefined for an absent or unusable value rather than throwing, so a
|
|
63
|
+
* malformed timeout never fails an activity that would otherwise succeed.
|
|
64
|
+
*
|
|
65
|
+
* @param value - Raw timeout value
|
|
66
|
+
* @returns Milliseconds, or undefined when the value is absent or unusable
|
|
67
|
+
*/
|
|
68
|
+
export function toTimeoutMs(value: unknown): number | undefined {
|
|
69
|
+
if (typeof value === 'number') {
|
|
70
|
+
return Number.isFinite(value) && value > 0 ? value : undefined
|
|
71
|
+
}
|
|
72
|
+
if (typeof value !== 'string') return undefined
|
|
73
|
+
|
|
74
|
+
const trimmed = value.trim()
|
|
75
|
+
if (!trimmed) return undefined
|
|
76
|
+
|
|
77
|
+
if (/^\d+$/.test(trimmed)) {
|
|
78
|
+
const milliseconds = Number(trimmed)
|
|
79
|
+
return milliseconds > 0 ? milliseconds : undefined
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const milliseconds = parseDuration(trimmed)
|
|
84
|
+
return Number.isFinite(milliseconds) && milliseconds > 0 ? milliseconds : undefined
|
|
85
|
+
} catch {
|
|
86
|
+
return undefined
|
|
87
|
+
}
|
|
88
|
+
}
|