@open-mercato/core 0.4.2-canary-d0a025141f → 0.4.2-canary-3efa759f5c

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.
Files changed (158) hide show
  1. package/dist/generated/entities.ids.generated.js +0 -1
  2. package/dist/generated/entities.ids.generated.js.map +2 -2
  3. package/dist/generated/entity-fields-registry.js +0 -2
  4. package/dist/generated/entity-fields-registry.js.map +2 -2
  5. package/dist/modules/business_rules/data/validators.js +0 -34
  6. package/dist/modules/business_rules/data/validators.js.map +2 -2
  7. package/dist/modules/business_rules/index.js +1 -21
  8. package/dist/modules/business_rules/index.js.map +2 -2
  9. package/dist/modules/business_rules/lib/rule-engine.js +1 -182
  10. package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
  11. package/dist/modules/sales/acl.js +0 -1
  12. package/dist/modules/sales/acl.js.map +2 -2
  13. package/dist/modules/sales/backend/sales/documents/[id]/page.js +0 -12
  14. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  15. package/dist/modules/sales/commands/documents.js +0 -62
  16. package/dist/modules/sales/commands/documents.js.map +2 -2
  17. package/dist/modules/sales/lib/dictionaries.js +0 -3
  18. package/dist/modules/sales/lib/dictionaries.js.map +2 -2
  19. package/dist/modules/workflows/acl.js +0 -2
  20. package/dist/modules/workflows/acl.js.map +2 -2
  21. package/dist/modules/workflows/api/instances/route.js +6 -18
  22. package/dist/modules/workflows/api/instances/route.js.map +2 -2
  23. package/dist/modules/workflows/api/tasks/route.js +1 -6
  24. package/dist/modules/workflows/api/tasks/route.js.map +2 -2
  25. package/dist/modules/workflows/backend/definitions/[id]/page.js +1 -9
  26. package/dist/modules/workflows/backend/definitions/[id]/page.js.map +2 -2
  27. package/dist/modules/workflows/backend/definitions/[id]/page.meta.js +1 -1
  28. package/dist/modules/workflows/backend/definitions/[id]/page.meta.js.map +2 -2
  29. package/dist/modules/workflows/backend/definitions/create/page.js +15 -24
  30. package/dist/modules/workflows/backend/definitions/create/page.js.map +2 -2
  31. package/dist/modules/workflows/backend/definitions/create/page.meta.js +1 -1
  32. package/dist/modules/workflows/backend/definitions/create/page.meta.js.map +2 -2
  33. package/dist/modules/workflows/backend/definitions/visual-editor/page.js +132 -150
  34. package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
  35. package/dist/modules/workflows/backend/definitions/visual-editor/page.meta.js +1 -1
  36. package/dist/modules/workflows/backend/definitions/visual-editor/page.meta.js.map +2 -2
  37. package/dist/modules/workflows/backend/events/[id]/page.js +1 -1
  38. package/dist/modules/workflows/backend/events/[id]/page.js.map +2 -2
  39. package/dist/modules/workflows/backend/events/[id]/page.meta.js +2 -2
  40. package/dist/modules/workflows/backend/events/[id]/page.meta.js.map +2 -2
  41. package/dist/modules/workflows/backend/instances/[id]/page.meta.js +2 -2
  42. package/dist/modules/workflows/backend/instances/[id]/page.meta.js.map +2 -2
  43. package/dist/modules/workflows/backend/tasks/[id]/page.js +1 -1
  44. package/dist/modules/workflows/backend/tasks/[id]/page.js.map +2 -2
  45. package/dist/modules/workflows/backend/tasks/[id]/page.meta.js +2 -2
  46. package/dist/modules/workflows/backend/tasks/[id]/page.meta.js.map +2 -2
  47. package/dist/modules/workflows/backend/tasks/page.js +6 -5
  48. package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
  49. package/dist/modules/workflows/cli.js +3 -81
  50. package/dist/modules/workflows/cli.js.map +3 -3
  51. package/dist/modules/workflows/data/entities.js +1 -64
  52. package/dist/modules/workflows/data/entities.js.map +2 -2
  53. package/dist/modules/workflows/data/validators.js +0 -115
  54. package/dist/modules/workflows/data/validators.js.map +2 -2
  55. package/dist/modules/workflows/examples/checkout-demo-definition.json +5 -1
  56. package/dist/modules/workflows/lib/activity-executor.js +13 -75
  57. package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
  58. package/dist/modules/workflows/lib/graph-utils.js +2 -71
  59. package/dist/modules/workflows/lib/graph-utils.js.map +2 -2
  60. package/dist/modules/workflows/lib/seeds.js +5 -22
  61. package/dist/modules/workflows/lib/seeds.js.map +2 -2
  62. package/dist/modules/workflows/lib/start-validator.js +23 -33
  63. package/dist/modules/workflows/lib/start-validator.js.map +2 -2
  64. package/dist/modules/workflows/lib/transition-handler.js +45 -157
  65. package/dist/modules/workflows/lib/transition-handler.js.map +3 -3
  66. package/generated/entities.ids.generated.ts +0 -1
  67. package/generated/entity-fields-registry.ts +0 -2
  68. package/package.json +2 -2
  69. package/src/modules/business_rules/data/validators.ts +0 -40
  70. package/src/modules/business_rules/index.ts +0 -25
  71. package/src/modules/business_rules/lib/rule-engine.ts +1 -281
  72. package/src/modules/sales/acl.ts +0 -1
  73. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +0 -16
  74. package/src/modules/sales/commands/documents.ts +1 -74
  75. package/src/modules/sales/lib/dictionaries.ts +0 -3
  76. package/src/modules/workflows/acl.ts +0 -2
  77. package/src/modules/workflows/api/__tests__/instances.route.test.ts +2 -5
  78. package/src/modules/workflows/api/instances/route.ts +7 -21
  79. package/src/modules/workflows/api/tasks/route.ts +1 -7
  80. package/src/modules/workflows/backend/definitions/[id]/page.meta.ts +1 -1
  81. package/src/modules/workflows/backend/definitions/[id]/page.tsx +0 -9
  82. package/src/modules/workflows/backend/definitions/create/page.meta.ts +1 -1
  83. package/src/modules/workflows/backend/definitions/create/page.tsx +0 -9
  84. package/src/modules/workflows/backend/definitions/visual-editor/page.meta.ts +1 -1
  85. package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +3 -21
  86. package/src/modules/workflows/backend/events/[id]/page.meta.ts +2 -2
  87. package/src/modules/workflows/backend/events/[id]/page.tsx +1 -1
  88. package/src/modules/workflows/backend/instances/[id]/page.meta.ts +2 -2
  89. package/src/modules/workflows/backend/tasks/[id]/page.meta.ts +2 -2
  90. package/src/modules/workflows/backend/tasks/[id]/page.tsx +1 -1
  91. package/src/modules/workflows/backend/tasks/page.tsx +6 -5
  92. package/src/modules/workflows/cli.ts +0 -111
  93. package/src/modules/workflows/data/entities.ts +0 -124
  94. package/src/modules/workflows/data/validators.ts +0 -138
  95. package/src/modules/workflows/examples/checkout-demo-definition.json +5 -1
  96. package/src/modules/workflows/i18n/en.json +0 -71
  97. package/src/modules/workflows/lib/__tests__/activity-executor.test.ts +36 -43
  98. package/src/modules/workflows/lib/__tests__/transition-handler.test.ts +90 -170
  99. package/src/modules/workflows/lib/activity-executor.ts +16 -129
  100. package/src/modules/workflows/lib/graph-utils.ts +2 -117
  101. package/src/modules/workflows/lib/seeds.ts +8 -34
  102. package/src/modules/workflows/lib/start-validator.ts +28 -38
  103. package/src/modules/workflows/lib/transition-handler.ts +55 -208
  104. package/dist/generated/entities/workflow_event_trigger/index.js +0 -33
  105. package/dist/generated/entities/workflow_event_trigger/index.js.map +0 -7
  106. package/dist/modules/auth/events.js +0 -30
  107. package/dist/modules/auth/events.js.map +0 -7
  108. package/dist/modules/business_rules/api/execute/[ruleId]/route.js +0 -145
  109. package/dist/modules/business_rules/api/execute/[ruleId]/route.js.map +0 -7
  110. package/dist/modules/catalog/events.js +0 -34
  111. package/dist/modules/catalog/events.js.map +0 -7
  112. package/dist/modules/customers/events.js +0 -49
  113. package/dist/modules/customers/events.js.map +0 -7
  114. package/dist/modules/directory/events.js +0 -23
  115. package/dist/modules/directory/events.js.map +0 -7
  116. package/dist/modules/sales/events.js +0 -63
  117. package/dist/modules/sales/events.js.map +0 -7
  118. package/dist/modules/sales/lib/frontend/documentDataEvents.js +0 -25
  119. package/dist/modules/sales/lib/frontend/documentDataEvents.js.map +0 -7
  120. package/dist/modules/workflows/components/DefinitionTriggersEditor.js +0 -481
  121. package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +0 -7
  122. package/dist/modules/workflows/components/EventTriggersEditor.js +0 -553
  123. package/dist/modules/workflows/components/EventTriggersEditor.js.map +0 -7
  124. package/dist/modules/workflows/events.js +0 -38
  125. package/dist/modules/workflows/events.js.map +0 -7
  126. package/dist/modules/workflows/examples/order-approval-definition.json +0 -257
  127. package/dist/modules/workflows/examples/order-approval-guard-rules.json +0 -32
  128. package/dist/modules/workflows/lib/event-trigger-service.js +0 -308
  129. package/dist/modules/workflows/lib/event-trigger-service.js.map +0 -7
  130. package/dist/modules/workflows/migrations/Migration20260123143500.js +0 -36
  131. package/dist/modules/workflows/migrations/Migration20260123143500.js.map +0 -7
  132. package/dist/modules/workflows/subscribers/event-trigger.js +0 -78
  133. package/dist/modules/workflows/subscribers/event-trigger.js.map +0 -7
  134. package/dist/modules/workflows/widgets/injection/order-approval/widget.client.js +0 -323
  135. package/dist/modules/workflows/widgets/injection/order-approval/widget.client.js.map +0 -7
  136. package/dist/modules/workflows/widgets/injection/order-approval/widget.js +0 -17
  137. package/dist/modules/workflows/widgets/injection/order-approval/widget.js.map +0 -7
  138. package/dist/modules/workflows/widgets/injection-table.js +0 -19
  139. package/dist/modules/workflows/widgets/injection-table.js.map +0 -7
  140. package/generated/entities/workflow_event_trigger/index.ts +0 -15
  141. package/src/modules/auth/events.ts +0 -39
  142. package/src/modules/business_rules/api/execute/[ruleId]/route.ts +0 -163
  143. package/src/modules/catalog/events.ts +0 -45
  144. package/src/modules/customers/events.ts +0 -63
  145. package/src/modules/directory/events.ts +0 -31
  146. package/src/modules/sales/events.ts +0 -82
  147. package/src/modules/sales/lib/frontend/documentDataEvents.ts +0 -28
  148. package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +0 -581
  149. package/src/modules/workflows/components/EventTriggersEditor.tsx +0 -664
  150. package/src/modules/workflows/events.ts +0 -49
  151. package/src/modules/workflows/examples/order-approval-definition.json +0 -257
  152. package/src/modules/workflows/examples/order-approval-guard-rules.json +0 -32
  153. package/src/modules/workflows/lib/event-trigger-service.ts +0 -557
  154. package/src/modules/workflows/migrations/Migration20260123143500.ts +0 -38
  155. package/src/modules/workflows/subscribers/event-trigger.ts +0 -109
  156. package/src/modules/workflows/widgets/injection/order-approval/widget.client.tsx +0 -446
  157. package/src/modules/workflows/widgets/injection/order-approval/widget.ts +0 -16
  158. package/src/modules/workflows/widgets/injection-table.ts +0 -21
