@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
|
@@ -6,11 +6,15 @@ import checkoutDemoDefinition from "../examples/checkout-demo-definition.json";
|
|
|
6
6
|
import guardRulesExample from "../examples/guard-rules-example.json";
|
|
7
7
|
import salesPipelineDefinition from "../examples/sales-pipeline-definition.json";
|
|
8
8
|
import simpleApprovalDefinition from "../examples/simple-approval-definition.json";
|
|
9
|
+
import orderApprovalDefinition from "../examples/order-approval-definition.json";
|
|
10
|
+
import orderApprovalGuardRules from "../examples/order-approval-guard-rules.json";
|
|
9
11
|
const embeddedSeeds = {
|
|
10
12
|
"checkout-demo-definition.json": checkoutDemoDefinition,
|
|
11
13
|
"guard-rules-example.json": guardRulesExample,
|
|
12
14
|
"sales-pipeline-definition.json": salesPipelineDefinition,
|
|
13
|
-
"simple-approval-definition.json": simpleApprovalDefinition
|
|
15
|
+
"simple-approval-definition.json": simpleApprovalDefinition,
|
|
16
|
+
"order-approval-definition.json": orderApprovalDefinition,
|
|
17
|
+
"order-approval-guard-rules.json": orderApprovalGuardRules
|
|
14
18
|
};
|
|
15
19
|
function readExampleJson(fileName) {
|
|
16
20
|
const embedded = embeddedSeeds[fileName];
|
|
@@ -41,12 +45,23 @@ async function seedWorkflowDefinition(em, scope, fileName) {
|
|
|
41
45
|
organizationId: scope.organizationId
|
|
42
46
|
});
|
|
43
47
|
if (existing) {
|
|
48
|
+
const seedStepCount = seed.definition.steps.length;
|
|
49
|
+
const existingStepCount = existing.definition.steps.length;
|
|
50
|
+
const seedTransitionCount = seed.definition.transitions.length;
|
|
51
|
+
const existingTransitionCount = existing.definition.transitions.length;
|
|
52
|
+
const seedHasTransitionPreConditions = seed.definition.transitions.some(
|
|
53
|
+
(t) => t.preConditions && t.preConditions.length > 0
|
|
54
|
+
);
|
|
55
|
+
const existingHasTransitionPreConditions = existing.definition.transitions.some(
|
|
56
|
+
(t) => t.preConditions && t.preConditions.length > 0
|
|
57
|
+
);
|
|
44
58
|
const seedStartStep = seed.definition.steps.find((s) => s.stepType === "START");
|
|
45
59
|
const existingStartStep = existing.definition.steps.find((s) => s.stepType === "START");
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
60
|
+
const seedHasStartPreConditions = seedStartStep?.preConditions && seedStartStep.preConditions.length > 0;
|
|
61
|
+
const existingHasStartPreConditions = existingStartStep?.preConditions && existingStartStep.preConditions.length > 0;
|
|
62
|
+
const needsUpdate = seedStepCount !== existingStepCount || seedTransitionCount !== existingTransitionCount || seedHasStartPreConditions && !existingHasStartPreConditions || seedHasTransitionPreConditions && !existingHasTransitionPreConditions;
|
|
63
|
+
if (needsUpdate) {
|
|
64
|
+
console.log(`[seed] Updating workflow ${workflowId} (steps: ${existingStepCount}\u2192${seedStepCount}, transitions: ${existingTransitionCount}\u2192${seedTransitionCount})`);
|
|
50
65
|
existing.definition = seed.definition;
|
|
51
66
|
await em.flush();
|
|
52
67
|
return true;
|
|
@@ -109,6 +124,8 @@ async function seedExampleWorkflows(em, scope) {
|
|
|
109
124
|
await seedGuardRules(em, scope, "guard-rules-example.json");
|
|
110
125
|
await seedWorkflowDefinition(em, scope, "sales-pipeline-definition.json");
|
|
111
126
|
await seedWorkflowDefinition(em, scope, "simple-approval-definition.json");
|
|
127
|
+
await seedGuardRules(em, scope, "order-approval-guard-rules.json");
|
|
128
|
+
await seedWorkflowDefinition(em, scope, "order-approval-definition.json");
|
|
112
129
|
}
|
|
113
130
|
export {
|
|
114
131
|
seedExampleWorkflows
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/workflows/lib/seeds.ts"],
|
|
4
|
-
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { WorkflowDefinition, type WorkflowDefinitionData } from '../data/entities'\nimport { BusinessRule, type RuleType } from '@open-mercato/core/modules/business_rules/data/entities'\nimport checkoutDemoDefinition from '../examples/checkout-demo-definition.json'\nimport guardRulesExample from '../examples/guard-rules-example.json'\nimport salesPipelineDefinition from '../examples/sales-pipeline-definition.json'\nimport simpleApprovalDefinition from '../examples/simple-approval-definition.json'\n\nexport type WorkflowSeedScope = { tenantId: string; organizationId: string }\n\ntype WorkflowSeedDefinition = {\n workflowId: string\n workflowName: string\n description?: string | null\n version?: number\n definition: WorkflowDefinitionData\n metadata?: Record<string, unknown> | null\n enabled?: boolean\n effectiveFrom?: string | null\n effectiveTo?: string | null\n createdBy?: string | null\n updatedBy?: string | null\n}\n\ntype GuardRuleSeed = {\n ruleId: string\n ruleName: string\n ruleType: RuleType\n entityType: string\n conditionExpression: unknown\n eventType?: string | null\n ruleCategory?: string | null\n description?: string | null\n successActions?: unknown\n failureActions?: unknown\n enabled?: boolean\n priority?: number\n version?: number\n effectiveFrom?: string | null\n effectiveTo?: string | null\n createdBy?: string | null\n updatedBy?: string | null\n tagsJson?: string[]\n labelsJson?: Record<string, string>\n}\n\nconst embeddedSeeds: Record<string, unknown> = {\n 'checkout-demo-definition.json': checkoutDemoDefinition,\n 'guard-rules-example.json': guardRulesExample,\n 'sales-pipeline-definition.json': salesPipelineDefinition,\n 'simple-approval-definition.json': simpleApprovalDefinition,\n}\n\nfunction readExampleJson<T>(fileName: string): T {\n const embedded = embeddedSeeds[fileName]\n if (embedded) {\n return embedded as T\n }\n const candidates = [\n path.join(__dirname, '..', 'examples', fileName),\n path.join(process.cwd(), 'packages', 'core', 'src', 'modules', 'workflows', 'examples', fileName),\n path.join(process.cwd(), 'src', 'modules', 'workflows', 'examples', fileName),\n ]\n const filePath = candidates.find((candidate) => fs.existsSync(candidate))\n if (!filePath) {\n throw new Error(`Missing workflow seed file: ${fileName}`)\n }\n return JSON.parse(fs.readFileSync(filePath, 'utf8')) as T\n}\n\nfunction requireString(value: unknown, label: string): string {\n if (typeof value === 'string' && value.trim().length > 0) return value\n throw new Error(`Invalid ${label} in workflow seed data.`)\n}\n\nasync function seedWorkflowDefinition(\n em: EntityManager,\n scope: WorkflowSeedScope,\n fileName: string,\n): Promise<boolean> {\n const seed = readExampleJson<WorkflowSeedDefinition>(fileName)\n const workflowId = requireString(seed.workflowId, 'workflowId')\n\n const existing = await em.findOne(WorkflowDefinition, {\n workflowId,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n\n if (existing) {\n // Check if the definition needs to be updated (
|
|
5
|
-
"mappings": "AACA,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,SAAS,0BAAuD;AAChE,SAAS,oBAAmC;AAC5C,OAAO,4BAA4B;AACnC,OAAO,uBAAuB;AAC9B,OAAO,6BAA6B;AACpC,OAAO,8BAA8B;
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { WorkflowDefinition, type WorkflowDefinitionData } from '../data/entities'\nimport { BusinessRule, type RuleType } from '@open-mercato/core/modules/business_rules/data/entities'\nimport checkoutDemoDefinition from '../examples/checkout-demo-definition.json'\nimport guardRulesExample from '../examples/guard-rules-example.json'\nimport salesPipelineDefinition from '../examples/sales-pipeline-definition.json'\nimport simpleApprovalDefinition from '../examples/simple-approval-definition.json'\nimport orderApprovalDefinition from '../examples/order-approval-definition.json'\nimport orderApprovalGuardRules from '../examples/order-approval-guard-rules.json'\n\nexport type WorkflowSeedScope = { tenantId: string; organizationId: string }\n\ntype WorkflowSeedDefinition = {\n workflowId: string\n workflowName: string\n description?: string | null\n version?: number\n definition: WorkflowDefinitionData\n metadata?: Record<string, unknown> | null\n enabled?: boolean\n effectiveFrom?: string | null\n effectiveTo?: string | null\n createdBy?: string | null\n updatedBy?: string | null\n}\n\ntype GuardRuleSeed = {\n ruleId: string\n ruleName: string\n ruleType: RuleType\n entityType: string\n conditionExpression: unknown\n eventType?: string | null\n ruleCategory?: string | null\n description?: string | null\n successActions?: unknown\n failureActions?: unknown\n enabled?: boolean\n priority?: number\n version?: number\n effectiveFrom?: string | null\n effectiveTo?: string | null\n createdBy?: string | null\n updatedBy?: string | null\n tagsJson?: string[]\n labelsJson?: Record<string, string>\n}\n\nconst embeddedSeeds: Record<string, unknown> = {\n 'checkout-demo-definition.json': checkoutDemoDefinition,\n 'guard-rules-example.json': guardRulesExample,\n 'sales-pipeline-definition.json': salesPipelineDefinition,\n 'simple-approval-definition.json': simpleApprovalDefinition,\n 'order-approval-definition.json': orderApprovalDefinition,\n 'order-approval-guard-rules.json': orderApprovalGuardRules,\n}\n\nfunction readExampleJson<T>(fileName: string): T {\n const embedded = embeddedSeeds[fileName]\n if (embedded) {\n return embedded as T\n }\n const candidates = [\n path.join(__dirname, '..', 'examples', fileName),\n path.join(process.cwd(), 'packages', 'core', 'src', 'modules', 'workflows', 'examples', fileName),\n path.join(process.cwd(), 'src', 'modules', 'workflows', 'examples', fileName),\n ]\n const filePath = candidates.find((candidate) => fs.existsSync(candidate))\n if (!filePath) {\n throw new Error(`Missing workflow seed file: ${fileName}`)\n }\n return JSON.parse(fs.readFileSync(filePath, 'utf8')) as T\n}\n\nfunction requireString(value: unknown, label: string): string {\n if (typeof value === 'string' && value.trim().length > 0) return value\n throw new Error(`Invalid ${label} in workflow seed data.`)\n}\n\nasync function seedWorkflowDefinition(\n em: EntityManager,\n scope: WorkflowSeedScope,\n fileName: string,\n): Promise<boolean> {\n const seed = readExampleJson<WorkflowSeedDefinition>(fileName)\n const workflowId = requireString(seed.workflowId, 'workflowId')\n\n const existing = await em.findOne(WorkflowDefinition, {\n workflowId,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n\n if (existing) {\n // Check if the definition needs to be updated by comparing steps and transitions\n const seedStepCount = seed.definition.steps.length\n const existingStepCount = existing.definition.steps.length\n const seedTransitionCount = seed.definition.transitions.length\n const existingTransitionCount = existing.definition.transitions.length\n\n // Check for preConditions on transitions\n const seedHasTransitionPreConditions = seed.definition.transitions.some(\n (t: any) => t.preConditions && t.preConditions.length > 0\n )\n const existingHasTransitionPreConditions = existing.definition.transitions.some(\n (t: any) => t.preConditions && t.preConditions.length > 0\n )\n\n // Check for preConditions on START step\n const seedStartStep = seed.definition.steps.find((s: any) => s.stepType === 'START')\n const existingStartStep = existing.definition.steps.find((s: any) => s.stepType === 'START')\n const seedHasStartPreConditions = seedStartStep?.preConditions && seedStartStep.preConditions.length > 0\n const existingHasStartPreConditions = existingStartStep?.preConditions && existingStartStep.preConditions.length > 0\n\n // Update if structure has changed\n const needsUpdate =\n seedStepCount !== existingStepCount ||\n seedTransitionCount !== existingTransitionCount ||\n (seedHasStartPreConditions && !existingHasStartPreConditions) ||\n (seedHasTransitionPreConditions && !existingHasTransitionPreConditions)\n\n if (needsUpdate) {\n console.log(`[seed] Updating workflow ${workflowId} (steps: ${existingStepCount}\u2192${seedStepCount}, transitions: ${existingTransitionCount}\u2192${seedTransitionCount})`)\n existing.definition = seed.definition\n await em.flush()\n return true\n }\n\n return false\n }\n\n const workflow = em.create(WorkflowDefinition, {\n ...seed,\n workflowId,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n em.persist(workflow)\n await em.flush()\n return true\n}\n\nasync function seedGuardRules(\n em: EntityManager,\n scope: WorkflowSeedScope,\n fileName: string,\n): Promise<{ seeded: number; skipped: number; updated: number }> {\n const seeds = readExampleJson<GuardRuleSeed[]>(fileName)\n if (!Array.isArray(seeds)) {\n throw new Error('Invalid guard rules seed data.')\n }\n\n let seeded = 0\n let skipped = 0\n let updated = 0\n for (const rule of seeds) {\n const ruleId = requireString(rule.ruleId, 'ruleId')\n const existing = await em.findOne(BusinessRule, {\n ruleId,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n if (existing) {\n // Check if entityType or eventType needs updating\n const needsUpdate = existing.entityType !== rule.entityType || existing.eventType !== rule.eventType\n if (needsUpdate) {\n console.log(`[seed] Updating business rule ${ruleId}: entityType=${rule.entityType}, eventType=${rule.eventType}`)\n existing.entityType = rule.entityType\n existing.eventType = rule.eventType ?? null\n updated += 1\n } else {\n skipped += 1\n }\n continue\n }\n const entry = em.create(BusinessRule, {\n ...rule,\n ruleId,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n em.persist(entry)\n seeded += 1\n }\n if (seeded > 0 || updated > 0) {\n await em.flush()\n }\n return { seeded, skipped, updated }\n}\n\nexport async function seedExampleWorkflows(em: EntityManager, scope: WorkflowSeedScope): Promise<void> {\n await seedWorkflowDefinition(em, scope, 'checkout-demo-definition.json')\n await seedGuardRules(em, scope, 'guard-rules-example.json')\n await seedWorkflowDefinition(em, scope, 'sales-pipeline-definition.json')\n await seedWorkflowDefinition(em, scope, 'simple-approval-definition.json')\n // Seed order approval guard rules before the workflow definition\n await seedGuardRules(em, scope, 'order-approval-guard-rules.json')\n await seedWorkflowDefinition(em, scope, 'order-approval-definition.json')\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -58,50 +58,60 @@ async function validateWorkflowStart(em, options) {
|
|
|
58
58
|
const errors = [];
|
|
59
59
|
const validatedRules = [];
|
|
60
60
|
for (const condition of preConditions) {
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
eventType: "validate_start",
|
|
61
|
+
const result = await ruleEngine.executeRuleByRuleId(em, {
|
|
62
|
+
ruleId: condition.ruleId,
|
|
63
|
+
// String identifier like "workflow_checkout_inventory_available"
|
|
65
64
|
data: {
|
|
66
65
|
workflowId,
|
|
67
66
|
workflowContext: context
|
|
68
67
|
},
|
|
69
68
|
tenantId,
|
|
70
69
|
organizationId,
|
|
70
|
+
entityType: `workflow:${workflowId}:start`,
|
|
71
|
+
entityId: "pre_start_validation",
|
|
72
|
+
eventType: "validate_start",
|
|
71
73
|
dryRun: true
|
|
72
74
|
// Don't log execution during validation
|
|
73
|
-
};
|
|
74
|
-
const rules = await ruleEngine.findApplicableRules(em, {
|
|
75
|
-
entityType: ruleContext.entityType,
|
|
76
|
-
eventType: ruleContext.eventType,
|
|
77
|
-
tenantId,
|
|
78
|
-
organizationId,
|
|
79
|
-
ruleType: "GUARD"
|
|
80
75
|
});
|
|
81
|
-
|
|
82
|
-
|
|
76
|
+
validatedRules.push({
|
|
77
|
+
ruleId: condition.ruleId,
|
|
78
|
+
passed: result.conditionResult,
|
|
79
|
+
executionTime: result.executionTime
|
|
80
|
+
});
|
|
81
|
+
if (result.error === "Rule not found") {
|
|
83
82
|
if (condition.required) {
|
|
84
83
|
errors.push({
|
|
85
84
|
ruleId: condition.ruleId,
|
|
86
|
-
message: getLocalizedMessage(condition, null, locale, `Business rule
|
|
85
|
+
message: getLocalizedMessage(condition, null, locale, `Business rule not found: ${condition.ruleId}`),
|
|
87
86
|
code: "RULE_NOT_FOUND"
|
|
88
87
|
});
|
|
89
|
-
validatedRules.push({ ruleId: condition.ruleId, passed: false });
|
|
90
88
|
}
|
|
91
89
|
continue;
|
|
92
90
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
91
|
+
if (result.error === "Rule is disabled") {
|
|
92
|
+
if (condition.required) {
|
|
93
|
+
errors.push({
|
|
94
|
+
ruleId: condition.ruleId,
|
|
95
|
+
message: getLocalizedMessage(condition, null, locale, `Business rule is disabled: ${result.ruleName}`),
|
|
96
|
+
code: "RULE_DISABLED"
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (result.error && condition.required) {
|
|
102
|
+
errors.push({
|
|
103
|
+
ruleId: condition.ruleId,
|
|
104
|
+
message: getLocalizedMessage(condition, null, locale, `Rule error: ${result.error}`),
|
|
105
|
+
code: "RULE_ERROR"
|
|
106
|
+
});
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
99
109
|
if (!result.conditionResult && condition.required) {
|
|
100
110
|
const message = getLocalizedMessage(
|
|
101
111
|
condition,
|
|
102
|
-
|
|
112
|
+
null,
|
|
103
113
|
locale,
|
|
104
|
-
`Pre-condition '${
|
|
114
|
+
`Pre-condition '${result.ruleName || condition.ruleId}' failed`
|
|
105
115
|
);
|
|
106
116
|
errors.push({
|
|
107
117
|
ruleId: condition.ruleId,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/workflows/lib/start-validator.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Workflows Module - Start Validator Service\n *\n * Validates pre-conditions on START step before workflow instance creation.\n * This enables guard rules that determine whether a workflow can be started\n * based on the initial context provided.\n */\n\nimport { EntityManager } from '@mikro-orm/core'\nimport { WorkflowDefinition } from '../data/entities'\nimport * as ruleEngine from '../../business_rules/lib/rule-engine'\nimport type { StartPreCondition } from '../data/validators'\n\n// ============================================================================\n// Types and Interfaces\n// ============================================================================\n\nexport interface ValidateStartOptions {\n workflowId: string\n version?: number\n context: Record<string, any>\n locale?: string\n tenantId: string\n organizationId: string\n}\n\nexport interface ValidationError {\n ruleId: string\n message: string\n code: string\n}\n\nexport interface ValidatedRule {\n ruleId: string\n passed: boolean\n executionTime?: number\n}\n\nexport interface ValidateStartResult {\n canStart: boolean\n errors: ValidationError[]\n validatedRules: ValidatedRule[]\n}\n\n// ============================================================================\n// Main Validation Function\n// ============================================================================\n\n/**\n * Validate if a workflow can be started with the given context\n *\n * @param em - Entity manager for database operations\n * @param options - Validation options including workflowId, context, and scope\n * @returns Validation result with canStart flag, errors, and validated rules\n */\nexport async function validateWorkflowStart(\n em: EntityManager,\n options: ValidateStartOptions\n): Promise<ValidateStartResult> {\n const { workflowId, version, context, locale = 'en', tenantId, organizationId } = options\n\n // Find workflow definition\n const definition = await findWorkflowDefinition(em, {\n workflowId,\n version,\n tenantId,\n organizationId,\n })\n\n if (!definition) {\n return {\n canStart: false,\n errors: [{\n ruleId: '_DEFINITION_NOT_FOUND',\n message: `Workflow definition not found: ${workflowId}`,\n code: 'DEFINITION_NOT_FOUND',\n }],\n validatedRules: [],\n }\n }\n\n if (!definition.enabled) {\n return {\n canStart: false,\n errors: [{\n ruleId: '_DEFINITION_DISABLED',\n message: `Workflow is disabled: ${workflowId}`,\n code: 'DEFINITION_DISABLED',\n }],\n validatedRules: [],\n }\n }\n\n // Find START step and get pre-conditions\n const startStep = definition.definition.steps.find(\n (s: any) => s.stepType === 'START'\n )\n\n if (!startStep) {\n return {\n canStart: false,\n errors: [{\n ruleId: '_NO_START_STEP',\n message: 'Workflow has no START step',\n code: 'INVALID_DEFINITION',\n }],\n validatedRules: [],\n }\n }\n\n const preConditions: StartPreCondition[] = startStep.preConditions || []\n\n console.log('[start-validator] START step:', JSON.stringify(startStep, null, 2))\n console.log('[start-validator] preConditions:', preConditions.length, JSON.stringify(preConditions))\n\n // If no pre-conditions, workflow can start\n if (preConditions.length === 0) {\n console.log('[start-validator] No pre-conditions defined, allowing start')\n return {\n canStart: true,\n errors: [],\n validatedRules: [],\n }\n }\n\n // Evaluate each pre-condition using rule engine\n const errors: ValidationError[] = []\n const validatedRules: ValidatedRule[] = []\n\n for (const condition of preConditions) {\n const
|
|
5
|
-
"mappings": "AASA,SAAS,0BAA0B;AACnC,YAAY,gBAAgB;AA6C5B,eAAsB,sBACpB,IACA,SAC8B;AAC9B,QAAM,EAAE,YAAY,SAAS,SAAS,SAAS,MAAM,UAAU,eAAe,IAAI;AAGlF,QAAM,aAAa,MAAM,uBAAuB,IAAI;AAAA,IAClD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,MAAI,CAAC,YAAY;AACf,WAAO;AAAA,MACL,UAAU;AAAA,MACV,QAAQ,CAAC;AAAA,QACP,QAAQ;AAAA,QACR,SAAS,kCAAkC,UAAU;AAAA,QACrD,MAAM;AAAA,MACR,CAAC;AAAA,MACD,gBAAgB,CAAC;AAAA,IACnB;AAAA,EACF;AAEA,MAAI,CAAC,WAAW,SAAS;AACvB,WAAO;AAAA,MACL,UAAU;AAAA,MACV,QAAQ,CAAC;AAAA,QACP,QAAQ;AAAA,QACR,SAAS,yBAAyB,UAAU;AAAA,QAC5C,MAAM;AAAA,MACR,CAAC;AAAA,MACD,gBAAgB,CAAC;AAAA,IACnB;AAAA,EACF;AAGA,QAAM,YAAY,WAAW,WAAW,MAAM;AAAA,IAC5C,CAAC,MAAW,EAAE,aAAa;AAAA,EAC7B;AAEA,MAAI,CAAC,WAAW;AACd,WAAO;AAAA,MACL,UAAU;AAAA,MACV,QAAQ,CAAC;AAAA,QACP,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,MAAM;AAAA,MACR,CAAC;AAAA,MACD,gBAAgB,CAAC;AAAA,IACnB;AAAA,EACF;AAEA,QAAM,gBAAqC,UAAU,iBAAiB,CAAC;AAEvE,UAAQ,IAAI,iCAAiC,KAAK,UAAU,WAAW,MAAM,CAAC,CAAC;AAC/E,UAAQ,IAAI,oCAAoC,cAAc,QAAQ,KAAK,UAAU,aAAa,CAAC;AAGnG,MAAI,cAAc,WAAW,GAAG;AAC9B,YAAQ,IAAI,6DAA6D;AACzE,WAAO;AAAA,MACL,UAAU;AAAA,MACV,QAAQ,CAAC;AAAA,MACT,gBAAgB,CAAC;AAAA,IACnB;AAAA,EACF;AAGA,QAAM,SAA4B,CAAC;AACnC,QAAM,iBAAkC,CAAC;AAEzC,aAAW,aAAa,eAAe;
|
|
4
|
+
"sourcesContent": ["/**\n * Workflows Module - Start Validator Service\n *\n * Validates pre-conditions on START step before workflow instance creation.\n * This enables guard rules that determine whether a workflow can be started\n * based on the initial context provided.\n */\n\nimport { EntityManager } from '@mikro-orm/core'\nimport { WorkflowDefinition } from '../data/entities'\nimport * as ruleEngine from '../../business_rules/lib/rule-engine'\nimport type { StartPreCondition } from '../data/validators'\n\n// ============================================================================\n// Types and Interfaces\n// ============================================================================\n\nexport interface ValidateStartOptions {\n workflowId: string\n version?: number\n context: Record<string, any>\n locale?: string\n tenantId: string\n organizationId: string\n}\n\nexport interface ValidationError {\n ruleId: string\n message: string\n code: string\n}\n\nexport interface ValidatedRule {\n ruleId: string\n passed: boolean\n executionTime?: number\n}\n\nexport interface ValidateStartResult {\n canStart: boolean\n errors: ValidationError[]\n validatedRules: ValidatedRule[]\n}\n\n// ============================================================================\n// Main Validation Function\n// ============================================================================\n\n/**\n * Validate if a workflow can be started with the given context\n *\n * @param em - Entity manager for database operations\n * @param options - Validation options including workflowId, context, and scope\n * @returns Validation result with canStart flag, errors, and validated rules\n */\nexport async function validateWorkflowStart(\n em: EntityManager,\n options: ValidateStartOptions\n): Promise<ValidateStartResult> {\n const { workflowId, version, context, locale = 'en', tenantId, organizationId } = options\n\n // Find workflow definition\n const definition = await findWorkflowDefinition(em, {\n workflowId,\n version,\n tenantId,\n organizationId,\n })\n\n if (!definition) {\n return {\n canStart: false,\n errors: [{\n ruleId: '_DEFINITION_NOT_FOUND',\n message: `Workflow definition not found: ${workflowId}`,\n code: 'DEFINITION_NOT_FOUND',\n }],\n validatedRules: [],\n }\n }\n\n if (!definition.enabled) {\n return {\n canStart: false,\n errors: [{\n ruleId: '_DEFINITION_DISABLED',\n message: `Workflow is disabled: ${workflowId}`,\n code: 'DEFINITION_DISABLED',\n }],\n validatedRules: [],\n }\n }\n\n // Find START step and get pre-conditions\n const startStep = definition.definition.steps.find(\n (s: any) => s.stepType === 'START'\n )\n\n if (!startStep) {\n return {\n canStart: false,\n errors: [{\n ruleId: '_NO_START_STEP',\n message: 'Workflow has no START step',\n code: 'INVALID_DEFINITION',\n }],\n validatedRules: [],\n }\n }\n\n const preConditions: StartPreCondition[] = startStep.preConditions || []\n\n console.log('[start-validator] START step:', JSON.stringify(startStep, null, 2))\n console.log('[start-validator] preConditions:', preConditions.length, JSON.stringify(preConditions))\n\n // If no pre-conditions, workflow can start\n if (preConditions.length === 0) {\n console.log('[start-validator] No pre-conditions defined, allowing start')\n return {\n canStart: true,\n errors: [],\n validatedRules: [],\n }\n }\n\n // Evaluate each pre-condition using rule engine\n const errors: ValidationError[] = []\n const validatedRules: ValidatedRule[] = []\n\n for (const condition of preConditions) {\n // Execute rule directly by string rule_id\n const result = await ruleEngine.executeRuleByRuleId(em, {\n ruleId: condition.ruleId, // String identifier like \"workflow_checkout_inventory_available\"\n data: {\n workflowId,\n workflowContext: context,\n },\n tenantId,\n organizationId,\n entityType: `workflow:${workflowId}:start`,\n entityId: 'pre_start_validation',\n eventType: 'validate_start',\n dryRun: true, // Don't log execution during validation\n })\n\n validatedRules.push({\n ruleId: condition.ruleId,\n passed: result.conditionResult,\n executionTime: result.executionTime,\n })\n\n // Handle rule not found\n if (result.error === 'Rule not found') {\n if (condition.required) {\n errors.push({\n ruleId: condition.ruleId,\n message: getLocalizedMessage(condition, null, locale, `Business rule not found: ${condition.ruleId}`),\n code: 'RULE_NOT_FOUND',\n })\n }\n continue\n }\n\n // Handle disabled rule\n if (result.error === 'Rule is disabled') {\n if (condition.required) {\n errors.push({\n ruleId: condition.ruleId,\n message: getLocalizedMessage(condition, null, locale, `Business rule is disabled: ${result.ruleName}`),\n code: 'RULE_DISABLED',\n })\n }\n continue\n }\n\n // Handle other errors (not yet effective, expired, etc.)\n if (result.error && condition.required) {\n errors.push({\n ruleId: condition.ruleId,\n message: getLocalizedMessage(condition, null, locale, `Rule error: ${result.error}`),\n code: 'RULE_ERROR',\n })\n continue\n }\n\n // Handle condition failure\n if (!result.conditionResult && condition.required) {\n // Get localized message from condition or use default with rule name\n const message = getLocalizedMessage(\n condition,\n null,\n locale,\n `Pre-condition '${result.ruleName || condition.ruleId}' failed`\n )\n errors.push({\n ruleId: condition.ruleId,\n message,\n code: 'PRE_CONDITION_FAILED',\n })\n }\n }\n\n return {\n canStart: errors.length === 0,\n errors,\n validatedRules,\n }\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Get localized message with fallback chain\n *\n * Priority:\n * 1. condition.validationMessage[locale]\n * 2. condition.validationMessage['en']\n * 3. rule.failureActions[BLOCK_TRANSITION].config.message\n * 4. rule.description\n * 5. defaultMessage\n */\nfunction getLocalizedMessage(\n condition: StartPreCondition,\n rule: any | null,\n locale: string,\n defaultMessage: string\n): string {\n // Priority 1: Localized message in condition definition (requested locale)\n if (condition.validationMessage?.[locale]) {\n return condition.validationMessage[locale]\n }\n\n // Priority 2: English fallback in condition\n if (condition.validationMessage?.['en']) {\n return condition.validationMessage['en']\n }\n\n if (rule) {\n // Priority 3: Message from rule's failureActions\n if (rule.failureActions && Array.isArray(rule.failureActions)) {\n const blockAction = rule.failureActions.find(\n (a: any) => a.type === 'BLOCK_TRANSITION'\n )\n if (blockAction?.config?.message) {\n return blockAction.config.message\n }\n }\n\n // Priority 4: Rule description\n if (rule.description) {\n return rule.description\n }\n }\n\n // Priority 5: Default message\n return defaultMessage\n}\n\n/**\n * Find workflow definition by ID and optional version\n */\nasync function findWorkflowDefinition(\n em: EntityManager,\n options: {\n workflowId: string\n version?: number\n tenantId: string\n organizationId: string\n }\n): Promise<WorkflowDefinition | null> {\n const { workflowId, version, tenantId, organizationId } = options\n\n const where: any = {\n workflowId,\n tenantId,\n organizationId,\n deletedAt: null,\n }\n\n if (version !== undefined) {\n where.version = version\n }\n\n // If no version specified, get latest enabled version\n if (version === undefined) {\n where.enabled = true\n return em.findOne(WorkflowDefinition, where, {\n orderBy: { version: 'DESC' },\n })\n }\n\n return em.findOne(WorkflowDefinition, where)\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -99,19 +99,43 @@ async function findValidTransitions(em, instance, fromStepId, context) {
|
|
|
99
99
|
if (!definition) {
|
|
100
100
|
return [];
|
|
101
101
|
}
|
|
102
|
-
const transitions = (definition.definition.transitions || []).filter(
|
|
103
|
-
(t) => t.fromStepId === fromStepId
|
|
104
|
-
);
|
|
102
|
+
const transitions = (definition.definition.transitions || []).filter((t) => t.fromStepId === fromStepId).sort((a, b) => (b.priority || 0) - (a.priority || 0));
|
|
105
103
|
const results = [];
|
|
106
104
|
for (const transition of transitions) {
|
|
107
|
-
const
|
|
105
|
+
const conditionResult = await evaluateTransition(
|
|
108
106
|
em,
|
|
109
107
|
instance,
|
|
110
108
|
fromStepId,
|
|
111
109
|
transition.toStepId,
|
|
112
110
|
context
|
|
113
111
|
);
|
|
114
|
-
|
|
112
|
+
if (!conditionResult.isValid) {
|
|
113
|
+
results.push(conditionResult);
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const preConditions = transition.preConditions || [];
|
|
117
|
+
if (preConditions.length > 0) {
|
|
118
|
+
const preConditionsResult = await evaluatePreConditions(
|
|
119
|
+
em,
|
|
120
|
+
instance,
|
|
121
|
+
transition,
|
|
122
|
+
context
|
|
123
|
+
);
|
|
124
|
+
if (!preConditionsResult.allowed) {
|
|
125
|
+
const failedRules = preConditionsResult.executedRules.filter((r) => !r.conditionResult).map((r) => r.rule.ruleId || r.rule.ruleName);
|
|
126
|
+
results.push({
|
|
127
|
+
isValid: false,
|
|
128
|
+
transition,
|
|
129
|
+
reason: `Pre-conditions failed: ${failedRules.join(", ")}`,
|
|
130
|
+
failedConditions: failedRules
|
|
131
|
+
});
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
results.push({
|
|
136
|
+
...conditionResult,
|
|
137
|
+
transition
|
|
138
|
+
});
|
|
115
139
|
}
|
|
116
140
|
return results;
|
|
117
141
|
} catch (error) {
|
|
@@ -415,28 +439,74 @@ async function evaluatePreConditions(em, instance, transition, context) {
|
|
|
415
439
|
totalExecutionTime: 0
|
|
416
440
|
};
|
|
417
441
|
}
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
442
|
+
const preConditions = transition.preConditions || [];
|
|
443
|
+
if (preConditions.length === 0) {
|
|
444
|
+
return {
|
|
445
|
+
allowed: true,
|
|
446
|
+
executedRules: [],
|
|
447
|
+
totalExecutionTime: 0
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
const startTime = Date.now();
|
|
451
|
+
const executedRules = [];
|
|
452
|
+
const errors = [];
|
|
453
|
+
let allowed = true;
|
|
454
|
+
for (const condition of preConditions) {
|
|
455
|
+
const result = await ruleEngine.executeRuleByRuleId(em, {
|
|
456
|
+
ruleId: condition.ruleId,
|
|
457
|
+
// String identifier
|
|
458
|
+
data: {
|
|
459
|
+
workflowInstanceId: instance.id,
|
|
460
|
+
workflowId: definition.workflowId,
|
|
461
|
+
fromStepId: transition.fromStepId,
|
|
462
|
+
toStepId: transition.toStepId,
|
|
463
|
+
workflowContext: {
|
|
464
|
+
...instance.context,
|
|
465
|
+
...context.workflowContext
|
|
466
|
+
},
|
|
467
|
+
triggerData: context.triggerData
|
|
430
468
|
},
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
469
|
+
user: context.userId ? { id: context.userId } : void 0,
|
|
470
|
+
tenantId: instance.tenantId,
|
|
471
|
+
organizationId: instance.organizationId,
|
|
472
|
+
executedBy: context.userId,
|
|
473
|
+
entityType: `workflow:${definition.workflowId}:transition`,
|
|
474
|
+
entityId: transition.transitionId || `${transition.fromStepId}->${transition.toStepId}`,
|
|
475
|
+
eventType: "pre_transition"
|
|
476
|
+
});
|
|
477
|
+
const ruleResult = {
|
|
478
|
+
rule: {
|
|
479
|
+
ruleId: result.ruleId,
|
|
480
|
+
ruleName: result.ruleName,
|
|
481
|
+
ruleType: "GUARD"
|
|
482
|
+
},
|
|
483
|
+
conditionResult: result.conditionResult,
|
|
484
|
+
actionsExecuted: result.actionsExecuted,
|
|
485
|
+
executionTime: result.executionTime,
|
|
486
|
+
error: result.error,
|
|
487
|
+
logId: result.logId
|
|
488
|
+
};
|
|
489
|
+
executedRules.push(ruleResult);
|
|
490
|
+
if (result.error) {
|
|
491
|
+
const isRequired2 = condition.required !== false;
|
|
492
|
+
if (isRequired2) {
|
|
493
|
+
allowed = false;
|
|
494
|
+
errors.push(`Rule '${result.ruleId}': ${result.error}`);
|
|
495
|
+
}
|
|
496
|
+
continue;
|
|
497
|
+
}
|
|
498
|
+
const isRequired = condition.required !== false;
|
|
499
|
+
if (isRequired && !result.conditionResult) {
|
|
500
|
+
allowed = false;
|
|
501
|
+
errors.push(`Pre-condition '${result.ruleName || result.ruleId}' failed`);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return {
|
|
505
|
+
allowed,
|
|
506
|
+
executedRules,
|
|
507
|
+
totalExecutionTime: Date.now() - startTime,
|
|
508
|
+
errors: errors.length > 0 ? errors : void 0
|
|
437
509
|
};
|
|
438
|
-
const result = await ruleEngine.executeRules(em, ruleContext);
|
|
439
|
-
return result;
|
|
440
510
|
} catch (error) {
|
|
441
511
|
console.error("Error evaluating pre-conditions:", error);
|
|
442
512
|
return {
|
|
@@ -459,28 +529,70 @@ async function evaluatePostConditions(em, instance, transition, context) {
|
|
|
459
529
|
totalExecutionTime: 0
|
|
460
530
|
};
|
|
461
531
|
}
|
|
462
|
-
const
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
532
|
+
const postConditions = transition.postConditions || [];
|
|
533
|
+
if (postConditions.length === 0) {
|
|
534
|
+
return {
|
|
535
|
+
allowed: true,
|
|
536
|
+
executedRules: [],
|
|
537
|
+
totalExecutionTime: 0
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
const startTime = Date.now();
|
|
541
|
+
const executedRules = [];
|
|
542
|
+
const errors = [];
|
|
543
|
+
let allowed = true;
|
|
544
|
+
for (const condition of postConditions) {
|
|
545
|
+
const result = await ruleEngine.executeRuleByRuleId(em, {
|
|
546
|
+
ruleId: condition.ruleId,
|
|
547
|
+
// String identifier
|
|
548
|
+
data: {
|
|
549
|
+
workflowInstanceId: instance.id,
|
|
550
|
+
workflowId: definition.workflowId,
|
|
551
|
+
fromStepId: transition.fromStepId,
|
|
552
|
+
toStepId: transition.toStepId,
|
|
553
|
+
workflowContext: {
|
|
554
|
+
...instance.context,
|
|
555
|
+
...context.workflowContext
|
|
556
|
+
},
|
|
557
|
+
triggerData: context.triggerData
|
|
474
558
|
},
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
559
|
+
user: context.userId ? { id: context.userId } : void 0,
|
|
560
|
+
tenantId: instance.tenantId,
|
|
561
|
+
organizationId: instance.organizationId,
|
|
562
|
+
executedBy: context.userId,
|
|
563
|
+
entityType: `workflow:${definition.workflowId}:transition`,
|
|
564
|
+
entityId: transition.transitionId || `${transition.fromStepId}->${transition.toStepId}`,
|
|
565
|
+
eventType: "post_transition"
|
|
566
|
+
});
|
|
567
|
+
const ruleResult = {
|
|
568
|
+
rule: {
|
|
569
|
+
ruleId: result.ruleId,
|
|
570
|
+
ruleName: result.ruleName,
|
|
571
|
+
ruleType: "GUARD"
|
|
572
|
+
},
|
|
573
|
+
conditionResult: result.conditionResult,
|
|
574
|
+
actionsExecuted: result.actionsExecuted,
|
|
575
|
+
executionTime: result.executionTime,
|
|
576
|
+
error: result.error,
|
|
577
|
+
logId: result.logId
|
|
578
|
+
};
|
|
579
|
+
executedRules.push(ruleResult);
|
|
580
|
+
if (result.error) {
|
|
581
|
+
errors.push(`Rule '${result.ruleId}': ${result.error}`);
|
|
582
|
+
allowed = false;
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
if (!result.conditionResult) {
|
|
586
|
+
allowed = false;
|
|
587
|
+
errors.push(`Post-condition '${result.ruleName || result.ruleId}' failed`);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
return {
|
|
591
|
+
allowed,
|
|
592
|
+
executedRules,
|
|
593
|
+
totalExecutionTime: Date.now() - startTime,
|
|
594
|
+
errors: errors.length > 0 ? errors : void 0
|
|
481
595
|
};
|
|
482
|
-
const result = await ruleEngine.executeRules(em, ruleContext);
|
|
483
|
-
return result;
|
|
484
596
|
} catch (error) {
|
|
485
597
|
console.error("Error evaluating post-conditions:", error);
|
|
486
598
|
return {
|