@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
@@ -227,84 +227,22 @@ async function executeEmitEvent(config, context, container) {
227
227
  return { emitted: true, eventName, payload: enrichedPayload };
228
228
  }
229
229
  async function executeUpdateEntity(em, config, context, container) {
230
- const { commandId, input, statusDictionary } = config;
231
- if (!commandId) {
232
- throw new Error('UPDATE_ENTITY requires "commandId" field (e.g., "sales.documents.update")');
230
+ const { entityType, entityId, updates } = config;
231
+ if (!entityType || !entityId || !updates) {
232
+ throw new Error('UPDATE_ENTITY requires "entityType", "entityId", and "updates" fields');
233
233
  }
234
- if (!input || typeof input !== "object") {
235
- throw new Error('UPDATE_ENTITY requires "input" object with entity data');
234
+ const queryEngine = container.resolve("queryEngine");
235
+ if (!queryEngine || typeof queryEngine.update !== "function") {
236
+ throw new Error("Query engine not available in container");
236
237
  }
237
- const commandBus = container.resolve("commandBus");
238
- if (!commandBus || typeof commandBus.execute !== "function") {
239
- throw new Error("CommandBus not available in container");
240
- }
241
- let finalInput = { ...input };
242
- if (finalInput.statusValue && statusDictionary) {
243
- const statusEntryId = await resolveDictionaryEntryId(
244
- em,
245
- statusDictionary,
246
- finalInput.statusValue,
247
- context.workflowInstance.tenantId,
248
- context.workflowInstance.organizationId
249
- );
250
- if (statusEntryId) {
251
- finalInput.statusEntryId = statusEntryId;
252
- }
253
- delete finalInput.statusValue;
254
- }
255
- const SYSTEM_USER_ID = "00000000-0000-0000-0000-000000000000";
256
- const ctx = {
257
- container,
258
- auth: {
259
- sub: context.userId || SYSTEM_USER_ID,
260
- tenantId: context.workflowInstance.tenantId,
261
- orgId: context.workflowInstance.organizationId,
262
- isSuperAdmin: false
263
- },
264
- organizationScope: null,
265
- selectedOrganizationId: context.workflowInstance.organizationId,
266
- organizationIds: context.workflowInstance.organizationId ? [context.workflowInstance.organizationId] : null
267
- };
268
- const { result, logEntry } = await commandBus.execute(commandId, {
269
- input: finalInput,
270
- ctx
238
+ await queryEngine.update({
239
+ entity: entityType,
240
+ where: { id: entityId },
241
+ data: updates,
242
+ tenantId: context.workflowInstance.tenantId,
243
+ organizationId: context.workflowInstance.organizationId
271
244
  });
272
- return {
273
- executed: true,
274
- commandId,
275
- result,
276
- logEntryId: logEntry?.id
277
- };
278
- }
279
- async function resolveDictionaryEntryId(em, dictionaryKey, value, tenantId, organizationId) {
280
- try {
281
- const { Dictionary, DictionaryEntry } = await import("@open-mercato/core/modules/dictionaries/data/entities");
282
- const dictionary = await em.findOne(Dictionary, {
283
- key: dictionaryKey,
284
- tenantId,
285
- organizationId,
286
- deletedAt: null
287
- });
288
- if (!dictionary) {
289
- console.warn(`[UPDATE_ENTITY] Dictionary not found: ${dictionaryKey}`);
290
- return null;
291
- }
292
- const normalizedValue = value.toLowerCase().trim();
293
- const entry = await em.findOne(DictionaryEntry, {
294
- dictionary: dictionary.id,
295
- tenantId,
296
- organizationId,
297
- normalizedValue
298
- });
299
- if (!entry) {
300
- console.warn(`[UPDATE_ENTITY] Dictionary entry not found: ${dictionaryKey}/${value}`);
301
- return null;
302
- }
303
- return entry.id;
304
- } catch (error) {
305
- console.error(`[UPDATE_ENTITY] Error resolving dictionary entry:`, error);
306
- return null;
307
- }
245
+ return { updated: true, entityType, entityId, updates };
308
246
  }
309
247
  async function executeCallWebhook(config, context) {
310
248
  const { url, method = "POST", headers = {}, body } = config;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/workflows/lib/activity-executor.ts"],
4
- "sourcesContent": ["/**\n * Workflows Module - Activity Executor Service\n *\n * Executes workflow activities (send email, call API, emit events, etc.)\n * - Supports multiple activity types\n * - Implements retry logic with exponential backoff\n * - Handles timeouts\n * - Variable interpolation from workflow context\n *\n * Functional API (no classes) following Open Mercato conventions.\n */\n\nimport { EntityManager } from '@mikro-orm/core'\nimport type { AwilixContainer } from 'awilix'\nimport { WorkflowInstance } from '../data/entities'\nimport { createQueue, Queue } from '@open-mercato/queue'\nimport { WorkflowActivityJob, WORKFLOW_ACTIVITIES_QUEUE_NAME } from './activity-queue-types'\nimport { logWorkflowEvent } from './event-logger'\n\n// ============================================================================\n// Types and Interfaces\n// ============================================================================\n\nexport type ActivityType =\n | 'SEND_EMAIL'\n | 'CALL_API'\n | 'EMIT_EVENT'\n | 'UPDATE_ENTITY'\n | 'CALL_WEBHOOK'\n | 'EXECUTE_FUNCTION'\n\nexport interface ActivityDefinition {\n activityId: string // Unique identifier for activity\n activityName?: string // Optional, for debugging/logging\n activityType: ActivityType\n config: any\n async?: boolean // Flag to execute activity asynchronously via queue\n retryPolicy?: RetryPolicy\n timeoutMs?: number\n compensate?: boolean // Flag to execute compensation on failure\n}\n\nexport interface RetryPolicy {\n maxAttempts: number\n initialIntervalMs: number\n backoffCoefficient: number\n maxIntervalMs: number\n}\n\nexport interface ActivityContext {\n workflowInstance: WorkflowInstance\n workflowContext: Record<string, any>\n stepContext?: Record<string, any>\n stepInstanceId?: string\n transitionId?: string\n userId?: string\n}\n\nexport interface ActivityExecutionResult {\n activityId: string\n activityName?: string\n activityType: ActivityType\n success: boolean\n output?: any\n error?: string\n retryCount: number\n executionTimeMs: number\n async?: boolean // Marks activity as async (queued)\n jobId?: string // Queue job ID for async activities\n}\n\nexport class ActivityExecutionError extends Error {\n constructor(\n message: string,\n public activityType: ActivityType,\n public activityName?: string,\n public details?: any\n ) {\n super(message)\n this.name = 'ActivityExecutionError'\n }\n}\n\n// ============================================================================\n// Queue Integration for Async Activities\n// ============================================================================\n\nlet activityQueue: Queue<WorkflowActivityJob> | null = null\n\n/**\n * Get or create the activity queue (lazy initialization)\n */\nfunction getActivityQueue(): Queue<WorkflowActivityJob> {\n if (!activityQueue) {\n if (process.env.QUEUE_STRATEGY === 'async') {\n activityQueue = createQueue<WorkflowActivityJob>(\n WORKFLOW_ACTIVITIES_QUEUE_NAME,\n 'async',\n {\n connection: {\n url: process.env.REDIS_URL || process.env.QUEUE_REDIS_URL,\n },\n concurrency: parseInt(process.env.WORKFLOW_WORKER_CONCURRENCY || '5'),\n }\n )\n } else {\n activityQueue = createQueue<WorkflowActivityJob>(\n WORKFLOW_ACTIVITIES_QUEUE_NAME,\n 'local',\n {\n baseDir: process.env.QUEUE_BASE_DIR || '.queue',\n }\n )\n }\n }\n\n return activityQueue\n}\n\n/**\n * Enqueue an activity for background execution\n *\n * @param em - Entity manager\n * @param activity - Activity definition\n * @param context - Execution context\n * @returns Job ID\n */\nexport async function enqueueActivity(\n em: EntityManager,\n activity: ActivityDefinition,\n context: ActivityContext\n): Promise<string> {\n const { workflowInstance, workflowContext, stepContext, transitionId, stepInstanceId } =\n context\n\n // Interpolate config variables NOW (before queuing)\n const interpolatedConfig = interpolateVariables(activity.config, workflowContext, workflowInstance)\n\n // Create job payload\n const job: WorkflowActivityJob = {\n workflowInstanceId: workflowInstance.id,\n stepInstanceId,\n transitionId,\n activityId: activity.activityId,\n activityName: activity.activityName || activity.activityType,\n activityType: activity.activityType,\n activityConfig: interpolatedConfig,\n workflowContext,\n stepContext,\n retryPolicy: activity.retryPolicy,\n timeoutMs: activity.timeoutMs,\n tenantId: workflowInstance.tenantId,\n organizationId: workflowInstance.organizationId,\n userId: context.userId,\n }\n\n // Enqueue to queue\n const queue = getActivityQueue()\n const jobId = await queue.enqueue(job)\n\n // Log event\n await logWorkflowEvent(em, {\n workflowInstanceId: workflowInstance.id,\n stepInstanceId,\n eventType: 'ACTIVITY_QUEUED',\n eventData: {\n activityId: activity.activityId,\n activityName: activity.activityName,\n activityType: activity.activityType,\n async: true,\n jobId,\n },\n tenantId: workflowInstance.tenantId,\n organizationId: workflowInstance.organizationId,\n })\n\n return jobId\n}\n\n// ============================================================================\n// Main Activity Execution Functions\n// ============================================================================\n\n/**\n * Execute a single activity with retry logic and timeout\n *\n * @param em - Entity manager\n * @param container - DI container\n * @param activity - Activity definition\n * @param context - Execution context\n * @returns Execution result\n */\nexport async function executeActivity(\n em: EntityManager,\n container: AwilixContainer,\n activity: ActivityDefinition,\n context: ActivityContext\n): Promise<ActivityExecutionResult> {\n const retryPolicy = activity.retryPolicy || {\n maxAttempts: 1,\n initialIntervalMs: 0,\n backoffCoefficient: 1,\n maxIntervalMs: 0,\n }\n\n let lastError: any\n let retryCount = 0\n\n for (let attempt = 0; attempt < retryPolicy.maxAttempts; attempt++) {\n try {\n const startTime = Date.now()\n\n // Execute with timeout if specified\n const result = activity.timeoutMs\n ? await executeWithTimeout(\n () => executeActivityByType(em, container, activity, context),\n activity.timeoutMs\n )\n : await executeActivityByType(em, container, activity, context)\n\n const executionTimeMs = Date.now() - startTime\n\n return {\n activityId: activity.activityId,\n activityName: activity.activityName,\n activityType: activity.activityType,\n success: true,\n output: result,\n retryCount: attempt,\n executionTimeMs,\n async: activity.async || false,\n }\n } catch (error) {\n lastError = error\n retryCount = attempt + 1\n\n // If not the last attempt, apply backoff and retry\n if (attempt < retryPolicy.maxAttempts - 1) {\n const backoff = calculateBackoff(\n retryPolicy.initialIntervalMs,\n retryPolicy.backoffCoefficient,\n attempt,\n retryPolicy.maxIntervalMs\n )\n\n await sleep(backoff)\n }\n }\n }\n\n // All retries exhausted\n const errorMessage = lastError instanceof Error ? lastError.message : String(lastError)\n\n return {\n activityId: activity.activityId,\n activityName: activity.activityName,\n activityType: activity.activityType,\n success: false,\n error: `Activity failed after ${retryCount} attempts: ${errorMessage}`,\n retryCount,\n executionTimeMs: 0,\n async: activity.async || false,\n }\n}\n\n/**\n * Execute multiple activities in sequence\n * Supports both synchronous and asynchronous (queued) execution\n *\n * @param em - Entity manager\n * @param container - DI container\n * @param activities - Array of activity definitions\n * @param context - Execution context\n * @returns Array of execution results\n */\nexport async function executeActivities(\n em: EntityManager,\n container: AwilixContainer,\n activities: ActivityDefinition[],\n context: ActivityContext\n): Promise<ActivityExecutionResult[]> {\n const results: ActivityExecutionResult[] = []\n\n for (let i = 0; i < activities.length; i++) {\n const activity = activities[i]\n\n // Check if activity should run async\n if (activity.async) {\n // Enqueue for background execution\n const jobId = await enqueueActivity(em, activity, context)\n\n results.push({\n activityId: activity.activityId,\n activityName: activity.activityName,\n activityType: activity.activityType,\n success: true, // Queued successfully\n async: true,\n jobId,\n retryCount: 0,\n executionTimeMs: 0,\n })\n } else {\n // Execute synchronously (existing logic)\n const result = await executeActivity(em, container, activity, context)\n results.push(result)\n\n // Stop execution if activity fails (fail-fast)\n if (!result.success) {\n break\n }\n\n // Update workflow context with activity output\n if (result.output && typeof result.output === 'object') {\n const key = activity.activityName || activity.activityType\n context.workflowContext = {\n ...context.workflowContext,\n [key]: result.output,\n }\n }\n }\n }\n\n return results\n}\n\n// ============================================================================\n// Activity Type Handlers\n// ============================================================================\n\n/**\n * Execute activity based on its type\n */\nasync function executeActivityByType(\n em: EntityManager,\n container: AwilixContainer,\n activity: ActivityDefinition,\n context: ActivityContext\n): Promise<any> {\n // Interpolate config variables from context (including workflow metadata)\n const interpolatedConfig = interpolateVariables(activity.config, context.workflowContext, context.workflowInstance)\n\n switch (activity.activityType) {\n case 'SEND_EMAIL':\n return await executeSendEmail(interpolatedConfig, context, container)\n\n case 'CALL_API':\n return await executeCallApi(em, interpolatedConfig, context, container)\n\n case 'EMIT_EVENT':\n return await executeEmitEvent(interpolatedConfig, context, container)\n\n case 'UPDATE_ENTITY':\n return await executeUpdateEntity(em, interpolatedConfig, context, container)\n\n case 'CALL_WEBHOOK':\n return await executeCallWebhook(interpolatedConfig, context)\n\n case 'EXECUTE_FUNCTION':\n return await executeFunction(interpolatedConfig, context, container)\n\n default:\n throw new ActivityExecutionError(\n `Unknown activity type: ${activity.activityType}`,\n activity.activityType,\n activity.activityName\n )\n }\n}\n\n/**\n * SEND_EMAIL activity handler\n *\n * For MVP, this logs the email (actual email sending can be added later)\n */\nexport async function executeSendEmail(\n config: any,\n context: ActivityContext,\n container: AwilixContainer\n): Promise<any> {\n const { to, subject, template, templateData, body } = config\n\n if (!to || !subject) {\n throw new Error('SEND_EMAIL requires \"to\" and \"subject\" fields')\n }\n\n // For MVP: Log the email (actual email service integration can be added later)\n console.log(`[Workflow Activity] Send email to ${to}: ${subject}`)\n\n // Check if email service is available in container\n try {\n const emailService = container.resolve('emailService')\n if (emailService && typeof emailService.send === 'function') {\n await emailService.send({\n to,\n subject,\n template,\n templateData,\n body,\n })\n return { sent: true, to, subject, via: 'emailService' }\n }\n } catch (error) {\n // Email service not available, just log\n }\n\n return { sent: true, to, subject, via: 'console' }\n}\n\n/**\n * EMIT_EVENT activity handler\n *\n * Publishes a domain event to the event bus\n */\nexport async function executeEmitEvent(\n config: any,\n context: ActivityContext,\n container: AwilixContainer\n): Promise<any> {\n const { eventName, payload } = config\n\n if (!eventName) {\n throw new Error('EMIT_EVENT requires \"eventName\" field')\n }\n\n // Get event bus from container\n const eventBus = container.resolve('eventBus')\n\n if (!eventBus || typeof eventBus.emitEvent !== 'function') {\n throw new Error('Event bus not available in container')\n }\n\n // Publish event with workflow metadata\n const enrichedPayload = {\n ...payload,\n _workflow: {\n workflowInstanceId: context.workflowInstance.id,\n workflowId: context.workflowInstance.workflowId,\n tenantId: context.workflowInstance.tenantId,\n organizationId: context.workflowInstance.organizationId,\n },\n }\n\n await eventBus.emitEvent(eventName, enrichedPayload)\n\n return { emitted: true, eventName, payload: enrichedPayload }\n}\n\n/**\n * UPDATE_ENTITY activity handler\n *\n * Updates an entity via CommandBus for proper audit logging, undo support, and side effects.\n *\n * Config format:\n * ```json\n * {\n * \"commandId\": \"sales.documents.update\",\n * \"input\": {\n * \"id\": \"{{context.orderId}}\",\n * \"statusEntryId\": \"{{context.approvedStatusId}}\"\n * }\n * }\n * ```\n *\n * Alternative format with statusValue (auto-resolves to statusEntryId):\n * ```json\n * {\n * \"commandId\": \"sales.orders.update\",\n * \"statusDictionary\": \"sales.order_status\",\n * \"input\": {\n * \"id\": \"{{context.id}}\",\n * \"statusValue\": \"pending_approval\"\n * }\n * }\n * ```\n */\nexport async function executeUpdateEntity(\n em: EntityManager,\n config: any,\n context: ActivityContext,\n container: AwilixContainer\n): Promise<any> {\n const { commandId, input, statusDictionary } = config\n\n if (!commandId) {\n throw new Error('UPDATE_ENTITY requires \"commandId\" field (e.g., \"sales.documents.update\")')\n }\n\n if (!input || typeof input !== 'object') {\n throw new Error('UPDATE_ENTITY requires \"input\" object with entity data')\n }\n\n // Resolve CommandBus from container\n const commandBus = container.resolve('commandBus') as any\n\n if (!commandBus || typeof commandBus.execute !== 'function') {\n throw new Error('CommandBus not available in container')\n }\n\n // Prepare final input, resolving statusValue if provided\n let finalInput = { ...input }\n\n // If statusValue is provided with a statusDictionary, resolve it to statusEntryId\n if (finalInput.statusValue && statusDictionary) {\n const statusEntryId = await resolveDictionaryEntryId(\n em,\n statusDictionary,\n finalInput.statusValue,\n context.workflowInstance.tenantId,\n context.workflowInstance.organizationId\n )\n if (statusEntryId) {\n finalInput.statusEntryId = statusEntryId\n }\n delete finalInput.statusValue\n }\n\n // Build synthetic CommandRuntimeContext for workflow execution\n // Use nil UUID for system actions when no user context is available\n const SYSTEM_USER_ID = '00000000-0000-0000-0000-000000000000'\n const ctx = {\n container,\n auth: {\n sub: context.userId || SYSTEM_USER_ID,\n tenantId: context.workflowInstance.tenantId,\n orgId: context.workflowInstance.organizationId,\n isSuperAdmin: false,\n },\n organizationScope: null,\n selectedOrganizationId: context.workflowInstance.organizationId,\n organizationIds: context.workflowInstance.organizationId\n ? [context.workflowInstance.organizationId]\n : null,\n }\n\n // Execute the command\n const { result, logEntry } = await commandBus.execute(commandId, {\n input: finalInput,\n ctx,\n })\n\n return {\n executed: true,\n commandId,\n result,\n logEntryId: logEntry?.id,\n }\n}\n\n/**\n * Helper to resolve dictionary entry ID by value\n */\nasync function resolveDictionaryEntryId(\n em: EntityManager,\n dictionaryKey: string,\n value: string,\n tenantId: string,\n organizationId: string\n): Promise<string | null> {\n try {\n // Import here to avoid circular dependencies\n const { Dictionary, DictionaryEntry } = await import('@open-mercato/core/modules/dictionaries/data/entities')\n\n // Find the dictionary\n const dictionary = await em.findOne(Dictionary, {\n key: dictionaryKey,\n tenantId,\n organizationId,\n deletedAt: null,\n })\n\n if (!dictionary) {\n console.warn(`[UPDATE_ENTITY] Dictionary not found: ${dictionaryKey}`)\n return null\n }\n\n // Find the entry by normalized value\n const normalizedValue = value.toLowerCase().trim()\n const entry = await em.findOne(DictionaryEntry, {\n dictionary: dictionary.id,\n tenantId,\n organizationId,\n normalizedValue,\n })\n\n if (!entry) {\n console.warn(`[UPDATE_ENTITY] Dictionary entry not found: ${dictionaryKey}/${value}`)\n return null\n }\n\n return entry.id\n } catch (error) {\n console.error(`[UPDATE_ENTITY] Error resolving dictionary entry:`, error)\n return null\n }\n}\n\n/**\n * CALL_WEBHOOK activity handler\n *\n * Makes HTTP request to external URL\n */\nexport async function executeCallWebhook(\n config: any,\n context: ActivityContext\n): Promise<any> {\n const { url, method = 'POST', headers = {}, body } = config\n\n if (!url) {\n throw new Error('CALL_WEBHOOK requires \"url\" field')\n }\n\n // Make HTTP request\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...headers,\n },\n body: body ? JSON.stringify(body) : undefined,\n })\n\n // Parse response\n let result: any\n const contentType = response.headers.get('content-type')\n\n if (contentType && contentType.includes('application/json')) {\n result = await response.json()\n } else {\n result = await response.text()\n }\n\n // Check for HTTP errors\n if (!response.ok) {\n throw new Error(\n `Webhook request failed with status ${response.status}: ${JSON.stringify(result)}`\n )\n }\n\n return {\n status: response.status,\n statusText: response.statusText,\n result,\n }\n}\n\n/**\n * EXECUTE_FUNCTION activity handler\n *\n * Calls a registered function from DI container\n */\nexport async function executeFunction(\n config: any,\n context: ActivityContext,\n container: AwilixContainer\n): Promise<any> {\n const { functionName, args = {} } = config\n\n if (!functionName) {\n throw new Error('EXECUTE_FUNCTION requires \"functionName\" field')\n }\n\n // Look up function in container\n const fnKey = `workflowFunction:${functionName}`\n\n try {\n const fn = container.resolve(fnKey)\n\n if (typeof fn !== 'function') {\n throw new Error(`Registered workflow function \"${functionName}\" is not a function`)\n }\n\n // Call function with args and context\n const result = await fn(args, context)\n\n return { executed: true, functionName, result }\n } catch (error) {\n if (error instanceof Error && error.message.includes('not registered')) {\n throw new Error(\n `Workflow function \"${functionName}\" not registered in DI container (key: ${fnKey})`\n )\n }\n throw error\n }\n}\n\n/**\n * CALL_API activity handler\n *\n * Makes authenticated HTTP request to internal Open Mercato APIs\n * - Automatically creates one-time API key for authentication\n * - Injects tenant/organization context headers\n * - Validates URL security (SSRF prevention)\n * - Classifies errors (retriable vs non-retriable)\n * - Deletes API key after request (no stored credentials!)\n */\nexport async function executeCallApi(\n em: EntityManager,\n config: any,\n context: ActivityContext,\n container: AwilixContainer\n): Promise<any> {\n // 1. Interpolate variables in config (including {{workflow.*}}, {{context.*}}, {{env.*}}, {{now}})\n const interpolatedConfig = interpolateVariables(config, context.workflowContext, context.workflowInstance)\n\n const {\n endpoint,\n method = 'GET',\n headers = {},\n body,\n validateTenantMatch = true,\n } = interpolatedConfig\n\n\n if (!endpoint) {\n throw new Error('CALL_API requires \"endpoint\" field')\n }\n\n // 2. Build full URL (prepend APP_URL for relative paths)\n const fullUrl = buildApiUrl(endpoint)\n\n // 3. Import the one-time API key helper\n const { withOnetimeApiKey } = await import('../../api_keys/services/apiKeyService')\n\n // 4. Get EntityManager from container (for correct type)\n const apiKeyEm = container.resolve('em')\n\n // 5. Look up an admin role for the tenant to assign to the one-time key\n // CRITICAL: rolesJson must contain role IDs (UUIDs), not role names!\n const { Role } = await import('../../auth/data/entities')\n const adminRole = await apiKeyEm.findOne(Role, {\n tenantId: context.workflowInstance.tenantId,\n name: { $in: ['superadmin', 'admin', 'administrator'] } // Try common admin role names\n })\n\n if (!adminRole) {\n throw new Error(\n `[CALL_API] No admin role found for tenant ${context.workflowInstance.tenantId}. ` +\n `Cannot create one-time API key without role assignment. ` +\n `Ensure 'mercato init' has been run to create default roles.`\n )\n }\n\n // 6. Execute request with one-time API key (using role ID, not name)\n return await withOnetimeApiKey(\n apiKeyEm,\n {\n name: `__workflow_${context.workflowInstance.id}__`,\n description: `One-time key for workflow ${context.workflowInstance.workflowId} instance ${context.workflowInstance.id}`,\n tenantId: context.workflowInstance.tenantId,\n organizationId: context.workflowInstance.organizationId,\n roles: [adminRole.id], // \u2705 FIX: Use role ID (UUID), not role name\n expiresAt: null,\n },\n async (apiKeySecret) => {\n // Build request headers (auth + context + custom)\n const requestHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'Authorization': `apikey ${apiKeySecret}`,\n 'X-Tenant-Id': context.workflowInstance.tenantId,\n 'X-Organization-Id': context.workflowInstance.organizationId,\n 'X-Workflow-Instance-Id': context.workflowInstance.id,\n ...headers,\n }\n\n // Make HTTP request\n const response = await fetch(fullUrl, {\n method,\n headers: requestHeaders,\n body: body ? JSON.stringify(body) : undefined,\n })\n\n // Parse response body (JSON-safe)\n let responseBody: any\n const contentType = response.headers.get('content-type')\n\n try {\n if (contentType && contentType.includes('application/json')) {\n responseBody = await response.json()\n } else {\n responseBody = await response.text()\n }\n } catch (error) {\n responseBody = null\n }\n\n // Check for HTTP errors and classify\n if (!response.ok) {\n classifyAndThrowError(response.status, responseBody, fullUrl)\n }\n\n // Validate tenant match (security check)\n if (validateTenantMatch && responseBody && typeof responseBody === 'object') {\n if (responseBody.tenantId && responseBody.tenantId !== context.workflowInstance.tenantId) {\n throw new Error(\n `Tenant ID mismatch: workflow expects ${context.workflowInstance.tenantId} but API returned ${responseBody.tenantId}`\n )\n }\n }\n\n // Return structured result\n return {\n status: response.status,\n statusText: response.statusText,\n headers: Object.fromEntries(response.headers.entries()),\n body: responseBody,\n authenticated: true,\n tenantId: context.workflowInstance.tenantId,\n organizationId: context.workflowInstance.organizationId,\n }\n }\n )\n}\n\n// ============================================================================\n// CALL_API Helper Functions\n// ============================================================================\n\n/**\n * Build full API URL from endpoint\n * - Relative paths (/api/...) \u2192 prepend APP_URL\n * - Absolute URLs \u2192 validate domain matches APP_URL (SSRF prevention)\n */\nfunction buildApiUrl(endpoint: string): string {\n const appUrl = process.env.APP_URL || 'http://localhost:3000'\n\n // Relative path - prepend APP_URL\n if (endpoint.startsWith('/')) {\n // Security: Only allow /api/* paths\n if (!endpoint.startsWith('/api/')) {\n throw new Error(`CALL_API only supports /api/* paths, got: ${endpoint}`)\n }\n return `${appUrl}${endpoint}`\n }\n\n // Absolute URL - validate domain matches APP_URL (SSRF prevention)\n try {\n const endpointUrl = new URL(endpoint)\n const appUrlObj = new URL(appUrl)\n\n if (endpointUrl.host !== appUrlObj.host) {\n throw new Error(\n `SSRF Prevention: CALL_API endpoint domain (${endpointUrl.host}) does not match APP_URL (${appUrlObj.host})`\n )\n }\n\n return endpoint\n } catch (error) {\n if (error instanceof TypeError) {\n throw new Error(`Invalid endpoint URL: ${endpoint}`)\n }\n throw error\n }\n}\n\n/**\n * Classify HTTP error and throw appropriate error\n * - 400-499: Non-retriable (client error - validation/auth)\n * - 500-599: Retriable (server error)\n */\nfunction classifyAndThrowError(status: number, body: any, url: string): never {\n const bodyStr = typeof body === 'string' ? body : JSON.stringify(body)\n\n if (status >= 400 && status < 500) {\n // Client errors - non-retriable\n throw new Error(\n `CALL_API request failed with status ${status} (non-retriable): ${bodyStr}`\n )\n }\n\n if (status >= 500) {\n // Server errors - retriable\n const error: any = new Error(\n `CALL_API request failed with status ${status} (retriable): ${bodyStr}`\n )\n error.retriable = true\n throw error\n }\n\n // Other errors\n throw new Error(`CALL_API request failed with status ${status}: ${bodyStr}`)\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Interpolate variables in config from workflow context\n *\n * Supports syntax:\n * - {{context.field}} or {{context.nested.field}} - from workflow context\n * - {{workflow.instanceId}} - workflow instance ID\n * - {{workflow.tenantId}} - tenant ID\n * - {{workflow.organizationId}} - organization ID\n * - {{workflow.currentStepId}} - current step ID\n * - {{env.VAR_NAME}} - environment variables\n * - {{now}} - current ISO timestamp\n */\nfunction interpolateVariables(\n config: any,\n context: Record<string, any>,\n workflowInstance?: WorkflowInstance\n): any {\n if (typeof config === 'string') {\n // Check if this is a single variable reference (e.g., \"{{context.cart.items}}\")\n // This preserves the original type (array, object, number, boolean)\n const singleVarMatch = config.match(/^\\{\\{([^}]+)\\}\\}$/)\n\n if (singleVarMatch) {\n const trimmedPath = singleVarMatch[1].trim()\n\n // Handle {{workflow.*}} variables\n if (trimmedPath.startsWith('workflow.') && workflowInstance) {\n const workflowKey = trimmedPath.substring('workflow.'.length)\n switch (workflowKey) {\n case 'instanceId':\n return workflowInstance.id\n case 'tenantId':\n return workflowInstance.tenantId\n case 'organizationId':\n return workflowInstance.organizationId\n case 'currentStepId':\n return workflowInstance.currentStepId\n case 'workflowId':\n return workflowInstance.workflowId\n case 'version':\n return workflowInstance.version // Return as number\n default:\n return config // Return original if unknown\n }\n }\n\n // Handle {{env.*}} variables\n if (trimmedPath.startsWith('env.')) {\n const envKey = trimmedPath.substring('env.'.length)\n return process.env[envKey] ?? config\n }\n\n // Handle {{now}} - current timestamp\n if (trimmedPath === 'now') {\n return new Date().toISOString()\n }\n\n // Handle {{context.*}} variables (default behavior)\n const contextPath = trimmedPath.startsWith('context.')\n ? trimmedPath.substring('context.'.length)\n : trimmedPath\n\n const value = getNestedValue(context, contextPath)\n return value !== undefined ? value : config // Return raw value to preserve type\n }\n\n // Multiple interpolations or mixed text - return string\n return config.replace(/\\{\\{([^}]+)\\}\\}/g, (match, path) => {\n const trimmedPath = path.trim()\n\n // Handle {{workflow.*}} variables\n if (trimmedPath.startsWith('workflow.') && workflowInstance) {\n const workflowKey = trimmedPath.substring('workflow.'.length)\n switch (workflowKey) {\n case 'instanceId':\n return workflowInstance.id\n case 'tenantId':\n return workflowInstance.tenantId\n case 'organizationId':\n return workflowInstance.organizationId\n case 'currentStepId':\n return workflowInstance.currentStepId\n case 'workflowId':\n return workflowInstance.workflowId\n case 'version':\n return String(workflowInstance.version)\n default:\n return match // Unknown workflow key\n }\n }\n\n // Handle {{env.*}} variables\n if (trimmedPath.startsWith('env.')) {\n const envKey = trimmedPath.substring('env.'.length)\n const envValue = process.env[envKey]\n return envValue !== undefined ? envValue : match\n }\n\n // Handle {{now}} - current timestamp\n if (trimmedPath === 'now') {\n return new Date().toISOString()\n }\n\n // Handle {{context.*}} variables (default behavior)\n const contextPath = trimmedPath.startsWith('context.')\n ? trimmedPath.substring('context.'.length)\n : trimmedPath\n\n const value = getNestedValue(context, contextPath)\n return value !== undefined ? String(value) : match\n })\n }\n\n if (Array.isArray(config)) {\n return config.map((item) => interpolateVariables(item, context, workflowInstance))\n }\n\n if (config && typeof config === 'object') {\n const result: Record<string, any> = {}\n for (const [key, value] of Object.entries(config)) {\n result[key] = interpolateVariables(value, context, workflowInstance)\n }\n return result\n }\n\n return config\n}\n\n/**\n * Get nested value from object by path (e.g., \"user.email\")\n */\nfunction getNestedValue(obj: any, path: string): any {\n const parts = path.split('.')\n let value = obj\n\n for (const part of parts) {\n if (value && typeof value === 'object' && part in value) {\n value = value[part]\n } else {\n return undefined\n }\n }\n\n return value\n}\n\n/**\n * Calculate exponential backoff delay\n */\nfunction calculateBackoff(\n initialIntervalMs: number,\n backoffCoefficient: number,\n attempt: number,\n maxIntervalMs: number\n): number {\n const backoff = initialIntervalMs * Math.pow(backoffCoefficient, attempt)\n return Math.min(backoff, maxIntervalMs || Infinity)\n}\n\n/**\n * Sleep for specified milliseconds\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\n/**\n * Execute a promise with timeout\n */\nasync function executeWithTimeout<T>(\n executor: () => Promise<T>,\n timeoutMs: number\n): Promise<T> {\n let timeoutId: NodeJS.Timeout\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new Error(`Activity execution timeout after ${timeoutMs}ms`))\n }, timeoutMs)\n })\n\n try {\n return await Promise.race([executor(), timeoutPromise])\n } finally {\n clearTimeout(timeoutId!)\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Workflows Module - Activity Executor Service\n *\n * Executes workflow activities (send email, call API, emit events, etc.)\n * - Supports multiple activity types\n * - Implements retry logic with exponential backoff\n * - Handles timeouts\n * - Variable interpolation from workflow context\n *\n * Functional API (no classes) following Open Mercato conventions.\n */\n\nimport { EntityManager } from '@mikro-orm/core'\nimport type { AwilixContainer } from 'awilix'\nimport { WorkflowInstance } from '../data/entities'\nimport { createQueue, Queue } from '@open-mercato/queue'\nimport { WorkflowActivityJob, WORKFLOW_ACTIVITIES_QUEUE_NAME } from './activity-queue-types'\nimport { logWorkflowEvent } from './event-logger'\n\n// ============================================================================\n// Types and Interfaces\n// ============================================================================\n\nexport type ActivityType =\n | 'SEND_EMAIL'\n | 'CALL_API'\n | 'EMIT_EVENT'\n | 'UPDATE_ENTITY'\n | 'CALL_WEBHOOK'\n | 'EXECUTE_FUNCTION'\n\nexport interface ActivityDefinition {\n activityId: string // Unique identifier for activity\n activityName?: string // Optional, for debugging/logging\n activityType: ActivityType\n config: any\n async?: boolean // Flag to execute activity asynchronously via queue\n retryPolicy?: RetryPolicy\n timeoutMs?: number\n compensate?: boolean // Flag to execute compensation on failure\n}\n\nexport interface RetryPolicy {\n maxAttempts: number\n initialIntervalMs: number\n backoffCoefficient: number\n maxIntervalMs: number\n}\n\nexport interface ActivityContext {\n workflowInstance: WorkflowInstance\n workflowContext: Record<string, any>\n stepContext?: Record<string, any>\n stepInstanceId?: string\n transitionId?: string\n userId?: string\n}\n\nexport interface ActivityExecutionResult {\n activityId: string\n activityName?: string\n activityType: ActivityType\n success: boolean\n output?: any\n error?: string\n retryCount: number\n executionTimeMs: number\n async?: boolean // Marks activity as async (queued)\n jobId?: string // Queue job ID for async activities\n}\n\nexport class ActivityExecutionError extends Error {\n constructor(\n message: string,\n public activityType: ActivityType,\n public activityName?: string,\n public details?: any\n ) {\n super(message)\n this.name = 'ActivityExecutionError'\n }\n}\n\n// ============================================================================\n// Queue Integration for Async Activities\n// ============================================================================\n\nlet activityQueue: Queue<WorkflowActivityJob> | null = null\n\n/**\n * Get or create the activity queue (lazy initialization)\n */\nfunction getActivityQueue(): Queue<WorkflowActivityJob> {\n if (!activityQueue) {\n if (process.env.QUEUE_STRATEGY === 'async') {\n activityQueue = createQueue<WorkflowActivityJob>(\n WORKFLOW_ACTIVITIES_QUEUE_NAME,\n 'async',\n {\n connection: {\n url: process.env.REDIS_URL || process.env.QUEUE_REDIS_URL,\n },\n concurrency: parseInt(process.env.WORKFLOW_WORKER_CONCURRENCY || '5'),\n }\n )\n } else {\n activityQueue = createQueue<WorkflowActivityJob>(\n WORKFLOW_ACTIVITIES_QUEUE_NAME,\n 'local',\n {\n baseDir: process.env.QUEUE_BASE_DIR || '.queue',\n }\n )\n }\n }\n\n return activityQueue\n}\n\n/**\n * Enqueue an activity for background execution\n *\n * @param em - Entity manager\n * @param activity - Activity definition\n * @param context - Execution context\n * @returns Job ID\n */\nexport async function enqueueActivity(\n em: EntityManager,\n activity: ActivityDefinition,\n context: ActivityContext\n): Promise<string> {\n const { workflowInstance, workflowContext, stepContext, transitionId, stepInstanceId } =\n context\n\n // Interpolate config variables NOW (before queuing)\n const interpolatedConfig = interpolateVariables(activity.config, workflowContext, workflowInstance)\n\n // Create job payload\n const job: WorkflowActivityJob = {\n workflowInstanceId: workflowInstance.id,\n stepInstanceId,\n transitionId,\n activityId: activity.activityId,\n activityName: activity.activityName || activity.activityType,\n activityType: activity.activityType,\n activityConfig: interpolatedConfig,\n workflowContext,\n stepContext,\n retryPolicy: activity.retryPolicy,\n timeoutMs: activity.timeoutMs,\n tenantId: workflowInstance.tenantId,\n organizationId: workflowInstance.organizationId,\n userId: context.userId,\n }\n\n // Enqueue to queue\n const queue = getActivityQueue()\n const jobId = await queue.enqueue(job)\n\n // Log event\n await logWorkflowEvent(em, {\n workflowInstanceId: workflowInstance.id,\n stepInstanceId,\n eventType: 'ACTIVITY_QUEUED',\n eventData: {\n activityId: activity.activityId,\n activityName: activity.activityName,\n activityType: activity.activityType,\n async: true,\n jobId,\n },\n tenantId: workflowInstance.tenantId,\n organizationId: workflowInstance.organizationId,\n })\n\n return jobId\n}\n\n// ============================================================================\n// Main Activity Execution Functions\n// ============================================================================\n\n/**\n * Execute a single activity with retry logic and timeout\n *\n * @param em - Entity manager\n * @param container - DI container\n * @param activity - Activity definition\n * @param context - Execution context\n * @returns Execution result\n */\nexport async function executeActivity(\n em: EntityManager,\n container: AwilixContainer,\n activity: ActivityDefinition,\n context: ActivityContext\n): Promise<ActivityExecutionResult> {\n const retryPolicy = activity.retryPolicy || {\n maxAttempts: 1,\n initialIntervalMs: 0,\n backoffCoefficient: 1,\n maxIntervalMs: 0,\n }\n\n let lastError: any\n let retryCount = 0\n\n for (let attempt = 0; attempt < retryPolicy.maxAttempts; attempt++) {\n try {\n const startTime = Date.now()\n\n // Execute with timeout if specified\n const result = activity.timeoutMs\n ? await executeWithTimeout(\n () => executeActivityByType(em, container, activity, context),\n activity.timeoutMs\n )\n : await executeActivityByType(em, container, activity, context)\n\n const executionTimeMs = Date.now() - startTime\n\n return {\n activityId: activity.activityId,\n activityName: activity.activityName,\n activityType: activity.activityType,\n success: true,\n output: result,\n retryCount: attempt,\n executionTimeMs,\n async: activity.async || false,\n }\n } catch (error) {\n lastError = error\n retryCount = attempt + 1\n\n // If not the last attempt, apply backoff and retry\n if (attempt < retryPolicy.maxAttempts - 1) {\n const backoff = calculateBackoff(\n retryPolicy.initialIntervalMs,\n retryPolicy.backoffCoefficient,\n attempt,\n retryPolicy.maxIntervalMs\n )\n\n await sleep(backoff)\n }\n }\n }\n\n // All retries exhausted\n const errorMessage = lastError instanceof Error ? lastError.message : String(lastError)\n\n return {\n activityId: activity.activityId,\n activityName: activity.activityName,\n activityType: activity.activityType,\n success: false,\n error: `Activity failed after ${retryCount} attempts: ${errorMessage}`,\n retryCount,\n executionTimeMs: 0,\n async: activity.async || false,\n }\n}\n\n/**\n * Execute multiple activities in sequence\n * Supports both synchronous and asynchronous (queued) execution\n *\n * @param em - Entity manager\n * @param container - DI container\n * @param activities - Array of activity definitions\n * @param context - Execution context\n * @returns Array of execution results\n */\nexport async function executeActivities(\n em: EntityManager,\n container: AwilixContainer,\n activities: ActivityDefinition[],\n context: ActivityContext\n): Promise<ActivityExecutionResult[]> {\n const results: ActivityExecutionResult[] = []\n\n for (let i = 0; i < activities.length; i++) {\n const activity = activities[i]\n\n // Check if activity should run async\n if (activity.async) {\n // Enqueue for background execution\n const jobId = await enqueueActivity(em, activity, context)\n\n results.push({\n activityId: activity.activityId,\n activityName: activity.activityName,\n activityType: activity.activityType,\n success: true, // Queued successfully\n async: true,\n jobId,\n retryCount: 0,\n executionTimeMs: 0,\n })\n } else {\n // Execute synchronously (existing logic)\n const result = await executeActivity(em, container, activity, context)\n results.push(result)\n\n // Stop execution if activity fails (fail-fast)\n if (!result.success) {\n break\n }\n\n // Update workflow context with activity output\n if (result.output && typeof result.output === 'object') {\n const key = activity.activityName || activity.activityType\n context.workflowContext = {\n ...context.workflowContext,\n [key]: result.output,\n }\n }\n }\n }\n\n return results\n}\n\n// ============================================================================\n// Activity Type Handlers\n// ============================================================================\n\n/**\n * Execute activity based on its type\n */\nasync function executeActivityByType(\n em: EntityManager,\n container: AwilixContainer,\n activity: ActivityDefinition,\n context: ActivityContext\n): Promise<any> {\n // Interpolate config variables from context (including workflow metadata)\n const interpolatedConfig = interpolateVariables(activity.config, context.workflowContext, context.workflowInstance)\n\n switch (activity.activityType) {\n case 'SEND_EMAIL':\n return await executeSendEmail(interpolatedConfig, context, container)\n\n case 'CALL_API':\n return await executeCallApi(em, interpolatedConfig, context, container)\n\n case 'EMIT_EVENT':\n return await executeEmitEvent(interpolatedConfig, context, container)\n\n case 'UPDATE_ENTITY':\n return await executeUpdateEntity(em, interpolatedConfig, context, container)\n\n case 'CALL_WEBHOOK':\n return await executeCallWebhook(interpolatedConfig, context)\n\n case 'EXECUTE_FUNCTION':\n return await executeFunction(interpolatedConfig, context, container)\n\n default:\n throw new ActivityExecutionError(\n `Unknown activity type: ${activity.activityType}`,\n activity.activityType,\n activity.activityName\n )\n }\n}\n\n/**\n * SEND_EMAIL activity handler\n *\n * For MVP, this logs the email (actual email sending can be added later)\n */\nexport async function executeSendEmail(\n config: any,\n context: ActivityContext,\n container: AwilixContainer\n): Promise<any> {\n const { to, subject, template, templateData, body } = config\n\n if (!to || !subject) {\n throw new Error('SEND_EMAIL requires \"to\" and \"subject\" fields')\n }\n\n // For MVP: Log the email (actual email service integration can be added later)\n console.log(`[Workflow Activity] Send email to ${to}: ${subject}`)\n\n // Check if email service is available in container\n try {\n const emailService = container.resolve('emailService')\n if (emailService && typeof emailService.send === 'function') {\n await emailService.send({\n to,\n subject,\n template,\n templateData,\n body,\n })\n return { sent: true, to, subject, via: 'emailService' }\n }\n } catch (error) {\n // Email service not available, just log\n }\n\n return { sent: true, to, subject, via: 'console' }\n}\n\n/**\n * EMIT_EVENT activity handler\n *\n * Publishes a domain event to the event bus\n */\nexport async function executeEmitEvent(\n config: any,\n context: ActivityContext,\n container: AwilixContainer\n): Promise<any> {\n const { eventName, payload } = config\n\n if (!eventName) {\n throw new Error('EMIT_EVENT requires \"eventName\" field')\n }\n\n // Get event bus from container\n const eventBus = container.resolve('eventBus')\n\n if (!eventBus || typeof eventBus.emitEvent !== 'function') {\n throw new Error('Event bus not available in container')\n }\n\n // Publish event with workflow metadata\n const enrichedPayload = {\n ...payload,\n _workflow: {\n workflowInstanceId: context.workflowInstance.id,\n workflowId: context.workflowInstance.workflowId,\n tenantId: context.workflowInstance.tenantId,\n organizationId: context.workflowInstance.organizationId,\n },\n }\n\n await eventBus.emitEvent(eventName, enrichedPayload)\n\n return { emitted: true, eventName, payload: enrichedPayload }\n}\n\n/**\n * UPDATE_ENTITY activity handler\n *\n * Updates an entity via query engine\n */\nexport async function executeUpdateEntity(\n em: EntityManager,\n config: any,\n context: ActivityContext,\n container: AwilixContainer\n): Promise<any> {\n const { entityType, entityId, updates } = config\n\n if (!entityType || !entityId || !updates) {\n throw new Error('UPDATE_ENTITY requires \"entityType\", \"entityId\", and \"updates\" fields')\n }\n\n // Get query engine from container\n const queryEngine = container.resolve('queryEngine')\n\n if (!queryEngine || typeof queryEngine.update !== 'function') {\n throw new Error('Query engine not available in container')\n }\n\n // Execute update with tenant scoping\n await queryEngine.update({\n entity: entityType,\n where: { id: entityId },\n data: updates,\n tenantId: context.workflowInstance.tenantId,\n organizationId: context.workflowInstance.organizationId,\n })\n\n return { updated: true, entityType, entityId, updates }\n}\n\n/**\n * CALL_WEBHOOK activity handler\n *\n * Makes HTTP request to external URL\n */\nexport async function executeCallWebhook(\n config: any,\n context: ActivityContext\n): Promise<any> {\n const { url, method = 'POST', headers = {}, body } = config\n\n if (!url) {\n throw new Error('CALL_WEBHOOK requires \"url\" field')\n }\n\n // Make HTTP request\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...headers,\n },\n body: body ? JSON.stringify(body) : undefined,\n })\n\n // Parse response\n let result: any\n const contentType = response.headers.get('content-type')\n\n if (contentType && contentType.includes('application/json')) {\n result = await response.json()\n } else {\n result = await response.text()\n }\n\n // Check for HTTP errors\n if (!response.ok) {\n throw new Error(\n `Webhook request failed with status ${response.status}: ${JSON.stringify(result)}`\n )\n }\n\n return {\n status: response.status,\n statusText: response.statusText,\n result,\n }\n}\n\n/**\n * EXECUTE_FUNCTION activity handler\n *\n * Calls a registered function from DI container\n */\nexport async function executeFunction(\n config: any,\n context: ActivityContext,\n container: AwilixContainer\n): Promise<any> {\n const { functionName, args = {} } = config\n\n if (!functionName) {\n throw new Error('EXECUTE_FUNCTION requires \"functionName\" field')\n }\n\n // Look up function in container\n const fnKey = `workflowFunction:${functionName}`\n\n try {\n const fn = container.resolve(fnKey)\n\n if (typeof fn !== 'function') {\n throw new Error(`Registered workflow function \"${functionName}\" is not a function`)\n }\n\n // Call function with args and context\n const result = await fn(args, context)\n\n return { executed: true, functionName, result }\n } catch (error) {\n if (error instanceof Error && error.message.includes('not registered')) {\n throw new Error(\n `Workflow function \"${functionName}\" not registered in DI container (key: ${fnKey})`\n )\n }\n throw error\n }\n}\n\n/**\n * CALL_API activity handler\n *\n * Makes authenticated HTTP request to internal Open Mercato APIs\n * - Automatically creates one-time API key for authentication\n * - Injects tenant/organization context headers\n * - Validates URL security (SSRF prevention)\n * - Classifies errors (retriable vs non-retriable)\n * - Deletes API key after request (no stored credentials!)\n */\nexport async function executeCallApi(\n em: EntityManager,\n config: any,\n context: ActivityContext,\n container: AwilixContainer\n): Promise<any> {\n // 1. Interpolate variables in config (including {{workflow.*}}, {{context.*}}, {{env.*}}, {{now}})\n const interpolatedConfig = interpolateVariables(config, context.workflowContext, context.workflowInstance)\n\n const {\n endpoint,\n method = 'GET',\n headers = {},\n body,\n validateTenantMatch = true,\n } = interpolatedConfig\n\n\n if (!endpoint) {\n throw new Error('CALL_API requires \"endpoint\" field')\n }\n\n // 2. Build full URL (prepend APP_URL for relative paths)\n const fullUrl = buildApiUrl(endpoint)\n\n // 3. Import the one-time API key helper\n const { withOnetimeApiKey } = await import('../../api_keys/services/apiKeyService')\n\n // 4. Get EntityManager from container (for correct type)\n const apiKeyEm = container.resolve('em')\n\n // 5. Look up an admin role for the tenant to assign to the one-time key\n // CRITICAL: rolesJson must contain role IDs (UUIDs), not role names!\n const { Role } = await import('../../auth/data/entities')\n const adminRole = await apiKeyEm.findOne(Role, {\n tenantId: context.workflowInstance.tenantId,\n name: { $in: ['superadmin', 'admin', 'administrator'] } // Try common admin role names\n })\n\n if (!adminRole) {\n throw new Error(\n `[CALL_API] No admin role found for tenant ${context.workflowInstance.tenantId}. ` +\n `Cannot create one-time API key without role assignment. ` +\n `Ensure 'mercato init' has been run to create default roles.`\n )\n }\n\n // 6. Execute request with one-time API key (using role ID, not name)\n return await withOnetimeApiKey(\n apiKeyEm,\n {\n name: `__workflow_${context.workflowInstance.id}__`,\n description: `One-time key for workflow ${context.workflowInstance.workflowId} instance ${context.workflowInstance.id}`,\n tenantId: context.workflowInstance.tenantId,\n organizationId: context.workflowInstance.organizationId,\n roles: [adminRole.id], // \u2705 FIX: Use role ID (UUID), not role name\n expiresAt: null,\n },\n async (apiKeySecret) => {\n // Build request headers (auth + context + custom)\n const requestHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'Authorization': `apikey ${apiKeySecret}`,\n 'X-Tenant-Id': context.workflowInstance.tenantId,\n 'X-Organization-Id': context.workflowInstance.organizationId,\n 'X-Workflow-Instance-Id': context.workflowInstance.id,\n ...headers,\n }\n\n // Make HTTP request\n const response = await fetch(fullUrl, {\n method,\n headers: requestHeaders,\n body: body ? JSON.stringify(body) : undefined,\n })\n\n // Parse response body (JSON-safe)\n let responseBody: any\n const contentType = response.headers.get('content-type')\n\n try {\n if (contentType && contentType.includes('application/json')) {\n responseBody = await response.json()\n } else {\n responseBody = await response.text()\n }\n } catch (error) {\n responseBody = null\n }\n\n // Check for HTTP errors and classify\n if (!response.ok) {\n classifyAndThrowError(response.status, responseBody, fullUrl)\n }\n\n // Validate tenant match (security check)\n if (validateTenantMatch && responseBody && typeof responseBody === 'object') {\n if (responseBody.tenantId && responseBody.tenantId !== context.workflowInstance.tenantId) {\n throw new Error(\n `Tenant ID mismatch: workflow expects ${context.workflowInstance.tenantId} but API returned ${responseBody.tenantId}`\n )\n }\n }\n\n // Return structured result\n return {\n status: response.status,\n statusText: response.statusText,\n headers: Object.fromEntries(response.headers.entries()),\n body: responseBody,\n authenticated: true,\n tenantId: context.workflowInstance.tenantId,\n organizationId: context.workflowInstance.organizationId,\n }\n }\n )\n}\n\n// ============================================================================\n// CALL_API Helper Functions\n// ============================================================================\n\n/**\n * Build full API URL from endpoint\n * - Relative paths (/api/...) \u2192 prepend APP_URL\n * - Absolute URLs \u2192 validate domain matches APP_URL (SSRF prevention)\n */\nfunction buildApiUrl(endpoint: string): string {\n const appUrl = process.env.APP_URL || 'http://localhost:3000'\n\n // Relative path - prepend APP_URL\n if (endpoint.startsWith('/')) {\n // Security: Only allow /api/* paths\n if (!endpoint.startsWith('/api/')) {\n throw new Error(`CALL_API only supports /api/* paths, got: ${endpoint}`)\n }\n return `${appUrl}${endpoint}`\n }\n\n // Absolute URL - validate domain matches APP_URL (SSRF prevention)\n try {\n const endpointUrl = new URL(endpoint)\n const appUrlObj = new URL(appUrl)\n\n if (endpointUrl.host !== appUrlObj.host) {\n throw new Error(\n `SSRF Prevention: CALL_API endpoint domain (${endpointUrl.host}) does not match APP_URL (${appUrlObj.host})`\n )\n }\n\n return endpoint\n } catch (error) {\n if (error instanceof TypeError) {\n throw new Error(`Invalid endpoint URL: ${endpoint}`)\n }\n throw error\n }\n}\n\n/**\n * Classify HTTP error and throw appropriate error\n * - 400-499: Non-retriable (client error - validation/auth)\n * - 500-599: Retriable (server error)\n */\nfunction classifyAndThrowError(status: number, body: any, url: string): never {\n const bodyStr = typeof body === 'string' ? body : JSON.stringify(body)\n\n if (status >= 400 && status < 500) {\n // Client errors - non-retriable\n throw new Error(\n `CALL_API request failed with status ${status} (non-retriable): ${bodyStr}`\n )\n }\n\n if (status >= 500) {\n // Server errors - retriable\n const error: any = new Error(\n `CALL_API request failed with status ${status} (retriable): ${bodyStr}`\n )\n error.retriable = true\n throw error\n }\n\n // Other errors\n throw new Error(`CALL_API request failed with status ${status}: ${bodyStr}`)\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Interpolate variables in config from workflow context\n *\n * Supports syntax:\n * - {{context.field}} or {{context.nested.field}} - from workflow context\n * - {{workflow.instanceId}} - workflow instance ID\n * - {{workflow.tenantId}} - tenant ID\n * - {{workflow.organizationId}} - organization ID\n * - {{workflow.currentStepId}} - current step ID\n * - {{env.VAR_NAME}} - environment variables\n * - {{now}} - current ISO timestamp\n */\nfunction interpolateVariables(\n config: any,\n context: Record<string, any>,\n workflowInstance?: WorkflowInstance\n): any {\n if (typeof config === 'string') {\n // Check if this is a single variable reference (e.g., \"{{context.cart.items}}\")\n // This preserves the original type (array, object, number, boolean)\n const singleVarMatch = config.match(/^\\{\\{([^}]+)\\}\\}$/)\n\n if (singleVarMatch) {\n const trimmedPath = singleVarMatch[1].trim()\n\n // Handle {{workflow.*}} variables\n if (trimmedPath.startsWith('workflow.') && workflowInstance) {\n const workflowKey = trimmedPath.substring('workflow.'.length)\n switch (workflowKey) {\n case 'instanceId':\n return workflowInstance.id\n case 'tenantId':\n return workflowInstance.tenantId\n case 'organizationId':\n return workflowInstance.organizationId\n case 'currentStepId':\n return workflowInstance.currentStepId\n case 'workflowId':\n return workflowInstance.workflowId\n case 'version':\n return workflowInstance.version // Return as number\n default:\n return config // Return original if unknown\n }\n }\n\n // Handle {{env.*}} variables\n if (trimmedPath.startsWith('env.')) {\n const envKey = trimmedPath.substring('env.'.length)\n return process.env[envKey] ?? config\n }\n\n // Handle {{now}} - current timestamp\n if (trimmedPath === 'now') {\n return new Date().toISOString()\n }\n\n // Handle {{context.*}} variables (default behavior)\n const contextPath = trimmedPath.startsWith('context.')\n ? trimmedPath.substring('context.'.length)\n : trimmedPath\n\n const value = getNestedValue(context, contextPath)\n return value !== undefined ? value : config // Return raw value to preserve type\n }\n\n // Multiple interpolations or mixed text - return string\n return config.replace(/\\{\\{([^}]+)\\}\\}/g, (match, path) => {\n const trimmedPath = path.trim()\n\n // Handle {{workflow.*}} variables\n if (trimmedPath.startsWith('workflow.') && workflowInstance) {\n const workflowKey = trimmedPath.substring('workflow.'.length)\n switch (workflowKey) {\n case 'instanceId':\n return workflowInstance.id\n case 'tenantId':\n return workflowInstance.tenantId\n case 'organizationId':\n return workflowInstance.organizationId\n case 'currentStepId':\n return workflowInstance.currentStepId\n case 'workflowId':\n return workflowInstance.workflowId\n case 'version':\n return String(workflowInstance.version)\n default:\n return match // Unknown workflow key\n }\n }\n\n // Handle {{env.*}} variables\n if (trimmedPath.startsWith('env.')) {\n const envKey = trimmedPath.substring('env.'.length)\n const envValue = process.env[envKey]\n return envValue !== undefined ? envValue : match\n }\n\n // Handle {{now}} - current timestamp\n if (trimmedPath === 'now') {\n return new Date().toISOString()\n }\n\n // Handle {{context.*}} variables (default behavior)\n const contextPath = trimmedPath.startsWith('context.')\n ? trimmedPath.substring('context.'.length)\n : trimmedPath\n\n const value = getNestedValue(context, contextPath)\n return value !== undefined ? String(value) : match\n })\n }\n\n if (Array.isArray(config)) {\n return config.map((item) => interpolateVariables(item, context, workflowInstance))\n }\n\n if (config && typeof config === 'object') {\n const result: Record<string, any> = {}\n for (const [key, value] of Object.entries(config)) {\n result[key] = interpolateVariables(value, context, workflowInstance)\n }\n return result\n }\n\n return config\n}\n\n/**\n * Get nested value from object by path (e.g., \"user.email\")\n */\nfunction getNestedValue(obj: any, path: string): any {\n const parts = path.split('.')\n let value = obj\n\n for (const part of parts) {\n if (value && typeof value === 'object' && part in value) {\n value = value[part]\n } else {\n return undefined\n }\n }\n\n return value\n}\n\n/**\n * Calculate exponential backoff delay\n */\nfunction calculateBackoff(\n initialIntervalMs: number,\n backoffCoefficient: number,\n attempt: number,\n maxIntervalMs: number\n): number {\n const backoff = initialIntervalMs * Math.pow(backoffCoefficient, attempt)\n return Math.min(backoff, maxIntervalMs || Infinity)\n}\n\n/**\n * Sleep for specified milliseconds\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\n/**\n * Execute a promise with timeout\n */\nasync function executeWithTimeout<T>(\n executor: () => Promise<T>,\n timeoutMs: number\n): Promise<T> {\n let timeoutId: NodeJS.Timeout\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new Error(`Activity execution timeout after ${timeoutMs}ms`))\n }, timeoutMs)\n })\n\n try {\n return await Promise.race([executor(), timeoutPromise])\n } finally {\n clearTimeout(timeoutId!)\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -128,11 +128,9 @@ function graphToDefinition(nodes, edges, options = {}) {
128
128
  };
129
129
  }
130
130
  function definitionToGraph(definition, options = {}) {
131
- const { autoLayout = true, layoutSpacing = { vertical: 200, horizontal: 300 } } = options;
132
- const stepMap = new Map(definition.steps.map((step) => [step.stepId, step]));
133
- const positions = autoLayout ? calculateSmartLayout(definition.steps, definition.transitions, layoutSpacing) : null;
131
+ const { autoLayout = true, layoutSpacing = { vertical: 250, horizontal: 100 } } = options;
134
132
  const nodes = definition.steps.map((step, index) => {
135
- let position = positions?.get(step.stepId) || { x: 250, y: 50 + index * layoutSpacing.vertical };
133
+ let position = { x: 250, y: 50 + index * layoutSpacing.vertical };
136
134
  if (!autoLayout && step._editorPosition) {
137
135
  position = step._editorPosition;
138
136
  }
@@ -211,73 +209,6 @@ function definitionToGraph(definition, options = {}) {
211
209
  });
212
210
  return { nodes, edges };
213
211
  }
214
- function calculateSmartLayout(steps, transitions, spacing) {
215
- const positions = /* @__PURE__ */ new Map();
216
- if (steps.length === 0) return positions;
217
- const outgoing = /* @__PURE__ */ new Map();
218
- const incoming = /* @__PURE__ */ new Map();
219
- for (const step of steps) {
220
- outgoing.set(step.stepId, []);
221
- incoming.set(step.stepId, []);
222
- }
223
- for (const t of transitions) {
224
- const children = outgoing.get(t.fromStepId) || [];
225
- children.push(t.toStepId);
226
- outgoing.set(t.fromStepId, children);
227
- const parents = incoming.get(t.toStepId) || [];
228
- parents.push(t.fromStepId);
229
- incoming.set(t.toStepId, parents);
230
- }
231
- const startNodes = steps.filter((s) => (incoming.get(s.stepId) || []).length === 0);
232
- if (startNodes.length === 0) {
233
- startNodes.push(steps[0]);
234
- }
235
- const levels = /* @__PURE__ */ new Map();
236
- const queue = [];
237
- for (const start of startNodes) {
238
- queue.push({ id: start.stepId, level: 0 });
239
- }
240
- while (queue.length > 0) {
241
- const { id, level } = queue.shift();
242
- const currentLevel = levels.get(id);
243
- if (currentLevel === void 0 || level > currentLevel) {
244
- levels.set(id, level);
245
- }
246
- const children = outgoing.get(id) || [];
247
- for (const child of children) {
248
- queue.push({ id: child, level: level + 1 });
249
- }
250
- }
251
- const nodesByLevel = /* @__PURE__ */ new Map();
252
- for (const [nodeId, level] of levels) {
253
- const nodesAtLevel = nodesByLevel.get(level) || [];
254
- nodesAtLevel.push(nodeId);
255
- nodesByLevel.set(level, nodesAtLevel);
256
- }
257
- const centerX = 400;
258
- const startY = 50;
259
- for (const [level, nodeIds] of nodesByLevel) {
260
- const count = nodeIds.length;
261
- const y = startY + level * spacing.vertical;
262
- if (count === 1) {
263
- positions.set(nodeIds[0], { x: centerX, y });
264
- } else {
265
- const totalWidth = (count - 1) * spacing.horizontal;
266
- const startX = centerX - totalWidth / 2;
267
- nodeIds.sort((a, b) => {
268
- const parentsA = incoming.get(a) || [];
269
- const parentsB = incoming.get(b) || [];
270
- const parentPosA = parentsA.length > 0 ? positions.get(parentsA[0])?.x || 0 : 0;
271
- const parentPosB = parentsB.length > 0 ? positions.get(parentsB[0])?.x || 0 : 0;
272
- return parentPosA - parentPosB;
273
- });
274
- nodeIds.forEach((nodeId, idx) => {
275
- positions.set(nodeId, { x: startX + idx * spacing.horizontal, y });
276
- });
277
- }
278
- }
279
- return positions;
280
- }
281
212
  function mapNodeTypeToStepType(nodeType) {
282
213
  const mapping = {
283
214
  start: "START",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/workflows/lib/graph-utils.ts"],
4
- "sourcesContent": ["import { Node, Edge } from '@xyflow/react'\nimport type { WorkflowDefinition } from '../data/entities'\n\n/**\n * Graph Utilities for Visual Workflow Editor\n *\n * Converts between ReactFlow graph representation and workflow definition JSON\n */\n\nexport interface GraphToDefinitionOptions {\n includePositions?: boolean\n}\n\nexport interface DefinitionToGraphOptions {\n autoLayout?: boolean\n layoutSpacing?: { vertical: number; horizontal: number }\n}\n\n/**\n * Convert ReactFlow graph (nodes + edges) to workflow definition JSON\n */\nexport function graphToDefinition(\n nodes: Node[],\n edges: Edge[],\n options: GraphToDefinitionOptions = {}\n): WorkflowDefinition['definition'] {\n // Extract steps from nodes\n const steps = nodes.map((node) => {\n const step: any = {\n stepId: node.id,\n stepName: node.data.label || node.id,\n stepType: mapNodeTypeToStepType(node.type || 'automated'),\n }\n\n // Add step-specific configuration\n if (node.data.description) {\n step.description = node.data.description\n }\n\n // Add timeout if present\n if (node.data.timeout) {\n step.timeout = node.data.timeout\n }\n\n // Add retryPolicy if present\n if (node.data.retryPolicy) {\n step.retryPolicy = node.data.retryPolicy\n }\n\n // Add generic config if present\n if (node.data.config) {\n step.config = node.data.config\n }\n\n // User task configuration\n if (node.type === 'userTask' && node.data) {\n step.userTaskConfig = {\n assignedTo: node.data.assignedTo,\n assignedToRoles: node.data.assignedToRoles || [],\n formKey: node.data.formKey,\n allowedActions: node.data.allowedActions || ['complete', 'cancel'],\n }\n\n // Add form schema if present\n if ((node.data as any).formSchema || (node.data as any).userTaskConfig?.formSchema) {\n step.userTaskConfig.formSchema = (node.data as any).formSchema || (node.data as any).userTaskConfig.formSchema\n }\n\n // Add advanced fields if present\n if ((node.data as any).assignmentRule || (node.data as any).userTaskConfig?.assignmentRule) {\n step.userTaskConfig.assignmentRule = (node.data as any).assignmentRule || (node.data as any).userTaskConfig.assignmentRule\n }\n\n if ((node.data as any).slaDuration || (node.data as any).userTaskConfig?.slaDuration) {\n step.userTaskConfig.slaDuration = (node.data as any).slaDuration || (node.data as any).userTaskConfig.slaDuration\n }\n\n if ((node.data as any).escalationRules || (node.data as any).userTaskConfig?.escalationRules) {\n step.userTaskConfig.escalationRules = (node.data as any).escalationRules || (node.data as any).userTaskConfig.escalationRules\n }\n }\n\n // Wait for signal configuration\n if (node.type === 'waitForSignal' && node.data.signalConfig) {\n step.signalConfig = node.data.signalConfig\n }\n\n // Step activities (for AUTOMATED steps)\n if (node.type === 'automated' && node.data.activities) {\n step.activities = node.data.activities\n }\n\n // Pre-conditions (for START steps)\n if (node.type === 'start' && (node.data as any).preConditions && (node.data as any).preConditions.length > 0) {\n step.preConditions = (node.data as any).preConditions\n }\n\n // Store position for visual editor\n if (options.includePositions && node.position) {\n step._editorPosition = {\n x: node.position.x,\n y: node.position.y,\n }\n }\n\n return step\n })\n\n // Extract transitions from edges\n const transitions = edges.map((edge) => {\n const edgeData = edge.data as any\n const transition: any = {\n transitionId: edge.id,\n fromStepId: edge.source,\n toStepId: edge.target,\n trigger: edgeData?.trigger || 'auto',\n }\n\n // Add transition name if present\n if (edgeData?.transitionName) {\n transition.transitionName = edgeData.transitionName\n }\n\n // Add priority if present (default 0)\n if (edgeData?.priority !== undefined) {\n transition.priority = edgeData.priority\n }\n\n // Add continueOnActivityFailure if present (default true)\n if (edgeData?.continueOnActivityFailure !== undefined) {\n transition.continueOnActivityFailure = edgeData.continueOnActivityFailure\n }\n\n // Add conditions if present\n if (edgeData?.preConditions && edgeData.preConditions.length > 0) {\n transition.preConditions = edgeData.preConditions\n }\n\n if (edgeData?.postConditions && edgeData.postConditions.length > 0) {\n transition.postConditions = edgeData.postConditions\n }\n\n // Add activities if present in edge data\n if (edgeData?.activities && edgeData.activities.length > 0) {\n transition.activities = edgeData.activities.map((activity: any) => ({\n activityId: activity.activityId,\n activityName: activity.activityName,\n activityType: activity.activityType,\n config: activity.config || {},\n // Include all optional fields\n ...(activity.async !== undefined && { async: activity.async }),\n ...(activity.timeout && { timeout: activity.timeout }),\n ...(activity.retryPolicy && { retryPolicy: activity.retryPolicy }),\n ...(activity.compensate !== undefined && { compensate: activity.compensate }),\n }))\n } else {\n // Check if source node is automated and has activity data\n // If so, place the activity in this transition\n const sourceNode = nodes.find(n => n.id === edge.source)\n if (sourceNode && sourceNode.type === 'automated' && sourceNode.data) {\n if (sourceNode.data.activityType || sourceNode.data.activityId) {\n const activity: any = {\n activityId: sourceNode.data.activityId || `activity_${sourceNode.id}`,\n activityName: sourceNode.data.activityName || sourceNode.data.label || 'Automated Activity',\n activityType: sourceNode.data.activityType || 'CALL_API',\n config: sourceNode.data.activityConfig || {},\n }\n // Include optional activity fields from node data\n if ((sourceNode.data as any).activityAsync !== undefined) {\n activity.async = (sourceNode.data as any).activityAsync\n }\n if ((sourceNode.data as any).activityTimeout) {\n activity.timeout = (sourceNode.data as any).activityTimeout\n }\n if ((sourceNode.data as any).activityRetryPolicy) {\n activity.retryPolicy = (sourceNode.data as any).activityRetryPolicy\n }\n if ((sourceNode.data as any).activityCompensate !== undefined) {\n activity.compensate = (sourceNode.data as any).activityCompensate\n }\n transition.activities = [activity]\n }\n }\n }\n\n // Add label if present (legacy field, transitionName is preferred)\n if (edgeData?.label && !transition.transitionName) {\n transition.transitionName = edgeData.label\n }\n\n return transition\n })\n\n return {\n steps,\n transitions,\n activities: [], // Global activities can be added later\n }\n}\n\n/**\n * Convert workflow definition JSON to ReactFlow graph (nodes + edges)\n */\nexport function definitionToGraph(\n definition: WorkflowDefinition['definition'],\n options: DefinitionToGraphOptions = {}\n): { nodes: Node[]; edges: Edge[] } {\n const { autoLayout = true, layoutSpacing = { vertical: 200, horizontal: 300 } } = options\n\n // Build step map for quick lookup\n const stepMap = new Map(definition.steps.map(step => [step.stepId, step]))\n\n // Calculate smart layout positions if autoLayout is enabled\n const positions = autoLayout\n ? calculateSmartLayout(definition.steps, definition.transitions, layoutSpacing)\n : null\n\n // Convert steps to nodes\n const nodes: Node[] = definition.steps.map((step, index) => {\n // Determine position\n let position = positions?.get(step.stepId) || { x: 250, y: 50 + index * layoutSpacing.vertical }\n\n // Use stored position if available and not auto-layouting\n if (!autoLayout && (step as any)._editorPosition) {\n position = (step as any)._editorPosition\n }\n\n // Map step type to node type\n const nodeType = mapStepTypeToNodeType(step.stepType)\n\n // Build node data\n const nodeData: any = {\n label: step.stepName,\n description: (step as any).description,\n stepNumber: index > 0 ? index : undefined,\n }\n\n // Add timeout if present\n if ((step as any).timeout) {\n nodeData.timeout = (step as any).timeout\n }\n\n // Add retryPolicy if present\n if ((step as any).retryPolicy) {\n nodeData.retryPolicy = (step as any).retryPolicy\n }\n\n // Add generic config if present\n if ((step as any).config) {\n nodeData.config = (step as any).config\n }\n\n // Add user task data\n if (step.stepType === 'USER_TASK' && step.userTaskConfig) {\n nodeData.assignedTo = step.userTaskConfig.assignedTo\n nodeData.assignedToRoles = step.userTaskConfig.assignedToRoles || []\n nodeData.formKey = step.userTaskConfig.formKey\n nodeData.allowedActions = step.userTaskConfig.allowedActions\n\n // Store full userTaskConfig for advanced fields\n nodeData.userTaskConfig = step.userTaskConfig\n\n // Add form schema if present\n if (step.userTaskConfig.formSchema) {\n nodeData.formSchema = step.userTaskConfig.formSchema\n }\n\n // Add advanced fields if present\n if (step.userTaskConfig.assignmentRule) {\n nodeData.assignmentRule = step.userTaskConfig.assignmentRule\n }\n\n if (step.userTaskConfig.slaDuration) {\n nodeData.slaDuration = step.userTaskConfig.slaDuration\n }\n\n if (step.userTaskConfig.escalationRules) {\n nodeData.escalationRules = step.userTaskConfig.escalationRules\n }\n }\n\n // Add wait for signal data\n if (step.stepType === 'WAIT_FOR_SIGNAL' && (step as any).signalConfig) {\n nodeData.signalConfig = (step as any).signalConfig\n }\n\n // Add step activities data (for AUTOMATED steps)\n if (step.stepType === 'AUTOMATED' && (step as any).activities) {\n nodeData.activities = (step as any).activities\n }\n\n // Add pre-conditions data (for START steps)\n if (step.stepType === 'START' && (step as any).preConditions) {\n nodeData.preConditions = (step as any).preConditions\n }\n\n // Set badge based on type\n nodeData.badge = getBadgeForNodeType(nodeType)\n\n // Default status is pending\n nodeData.status = 'pending'\n\n return {\n id: step.stepId,\n type: nodeType,\n position,\n data: nodeData,\n }\n })\n\n // Convert transitions to edges\n const edges: Edge[] = definition.transitions.map((transition) => {\n return {\n id: transition.transitionId,\n source: transition.fromStepId,\n target: transition.toStepId,\n type: 'workflowTransition',\n data: {\n trigger: transition.trigger,\n transitionName: (transition as any).transitionName,\n priority: (transition as any).priority !== undefined ? (transition as any).priority : 0,\n continueOnActivityFailure: (transition as any).continueOnActivityFailure !== undefined\n ? (transition as any).continueOnActivityFailure\n : true,\n preConditions: transition.preConditions || [],\n postConditions: transition.postConditions || [],\n activities: transition.activities || [],\n label: (transition as any).transitionName || (transition as any).label, // Backward compat\n state: (transition as any).state || 'pending', // Default edge state\n },\n }\n })\n\n return { nodes, edges }\n}\n\n/**\n * Calculate smart layout positions for workflow nodes\n * Uses a layered/hierarchical layout algorithm that:\n * 1. Assigns levels (ranks) to nodes based on graph topology\n * 2. Spreads sibling nodes horizontally at the same level\n * 3. Centers merge points below their incoming nodes\n */\nfunction calculateSmartLayout(\n steps: any[],\n transitions: any[],\n spacing: { vertical: number; horizontal: number }\n): Map<string, { x: number; y: number }> {\n const positions = new Map<string, { x: number; y: number }>()\n\n if (steps.length === 0) return positions\n\n // Build adjacency lists\n const outgoing = new Map<string, string[]>() // node -> children\n const incoming = new Map<string, string[]>() // node -> parents\n\n for (const step of steps) {\n outgoing.set(step.stepId, [])\n incoming.set(step.stepId, [])\n }\n\n for (const t of transitions) {\n const children = outgoing.get(t.fromStepId) || []\n children.push(t.toStepId)\n outgoing.set(t.fromStepId, children)\n\n const parents = incoming.get(t.toStepId) || []\n parents.push(t.fromStepId)\n incoming.set(t.toStepId, parents)\n }\n\n // Find start node(s) - nodes with no incoming edges\n const startNodes = steps.filter(s => (incoming.get(s.stepId) || []).length === 0)\n if (startNodes.length === 0) {\n // Fallback: use first step as start\n startNodes.push(steps[0])\n }\n\n // Assign levels using BFS (longest path from start)\n const levels = new Map<string, number>()\n const queue: Array<{ id: string; level: number }> = []\n\n for (const start of startNodes) {\n queue.push({ id: start.stepId, level: 0 })\n }\n\n while (queue.length > 0) {\n const { id, level } = queue.shift()!\n const currentLevel = levels.get(id)\n\n // Take the maximum level (longest path)\n if (currentLevel === undefined || level > currentLevel) {\n levels.set(id, level)\n }\n\n const children = outgoing.get(id) || []\n for (const child of children) {\n queue.push({ id: child, level: level + 1 })\n }\n }\n\n // Group nodes by level\n const nodesByLevel = new Map<number, string[]>()\n for (const [nodeId, level] of levels) {\n const nodesAtLevel = nodesByLevel.get(level) || []\n nodesAtLevel.push(nodeId)\n nodesByLevel.set(level, nodesAtLevel)\n }\n\n // Calculate positions\n const centerX = 400 // Center line for the graph\n const startY = 50\n\n for (const [level, nodeIds] of nodesByLevel) {\n const count = nodeIds.length\n const y = startY + level * spacing.vertical\n\n if (count === 1) {\n // Single node at this level - center it\n positions.set(nodeIds[0], { x: centerX, y })\n } else {\n // Multiple nodes at this level - spread them horizontally\n const totalWidth = (count - 1) * spacing.horizontal\n const startX = centerX - totalWidth / 2\n\n // Sort nodes by their parent's position for consistent ordering\n nodeIds.sort((a, b) => {\n const parentsA = incoming.get(a) || []\n const parentsB = incoming.get(b) || []\n const parentPosA = parentsA.length > 0 ? (positions.get(parentsA[0])?.x || 0) : 0\n const parentPosB = parentsB.length > 0 ? (positions.get(parentsB[0])?.x || 0) : 0\n return parentPosA - parentPosB\n })\n\n nodeIds.forEach((nodeId, idx) => {\n positions.set(nodeId, { x: startX + idx * spacing.horizontal, y })\n })\n }\n }\n\n return positions\n}\n\n/**\n * Map node type to step type (for graph \u2192 definition)\n */\nfunction mapNodeTypeToStepType(nodeType: string): string {\n const mapping: Record<string, string> = {\n start: 'START',\n end: 'END',\n userTask: 'USER_TASK',\n automated: 'AUTOMATED',\n decision: 'DECISION',\n waitForSignal: 'WAIT_FOR_SIGNAL',\n }\n return mapping[nodeType] || 'AUTOMATED'\n}\n\n/**\n * Map step type to node type (for definition \u2192 graph)\n */\nfunction mapStepTypeToNodeType(stepType: string): string {\n const mapping: Record<string, string> = {\n START: 'start',\n END: 'end',\n USER_TASK: 'userTask',\n AUTOMATED: 'automated',\n DECISION: 'decision',\n WAIT_FOR_SIGNAL: 'waitForSignal',\n }\n return mapping[stepType] || 'automated'\n}\n\n/**\n * Get badge text for node type\n */\nfunction getBadgeForNodeType(nodeType: string): string {\n const badges: Record<string, string> = {\n start: 'Start',\n end: 'End',\n userTask: 'User Task',\n automated: 'Automated',\n decision: 'Decision',\n waitForSignal: 'Wait for Signal',\n }\n return badges[nodeType] || 'Task'\n}\n\n/**\n * Validate workflow graph\n */\nexport interface ValidationError {\n type: 'error' | 'warning'\n message: string\n nodeId?: string\n edgeId?: string\n}\n\nexport function validateWorkflowGraph(nodes: Node[], edges: Edge[]): ValidationError[] {\n const errors: ValidationError[] = []\n\n // Check for at least one start node\n const startNodes = nodes.filter((n) => n.type === 'start')\n if (startNodes.length === 0) {\n errors.push({\n type: 'error',\n message: 'Workflow must have at least one START node',\n })\n }\n if (startNodes.length > 1) {\n errors.push({\n type: 'warning',\n message: 'Workflow has multiple START nodes',\n })\n }\n\n // Check for at least one end node\n const endNodes = nodes.filter((n) => n.type === 'end')\n if (endNodes.length === 0) {\n errors.push({\n type: 'error',\n message: 'Workflow must have at least one END node',\n })\n }\n\n // Check for orphan nodes (no incoming or outgoing edges)\n for (const node of nodes) {\n if (node.type === 'start') continue // Start nodes don't need incoming edges\n if (node.type === 'end') continue // End nodes don't need outgoing edges\n\n const hasIncoming = edges.some((e) => e.target === node.id)\n const hasOutgoing = edges.some((e) => e.source === node.id)\n\n if (!hasIncoming && !hasOutgoing) {\n errors.push({\n type: 'error',\n message: `Node \"${node.data.label}\" is disconnected`,\n nodeId: node.id,\n })\n } else if (!hasIncoming) {\n errors.push({\n type: 'warning',\n message: `Node \"${node.data.label}\" has no incoming connections`,\n nodeId: node.id,\n })\n } else if (!hasOutgoing) {\n errors.push({\n type: 'warning',\n message: `Node \"${node.data.label}\" has no outgoing connections`,\n nodeId: node.id,\n })\n }\n }\n\n // Check for cycles (simple detection)\n const hasCycle = detectCycle(nodes, edges)\n if (hasCycle) {\n errors.push({\n type: 'warning',\n message: 'Workflow contains cycles (loops)',\n })\n }\n\n // Check for duplicate step IDs\n const stepIds = new Set<string>()\n for (const node of nodes) {\n if (stepIds.has(node.id)) {\n errors.push({\n type: 'error',\n message: `Duplicate step ID: ${node.id}`,\n nodeId: node.id,\n })\n }\n stepIds.add(node.id)\n }\n\n return errors\n}\n\n/**\n * Simple cycle detection using DFS\n */\nfunction detectCycle(nodes: Node[], edges: Edge[]): boolean {\n const adjList = new Map<string, string[]>()\n\n // Build adjacency list\n for (const node of nodes) {\n adjList.set(node.id, [])\n }\n for (const edge of edges) {\n const neighbors = adjList.get(edge.source) || []\n neighbors.push(edge.target)\n adjList.set(edge.source, neighbors)\n }\n\n const visited = new Set<string>()\n const recStack = new Set<string>()\n\n function dfs(nodeId: string): boolean {\n visited.add(nodeId)\n recStack.add(nodeId)\n\n const neighbors = adjList.get(nodeId) || []\n for (const neighbor of neighbors) {\n if (!visited.has(neighbor)) {\n if (dfs(neighbor)) return true\n } else if (recStack.has(neighbor)) {\n return true // Cycle detected\n }\n }\n\n recStack.delete(nodeId)\n return false\n }\n\n for (const node of nodes) {\n if (!visited.has(node.id)) {\n if (dfs(node.id)) return true\n }\n }\n\n return false\n}\n\n/**\n * Sanitize ID to match schema regex: /^[a-z0-9_-]+$/\n * Converts to lowercase, replaces invalid characters with underscores\n */\nexport function sanitizeId(input: string): string {\n return input\n .toLowerCase()\n .replace(/[^a-z0-9_-]/g, '_')\n .replace(/_{2,}/g, '_') // Replace multiple underscores with single\n .replace(/^_|_$/g, '') // Remove leading/trailing underscores\n}\n\n/**\n * Validate ID matches schema regex: /^[a-z0-9_-]+$/\n */\nexport function validateId(id: string): boolean {\n return /^[a-z0-9_-]+$/.test(id)\n}\n\n/**\n * Generate unique step ID\n */\nexport function generateStepId(prefix: string = 'step'): string {\n const id = `${prefix}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`\n return sanitizeId(id)\n}\n\n/**\n * Generate unique transition ID\n */\nexport function generateTransitionId(fromStepId: string, toStepId: string): string {\n const id = `e_${fromStepId}_${toStepId}`\n return sanitizeId(id)\n}\n"],
5
- "mappings": "AAqBO,SAAS,kBACd,OACA,OACA,UAAoC,CAAC,GACH;AAElC,QAAM,QAAQ,MAAM,IAAI,CAAC,SAAS;AAChC,UAAM,OAAY;AAAA,MAChB,QAAQ,KAAK;AAAA,MACb,UAAU,KAAK,KAAK,SAAS,KAAK;AAAA,MAClC,UAAU,sBAAsB,KAAK,QAAQ,WAAW;AAAA,IAC1D;AAGA,QAAI,KAAK,KAAK,aAAa;AACzB,WAAK,cAAc,KAAK,KAAK;AAAA,IAC/B;AAGA,QAAI,KAAK,KAAK,SAAS;AACrB,WAAK,UAAU,KAAK,KAAK;AAAA,IAC3B;AAGA,QAAI,KAAK,KAAK,aAAa;AACzB,WAAK,cAAc,KAAK,KAAK;AAAA,IAC/B;AAGA,QAAI,KAAK,KAAK,QAAQ;AACpB,WAAK,SAAS,KAAK,KAAK;AAAA,IAC1B;AAGA,QAAI,KAAK,SAAS,cAAc,KAAK,MAAM;AACzC,WAAK,iBAAiB;AAAA,QACpB,YAAY,KAAK,KAAK;AAAA,QACtB,iBAAiB,KAAK,KAAK,mBAAmB,CAAC;AAAA,QAC/C,SAAS,KAAK,KAAK;AAAA,QACnB,gBAAgB,KAAK,KAAK,kBAAkB,CAAC,YAAY,QAAQ;AAAA,MACnE;AAGA,UAAK,KAAK,KAAa,cAAe,KAAK,KAAa,gBAAgB,YAAY;AAClF,aAAK,eAAe,aAAc,KAAK,KAAa,cAAe,KAAK,KAAa,eAAe;AAAA,MACtG;AAGA,UAAK,KAAK,KAAa,kBAAmB,KAAK,KAAa,gBAAgB,gBAAgB;AAC1F,aAAK,eAAe,iBAAkB,KAAK,KAAa,kBAAmB,KAAK,KAAa,eAAe;AAAA,MAC9G;AAEA,UAAK,KAAK,KAAa,eAAgB,KAAK,KAAa,gBAAgB,aAAa;AACpF,aAAK,eAAe,cAAe,KAAK,KAAa,eAAgB,KAAK,KAAa,eAAe;AAAA,MACxG;AAEA,UAAK,KAAK,KAAa,mBAAoB,KAAK,KAAa,gBAAgB,iBAAiB;AAC5F,aAAK,eAAe,kBAAmB,KAAK,KAAa,mBAAoB,KAAK,KAAa,eAAe;AAAA,MAChH;AAAA,IACF;AAGA,QAAI,KAAK,SAAS,mBAAmB,KAAK,KAAK,cAAc;AAC3D,WAAK,eAAe,KAAK,KAAK;AAAA,IAChC;AAGA,QAAI,KAAK,SAAS,eAAe,KAAK,KAAK,YAAY;AACrD,WAAK,aAAa,KAAK,KAAK;AAAA,IAC9B;AAGA,QAAI,KAAK,SAAS,WAAY,KAAK,KAAa,iBAAkB,KAAK,KAAa,cAAc,SAAS,GAAG;AAC5G,WAAK,gBAAiB,KAAK,KAAa;AAAA,IAC1C;AAGA,QAAI,QAAQ,oBAAoB,KAAK,UAAU;AAC7C,WAAK,kBAAkB;AAAA,QACrB,GAAG,KAAK,SAAS;AAAA,QACjB,GAAG,KAAK,SAAS;AAAA,MACnB;AAAA,IACF;AAEA,WAAO;AAAA,EACT,CAAC;AAGD,QAAM,cAAc,MAAM,IAAI,CAAC,SAAS;AACtC,UAAM,WAAW,KAAK;AACtB,UAAM,aAAkB;AAAA,MACtB,cAAc,KAAK;AAAA,MACnB,YAAY,KAAK;AAAA,MACjB,UAAU,KAAK;AAAA,MACf,SAAS,UAAU,WAAW;AAAA,IAChC;AAGA,QAAI,UAAU,gBAAgB;AAC5B,iBAAW,iBAAiB,SAAS;AAAA,IACvC;AAGA,QAAI,UAAU,aAAa,QAAW;AACpC,iBAAW,WAAW,SAAS;AAAA,IACjC;AAGA,QAAI,UAAU,8BAA8B,QAAW;AACrD,iBAAW,4BAA4B,SAAS;AAAA,IAClD;AAGA,QAAI,UAAU,iBAAiB,SAAS,cAAc,SAAS,GAAG;AAChE,iBAAW,gBAAgB,SAAS;AAAA,IACtC;AAEA,QAAI,UAAU,kBAAkB,SAAS,eAAe,SAAS,GAAG;AAClE,iBAAW,iBAAiB,SAAS;AAAA,IACvC;AAGA,QAAI,UAAU,cAAc,SAAS,WAAW,SAAS,GAAG;AAC1D,iBAAW,aAAa,SAAS,WAAW,IAAI,CAAC,cAAmB;AAAA,QAClE,YAAY,SAAS;AAAA,QACrB,cAAc,SAAS;AAAA,QACvB,cAAc,SAAS;AAAA,QACvB,QAAQ,SAAS,UAAU,CAAC;AAAA;AAAA,QAE5B,GAAI,SAAS,UAAU,UAAa,EAAE,OAAO,SAAS,MAAM;AAAA,QAC5D,GAAI,SAAS,WAAW,EAAE,SAAS,SAAS,QAAQ;AAAA,QACpD,GAAI,SAAS,eAAe,EAAE,aAAa,SAAS,YAAY;AAAA,QAChE,GAAI,SAAS,eAAe,UAAa,EAAE,YAAY,SAAS,WAAW;AAAA,MAC7E,EAAE;AAAA,IACJ,OAAO;AAGL,YAAM,aAAa,MAAM,KAAK,OAAK,EAAE,OAAO,KAAK,MAAM;AACvD,UAAI,cAAc,WAAW,SAAS,eAAe,WAAW,MAAM;AACpE,YAAI,WAAW,KAAK,gBAAgB,WAAW,KAAK,YAAY;AAC9D,gBAAM,WAAgB;AAAA,YACpB,YAAY,WAAW,KAAK,cAAc,YAAY,WAAW,EAAE;AAAA,YACnE,cAAc,WAAW,KAAK,gBAAgB,WAAW,KAAK,SAAS;AAAA,YACvE,cAAc,WAAW,KAAK,gBAAgB;AAAA,YAC9C,QAAQ,WAAW,KAAK,kBAAkB,CAAC;AAAA,UAC7C;AAEA,cAAK,WAAW,KAAa,kBAAkB,QAAW;AACxD,qBAAS,QAAS,WAAW,KAAa;AAAA,UAC5C;AACA,cAAK,WAAW,KAAa,iBAAiB;AAC5C,qBAAS,UAAW,WAAW,KAAa;AAAA,UAC9C;AACA,cAAK,WAAW,KAAa,qBAAqB;AAChD,qBAAS,cAAe,WAAW,KAAa;AAAA,UAClD;AACA,cAAK,WAAW,KAAa,uBAAuB,QAAW;AAC7D,qBAAS,aAAc,WAAW,KAAa;AAAA,UACjD;AACA,qBAAW,aAAa,CAAC,QAAQ;AAAA,QACnC;AAAA,MACF;AAAA,IACF;AAGA,QAAI,UAAU,SAAS,CAAC,WAAW,gBAAgB;AACjD,iBAAW,iBAAiB,SAAS;AAAA,IACvC;AAEA,WAAO;AAAA,EACT,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,YAAY,CAAC;AAAA;AAAA,EACf;AACF;AAKO,SAAS,kBACd,YACA,UAAoC,CAAC,GACH;AAClC,QAAM,EAAE,aAAa,MAAM,gBAAgB,EAAE,UAAU,KAAK,YAAY,IAAI,EAAE,IAAI;AAGlF,QAAM,UAAU,IAAI,IAAI,WAAW,MAAM,IAAI,UAAQ,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC;AAGzE,QAAM,YAAY,aACd,qBAAqB,WAAW,OAAO,WAAW,aAAa,aAAa,IAC5E;AAGJ,QAAM,QAAgB,WAAW,MAAM,IAAI,CAAC,MAAM,UAAU;AAE1D,QAAI,WAAW,WAAW,IAAI,KAAK,MAAM,KAAK,EAAE,GAAG,KAAK,GAAG,KAAK,QAAQ,cAAc,SAAS;AAG/F,QAAI,CAAC,cAAe,KAAa,iBAAiB;AAChD,iBAAY,KAAa;AAAA,IAC3B;AAGA,UAAM,WAAW,sBAAsB,KAAK,QAAQ;AAGpD,UAAM,WAAgB;AAAA,MACpB,OAAO,KAAK;AAAA,MACZ,aAAc,KAAa;AAAA,MAC3B,YAAY,QAAQ,IAAI,QAAQ;AAAA,IAClC;AAGA,QAAK,KAAa,SAAS;AACzB,eAAS,UAAW,KAAa;AAAA,IACnC;AAGA,QAAK,KAAa,aAAa;AAC7B,eAAS,cAAe,KAAa;AAAA,IACvC;AAGA,QAAK,KAAa,QAAQ;AACxB,eAAS,SAAU,KAAa;AAAA,IAClC;AAGA,QAAI,KAAK,aAAa,eAAe,KAAK,gBAAgB;AACxD,eAAS,aAAa,KAAK,eAAe;AAC1C,eAAS,kBAAkB,KAAK,eAAe,mBAAmB,CAAC;AACnE,eAAS,UAAU,KAAK,eAAe;AACvC,eAAS,iBAAiB,KAAK,eAAe;AAG9C,eAAS,iBAAiB,KAAK;AAG/B,UAAI,KAAK,eAAe,YAAY;AAClC,iBAAS,aAAa,KAAK,eAAe;AAAA,MAC5C;AAGA,UAAI,KAAK,eAAe,gBAAgB;AACtC,iBAAS,iBAAiB,KAAK,eAAe;AAAA,MAChD;AAEA,UAAI,KAAK,eAAe,aAAa;AACnC,iBAAS,cAAc,KAAK,eAAe;AAAA,MAC7C;AAEA,UAAI,KAAK,eAAe,iBAAiB;AACvC,iBAAS,kBAAkB,KAAK,eAAe;AAAA,MACjD;AAAA,IACF;AAGA,QAAI,KAAK,aAAa,qBAAsB,KAAa,cAAc;AACrE,eAAS,eAAgB,KAAa;AAAA,IACxC;AAGA,QAAI,KAAK,aAAa,eAAgB,KAAa,YAAY;AAC7D,eAAS,aAAc,KAAa;AAAA,IACtC;AAGA,QAAI,KAAK,aAAa,WAAY,KAAa,eAAe;AAC5D,eAAS,gBAAiB,KAAa;AAAA,IACzC;AAGA,aAAS,QAAQ,oBAAoB,QAAQ;AAG7C,aAAS,SAAS;AAElB,WAAO;AAAA,MACL,IAAI,KAAK;AAAA,MACT,MAAM;AAAA,MACN;AAAA,MACA,MAAM;AAAA,IACR;AAAA,EACF,CAAC;AAGD,QAAM,QAAgB,WAAW,YAAY,IAAI,CAAC,eAAe;AAC/D,WAAO;AAAA,MACL,IAAI,WAAW;AAAA,MACf,QAAQ,WAAW;AAAA,MACnB,QAAQ,WAAW;AAAA,MACnB,MAAM;AAAA,MACN,MAAM;AAAA,QACJ,SAAS,WAAW;AAAA,QACpB,gBAAiB,WAAmB;AAAA,QACpC,UAAW,WAAmB,aAAa,SAAa,WAAmB,WAAW;AAAA,QACtF,2BAA4B,WAAmB,8BAA8B,SACxE,WAAmB,4BACpB;AAAA,QACJ,eAAe,WAAW,iBAAiB,CAAC;AAAA,QAC5C,gBAAgB,WAAW,kBAAkB,CAAC;AAAA,QAC9C,YAAY,WAAW,cAAc,CAAC;AAAA,QACtC,OAAQ,WAAmB,kBAAmB,WAAmB;AAAA;AAAA,QACjE,OAAQ,WAAmB,SAAS;AAAA;AAAA,MACtC;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO,EAAE,OAAO,MAAM;AACxB;AASA,SAAS,qBACP,OACA,aACA,SACuC;AACvC,QAAM,YAAY,oBAAI,IAAsC;AAE5D,MAAI,MAAM,WAAW,EAAG,QAAO;AAG/B,QAAM,WAAW,oBAAI,IAAsB;AAC3C,QAAM,WAAW,oBAAI,IAAsB;AAE3C,aAAW,QAAQ,OAAO;AACxB,aAAS,IAAI,KAAK,QAAQ,CAAC,CAAC;AAC5B,aAAS,IAAI,KAAK,QAAQ,CAAC,CAAC;AAAA,EAC9B;AAEA,aAAW,KAAK,aAAa;AAC3B,UAAM,WAAW,SAAS,IAAI,EAAE,UAAU,KAAK,CAAC;AAChD,aAAS,KAAK,EAAE,QAAQ;AACxB,aAAS,IAAI,EAAE,YAAY,QAAQ;AAEnC,UAAM,UAAU,SAAS,IAAI,EAAE,QAAQ,KAAK,CAAC;AAC7C,YAAQ,KAAK,EAAE,UAAU;AACzB,aAAS,IAAI,EAAE,UAAU,OAAO;AAAA,EAClC;AAGA,QAAM,aAAa,MAAM,OAAO,QAAM,SAAS,IAAI,EAAE,MAAM,KAAK,CAAC,GAAG,WAAW,CAAC;AAChF,MAAI,WAAW,WAAW,GAAG;AAE3B,eAAW,KAAK,MAAM,CAAC,CAAC;AAAA,EAC1B;AAGA,QAAM,SAAS,oBAAI,IAAoB;AACvC,QAAM,QAA8C,CAAC;AAErD,aAAW,SAAS,YAAY;AAC9B,UAAM,KAAK,EAAE,IAAI,MAAM,QAAQ,OAAO,EAAE,CAAC;AAAA,EAC3C;AAEA,SAAO,MAAM,SAAS,GAAG;AACvB,UAAM,EAAE,IAAI,MAAM,IAAI,MAAM,MAAM;AAClC,UAAM,eAAe,OAAO,IAAI,EAAE;AAGlC,QAAI,iBAAiB,UAAa,QAAQ,cAAc;AACtD,aAAO,IAAI,IAAI,KAAK;AAAA,IACtB;AAEA,UAAM,WAAW,SAAS,IAAI,EAAE,KAAK,CAAC;AACtC,eAAW,SAAS,UAAU;AAC5B,YAAM,KAAK,EAAE,IAAI,OAAO,OAAO,QAAQ,EAAE,CAAC;AAAA,IAC5C;AAAA,EACF;AAGA,QAAM,eAAe,oBAAI,IAAsB;AAC/C,aAAW,CAAC,QAAQ,KAAK,KAAK,QAAQ;AACpC,UAAM,eAAe,aAAa,IAAI,KAAK,KAAK,CAAC;AACjD,iBAAa,KAAK,MAAM;AACxB,iBAAa,IAAI,OAAO,YAAY;AAAA,EACtC;AAGA,QAAM,UAAU;AAChB,QAAM,SAAS;AAEf,aAAW,CAAC,OAAO,OAAO,KAAK,cAAc;AAC3C,UAAM,QAAQ,QAAQ;AACtB,UAAM,IAAI,SAAS,QAAQ,QAAQ;AAEnC,QAAI,UAAU,GAAG;AAEf,gBAAU,IAAI,QAAQ,CAAC,GAAG,EAAE,GAAG,SAAS,EAAE,CAAC;AAAA,IAC7C,OAAO;AAEL,YAAM,cAAc,QAAQ,KAAK,QAAQ;AACzC,YAAM,SAAS,UAAU,aAAa;AAGtC,cAAQ,KAAK,CAAC,GAAG,MAAM;AACrB,cAAM,WAAW,SAAS,IAAI,CAAC,KAAK,CAAC;AACrC,cAAM,WAAW,SAAS,IAAI,CAAC,KAAK,CAAC;AACrC,cAAM,aAAa,SAAS,SAAS,IAAK,UAAU,IAAI,SAAS,CAAC,CAAC,GAAG,KAAK,IAAK;AAChF,cAAM,aAAa,SAAS,SAAS,IAAK,UAAU,IAAI,SAAS,CAAC,CAAC,GAAG,KAAK,IAAK;AAChF,eAAO,aAAa;AAAA,MACtB,CAAC;AAED,cAAQ,QAAQ,CAAC,QAAQ,QAAQ;AAC/B,kBAAU,IAAI,QAAQ,EAAE,GAAG,SAAS,MAAM,QAAQ,YAAY,EAAE,CAAC;AAAA,MACnE,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,sBAAsB,UAA0B;AACvD,QAAM,UAAkC;AAAA,IACtC,OAAO;AAAA,IACP,KAAK;AAAA,IACL,UAAU;AAAA,IACV,WAAW;AAAA,IACX,UAAU;AAAA,IACV,eAAe;AAAA,EACjB;AACA,SAAO,QAAQ,QAAQ,KAAK;AAC9B;AAKA,SAAS,sBAAsB,UAA0B;AACvD,QAAM,UAAkC;AAAA,IACtC,OAAO;AAAA,IACP,KAAK;AAAA,IACL,WAAW;AAAA,IACX,WAAW;AAAA,IACX,UAAU;AAAA,IACV,iBAAiB;AAAA,EACnB;AACA,SAAO,QAAQ,QAAQ,KAAK;AAC9B;AAKA,SAAS,oBAAoB,UAA0B;AACrD,QAAM,SAAiC;AAAA,IACrC,OAAO;AAAA,IACP,KAAK;AAAA,IACL,UAAU;AAAA,IACV,WAAW;AAAA,IACX,UAAU;AAAA,IACV,eAAe;AAAA,EACjB;AACA,SAAO,OAAO,QAAQ,KAAK;AAC7B;AAYO,SAAS,sBAAsB,OAAe,OAAkC;AACrF,QAAM,SAA4B,CAAC;AAGnC,QAAM,aAAa,MAAM,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO;AACzD,MAAI,WAAW,WAAW,GAAG;AAC3B,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AACA,MAAI,WAAW,SAAS,GAAG;AACzB,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAGA,QAAM,WAAW,MAAM,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK;AACrD,MAAI,SAAS,WAAW,GAAG;AACzB,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAGA,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,SAAS,QAAS;AAC3B,QAAI,KAAK,SAAS,MAAO;AAEzB,UAAM,cAAc,MAAM,KAAK,CAAC,MAAM,EAAE,WAAW,KAAK,EAAE;AAC1D,UAAM,cAAc,MAAM,KAAK,CAAC,MAAM,EAAE,WAAW,KAAK,EAAE;AAE1D,QAAI,CAAC,eAAe,CAAC,aAAa;AAChC,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,SAAS,SAAS,KAAK,KAAK,KAAK;AAAA,QACjC,QAAQ,KAAK;AAAA,MACf,CAAC;AAAA,IACH,WAAW,CAAC,aAAa;AACvB,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,SAAS,SAAS,KAAK,KAAK,KAAK;AAAA,QACjC,QAAQ,KAAK;AAAA,MACf,CAAC;AAAA,IACH,WAAW,CAAC,aAAa;AACvB,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,SAAS,SAAS,KAAK,KAAK,KAAK;AAAA,QACjC,QAAQ,KAAK;AAAA,MACf,CAAC;AAAA,IACH;AAAA,EACF;AAGA,QAAM,WAAW,YAAY,OAAO,KAAK;AACzC,MAAI,UAAU;AACZ,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAGA,QAAM,UAAU,oBAAI,IAAY;AAChC,aAAW,QAAQ,OAAO;AACxB,QAAI,QAAQ,IAAI,KAAK,EAAE,GAAG;AACxB,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,SAAS,sBAAsB,KAAK,EAAE;AAAA,QACtC,QAAQ,KAAK;AAAA,MACf,CAAC;AAAA,IACH;AACA,YAAQ,IAAI,KAAK,EAAE;AAAA,EACrB;AAEA,SAAO;AACT;AAKA,SAAS,YAAY,OAAe,OAAwB;AAC1D,QAAM,UAAU,oBAAI,IAAsB;AAG1C,aAAW,QAAQ,OAAO;AACxB,YAAQ,IAAI,KAAK,IAAI,CAAC,CAAC;AAAA,EACzB;AACA,aAAW,QAAQ,OAAO;AACxB,UAAM,YAAY,QAAQ,IAAI,KAAK,MAAM,KAAK,CAAC;AAC/C,cAAU,KAAK,KAAK,MAAM;AAC1B,YAAQ,IAAI,KAAK,QAAQ,SAAS;AAAA,EACpC;AAEA,QAAM,UAAU,oBAAI,IAAY;AAChC,QAAM,WAAW,oBAAI,IAAY;AAEjC,WAAS,IAAI,QAAyB;AACpC,YAAQ,IAAI,MAAM;AAClB,aAAS,IAAI,MAAM;AAEnB,UAAM,YAAY,QAAQ,IAAI,MAAM,KAAK,CAAC;AAC1C,eAAW,YAAY,WAAW;AAChC,UAAI,CAAC,QAAQ,IAAI,QAAQ,GAAG;AAC1B,YAAI,IAAI,QAAQ,EAAG,QAAO;AAAA,MAC5B,WAAW,SAAS,IAAI,QAAQ,GAAG;AACjC,eAAO;AAAA,MACT;AAAA,IACF;AAEA,aAAS,OAAO,MAAM;AACtB,WAAO;AAAA,EACT;AAEA,aAAW,QAAQ,OAAO;AACxB,QAAI,CAAC,QAAQ,IAAI,KAAK,EAAE,GAAG;AACzB,UAAI,IAAI,KAAK,EAAE,EAAG,QAAO;AAAA,IAC3B;AAAA,EACF;AAEA,SAAO;AACT;AAMO,SAAS,WAAW,OAAuB;AAChD,SAAO,MACJ,YAAY,EACZ,QAAQ,gBAAgB,GAAG,EAC3B,QAAQ,UAAU,GAAG,EACrB,QAAQ,UAAU,EAAE;AACzB;AAKO,SAAS,WAAW,IAAqB;AAC9C,SAAO,gBAAgB,KAAK,EAAE;AAChC;AAKO,SAAS,eAAe,SAAiB,QAAgB;AAC9D,QAAM,KAAK,GAAG,MAAM,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,OAAO,GAAG,CAAC,CAAC;AAC7E,SAAO,WAAW,EAAE;AACtB;AAKO,SAAS,qBAAqB,YAAoB,UAA0B;AACjF,QAAM,KAAK,KAAK,UAAU,IAAI,QAAQ;AACtC,SAAO,WAAW,EAAE;AACtB;",
4
+ "sourcesContent": ["import { Node, Edge } from '@xyflow/react'\nimport type { WorkflowDefinition } from '../data/entities'\n\n/**\n * Graph Utilities for Visual Workflow Editor\n *\n * Converts between ReactFlow graph representation and workflow definition JSON\n */\n\nexport interface GraphToDefinitionOptions {\n includePositions?: boolean\n}\n\nexport interface DefinitionToGraphOptions {\n autoLayout?: boolean\n layoutSpacing?: { vertical: number; horizontal: number }\n}\n\n/**\n * Convert ReactFlow graph (nodes + edges) to workflow definition JSON\n */\nexport function graphToDefinition(\n nodes: Node[],\n edges: Edge[],\n options: GraphToDefinitionOptions = {}\n): WorkflowDefinition['definition'] {\n // Extract steps from nodes\n const steps = nodes.map((node) => {\n const step: any = {\n stepId: node.id,\n stepName: node.data.label || node.id,\n stepType: mapNodeTypeToStepType(node.type || 'automated'),\n }\n\n // Add step-specific configuration\n if (node.data.description) {\n step.description = node.data.description\n }\n\n // Add timeout if present\n if (node.data.timeout) {\n step.timeout = node.data.timeout\n }\n\n // Add retryPolicy if present\n if (node.data.retryPolicy) {\n step.retryPolicy = node.data.retryPolicy\n }\n\n // Add generic config if present\n if (node.data.config) {\n step.config = node.data.config\n }\n\n // User task configuration\n if (node.type === 'userTask' && node.data) {\n step.userTaskConfig = {\n assignedTo: node.data.assignedTo,\n assignedToRoles: node.data.assignedToRoles || [],\n formKey: node.data.formKey,\n allowedActions: node.data.allowedActions || ['complete', 'cancel'],\n }\n\n // Add form schema if present\n if ((node.data as any).formSchema || (node.data as any).userTaskConfig?.formSchema) {\n step.userTaskConfig.formSchema = (node.data as any).formSchema || (node.data as any).userTaskConfig.formSchema\n }\n\n // Add advanced fields if present\n if ((node.data as any).assignmentRule || (node.data as any).userTaskConfig?.assignmentRule) {\n step.userTaskConfig.assignmentRule = (node.data as any).assignmentRule || (node.data as any).userTaskConfig.assignmentRule\n }\n\n if ((node.data as any).slaDuration || (node.data as any).userTaskConfig?.slaDuration) {\n step.userTaskConfig.slaDuration = (node.data as any).slaDuration || (node.data as any).userTaskConfig.slaDuration\n }\n\n if ((node.data as any).escalationRules || (node.data as any).userTaskConfig?.escalationRules) {\n step.userTaskConfig.escalationRules = (node.data as any).escalationRules || (node.data as any).userTaskConfig.escalationRules\n }\n }\n\n // Wait for signal configuration\n if (node.type === 'waitForSignal' && node.data.signalConfig) {\n step.signalConfig = node.data.signalConfig\n }\n\n // Step activities (for AUTOMATED steps)\n if (node.type === 'automated' && node.data.activities) {\n step.activities = node.data.activities\n }\n\n // Pre-conditions (for START steps)\n if (node.type === 'start' && (node.data as any).preConditions && (node.data as any).preConditions.length > 0) {\n step.preConditions = (node.data as any).preConditions\n }\n\n // Store position for visual editor\n if (options.includePositions && node.position) {\n step._editorPosition = {\n x: node.position.x,\n y: node.position.y,\n }\n }\n\n return step\n })\n\n // Extract transitions from edges\n const transitions = edges.map((edge) => {\n const edgeData = edge.data as any\n const transition: any = {\n transitionId: edge.id,\n fromStepId: edge.source,\n toStepId: edge.target,\n trigger: edgeData?.trigger || 'auto',\n }\n\n // Add transition name if present\n if (edgeData?.transitionName) {\n transition.transitionName = edgeData.transitionName\n }\n\n // Add priority if present (default 0)\n if (edgeData?.priority !== undefined) {\n transition.priority = edgeData.priority\n }\n\n // Add continueOnActivityFailure if present (default true)\n if (edgeData?.continueOnActivityFailure !== undefined) {\n transition.continueOnActivityFailure = edgeData.continueOnActivityFailure\n }\n\n // Add conditions if present\n if (edgeData?.preConditions && edgeData.preConditions.length > 0) {\n transition.preConditions = edgeData.preConditions\n }\n\n if (edgeData?.postConditions && edgeData.postConditions.length > 0) {\n transition.postConditions = edgeData.postConditions\n }\n\n // Add activities if present in edge data\n if (edgeData?.activities && edgeData.activities.length > 0) {\n transition.activities = edgeData.activities.map((activity: any) => ({\n activityId: activity.activityId,\n activityName: activity.activityName,\n activityType: activity.activityType,\n config: activity.config || {},\n // Include all optional fields\n ...(activity.async !== undefined && { async: activity.async }),\n ...(activity.timeout && { timeout: activity.timeout }),\n ...(activity.retryPolicy && { retryPolicy: activity.retryPolicy }),\n ...(activity.compensate !== undefined && { compensate: activity.compensate }),\n }))\n } else {\n // Check if source node is automated and has activity data\n // If so, place the activity in this transition\n const sourceNode = nodes.find(n => n.id === edge.source)\n if (sourceNode && sourceNode.type === 'automated' && sourceNode.data) {\n if (sourceNode.data.activityType || sourceNode.data.activityId) {\n const activity: any = {\n activityId: sourceNode.data.activityId || `activity_${sourceNode.id}`,\n activityName: sourceNode.data.activityName || sourceNode.data.label || 'Automated Activity',\n activityType: sourceNode.data.activityType || 'CALL_API',\n config: sourceNode.data.activityConfig || {},\n }\n // Include optional activity fields from node data\n if ((sourceNode.data as any).activityAsync !== undefined) {\n activity.async = (sourceNode.data as any).activityAsync\n }\n if ((sourceNode.data as any).activityTimeout) {\n activity.timeout = (sourceNode.data as any).activityTimeout\n }\n if ((sourceNode.data as any).activityRetryPolicy) {\n activity.retryPolicy = (sourceNode.data as any).activityRetryPolicy\n }\n if ((sourceNode.data as any).activityCompensate !== undefined) {\n activity.compensate = (sourceNode.data as any).activityCompensate\n }\n transition.activities = [activity]\n }\n }\n }\n\n // Add label if present (legacy field, transitionName is preferred)\n if (edgeData?.label && !transition.transitionName) {\n transition.transitionName = edgeData.label\n }\n\n return transition\n })\n\n return {\n steps,\n transitions,\n activities: [], // Global activities can be added later\n }\n}\n\n/**\n * Convert workflow definition JSON to ReactFlow graph (nodes + edges)\n */\nexport function definitionToGraph(\n definition: WorkflowDefinition['definition'],\n options: DefinitionToGraphOptions = {}\n): { nodes: Node[]; edges: Edge[] } {\n const { autoLayout = true, layoutSpacing = { vertical: 250, horizontal: 100 } } = options\n\n // Convert steps to nodes\n const nodes: Node[] = definition.steps.map((step, index) => {\n // Determine position\n let position = { x: 250, y: 50 + index * layoutSpacing.vertical }\n\n // Use stored position if available and not auto-layouting\n if (!autoLayout && (step as any)._editorPosition) {\n position = (step as any)._editorPosition\n }\n\n // Map step type to node type\n const nodeType = mapStepTypeToNodeType(step.stepType)\n\n // Build node data\n const nodeData: any = {\n label: step.stepName,\n description: (step as any).description,\n stepNumber: index > 0 ? index : undefined,\n }\n\n // Add timeout if present\n if ((step as any).timeout) {\n nodeData.timeout = (step as any).timeout\n }\n\n // Add retryPolicy if present\n if ((step as any).retryPolicy) {\n nodeData.retryPolicy = (step as any).retryPolicy\n }\n\n // Add generic config if present\n if ((step as any).config) {\n nodeData.config = (step as any).config\n }\n\n // Add user task data\n if (step.stepType === 'USER_TASK' && step.userTaskConfig) {\n nodeData.assignedTo = step.userTaskConfig.assignedTo\n nodeData.assignedToRoles = step.userTaskConfig.assignedToRoles || []\n nodeData.formKey = step.userTaskConfig.formKey\n nodeData.allowedActions = step.userTaskConfig.allowedActions\n\n // Store full userTaskConfig for advanced fields\n nodeData.userTaskConfig = step.userTaskConfig\n\n // Add form schema if present\n if (step.userTaskConfig.formSchema) {\n nodeData.formSchema = step.userTaskConfig.formSchema\n }\n\n // Add advanced fields if present\n if (step.userTaskConfig.assignmentRule) {\n nodeData.assignmentRule = step.userTaskConfig.assignmentRule\n }\n\n if (step.userTaskConfig.slaDuration) {\n nodeData.slaDuration = step.userTaskConfig.slaDuration\n }\n\n if (step.userTaskConfig.escalationRules) {\n nodeData.escalationRules = step.userTaskConfig.escalationRules\n }\n }\n\n // Add wait for signal data\n if (step.stepType === 'WAIT_FOR_SIGNAL' && (step as any).signalConfig) {\n nodeData.signalConfig = (step as any).signalConfig\n }\n\n // Add step activities data (for AUTOMATED steps)\n if (step.stepType === 'AUTOMATED' && (step as any).activities) {\n nodeData.activities = (step as any).activities\n }\n\n // Add pre-conditions data (for START steps)\n if (step.stepType === 'START' && (step as any).preConditions) {\n nodeData.preConditions = (step as any).preConditions\n }\n\n // Set badge based on type\n nodeData.badge = getBadgeForNodeType(nodeType)\n\n // Default status is pending\n nodeData.status = 'pending'\n\n return {\n id: step.stepId,\n type: nodeType,\n position,\n data: nodeData,\n }\n })\n\n // Convert transitions to edges\n const edges: Edge[] = definition.transitions.map((transition) => {\n return {\n id: transition.transitionId,\n source: transition.fromStepId,\n target: transition.toStepId,\n type: 'workflowTransition',\n data: {\n trigger: transition.trigger,\n transitionName: (transition as any).transitionName,\n priority: (transition as any).priority !== undefined ? (transition as any).priority : 0,\n continueOnActivityFailure: (transition as any).continueOnActivityFailure !== undefined\n ? (transition as any).continueOnActivityFailure\n : true,\n preConditions: transition.preConditions || [],\n postConditions: transition.postConditions || [],\n activities: transition.activities || [],\n label: (transition as any).transitionName || (transition as any).label, // Backward compat\n state: (transition as any).state || 'pending', // Default edge state\n },\n }\n })\n\n return { nodes, edges }\n}\n\n/**\n * Map node type to step type (for graph \u2192 definition)\n */\nfunction mapNodeTypeToStepType(nodeType: string): string {\n const mapping: Record<string, string> = {\n start: 'START',\n end: 'END',\n userTask: 'USER_TASK',\n automated: 'AUTOMATED',\n decision: 'DECISION',\n waitForSignal: 'WAIT_FOR_SIGNAL',\n }\n return mapping[nodeType] || 'AUTOMATED'\n}\n\n/**\n * Map step type to node type (for definition \u2192 graph)\n */\nfunction mapStepTypeToNodeType(stepType: string): string {\n const mapping: Record<string, string> = {\n START: 'start',\n END: 'end',\n USER_TASK: 'userTask',\n AUTOMATED: 'automated',\n DECISION: 'decision',\n WAIT_FOR_SIGNAL: 'waitForSignal',\n }\n return mapping[stepType] || 'automated'\n}\n\n/**\n * Get badge text for node type\n */\nfunction getBadgeForNodeType(nodeType: string): string {\n const badges: Record<string, string> = {\n start: 'Start',\n end: 'End',\n userTask: 'User Task',\n automated: 'Automated',\n decision: 'Decision',\n waitForSignal: 'Wait for Signal',\n }\n return badges[nodeType] || 'Task'\n}\n\n/**\n * Validate workflow graph\n */\nexport interface ValidationError {\n type: 'error' | 'warning'\n message: string\n nodeId?: string\n edgeId?: string\n}\n\nexport function validateWorkflowGraph(nodes: Node[], edges: Edge[]): ValidationError[] {\n const errors: ValidationError[] = []\n\n // Check for at least one start node\n const startNodes = nodes.filter((n) => n.type === 'start')\n if (startNodes.length === 0) {\n errors.push({\n type: 'error',\n message: 'Workflow must have at least one START node',\n })\n }\n if (startNodes.length > 1) {\n errors.push({\n type: 'warning',\n message: 'Workflow has multiple START nodes',\n })\n }\n\n // Check for at least one end node\n const endNodes = nodes.filter((n) => n.type === 'end')\n if (endNodes.length === 0) {\n errors.push({\n type: 'error',\n message: 'Workflow must have at least one END node',\n })\n }\n\n // Check for orphan nodes (no incoming or outgoing edges)\n for (const node of nodes) {\n if (node.type === 'start') continue // Start nodes don't need incoming edges\n if (node.type === 'end') continue // End nodes don't need outgoing edges\n\n const hasIncoming = edges.some((e) => e.target === node.id)\n const hasOutgoing = edges.some((e) => e.source === node.id)\n\n if (!hasIncoming && !hasOutgoing) {\n errors.push({\n type: 'error',\n message: `Node \"${node.data.label}\" is disconnected`,\n nodeId: node.id,\n })\n } else if (!hasIncoming) {\n errors.push({\n type: 'warning',\n message: `Node \"${node.data.label}\" has no incoming connections`,\n nodeId: node.id,\n })\n } else if (!hasOutgoing) {\n errors.push({\n type: 'warning',\n message: `Node \"${node.data.label}\" has no outgoing connections`,\n nodeId: node.id,\n })\n }\n }\n\n // Check for cycles (simple detection)\n const hasCycle = detectCycle(nodes, edges)\n if (hasCycle) {\n errors.push({\n type: 'warning',\n message: 'Workflow contains cycles (loops)',\n })\n }\n\n // Check for duplicate step IDs\n const stepIds = new Set<string>()\n for (const node of nodes) {\n if (stepIds.has(node.id)) {\n errors.push({\n type: 'error',\n message: `Duplicate step ID: ${node.id}`,\n nodeId: node.id,\n })\n }\n stepIds.add(node.id)\n }\n\n return errors\n}\n\n/**\n * Simple cycle detection using DFS\n */\nfunction detectCycle(nodes: Node[], edges: Edge[]): boolean {\n const adjList = new Map<string, string[]>()\n\n // Build adjacency list\n for (const node of nodes) {\n adjList.set(node.id, [])\n }\n for (const edge of edges) {\n const neighbors = adjList.get(edge.source) || []\n neighbors.push(edge.target)\n adjList.set(edge.source, neighbors)\n }\n\n const visited = new Set<string>()\n const recStack = new Set<string>()\n\n function dfs(nodeId: string): boolean {\n visited.add(nodeId)\n recStack.add(nodeId)\n\n const neighbors = adjList.get(nodeId) || []\n for (const neighbor of neighbors) {\n if (!visited.has(neighbor)) {\n if (dfs(neighbor)) return true\n } else if (recStack.has(neighbor)) {\n return true // Cycle detected\n }\n }\n\n recStack.delete(nodeId)\n return false\n }\n\n for (const node of nodes) {\n if (!visited.has(node.id)) {\n if (dfs(node.id)) return true\n }\n }\n\n return false\n}\n\n/**\n * Sanitize ID to match schema regex: /^[a-z0-9_-]+$/\n * Converts to lowercase, replaces invalid characters with underscores\n */\nexport function sanitizeId(input: string): string {\n return input\n .toLowerCase()\n .replace(/[^a-z0-9_-]/g, '_')\n .replace(/_{2,}/g, '_') // Replace multiple underscores with single\n .replace(/^_|_$/g, '') // Remove leading/trailing underscores\n}\n\n/**\n * Validate ID matches schema regex: /^[a-z0-9_-]+$/\n */\nexport function validateId(id: string): boolean {\n return /^[a-z0-9_-]+$/.test(id)\n}\n\n/**\n * Generate unique step ID\n */\nexport function generateStepId(prefix: string = 'step'): string {\n const id = `${prefix}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`\n return sanitizeId(id)\n}\n\n/**\n * Generate unique transition ID\n */\nexport function generateTransitionId(fromStepId: string, toStepId: string): string {\n const id = `e_${fromStepId}_${toStepId}`\n return sanitizeId(id)\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -6,15 +6,11 @@ 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";
11
9
  const embeddedSeeds = {
12
10
  "checkout-demo-definition.json": checkoutDemoDefinition,
13
11
  "guard-rules-example.json": guardRulesExample,
14
12
  "sales-pipeline-definition.json": salesPipelineDefinition,
15
- "simple-approval-definition.json": simpleApprovalDefinition,
16
- "order-approval-definition.json": orderApprovalDefinition,
17
- "order-approval-guard-rules.json": orderApprovalGuardRules
13
+ "simple-approval-definition.json": simpleApprovalDefinition
18
14
  };
19
15
  function readExampleJson(fileName) {
20
16
  const embedded = embeddedSeeds[fileName];
@@ -45,23 +41,12 @@ async function seedWorkflowDefinition(em, scope, fileName) {
45
41
  organizationId: scope.organizationId
46
42
  });
47
43
  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
- );
58
44
  const seedStartStep = seed.definition.steps.find((s) => s.stepType === "START");
59
45
  const existingStartStep = existing.definition.steps.find((s) => s.stepType === "START");
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})`);
46
+ const seedHasPreConditions = seedStartStep?.preConditions && seedStartStep.preConditions.length > 0;
47
+ const existingHasPreConditions = existingStartStep?.preConditions && existingStartStep.preConditions.length > 0;
48
+ if (seedHasPreConditions && !existingHasPreConditions) {
49
+ console.log(`[seed] Updating workflow ${workflowId} with preConditions`);
65
50
  existing.definition = seed.definition;
66
51
  await em.flush();
67
52
  return true;
@@ -124,8 +109,6 @@ async function seedExampleWorkflows(em, scope) {
124
109
  await seedGuardRules(em, scope, "guard-rules-example.json");
125
110
  await seedWorkflowDefinition(em, scope, "sales-pipeline-definition.json");
126
111
  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");
129
112
  }
130
113
  export {
131
114
  seedExampleWorkflows