@open-mercato/core 0.4.2-canary-51881f6bf3 → 0.4.2-canary-5f415b8a44
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/workflow_event_trigger/index.js +33 -0
- package/dist/generated/entities/workflow_event_trigger/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +59 -58
- package/dist/generated/entities.ids.generated.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/auth/events.js +30 -0
- package/dist/modules/auth/events.js.map +7 -0
- package/dist/modules/business_rules/api/execute/[ruleId]/route.js +145 -0
- package/dist/modules/business_rules/api/execute/[ruleId]/route.js.map +7 -0
- package/dist/modules/business_rules/data/validators.js +34 -0
- package/dist/modules/business_rules/data/validators.js.map +2 -2
- package/dist/modules/business_rules/index.js +21 -1
- package/dist/modules/business_rules/index.js.map +2 -2
- package/dist/modules/business_rules/lib/rule-engine.js +182 -1
- package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
- package/dist/modules/catalog/events.js +34 -0
- package/dist/modules/catalog/events.js.map +7 -0
- package/dist/modules/customers/events.js +49 -0
- package/dist/modules/customers/events.js.map +7 -0
- package/dist/modules/directory/events.js +23 -0
- package/dist/modules/directory/events.js.map +7 -0
- package/dist/modules/sales/acl.js +1 -0
- package/dist/modules/sales/acl.js.map +2 -2
- package/dist/modules/sales/backend/sales/documents/[id]/page.js +12 -0
- package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +62 -0
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/events.js +63 -0
- package/dist/modules/sales/events.js.map +7 -0
- package/dist/modules/sales/lib/dictionaries.js +3 -0
- package/dist/modules/sales/lib/dictionaries.js.map +2 -2
- package/dist/modules/sales/lib/frontend/documentDataEvents.js +25 -0
- package/dist/modules/sales/lib/frontend/documentDataEvents.js.map +7 -0
- package/dist/modules/workflows/acl.js +2 -0
- package/dist/modules/workflows/acl.js.map +2 -2
- package/dist/modules/workflows/api/instances/route.js +18 -6
- package/dist/modules/workflows/api/instances/route.js.map +2 -2
- package/dist/modules/workflows/api/tasks/route.js +6 -1
- package/dist/modules/workflows/api/tasks/route.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/[id]/page.js +9 -1
- 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 +24 -15
- 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 +150 -132
- 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 +5 -6
- package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
- package/dist/modules/workflows/cli.js +81 -3
- package/dist/modules/workflows/cli.js.map +3 -3
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js +481 -0
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +7 -0
- package/dist/modules/workflows/components/EventTriggersEditor.js +553 -0
- package/dist/modules/workflows/components/EventTriggersEditor.js.map +7 -0
- package/dist/modules/workflows/data/entities.js +64 -1
- package/dist/modules/workflows/data/entities.js.map +2 -2
- package/dist/modules/workflows/data/validators.js +115 -0
- package/dist/modules/workflows/data/validators.js.map +2 -2
- package/dist/modules/workflows/events.js +38 -0
- package/dist/modules/workflows/events.js.map +7 -0
- package/dist/modules/workflows/examples/checkout-demo-definition.json +1 -5
- package/dist/modules/workflows/examples/order-approval-definition.json +257 -0
- package/dist/modules/workflows/examples/order-approval-guard-rules.json +32 -0
- package/dist/modules/workflows/lib/activity-executor.js +75 -13
- package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
- package/dist/modules/workflows/lib/event-trigger-service.js +308 -0
- package/dist/modules/workflows/lib/event-trigger-service.js.map +7 -0
- package/dist/modules/workflows/lib/graph-utils.js +71 -2
- package/dist/modules/workflows/lib/graph-utils.js.map +2 -2
- package/dist/modules/workflows/lib/seeds.js +22 -5
- package/dist/modules/workflows/lib/seeds.js.map +2 -2
- package/dist/modules/workflows/lib/start-validator.js +33 -23
- package/dist/modules/workflows/lib/start-validator.js.map +2 -2
- package/dist/modules/workflows/lib/transition-handler.js +157 -45
- package/dist/modules/workflows/lib/transition-handler.js.map +3 -3
- package/dist/modules/workflows/migrations/Migration20260123143500.js +36 -0
- package/dist/modules/workflows/migrations/Migration20260123143500.js.map +7 -0
- package/dist/modules/workflows/subscribers/event-trigger.js +78 -0
- package/dist/modules/workflows/subscribers/event-trigger.js.map +7 -0
- package/dist/modules/workflows/widgets/injection/order-approval/widget.client.js +323 -0
- package/dist/modules/workflows/widgets/injection/order-approval/widget.client.js.map +7 -0
- package/dist/modules/workflows/widgets/injection/order-approval/widget.js +17 -0
- package/dist/modules/workflows/widgets/injection/order-approval/widget.js.map +7 -0
- package/dist/modules/workflows/widgets/injection-table.js +19 -0
- package/dist/modules/workflows/widgets/injection-table.js.map +7 -0
- package/generated/entities/workflow_event_trigger/index.ts +15 -0
- package/generated/entities.ids.generated.ts +59 -58
- package/generated/entity-fields-registry.ts +2 -0
- package/package.json +3 -5
- package/src/modules/auth/events.ts +39 -0
- package/src/modules/business_rules/api/execute/[ruleId]/route.ts +163 -0
- package/src/modules/business_rules/data/validators.ts +40 -0
- package/src/modules/business_rules/index.ts +25 -0
- package/src/modules/business_rules/lib/rule-engine.ts +281 -1
- package/src/modules/catalog/events.ts +45 -0
- package/src/modules/customers/events.ts +63 -0
- package/src/modules/directory/events.ts +31 -0
- package/src/modules/sales/acl.ts +1 -0
- package/src/modules/sales/backend/sales/documents/[id]/page.tsx +16 -0
- package/src/modules/sales/commands/documents.ts +74 -1
- package/src/modules/sales/events.ts +82 -0
- package/src/modules/sales/lib/dictionaries.ts +3 -0
- package/src/modules/sales/lib/frontend/documentDataEvents.ts +28 -0
- package/src/modules/workflows/acl.ts +2 -0
- package/src/modules/workflows/api/instances/route.ts +21 -7
- package/src/modules/workflows/api/tasks/route.ts +7 -1
- package/src/modules/workflows/backend/definitions/[id]/page.meta.ts +1 -1
- package/src/modules/workflows/backend/definitions/[id]/page.tsx +9 -0
- package/src/modules/workflows/backend/definitions/create/page.meta.ts +1 -1
- package/src/modules/workflows/backend/definitions/create/page.tsx +9 -0
- package/src/modules/workflows/backend/definitions/visual-editor/page.meta.ts +1 -1
- package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +21 -3
- 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 +5 -6
- package/src/modules/workflows/cli.ts +111 -0
- package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +581 -0
- package/src/modules/workflows/components/EventTriggersEditor.tsx +664 -0
- package/src/modules/workflows/data/entities.ts +124 -0
- package/src/modules/workflows/data/validators.ts +138 -0
- package/src/modules/workflows/events.ts +49 -0
- package/src/modules/workflows/examples/checkout-demo-definition.json +1 -5
- package/src/modules/workflows/examples/order-approval-definition.json +257 -0
- package/src/modules/workflows/examples/order-approval-guard-rules.json +32 -0
- package/src/modules/workflows/i18n/en.json +71 -0
- package/src/modules/workflows/lib/activity-executor.ts +129 -16
- package/src/modules/workflows/lib/event-trigger-service.ts +557 -0
- package/src/modules/workflows/lib/graph-utils.ts +117 -2
- package/src/modules/workflows/lib/seeds.ts +34 -8
- package/src/modules/workflows/lib/start-validator.ts +38 -28
- package/src/modules/workflows/lib/transition-handler.ts +208 -55
- package/src/modules/workflows/migrations/Migration20260123143500.ts +38 -0
- package/src/modules/workflows/subscribers/event-trigger.ts +109 -0
- package/src/modules/workflows/widgets/injection/order-approval/widget.client.tsx +446 -0
- package/src/modules/workflows/widgets/injection/order-approval/widget.ts +16 -0
- package/src/modules/workflows/widgets/injection-table.ts +21 -0
|
@@ -46,6 +46,63 @@ export type UserTaskStatus =
|
|
|
46
46
|
| 'CANCELLED'
|
|
47
47
|
| 'ESCALATED'
|
|
48
48
|
|
|
49
|
+
// ============================================================================
|
|
50
|
+
// Event Trigger Types
|
|
51
|
+
// ============================================================================
|
|
52
|
+
|
|
53
|
+
export type TriggerFilterOperator =
|
|
54
|
+
| 'eq'
|
|
55
|
+
| 'neq'
|
|
56
|
+
| 'gt'
|
|
57
|
+
| 'gte'
|
|
58
|
+
| 'lt'
|
|
59
|
+
| 'lte'
|
|
60
|
+
| 'contains'
|
|
61
|
+
| 'startsWith'
|
|
62
|
+
| 'endsWith'
|
|
63
|
+
| 'in'
|
|
64
|
+
| 'notIn'
|
|
65
|
+
| 'exists'
|
|
66
|
+
| 'notExists'
|
|
67
|
+
| 'regex'
|
|
68
|
+
|
|
69
|
+
export interface TriggerFilterCondition {
|
|
70
|
+
field: string // JSON path (e.g., "status", "metadata.type")
|
|
71
|
+
operator: TriggerFilterOperator
|
|
72
|
+
value: unknown
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface TriggerContextMapping {
|
|
76
|
+
targetKey: string // Key in workflow initial context
|
|
77
|
+
sourceExpression: string // Path from event payload (supports dot notation)
|
|
78
|
+
defaultValue?: unknown
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface WorkflowEventTriggerConfig {
|
|
82
|
+
filterConditions?: TriggerFilterCondition[]
|
|
83
|
+
contextMapping?: TriggerContextMapping[]
|
|
84
|
+
debounceMs?: number // Debounce rapid events
|
|
85
|
+
maxConcurrentInstances?: number // Limit concurrent instances
|
|
86
|
+
entityType?: string // Entity type for workflow instance metadata (e.g., "SalesOrder")
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* WorkflowDefinitionTrigger - Embedded trigger configuration
|
|
91
|
+
*
|
|
92
|
+
* Triggers are now embedded directly in the workflow definition,
|
|
93
|
+
* allowing users to configure event-based workflow starts during
|
|
94
|
+
* workflow creation in the visual editor.
|
|
95
|
+
*/
|
|
96
|
+
export interface WorkflowDefinitionTrigger {
|
|
97
|
+
triggerId: string
|
|
98
|
+
name: string
|
|
99
|
+
description?: string | null
|
|
100
|
+
eventPattern: string // e.g., "sales.orders.created", "customers.*"
|
|
101
|
+
config?: WorkflowEventTriggerConfig | null
|
|
102
|
+
enabled: boolean
|
|
103
|
+
priority: number
|
|
104
|
+
}
|
|
105
|
+
|
|
49
106
|
// ============================================================================
|
|
50
107
|
// JSONB Structure Interfaces
|
|
51
108
|
// ============================================================================
|
|
@@ -53,6 +110,7 @@ export type UserTaskStatus =
|
|
|
53
110
|
export interface WorkflowDefinitionData {
|
|
54
111
|
steps: any[] // WorkflowStep[] - will define schema in validators.ts
|
|
55
112
|
transitions: any[] // WorkflowTransition[] - will define schema in validators.ts
|
|
113
|
+
triggers?: WorkflowDefinitionTrigger[] // Event triggers for automatic workflow start
|
|
56
114
|
activities?: any[] // ActivityDefinition[] - will define schema in validators.ts
|
|
57
115
|
queries?: any[]
|
|
58
116
|
signals?: any[]
|
|
@@ -429,6 +487,71 @@ export class WorkflowEvent {
|
|
|
429
487
|
organizationId!: string
|
|
430
488
|
}
|
|
431
489
|
|
|
490
|
+
// ============================================================================
|
|
491
|
+
// Entity: WorkflowEventTrigger
|
|
492
|
+
// ============================================================================
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* WorkflowEventTrigger entity
|
|
496
|
+
*
|
|
497
|
+
* Maps event patterns to workflow definitions for automatic triggering.
|
|
498
|
+
* When a matching event is emitted, the corresponding workflow is started
|
|
499
|
+
* with context mapped from the event payload.
|
|
500
|
+
*/
|
|
501
|
+
@Entity({ tableName: 'workflow_event_triggers' })
|
|
502
|
+
@Index({ name: 'workflow_event_triggers_event_pattern_idx', properties: ['eventPattern', 'enabled'] })
|
|
503
|
+
@Index({ name: 'workflow_event_triggers_definition_idx', properties: ['workflowDefinitionId'] })
|
|
504
|
+
@Index({ name: 'workflow_event_triggers_tenant_org_idx', properties: ['tenantId', 'organizationId'] })
|
|
505
|
+
@Index({ name: 'workflow_event_triggers_enabled_priority_idx', properties: ['enabled', 'priority'] })
|
|
506
|
+
export class WorkflowEventTrigger {
|
|
507
|
+
[OptionalProps]?: 'enabled' | 'priority' | 'createdAt' | 'updatedAt' | 'deletedAt'
|
|
508
|
+
|
|
509
|
+
@PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })
|
|
510
|
+
id!: string
|
|
511
|
+
|
|
512
|
+
@Property({ name: 'name', type: 'varchar', length: 255 })
|
|
513
|
+
name!: string
|
|
514
|
+
|
|
515
|
+
@Property({ name: 'description', type: 'text', nullable: true })
|
|
516
|
+
description?: string | null
|
|
517
|
+
|
|
518
|
+
@Property({ name: 'workflow_definition_id', type: 'uuid' })
|
|
519
|
+
workflowDefinitionId!: string
|
|
520
|
+
|
|
521
|
+
@Property({ name: 'event_pattern', type: 'varchar', length: 255 })
|
|
522
|
+
eventPattern!: string
|
|
523
|
+
|
|
524
|
+
@Property({ name: 'config', type: 'jsonb', nullable: true })
|
|
525
|
+
config?: WorkflowEventTriggerConfig | null
|
|
526
|
+
|
|
527
|
+
@Property({ name: 'enabled', type: 'boolean', default: true })
|
|
528
|
+
enabled: boolean = true
|
|
529
|
+
|
|
530
|
+
@Property({ name: 'priority', type: 'integer', default: 0 })
|
|
531
|
+
priority: number = 0
|
|
532
|
+
|
|
533
|
+
@Property({ name: 'tenant_id', type: 'uuid' })
|
|
534
|
+
tenantId!: string
|
|
535
|
+
|
|
536
|
+
@Property({ name: 'organization_id', type: 'uuid' })
|
|
537
|
+
organizationId!: string
|
|
538
|
+
|
|
539
|
+
@Property({ name: 'created_by', type: 'varchar', length: 255, nullable: true })
|
|
540
|
+
createdBy?: string | null
|
|
541
|
+
|
|
542
|
+
@Property({ name: 'updated_by', type: 'varchar', length: 255, nullable: true })
|
|
543
|
+
updatedBy?: string | null
|
|
544
|
+
|
|
545
|
+
@Property({ name: 'created_at', type: Date, onCreate: () => new Date() })
|
|
546
|
+
createdAt: Date = new Date()
|
|
547
|
+
|
|
548
|
+
@Property({ name: 'updated_at', type: Date, onUpdate: () => new Date() })
|
|
549
|
+
updatedAt: Date = new Date()
|
|
550
|
+
|
|
551
|
+
@Property({ name: 'deleted_at', type: Date, nullable: true })
|
|
552
|
+
deletedAt?: Date | null
|
|
553
|
+
}
|
|
554
|
+
|
|
432
555
|
// Export all entities as default for MikroORM discovery
|
|
433
556
|
export default [
|
|
434
557
|
WorkflowDefinition,
|
|
@@ -436,4 +559,5 @@ export default [
|
|
|
436
559
|
StepInstance,
|
|
437
560
|
UserTask,
|
|
438
561
|
WorkflowEvent,
|
|
562
|
+
WorkflowEventTrigger,
|
|
439
563
|
]
|
|
@@ -215,10 +215,43 @@ export const workflowTransitionSchema = z.object({
|
|
|
215
215
|
priority: z.number().int().min(0).max(9999).default(0),
|
|
216
216
|
})
|
|
217
217
|
|
|
218
|
+
// Workflow definition trigger schema (embedded in definition)
|
|
219
|
+
// Note: Uses forward reference pattern since eventPatternSchema and eventTriggerConfigSchema are defined later
|
|
220
|
+
export const workflowDefinitionTriggerSchema = z.object({
|
|
221
|
+
triggerId: z.string().min(1).max(100).regex(/^[a-z0-9_-]+$/, 'Trigger ID must contain only lowercase letters, numbers, hyphens, and underscores'),
|
|
222
|
+
name: z.string().min(1).max(255),
|
|
223
|
+
description: z.string().max(2000).optional().nullable(),
|
|
224
|
+
eventPattern: z.string()
|
|
225
|
+
.min(1, 'Event pattern is required')
|
|
226
|
+
.max(255, 'Event pattern must be at most 255 characters')
|
|
227
|
+
.regex(
|
|
228
|
+
/^(\*|[a-z0-9_]+(\.[a-z0-9_*]+)*)$/i,
|
|
229
|
+
'Event pattern must be "*" or a dot-separated pattern with optional wildcards (e.g., "customers.people.created", "sales.orders.*")'
|
|
230
|
+
),
|
|
231
|
+
config: z.object({
|
|
232
|
+
filterConditions: z.array(z.object({
|
|
233
|
+
field: z.string().min(1).max(255),
|
|
234
|
+
operator: z.enum(['eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'contains', 'startsWith', 'endsWith', 'in', 'notIn', 'exists', 'notExists', 'regex']),
|
|
235
|
+
value: z.any(),
|
|
236
|
+
})).max(20).optional(),
|
|
237
|
+
contextMapping: z.array(z.object({
|
|
238
|
+
targetKey: z.string().min(1).max(100),
|
|
239
|
+
sourceExpression: z.string().min(1).max(255),
|
|
240
|
+
defaultValue: z.any().optional(),
|
|
241
|
+
})).max(50).optional(),
|
|
242
|
+
debounceMs: z.number().int().min(0).max(3600000).optional(),
|
|
243
|
+
maxConcurrentInstances: z.number().int().min(1).max(1000).optional(),
|
|
244
|
+
}).optional().nullable(),
|
|
245
|
+
enabled: z.boolean().default(true),
|
|
246
|
+
priority: z.number().int().min(0).max(9999).default(0),
|
|
247
|
+
})
|
|
248
|
+
export type WorkflowDefinitionTrigger = z.infer<typeof workflowDefinitionTriggerSchema>
|
|
249
|
+
|
|
218
250
|
// Workflow definition data (JSONB structure)
|
|
219
251
|
export const workflowDefinitionDataSchema = z.object({
|
|
220
252
|
steps: z.array(workflowStepSchema).min(2, 'Workflow must have at least START and END steps'),
|
|
221
253
|
transitions: z.array(workflowTransitionSchema).min(1, 'Workflow must have at least one transition'),
|
|
254
|
+
triggers: z.array(workflowDefinitionTriggerSchema).optional(), // Event triggers for automatic workflow start
|
|
222
255
|
queries: z.array(z.any()).optional(), // For Phase 7
|
|
223
256
|
signals: z.array(z.any()).optional(), // For Phase 9
|
|
224
257
|
timers: z.array(z.any()).optional(), // For Phase 9
|
|
@@ -488,3 +521,108 @@ export const startWorkflowInputSchema = z.object({
|
|
|
488
521
|
})
|
|
489
522
|
|
|
490
523
|
export type StartWorkflowApiInput = z.infer<typeof startWorkflowInputSchema>
|
|
524
|
+
|
|
525
|
+
// ============================================================================
|
|
526
|
+
// WorkflowEventTrigger Schemas
|
|
527
|
+
// ============================================================================
|
|
528
|
+
|
|
529
|
+
export const triggerFilterOperatorSchema = z.enum([
|
|
530
|
+
'eq',
|
|
531
|
+
'neq',
|
|
532
|
+
'gt',
|
|
533
|
+
'gte',
|
|
534
|
+
'lt',
|
|
535
|
+
'lte',
|
|
536
|
+
'contains',
|
|
537
|
+
'startsWith',
|
|
538
|
+
'endsWith',
|
|
539
|
+
'in',
|
|
540
|
+
'notIn',
|
|
541
|
+
'exists',
|
|
542
|
+
'notExists',
|
|
543
|
+
'regex',
|
|
544
|
+
])
|
|
545
|
+
export type TriggerFilterOperator = z.infer<typeof triggerFilterOperatorSchema>
|
|
546
|
+
|
|
547
|
+
export const triggerFilterConditionSchema = z.object({
|
|
548
|
+
field: z.string().min(1).max(255, 'Field path must be at most 255 characters'),
|
|
549
|
+
operator: triggerFilterOperatorSchema,
|
|
550
|
+
value: z.any(),
|
|
551
|
+
})
|
|
552
|
+
export type TriggerFilterCondition = z.infer<typeof triggerFilterConditionSchema>
|
|
553
|
+
|
|
554
|
+
export const triggerContextMappingSchema = z.object({
|
|
555
|
+
targetKey: z.string().min(1).max(100, 'Target key must be at most 100 characters'),
|
|
556
|
+
sourceExpression: z.string().min(1).max(255, 'Source expression must be at most 255 characters'),
|
|
557
|
+
defaultValue: z.any().optional(),
|
|
558
|
+
})
|
|
559
|
+
export type TriggerContextMapping = z.infer<typeof triggerContextMappingSchema>
|
|
560
|
+
|
|
561
|
+
export const eventTriggerConfigSchema = z.object({
|
|
562
|
+
filterConditions: z.array(triggerFilterConditionSchema).max(20, 'Maximum 20 filter conditions allowed').optional(),
|
|
563
|
+
contextMapping: z.array(triggerContextMappingSchema).max(50, 'Maximum 50 context mappings allowed').optional(),
|
|
564
|
+
debounceMs: z.number().int().min(0).max(3600000, 'Debounce cannot exceed 1 hour').optional(),
|
|
565
|
+
maxConcurrentInstances: z.number().int().min(1).max(1000, 'Max concurrent instances must be between 1 and 1000').optional(),
|
|
566
|
+
})
|
|
567
|
+
export type EventTriggerConfig = z.infer<typeof eventTriggerConfigSchema>
|
|
568
|
+
|
|
569
|
+
export const eventPatternSchema = z.string()
|
|
570
|
+
.min(1, 'Event pattern is required')
|
|
571
|
+
.max(255, 'Event pattern must be at most 255 characters')
|
|
572
|
+
.regex(
|
|
573
|
+
/^(\*|[a-z0-9_]+(\.[a-z0-9_*]+)*)$/i,
|
|
574
|
+
'Event pattern must be "*" or a dot-separated pattern with optional wildcards (e.g., "customers.people.created", "sales.orders.*")'
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
export const createEventTriggerSchema = z.object({
|
|
578
|
+
name: z.string().min(1).max(255),
|
|
579
|
+
description: z.string().max(2000).optional().nullable(),
|
|
580
|
+
workflowDefinitionId: uuid,
|
|
581
|
+
eventPattern: eventPatternSchema,
|
|
582
|
+
config: eventTriggerConfigSchema.optional().nullable(),
|
|
583
|
+
enabled: z.boolean().default(true),
|
|
584
|
+
priority: z.number().int().min(0).max(9999).default(0),
|
|
585
|
+
tenantId: uuid,
|
|
586
|
+
organizationId: uuid,
|
|
587
|
+
createdBy: z.string().max(255).optional().nullable(),
|
|
588
|
+
})
|
|
589
|
+
export type CreateEventTriggerInput = z.infer<typeof createEventTriggerSchema>
|
|
590
|
+
|
|
591
|
+
// API input schema (omits tenant fields - injected from auth context)
|
|
592
|
+
export const createEventTriggerInputSchema = z.object({
|
|
593
|
+
name: z.string().min(1).max(255),
|
|
594
|
+
description: z.string().max(2000).optional().nullable(),
|
|
595
|
+
workflowDefinitionId: uuid,
|
|
596
|
+
eventPattern: eventPatternSchema,
|
|
597
|
+
config: eventTriggerConfigSchema.optional().nullable(),
|
|
598
|
+
enabled: z.boolean().default(true).optional(),
|
|
599
|
+
priority: z.number().int().min(0).max(9999).default(0).optional(),
|
|
600
|
+
})
|
|
601
|
+
export type CreateEventTriggerApiInput = z.infer<typeof createEventTriggerInputSchema>
|
|
602
|
+
|
|
603
|
+
export const updateEventTriggerSchema = createEventTriggerSchema.partial().extend({
|
|
604
|
+
id: uuid,
|
|
605
|
+
})
|
|
606
|
+
export type UpdateEventTriggerInput = z.infer<typeof updateEventTriggerSchema>
|
|
607
|
+
|
|
608
|
+
// API update schema (omits tenant fields and allows partial updates)
|
|
609
|
+
export const updateEventTriggerInputSchema = z.object({
|
|
610
|
+
name: z.string().min(1).max(255).optional(),
|
|
611
|
+
description: z.string().max(2000).optional().nullable(),
|
|
612
|
+
workflowDefinitionId: uuid.optional(),
|
|
613
|
+
eventPattern: eventPatternSchema.optional(),
|
|
614
|
+
config: eventTriggerConfigSchema.optional().nullable(),
|
|
615
|
+
enabled: z.boolean().optional(),
|
|
616
|
+
priority: z.number().int().min(0).max(9999).optional(),
|
|
617
|
+
}).strict()
|
|
618
|
+
export type UpdateEventTriggerApiInput = z.infer<typeof updateEventTriggerInputSchema>
|
|
619
|
+
|
|
620
|
+
export const eventTriggerFilterSchema = z.object({
|
|
621
|
+
name: z.string().optional(),
|
|
622
|
+
workflowDefinitionId: uuid.optional(),
|
|
623
|
+
eventPattern: z.string().optional(),
|
|
624
|
+
enabled: z.boolean().optional(),
|
|
625
|
+
tenantId: uuid.optional(),
|
|
626
|
+
organizationId: uuid.optional(),
|
|
627
|
+
})
|
|
628
|
+
export type EventTriggerFilter = z.infer<typeof eventTriggerFilterSchema>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { createModuleEvents } from '@open-mercato/shared/modules/events'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Workflows Module Events
|
|
5
|
+
*
|
|
6
|
+
* Declares all events that can be emitted by the workflows module.
|
|
7
|
+
*/
|
|
8
|
+
const events = [
|
|
9
|
+
// Workflow Definitions
|
|
10
|
+
{ id: 'workflows.definitions.created', label: 'Workflow Definition Created', entity: 'definitions', category: 'crud' },
|
|
11
|
+
{ id: 'workflows.definitions.updated', label: 'Workflow Definition Updated', entity: 'definitions', category: 'crud' },
|
|
12
|
+
{ id: 'workflows.definitions.deleted', label: 'Workflow Definition Deleted', entity: 'definitions', category: 'crud' },
|
|
13
|
+
|
|
14
|
+
// Workflow Instances
|
|
15
|
+
{ id: 'workflows.instances.created', label: 'Workflow Instance Created', entity: 'instances', category: 'crud' },
|
|
16
|
+
{ id: 'workflows.instances.updated', label: 'Workflow Instance Updated', entity: 'instances', category: 'crud' },
|
|
17
|
+
{ id: 'workflows.instances.deleted', label: 'Workflow Instance Deleted', entity: 'instances', category: 'crud' },
|
|
18
|
+
|
|
19
|
+
// Workflow Lifecycle Events
|
|
20
|
+
{ id: 'workflows.instance.started', label: 'Workflow Started', category: 'lifecycle' },
|
|
21
|
+
{ id: 'workflows.instance.completed', label: 'Workflow Completed', category: 'lifecycle' },
|
|
22
|
+
{ id: 'workflows.instance.failed', label: 'Workflow Failed', category: 'lifecycle' },
|
|
23
|
+
{ id: 'workflows.instance.cancelled', label: 'Workflow Cancelled', category: 'lifecycle' },
|
|
24
|
+
{ id: 'workflows.instance.paused', label: 'Workflow Paused', category: 'lifecycle' },
|
|
25
|
+
{ id: 'workflows.instance.resumed', label: 'Workflow Resumed', category: 'lifecycle' },
|
|
26
|
+
|
|
27
|
+
// Activity Events
|
|
28
|
+
{ id: 'workflows.activity.started', label: 'Activity Started', category: 'lifecycle' },
|
|
29
|
+
{ id: 'workflows.activity.completed', label: 'Activity Completed', category: 'lifecycle' },
|
|
30
|
+
{ id: 'workflows.activity.failed', label: 'Activity Failed', category: 'lifecycle' },
|
|
31
|
+
|
|
32
|
+
// Event Triggers
|
|
33
|
+
{ id: 'workflows.triggers.created', label: 'Trigger Created', entity: 'triggers', category: 'crud' },
|
|
34
|
+
{ id: 'workflows.triggers.updated', label: 'Trigger Updated', entity: 'triggers', category: 'crud' },
|
|
35
|
+
{ id: 'workflows.triggers.deleted', label: 'Trigger Deleted', entity: 'triggers', category: 'crud' },
|
|
36
|
+
] as const
|
|
37
|
+
|
|
38
|
+
export const eventsConfig = createModuleEvents({
|
|
39
|
+
moduleId: 'workflows',
|
|
40
|
+
events,
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
/** Type-safe event emitter for workflows module */
|
|
44
|
+
export const emitWorkflowsEvent = eventsConfig.emit
|
|
45
|
+
|
|
46
|
+
/** Event IDs that can be emitted by the workflows module */
|
|
47
|
+
export type WorkflowsEventId = typeof events[number]['id']
|
|
48
|
+
|
|
49
|
+
export default eventsConfig
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
{
|
|
2
|
+
"workflowId": "sales_order_approval_v1",
|
|
3
|
+
"workflowName": "Sales Order Approval Workflow",
|
|
4
|
+
"description": "Approval workflow for sales orders requiring authorization before processing",
|
|
5
|
+
"version": 1,
|
|
6
|
+
"enabled": true,
|
|
7
|
+
"metadata": {
|
|
8
|
+
"category": "Sales",
|
|
9
|
+
"tags": [
|
|
10
|
+
"sales",
|
|
11
|
+
"approval",
|
|
12
|
+
"order"
|
|
13
|
+
],
|
|
14
|
+
"icon": "check-circle",
|
|
15
|
+
"entityType": "SalesOrder"
|
|
16
|
+
},
|
|
17
|
+
"definition": {
|
|
18
|
+
"steps": [
|
|
19
|
+
{
|
|
20
|
+
"stepId": "start",
|
|
21
|
+
"stepName": "Start",
|
|
22
|
+
"stepType": "START",
|
|
23
|
+
"description": "Initialize order approval workflow"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"stepId": "pending_approval",
|
|
27
|
+
"stepName": "Pending Approval",
|
|
28
|
+
"stepType": "USER_TASK",
|
|
29
|
+
"description": "Order awaiting approval decision",
|
|
30
|
+
"userTaskConfig": {
|
|
31
|
+
"formSchema": {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"required": [
|
|
34
|
+
"decision"
|
|
35
|
+
],
|
|
36
|
+
"properties": {
|
|
37
|
+
"comments": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"title": "Comments",
|
|
40
|
+
"description": "Optional comments for the decision"
|
|
41
|
+
},
|
|
42
|
+
"decision": {
|
|
43
|
+
"enum": [
|
|
44
|
+
"approve",
|
|
45
|
+
"reject"
|
|
46
|
+
],
|
|
47
|
+
"type": "string",
|
|
48
|
+
"title": "Decision",
|
|
49
|
+
"description": "Approve or reject the order"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"slaDuration": "PT24H"
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"stepId": "approved",
|
|
58
|
+
"stepName": "Approved",
|
|
59
|
+
"stepType": "AUTOMATED",
|
|
60
|
+
"description": "Order has been approved"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"stepId": "rejected",
|
|
64
|
+
"stepName": "Rejected",
|
|
65
|
+
"stepType": "AUTOMATED",
|
|
66
|
+
"description": "Order has been rejected"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"stepId": "end",
|
|
70
|
+
"stepName": "Complete",
|
|
71
|
+
"stepType": "END",
|
|
72
|
+
"description": "Workflow complete"
|
|
73
|
+
}
|
|
74
|
+
],
|
|
75
|
+
"triggers": [
|
|
76
|
+
{
|
|
77
|
+
"name": "Order Approval Trigger",
|
|
78
|
+
"config": {
|
|
79
|
+
"entityType": "SalesOrder"
|
|
80
|
+
},
|
|
81
|
+
"enabled": true,
|
|
82
|
+
"priority": 0,
|
|
83
|
+
"triggerId": "order_approval_trigger",
|
|
84
|
+
"description": "Triggers when a new sales order is created",
|
|
85
|
+
"eventPattern": "sales.orders.created"
|
|
86
|
+
}
|
|
87
|
+
],
|
|
88
|
+
"transitions": [
|
|
89
|
+
{
|
|
90
|
+
"transitionId": "start_to_pending",
|
|
91
|
+
"transitionName": "Submit for Approval",
|
|
92
|
+
"fromStepId": "start",
|
|
93
|
+
"toStepId": "pending_approval",
|
|
94
|
+
"trigger": "auto",
|
|
95
|
+
"priority": 100,
|
|
96
|
+
"continueOnActivityFailure": true,
|
|
97
|
+
"activities": [
|
|
98
|
+
{
|
|
99
|
+
"activityId": "set_pending_status",
|
|
100
|
+
"activityName": "Set Order to Pending Approval",
|
|
101
|
+
"activityType": "UPDATE_ENTITY",
|
|
102
|
+
"async": false,
|
|
103
|
+
"config": {
|
|
104
|
+
"commandId": "sales.orders.update",
|
|
105
|
+
"statusDictionary": "sales.order_status",
|
|
106
|
+
"input": {
|
|
107
|
+
"id": "{{context.id}}",
|
|
108
|
+
"statusValue": "pending_approval"
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
"retryPolicy": {
|
|
112
|
+
"maxAttempts": 3,
|
|
113
|
+
"initialIntervalMs": 1000,
|
|
114
|
+
"backoffCoefficient": 2,
|
|
115
|
+
"maxIntervalMs": 10000
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"activityId": "emit_approval_requested",
|
|
120
|
+
"activityName": "Emit Approval Requested Event",
|
|
121
|
+
"activityType": "EMIT_EVENT",
|
|
122
|
+
"async": true,
|
|
123
|
+
"config": {
|
|
124
|
+
"eventName": "sales.order.approval.requested",
|
|
125
|
+
"payload": {
|
|
126
|
+
"orderId": "{{context.id}}",
|
|
127
|
+
"workflowInstanceId": "{{workflow.instanceId}}"
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
]
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"transitionId": "pending_approval_to_approved",
|
|
135
|
+
"transitionName": "Approve Order",
|
|
136
|
+
"fromStepId": "pending_approval",
|
|
137
|
+
"toStepId": "approved",
|
|
138
|
+
"trigger": "auto",
|
|
139
|
+
"priority": 100,
|
|
140
|
+
"preConditions": [
|
|
141
|
+
{
|
|
142
|
+
"ruleId": "workflow_order_approval_check_approved",
|
|
143
|
+
"required": true
|
|
144
|
+
}
|
|
145
|
+
],
|
|
146
|
+
"continueOnActivityFailure": true,
|
|
147
|
+
"activities": [
|
|
148
|
+
{
|
|
149
|
+
"activityId": "set_approved_status",
|
|
150
|
+
"activityName": "Set Order to Approved",
|
|
151
|
+
"activityType": "UPDATE_ENTITY",
|
|
152
|
+
"async": false,
|
|
153
|
+
"config": {
|
|
154
|
+
"commandId": "sales.orders.update",
|
|
155
|
+
"statusDictionary": "sales.order_status",
|
|
156
|
+
"input": {
|
|
157
|
+
"id": "{{context.id}}",
|
|
158
|
+
"statusValue": "approved"
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
"retryPolicy": {
|
|
162
|
+
"maxAttempts": 3,
|
|
163
|
+
"initialIntervalMs": 1000,
|
|
164
|
+
"backoffCoefficient": 2,
|
|
165
|
+
"maxIntervalMs": 10000
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"activityId": "emit_order_approved",
|
|
170
|
+
"activityName": "Emit Order Approved Event",
|
|
171
|
+
"activityType": "EMIT_EVENT",
|
|
172
|
+
"async": true,
|
|
173
|
+
"config": {
|
|
174
|
+
"eventName": "sales.order.approval.approved",
|
|
175
|
+
"payload": {
|
|
176
|
+
"orderId": "{{context.id}}",
|
|
177
|
+
"workflowInstanceId": "{{workflow.instanceId}}",
|
|
178
|
+
"approvedBy": "{{context.completedBy}}",
|
|
179
|
+
"comments": "{{context.comments}}"
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
]
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"transitionId": "pending_approval_to_rejected",
|
|
187
|
+
"transitionName": "Reject Order",
|
|
188
|
+
"fromStepId": "pending_approval",
|
|
189
|
+
"toStepId": "rejected",
|
|
190
|
+
"trigger": "auto",
|
|
191
|
+
"priority": 90,
|
|
192
|
+
"preConditions": [
|
|
193
|
+
{
|
|
194
|
+
"ruleId": "workflow_order_approval_check_rejected",
|
|
195
|
+
"required": true
|
|
196
|
+
}
|
|
197
|
+
],
|
|
198
|
+
"continueOnActivityFailure": true,
|
|
199
|
+
"activities": [
|
|
200
|
+
{
|
|
201
|
+
"activityId": "set_rejected_status",
|
|
202
|
+
"activityName": "Set Order to Rejected",
|
|
203
|
+
"activityType": "UPDATE_ENTITY",
|
|
204
|
+
"async": false,
|
|
205
|
+
"config": {
|
|
206
|
+
"commandId": "sales.orders.update",
|
|
207
|
+
"statusDictionary": "sales.order_status",
|
|
208
|
+
"input": {
|
|
209
|
+
"id": "{{context.id}}",
|
|
210
|
+
"statusValue": "rejected"
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
"retryPolicy": {
|
|
214
|
+
"maxAttempts": 3,
|
|
215
|
+
"initialIntervalMs": 1000,
|
|
216
|
+
"backoffCoefficient": 2,
|
|
217
|
+
"maxIntervalMs": 10000
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"activityId": "emit_order_rejected",
|
|
222
|
+
"activityName": "Emit Order Rejected Event",
|
|
223
|
+
"activityType": "EMIT_EVENT",
|
|
224
|
+
"async": true,
|
|
225
|
+
"config": {
|
|
226
|
+
"eventName": "sales.order.approval.rejected",
|
|
227
|
+
"payload": {
|
|
228
|
+
"orderId": "{{context.id}}",
|
|
229
|
+
"workflowInstanceId": "{{workflow.instanceId}}",
|
|
230
|
+
"rejectedBy": "{{context.completedBy}}",
|
|
231
|
+
"comments": "{{context.comments}}"
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
]
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
"transitionId": "approved_to_end",
|
|
239
|
+
"transitionName": "Complete After Approval",
|
|
240
|
+
"fromStepId": "approved",
|
|
241
|
+
"toStepId": "end",
|
|
242
|
+
"trigger": "auto",
|
|
243
|
+
"priority": 100,
|
|
244
|
+
"continueOnActivityFailure": true
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
"transitionId": "rejected_to_end",
|
|
248
|
+
"transitionName": "Complete After Rejection",
|
|
249
|
+
"fromStepId": "rejected",
|
|
250
|
+
"toStepId": "end",
|
|
251
|
+
"trigger": "auto",
|
|
252
|
+
"priority": 100,
|
|
253
|
+
"continueOnActivityFailure": true
|
|
254
|
+
}
|
|
255
|
+
]
|
|
256
|
+
}
|
|
257
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"ruleId": "workflow_order_approval_check_approved",
|
|
4
|
+
"ruleName": "Check Decision is Approve",
|
|
5
|
+
"description": "Validates that the approval task decision is 'approve'",
|
|
6
|
+
"ruleType": "GUARD",
|
|
7
|
+
"entityType": "workflow:sales_order_approval_v1:transition",
|
|
8
|
+
"eventType": "pre_transition",
|
|
9
|
+
"conditionExpression": {
|
|
10
|
+
"field": "workflowContext.decision",
|
|
11
|
+
"operator": "=",
|
|
12
|
+
"value": "approve"
|
|
13
|
+
},
|
|
14
|
+
"enabled": true,
|
|
15
|
+
"priority": 100
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"ruleId": "workflow_order_approval_check_rejected",
|
|
19
|
+
"ruleName": "Check Decision is Reject",
|
|
20
|
+
"description": "Validates that the approval task decision is 'reject'",
|
|
21
|
+
"ruleType": "GUARD",
|
|
22
|
+
"entityType": "workflow:sales_order_approval_v1:transition",
|
|
23
|
+
"eventType": "pre_transition",
|
|
24
|
+
"conditionExpression": {
|
|
25
|
+
"field": "workflowContext.decision",
|
|
26
|
+
"operator": "=",
|
|
27
|
+
"value": "reject"
|
|
28
|
+
},
|
|
29
|
+
"enabled": true,
|
|
30
|
+
"priority": 100
|
|
31
|
+
}
|
|
32
|
+
]
|