@xtr-dev/payload-automation 0.0.43 → 0.0.46

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 (74) hide show
  1. package/README.md +221 -49
  2. package/dist/collections/Steps.d.ts +6 -0
  3. package/dist/collections/Steps.js +166 -0
  4. package/dist/collections/Steps.js.map +1 -0
  5. package/dist/collections/Triggers.d.ts +7 -0
  6. package/dist/collections/Triggers.js +224 -0
  7. package/dist/collections/Triggers.js.map +1 -0
  8. package/dist/collections/Workflow.d.ts +5 -2
  9. package/dist/collections/Workflow.js +179 -39
  10. package/dist/collections/Workflow.js.map +1 -1
  11. package/dist/collections/WorkflowRuns.d.ts +4 -0
  12. package/dist/collections/WorkflowRuns.js +219 -24
  13. package/dist/collections/WorkflowRuns.js.map +1 -1
  14. package/dist/components/WorkflowBuilder/WorkflowBuilder.js.map +1 -1
  15. package/dist/core/expression-engine.d.ts +58 -0
  16. package/dist/core/expression-engine.js +191 -0
  17. package/dist/core/expression-engine.js.map +1 -0
  18. package/dist/core/workflow-executor.d.ts +70 -56
  19. package/dist/core/workflow-executor.js +354 -677
  20. package/dist/core/workflow-executor.js.map +1 -1
  21. package/dist/exports/client.js +1 -3
  22. package/dist/exports/client.js.map +1 -1
  23. package/dist/exports/views.js +2 -4
  24. package/dist/exports/views.js.map +1 -1
  25. package/dist/index.d.ts +3 -2
  26. package/dist/index.js +1 -1
  27. package/dist/index.js.map +1 -1
  28. package/dist/plugin/config-types.d.ts +43 -5
  29. package/dist/plugin/config-types.js +3 -1
  30. package/dist/plugin/config-types.js.map +1 -1
  31. package/dist/plugin/index.d.ts +1 -1
  32. package/dist/plugin/index.js +150 -55
  33. package/dist/plugin/index.js.map +1 -1
  34. package/dist/plugin/trigger-hook.d.ts +13 -0
  35. package/dist/plugin/trigger-hook.js +184 -0
  36. package/dist/plugin/trigger-hook.js.map +1 -0
  37. package/dist/steps/create-step.d.ts +66 -0
  38. package/dist/steps/create-step.js +59 -0
  39. package/dist/steps/create-step.js.map +1 -0
  40. package/dist/steps/index.d.ts +2 -0
  41. package/dist/steps/index.js +3 -0
  42. package/dist/steps/index.js.map +1 -1
  43. package/dist/steps/read-document-handler.js +1 -1
  44. package/dist/steps/read-document-handler.js.map +1 -1
  45. package/dist/steps/update-document-handler.js +1 -1
  46. package/dist/steps/update-document-handler.js.map +1 -1
  47. package/dist/triggers/hook-options.d.ts +34 -0
  48. package/dist/triggers/hook-options.js +158 -0
  49. package/dist/triggers/hook-options.js.map +1 -0
  50. package/dist/triggers/index.d.ts +2 -2
  51. package/dist/triggers/index.js +1 -2
  52. package/dist/triggers/index.js.map +1 -1
  53. package/dist/types/index.d.ts +8 -0
  54. package/dist/types/index.js +4 -5
  55. package/dist/types/index.js.map +1 -1
  56. package/dist/utils/validation.d.ts +64 -0
  57. package/dist/utils/validation.js +107 -0
  58. package/dist/utils/validation.js.map +1 -0
  59. package/package.json +2 -1
  60. package/dist/plugin/collection-hook.d.ts +0 -1
  61. package/dist/plugin/collection-hook.js +0 -92
  62. package/dist/plugin/collection-hook.js.map +0 -1
  63. package/dist/plugin/global-hook.d.ts +0 -1
  64. package/dist/plugin/global-hook.js +0 -83
  65. package/dist/plugin/global-hook.js.map +0 -1
  66. package/dist/triggers/collection-trigger.d.ts +0 -2
  67. package/dist/triggers/collection-trigger.js +0 -36
  68. package/dist/triggers/collection-trigger.js.map +0 -1
  69. package/dist/triggers/global-trigger.d.ts +0 -2
  70. package/dist/triggers/global-trigger.js +0 -29
  71. package/dist/triggers/global-trigger.js.map +0 -1
  72. package/dist/triggers/types.d.ts +0 -5
  73. package/dist/triggers/types.js +0 -3
  74. package/dist/triggers/types.js.map +0 -1
@@ -1,15 +1,16 @@
1
+ import { createTriggersCollection } from '../collections/Triggers.js';
2
+ import { createStepsCollection } from '../collections/Steps.js';
1
3
  import { createWorkflowCollection } from '../collections/Workflow.js';
2
4
  import { WorkflowRunsCollection } from '../collections/WorkflowRuns.js';
3
5
  import { getConfigLogger, initializeLogger } from './logger.js';
4
- import { createCollectionTriggerHook } from "./collection-hook.js";
5
- import { createGlobalTriggerHook } from "./global-hook.js";
6
+ import { createCollectionTriggerHook, createGlobalTriggerHook } from './trigger-hook.js';
6
7
  export { getLogger } from './logger.js';
7
8
  const applyCollectionsConfig = (pluginOptions, config)=>{
8
- // Add workflow collections
9
9
  if (!config.collections) {
10
10
  config.collections = [];
11
11
  }
12
- config.collections.push(createWorkflowCollection(pluginOptions), WorkflowRunsCollection);
12
+ // Add all automation collections
13
+ config.collections.push(createTriggersCollection(pluginOptions), createStepsCollection(pluginOptions.steps), createWorkflowCollection(), WorkflowRunsCollection);
13
14
  };
