@open-mercato/core 0.4.2-canary-ec4978dbb3 → 0.4.2-canary-9074fd41ce
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/generated/entities.ids.generated.js +0 -1
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +0 -2
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/business_rules/data/validators.js +0 -34
- package/dist/modules/business_rules/data/validators.js.map +2 -2
- package/dist/modules/business_rules/index.js +1 -21
- package/dist/modules/business_rules/index.js.map +2 -2
- package/dist/modules/business_rules/lib/rule-engine.js +1 -182
- package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
- package/dist/modules/sales/acl.js +0 -1
- package/dist/modules/sales/acl.js.map +2 -2
- package/dist/modules/sales/backend/sales/documents/[id]/page.js +0 -12
- package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +0 -62
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/lib/dictionaries.js +0 -3
- package/dist/modules/sales/lib/dictionaries.js.map +2 -2
- package/dist/modules/workflows/acl.js +0 -2
- package/dist/modules/workflows/acl.js.map +2 -2
- package/dist/modules/workflows/api/instances/route.js +6 -18
- package/dist/modules/workflows/api/instances/route.js.map +2 -2
- package/dist/modules/workflows/api/tasks/route.js +1 -6
- package/dist/modules/workflows/api/tasks/route.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/[id]/page.js +1 -9
- package/dist/modules/workflows/backend/definitions/[id]/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/[id]/page.meta.js +1 -1
- package/dist/modules/workflows/backend/definitions/[id]/page.meta.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/create/page.js +15 -24
- package/dist/modules/workflows/backend/definitions/create/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/create/page.meta.js +1 -1
- package/dist/modules/workflows/backend/definitions/create/page.meta.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js +132 -150
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.meta.js +1 -1
- package/dist/modules/workflows/backend/definitions/visual-editor/page.meta.js.map +2 -2
- package/dist/modules/workflows/backend/events/[id]/page.js +1 -1
- package/dist/modules/workflows/backend/events/[id]/page.js.map +2 -2
- package/dist/modules/workflows/backend/events/[id]/page.meta.js +2 -2
- package/dist/modules/workflows/backend/events/[id]/page.meta.js.map +2 -2
- package/dist/modules/workflows/backend/instances/[id]/page.meta.js +2 -2
- package/dist/modules/workflows/backend/instances/[id]/page.meta.js.map +2 -2
- package/dist/modules/workflows/backend/tasks/[id]/page.js +1 -1
- package/dist/modules/workflows/backend/tasks/[id]/page.js.map +2 -2
- package/dist/modules/workflows/backend/tasks/[id]/page.meta.js +2 -2
- package/dist/modules/workflows/backend/tasks/[id]/page.meta.js.map +2 -2
- package/dist/modules/workflows/backend/tasks/page.js +6 -5
- package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
- package/dist/modules/workflows/cli.js +3 -81
- package/dist/modules/workflows/cli.js.map +3 -3
- package/dist/modules/workflows/data/entities.js +1 -64
- package/dist/modules/workflows/data/entities.js.map +2 -2
- package/dist/modules/workflows/data/validators.js +0 -115
- package/dist/modules/workflows/data/validators.js.map +2 -2
- package/dist/modules/workflows/examples/checkout-demo-definition.json +5 -1
- package/dist/modules/workflows/lib/activity-executor.js +13 -75
- package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
- package/dist/modules/workflows/lib/graph-utils.js +2 -71
- package/dist/modules/workflows/lib/graph-utils.js.map +2 -2
- package/dist/modules/workflows/lib/seeds.js +5 -22
- package/dist/modules/workflows/lib/seeds.js.map +2 -2
- package/dist/modules/workflows/lib/start-validator.js +23 -33
- package/dist/modules/workflows/lib/start-validator.js.map +2 -2
- package/dist/modules/workflows/lib/transition-handler.js +45 -157
- package/dist/modules/workflows/lib/transition-handler.js.map +3 -3
- package/generated/entities.ids.generated.ts +0 -1
- package/generated/entity-fields-registry.ts +0 -2
- package/package.json +2 -2
- package/src/modules/business_rules/data/validators.ts +0 -40
- package/src/modules/business_rules/index.ts +0 -25
- package/src/modules/business_rules/lib/rule-engine.ts +1 -281
- package/src/modules/sales/acl.ts +0 -1
- package/src/modules/sales/backend/sales/documents/[id]/page.tsx +0 -16
- package/src/modules/sales/commands/documents.ts +1 -74
- package/src/modules/sales/lib/dictionaries.ts +0 -3
- package/src/modules/workflows/acl.ts +0 -2
- package/src/modules/workflows/api/instances/route.ts +7 -21
- package/src/modules/workflows/api/tasks/route.ts +1 -7
- package/src/modules/workflows/backend/definitions/[id]/page.meta.ts +1 -1
- package/src/modules/workflows/backend/definitions/[id]/page.tsx +0 -9
- package/src/modules/workflows/backend/definitions/create/page.meta.ts +1 -1
- package/src/modules/workflows/backend/definitions/create/page.tsx +0 -9
- package/src/modules/workflows/backend/definitions/visual-editor/page.meta.ts +1 -1
- package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +3 -21
- package/src/modules/workflows/backend/events/[id]/page.meta.ts +2 -2
- package/src/modules/workflows/backend/events/[id]/page.tsx +1 -1
- package/src/modules/workflows/backend/instances/[id]/page.meta.ts +2 -2
- package/src/modules/workflows/backend/tasks/[id]/page.meta.ts +2 -2
- package/src/modules/workflows/backend/tasks/[id]/page.tsx +1 -1
- package/src/modules/workflows/backend/tasks/page.tsx +6 -5
- package/src/modules/workflows/cli.ts +0 -111
- package/src/modules/workflows/data/entities.ts +0 -124
- package/src/modules/workflows/data/validators.ts +0 -138
- package/src/modules/workflows/examples/checkout-demo-definition.json +5 -1
- package/src/modules/workflows/i18n/en.json +0 -71
- package/src/modules/workflows/lib/activity-executor.ts +16 -129
- package/src/modules/workflows/lib/graph-utils.ts +2 -117
- package/src/modules/workflows/lib/seeds.ts +8 -34
- package/src/modules/workflows/lib/start-validator.ts +28 -38
- package/src/modules/workflows/lib/transition-handler.ts +55 -208
- package/dist/generated/entities/workflow_event_trigger/index.js +0 -33
- package/dist/generated/entities/workflow_event_trigger/index.js.map +0 -7
- package/dist/modules/auth/events.js +0 -30
- package/dist/modules/auth/events.js.map +0 -7
- package/dist/modules/business_rules/api/execute/[ruleId]/route.js +0 -145
- package/dist/modules/business_rules/api/execute/[ruleId]/route.js.map +0 -7
- package/dist/modules/catalog/events.js +0 -34
- package/dist/modules/catalog/events.js.map +0 -7
- package/dist/modules/customers/events.js +0 -49
- package/dist/modules/customers/events.js.map +0 -7
- package/dist/modules/directory/events.js +0 -23
- package/dist/modules/directory/events.js.map +0 -7
- package/dist/modules/sales/events.js +0 -63
- package/dist/modules/sales/events.js.map +0 -7
- package/dist/modules/sales/lib/frontend/documentDataEvents.js +0 -25
- package/dist/modules/sales/lib/frontend/documentDataEvents.js.map +0 -7
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js +0 -481
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +0 -7
- package/dist/modules/workflows/components/EventTriggersEditor.js +0 -553
- package/dist/modules/workflows/components/EventTriggersEditor.js.map +0 -7
- package/dist/modules/workflows/events.js +0 -38
- package/dist/modules/workflows/events.js.map +0 -7
- package/dist/modules/workflows/examples/order-approval-definition.json +0 -257
- package/dist/modules/workflows/examples/order-approval-guard-rules.json +0 -32
- package/dist/modules/workflows/lib/event-trigger-service.js +0 -308
- package/dist/modules/workflows/lib/event-trigger-service.js.map +0 -7
- package/dist/modules/workflows/migrations/Migration20260123143500.js +0 -36
- package/dist/modules/workflows/migrations/Migration20260123143500.js.map +0 -7
- package/dist/modules/workflows/subscribers/event-trigger.js +0 -78
- package/dist/modules/workflows/subscribers/event-trigger.js.map +0 -7
- package/dist/modules/workflows/widgets/injection/order-approval/widget.client.js +0 -323
- package/dist/modules/workflows/widgets/injection/order-approval/widget.client.js.map +0 -7
- package/dist/modules/workflows/widgets/injection/order-approval/widget.js +0 -17
- package/dist/modules/workflows/widgets/injection/order-approval/widget.js.map +0 -7
- package/dist/modules/workflows/widgets/injection-table.js +0 -19
- package/dist/modules/workflows/widgets/injection-table.js.map +0 -7
- package/generated/entities/workflow_event_trigger/index.ts +0 -15
- package/src/modules/auth/events.ts +0 -39
- package/src/modules/business_rules/api/execute/[ruleId]/route.ts +0 -163
- package/src/modules/catalog/events.ts +0 -45
- package/src/modules/customers/events.ts +0 -63
- package/src/modules/directory/events.ts +0 -31
- package/src/modules/sales/events.ts +0 -82
- package/src/modules/sales/lib/frontend/documentDataEvents.ts +0 -28
- package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +0 -581
- package/src/modules/workflows/components/EventTriggersEditor.tsx +0 -664
- package/src/modules/workflows/events.ts +0 -49
- package/src/modules/workflows/examples/order-approval-definition.json +0 -257
- package/src/modules/workflows/examples/order-approval-guard-rules.json +0 -32
- package/src/modules/workflows/lib/event-trigger-service.ts +0 -557
- package/src/modules/workflows/migrations/Migration20260123143500.ts +0 -38
- package/src/modules/workflows/subscribers/event-trigger.ts +0 -109
- package/src/modules/workflows/widgets/injection/order-approval/widget.client.tsx +0 -446
- package/src/modules/workflows/widgets/injection/order-approval/widget.ts +0 -16
- package/src/modules/workflows/widgets/injection-table.ts +0 -21
|
@@ -205,20 +205,12 @@ export function definitionToGraph(
|
|
|
205
205
|
definition: WorkflowDefinition['definition'],
|
|
206
206
|
options: DefinitionToGraphOptions = {}
|
|
207
207
|
): { nodes: Node[]; edges: Edge[] } {
|
|
208
|
-
const { autoLayout = true, layoutSpacing = { vertical:
|
|
209
|
-
|
|
210
|
-
// Build step map for quick lookup
|
|
211
|
-
const stepMap = new Map(definition.steps.map(step => [step.stepId, step]))
|
|
212
|
-
|
|
213
|
-
// Calculate smart layout positions if autoLayout is enabled
|
|
214
|
-
const positions = autoLayout
|
|
215
|
-
? calculateSmartLayout(definition.steps, definition.transitions, layoutSpacing)
|
|
216
|
-
: null
|
|
208
|
+
const { autoLayout = true, layoutSpacing = { vertical: 250, horizontal: 100 } } = options
|
|
217
209
|
|
|
218
210
|
// Convert steps to nodes
|
|
219
211
|
const nodes: Node[] = definition.steps.map((step, index) => {
|
|
220
212
|
// Determine position
|
|
221
|
-
let position =
|
|
213
|
+
let position = { x: 250, y: 50 + index * layoutSpacing.vertical }
|
|
222
214
|
|
|
223
215
|
// Use stored position if available and not auto-layouting
|
|
224
216
|
if (!autoLayout && (step as any)._editorPosition) {
|
|
@@ -334,113 +326,6 @@ export function definitionToGraph(
|
|
|
334
326
|
return { nodes, edges }
|
|
335
327
|
}
|
|
336
328
|
|
|
337
|
-
/**
|
|
338
|
-
* Calculate smart layout positions for workflow nodes
|
|
339
|
-
* Uses a layered/hierarchical layout algorithm that:
|
|
340
|
-
* 1. Assigns levels (ranks) to nodes based on graph topology
|
|
341
|
-
* 2. Spreads sibling nodes horizontally at the same level
|
|
342
|
-
* 3. Centers merge points below their incoming nodes
|
|
343
|
-
*/
|
|
344
|
-
function calculateSmartLayout(
|
|
345
|
-
steps: any[],
|
|
346
|
-
transitions: any[],
|
|
347
|
-
spacing: { vertical: number; horizontal: number }
|
|
348
|
-
): Map<string, { x: number; y: number }> {
|
|
349
|
-
const positions = new Map<string, { x: number; y: number }>()
|
|
350
|
-
|
|
351
|
-
if (steps.length === 0) return positions
|
|
352
|
-
|
|
353
|
-
// Build adjacency lists
|
|
354
|
-
const outgoing = new Map<string, string[]>() // node -> children
|
|
355
|
-
const incoming = new Map<string, string[]>() // node -> parents
|
|
356
|
-
|
|
357
|
-
for (const step of steps) {
|
|
358
|
-
outgoing.set(step.stepId, [])
|
|
359
|
-
incoming.set(step.stepId, [])
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
for (const t of transitions) {
|
|
363
|
-
const children = outgoing.get(t.fromStepId) || []
|
|
364
|
-
children.push(t.toStepId)
|
|
365
|
-
outgoing.set(t.fromStepId, children)
|
|
366
|
-
|
|
367
|
-
const parents = incoming.get(t.toStepId) || []
|
|
368
|
-
parents.push(t.fromStepId)
|
|
369
|
-
incoming.set(t.toStepId, parents)
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// Find start node(s) - nodes with no incoming edges
|
|
373
|
-
const startNodes = steps.filter(s => (incoming.get(s.stepId) || []).length === 0)
|
|
374
|
-
if (startNodes.length === 0) {
|
|
375
|
-
// Fallback: use first step as start
|
|
376
|
-
startNodes.push(steps[0])
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// Assign levels using BFS (longest path from start)
|
|
380
|
-
const levels = new Map<string, number>()
|
|
381
|
-
const queue: Array<{ id: string; level: number }> = []
|
|
382
|
-
|
|
383
|
-
for (const start of startNodes) {
|
|
384
|
-
queue.push({ id: start.stepId, level: 0 })
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
while (queue.length > 0) {
|
|
388
|
-
const { id, level } = queue.shift()!
|
|
389
|
-
const currentLevel = levels.get(id)
|
|
390
|
-
|
|
391
|
-
// Take the maximum level (longest path)
|
|
392
|
-
if (currentLevel === undefined || level > currentLevel) {
|
|
393
|
-
levels.set(id, level)
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
const children = outgoing.get(id) || []
|
|
397
|
-
for (const child of children) {
|
|
398
|
-
queue.push({ id: child, level: level + 1 })
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// Group nodes by level
|
|
403
|
-
const nodesByLevel = new Map<number, string[]>()
|
|
404
|
-
for (const [nodeId, level] of levels) {
|
|
405
|
-
const nodesAtLevel = nodesByLevel.get(level) || []
|
|
406
|
-
nodesAtLevel.push(nodeId)
|
|
407
|
-
nodesByLevel.set(level, nodesAtLevel)
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
// Calculate positions
|
|
411
|
-
const centerX = 400 // Center line for the graph
|
|
412
|
-
const startY = 50
|
|
413
|
-
|
|
414
|
-
for (const [level, nodeIds] of nodesByLevel) {
|
|
415
|
-
const count = nodeIds.length
|
|
416
|
-
const y = startY + level * spacing.vertical
|
|
417
|
-
|
|
418
|
-
if (count === 1) {
|
|
419
|
-
// Single node at this level - center it
|
|
420
|
-
positions.set(nodeIds[0], { x: centerX, y })
|
|
421
|
-
} else {
|
|
422
|
-
// Multiple nodes at this level - spread them horizontally
|
|
423
|
-
const totalWidth = (count - 1) * spacing.horizontal
|
|
424
|
-
const startX = centerX - totalWidth / 2
|
|
425
|
-
|
|
426
|
-
// Sort nodes by their parent's position for consistent ordering
|
|
427
|
-
nodeIds.sort((a, b) => {
|
|
428
|
-
const parentsA = incoming.get(a) || []
|
|
429
|
-
const parentsB = incoming.get(b) || []
|
|
430
|
-
const parentPosA = parentsA.length > 0 ? (positions.get(parentsA[0])?.x || 0) : 0
|
|
431
|
-
const parentPosB = parentsB.length > 0 ? (positions.get(parentsB[0])?.x || 0) : 0
|
|
432
|
-
return parentPosA - parentPosB
|
|
433
|
-
})
|
|
434
|
-
|
|
435
|
-
nodeIds.forEach((nodeId, idx) => {
|
|
436
|
-
positions.set(nodeId, { x: startX + idx * spacing.horizontal, y })
|
|
437
|
-
})
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
return positions
|
|
442
|
-
}
|
|
443
|
-
|
|
444
329
|
/**
|
|
445
330
|
* Map node type to step type (for graph → definition)
|
|
446
331
|
*/
|
|
@@ -7,8 +7,6 @@ import checkoutDemoDefinition from '../examples/checkout-demo-definition.json'
|
|
|
7
7
|
import guardRulesExample from '../examples/guard-rules-example.json'
|
|
8
8
|
import salesPipelineDefinition from '../examples/sales-pipeline-definition.json'
|
|
9
9
|
import simpleApprovalDefinition from '../examples/simple-approval-definition.json'
|
|
10
|
-
import orderApprovalDefinition from '../examples/order-approval-definition.json'
|
|
11
|
-
import orderApprovalGuardRules from '../examples/order-approval-guard-rules.json'
|
|
12
10
|
|
|
13
11
|
export type WorkflowSeedScope = { tenantId: string; organizationId: string }
|
|
14
12
|
|
|
@@ -53,8 +51,6 @@ const embeddedSeeds: Record<string, unknown> = {
|
|
|
53
51
|
'guard-rules-example.json': guardRulesExample,
|
|
54
52
|
'sales-pipeline-definition.json': salesPipelineDefinition,
|
|
55
53
|
'simple-approval-definition.json': simpleApprovalDefinition,
|
|
56
|
-
'order-approval-definition.json': orderApprovalDefinition,
|
|
57
|
-
'order-approval-guard-rules.json': orderApprovalGuardRules,
|
|
58
54
|
}
|
|
59
55
|
|
|
60
56
|
function readExampleJson<T>(fileName: string): T {
|
|
@@ -94,35 +90,16 @@ async function seedWorkflowDefinition(
|
|
|
94
90
|
})
|
|
95
91
|
|
|
96
92
|
if (existing) {
|
|
97
|
-
// Check if the definition needs to be updated
|
|
98
|
-
const seedStepCount = seed.definition.steps.length
|
|
99
|
-
const existingStepCount = existing.definition.steps.length
|
|
100
|
-
const seedTransitionCount = seed.definition.transitions.length
|
|
101
|
-
const existingTransitionCount = existing.definition.transitions.length
|
|
102
|
-
|
|
103
|
-
// Check for preConditions on transitions
|
|
104
|
-
const seedHasTransitionPreConditions = seed.definition.transitions.some(
|
|
105
|
-
(t: any) => t.preConditions && t.preConditions.length > 0
|
|
106
|
-
)
|
|
107
|
-
const existingHasTransitionPreConditions = existing.definition.transitions.some(
|
|
108
|
-
(t: any) => t.preConditions && t.preConditions.length > 0
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
// Check for preConditions on START step
|
|
93
|
+
// Check if the definition needs to be updated (e.g., missing preConditions on START step)
|
|
112
94
|
const seedStartStep = seed.definition.steps.find((s: any) => s.stepType === 'START')
|
|
113
95
|
const existingStartStep = existing.definition.steps.find((s: any) => s.stepType === 'START')
|
|
114
|
-
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
(seedHasStartPreConditions && !existingHasStartPreConditions) ||
|
|
122
|
-
(seedHasTransitionPreConditions && !existingHasTransitionPreConditions)
|
|
123
|
-
|
|
124
|
-
if (needsUpdate) {
|
|
125
|
-
console.log(`[seed] Updating workflow ${workflowId} (steps: ${existingStepCount}→${seedStepCount}, transitions: ${existingTransitionCount}→${seedTransitionCount})`)
|
|
96
|
+
|
|
97
|
+
const seedHasPreConditions = seedStartStep?.preConditions && seedStartStep.preConditions.length > 0
|
|
98
|
+
const existingHasPreConditions = existingStartStep?.preConditions && existingStartStep.preConditions.length > 0
|
|
99
|
+
|
|
100
|
+
// Update if seed has preConditions but existing doesn't
|
|
101
|
+
if (seedHasPreConditions && !existingHasPreConditions) {
|
|
102
|
+
console.log(`[seed] Updating workflow ${workflowId} with preConditions`)
|
|
126
103
|
existing.definition = seed.definition
|
|
127
104
|
await em.flush()
|
|
128
105
|
return true
|
|
@@ -195,7 +172,4 @@ export async function seedExampleWorkflows(em: EntityManager, scope: WorkflowSee
|
|
|
195
172
|
await seedGuardRules(em, scope, 'guard-rules-example.json')
|
|
196
173
|
await seedWorkflowDefinition(em, scope, 'sales-pipeline-definition.json')
|
|
197
174
|
await seedWorkflowDefinition(em, scope, 'simple-approval-definition.json')
|
|
198
|
-
// Seed order approval guard rules before the workflow definition
|
|
199
|
-
await seedGuardRules(em, scope, 'order-approval-guard-rules.json')
|
|
200
|
-
await seedWorkflowDefinition(em, scope, 'order-approval-definition.json')
|
|
201
175
|
}
|
|
@@ -128,69 +128,59 @@ export async function validateWorkflowStart(
|
|
|
128
128
|
const validatedRules: ValidatedRule[] = []
|
|
129
129
|
|
|
130
130
|
for (const condition of preConditions) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
131
|
+
const ruleContext: ruleEngine.RuleEngineContext = {
|
|
132
|
+
entityType: `workflow:${workflowId}:start`,
|
|
133
|
+
entityId: 'pre_start_validation',
|
|
134
|
+
eventType: 'validate_start',
|
|
134
135
|
data: {
|
|
135
136
|
workflowId,
|
|
136
137
|
workflowContext: context,
|
|
137
138
|
},
|
|
138
139
|
tenantId,
|
|
139
140
|
organizationId,
|
|
140
|
-
entityType: `workflow:${workflowId}:start`,
|
|
141
|
-
entityId: 'pre_start_validation',
|
|
142
|
-
eventType: 'validate_start',
|
|
143
141
|
dryRun: true, // Don't log execution during validation
|
|
144
|
-
}
|
|
142
|
+
}
|
|
145
143
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
144
|
+
// Find applicable rules for this context
|
|
145
|
+
const rules = await ruleEngine.findApplicableRules(em, {
|
|
146
|
+
entityType: ruleContext.entityType,
|
|
147
|
+
eventType: ruleContext.eventType,
|
|
148
|
+
tenantId,
|
|
149
|
+
organizationId,
|
|
150
|
+
ruleType: 'GUARD',
|
|
150
151
|
})
|
|
151
152
|
|
|
152
|
-
|
|
153
|
-
|
|
153
|
+
const rule = rules.find(r => r.ruleId === condition.ruleId)
|
|
154
|
+
|
|
155
|
+
if (!rule) {
|
|
156
|
+
// Rule not found - if required, this is an error
|
|
154
157
|
if (condition.required) {
|
|
155
158
|
errors.push({
|
|
156
159
|
ruleId: condition.ruleId,
|
|
157
|
-
message: getLocalizedMessage(condition, null, locale, `Business rule
|
|
160
|
+
message: getLocalizedMessage(condition, null, locale, `Business rule '${condition.ruleId}' not found`),
|
|
158
161
|
code: 'RULE_NOT_FOUND',
|
|
159
162
|
})
|
|
163
|
+
validatedRules.push({ ruleId: condition.ruleId, passed: false })
|
|
160
164
|
}
|
|
161
165
|
continue
|
|
162
166
|
}
|
|
163
167
|
|
|
164
|
-
//
|
|
165
|
-
|
|
166
|
-
if (condition.required) {
|
|
167
|
-
errors.push({
|
|
168
|
-
ruleId: condition.ruleId,
|
|
169
|
-
message: getLocalizedMessage(condition, null, locale, `Business rule is disabled: ${result.ruleName}`),
|
|
170
|
-
code: 'RULE_DISABLED',
|
|
171
|
-
})
|
|
172
|
-
}
|
|
173
|
-
continue
|
|
174
|
-
}
|
|
168
|
+
// Execute the single rule
|
|
169
|
+
const result = await ruleEngine.executeSingleRule(em, rule, ruleContext)
|
|
175
170
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
code: 'RULE_ERROR',
|
|
182
|
-
})
|
|
183
|
-
continue
|
|
184
|
-
}
|
|
171
|
+
validatedRules.push({
|
|
172
|
+
ruleId: condition.ruleId,
|
|
173
|
+
passed: result.conditionResult,
|
|
174
|
+
executionTime: result.executionTime,
|
|
175
|
+
})
|
|
185
176
|
|
|
186
|
-
// Handle condition failure
|
|
187
177
|
if (!result.conditionResult && condition.required) {
|
|
188
|
-
// Get localized message from condition
|
|
178
|
+
// Get localized message from condition, rule failure actions, or default
|
|
189
179
|
const message = getLocalizedMessage(
|
|
190
180
|
condition,
|
|
191
|
-
|
|
181
|
+
rule,
|
|
192
182
|
locale,
|
|
193
|
-
`Pre-condition '${
|
|
183
|
+
`Pre-condition '${rule.ruleName || condition.ruleId}' failed`
|
|
194
184
|
)
|
|
195
185
|
errors.push({
|
|
196
186
|
ruleId: condition.ruleId,
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
} from '../data/entities'
|
|
20
20
|
import * as ruleEvaluator from '../../business_rules/lib/rule-evaluator'
|
|
21
21
|
import * as ruleEngine from '../../business_rules/lib/rule-engine'
|
|
22
|
+
import type { RuleEngineContext } from '../../business_rules/lib/rule-engine'
|
|
22
23
|
import * as activityExecutor from './activity-executor'
|
|
23
24
|
import type { ActivityDefinition } from './activity-executor'
|
|
24
25
|
import * as stepHandler from './step-handler'
|
|
@@ -193,15 +194,11 @@ export async function evaluateTransition(
|
|
|
193
194
|
/**
|
|
194
195
|
* Find all valid transitions from current step
|
|
195
196
|
*
|
|
196
|
-
* This function evaluates both inline conditions AND preConditions (business rules)
|
|
197
|
-
* to determine which transitions are truly valid. This is important for decision
|
|
198
|
-
* branching where multiple transitions exist with different preConditions.
|
|
199
|
-
*
|
|
200
197
|
* @param em - Entity manager
|
|
201
198
|
* @param instance - Workflow instance
|
|
202
199
|
* @param fromStepId - Current step ID
|
|
203
200
|
* @param context - Evaluation context
|
|
204
|
-
* @returns Array of evaluation results for all transitions
|
|
201
|
+
* @returns Array of evaluation results for all transitions
|
|
205
202
|
*/
|
|
206
203
|
export async function findValidTransitions(
|
|
207
204
|
em: EntityManager,
|
|
@@ -219,17 +216,16 @@ export async function findValidTransitions(
|
|
|
219
216
|
return []
|
|
220
217
|
}
|
|
221
218
|
|
|
222
|
-
// Find all transitions from current step
|
|
223
|
-
const transitions = (definition.definition.transitions || [])
|
|
224
|
-
|
|
225
|
-
|
|
219
|
+
// Find all transitions from current step
|
|
220
|
+
const transitions = (definition.definition.transitions || []).filter(
|
|
221
|
+
(t: any) => t.fromStepId === fromStepId
|
|
222
|
+
)
|
|
226
223
|
|
|
227
|
-
// Evaluate each transition
|
|
224
|
+
// Evaluate each transition
|
|
228
225
|
const results: TransitionEvaluationResult[] = []
|
|
229
226
|
|
|
230
227
|
for (const transition of transitions) {
|
|
231
|
-
|
|
232
|
-
const conditionResult = await evaluateTransition(
|
|
228
|
+
const result = await evaluateTransition(
|
|
233
229
|
em,
|
|
234
230
|
instance,
|
|
235
231
|
fromStepId,
|
|
@@ -237,42 +233,7 @@ export async function findValidTransitions(
|
|
|
237
233
|
context
|
|
238
234
|
)
|
|
239
235
|
|
|
240
|
-
|
|
241
|
-
results.push(conditionResult)
|
|
242
|
-
continue
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Also evaluate preConditions if they exist
|
|
246
|
-
const preConditions = transition.preConditions || []
|
|
247
|
-
if (preConditions.length > 0) {
|
|
248
|
-
const preConditionsResult = await evaluatePreConditions(
|
|
249
|
-
em,
|
|
250
|
-
instance,
|
|
251
|
-
transition,
|
|
252
|
-
context as TransitionExecutionContext
|
|
253
|
-
)
|
|
254
|
-
|
|
255
|
-
if (!preConditionsResult.allowed) {
|
|
256
|
-
// Transition is invalid due to preConditions
|
|
257
|
-
const failedRules = preConditionsResult.executedRules
|
|
258
|
-
.filter((r) => !r.conditionResult)
|
|
259
|
-
.map((r) => r.rule.ruleId || r.rule.ruleName)
|
|
260
|
-
|
|
261
|
-
results.push({
|
|
262
|
-
isValid: false,
|
|
263
|
-
transition,
|
|
264
|
-
reason: `Pre-conditions failed: ${failedRules.join(', ')}`,
|
|
265
|
-
failedConditions: failedRules,
|
|
266
|
-
})
|
|
267
|
-
continue
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// Transition is valid (both condition and preConditions passed)
|
|
272
|
-
results.push({
|
|
273
|
-
...conditionResult,
|
|
274
|
-
transition,
|
|
275
|
-
})
|
|
236
|
+
results.push(result)
|
|
276
237
|
}
|
|
277
238
|
|
|
278
239
|
return results
|
|
@@ -688,10 +649,6 @@ async function evaluateTransitionConditions(
|
|
|
688
649
|
* Pre-conditions are GUARD rules that must pass before transition can execute.
|
|
689
650
|
* If any GUARD rule fails, the transition is blocked.
|
|
690
651
|
*
|
|
691
|
-
* If the transition defines specific preConditions with ruleIds, those are
|
|
692
|
-
* executed directly via executeRuleByRuleId. Otherwise, falls back to
|
|
693
|
-
* discovery-based execution via executeRules.
|
|
694
|
-
*
|
|
695
652
|
* @param em - Entity manager
|
|
696
653
|
* @param instance - Workflow instance
|
|
697
654
|
* @param transition - Transition definition
|
|
@@ -718,88 +675,32 @@ async function evaluatePreConditions(
|
|
|
718
675
|
}
|
|
719
676
|
}
|
|
720
677
|
|
|
721
|
-
//
|
|
722
|
-
const
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
const startTime = Date.now()
|
|
735
|
-
const executedRules: ruleEngine.RuleExecutionResult[] = []
|
|
736
|
-
const errors: string[] = []
|
|
737
|
-
let allowed = true
|
|
738
|
-
|
|
739
|
-
for (const condition of preConditions) {
|
|
740
|
-
const result = await ruleEngine.executeRuleByRuleId(em, {
|
|
741
|
-
ruleId: condition.ruleId, // String identifier
|
|
742
|
-
data: {
|
|
743
|
-
workflowInstanceId: instance.id,
|
|
744
|
-
workflowId: definition.workflowId,
|
|
745
|
-
fromStepId: transition.fromStepId,
|
|
746
|
-
toStepId: transition.toStepId,
|
|
747
|
-
workflowContext: {
|
|
748
|
-
...instance.context,
|
|
749
|
-
...context.workflowContext,
|
|
750
|
-
},
|
|
751
|
-
triggerData: context.triggerData,
|
|
678
|
+
// Build rule engine context
|
|
679
|
+
const ruleContext: RuleEngineContext = {
|
|
680
|
+
entityType: `workflow:${definition.workflowId}:transition`,
|
|
681
|
+
entityId: transition.transitionId || `${transition.fromStepId}->${transition.toStepId}`,
|
|
682
|
+
eventType: 'pre_transition',
|
|
683
|
+
data: {
|
|
684
|
+
workflowInstanceId: instance.id,
|
|
685
|
+
workflowId: definition.workflowId,
|
|
686
|
+
fromStepId: transition.fromStepId,
|
|
687
|
+
toStepId: transition.toStepId,
|
|
688
|
+
workflowContext: {
|
|
689
|
+
...instance.context,
|
|
690
|
+
...context.workflowContext,
|
|
752
691
|
},
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
eventType: 'pre_transition',
|
|
760
|
-
})
|
|
761
|
-
|
|
762
|
-
// Create a compatible RuleExecutionResult for tracking
|
|
763
|
-
// We don't have the full BusinessRule entity, but we can create a partial result
|
|
764
|
-
const ruleResult: ruleEngine.RuleExecutionResult = {
|
|
765
|
-
rule: {
|
|
766
|
-
ruleId: result.ruleId,
|
|
767
|
-
ruleName: result.ruleName,
|
|
768
|
-
ruleType: 'GUARD',
|
|
769
|
-
} as any,
|
|
770
|
-
conditionResult: result.conditionResult,
|
|
771
|
-
actionsExecuted: result.actionsExecuted,
|
|
772
|
-
executionTime: result.executionTime,
|
|
773
|
-
error: result.error,
|
|
774
|
-
logId: result.logId,
|
|
775
|
-
}
|
|
776
|
-
executedRules.push(ruleResult)
|
|
777
|
-
|
|
778
|
-
// Handle rule errors
|
|
779
|
-
if (result.error) {
|
|
780
|
-
// Rule not found, disabled, or other errors
|
|
781
|
-
const isRequired = condition.required !== false // Default to required
|
|
782
|
-
if (isRequired) {
|
|
783
|
-
allowed = false
|
|
784
|
-
errors.push(`Rule '${result.ruleId}': ${result.error}`)
|
|
785
|
-
}
|
|
786
|
-
continue
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
// If required and condition failed, block transition
|
|
790
|
-
const isRequired = condition.required !== false // Default to required
|
|
791
|
-
if (isRequired && !result.conditionResult) {
|
|
792
|
-
allowed = false
|
|
793
|
-
errors.push(`Pre-condition '${result.ruleName || result.ruleId}' failed`)
|
|
794
|
-
}
|
|
692
|
+
triggerData: context.triggerData,
|
|
693
|
+
},
|
|
694
|
+
user: context.userId ? { id: context.userId } : undefined,
|
|
695
|
+
tenantId: instance.tenantId,
|
|
696
|
+
organizationId: instance.organizationId,
|
|
697
|
+
executedBy: context.userId,
|
|
795
698
|
}
|
|
796
699
|
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
errors: errors.length > 0 ? errors : undefined,
|
|
802
|
-
}
|
|
700
|
+
// Execute rules - only GUARD rules will affect the 'allowed' status
|
|
701
|
+
const result = await ruleEngine.executeRules(em, ruleContext)
|
|
702
|
+
|
|
703
|
+
return result
|
|
803
704
|
} catch (error) {
|
|
804
705
|
console.error('Error evaluating pre-conditions:', error)
|
|
805
706
|
return {
|
|
@@ -817,9 +718,6 @@ async function evaluatePreConditions(
|
|
|
817
718
|
* Post-conditions are GUARD rules that should pass after transition executes.
|
|
818
719
|
* Unlike pre-conditions, post-condition failures are logged but don't block the transition.
|
|
819
720
|
*
|
|
820
|
-
* If the transition defines specific postConditions with ruleIds, those are
|
|
821
|
-
* executed directly via executeRuleByRuleId. Otherwise, returns allowed: true.
|
|
822
|
-
*
|
|
823
721
|
* @param em - Entity manager
|
|
824
722
|
* @param instance - Workflow instance
|
|
825
723
|
* @param transition - Transition definition
|
|
@@ -846,83 +744,32 @@ async function evaluatePostConditions(
|
|
|
846
744
|
}
|
|
847
745
|
}
|
|
848
746
|
|
|
849
|
-
//
|
|
850
|
-
const
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
const startTime = Date.now()
|
|
863
|
-
const executedRules: ruleEngine.RuleExecutionResult[] = []
|
|
864
|
-
const errors: string[] = []
|
|
865
|
-
let allowed = true
|
|
866
|
-
|
|
867
|
-
for (const condition of postConditions) {
|
|
868
|
-
const result = await ruleEngine.executeRuleByRuleId(em, {
|
|
869
|
-
ruleId: condition.ruleId, // String identifier
|
|
870
|
-
data: {
|
|
871
|
-
workflowInstanceId: instance.id,
|
|
872
|
-
workflowId: definition.workflowId,
|
|
873
|
-
fromStepId: transition.fromStepId,
|
|
874
|
-
toStepId: transition.toStepId,
|
|
875
|
-
workflowContext: {
|
|
876
|
-
...instance.context,
|
|
877
|
-
...context.workflowContext,
|
|
878
|
-
},
|
|
879
|
-
triggerData: context.triggerData,
|
|
747
|
+
// Build rule engine context
|
|
748
|
+
const ruleContext: RuleEngineContext = {
|
|
749
|
+
entityType: `workflow:${definition.workflowId}:transition`,
|
|
750
|
+
entityId: transition.transitionId || `${transition.fromStepId}->${transition.toStepId}`,
|
|
751
|
+
eventType: 'post_transition',
|
|
752
|
+
data: {
|
|
753
|
+
workflowInstanceId: instance.id,
|
|
754
|
+
workflowId: definition.workflowId,
|
|
755
|
+
fromStepId: transition.fromStepId,
|
|
756
|
+
toStepId: transition.toStepId,
|
|
757
|
+
workflowContext: {
|
|
758
|
+
...instance.context,
|
|
759
|
+
...context.workflowContext,
|
|
880
760
|
},
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
eventType: 'post_transition',
|
|
888
|
-
})
|
|
889
|
-
|
|
890
|
-
// Create a compatible RuleExecutionResult for tracking
|
|
891
|
-
const ruleResult: ruleEngine.RuleExecutionResult = {
|
|
892
|
-
rule: {
|
|
893
|
-
ruleId: result.ruleId,
|
|
894
|
-
ruleName: result.ruleName,
|
|
895
|
-
ruleType: 'GUARD',
|
|
896
|
-
} as any,
|
|
897
|
-
conditionResult: result.conditionResult,
|
|
898
|
-
actionsExecuted: result.actionsExecuted,
|
|
899
|
-
executionTime: result.executionTime,
|
|
900
|
-
error: result.error,
|
|
901
|
-
logId: result.logId,
|
|
902
|
-
}
|
|
903
|
-
executedRules.push(ruleResult)
|
|
904
|
-
|
|
905
|
-
// Handle rule errors
|
|
906
|
-
if (result.error) {
|
|
907
|
-
errors.push(`Rule '${result.ruleId}': ${result.error}`)
|
|
908
|
-
// Post-conditions don't block, but track the failure
|
|
909
|
-
allowed = false
|
|
910
|
-
continue
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
// Track condition failures (post-conditions are warnings, not blockers)
|
|
914
|
-
if (!result.conditionResult) {
|
|
915
|
-
allowed = false
|
|
916
|
-
errors.push(`Post-condition '${result.ruleName || result.ruleId}' failed`)
|
|
917
|
-
}
|
|
761
|
+
triggerData: context.triggerData,
|
|
762
|
+
},
|
|
763
|
+
user: context.userId ? { id: context.userId } : undefined,
|
|
764
|
+
tenantId: instance.tenantId,
|
|
765
|
+
organizationId: instance.organizationId,
|
|
766
|
+
executedBy: context.userId,
|
|
918
767
|
}
|
|
919
768
|
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
errors: errors.length > 0 ? errors : undefined,
|
|
925
|
-
}
|
|
769
|
+
// Execute rules
|
|
770
|
+
const result = await ruleEngine.executeRules(em, ruleContext)
|
|
771
|
+
|
|
772
|
+
return result
|
|
926
773
|
} catch (error) {
|
|
927
774
|
console.error('Error evaluating post-conditions:', error)
|
|
928
775
|
return {
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
const id = "id";
|
|
2
|
-
const name = "name";
|
|
3
|
-
const description = "description";
|
|
4
|
-
const workflow_definition_id = "workflow_definition_id";
|
|
5
|
-
const event_pattern = "event_pattern";
|
|
6
|
-
const config = "config";
|
|
7
|
-
const enabled = "enabled";
|
|
8
|
-
const priority = "priority";
|
|
9
|
-
const tenant_id = "tenant_id";
|
|
10
|
-
const organization_id = "organization_id";
|
|
11
|
-
const created_by = "created_by";
|
|
12
|
-
const updated_by = "updated_by";
|
|
13
|
-
const created_at = "created_at";
|
|
14
|
-
const updated_at = "updated_at";
|
|
15
|
-
const deleted_at = "deleted_at";
|
|
16
|
-
export {
|
|
17
|
-
config,
|
|
18
|
-
created_at,
|
|
19
|
-
created_by,
|
|
20
|
-
deleted_at,
|
|
21
|
-
description,
|
|
22
|
-
enabled,
|
|
23
|
-
event_pattern,
|
|
24
|
-
id,
|
|
25
|
-
name,
|
|
26
|
-
organization_id,
|
|
27
|
-
priority,
|
|
28
|
-
tenant_id,
|
|
29
|
-
updated_at,
|
|
30
|
-
updated_by,
|
|
31
|
-
workflow_definition_id
|
|
32
|
-
};
|
|
33
|
-
//# sourceMappingURL=index.js.map
|