@open-mercato/core 0.4.2-canary-51881f6bf3 → 0.4.2-canary-f728a6d66a

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 (155) hide show
  1. package/dist/generated/entities/workflow_event_trigger/index.js +33 -0
  2. package/dist/generated/entities/workflow_event_trigger/index.js.map +7 -0
  3. package/dist/generated/entities.ids.generated.js +59 -58
  4. package/dist/generated/entities.ids.generated.js.map +2 -2
  5. package/dist/generated/entity-fields-registry.js +2 -0
  6. package/dist/generated/entity-fields-registry.js.map +2 -2
  7. package/dist/modules/auth/events.js +30 -0
  8. package/dist/modules/auth/events.js.map +7 -0
  9. package/dist/modules/business_rules/api/execute/[ruleId]/route.js +150 -0
  10. package/dist/modules/business_rules/api/execute/[ruleId]/route.js.map +7 -0
  11. package/dist/modules/business_rules/data/validators.js +34 -0
  12. package/dist/modules/business_rules/data/validators.js.map +2 -2
  13. package/dist/modules/business_rules/index.js +21 -1
  14. package/dist/modules/business_rules/index.js.map +2 -2
  15. package/dist/modules/business_rules/lib/rule-engine.js +182 -1
  16. package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
  17. package/dist/modules/catalog/events.js +34 -0
  18. package/dist/modules/catalog/events.js.map +7 -0
  19. package/dist/modules/customers/events.js +49 -0
  20. package/dist/modules/customers/events.js.map +7 -0
  21. package/dist/modules/directory/events.js +23 -0
  22. package/dist/modules/directory/events.js.map +7 -0
  23. package/dist/modules/sales/acl.js +1 -0
  24. package/dist/modules/sales/acl.js.map +2 -2
  25. package/dist/modules/sales/backend/sales/documents/[id]/page.js +12 -0
  26. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  27. package/dist/modules/sales/commands/documents.js +62 -0
  28. package/dist/modules/sales/commands/documents.js.map +2 -2
  29. package/dist/modules/sales/events.js +63 -0
  30. package/dist/modules/sales/events.js.map +7 -0
  31. package/dist/modules/sales/lib/dictionaries.js +3 -0
  32. package/dist/modules/sales/lib/dictionaries.js.map +2 -2
  33. package/dist/modules/sales/lib/frontend/documentDataEvents.js +25 -0
  34. package/dist/modules/sales/lib/frontend/documentDataEvents.js.map +7 -0
  35. package/dist/modules/workflows/acl.js +2 -0
  36. package/dist/modules/workflows/acl.js.map +2 -2
  37. package/dist/modules/workflows/api/instances/route.js +18 -6
  38. package/dist/modules/workflows/api/instances/route.js.map +2 -2
  39. package/dist/modules/workflows/api/tasks/route.js +6 -1
  40. package/dist/modules/workflows/api/tasks/route.js.map +2 -2
  41. package/dist/modules/workflows/backend/definitions/[id]/page.js +9 -1
  42. package/dist/modules/workflows/backend/definitions/[id]/page.js.map +2 -2
  43. package/dist/modules/workflows/backend/definitions/[id]/page.meta.js +1 -1
  44. package/dist/modules/workflows/backend/definitions/[id]/page.meta.js.map +2 -2
  45. package/dist/modules/workflows/backend/definitions/create/page.js +24 -15
  46. package/dist/modules/workflows/backend/definitions/create/page.js.map +2 -2
  47. package/dist/modules/workflows/backend/definitions/create/page.meta.js +1 -1
  48. package/dist/modules/workflows/backend/definitions/create/page.meta.js.map +2 -2
  49. package/dist/modules/workflows/backend/definitions/visual-editor/page.js +150 -132
  50. package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
  51. package/dist/modules/workflows/backend/definitions/visual-editor/page.meta.js +1 -1
  52. package/dist/modules/workflows/backend/definitions/visual-editor/page.meta.js.map +2 -2
  53. package/dist/modules/workflows/backend/events/[id]/page.js +1 -1
  54. package/dist/modules/workflows/backend/events/[id]/page.js.map +2 -2
  55. package/dist/modules/workflows/backend/events/[id]/page.meta.js +2 -2
  56. package/dist/modules/workflows/backend/events/[id]/page.meta.js.map +2 -2
  57. package/dist/modules/workflows/backend/instances/[id]/page.meta.js +2 -2
  58. package/dist/modules/workflows/backend/instances/[id]/page.meta.js.map +2 -2
  59. package/dist/modules/workflows/backend/tasks/[id]/page.js +1 -1
  60. package/dist/modules/workflows/backend/tasks/[id]/page.js.map +2 -2
  61. package/dist/modules/workflows/backend/tasks/[id]/page.meta.js +2 -2
  62. package/dist/modules/workflows/backend/tasks/[id]/page.meta.js.map +2 -2
  63. package/dist/modules/workflows/backend/tasks/page.js +5 -6
  64. package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
  65. package/dist/modules/workflows/cli.js +81 -3
  66. package/dist/modules/workflows/cli.js.map +3 -3
  67. package/dist/modules/workflows/components/DefinitionTriggersEditor.js +481 -0
  68. package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +7 -0
  69. package/dist/modules/workflows/components/EventTriggersEditor.js +553 -0
  70. package/dist/modules/workflows/components/EventTriggersEditor.js.map +7 -0
  71. package/dist/modules/workflows/data/entities.js +64 -1
  72. package/dist/modules/workflows/data/entities.js.map +2 -2
  73. package/dist/modules/workflows/data/validators.js +115 -0
  74. package/dist/modules/workflows/data/validators.js.map +2 -2
  75. package/dist/modules/workflows/events.js +38 -0
  76. package/dist/modules/workflows/events.js.map +7 -0
  77. package/dist/modules/workflows/examples/checkout-demo-definition.json +1 -5
  78. package/dist/modules/workflows/examples/order-approval-definition.json +257 -0
  79. package/dist/modules/workflows/examples/order-approval-guard-rules.json +32 -0
  80. package/dist/modules/workflows/lib/activity-executor.js +75 -13
  81. package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
  82. package/dist/modules/workflows/lib/event-trigger-service.js +308 -0
  83. package/dist/modules/workflows/lib/event-trigger-service.js.map +7 -0
  84. package/dist/modules/workflows/lib/graph-utils.js +71 -2
  85. package/dist/modules/workflows/lib/graph-utils.js.map +2 -2
  86. package/dist/modules/workflows/lib/seeds.js +22 -5
  87. package/dist/modules/workflows/lib/seeds.js.map +2 -2
  88. package/dist/modules/workflows/lib/start-validator.js +33 -23
  89. package/dist/modules/workflows/lib/start-validator.js.map +2 -2
  90. package/dist/modules/workflows/lib/transition-handler.js +157 -45
  91. package/dist/modules/workflows/lib/transition-handler.js.map +3 -3
  92. package/dist/modules/workflows/migrations/Migration20260123143500.js +36 -0
  93. package/dist/modules/workflows/migrations/Migration20260123143500.js.map +7 -0
  94. package/dist/modules/workflows/subscribers/event-trigger.js +78 -0
  95. package/dist/modules/workflows/subscribers/event-trigger.js.map +7 -0
  96. package/dist/modules/workflows/widgets/injection/order-approval/widget.client.js +323 -0
  97. package/dist/modules/workflows/widgets/injection/order-approval/widget.client.js.map +7 -0
  98. package/dist/modules/workflows/widgets/injection/order-approval/widget.js +17 -0
  99. package/dist/modules/workflows/widgets/injection/order-approval/widget.js.map +7 -0
  100. package/dist/modules/workflows/widgets/injection-table.js +19 -0
  101. package/dist/modules/workflows/widgets/injection-table.js.map +7 -0
  102. package/generated/entities/workflow_event_trigger/index.ts +15 -0
  103. package/generated/entities.ids.generated.ts +59 -58
  104. package/generated/entity-fields-registry.ts +2 -0
  105. package/package.json +3 -5
  106. package/src/modules/auth/events.ts +39 -0
  107. package/src/modules/business_rules/api/execute/[ruleId]/route.ts +168 -0
  108. package/src/modules/business_rules/data/validators.ts +40 -0
  109. package/src/modules/business_rules/index.ts +25 -0
  110. package/src/modules/business_rules/lib/rule-engine.ts +281 -1
  111. package/src/modules/catalog/events.ts +45 -0
  112. package/src/modules/customers/events.ts +63 -0
  113. package/src/modules/directory/events.ts +31 -0
  114. package/src/modules/sales/acl.ts +1 -0
  115. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +16 -0
  116. package/src/modules/sales/commands/documents.ts +74 -1
  117. package/src/modules/sales/events.ts +82 -0
  118. package/src/modules/sales/lib/dictionaries.ts +3 -0
  119. package/src/modules/sales/lib/frontend/documentDataEvents.ts +28 -0
  120. package/src/modules/workflows/acl.ts +2 -0
  121. package/src/modules/workflows/api/instances/route.ts +21 -7
  122. package/src/modules/workflows/api/tasks/route.ts +7 -1
  123. package/src/modules/workflows/backend/definitions/[id]/page.meta.ts +1 -1
  124. package/src/modules/workflows/backend/definitions/[id]/page.tsx +9 -0
  125. package/src/modules/workflows/backend/definitions/create/page.meta.ts +1 -1
  126. package/src/modules/workflows/backend/definitions/create/page.tsx +9 -0
  127. package/src/modules/workflows/backend/definitions/visual-editor/page.meta.ts +1 -1
  128. package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +21 -3
  129. package/src/modules/workflows/backend/events/[id]/page.meta.ts +2 -2
  130. package/src/modules/workflows/backend/events/[id]/page.tsx +1 -1
  131. package/src/modules/workflows/backend/instances/[id]/page.meta.ts +2 -2
  132. package/src/modules/workflows/backend/tasks/[id]/page.meta.ts +2 -2
  133. package/src/modules/workflows/backend/tasks/[id]/page.tsx +1 -1
  134. package/src/modules/workflows/backend/tasks/page.tsx +5 -6
  135. package/src/modules/workflows/cli.ts +105 -0
  136. package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +581 -0
  137. package/src/modules/workflows/components/EventTriggersEditor.tsx +664 -0
  138. package/src/modules/workflows/data/entities.ts +123 -0
  139. package/src/modules/workflows/data/validators.ts +138 -0
  140. package/src/modules/workflows/events.ts +49 -0
  141. package/src/modules/workflows/examples/checkout-demo-definition.json +1 -5
  142. package/src/modules/workflows/examples/order-approval-definition.json +257 -0
  143. package/src/modules/workflows/examples/order-approval-guard-rules.json +32 -0
  144. package/src/modules/workflows/i18n/en.json +71 -0
  145. package/src/modules/workflows/lib/activity-executor.ts +129 -16
  146. package/src/modules/workflows/lib/event-trigger-service.ts +557 -0
  147. package/src/modules/workflows/lib/graph-utils.ts +117 -2
  148. package/src/modules/workflows/lib/seeds.ts +34 -8
  149. package/src/modules/workflows/lib/start-validator.ts +38 -28
  150. package/src/modules/workflows/lib/transition-handler.ts +208 -55
  151. package/src/modules/workflows/migrations/Migration20260123143500.ts +38 -0
  152. package/src/modules/workflows/subscribers/event-trigger.ts +109 -0
  153. package/src/modules/workflows/widgets/injection/order-approval/widget.client.tsx +446 -0
  154. package/src/modules/workflows/widgets/injection/order-approval/widget.ts +16 -0
  155. package/src/modules/workflows/widgets/injection-table.ts +21 -0