@@ -462,35 +462,15 @@ describe('Transition Handler (Unit Tests)', () => {
462
462
  })
463
463
 
464
464
  test('should reject transition if pre-conditions fail', async () => {
465
- // Create a definition with preConditions
466
- const definitionWithPreConditions = {
467
- ...mockDefinition,
468
- definition: {
469
- ...mockDefinition.definition,
470
- transitions: [
471
- { fromStepId: 'start', toStepId: 'step-1' },
472
- {
473
- fromStepId: 'step-1',
474
- toStepId: 'step-2',
475
- preConditions: [{ ruleId: 'test-guard-rule', required: true }],
476
- },
477
- { fromStepId: 'step-2', toStepId: 'end' },
478
- ],
479
- },
480
- }
481
-
482
465
  mockEm.findOne
483
- .mockResolvedValueOnce(definitionWithPreConditions) // evaluateTransition
484
- .mockResolvedValueOnce(definitionWithPreConditions) // evaluatePreConditions
485
-
486
- ;(ruleEngine.executeRuleByRuleId as jest.Mock).mockResolvedValueOnce({
487
- success: false,
488
- ruleId: 'test-guard-rule',
489
- ruleName: 'Test Guard Rule',
490
- conditionResult: false,
491
- actionsExecuted: null,
492
- executionTime: 10,
493
- error: undefined,
466
+ .mockResolvedValueOnce(mockDefinition) // evaluateTransition
467
+ .mockResolvedValueOnce(mockDefinition) // evaluatePreConditions
468
+
469
+ ;(ruleEngine.executeRules as jest.Mock).mockResolvedValueOnce({
470
+ allowed: false,
471
+ executedRules: [],
472
+ totalExecutionTime: 10,
473
+ errors: ['Pre-condition rule failed'],
494
474
  })
495
475
 
496
476
  mockEm.create.mockReturnValue({} as any)
@@ -511,35 +491,15 @@ describe('Transition Handler (Unit Tests)', () => {
511
491
  })
512
492
 
513
493
  test('should log transition rejection event', async () => {
514
- // Create a definition with preConditions
515
- const definitionWithPreConditions = {
516
- ...mockDefinition,
517
- definition: {
518
- ...mockDefinition.definition,
519
- transitions: [
520
- { fromStepId: 'start', toStepId: 'step-1' },
521
- {
522
- fromStepId: 'step-1',
523
- toStepId: 'step-2',
524
- preConditions: [{ ruleId: 'test-guard-rule', required: true }],
525
- },
526
- { fromStepId: 'step-2', toStepId: 'end' },
527
- ],
528
- },
529
- }
530
-
531
494
  mockEm.findOne
532
- .mockResolvedValueOnce(definitionWithPreConditions)
533
- .mockResolvedValueOnce(definitionWithPreConditions)
534
-
535
- ;(ruleEngine.executeRuleByRuleId as jest.Mock).mockResolvedValueOnce({
536
- success: false,
537
- ruleId: 'test-guard-rule',
538
- ruleName: 'Test Guard Rule',
539
- conditionResult: false,
540
- actionsExecuted: null,
541
- executionTime: 10,
542
- error: undefined,
495
+ .mockResolvedValueOnce(mockDefinition)
496
+ .mockResolvedValueOnce(mockDefinition)
497
+
498
+ ;(ruleEngine.executeRules as jest.Mock).mockResolvedValueOnce({
499
+ allowed: false,
500
+ executedRules: [],
501
+ totalExecutionTime: 10,
502
+ errors: ['Rule XYZ failed'],
543
503
  })
544
504
 
545
505
  mockEm.create.mockReturnValue({} as any)
@@ -567,36 +527,27 @@ describe('Transition Handler (Unit Tests)', () => {
567
527
  })
568
528
 
569
529
  test('should execute transition even if post-conditions fail (warning only)', async () => {
570
- // Create a definition with postConditions
571
- const definitionWithPostConditions = {
572
- ...mockDefinition,
573
- definition: {
574
- ...mockDefinition.definition,
575
- transitions: [
576
- { fromStepId: 'start', toStepId: 'step-1' },
577
- {
578
- fromStepId: 'step-1',
579
- toStepId: 'step-2',
580
- postConditions: [{ ruleId: 'test-post-rule', required: true }],
581
- },
582
- { fromStepId: 'step-2', toStepId: 'end' },
583
- ],
584
- },
585
- }
586
-
587
530
  mockEm.findOne.mockReset()
588
- mockEm.findOne.mockResolvedValue(definitionWithPostConditions)
589
-
590
- // Post-condition fails
591
- ;(ruleEngine.executeRuleByRuleId as jest.Mock).mockResolvedValueOnce({
592
- success: false,
593
- ruleId: 'test-post-rule',
594
- ruleName: 'Test Post Rule',
595
- conditionResult: false,
596
- actionsExecuted: null,
597
- executionTime: 5,
598
- error: undefined,
599
- })
531
+ mockEm.findOne
532
+ .mockResolvedValueOnce(mockDefinition)
533
+ .mockResolvedValueOnce(mockDefinition)
534
+ .mockResolvedValueOnce(mockDefinition)
535
+ .mockResolvedValueOnce(mockDefinition)
536
+ .mockResolvedValueOnce(mockDefinition)
537
+ .mockResolvedValue(mockDefinition)
538
+
539
+ ;(ruleEngine.executeRules as jest.Mock)
540
+ .mockResolvedValueOnce({
541
+ allowed: true,
542
+ executedRules: [],
543
+ totalExecutionTime: 10,
544
+ })
545
+ .mockResolvedValueOnce({
546
+ allowed: false, // Post-condition failed
547
+ executedRules: [],
548
+ totalExecutionTime: 5,
549
+ errors: ['Post-condition rule failed'],
550
+ })
600
551
 
601
552
  mockEm.create.mockReturnValue({} as any)
602
553
 
@@ -758,33 +709,22 @@ describe('Transition Handler (Unit Tests)', () => {
758
709
 
759
710
  describe('Business Rules Integration', () => {
760
711
  test('should call rule engine for pre-conditions with correct entityType', async () => {
761
- // Create a definition with preConditions
762
- const definitionWithPreConditions = {
763
- ...mockDefinition,
764
- definition: {
765
- ...mockDefinition.definition,
766
- transitions: [
767
- { fromStepId: 'start', toStepId: 'step-1' },
768
- {
769
- fromStepId: 'step-1',
770
- toStepId: 'step-2',
771
- preConditions: [{ ruleId: 'test-guard-rule', required: true }],
772
- },
773
- { fromStepId: 'step-2', toStepId: 'end' },
774
- ],
775
- },
776
- }
777
-
778
- mockEm.findOne.mockResolvedValue(definitionWithPreConditions)
712
+ mockEm.findOne
713
+ .mockResolvedValueOnce(mockDefinition)
714
+ .mockResolvedValueOnce(mockDefinition)
715
+ .mockResolvedValueOnce(mockDefinition)
779
716
 
780
- ;(ruleEngine.executeRuleByRuleId as jest.Mock).mockResolvedValueOnce({
781
- success: true,
782
- ruleId: 'test-guard-rule',
783
- ruleName: 'Test Guard Rule',
784
- conditionResult: true,
785
- actionsExecuted: null,
786
- executionTime: 10,
787
- })
717
+ ;(ruleEngine.executeRules as jest.Mock)
718
+ .mockResolvedValueOnce({
719
+ allowed: true,
720
+ executedRules: [],
721
+ totalExecutionTime: 10,
722
+ })
723
+ .mockResolvedValueOnce({
724
+ allowed: true,
725
+ executedRules: [],
726
+ totalExecutionTime: 5,
727
+ })
788
728
 
789
729
  mockEm.create.mockReturnValue({} as any)
790
730
 
@@ -797,10 +737,9 @@ describe('Transition Handler (Unit Tests)', () => {
797
737
  { workflowContext: {} }
798
738
  )
799
739
 
800
- expect(ruleEngine.executeRuleByRuleId).toHaveBeenCalledWith(
740
+ expect(ruleEngine.executeRules).toHaveBeenCalledWith(
801
741
  mockEm,
802
742
  expect.objectContaining({
803
- ruleId: 'test-guard-rule',
804
743
  entityType: 'workflow:simple-approval:transition',
805
744
  eventType: 'pre_transition',
806
745
  data: expect.objectContaining({
@@ -812,34 +751,26 @@ describe('Transition Handler (Unit Tests)', () => {
812
751
  })
813
752
 
814
753
  test('should call rule engine for post-conditions with correct eventType', async () => {
815
- // Create a definition with postConditions
816
- const definitionWithPostConditions = {
817
- ...mockDefinition,
818
- definition: {
819
- ...mockDefinition.definition,
820
- transitions: [
821
- { fromStepId: 'start', toStepId: 'step-1' },
822
- {
823
- fromStepId: 'step-1',
824
- toStepId: 'step-2',
825
- postConditions: [{ ruleId: 'test-post-rule', required: true }],
826
- },
827
- { fromStepId: 'step-2', toStepId: 'end' },
828
- ],
829
- },
830
- }
831
-
832
754
  mockEm.findOne.mockReset()
833
- mockEm.findOne.mockResolvedValue(definitionWithPostConditions)
834
-
835
- ;(ruleEngine.executeRuleByRuleId as jest.Mock).mockResolvedValueOnce({
836
- success: true,
837
- ruleId: 'test-post-rule',
838
- ruleName: 'Test Post Rule',
839
- conditionResult: true,
840
- actionsExecuted: null,
841
- executionTime: 5,
842
- })
755
+ mockEm.findOne
756
+ .mockResolvedValueOnce(mockDefinition)
757
+ .mockResolvedValueOnce(mockDefinition)
758
+ .mockResolvedValueOnce(mockDefinition)
759
+ .mockResolvedValueOnce(mockDefinition)
760
+ .mockResolvedValueOnce(mockDefinition)
761
+ .mockResolvedValue(mockDefinition)
762
+
763
+ ;(ruleEngine.executeRules as jest.Mock)
764
+ .mockResolvedValueOnce({
765
+ allowed: true,
766
+ executedRules: [],
767
+ totalExecutionTime: 10,
768
+ })
769
+ .mockResolvedValueOnce({
770
+ allowed: true,
771
+ executedRules: [],
772
+ totalExecutionTime: 5,
773
+ })
843
774
 
844
775
  mockEm.create.mockReturnValue({} as any)
845
776
 
@@ -852,11 +783,11 @@ describe('Transition Handler (Unit Tests)', () => {
852
783
  { workflowContext: {} }
853
784
  )
854
785
 
855
- // Check call to executeRuleByRuleId for post-conditions
856
- expect(ruleEngine.executeRuleByRuleId).toHaveBeenCalledWith(
786
+ // Check second call to executeRules (post-conditions)
787
+ expect(ruleEngine.executeRules).toHaveBeenNthCalledWith(
788
+ 2,
857
789
  mockEm,
858
790
  expect.objectContaining({
859
- ruleId: 'test-post-rule',
860
791
  entityType: 'workflow:simple-approval:transition',
861
792
  eventType: 'post_transition',
862
793
  })
@@ -864,33 +795,22 @@ describe('Transition Handler (Unit Tests)', () => {
864
795
  })
865
796
 
866
797
  test('should pass workflow context and trigger data to rule engine', async () => {
867
- // Create a definition with preConditions
868
- const definitionWithPreConditions = {
869
- ...mockDefinition,
870
- definition: {
871
- ...mockDefinition.definition,
872
- transitions: [
873
- { fromStepId: 'start', toStepId: 'step-1' },
874
- {
875
- fromStepId: 'step-1',
876
- toStepId: 'step-2',
877
- preConditions: [{ ruleId: 'test-guard-rule', required: true }],
878
- },
879
- { fromStepId: 'step-2', toStepId: 'end' },
880
- ],
881
- },
882
- }
883
-
884
- mockEm.findOne.mockResolvedValue(definitionWithPreConditions)
798
+ mockEm.findOne
799
+ .mockResolvedValueOnce(mockDefinition)
800
+ .mockResolvedValueOnce(mockDefinition)
801
+ .mockResolvedValueOnce(mockDefinition)
885
802
 
886
- ;(ruleEngine.executeRuleByRuleId as jest.Mock).mockResolvedValueOnce({
887
- success: true,
888
- ruleId: 'test-guard-rule',
889
- ruleName: 'Test Guard Rule',
890
- conditionResult: true,
891
- actionsExecuted: null,
892
- executionTime: 10,
893
- })
803
+ ;(ruleEngine.executeRules as jest.Mock)
804
+ .mockResolvedValueOnce({
805
+ allowed: true,
806
+ executedRules: [],
807
+ totalExecutionTime: 10,
808
+ })
809
+ .mockResolvedValueOnce({
810
+ allowed: true,
811
+ executedRules: [],
812
+ totalExecutionTime: 5,
813
+ })
894
814
 
895
815
  mockEm.create.mockReturnValue({} as any)
896
816
 
@@ -910,7 +830,7 @@ describe('Transition Handler (Unit Tests)', () => {
910
830
  }
911
831
  )
912
832
 
913
- expect(ruleEngine.executeRuleByRuleId).toHaveBeenCalledWith(
833
+ expect(ruleEngine.executeRules).toHaveBeenCalledWith(
914
834
  mockEm,
915
835
  expect.objectContaining({
916
836
  data: expect.objectContaining({
@@ -448,30 +448,7 @@ export async function executeEmitEvent(
448
448
  /**
449
449
  * UPDATE_ENTITY activity handler
450
450
  *
451
- * Updates an entity via CommandBus for proper audit logging, undo support, and side effects.
452
- *
453
- * Config format:
454
- * ```json
455
- * {
456
- * "commandId": "sales.documents.update",
457
- * "input": {
458
- * "id": "{{context.orderId}}",
459
- * "statusEntryId": "{{context.approvedStatusId}}"
460
- * }
461
- * }
462
- * ```
463
- *
464
- * Alternative format with statusValue (auto-resolves to statusEntryId):
465
- * ```json
466
- * {
467
- * "commandId": "sales.orders.update",
468
- * "statusDictionary": "sales.order_status",
469
- * "input": {
470
- * "id": "{{context.id}}",
471
- * "statusValue": "pending_approval"
472
- * }
473
- * }
474
- * ```
451
+ * Updates an entity via query engine
475
452
  */
476
453
  export async function executeUpdateEntity(
477
454
  em: EntityManager,
@@ -479,119 +456,29 @@ export async function executeUpdateEntity(
479
456
  context: ActivityContext,
480
457
  container: AwilixContainer
481
458
  ): Promise<any> {
482
- const { commandId, input, statusDictionary } = config
483
-
484
- if (!commandId) {
485
- throw new Error('UPDATE_ENTITY requires "commandId" field (e.g., "sales.documents.update")')
486
- }
459
+ const { entityType, entityId, updates } = config
487
460
 
488
- if (!input || typeof input !== 'object') {
489
- throw new Error('UPDATE_ENTITY requires "input" object with entity data')
461
+ if (!entityType || !entityId || !updates) {
462
+ throw new Error('UPDATE_ENTITY requires "entityType", "entityId", and "updates" fields')
490
463
  }
491
464
 
492
- // Resolve CommandBus from container
493
- const commandBus = container.resolve('commandBus') as any
494
-
495
- if (!commandBus || typeof commandBus.execute !== 'function') {
496
- throw new Error('CommandBus not available in container')
497
- }
498
-
499
- // Prepare final input, resolving statusValue if provided
500
- let finalInput = { ...input }
501
-
502
- // If statusValue is provided with a statusDictionary, resolve it to statusEntryId
503
- if (finalInput.statusValue && statusDictionary) {
504
- const statusEntryId = await resolveDictionaryEntryId(
505
- em,
506
- statusDictionary,
507
- finalInput.statusValue,
508
- context.workflowInstance.tenantId,
509
- context.workflowInstance.organizationId
510
- )
511
- if (statusEntryId) {
512
- finalInput.statusEntryId = statusEntryId
513
- }
514
- delete finalInput.statusValue
515
- }
465
+ // Get query engine from container
466
+ const queryEngine = container.resolve('queryEngine')
516
467
 
517
- // Build synthetic CommandRuntimeContext for workflow execution
518
- // Use nil UUID for system actions when no user context is available
519
- const SYSTEM_USER_ID = '00000000-0000-0000-0000-000000000000'
520
- const ctx = {
521
- container,
522
- auth: {
523
- sub: context.userId || SYSTEM_USER_ID,
524
- tenantId: context.workflowInstance.tenantId,
525
- orgId: context.workflowInstance.organizationId,
526
- isSuperAdmin: false,
527
- },
528
- organizationScope: null,
529
- selectedOrganizationId: context.workflowInstance.organizationId,
530
- organizationIds: context.workflowInstance.organizationId
531
- ? [context.workflowInstance.organizationId]
532
- : null,
468
+ if (!queryEngine || typeof queryEngine.update !== 'function') {
469
+ throw new Error('Query engine not available in container')
533
470
  }
534
471
 
535
- // Execute the command
536
- const { result, logEntry } = await commandBus.execute(commandId, {
537
- input: finalInput,
538
- ctx,
472
+ // Execute update with tenant scoping
473
+ await queryEngine.update({
474
+ entity: entityType,
475
+ where: { id: entityId },
476
+ data: updates,
477
+ tenantId: context.workflowInstance.tenantId,
478
+ organizationId: context.workflowInstance.organizationId,
539
479
  })
540
480
 
541
- return {
542
- executed: true,
543
- commandId,
544
- result,
545
- logEntryId: logEntry?.id,
546
- }
547
- }
548
-
549
- /**
550
- * Helper to resolve dictionary entry ID by value
551
- */
552
- async function resolveDictionaryEntryId(
553
- em: EntityManager,
554
- dictionaryKey: string,
555
- value: string,
556
- tenantId: string,
557
- organizationId: string
558
- ): Promise<string | null> {
559
- try {
560
- // Import here to avoid circular dependencies
561
- const { Dictionary, DictionaryEntry } = await import('@open-mercato/core/modules/dictionaries/data/entities')
562
-
563
- // Find the dictionary
564
- const dictionary = await em.findOne(Dictionary, {
565
- key: dictionaryKey,
566
- tenantId,
567
- organizationId,
568
- deletedAt: null,
569
- })
570
-
571
- if (!dictionary) {
572
- console.warn(`[UPDATE_ENTITY] Dictionary not found: ${dictionaryKey}`)
573
- return null
574
- }
575
-
576
- // Find the entry by normalized value
577
- const normalizedValue = value.toLowerCase().trim()
578
- const entry = await em.findOne(DictionaryEntry, {
579
- dictionary: dictionary.id,
580
- tenantId,
581
- organizationId,
582
- normalizedValue,
583
- })
584
-
585
- if (!entry) {
586
- console.warn(`[UPDATE_ENTITY] Dictionary entry not found: ${dictionaryKey}/${value}`)
587
- return null
588
- }
589
-
590
- return entry.id
591
- } catch (error) {
592
- console.error(`[UPDATE_ENTITY] Error resolving dictionary entry:`, error)
593
- return null
594
- }
481
+ return { updated: true, entityType, entityId, updates }
595
482
  }
596
483
 
597
484
  /**
@@ -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: 200, horizontal: 300 } } = options
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 = positions?.get(step.stepId) || { x: 250, y: 50 + index * layoutSpacing.vertical }
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
  */