14
15
  export const workflowsPlugin = (pluginOptions)=>(config)=>{
15
16
  // If the plugin is disabled, return config unchanged
@@ -17,44 +18,36 @@ export const workflowsPlugin = (pluginOptions)=>(config)=>{
17
18
  return config;
18
19
  }
19
20
  applyCollectionsConfig(pluginOptions, config);
20
- // CRITICAL: Modify existing collection configs BEFORE PayloadCMS processes them
21
- // This is the ONLY time we can add hooks that will actually work
22
21
  const logger = getConfigLogger();
22
+ // Register collection hooks
23
23
  if (config.collections && pluginOptions.collectionTriggers) {
24
24
  for (const [collectionSlug, triggerConfig] of Object.entries(pluginOptions.collectionTriggers)){
25
25
  if (!triggerConfig) {
26
26
  continue;
27
27
  }
28
- // Find the collection config that matches
29
28
  const collectionIndex = config.collections.findIndex((c)=>c.slug === collectionSlug);
30
29
  if (collectionIndex === -1) {
31
30
  logger.warn(`Collection '${collectionSlug}' not found in config.collections`);
32
31
  continue;
33
32
  }
34
33
  const collection = config.collections[collectionIndex];
35
- // Initialize hooks if needed
36
34
  if (!collection.hooks) {
37
35
  collection.hooks = {};
38
36
  }
39
- // Determine which hooks to register based on config
40
37
  const hooksToRegister = triggerConfig === true ? {
41
38
  afterChange: true,
42
39
  afterDelete: true,
43
40
  afterRead: true
44
41
  } : triggerConfig;
45
- // Register each configured hook
46
42
  Object.entries(hooksToRegister).forEach(([hookName, enabled])=>{
47
43
  if (!enabled) {
48
44
  return;
49
45
  }
50
46
  const hookKey = hookName;
51
- // Initialize the hook array if needed
52
47
  if (!collection.hooks[hookKey]) {
53
48
  collection.hooks[hookKey] = [];
54
49
  }
55
- // Create the automation hook for this specific collection and hook type
56
50
  const automationHook = createCollectionTriggerHook(collectionSlug, hookKey);
57
- // Mark it for debugging
58
51
  Object.defineProperty(automationHook, '__isAutomationHook', {
59
52
  value: true,
60
53
  enumerable: false
@@ -68,41 +61,34 @@ export const workflowsPlugin = (pluginOptions)=>(config)=>{
68
61
  });
69
62
  }
70
63
  }
71
- // Handle global triggers similarly to collection triggers
64
+ // Register global hooks
72
65
  if (config.globals && pluginOptions.globalTriggers) {
73
66
  for (const [globalSlug, triggerConfig] of Object.entries(pluginOptions.globalTriggers)){
74
67
  if (!triggerConfig) {
75
68
  continue;
76
69
  }
77
- // Find the global config that matches
78
70
  const globalIndex = config.globals.findIndex((g)=>g.slug === globalSlug);
79
71
  if (globalIndex === -1) {
80
72
  logger.warn(`Global '${globalSlug}' not found in config.globals`);
81
73
  continue;
82
74
  }
83
75
  const global = config.globals[globalIndex];
84
- // Initialize hooks if needed
85
76
  if (!global.hooks) {
86
77
  global.hooks = {};
87
78
  }
88
- // Determine which hooks to register based on config
89
79
  const hooksToRegister = triggerConfig === true ? {
90
80
  afterChange: true,
91
81
  afterRead: true
92
82
  } : triggerConfig;
93
- // Register each configured hook
94
83
  Object.entries(hooksToRegister).forEach(([hookName, enabled])=>{
95
84
  if (!enabled) {
96
85
  return;
97
86
  }
98
87
  const hookKey = hookName;
99
- // Initialize the hook array if needed
100
88
  if (!global.hooks[hookKey]) {
101
89
  global.hooks[hookKey] = [];
102
90
  }
103
- // Create the automation hook for this specific global and hook type
104
91
  const automationHook = createGlobalTriggerHook(globalSlug, hookKey);
105
- // Mark it for debugging
106
92
  Object.defineProperty(automationHook, '__isAutomationHook', {
107
93
  value: true,
108
94
  enumerable: false
@@ -116,6 +102,7 @@ export const workflowsPlugin = (pluginOptions)=>(config)=>{
116
102
  });
117
103
  }
118
104
  }
105
+ // Register step tasks
119
106
  if (!config.jobs) {
120
107
  config.jobs = {
121
108
  tasks: []
@@ -126,18 +113,85 @@ export const workflowsPlugin = (pluginOptions)=>(config)=>{
126
113
  config.jobs?.tasks?.push(step);
127
114
  }
128
115
  }
129
- // Set up onInit to initialize features
116
+ // Extend payload-jobs collection with automation context fields
117
+ const existingJobsOverrides = config.jobs.jobsCollectionOverrides;
118
+ config.jobs.jobsCollectionOverrides = ({ defaultJobsCollection })=>{
119
+ // Apply any existing overrides first
120
+ const collection = existingJobsOverrides ? existingJobsOverrides({
121
+ defaultJobsCollection
122
+ }) : defaultJobsCollection;
123
+ return {
124
+ ...collection,
125
+ fields: [
126
+ ...collection.fields,
127
+ // Structured automation context fields for admin UI integration
128
+ {
129
+ name: 'automationWorkflow',
130
+ type: 'relationship',
131
+ relationTo: 'workflows',
132
+ admin: {
133
+ position: 'sidebar',
134
+ readOnly: true,
135
+ description: 'Workflow that created this job'
136
+ }
137
+ },
138
+ {
139
+ name: 'automationWorkflowRun',
140
+ type: 'relationship',
141
+ relationTo: 'workflow-runs',
142
+ admin: {
143
+ position: 'sidebar',
144
+ readOnly: true,
145
+ description: 'Workflow run that created this job'
146
+ }
147
+ },
148
+ {
149
+ name: 'automationTrigger',
150
+ type: 'relationship',
151
+ relationTo: 'automation-triggers',
152
+ admin: {
153
+ position: 'sidebar',
154
+ readOnly: true,
155
+ description: 'Trigger that initiated the workflow'
156
+ }
157
+ },
158
+ {
159
+ name: 'automationStepName',
160
+ type: 'text',
161
+ admin: {
162
+ position: 'sidebar',
163
+ readOnly: true,
164
+ description: 'Name of the workflow step that created this job'
165
+ }
166
+ }
167
+ ],
168
+ admin: {
169
+ ...collection.admin,
170
+ listSearchableFields: [
171
+ ...collection.admin?.listSearchableFields || []
172
+ ],
173
+ defaultColumns: [
174
+ ...collection.admin?.defaultColumns || [
175
+ 'taskSlug',
176
+ 'queue',
177
+ 'processing',
178
+ 'completedAt'
179
+ ]
180
+ ]
181
+ }
182
+ };
183
+ };
184
+ // Set up onInit
130
185
  const incomingOnInit = config.onInit;
131
186
  config.onInit = async (payload)=>{
132
- // Execute any existing onInit functions first
133
187
  if (incomingOnInit) {
134
188
  await incomingOnInit(payload);
135
189
  }
136
- // Initialize the logger with the payload instance
137
190
  const logger = initializeLogger(payload);
138
- logger.info('Logger initialized with payload instance');
139
- // Log trigger configuration
140
- logger.info(`Plugin configuration: ${Object.keys(pluginOptions.collectionTriggers || {}).length} collection triggers, ${Object.keys(pluginOptions.globalTriggers || {}).length} global triggers, ${pluginOptions.steps?.length || 0} steps`);
191
+ logger.info('Automation plugin initialized');
192
+ const collectionCount = Object.keys(pluginOptions.collectionTriggers || {}).length;
193
+ const globalCount = Object.keys(pluginOptions.globalTriggers || {}).length;
194
+ const stepCount = pluginOptions.steps?.length || 0;
141
195
  // Seed workflows if configured
142
196
  if (pluginOptions.seedWorkflows && pluginOptions.seedWorkflows.length > 0) {
143
197
  logger.info(`Seeding ${pluginOptions.seedWorkflows.length} workflows...`);
@@ -154,44 +208,85 @@ export const workflowsPlugin = (pluginOptions)=>(config)=>{
154
208
  limit: 1
155
209
  });
156
210
  if (existingWorkflow.docs.length > 0) {
157
- const existing = existingWorkflow.docs[0];
158
- // Detect changes by comparing workflow definition
159
- const stepsChanged = JSON.stringify(existing.steps) !== JSON.stringify(seedWorkflow.steps);
160
- const triggersChanged = JSON.stringify(existing.triggers) !== JSON.stringify(seedWorkflow.triggers);
161
- const nameChanged = existing.name !== seedWorkflow.name;
162
- const descriptionChanged = existing.description !== seedWorkflow.description;
163
- const hasChanges = stepsChanged || triggersChanged || nameChanged || descriptionChanged;
164
- if (hasChanges) {
165
- logger.info(`Updating seeded workflow '${seedWorkflow.slug}': ${seedWorkflow.name}`);
166
- await payload.update({
167
- collection: 'workflows',
168
- id: existing.id,
169
- data: {
170
- ...seedWorkflow,
171
- readOnly: true
172
- }
173
- });
174
- logger.info(`Updated workflow: ${seedWorkflow.name}`);
175
- } else {
176
- logger.debug(`Workflow '${seedWorkflow.slug}' is up to date, skipping`);
211
+ logger.debug(`Workflow '${seedWorkflow.slug}' already exists, skipping seed`);
212
+ continue;
213
+ }
214
+ // Create triggers in automation-triggers collection
215
+ const triggerIds = [];
216
+ for(let i = 0; i < seedWorkflow.triggers.length; i++){
217
+ const triggerDef = seedWorkflow.triggers[i];
218
+ const triggerName = `${seedWorkflow.name} - Trigger ${i + 1}`;
219
+ // Build trigger data based on type
220
+ const triggerData = {
221
+ name: triggerName,
222
+ type: triggerDef.type,
223
+ condition: triggerDef.condition
224
+ };
225
+ // Map parameters to trigger fields based on type
226
+ if (triggerDef.parameters) {
227
+ if (triggerDef.type === 'collection-hook') {
228
+ triggerData.collectionSlug = triggerDef.parameters.collectionSlug;
229
+ triggerData.hook = triggerDef.parameters.hook;
230
+ } else if (triggerDef.type === 'global-hook') {
231
+ triggerData.globalSlug = triggerDef.parameters.globalSlug;
232
+ triggerData.hook = triggerDef.parameters.hook;
233
+ } else if (triggerDef.type === 'scheduled') {
234
+ triggerData.schedule = triggerDef.parameters.schedule;
235
+ } else if (triggerDef.type === 'webhook') {
236
+ triggerData.webhookPath = triggerDef.parameters.webhookPath;
237
+ }
177
238
  }
178
- } else {
179
- // Create the workflow as read-only
180
- await payload.create({
181
- collection: 'workflows',
239
+ const trigger = await payload.create({
240
+ collection: 'automation-triggers',
241
+ data: triggerData
242
+ });
243
+ triggerIds.push(trigger.id);
244
+ logger.debug(`Created trigger: ${triggerName}`);
245
+ }
246
+ // Create steps in automation-steps collection and build workflow steps array
247
+ const workflowSteps = [];
248
+ for (const stepDef of seedWorkflow.steps){
249
+ const stepName = `${seedWorkflow.name} - ${stepDef.name}`;
250
+ // Create step in automation-steps collection
251
+ const step = await payload.create({
252
+ collection: 'automation-steps',
182
253
  data: {
183
- ...seedWorkflow,
184
- readOnly: true
254
+ name: stepName,
255
+ type: stepDef.type,
256
+ config: stepDef.input || {}
185
257
  }
186
258
  });
187
- logger.info(`Seeded workflow: ${seedWorkflow.name}`);
259
+ logger.debug(`Created step: ${stepName}`);
260
+ // Add to workflow steps with relationship ID
261
+ workflowSteps.push({
262
+ step: step.id,
263
+ stepName: stepDef.name,
264
+ inputOverrides: stepDef.input || {},
265
+ condition: stepDef.condition,
266
+ dependencies: stepDef.dependencies?.map((dep)=>({
267
+ stepName: dep
268
+ }))
269
+ });
188
270
  }
271
+ // Create the workflow with relationship IDs
272
+ await payload.create({
273
+ collection: 'workflows',
274
+ data: {
275
+ slug: seedWorkflow.slug,
276
+ name: seedWorkflow.name,
277
+ description: seedWorkflow.description,
278
+ triggers: triggerIds,
279
+ steps: workflowSteps,
280
+ readOnly: true
281
+ }
282
+ });
283
+ logger.info(`Seeded workflow: ${seedWorkflow.name}`);
189
284
  } catch (error) {
190
285
  logger.error(`Failed to seed workflow '${seedWorkflow.name}':`, error);
191
286
  }
192
287
  }
193
288
  }
194
- logger.info('Plugin initialized successfully - all hooks registered');
289
+ logger.info(`Plugin configuration: ${collectionCount} collection triggers, ${globalCount} global triggers, ${stepCount} steps`);
195
290
  };
196
291
  return config;
197
292
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/plugin/index.ts"],"sourcesContent":["import type {CollectionConfig, Config} from 'payload'\n\nimport type {WorkflowsPluginConfig} from \"./config-types.js\"\n\nimport {createWorkflowCollection} from '../collections/Workflow.js'\nimport {WorkflowRunsCollection} from '../collections/WorkflowRuns.js'\nimport {WorkflowExecutor} from '../core/workflow-executor.js'\nimport {getConfigLogger, initializeLogger} from './logger.js'\nimport {createCollectionTriggerHook} from \"./collection-hook.js\"\nimport {createGlobalTriggerHook} from \"./global-hook.js\"\n\nexport {getLogger} from './logger.js'\n\nconst applyCollectionsConfig = <T extends string>(pluginOptions: WorkflowsPluginConfig<T>, config: Config) => {\n // Add workflow collections\n if (!config.collections) {\n config.collections = []\n }\n\n config.collections.push(\n createWorkflowCollection(pluginOptions),\n WorkflowRunsCollection\n )\n}\n\ntype AnyHook =\n CollectionConfig['hooks'] extends infer H\n ? H extends Record<string, unknown>\n ? NonNullable<H[keyof H]> extends (infer U)[]\n ? U\n : never\n : never\n : never;\n\ntype HookArgs = Parameters<AnyHook>[0]\n\nexport const workflowsPlugin =\n <TSlug extends string>(pluginOptions: WorkflowsPluginConfig<TSlug>) =>\n (config: Config): Config => {\n // If the plugin is disabled, return config unchanged\n if (pluginOptions.enabled === false) {\n return config\n }\n\n applyCollectionsConfig<TSlug>(pluginOptions, config)\n\n // CRITICAL: Modify existing collection configs BEFORE PayloadCMS processes them\n // This is the ONLY time we can add hooks that will actually work\n const logger = getConfigLogger()\n\n if (config.collections && pluginOptions.collectionTriggers) {\n for (const [collectionSlug, triggerConfig] of Object.entries(pluginOptions.collectionTriggers)) {\n if (!triggerConfig) {\n continue\n }\n\n // Find the collection config that matches\n const collectionIndex = config.collections.findIndex(c => c.slug === collectionSlug)\n if (collectionIndex === -1) {\n logger.warn(`Collection '${collectionSlug}' not found in config.collections`)\n continue\n }\n\n const collection = config.collections[collectionIndex]\n\n // Initialize hooks if needed\n if (!collection.hooks) {\n collection.hooks = {}\n }\n\n // Determine which hooks to register based on config\n const hooksToRegister = triggerConfig === true\n ? {\n afterChange: true,\n afterDelete: true,\n afterRead: true,\n }\n : triggerConfig\n\n // Register each configured hook\n Object.entries(hooksToRegister).forEach(([hookName, enabled]) => {\n if (!enabled) {\n return\n }\n\n const hookKey = hookName as keyof typeof collection.hooks\n\n // Initialize the hook array if needed\n if (!collection.hooks![hookKey]) {\n collection.hooks![hookKey] = []\n }\n\n // Create the automation hook for this specific collection and hook type\n const automationHook = createCollectionTriggerHook(collectionSlug, hookKey)\n\n // Mark it for debugging\n Object.defineProperty(automationHook, '__isAutomationHook', {\n value: true,\n enumerable: false\n })\n Object.defineProperty(automationHook, '__hookType', {\n value: hookKey,\n enumerable: false\n })\n\n // Add the hook to the collection\n ;(collection.hooks![hookKey] as Array<unknown>).push(automationHook)\n\n logger.debug(`Registered ${hookKey} hook for collection '${collectionSlug}'`)\n })\n }\n }\n\n // Handle global triggers similarly to collection triggers\n if (config.globals && pluginOptions.globalTriggers) {\n for (const [globalSlug, triggerConfig] of Object.entries(pluginOptions.globalTriggers)) {\n if (!triggerConfig) {\n continue\n }\n\n // Find the global config that matches\n const globalIndex = config.globals.findIndex(g => g.slug === globalSlug)\n if (globalIndex === -1) {\n logger.warn(`Global '${globalSlug}' not found in config.globals`)\n continue\n }\n\n const global = config.globals[globalIndex]\n\n // Initialize hooks if needed\n if (!global.hooks) {\n global.hooks = {}\n }\n\n // Determine which hooks to register based on config\n const hooksToRegister = triggerConfig === true\n ? {\n afterChange: true,\n afterRead: true,\n }\n : triggerConfig\n\n // Register each configured hook\n Object.entries(hooksToRegister).forEach(([hookName, enabled]) => {\n if (!enabled) {\n return\n }\n\n const hookKey = hookName as keyof typeof global.hooks\n\n // Initialize the hook array if needed\n if (!global.hooks![hookKey]) {\n global.hooks![hookKey] = []\n }\n\n // Create the automation hook for this specific global and hook type\n const automationHook = createGlobalTriggerHook(globalSlug, hookKey)\n\n // Mark it for debugging\n Object.defineProperty(automationHook, '__isAutomationHook', {\n value: true,\n enumerable: false\n })\n Object.defineProperty(automationHook, '__hookType', {\n value: hookKey,\n enumerable: false\n })\n\n // Add the hook to the global\n ;(global.hooks![hookKey] as Array<unknown>).push(automationHook)\n\n logger.debug(`Registered ${hookKey} hook for global '${globalSlug}'`)\n })\n }\n }\n\n if (!config.jobs) {\n config.jobs = {tasks: []}\n }\n\n for (const step of pluginOptions.steps) {\n if (!config.jobs?.tasks?.find(task => task.slug === step.slug)) {\n config.jobs?.tasks?.push(step)\n }\n }\n\n\n // Set up onInit to initialize features\n const incomingOnInit = config.onInit\n config.onInit = async (payload) => {\n // Execute any existing onInit functions first\n if (incomingOnInit) {\n await incomingOnInit(payload)\n }\n\n // Initialize the logger with the payload instance\n const logger = initializeLogger(payload)\n logger.info('Logger initialized with payload instance')\n\n // Log trigger configuration\n logger.info(`Plugin configuration: ${Object.keys(pluginOptions.collectionTriggers || {}).length} collection triggers, ${Object.keys(pluginOptions.globalTriggers || {}).length} global triggers, ${pluginOptions.steps?.length || 0} steps`)\n\n // Seed workflows if configured\n if (pluginOptions.seedWorkflows && pluginOptions.seedWorkflows.length > 0) {\n logger.info(`Seeding ${pluginOptions.seedWorkflows.length} workflows...`)\n\n for (const seedWorkflow of pluginOptions.seedWorkflows) {\n try {\n // Check if workflow already exists by slug\n const existingWorkflow = await payload.find({\n collection: 'workflows',\n where: {\n slug: {\n equals: seedWorkflow.slug,\n },\n },\n limit: 1,\n })\n\n if (existingWorkflow.docs.length > 0) {\n const existing = existingWorkflow.docs[0]\n\n // Detect changes by comparing workflow definition\n const stepsChanged = JSON.stringify(existing.steps) !== JSON.stringify(seedWorkflow.steps)\n const triggersChanged = JSON.stringify(existing.triggers) !== JSON.stringify(seedWorkflow.triggers)\n const nameChanged = existing.name !== seedWorkflow.name\n const descriptionChanged = existing.description !== seedWorkflow.description\n\n const hasChanges = stepsChanged || triggersChanged || nameChanged || descriptionChanged\n\n if (hasChanges) {\n logger.info(`Updating seeded workflow '${seedWorkflow.slug}': ${seedWorkflow.name}`)\n\n await payload.update({\n collection: 'workflows',\n id: existing.id,\n data: {\n ...seedWorkflow,\n readOnly: true,\n },\n })\n\n logger.info(`Updated workflow: ${seedWorkflow.name}`)\n } else {\n logger.debug(`Workflow '${seedWorkflow.slug}' is up to date, skipping`)\n }\n } else {\n // Create the workflow as read-only\n await payload.create({\n collection: 'workflows',\n data: {\n ...seedWorkflow,\n readOnly: true,\n },\n })\n\n logger.info(`Seeded workflow: ${seedWorkflow.name}`)\n }\n } catch (error) {\n logger.error(`Failed to seed workflow '${seedWorkflow.name}':`, error)\n }\n }\n }\n\n logger.info('Plugin initialized successfully - all hooks registered')\n }\n\n return config\n }\n"],"names":["createWorkflowCollection","WorkflowRunsCollection","getConfigLogger","initializeLogger","createCollectionTriggerHook","createGlobalTriggerHook","getLogger","applyCollectionsConfig","pluginOptions","config","collections","push","workflowsPlugin","enabled","logger","collectionTriggers","collectionSlug","triggerConfig","Object","entries","collectionIndex","findIndex","c","slug","warn","collection","hooks","hooksToRegister","afterChange","afterDelete","afterRead","forEach","hookName","hookKey","automationHook","defineProperty","value","enumerable","debug","globals","globalTriggers","globalSlug","globalIndex","g","global","jobs","tasks","step","steps","find","task","incomingOnInit","onInit","payload","info","keys","length","seedWorkflows","seedWorkflow","existingWorkflow","where","equals","limit","docs","existing","stepsChanged","JSON","stringify","triggersChanged","triggers","nameChanged","name","descriptionChanged","description","hasChanges","update","id","data","readOnly","create","error"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/plugin/index.ts"],"sourcesContent":["import type { CollectionConfig, Config } from 'payload'\n\nimport type { WorkflowsPluginConfig } from './config-types.js'\n\nimport { createTriggersCollection } from '../collections/Triggers.js'\nimport { createStepsCollection } from '../collections/Steps.js'\nimport { createWorkflowCollection } from '../collections/Workflow.js'\nimport { WorkflowRunsCollection } from '../collections/WorkflowRuns.js'\nimport { getConfigLogger, initializeLogger } from './logger.js'\nimport { createCollectionTriggerHook, createGlobalTriggerHook } from './trigger-hook.js'\n\nexport { getLogger } from './logger.js'\n\nconst applyCollectionsConfig = <T extends string>(\n pluginOptions: WorkflowsPluginConfig<T>,\n config: Config\n) => {\n if (!config.collections) {\n config.collections = []\n }\n\n // Add all automation collections\n config.collections.push(\n createTriggersCollection(pluginOptions),\n createStepsCollection(pluginOptions.steps),\n createWorkflowCollection(),\n WorkflowRunsCollection\n )\n}\n\ntype AnyHook =\n CollectionConfig['hooks'] extends infer H\n ? H extends Record<string, unknown>\n ? NonNullable<H[keyof H]> extends (infer U)[]\n ? U\n : never\n : never\n : never\n\nexport const workflowsPlugin =\n <TSlug extends string>(pluginOptions: WorkflowsPluginConfig<TSlug>) =>\n (config: Config): Config => {\n // If the plugin is disabled, return config unchanged\n if (pluginOptions.enabled === false) {\n return config\n }\n\n applyCollectionsConfig<TSlug>(pluginOptions, config)\n\n const logger = getConfigLogger()\n\n // Register collection hooks\n if (config.collections && pluginOptions.collectionTriggers) {\n for (const [collectionSlug, triggerConfig] of Object.entries(pluginOptions.collectionTriggers)) {\n if (!triggerConfig) {\n continue\n }\n\n const collectionIndex = config.collections.findIndex(c => c.slug === collectionSlug)\n if (collectionIndex === -1) {\n logger.warn(`Collection '${collectionSlug}' not found in config.collections`)\n continue\n }\n\n const collection = config.collections[collectionIndex]\n\n if (!collection.hooks) {\n collection.hooks = {}\n }\n\n const hooksToRegister = triggerConfig === true\n ? { afterChange: true, afterDelete: true, afterRead: true }\n : triggerConfig\n\n Object.entries(hooksToRegister).forEach(([hookName, enabled]) => {\n if (!enabled) {\n return\n }\n\n const hookKey = hookName as keyof typeof collection.hooks\n\n if (!collection.hooks![hookKey]) {\n collection.hooks![hookKey] = []\n }\n\n const automationHook = createCollectionTriggerHook(collectionSlug, hookKey)\n\n Object.defineProperty(automationHook, '__isAutomationHook', {\n value: true,\n enumerable: false\n })\n Object.defineProperty(automationHook, '__hookType', {\n value: hookKey,\n enumerable: false\n })\n\n ;(collection.hooks![hookKey] as Array<unknown>).push(automationHook)\n\n logger.debug(`Registered ${hookKey} hook for collection '${collectionSlug}'`)\n })\n }\n }\n\n // Register global hooks\n if (config.globals && pluginOptions.globalTriggers) {\n for (const [globalSlug, triggerConfig] of Object.entries(pluginOptions.globalTriggers)) {\n if (!triggerConfig) {\n continue\n }\n\n const globalIndex = config.globals.findIndex(g => g.slug === globalSlug)\n if (globalIndex === -1) {\n logger.warn(`Global '${globalSlug}' not found in config.globals`)\n continue\n }\n\n const global = config.globals[globalIndex]\n\n if (!global.hooks) {\n global.hooks = {}\n }\n\n const hooksToRegister = triggerConfig === true\n ? { afterChange: true, afterRead: true }\n : triggerConfig\n\n Object.entries(hooksToRegister).forEach(([hookName, enabled]) => {\n if (!enabled) {\n return\n }\n\n const hookKey = hookName as keyof typeof global.hooks\n\n if (!global.hooks![hookKey]) {\n global.hooks![hookKey] = []\n }\n\n const automationHook = createGlobalTriggerHook(globalSlug, hookKey)\n\n Object.defineProperty(automationHook, '__isAutomationHook', {\n value: true,\n enumerable: false\n })\n Object.defineProperty(automationHook, '__hookType', {\n value: hookKey,\n enumerable: false\n })\n\n ;(global.hooks![hookKey] as Array<unknown>).push(automationHook)\n\n logger.debug(`Registered ${hookKey} hook for global '${globalSlug}'`)\n })\n }\n }\n\n // Register step tasks\n if (!config.jobs) {\n config.jobs = { tasks: [] }\n }\n\n for (const step of pluginOptions.steps) {\n if (!config.jobs?.tasks?.find(task => task.slug === step.slug)) {\n config.jobs?.tasks?.push(step)\n }\n }\n\n // Extend payload-jobs collection with automation context fields\n const existingJobsOverrides = config.jobs.jobsCollectionOverrides\n config.jobs.jobsCollectionOverrides = ({ defaultJobsCollection }) => {\n // Apply any existing overrides first\n const collection = existingJobsOverrides\n ? existingJobsOverrides({ defaultJobsCollection })\n : defaultJobsCollection\n\n return {\n ...collection,\n fields: [\n ...collection.fields,\n // Structured automation context fields for admin UI integration\n {\n name: 'automationWorkflow',\n type: 'relationship',\n relationTo: 'workflows',\n admin: {\n position: 'sidebar',\n readOnly: true,\n description: 'Workflow that created this job',\n },\n },\n {\n name: 'automationWorkflowRun',\n type: 'relationship',\n relationTo: 'workflow-runs',\n admin: {\n position: 'sidebar',\n readOnly: true,\n description: 'Workflow run that created this job',\n },\n },\n {\n name: 'automationTrigger',\n type: 'relationship',\n relationTo: 'automation-triggers',\n admin: {\n position: 'sidebar',\n readOnly: true,\n description: 'Trigger that initiated the workflow',\n },\n },\n {\n name: 'automationStepName',\n type: 'text',\n admin: {\n position: 'sidebar',\n readOnly: true,\n description: 'Name of the workflow step that created this job',\n },\n },\n ],\n admin: {\n ...collection.admin,\n listSearchableFields: [\n ...(collection.admin?.listSearchableFields || []),\n ],\n defaultColumns: [\n ...(collection.admin?.defaultColumns || ['taskSlug', 'queue', 'processing', 'completedAt']),\n ],\n },\n }\n }\n\n // Set up onInit\n const incomingOnInit = config.onInit\n config.onInit = async (payload) => {\n if (incomingOnInit) {\n await incomingOnInit(payload)\n }\n\n const logger = initializeLogger(payload)\n logger.info('Automation plugin initialized')\n\n const collectionCount = Object.keys(pluginOptions.collectionTriggers || {}).length\n const globalCount = Object.keys(pluginOptions.globalTriggers || {}).length\n const stepCount = pluginOptions.steps?.length || 0\n\n // Seed workflows if configured\n if (pluginOptions.seedWorkflows && pluginOptions.seedWorkflows.length > 0) {\n logger.info(`Seeding ${pluginOptions.seedWorkflows.length} workflows...`)\n\n for (const seedWorkflow of pluginOptions.seedWorkflows) {\n try {\n // Check if workflow already exists by slug\n const existingWorkflow = await payload.find({\n collection: 'workflows',\n where: {\n slug: {\n equals: seedWorkflow.slug,\n },\n },\n limit: 1,\n })\n\n if (existingWorkflow.docs.length > 0) {\n logger.debug(`Workflow '${seedWorkflow.slug}' already exists, skipping seed`)\n continue\n }\n\n // Create triggers in automation-triggers collection\n const triggerIds: (string | number)[] = []\n for (let i = 0; i < seedWorkflow.triggers.length; i++) {\n const triggerDef = seedWorkflow.triggers[i]\n const triggerName = `${seedWorkflow.name} - Trigger ${i + 1}`\n\n // Build trigger data based on type\n const triggerData: Record<string, unknown> = {\n name: triggerName,\n type: triggerDef.type,\n condition: triggerDef.condition,\n }\n\n // Map parameters to trigger fields based on type\n if (triggerDef.parameters) {\n if (triggerDef.type === 'collection-hook') {\n triggerData.collectionSlug = triggerDef.parameters.collectionSlug\n triggerData.hook = triggerDef.parameters.hook\n } else if (triggerDef.type === 'global-hook') {\n triggerData.globalSlug = triggerDef.parameters.globalSlug\n triggerData.hook = triggerDef.parameters.hook\n } else if (triggerDef.type === 'scheduled') {\n triggerData.schedule = triggerDef.parameters.schedule\n } else if (triggerDef.type === 'webhook') {\n triggerData.webhookPath = triggerDef.parameters.webhookPath\n }\n }\n\n const trigger = await payload.create({\n collection: 'automation-triggers',\n data: triggerData,\n })\n triggerIds.push(trigger.id)\n logger.debug(`Created trigger: ${triggerName}`)\n }\n\n // Create steps in automation-steps collection and build workflow steps array\n const workflowSteps: Array<{\n step: string | number\n stepName: string\n inputOverrides: Record<string, unknown>\n condition?: string\n dependencies?: Array<{ stepName: string }>\n }> = []\n\n for (const stepDef of seedWorkflow.steps) {\n const stepName = `${seedWorkflow.name} - ${stepDef.name}`\n\n // Create step in automation-steps collection\n const step = await payload.create({\n collection: 'automation-steps',\n data: {\n name: stepName,\n type: stepDef.type,\n config: stepDef.input || {},\n },\n })\n logger.debug(`Created step: ${stepName}`)\n\n // Add to workflow steps with relationship ID\n workflowSteps.push({\n step: step.id,\n stepName: stepDef.name,\n inputOverrides: stepDef.input || {},\n condition: stepDef.condition,\n dependencies: stepDef.dependencies?.map(dep => ({ stepName: dep })),\n })\n }\n\n // Create the workflow with relationship IDs\n await payload.create({\n collection: 'workflows',\n data: {\n slug: seedWorkflow.slug,\n name: seedWorkflow.name,\n description: seedWorkflow.description,\n triggers: triggerIds,\n steps: workflowSteps,\n readOnly: true,\n },\n })\n\n logger.info(`Seeded workflow: ${seedWorkflow.name}`)\n } catch (error) {\n logger.error(`Failed to seed workflow '${seedWorkflow.name}':`, error)\n }\n }\n }\n\n logger.info(`Plugin configuration: ${collectionCount} collection triggers, ${globalCount} global triggers, ${stepCount} steps`\n )\n }\n\n return config\n }\n"],"names":["createTriggersCollection","createStepsCollection","createWorkflowCollection","WorkflowRunsCollection","getConfigLogger","initializeLogger","createCollectionTriggerHook","createGlobalTriggerHook","getLogger","applyCollectionsConfig","pluginOptions","config","collections","push","steps","workflowsPlugin","enabled","logger","collectionTriggers","collectionSlug","triggerConfig","Object","entries","collectionIndex","findIndex","c","slug","warn","collection","hooks","hooksToRegister","afterChange","afterDelete","afterRead","forEach","hookName","hookKey","automationHook","defineProperty","value","enumerable","debug","globals","globalTriggers","globalSlug","globalIndex","g","global","jobs","tasks","step","find","task","existingJobsOverrides","jobsCollectionOverrides","defaultJobsCollection","fields","name","type","relationTo","admin","position","readOnly","description","listSearchableFields","defaultColumns","incomingOnInit","onInit","payload","info","collectionCount","keys","length","globalCount","stepCount","seedWorkflows","seedWorkflow","existingWorkflow","where","equals","limit","docs","triggerIds","i","triggers","triggerDef","triggerName","triggerData","condition","parameters","hook","schedule","webhookPath","trigger","create","data","id","workflowSteps","stepDef","stepName","input","inputOverrides","dependencies","map","dep","error"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export type TriggerType = 'collection' | 'global';
2
+ export interface TriggerConfig {
3
+ type: TriggerType;
4
+ slug: string;
5
+ hookType: string;
6
+ }
7
+ /**
8
+ * Creates a unified hook handler for both collection and global triggers.
9
+ * Works with the new relationship-based data model where triggers are a separate collection.
10
+ */
11
+ export declare const createTriggerHook: (config: TriggerConfig) => (args: any) => Promise<void>;
12
+ export declare const createCollectionTriggerHook: (collectionSlug: string, hookType: string) => (args: any) => Promise<void>;
13
+ export declare const createGlobalTriggerHook: (globalSlug: string, hookType: string) => (args: any) => Promise<void>;
@@ -0,0 +1,184 @@
1
+ import { WorkflowExecutor } from '../core/workflow-executor.js';
2
+ /**
3
+ * Creates a unified hook handler for both collection and global triggers.
4
+ * Works with the new relationship-based data model where triggers are a separate collection.
5
+ */ export const createTriggerHook = (config)=>{
6
+ const { type, slug, hookType } = config;
7
+ // Build context based on trigger type
8
+ const buildContext = (args, firedTrigger)=>({
9
+ steps: {},
10
+ trigger: {
11
+ ...args,
12
+ type,
13
+ firedTrigger,
14
+ ...type === 'collection' ? {
15
+ collection: slug
16
+ } : {
17
+ global: slug,
18
+ operation: hookType
19
+ }
20
+ }
21
+ });
22
+ return async function automationTriggerHook(args) {
23
+ const req = 'req' in args ? args.req : 'args' in args ? args.args.req : undefined;
24
+ if (!req) {
25
+ throw new Error(`No request object found in ${type} hook arguments`);
26
+ }
27
+ const payload = req.payload;
28
+ const logger = payload.logger;
29
+ try {
30
+ logger.info({
31
+ triggerType: type,
32
+ slug,
33
+ hookType
34
+ }, `${type} automation hook triggered`);
35
+ const executor = new WorkflowExecutor(payload, logger);
36
+ // Step 1: Find all triggers that match this hook event
37
+ const triggerQuery = type === 'collection' ? {
38
+ type: {
39
+ equals: 'collection-hook'
40
+ },
41
+ collectionSlug: {
42
+ equals: slug
43
+ },
44
+ hook: {
45
+ equals: hookType
46
+ }
47
+ } : {
48
+ type: {
49
+ equals: 'global-hook'
50
+ },
51
+ globalSlug: {
52
+ equals: slug
53
+ },
54
+ hook: {
55
+ equals: hookType
56
+ }
57
+ };
58
+ const { docs: matchingTriggers } = await payload.find({
59
+ collection: 'automation-triggers',
60
+ depth: 0,
61
+ limit: 100,
62
+ where: triggerQuery
63
+ });
64
+ if (matchingTriggers.length === 0) {
65
+ logger.debug({
66
+ triggerType: type,
67
+ slug,
68
+ hookType
69
+ }, 'No matching triggers found');
70
+ return;
71
+ }
72
+ const triggerIds = matchingTriggers.map((t)=>t.id);
73
+ // Step 2: Find all enabled workflows that reference any of these triggers
74
+ const { docs: workflows } = await payload.find({
75
+ collection: 'workflows',
76
+ depth: 2,
77
+ limit: 100,
78
+ where: {
79
+ enabled: {
80
+ equals: true
81
+ },
82
+ triggers: {
83
+ in: triggerIds
84
+ }
85
+ }
86
+ });
87
+ if (workflows.length === 0) {
88
+ logger.debug({
89
+ triggerType: type,
90
+ slug,
91
+ hookType,
92
+ triggerIds
93
+ }, 'No enabled workflows found for matching triggers');
94
+ return;
95
+ }
96
+ // Step 3: Execute each matching workflow
97
+ for (const workflow of workflows){
98
+ // Find which trigger actually fired (for tracking in WorkflowRuns)
99
+ const firedTrigger = matchingTriggers.find((trigger)=>{
100
+ const workflowTriggerIds = (workflow.triggers || []).map((t)=>typeof t === 'object' ? t.id : t);
101
+ return workflowTriggerIds.includes(trigger.id);
102
+ });
103
+ if (!firedTrigger) {
104
+ continue;
105
+ }
106
+ const context = buildContext(args, firedTrigger);
107
+ // Evaluate trigger condition if present
108
+ if (firedTrigger.condition) {
109
+ try {
110
+ const conditionMet = await executor.evaluateCondition(firedTrigger.condition, context);
111
+ if (!conditionMet) {
112
+ logger.debug({
113
+ workflowId: workflow.id,
114
+ triggerId: firedTrigger.id,
115
+ condition: firedTrigger.condition
116
+ }, 'Workflow skipped due to unmet trigger condition');
117
+ continue;
118
+ }
119
+ } catch (error) {
120
+ logger.error({
121
+ workflowId: workflow.id,
122
+ triggerId: firedTrigger.id,
123
+ condition: firedTrigger.condition,
124
+ error: error instanceof Error ? error.message : 'Unknown error'
125
+ }, 'Failed to evaluate trigger condition');
126
+ continue;
127
+ }
128
+ }
129
+ try {
130
+ await executor.execute(workflow, context, req, firedTrigger);
131
+ logger.info({
132
+ workflowId: workflow.id,
133
+ workflowName: workflow.name,
134
+ triggerId: firedTrigger.id,
135
+ triggerName: firedTrigger.name,
136
+ triggerType: type,
137
+ slug,
138
+ hookType
139
+ }, 'Workflow executed successfully');
140
+ } catch (error) {
141
+ logger.error({
142
+ workflowId: workflow.id,
143
+ workflowName: workflow.name,
144
+ triggerId: firedTrigger.id,
145
+ triggerName: firedTrigger.name,
146
+ triggerType: type,
147
+ slug,
148
+ hookType,
149
+ error: error instanceof Error ? error.message : 'Unknown error'
150
+ }, 'Workflow execution failed');
151
+ // Don't throw to prevent breaking the original operation
152
+ }
153
+ }
154
+ logger.info({
155
+ triggerType: type,
156
+ slug,
157
+ hookType,
158
+ workflowCount: workflows.length
159
+ }, `${type} workflow execution completed`);
160
+ } catch (error) {
161
+ logger.error({
162
+ triggerType: type,
163
+ slug,
164
+ hookType,
165
+ error: error instanceof Error ? error.message : 'Unknown error',
166
+ errorStack: error instanceof Error ? error.stack : undefined
167
+ }, `${type} hook execution failed`);
168
+ // Don't throw to prevent breaking the original operation
169
+ }
170
+ };
171
+ };
172
+ // Convenience functions for creating hooks
173
+ export const createCollectionTriggerHook = (collectionSlug, hookType)=>createTriggerHook({
174
+ type: 'collection',
175
+ slug: collectionSlug,
176
+ hookType
177
+ });
178
+ export const createGlobalTriggerHook = (globalSlug, hookType)=>createTriggerHook({
179
+ type: 'global',
180
+ slug: globalSlug,
181
+ hookType
182
+ });
183
+
184
+ //# sourceMappingURL=trigger-hook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/plugin/trigger-hook.ts"],"sourcesContent":["import { WorkflowExecutor } from '../core/workflow-executor.js'\n\nexport type TriggerType = 'collection' | 'global'\n\nexport interface TriggerConfig {\n type: TriggerType\n slug: string\n hookType: string\n}\n\n/**\n * Creates a unified hook handler for both collection and global triggers.\n * Works with the new relationship-based data model where triggers are a separate collection.\n */\nexport const createTriggerHook = (config: TriggerConfig) => {\n const { type, slug, hookType } = config\n\n // Build context based on trigger type\n const buildContext = (args: any, firedTrigger?: any) => ({\n steps: {},\n trigger: {\n ...args,\n type,\n firedTrigger,\n ...(type === 'collection'\n ? { collection: slug }\n : { global: slug, operation: hookType }\n )\n }\n })\n\n return async function automationTriggerHook(args: any) {\n const req = 'req' in args ? args.req :\n 'args' in args ? args.args.req :\n undefined\n\n if (!req) {\n throw new Error(`No request object found in ${type} hook arguments`)\n }\n\n const payload = req.payload\n const logger = payload.logger\n\n try {\n logger.info({\n triggerType: type,\n slug,\n hookType\n }, `${type} automation hook triggered`)\n\n const executor = new WorkflowExecutor(payload, logger)\n\n // Step 1: Find all triggers that match this hook event\n const triggerQuery = type === 'collection'\n ? {\n type: { equals: 'collection-hook' },\n collectionSlug: { equals: slug },\n hook: { equals: hookType }\n }\n : {\n type: { equals: 'global-hook' },\n globalSlug: { equals: slug },\n hook: { equals: hookType }\n }\n\n const { docs: matchingTriggers } = await payload.find({\n collection: 'automation-triggers',\n depth: 0,\n limit: 100,\n where: triggerQuery\n })\n\n if (matchingTriggers.length === 0) {\n logger.debug({\n triggerType: type,\n slug,\n hookType\n }, 'No matching triggers found')\n return\n }\n\n const triggerIds = matchingTriggers.map((t: { id: string | number }) => t.id)\n\n // Step 2: Find all enabled workflows that reference any of these triggers\n const { docs: workflows } = await payload.find({\n collection: 'workflows',\n depth: 2, // Load trigger and step details\n limit: 100,\n where: {\n enabled: { equals: true },\n triggers: { in: triggerIds }\n }\n })\n\n if (workflows.length === 0) {\n logger.debug({\n triggerType: type,\n slug,\n hookType,\n triggerIds\n }, 'No enabled workflows found for matching triggers')\n return\n }\n\n // Step 3: Execute each matching workflow\n for (const workflow of workflows) {\n // Find which trigger actually fired (for tracking in WorkflowRuns)\n const firedTrigger = matchingTriggers.find((trigger: { id: string | number }) => {\n const workflowTriggerIds = (workflow.triggers || []).map((t: any) =>\n typeof t === 'object' ? t.id : t\n )\n return workflowTriggerIds.includes(trigger.id)\n })\n\n if (!firedTrigger) {\n continue\n }\n\n const context = buildContext(args, firedTrigger)\n\n // Evaluate trigger condition if present\n if (firedTrigger.condition) {\n try {\n const conditionMet = await executor.evaluateCondition(firedTrigger.condition, context)\n if (!conditionMet) {\n logger.debug({\n workflowId: workflow.id,\n triggerId: firedTrigger.id,\n condition: firedTrigger.condition\n }, 'Workflow skipped due to unmet trigger condition')\n continue\n }\n } catch (error) {\n logger.error({\n workflowId: workflow.id,\n triggerId: firedTrigger.id,\n condition: firedTrigger.condition,\n error: error instanceof Error ? error.message : 'Unknown error'\n }, 'Failed to evaluate trigger condition')\n continue\n }\n }\n\n try {\n await executor.execute(workflow as any, context, req, firedTrigger)\n logger.info({\n workflowId: workflow.id,\n workflowName: workflow.name,\n triggerId: firedTrigger.id,\n triggerName: firedTrigger.name,\n triggerType: type,\n slug,\n hookType\n }, 'Workflow executed successfully')\n } catch (error) {\n logger.error({\n workflowId: workflow.id,\n workflowName: workflow.name,\n triggerId: firedTrigger.id,\n triggerName: firedTrigger.name,\n triggerType: type,\n slug,\n hookType,\n error: error instanceof Error ? error.message : 'Unknown error'\n }, 'Workflow execution failed')\n // Don't throw to prevent breaking the original operation\n }\n }\n\n logger.info({\n triggerType: type,\n slug,\n hookType,\n workflowCount: workflows.length\n }, `${type} workflow execution completed`)\n\n } catch (error) {\n logger.error({\n triggerType: type,\n slug,\n hookType,\n error: error instanceof Error ? error.message : 'Unknown error',\n errorStack: error instanceof Error ? error.stack : undefined\n }, `${type} hook execution failed`)\n // Don't throw to prevent breaking the original operation\n }\n }\n}\n\n// Convenience functions for creating hooks\nexport const createCollectionTriggerHook = (collectionSlug: string, hookType: string) =>\n createTriggerHook({ type: 'collection', slug: collectionSlug, hookType })\n\nexport const createGlobalTriggerHook = (globalSlug: string, hookType: string) =>\n createTriggerHook({ type: 'global', slug: globalSlug, hookType })\n"],"names":["WorkflowExecutor","createTriggerHook","config","type","slug","hookType","buildContext","args","firedTrigger","steps","trigger","collection","global","operation","automationTriggerHook","req","undefined","Error","payload","logger","info","triggerType","executor","triggerQuery","equals","collectionSlug","hook","globalSlug","docs","matchingTriggers","find","depth","limit","where","length","debug","triggerIds","map","t","id","workflows","enabled","triggers","in","workflow","workflowTriggerIds","includes","context","condition","conditionMet","evaluateCondition","workflowId","triggerId","error","message","execute","workflowName","name","triggerName","workflowCount","errorStack","stack","createCollectionTriggerHook","createGlobalTriggerHook"],"mappings":"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"}