@@ -0,0 +1,308 @@
1
+ import {
2
+ WorkflowEventTrigger,
3
+ WorkflowDefinition,
4
+ WorkflowInstance
5
+ } from "../data/entities.js";
6
+ import { startWorkflow, executeWorkflow } from "./workflow-executor.js";
7
+ const TRIGGER_CACHE_TTL = 5 * 60 * 1e3;
8
+ function matchEventPattern(eventName, pattern) {
9
+ if (pattern === "*") return true;
10
+ if (pattern === eventName) return true;
11
+ if (!pattern.includes("*")) return false;
12
+ const regexPattern = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, "[^.]+");
13
+ const regex = new RegExp(`^${regexPattern}$`);
14
+ return regex.test(eventName);
15
+ }
16
+ function getNestedValue(obj, path) {
17
+ if (obj === null || obj === void 0) return void 0;
18
+ const parts = path.split(".");
19
+ let current = obj;
20
+ for (const part of parts) {
21
+ if (current === null || current === void 0) return void 0;
22
+ if (typeof current !== "object") return void 0;
23
+ current = current[part];
24
+ }
25
+ return current;
26
+ }
27
+ function evaluateCondition(condition, payload) {
28
+ const value = getNestedValue(payload, condition.field);
29
+ const expected = condition.value;
30
+ switch (condition.operator) {
31
+ case "eq":
32
+ return value === expected;
33
+ case "neq":
34
+ return value !== expected;
35
+ case "gt":
36
+ return typeof value === "number" && typeof expected === "number" && value > expected;
37
+ case "gte":
38
+ return typeof value === "number" && typeof expected === "number" && value >= expected;
39
+ case "lt":
40
+ return typeof value === "number" && typeof expected === "number" && value < expected;
41
+ case "lte":
42
+ return typeof value === "number" && typeof expected === "number" && value <= expected;
43
+ case "contains":
44
+ if (typeof value === "string" && typeof expected === "string") {
45
+ return value.includes(expected);
46
+ }
47
+ if (Array.isArray(value)) {
48
+ return value.includes(expected);
49
+ }
50
+ return false;
51
+ case "startsWith":
52
+ return typeof value === "string" && typeof expected === "string" && value.startsWith(expected);
53
+ case "endsWith":
54
+ return typeof value === "string" && typeof expected === "string" && value.endsWith(expected);
55
+ case "in":
56
+ return Array.isArray(expected) && expected.includes(value);
57
+ case "notIn":
58
+ return Array.isArray(expected) && !expected.includes(value);
59
+ case "exists":
60
+ return value !== void 0 && value !== null;
61
+ case "notExists":
62
+ return value === void 0 || value === null;
63
+ case "regex":
64
+ if (typeof value !== "string" || typeof expected !== "string") return false;
65
+ try {
66
+ const regex = new RegExp(expected);
67
+ return regex.test(value);
68
+ } catch {
69
+ return false;
70
+ }
71
+ default:
72
+ return false;
73
+ }
74
+ }
75
+ function evaluateFilterConditions(conditions, payload) {
76
+ if (!conditions || conditions.length === 0) return true;
77
+ return conditions.every((condition) => evaluateCondition(condition, payload));
78
+ }
79
+ function mapEventToContext(mapping, payload) {
80
+ const context = {};
81
+ if (!mapping || mapping.length === 0) return context;
82
+ for (const item of mapping) {
83
+ const value = getNestedValue(payload, item.sourceExpression);
84
+ context[item.targetKey] = value !== void 0 ? value : item.defaultValue;
85
+ }
86
+ return context;
87
+ }
88
+ const triggerCache = /* @__PURE__ */ new Map();
89
+ function getCacheKey(tenantId, organizationId) {
90
+ return `${tenantId}:${organizationId}`;
91
+ }
92
+ async function loadLegacyTriggers(em, tenantId, organizationId) {
93
+ const legacyTriggers = await em.find(
94
+ WorkflowEventTrigger,
95
+ {
96
+ tenantId,
97
+ organizationId,
98
+ enabled: true,
99
+ deletedAt: null
100
+ },
101
+ {
102
+ orderBy: { priority: "DESC", createdAt: "ASC" }
103
+ }
104
+ );
105
+ const definitionIds = [...new Set(legacyTriggers.map((t) => t.workflowDefinitionId))];
106
+ const definitions = definitionIds.length > 0 ? await em.find(WorkflowDefinition, {
107
+ id: { $in: definitionIds },
108
+ enabled: true,
109
+ deletedAt: null
110
+ }) : [];
111
+ const definitionMap = new Map(definitions.map((d) => [d.id, d]));
112
+ return legacyTriggers.filter((t) => definitionMap.has(t.workflowDefinitionId)).map((t) => {
113
+ const def = definitionMap.get(t.workflowDefinitionId);
114
+ return {
115
+ id: t.id,
116
+ triggerId: t.id,
117
+ name: t.name,
118
+ description: t.description,
119
+ eventPattern: t.eventPattern,
120
+ config: t.config ?? null,
121
+ enabled: t.enabled,
122
+ priority: t.priority,
123
+ workflowDefinitionId: t.workflowDefinitionId,
124
+ workflowId: def.workflowId,
125
+ workflowVersion: def.version,
126
+ source: "legacy",
127
+ tenantId: t.tenantId,
128
+ organizationId: t.organizationId
129
+ };
130
+ });
131
+ }
132
+ async function loadEmbeddedTriggers(em, tenantId, organizationId) {
133
+ const definitions = await em.find(
134
+ WorkflowDefinition,
135
+ {
136
+ tenantId,
137
+ organizationId,
138
+ enabled: true,
139
+ deletedAt: null
140
+ }
141
+ );
142
+ const triggers = [];
143
+ for (const def of definitions) {
144
+ const embeddedTriggers = def.definition?.triggers;
145
+ if (!embeddedTriggers || embeddedTriggers.length === 0) continue;
146
+ for (const trigger of embeddedTriggers) {
147
+ if (!trigger.enabled) continue;
148
+ triggers.push({
149
+ id: `${def.id}:${trigger.triggerId}`,
150
+ triggerId: trigger.triggerId,
151
+ name: trigger.name,
152
+ description: trigger.description ?? null,
153
+ eventPattern: trigger.eventPattern,
154
+ config: trigger.config ?? null,
155
+ enabled: trigger.enabled,
156
+ priority: trigger.priority,
157
+ workflowDefinitionId: def.id,
158
+ workflowId: def.workflowId,
159
+ workflowVersion: def.version,
160
+ source: "embedded",
161
+ tenantId,
162
+ organizationId
163
+ });
164
+ }
165
+ }
166
+ return triggers;
167
+ }
168
+ async function loadTriggersForTenant(em, tenantId, organizationId, cacheService) {
169
+ const cacheKey = getCacheKey(tenantId, organizationId);
170
+ const cached = triggerCache.get(cacheKey);
171
+ if (cached && Date.now() - cached.cachedAt < TRIGGER_CACHE_TTL) {
172
+ return cached.triggers;
173
+ }
174
+ const [legacyTriggers, embeddedTriggers] = await Promise.all([
175
+ loadLegacyTriggers(em, tenantId, organizationId),
176
+ loadEmbeddedTriggers(em, tenantId, organizationId)
177
+ ]);
178
+ const allTriggers = [...legacyTriggers, ...embeddedTriggers].sort((a, b) => b.priority - a.priority);
179
+ triggerCache.set(cacheKey, {
180
+ triggers: allTriggers,
181
+ cachedAt: Date.now()
182
+ });
183
+ return allTriggers;
184
+ }
185
+ function invalidateTriggerCache(tenantId, organizationId) {
186
+ if (organizationId) {
187
+ const cacheKey = getCacheKey(tenantId, organizationId);
188
+ triggerCache.delete(cacheKey);
189
+ } else {
190
+ for (const key of triggerCache.keys()) {
191
+ if (key.startsWith(`${tenantId}:`)) {
192
+ triggerCache.delete(key);
193
+ }
194
+ }
195
+ }
196
+ }
197
+ async function findMatchingTriggers(em, context) {
198
+ const triggers = await loadTriggersForTenant(
199
+ em,
200
+ context.tenantId,
201
+ context.organizationId
202
+ );
203
+ return triggers.filter((trigger) => {
204
+ if (!matchEventPattern(context.eventName, trigger.eventPattern)) {
205
+ return false;
206
+ }
207
+ if (!evaluateFilterConditions(trigger.config?.filterConditions, context.payload)) {
208
+ return false;
209
+ }
210
+ return true;
211
+ });
212
+ }
213
+ async function checkConcurrencyLimit(em, trigger) {
214
+ const maxInstances = trigger.config?.maxConcurrentInstances;
215
+ if (!maxInstances) return true;
216
+ const runningCount = await em.count(WorkflowInstance, {
217
+ definitionId: trigger.workflowDefinitionId,
218
+ status: { $in: ["RUNNING", "WAITING_FOR_ACTIVITIES"] },
219
+ tenantId: trigger.tenantId,
220
+ organizationId: trigger.organizationId,
221
+ deletedAt: null
222
+ });
223
+ return runningCount < maxInstances;
224
+ }
225
+ async function processEventTriggers(em, container, context) {
226
+ const result = {
227
+ triggered: 0,
228
+ skipped: 0,
229
+ errors: [],
230
+ instances: []
231
+ };
232
+ const triggers = await findMatchingTriggers(em, context);
233
+ if (triggers.length === 0) {
234
+ return result;
235
+ }
236
+ for (const trigger of triggers) {
237
+ try {
238
+ const canStart = await checkConcurrencyLimit(em, trigger);
239
+ if (!canStart) {
240
+ console.log(`[workflow-trigger] Skipping trigger "${trigger.name}": max concurrent instances reached`);
241
+ result.skipped++;
242
+ continue;
243
+ }
244
+ const mappedContext = mapEventToContext(trigger.config?.contextMapping, context.payload);
245
+ const payloadId = context.payload?.id;
246
+ const payloadEntityType = context.payload?.entityType;
247
+ const initialContext = {
248
+ // Include raw event payload fields (e.g., id, organizationId, tenantId)
249
+ ...context.payload,
250
+ // Override with explicit mappings if provided
251
+ ...mappedContext,
252
+ __trigger: {
253
+ triggerId: trigger.id,
254
+ triggerName: trigger.name,
255
+ eventName: context.eventName,
256
+ eventPayload: context.payload,
257
+ triggeredAt: (/* @__PURE__ */ new Date()).toISOString(),
258
+ source: trigger.source
259
+ }
260
+ };
261
+ const instance = await startWorkflow(em, {
262
+ workflowId: trigger.workflowId,
263
+ version: trigger.workflowVersion,
264
+ initialContext,
265
+ metadata: {
266
+ initiatedBy: `trigger:${trigger.id}`,
267
+ // Include entityId and entityType for widget discovery
268
+ entityId: payloadId,
269
+ entityType: payloadEntityType || trigger.config?.entityType,
270
+ labels: {
271
+ trigger_id: trigger.id,
272
+ trigger_name: trigger.name,
273
+ event_name: context.eventName,
274
+ trigger_source: trigger.source
275
+ }
276
+ },
277
+ tenantId: context.tenantId,
278
+ organizationId: context.organizationId
279
+ });
280
+ result.triggered++;
281
+ result.instances.push({
282
+ triggerId: trigger.id,
283
+ instanceId: instance.id
284
+ });
285
+ executeWorkflow(em.fork(), container, instance.id).catch((err) => {
286
+ console.error(`[workflow-trigger] Error executing workflow ${instance.id}:`, err);
287
+ });
288
+ } catch (error) {
289
+ const errorMessage = error instanceof Error ? error.message : String(error);
290
+ console.error(`[workflow-trigger] Error processing trigger "${trigger.name}":`, error);
291
+ result.errors.push({
292
+ triggerId: trigger.id,
293
+ error: errorMessage
294
+ });
295
+ }
296
+ }
297
+ return result;
298
+ }
299
+ export {
300
+ evaluateFilterConditions,
301
+ findMatchingTriggers,
302
+ invalidateTriggerCache,
303
+ loadTriggersForTenant,
304
+ mapEventToContext,
305
+ matchEventPattern,
306
+ processEventTriggers
307
+ };
308
+ //# sourceMappingURL=event-trigger-service.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/workflows/lib/event-trigger-service.ts"],
4
+ "sourcesContent": ["/**\n * Workflows Module - Event Trigger Service\n *\n * Core service for evaluating and processing workflow event triggers.\n * Handles pattern matching, filter evaluation, context mapping, and workflow starting.\n */\n\nimport type { EntityManager } from '@mikro-orm/core'\nimport type { AwilixContainer } from 'awilix'\nimport type { CacheService } from '@open-mercato/cache'\nimport {\n WorkflowEventTrigger,\n WorkflowDefinition,\n WorkflowInstance,\n type TriggerFilterCondition,\n type TriggerContextMapping,\n type WorkflowEventTriggerConfig,\n type WorkflowDefinitionTrigger,\n} from '../data/entities'\nimport { startWorkflow, executeWorkflow } from './workflow-executor'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface EventTriggerContext {\n eventName: string\n payload: Record<string, unknown>\n tenantId: string\n organizationId: string\n}\n\nexport interface ProcessTriggersResult {\n triggered: number\n skipped: number\n errors: Array<{ triggerId: string; error: string }>\n instances: Array<{ triggerId: string; instanceId: string }>\n}\n\n/**\n * Unified trigger interface for both legacy (entity) and embedded (definition) triggers\n */\nexport interface UnifiedTrigger {\n id: string // For legacy: entity ID, for embedded: `${definitionId}:${triggerId}`\n triggerId: string\n name: string\n description?: string | null\n eventPattern: string\n config: WorkflowEventTriggerConfig | null\n enabled: boolean\n priority: number\n workflowDefinitionId: string\n workflowId: string\n workflowVersion: number\n source: 'legacy' | 'embedded'\n tenantId: string\n organizationId: string\n}\n\ninterface CachedTriggers {\n triggers: UnifiedTrigger[]\n cachedAt: number\n}\n\n// Cache TTL: 5 minutes\nconst TRIGGER_CACHE_TTL = 5 * 60 * 1000\n\n// ============================================================================\n// Pattern Matching\n// ============================================================================\n\n/**\n * Match an event name against a pattern.\n *\n * Supports:\n * - Exact match: `customers.people.created`\n * - Wildcard `*` matches single segment: `customers.*` matches `customers.people` but not `customers.people.created`\n * - Global wildcard: `*` alone matches all events\n */\nexport function matchEventPattern(eventName: string, pattern: string): boolean {\n // Global wildcard matches all events\n if (pattern === '*') return true\n\n // Exact match\n if (pattern === eventName) return true\n\n // No wildcards in pattern means we need exact match, which already failed\n if (!pattern.includes('*')) return false\n\n // Convert pattern to regex:\n // - Escape regex special chars (except *)\n // - Replace * with [^.]+ (match one or more non-dot chars)\n const regexPattern = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*/g, '[^.]+')\n const regex = new RegExp(`^${regexPattern}$`)\n return regex.test(eventName)\n}\n\n// ============================================================================\n// Filter Evaluation\n// ============================================================================\n\n/**\n * Get a nested value from an object using dot notation.\n */\nfunction getNestedValue(obj: unknown, path: string): unknown {\n if (obj === null || obj === undefined) return undefined\n\n const parts = path.split('.')\n let current: unknown = obj\n\n for (const part of parts) {\n if (current === null || current === undefined) return undefined\n if (typeof current !== 'object') return undefined\n current = (current as Record<string, unknown>)[part]\n }\n\n return current\n}\n\n/**\n * Evaluate a single filter condition against the event payload.\n */\nfunction evaluateCondition(condition: TriggerFilterCondition, payload: Record<string, unknown>): boolean {\n const value = getNestedValue(payload, condition.field)\n const expected = condition.value\n\n switch (condition.operator) {\n case 'eq':\n return value === expected\n\n case 'neq':\n return value !== expected\n\n case 'gt':\n return typeof value === 'number' && typeof expected === 'number' && value > expected\n\n case 'gte':\n return typeof value === 'number' && typeof expected === 'number' && value >= expected\n\n case 'lt':\n return typeof value === 'number' && typeof expected === 'number' && value < expected\n\n case 'lte':\n return typeof value === 'number' && typeof expected === 'number' && value <= expected\n\n case 'contains':\n if (typeof value === 'string' && typeof expected === 'string') {\n return value.includes(expected)\n }\n if (Array.isArray(value)) {\n return value.includes(expected)\n }\n return false\n\n case 'startsWith':\n return typeof value === 'string' && typeof expected === 'string' && value.startsWith(expected)\n\n case 'endsWith':\n return typeof value === 'string' && typeof expected === 'string' && value.endsWith(expected)\n\n case 'in':\n return Array.isArray(expected) && expected.includes(value)\n\n case 'notIn':\n return Array.isArray(expected) && !expected.includes(value)\n\n case 'exists':\n return value !== undefined && value !== null\n\n case 'notExists':\n return value === undefined || value === null\n\n case 'regex':\n if (typeof value !== 'string' || typeof expected !== 'string') return false\n try {\n const regex = new RegExp(expected)\n return regex.test(value)\n } catch {\n return false\n }\n\n default:\n return false\n }\n}\n\n/**\n * Evaluate all filter conditions against the event payload.\n * All conditions must pass (AND logic).\n */\nexport function evaluateFilterConditions(\n conditions: TriggerFilterCondition[] | undefined,\n payload: Record<string, unknown>\n): boolean {\n if (!conditions || conditions.length === 0) return true\n\n return conditions.every(condition => evaluateCondition(condition, payload))\n}\n\n// ============================================================================\n// Context Mapping\n// ============================================================================\n\n/**\n * Map event payload to workflow initial context.\n */\nexport function mapEventToContext(\n mapping: TriggerContextMapping[] | undefined,\n payload: Record<string, unknown>\n): Record<string, unknown> {\n const context: Record<string, unknown> = {}\n\n if (!mapping || mapping.length === 0) return context\n\n for (const item of mapping) {\n const value = getNestedValue(payload, item.sourceExpression)\n context[item.targetKey] = value !== undefined ? value : item.defaultValue\n }\n\n return context\n}\n\n// ============================================================================\n// Trigger Loading with Caching\n// ============================================================================\n\n// In-memory cache for triggers (per tenant/org)\nconst triggerCache = new Map<string, CachedTriggers>()\n\nfunction getCacheKey(tenantId: string, organizationId: string): string {\n return `${tenantId}:${organizationId}`\n}\n\n/**\n * Load legacy triggers from WorkflowEventTrigger entity table.\n * For backward compatibility with existing triggers.\n */\nasync function loadLegacyTriggers(\n em: EntityManager,\n tenantId: string,\n organizationId: string\n): Promise<UnifiedTrigger[]> {\n const legacyTriggers = await em.find(\n WorkflowEventTrigger,\n {\n tenantId,\n organizationId,\n enabled: true,\n deletedAt: null,\n },\n {\n orderBy: { priority: 'DESC', createdAt: 'ASC' },\n }\n )\n\n // Get definitions for these triggers to get workflowId\n const definitionIds = [...new Set(legacyTriggers.map(t => t.workflowDefinitionId))]\n const definitions = definitionIds.length > 0 ? await em.find(WorkflowDefinition, {\n id: { $in: definitionIds },\n enabled: true,\n deletedAt: null,\n }) : []\n const definitionMap = new Map(definitions.map(d => [d.id, d]))\n\n return legacyTriggers\n .filter(t => definitionMap.has(t.workflowDefinitionId))\n .map(t => {\n const def = definitionMap.get(t.workflowDefinitionId)!\n return {\n id: t.id,\n triggerId: t.id,\n name: t.name,\n description: t.description,\n eventPattern: t.eventPattern,\n config: t.config ?? null,\n enabled: t.enabled,\n priority: t.priority,\n workflowDefinitionId: t.workflowDefinitionId,\n workflowId: def.workflowId,\n workflowVersion: def.version,\n source: 'legacy' as const,\n tenantId: t.tenantId,\n organizationId: t.organizationId,\n }\n })\n}\n\n/**\n * Load embedded triggers from workflow definitions.\n * New triggers are embedded directly in the definition JSONB.\n */\nasync function loadEmbeddedTriggers(\n em: EntityManager,\n tenantId: string,\n organizationId: string\n): Promise<UnifiedTrigger[]> {\n // Load all enabled definitions that may have triggers\n const definitions = await em.find(\n WorkflowDefinition,\n {\n tenantId,\n organizationId,\n enabled: true,\n deletedAt: null,\n }\n )\n\n const triggers: UnifiedTrigger[] = []\n\n for (const def of definitions) {\n const embeddedTriggers = def.definition?.triggers as WorkflowDefinitionTrigger[] | undefined\n if (!embeddedTriggers || embeddedTriggers.length === 0) continue\n\n for (const trigger of embeddedTriggers) {\n if (!trigger.enabled) continue\n\n triggers.push({\n id: `${def.id}:${trigger.triggerId}`,\n triggerId: trigger.triggerId,\n name: trigger.name,\n description: trigger.description ?? null,\n eventPattern: trigger.eventPattern,\n config: trigger.config ?? null,\n enabled: trigger.enabled,\n priority: trigger.priority,\n workflowDefinitionId: def.id,\n workflowId: def.workflowId,\n workflowVersion: def.version,\n source: 'embedded' as const,\n tenantId,\n organizationId,\n })\n }\n }\n\n return triggers\n}\n\n/**\n * Load all enabled triggers for a tenant/organization with caching.\n * Merges both legacy (entity) triggers and embedded (definition) triggers.\n */\nexport async function loadTriggersForTenant(\n em: EntityManager,\n tenantId: string,\n organizationId: string,\n cacheService?: CacheService\n): Promise<UnifiedTrigger[]> {\n const cacheKey = getCacheKey(tenantId, organizationId)\n\n // Check in-memory cache\n const cached = triggerCache.get(cacheKey)\n if (cached && Date.now() - cached.cachedAt < TRIGGER_CACHE_TTL) {\n return cached.triggers\n }\n\n // Load from both sources\n const [legacyTriggers, embeddedTriggers] = await Promise.all([\n loadLegacyTriggers(em, tenantId, organizationId),\n loadEmbeddedTriggers(em, tenantId, organizationId),\n ])\n\n // Merge and sort by priority (higher first)\n const allTriggers = [...legacyTriggers, ...embeddedTriggers]\n .sort((a, b) => b.priority - a.priority)\n\n // Update cache\n triggerCache.set(cacheKey, {\n triggers: allTriggers,\n cachedAt: Date.now(),\n })\n\n return allTriggers\n}\n\n/**\n * Invalidate trigger cache for a tenant/organization.\n * Call this when:\n * - Legacy triggers are created/updated/deleted\n * - Workflow definitions with embedded triggers are created/updated/deleted\n */\nexport function invalidateTriggerCache(tenantId: string, organizationId?: string): void {\n if (organizationId) {\n // Invalidate specific org\n const cacheKey = getCacheKey(tenantId, organizationId)\n triggerCache.delete(cacheKey)\n } else {\n // Invalidate all orgs for tenant\n for (const key of triggerCache.keys()) {\n if (key.startsWith(`${tenantId}:`)) {\n triggerCache.delete(key)\n }\n }\n }\n}\n\n// ============================================================================\n// Trigger Matching\n// ============================================================================\n\n/**\n * Find all triggers that match an event.\n */\nexport async function findMatchingTriggers(\n em: EntityManager,\n context: EventTriggerContext\n): Promise<UnifiedTrigger[]> {\n const triggers = await loadTriggersForTenant(\n em,\n context.tenantId,\n context.organizationId\n )\n\n return triggers.filter(trigger => {\n // Check event pattern\n if (!matchEventPattern(context.eventName, trigger.eventPattern)) {\n return false\n }\n\n // Check filter conditions\n if (!evaluateFilterConditions(trigger.config?.filterConditions, context.payload)) {\n return false\n }\n\n return true\n })\n}\n\n// ============================================================================\n// Trigger Processing\n// ============================================================================\n\n/**\n * Check if max concurrent instances limit is reached.\n */\nasync function checkConcurrencyLimit(\n em: EntityManager,\n trigger: UnifiedTrigger\n): Promise<boolean> {\n const maxInstances = trigger.config?.maxConcurrentInstances\n\n if (!maxInstances) return true // No limit\n\n // Count running instances for this trigger's workflow definition\n const runningCount = await em.count(WorkflowInstance, {\n definitionId: trigger.workflowDefinitionId,\n status: { $in: ['RUNNING', 'WAITING_FOR_ACTIVITIES'] },\n tenantId: trigger.tenantId,\n organizationId: trigger.organizationId,\n deletedAt: null,\n })\n\n return runningCount < maxInstances\n}\n\n/**\n * Process all matching triggers for an event and start workflows.\n */\nexport async function processEventTriggers(\n em: EntityManager,\n container: AwilixContainer,\n context: EventTriggerContext\n): Promise<ProcessTriggersResult> {\n const result: ProcessTriggersResult = {\n triggered: 0,\n skipped: 0,\n errors: [],\n instances: [],\n }\n\n // Find matching triggers\n const triggers = await findMatchingTriggers(em, context)\n\n if (triggers.length === 0) {\n return result\n }\n\n // Process each trigger (definitions already validated during loading)\n for (const trigger of triggers) {\n try {\n // Check concurrency limit\n const canStart = await checkConcurrencyLimit(em, trigger)\n if (!canStart) {\n console.log(`[workflow-trigger] Skipping trigger \"${trigger.name}\": max concurrent instances reached`)\n result.skipped++\n continue\n }\n\n // Map event payload to workflow context\n const mappedContext = mapEventToContext(trigger.config?.contextMapping, context.payload)\n\n // Extract entity info from payload for metadata\n const payloadId = context.payload?.id as string | undefined\n const payloadEntityType = context.payload?.entityType as string | undefined\n\n // Include event metadata and payload in context\n const initialContext = {\n // Include raw event payload fields (e.g., id, organizationId, tenantId)\n ...context.payload,\n // Override with explicit mappings if provided\n ...mappedContext,\n __trigger: {\n triggerId: trigger.id,\n triggerName: trigger.name,\n eventName: context.eventName,\n eventPayload: context.payload,\n triggeredAt: new Date().toISOString(),\n source: trigger.source,\n },\n }\n\n // Start workflow\n const instance = await startWorkflow(em, {\n workflowId: trigger.workflowId,\n version: trigger.workflowVersion,\n initialContext,\n metadata: {\n initiatedBy: `trigger:${trigger.id}`,\n // Include entityId and entityType for widget discovery\n entityId: payloadId,\n entityType: payloadEntityType || trigger.config?.entityType,\n labels: {\n trigger_id: trigger.id,\n trigger_name: trigger.name,\n event_name: context.eventName,\n trigger_source: trigger.source,\n },\n },\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n })\n\n result.triggered++\n result.instances.push({\n triggerId: trigger.id,\n instanceId: instance.id,\n })\n\n // Execute workflow asynchronously (don't wait)\n executeWorkflow(em.fork(), container, instance.id).catch(err => {\n console.error(`[workflow-trigger] Error executing workflow ${instance.id}:`, err)\n })\n\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n console.error(`[workflow-trigger] Error processing trigger \"${trigger.name}\":`, error)\n result.errors.push({\n triggerId: trigger.id,\n error: errorMessage,\n })\n }\n }\n\n return result\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -128,9 +128,11 @@ function graphToDefinition(nodes, edges, options = {}) {
128
128
  };
129
129
  }
130
130
  function definitionToGraph(definition, options = {}) {
131
- const { autoLayout = true, layoutSpacing = { vertical: 250, horizontal: 100 } } = 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;
132
134
  const nodes = definition.steps.map((step, index) => {
133
- let position = { x: 250, y: 50 + index * layoutSpacing.vertical };
135
+ let position = positions?.get(step.stepId) || { x: 250, y: 50 + index * layoutSpacing.vertical };
134
136
  if (!autoLayout && step._editorPosition) {
135
137
  position = step._editorPosition;
136
138
  }
@@ -209,6 +211,73 @@ function definitionToGraph(definition, options = {}) {
209
211
  });
210
212
  return { nodes, edges };
211
213
  }
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
+ }
212
281
  function mapNodeTypeToStepType(nodeType) {
213
282
  const mapping = {
214
283
  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: 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": "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,QAAgB,WAAW,MAAM,IAAI,CAAC,MAAM,UAAU;AAE1D,QAAI,WAAW,EAAE,GAAG,KAAK,GAAG,KAAK,QAAQ,cAAc,SAAS;AAGhE,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;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: 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": "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",
6
6
  "names": []
7
7
  }