@open-mercato/core 0.6.7-develop.6676.1.7dad6df292 → 0.6.7-develop.6685.1.77c0a5591b
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/generated/entities/gateway_payment_operation/index.js +2 -0
- package/dist/generated/entities/gateway_payment_operation/index.js.map +2 -2
- package/dist/generated/entities/gateway_transaction/index.js +2 -0
- package/dist/generated/entities/gateway_transaction/index.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +2 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/payment_gateways/api/capture/route.js +1 -1
- package/dist/modules/payment_gateways/api/capture/route.js.map +2 -2
- package/dist/modules/payment_gateways/data/entities.js +7 -0
- package/dist/modules/payment_gateways/data/entities.js.map +2 -2
- package/dist/modules/payment_gateways/lib/capture-ledger.js +183 -0
- package/dist/modules/payment_gateways/lib/capture-ledger.js.map +7 -0
- package/dist/modules/payment_gateways/lib/gateway-service.js +70 -8
- package/dist/modules/payment_gateways/lib/gateway-service.js.map +2 -2
- package/dist/modules/payment_gateways/lib/payment-operation-idempotency.js +1 -0
- package/dist/modules/payment_gateways/lib/payment-operation-idempotency.js.map +2 -2
- package/dist/modules/payment_gateways/migrations/Migration20260725101500_payment_gateways.js +16 -0
- package/dist/modules/payment_gateways/migrations/Migration20260725101500_payment_gateways.js.map +7 -0
- package/dist/modules/query_index/lib/indexer.js +12 -2
- package/dist/modules/query_index/lib/indexer.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js +5 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
- package/dist/modules/workflows/components/ActivitiesEditor.js +67 -11
- package/dist/modules/workflows/components/ActivitiesEditor.js.map +2 -2
- package/dist/modules/workflows/components/TransitionsEditor.js +205 -163
- package/dist/modules/workflows/components/TransitionsEditor.js.map +2 -2
- package/dist/modules/workflows/data/validators.js +34 -1
- package/dist/modules/workflows/data/validators.js.map +3 -3
- package/dist/modules/workflows/lib/activity-executor.js +18 -2
- package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
- package/dist/modules/workflows/lib/format-validation-error.js +46 -1
- package/dist/modules/workflows/lib/format-validation-error.js.map +2 -2
- package/generated/entities/gateway_payment_operation/index.ts +1 -0
- package/generated/entities/gateway_transaction/index.ts +1 -0
- package/generated/entity-fields-registry.ts +2 -0
- package/package.json +7 -7
- package/src/modules/attachments/i18n/de.json +12 -0
- package/src/modules/attachments/i18n/en.json +12 -0
- package/src/modules/attachments/i18n/es.json +12 -0
- package/src/modules/attachments/i18n/pl.json +12 -0
- package/src/modules/auth/i18n/de.json +1 -0
- package/src/modules/auth/i18n/en.json +1 -0
- package/src/modules/auth/i18n/es.json +1 -0
- package/src/modules/auth/i18n/pl.json +1 -0
- package/src/modules/catalog/i18n/de.json +18 -0
- package/src/modules/catalog/i18n/en.json +18 -0
- package/src/modules/catalog/i18n/es.json +18 -0
- package/src/modules/catalog/i18n/pl.json +18 -0
- package/src/modules/customers/i18n/de.json +44 -0
- package/src/modules/customers/i18n/en.json +44 -0
- package/src/modules/customers/i18n/es.json +44 -0
- package/src/modules/customers/i18n/pl.json +44 -0
- package/src/modules/payment_gateways/api/capture/route.ts +1 -1
- package/src/modules/payment_gateways/data/entities.ts +8 -2
- package/src/modules/payment_gateways/lib/capture-ledger.ts +260 -0
- package/src/modules/payment_gateways/lib/gateway-service.ts +83 -10
- package/src/modules/payment_gateways/lib/payment-operation-idempotency.ts +1 -0
- package/src/modules/payment_gateways/migrations/.snapshot-open-mercato.json +34 -0
- package/src/modules/payment_gateways/migrations/Migration20260725101500_payment_gateways.ts +21 -0
- package/src/modules/query_index/lib/indexer.ts +25 -2
- package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +12 -3
- package/src/modules/workflows/components/ActivitiesEditor.tsx +96 -12
- package/src/modules/workflows/components/TransitionsEditor.tsx +75 -17
- package/src/modules/workflows/data/validators.ts +52 -1
- package/src/modules/workflows/i18n/de.json +2 -0
- package/src/modules/workflows/i18n/en.json +2 -0
- package/src/modules/workflows/i18n/es.json +2 -0
- package/src/modules/workflows/i18n/pl.json +2 -0
- package/src/modules/workflows/lib/activity-executor.ts +38 -3
- package/src/modules/workflows/lib/format-validation-error.ts +60 -0
|
@@ -202,6 +202,20 @@ export const activityRetryPolicySchema = z.object({
|
|
|
202
202
|
maxIntervalMs: z.number().int().min(0),
|
|
203
203
|
})
|
|
204
204
|
|
|
205
|
+
/**
|
|
206
|
+
* Config fields each activity executor requires to run (see
|
|
207
|
+
* `lib/activity-executor.ts`). WAIT is handled separately below because it
|
|
208
|
+
* takes "duration" OR "until" rather than a fixed key set.
|
|
209
|
+
*/
|
|
210
|
+
const REQUIRED_ACTIVITY_CONFIG_KEYS: Partial<Record<ActivityType, readonly string[]>> = {
|
|
211
|
+
SEND_EMAIL: ['to', 'subject'],
|
|
212
|
+
CALL_API: ['endpoint'],
|
|
213
|
+
CALL_WEBHOOK: ['url'],
|
|
214
|
+
UPDATE_ENTITY: ['commandId', 'input'],
|
|
215
|
+
EMIT_EVENT: ['eventName'],
|
|
216
|
+
EXECUTE_FUNCTION: ['functionName'],
|
|
217
|
+
}
|
|
218
|
+
|
|
205
219
|
// Activity definition (embedded in transitions)
|
|
206
220
|
export const activityDefinitionSchema = z.object({
|
|
207
221
|
activityId: z.string().min(1).max(100).regex(/^[a-z0-9_-]+$/, 'Activity ID must contain only lowercase letters, numbers, hyphens, and underscores'),
|
|
@@ -210,12 +224,49 @@ export const activityDefinitionSchema = z.object({
|
|
|
210
224
|
config: z.record(z.string(), z.any()),
|
|
211
225
|
async: z.boolean().default(false).optional(), // For Phase 8.3
|
|
212
226
|
retryPolicy: activityRetryPolicySchema.optional(),
|
|
213
|
-
|
|
227
|
+
/**
|
|
228
|
+
* Per-activity timeout in milliseconds. This is what the editor writes and
|
|
229
|
+
* what `executeActivity` reads; it was missing from the schema, so
|
|
230
|
+
* `z.object()` stripped it on save and UI-configured timeouts silently did
|
|
231
|
+
* nothing (#4424).
|
|
232
|
+
*/
|
|
233
|
+
timeoutMs: z.number().int().positive().optional(),
|
|
234
|
+
/**
|
|
235
|
+
* @deprecated Use `timeoutMs`. Accepted for definitions already stored with
|
|
236
|
+
* an ISO 8601 duration string; the executor normalizes it to milliseconds.
|
|
237
|
+
*/
|
|
238
|
+
timeout: z.string().optional(),
|
|
214
239
|
compensation: z.object({
|
|
215
240
|
activityId: z.string().min(1), // ID of compensation activity
|
|
216
241
|
automatic: z.boolean().default(true).optional() // Auto-trigger on failure
|
|
217
242
|
}).optional(), // Compensation configuration (Phase 8.2)
|
|
218
243
|
}).superRefine((activity, ctx) => {
|
|
244
|
+
// Config keys each activity executor requires at runtime. Without this, an
|
|
245
|
+
// activity missing e.g. CALL_API's `endpoint` saved cleanly from the visual
|
|
246
|
+
// editor and only failed once an instance ran it — the "edit silently
|
|
247
|
+
// disappeared / nothing told me why" report in #4232 (and the class of bug
|
|
248
|
+
// #4322 was an instance of). Validating here surfaces the exact missing field
|
|
249
|
+
// at edit time, since both the editor's save path and the API route parse
|
|
250
|
+
// through this schema.
|
|
251
|
+
const requiredConfigKeys = REQUIRED_ACTIVITY_CONFIG_KEYS[activity.activityType]
|
|
252
|
+
if (requiredConfigKeys) {
|
|
253
|
+
const config = activity.config || {}
|
|
254
|
+
for (const key of requiredConfigKeys) {
|
|
255
|
+
const value = config[key]
|
|
256
|
+
const isEmpty =
|
|
257
|
+
value == null ||
|
|
258
|
+
(typeof value === 'string' && value.trim() === '') ||
|
|
259
|
+
(key === 'input' && typeof value !== 'object')
|
|
260
|
+
if (isEmpty) {
|
|
261
|
+
ctx.addIssue({
|
|
262
|
+
code: 'custom',
|
|
263
|
+
path: ['config', key],
|
|
264
|
+
message: `${activity.activityType} activity requires "${key}"`,
|
|
265
|
+
})
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
219
270
|
if (activity.activityType !== 'WAIT') return
|
|
220
271
|
const config = activity.config || {}
|
|
221
272
|
const hasDuration = config.duration != null && config.duration !== ''
|
|
@@ -80,6 +80,8 @@
|
|
|
80
80
|
"workflows.activities.async": "Asynchron",
|
|
81
81
|
"workflows.activities.compensation": "Kompensation",
|
|
82
82
|
"workflows.activities.config": "Konfiguration",
|
|
83
|
+
"workflows.activities.configInvalidJson": "Ungültiges JSON",
|
|
84
|
+
"workflows.activities.configMustBeObject": "Konfiguration muss ein JSON-Objekt sein",
|
|
83
85
|
"workflows.activities.deleteActivity": "Aktivität löschen",
|
|
84
86
|
"workflows.activities.editActivity": "Aktivität bearbeiten",
|
|
85
87
|
"workflows.activities.plural": "Aktivitäten",
|
|
@@ -80,6 +80,8 @@
|
|
|
80
80
|
"workflows.activities.async": "Asynchronous",
|
|
81
81
|
"workflows.activities.compensation": "Compensation",
|
|
82
82
|
"workflows.activities.config": "Configuration",
|
|
83
|
+
"workflows.activities.configInvalidJson": "Invalid JSON",
|
|
84
|
+
"workflows.activities.configMustBeObject": "Config must be a JSON object",
|
|
83
85
|
"workflows.activities.deleteActivity": "Delete Activity",
|
|
84
86
|
"workflows.activities.editActivity": "Edit Activity",
|
|
85
87
|
"workflows.activities.plural": "Activities",
|
|
@@ -80,6 +80,8 @@
|
|
|
80
80
|
"workflows.activities.async": "Asincrona",
|
|
81
81
|
"workflows.activities.compensation": "Compensacion",
|
|
82
82
|
"workflows.activities.config": "Configuracion",
|
|
83
|
+
"workflows.activities.configInvalidJson": "JSON no válido",
|
|
84
|
+
"workflows.activities.configMustBeObject": "La configuración debe ser un objeto JSON",
|
|
83
85
|
"workflows.activities.deleteActivity": "Eliminar actividad",
|
|
84
86
|
"workflows.activities.editActivity": "Editar actividad",
|
|
85
87
|
"workflows.activities.plural": "Actividades",
|
|
@@ -80,6 +80,8 @@
|
|
|
80
80
|
"workflows.activities.async": "Asynchroniczne",
|
|
81
81
|
"workflows.activities.compensation": "Kompensacja",
|
|
82
82
|
"workflows.activities.config": "Konfiguracja",
|
|
83
|
+
"workflows.activities.configInvalidJson": "Nieprawidłowy JSON",
|
|
84
|
+
"workflows.activities.configMustBeObject": "Konfiguracja musi być obiektem JSON",
|
|
83
85
|
"workflows.activities.deleteActivity": "Usuń Działanie",
|
|
84
86
|
"workflows.activities.editActivity": "Edytuj Działanie",
|
|
85
87
|
"workflows.activities.plural": "Działania",
|
|
@@ -107,9 +107,42 @@ export interface ActivityDefinition {
|
|
|
107
107
|
async?: boolean // Flag to execute activity asynchronously via queue
|
|
108
108
|
retryPolicy?: RetryPolicy
|
|
109
109
|
timeoutMs?: number
|
|
110
|
+
/**
|
|
111
|
+
* @deprecated Use `timeoutMs`. Legacy ISO 8601 duration string accepted by
|
|
112
|
+
* the definition schema before #4424; normalized by `resolveActivityTimeoutMs`.
|
|
113
|
+
*/
|
|
114
|
+
timeout?: string
|
|
110
115
|
compensate?: boolean // Flag to execute compensation on failure
|
|
111
116
|
}
|
|
112
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Effective timeout for an activity, in milliseconds.
|
|
120
|
+
*
|
|
121
|
+
* The editor and this executor both speak `timeoutMs`, but the definition
|
|
122
|
+
* schema historically accepted only an ISO 8601 `timeout` string — so stored
|
|
123
|
+
* definitions can carry either. Prefer `timeoutMs`; fall back to parsing
|
|
124
|
+
* `timeout`, ignoring a malformed value rather than throwing mid-execution
|
|
125
|
+
* (an unparseable timeout must not fail an activity that would otherwise
|
|
126
|
+
* succeed). Returns undefined when no usable timeout is configured (#4424).
|
|
127
|
+
*/
|
|
128
|
+
export function resolveActivityTimeoutMs(activity: {
|
|
129
|
+
timeoutMs?: number
|
|
130
|
+
timeout?: string
|
|
131
|
+
}): number | undefined {
|
|
132
|
+
if (typeof activity.timeoutMs === 'number' && activity.timeoutMs > 0) {
|
|
133
|
+
return activity.timeoutMs
|
|
134
|
+
}
|
|
135
|
+
if (typeof activity.timeout === 'string' && activity.timeout.trim().length > 0) {
|
|
136
|
+
try {
|
|
137
|
+
const parsed = parseDuration(activity.timeout.trim())
|
|
138
|
+
if (Number.isFinite(parsed) && parsed > 0) return parsed
|
|
139
|
+
} catch {
|
|
140
|
+
return undefined
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return undefined
|
|
144
|
+
}
|
|
145
|
+
|
|
113
146
|
export interface RetryPolicy {
|
|
114
147
|
maxAttempts: number
|
|
115
148
|
initialIntervalMs: number
|
|
@@ -332,11 +365,13 @@ export async function executeActivity(
|
|
|
332
365
|
try {
|
|
333
366
|
const startTime = Date.now()
|
|
334
367
|
|
|
335
|
-
// Execute with timeout if specified
|
|
336
|
-
|
|
368
|
+
// Execute with timeout if specified (timeoutMs, or a legacy ISO 8601
|
|
369
|
+
// `timeout` string normalized to ms — see resolveActivityTimeoutMs).
|
|
370
|
+
const timeoutMs = resolveActivityTimeoutMs(activity)
|
|
371
|
+
const result = timeoutMs
|
|
337
372
|
? await executeWithTimeout(
|
|
338
373
|
() => executeActivityByType(em, container, activity, context),
|
|
339
|
-
|
|
374
|
+
timeoutMs
|
|
340
375
|
)
|
|
341
376
|
: await executeActivityByType(em, container, activity, context)
|
|
342
377
|
|
|
@@ -8,6 +8,66 @@ type ApiErrorBody = {
|
|
|
8
8
|
details?: ZodIssueLite[]
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
// Collection segments in a definition path, mapped to the label an operator
|
|
12
|
+
// recognizes from the editor. Indexes are rendered 1-based.
|
|
13
|
+
const COLLECTION_LABELS: Record<string, string> = {
|
|
14
|
+
steps: 'step',
|
|
15
|
+
transitions: 'transition',
|
|
16
|
+
activities: 'activity',
|
|
17
|
+
triggers: 'trigger',
|
|
18
|
+
preConditions: 'pre-condition',
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Turn a raw Zod path into something an operator can act on:
|
|
23
|
+
* `steps.2.activities.0.config.endpoint` → `step 3 › activity 1 › config.endpoint`.
|
|
24
|
+
*
|
|
25
|
+
* The raw dotted path reads as internal JSON and gives no hint which node to
|
|
26
|
+
* open in the visual editor (#4232).
|
|
27
|
+
*/
|
|
28
|
+
export function humanizeDefinitionIssuePath(path: ReadonlyArray<PropertyKey>): string {
|
|
29
|
+
if (!path.length) return 'definition'
|
|
30
|
+
const parts: string[] = []
|
|
31
|
+
let pending: string | null = null
|
|
32
|
+
|
|
33
|
+
for (const rawSegment of path) {
|
|
34
|
+
// Zod types issue paths as PropertyKey; symbols can't appear in JSON data
|
|
35
|
+
// but narrow defensively rather than casting.
|
|
36
|
+
const segment = typeof rawSegment === 'symbol' ? rawSegment.toString() : rawSegment
|
|
37
|
+
if (typeof segment === 'number') {
|
|
38
|
+
parts.push(pending ? `${pending} ${segment + 1}` : `#${segment + 1}`)
|
|
39
|
+
pending = null
|
|
40
|
+
continue
|
|
41
|
+
}
|
|
42
|
+
const label = COLLECTION_LABELS[segment]
|
|
43
|
+
if (label) {
|
|
44
|
+
if (pending) parts.push(pending)
|
|
45
|
+
pending = label
|
|
46
|
+
continue
|
|
47
|
+
}
|
|
48
|
+
if (pending) {
|
|
49
|
+
parts.push(pending)
|
|
50
|
+
pending = null
|
|
51
|
+
}
|
|
52
|
+
parts.push(segment)
|
|
53
|
+
}
|
|
54
|
+
if (pending) parts.push(pending)
|
|
55
|
+
|
|
56
|
+
const [head, ...rest] = parts
|
|
57
|
+
if (!rest.length) return head
|
|
58
|
+
// Keep trailing field names dotted (config.endpoint), collections chevroned.
|
|
59
|
+
const grouped: string[] = [head]
|
|
60
|
+
for (const part of rest) {
|
|
61
|
+
const isIndexed = /\s\d+$/.test(part) || part.startsWith('#')
|
|
62
|
+
if (!isIndexed && grouped.length > 0 && !/\s\d+$/.test(grouped[grouped.length - 1]) && !grouped[grouped.length - 1].startsWith('#')) {
|
|
63
|
+
grouped[grouped.length - 1] = `${grouped[grouped.length - 1]}.${part}`
|
|
64
|
+
continue
|
|
65
|
+
}
|
|
66
|
+
grouped.push(part)
|
|
67
|
+
}
|
|
68
|
+
return grouped.join(' › ')
|
|
69
|
+
}
|
|
70
|
+
|
|
11
71
|
/**
|
|
12
72
|
* Format an API validation error body into a user-readable message.
|
|
13
73
|
*
|