@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.
- package/README.md +221 -49
- package/dist/collections/Steps.d.ts +6 -0
- package/dist/collections/Steps.js +166 -0
- package/dist/collections/Steps.js.map +1 -0
- package/dist/collections/Triggers.d.ts +7 -0
- package/dist/collections/Triggers.js +224 -0
- package/dist/collections/Triggers.js.map +1 -0
- package/dist/collections/Workflow.d.ts +5 -2
- package/dist/collections/Workflow.js +179 -39
- package/dist/collections/Workflow.js.map +1 -1
- package/dist/collections/WorkflowRuns.d.ts +4 -0
- package/dist/collections/WorkflowRuns.js +219 -24
- package/dist/collections/WorkflowRuns.js.map +1 -1
- package/dist/components/WorkflowBuilder/WorkflowBuilder.js.map +1 -1
- package/dist/core/expression-engine.d.ts +58 -0
- package/dist/core/expression-engine.js +191 -0
- package/dist/core/expression-engine.js.map +1 -0
- package/dist/core/workflow-executor.d.ts +70 -56
- package/dist/core/workflow-executor.js +354 -677
- package/dist/core/workflow-executor.js.map +1 -1
- package/dist/exports/client.js +1 -3
- package/dist/exports/client.js.map +1 -1
- package/dist/exports/views.js +2 -4
- package/dist/exports/views.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/plugin/config-types.d.ts +43 -5
- package/dist/plugin/config-types.js +3 -1
- package/dist/plugin/config-types.js.map +1 -1
- package/dist/plugin/index.d.ts +1 -1
- package/dist/plugin/index.js +150 -55
- package/dist/plugin/index.js.map +1 -1
- package/dist/plugin/trigger-hook.d.ts +13 -0
- package/dist/plugin/trigger-hook.js +184 -0
- package/dist/plugin/trigger-hook.js.map +1 -0
- package/dist/steps/create-step.d.ts +66 -0
- package/dist/steps/create-step.js +59 -0
- package/dist/steps/create-step.js.map +1 -0
- package/dist/steps/index.d.ts +2 -0
- package/dist/steps/index.js +3 -0
- package/dist/steps/index.js.map +1 -1
- package/dist/steps/read-document-handler.js +1 -1
- package/dist/steps/read-document-handler.js.map +1 -1
- package/dist/steps/update-document-handler.js +1 -1
- package/dist/steps/update-document-handler.js.map +1 -1
- package/dist/triggers/hook-options.d.ts +34 -0
- package/dist/triggers/hook-options.js +158 -0
- package/dist/triggers/hook-options.js.map +1 -0
- package/dist/triggers/index.d.ts +2 -2
- package/dist/triggers/index.js +1 -2
- package/dist/triggers/index.js.map +1 -1
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.js +4 -5
- package/dist/types/index.js.map +1 -1
- package/dist/utils/validation.d.ts +64 -0
- package/dist/utils/validation.js +107 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +2 -1
- package/dist/plugin/collection-hook.d.ts +0 -1
- package/dist/plugin/collection-hook.js +0 -92
- package/dist/plugin/collection-hook.js.map +0 -1
- package/dist/plugin/global-hook.d.ts +0 -1
- package/dist/plugin/global-hook.js +0 -83
- package/dist/plugin/global-hook.js.map +0 -1
- package/dist/triggers/collection-trigger.d.ts +0 -2
- package/dist/triggers/collection-trigger.js +0 -36
- package/dist/triggers/collection-trigger.js.map +0 -1
- package/dist/triggers/global-trigger.d.ts +0 -2
- package/dist/triggers/global-trigger.js +0 -29
- package/dist/triggers/global-trigger.js.map +0 -1
- package/dist/triggers/types.d.ts +0 -5
- package/dist/triggers/types.js +0 -3
- package/dist/triggers/types.js.map +0 -1
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { collectionHookOptions } from '../triggers/hook-options.js';
|
|
2
|
+
/**
|
|
3
|
+
* Creates the automation-triggers collection.
|
|
4
|
+
* Triggers are reusable and can be shared across multiple workflows.
|
|
5
|
+
*/ export const createTriggersCollection = (options)=>{
|
|
6
|
+
const collectionSlugs = Object.keys(options.collectionTriggers || {});
|
|
7
|
+
const globalSlugs = Object.keys(options.globalTriggers || {});
|
|
8
|
+
return {
|
|
9
|
+
slug: 'automation-triggers',
|
|
10
|
+
access: {
|
|
11
|
+
create: ()=>true,
|
|
12
|
+
delete: ()=>true,
|
|
13
|
+
read: ()=>true,
|
|
14
|
+
update: ()=>true
|
|
15
|
+
},
|
|
16
|
+
admin: {
|
|
17
|
+
defaultColumns: [
|
|
18
|
+
'name',
|
|
19
|
+
'type',
|
|
20
|
+
'target',
|
|
21
|
+
'updatedAt'
|
|
22
|
+
],
|
|
23
|
+
description: 'Reusable trigger definitions that can be shared across workflows.',
|
|
24
|
+
group: 'Automation',
|
|
25
|
+
useAsTitle: 'name'
|
|
26
|
+
},
|
|
27
|
+
fields: [
|
|
28
|
+
{
|
|
29
|
+
name: 'name',
|
|
30
|
+
type: 'text',
|
|
31
|
+
admin: {
|
|
32
|
+
description: 'Human-readable name for this trigger'
|
|
33
|
+
},
|
|
34
|
+
required: true
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'description',
|
|
38
|
+
type: 'textarea',
|
|
39
|
+
admin: {
|
|
40
|
+
description: 'Optional description of when this trigger fires'
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: 'type',
|
|
45
|
+
type: 'select',
|
|
46
|
+
admin: {
|
|
47
|
+
description: 'The type of event that will fire this trigger'
|
|
48
|
+
},
|
|
49
|
+
defaultValue: 'collection-hook',
|
|
50
|
+
options: [
|
|
51
|
+
{
|
|
52
|
+
label: 'Collection Hook',
|
|
53
|
+
value: 'collection-hook'
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
label: 'Global Hook',
|
|
57
|
+
value: 'global-hook'
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
label: 'Scheduled (Cron)',
|
|
61
|
+
value: 'scheduled'
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
label: 'Webhook',
|
|
65
|
+
value: 'webhook'
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
label: 'Manual',
|
|
69
|
+
value: 'manual'
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
required: true
|
|
73
|
+
},
|
|
74
|
+
// Virtual field to show human-readable target
|
|
75
|
+
{
|
|
76
|
+
name: 'target',
|
|
77
|
+
type: 'text',
|
|
78
|
+
admin: {
|
|
79
|
+
readOnly: true,
|
|
80
|
+
description: 'The target of this trigger'
|
|
81
|
+
},
|
|
82
|
+
hooks: {
|
|
83
|
+
beforeChange: [
|
|
84
|
+
({ siblingData })=>{
|
|
85
|
+
// Compute target based on type
|
|
86
|
+
if (siblingData.type === 'collection-hook') {
|
|
87
|
+
return `${siblingData.collectionSlug}.${siblingData.hook}`;
|
|
88
|
+
} else if (siblingData.type === 'global-hook') {
|
|
89
|
+
return `${siblingData.globalSlug}.${siblingData.hook}`;
|
|
90
|
+
} else if (siblingData.type === 'scheduled') {
|
|
91
|
+
return siblingData.schedule || 'Not configured';
|
|
92
|
+
} else if (siblingData.type === 'webhook') {
|
|
93
|
+
return siblingData.webhookPath || 'Not configured';
|
|
94
|
+
}
|
|
95
|
+
return 'Manual trigger';
|
|
96
|
+
}
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
// Collection Hook fields
|
|
101
|
+
{
|
|
102
|
+
name: 'collectionSlug',
|
|
103
|
+
type: 'select',
|
|
104
|
+
admin: {
|
|
105
|
+
condition: (_, siblingData)=>siblingData?.type === 'collection-hook',
|
|
106
|
+
description: 'The collection to watch for events'
|
|
107
|
+
},
|
|
108
|
+
options: collectionSlugs.map((slug)=>({
|
|
109
|
+
label: slug,
|
|
110
|
+
value: slug
|
|
111
|
+
}))
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: 'hook',
|
|
115
|
+
type: 'select',
|
|
116
|
+
admin: {
|
|
117
|
+
condition: (_, siblingData)=>siblingData?.type === 'collection-hook' || siblingData?.type === 'global-hook',
|
|
118
|
+
description: 'The specific hook event to listen for'
|
|
119
|
+
},
|
|
120
|
+
options: collectionHookOptions.map((opt)=>({
|
|
121
|
+
label: opt.label,
|
|
122
|
+
value: opt.value
|
|
123
|
+
}))
|
|
124
|
+
},
|
|
125
|
+
// Global Hook fields
|
|
126
|
+
{
|
|
127
|
+
name: 'globalSlug',
|
|
128
|
+
type: 'select',
|
|
129
|
+
admin: {
|
|
130
|
+
condition: (_, siblingData)=>siblingData?.type === 'global-hook',
|
|
131
|
+
description: 'The global to watch for events'
|
|
132
|
+
},
|
|
133
|
+
options: globalSlugs.map((slug)=>({
|
|
134
|
+
label: slug,
|
|
135
|
+
value: slug
|
|
136
|
+
}))
|
|
137
|
+
},
|
|
138
|
+
// Scheduled fields
|
|
139
|
+
{
|
|
140
|
+
name: 'schedule',
|
|
141
|
+
type: 'text',
|
|
142
|
+
admin: {
|
|
143
|
+
condition: (_, siblingData)=>siblingData?.type === 'scheduled',
|
|
144
|
+
description: 'Cron expression (e.g., "0 9 * * *" for 9 AM daily)',
|
|
145
|
+
placeholder: '0 9 * * *'
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
// Webhook fields
|
|
149
|
+
{
|
|
150
|
+
name: 'webhookPath',
|
|
151
|
+
type: 'text',
|
|
152
|
+
admin: {
|
|
153
|
+
condition: (_, siblingData)=>siblingData?.type === 'webhook',
|
|
154
|
+
description: 'The URL path for this webhook (e.g., "my-webhook")',
|
|
155
|
+
placeholder: 'my-webhook'
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
// Condition configuration
|
|
159
|
+
{
|
|
160
|
+
type: 'collapsible',
|
|
161
|
+
label: 'Condition',
|
|
162
|
+
admin: {
|
|
163
|
+
initCollapsed: true
|
|
164
|
+
},
|
|
165
|
+
fields: [
|
|
166
|
+
{
|
|
167
|
+
name: 'condition',
|
|
168
|
+
type: 'code',
|
|
169
|
+
admin: {
|
|
170
|
+
description: 'JSONata expression that must evaluate to true for this trigger to fire. Leave empty to always fire. Example: trigger.doc._status = "published"',
|
|
171
|
+
language: 'javascript'
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
name: 'conditionDescription',
|
|
176
|
+
type: 'text',
|
|
177
|
+
admin: {
|
|
178
|
+
description: 'Human-readable explanation of the condition (for documentation)',
|
|
179
|
+
placeholder: 'e.g., "Only when status is published"'
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
]
|
|
183
|
+
},
|
|
184
|
+
// Usage tracking
|
|
185
|
+
{
|
|
186
|
+
name: 'usageCount',
|
|
187
|
+
type: 'number',
|
|
188
|
+
admin: {
|
|
189
|
+
description: 'Number of workflows using this trigger',
|
|
190
|
+
readOnly: true,
|
|
191
|
+
position: 'sidebar'
|
|
192
|
+
},
|
|
193
|
+
defaultValue: 0
|
|
194
|
+
}
|
|
195
|
+
],
|
|
196
|
+
hooks: {
|
|
197
|
+
beforeChange: [
|
|
198
|
+
// Validate required fields based on type
|
|
199
|
+
async ({ data, operation })=>{
|
|
200
|
+
if (operation === 'create' || operation === 'update') {
|
|
201
|
+
if (data?.type === 'collection-hook' && !data?.collectionSlug) {
|
|
202
|
+
throw new Error('Collection is required for collection hook triggers');
|
|
203
|
+
}
|
|
204
|
+
if (data?.type === 'global-hook' && !data?.globalSlug) {
|
|
205
|
+
throw new Error('Global is required for global hook triggers');
|
|
206
|
+
}
|
|
207
|
+
if ((data?.type === 'collection-hook' || data?.type === 'global-hook') && !data?.hook) {
|
|
208
|
+
throw new Error('Hook type is required');
|
|
209
|
+
}
|
|
210
|
+
if (data?.type === 'scheduled' && !data?.schedule) {
|
|
211
|
+
throw new Error('Schedule is required for scheduled triggers');
|
|
212
|
+
}
|
|
213
|
+
if (data?.type === 'webhook' && !data?.webhookPath) {
|
|
214
|
+
throw new Error('Webhook path is required for webhook triggers');
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return data;
|
|
218
|
+
}
|
|
219
|
+
]
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
//# sourceMappingURL=Triggers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/collections/Triggers.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\n\nimport type { WorkflowsPluginConfig } from '../plugin/config-types.js'\n\nimport { collectionHookOptions, globalHookOptions } from '../triggers/hook-options.js'\n\n/**\n * Creates the automation-triggers collection.\n * Triggers are reusable and can be shared across multiple workflows.\n */\nexport const createTriggersCollection = <T extends string>(\n options: WorkflowsPluginConfig<T>\n): CollectionConfig => {\n const collectionSlugs = Object.keys(options.collectionTriggers || {})\n const globalSlugs = Object.keys(options.globalTriggers || {})\n\n return {\n slug: 'automation-triggers',\n access: {\n create: () => true,\n delete: () => true,\n read: () => true,\n update: () => true,\n },\n admin: {\n defaultColumns: ['name', 'type', 'target', 'updatedAt'],\n description: 'Reusable trigger definitions that can be shared across workflows.',\n group: 'Automation',\n useAsTitle: 'name',\n },\n fields: [\n {\n name: 'name',\n type: 'text',\n admin: {\n description: 'Human-readable name for this trigger',\n },\n required: true,\n },\n {\n name: 'description',\n type: 'textarea',\n admin: {\n description: 'Optional description of when this trigger fires',\n },\n },\n {\n name: 'type',\n type: 'select',\n admin: {\n description: 'The type of event that will fire this trigger',\n },\n defaultValue: 'collection-hook',\n options: [\n { label: 'Collection Hook', value: 'collection-hook' },\n { label: 'Global Hook', value: 'global-hook' },\n { label: 'Scheduled (Cron)', value: 'scheduled' },\n { label: 'Webhook', value: 'webhook' },\n { label: 'Manual', value: 'manual' },\n ],\n required: true,\n },\n // Virtual field to show human-readable target\n {\n name: 'target',\n type: 'text',\n admin: {\n readOnly: true,\n description: 'The target of this trigger',\n },\n hooks: {\n beforeChange: [\n ({ siblingData }) => {\n // Compute target based on type\n if (siblingData.type === 'collection-hook') {\n return `${siblingData.collectionSlug}.${siblingData.hook}`\n } else if (siblingData.type === 'global-hook') {\n return `${siblingData.globalSlug}.${siblingData.hook}`\n } else if (siblingData.type === 'scheduled') {\n return siblingData.schedule || 'Not configured'\n } else if (siblingData.type === 'webhook') {\n return siblingData.webhookPath || 'Not configured'\n }\n return 'Manual trigger'\n }\n ]\n }\n },\n // Collection Hook fields\n {\n name: 'collectionSlug',\n type: 'select',\n admin: {\n condition: (_, siblingData) => siblingData?.type === 'collection-hook',\n description: 'The collection to watch for events',\n },\n options: collectionSlugs.map(slug => ({ label: slug, value: slug })),\n },\n {\n name: 'hook',\n type: 'select',\n admin: {\n condition: (_, siblingData) =>\n siblingData?.type === 'collection-hook' || siblingData?.type === 'global-hook',\n description: 'The specific hook event to listen for',\n },\n options: collectionHookOptions.map(opt => ({ label: opt.label, value: opt.value })),\n },\n // Global Hook fields\n {\n name: 'globalSlug',\n type: 'select',\n admin: {\n condition: (_, siblingData) => siblingData?.type === 'global-hook',\n description: 'The global to watch for events',\n },\n options: globalSlugs.map(slug => ({ label: slug, value: slug })),\n },\n // Scheduled fields\n {\n name: 'schedule',\n type: 'text',\n admin: {\n condition: (_, siblingData) => siblingData?.type === 'scheduled',\n description: 'Cron expression (e.g., \"0 9 * * *\" for 9 AM daily)',\n placeholder: '0 9 * * *',\n },\n },\n // Webhook fields\n {\n name: 'webhookPath',\n type: 'text',\n admin: {\n condition: (_, siblingData) => siblingData?.type === 'webhook',\n description: 'The URL path for this webhook (e.g., \"my-webhook\")',\n placeholder: 'my-webhook',\n },\n },\n // Condition configuration\n {\n type: 'collapsible',\n label: 'Condition',\n admin: {\n initCollapsed: true,\n },\n fields: [\n {\n name: 'condition',\n type: 'code',\n admin: {\n description: 'JSONata expression that must evaluate to true for this trigger to fire. Leave empty to always fire. Example: trigger.doc._status = \"published\"',\n language: 'javascript',\n },\n },\n {\n name: 'conditionDescription',\n type: 'text',\n admin: {\n description: 'Human-readable explanation of the condition (for documentation)',\n placeholder: 'e.g., \"Only when status is published\"',\n },\n },\n ],\n },\n // Usage tracking\n {\n name: 'usageCount',\n type: 'number',\n admin: {\n description: 'Number of workflows using this trigger',\n readOnly: true,\n position: 'sidebar',\n },\n defaultValue: 0,\n },\n ],\n hooks: {\n beforeChange: [\n // Validate required fields based on type\n async ({ data, operation }) => {\n if (operation === 'create' || operation === 'update') {\n if (data?.type === 'collection-hook' && !data?.collectionSlug) {\n throw new Error('Collection is required for collection hook triggers')\n }\n if (data?.type === 'global-hook' && !data?.globalSlug) {\n throw new Error('Global is required for global hook triggers')\n }\n if ((data?.type === 'collection-hook' || data?.type === 'global-hook') && !data?.hook) {\n throw new Error('Hook type is required')\n }\n if (data?.type === 'scheduled' && !data?.schedule) {\n throw new Error('Schedule is required for scheduled triggers')\n }\n if (data?.type === 'webhook' && !data?.webhookPath) {\n throw new Error('Webhook path is required for webhook triggers')\n }\n }\n return data\n }\n ],\n },\n }\n}\n"],"names":["collectionHookOptions","createTriggersCollection","options","collectionSlugs","Object","keys","collectionTriggers","globalSlugs","globalTriggers","slug","access","create","delete","read","update","admin","defaultColumns","description","group","useAsTitle","fields","name","type","required","defaultValue","label","value","readOnly","hooks","beforeChange","siblingData","collectionSlug","hook","globalSlug","schedule","webhookPath","condition","_","map","opt","placeholder","initCollapsed","language","position","data","operation","Error"],"mappings":"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"}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
import type { CollectionConfig } from 'payload';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Creates the workflows collection.
|
|
4
|
+
* Workflows reference triggers and steps via relationships for reusability.
|
|
5
|
+
*/
|
|
6
|
+
export declare const createWorkflowCollection: () => CollectionConfig;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const triggers = (options.triggers || []).map((t)=>t(options)).concat(collectionTrigger(options), globalTrigger(options));
|
|
1
|
+
/**
|
|
2
|
+
* Creates the workflows collection.
|
|
3
|
+
* Workflows reference triggers and steps via relationships for reusability.
|
|
4
|
+
*/ export const createWorkflowCollection = ()=>{
|
|
6
5
|
return {
|
|
7
6
|
slug: 'workflows',
|
|
8
7
|
access: {
|
|
@@ -28,6 +27,7 @@ export const createWorkflowCollection = (options)=>{
|
|
|
28
27
|
'name',
|
|
29
28
|
'slug',
|
|
30
29
|
'readOnly',
|
|
30
|
+
'enabled',
|
|
31
31
|
'updatedAt'
|
|
32
32
|
],
|
|
33
33
|
description: 'Create and manage automated workflows.',
|
|
@@ -80,79 +80,219 @@ export const createWorkflowCollection = (options)=>{
|
|
|
80
80
|
description: 'Optional description of what this workflow does'
|
|
81
81
|
}
|
|
82
82
|
},
|
|
83
|
+
{
|
|
84
|
+
name: 'enabled',
|
|
85
|
+
type: 'checkbox',
|
|
86
|
+
admin: {
|
|
87
|
+
description: 'Enable or disable this workflow',
|
|
88
|
+
position: 'sidebar'
|
|
89
|
+
},
|
|
90
|
+
defaultValue: true
|
|
91
|
+
},
|
|
92
|
+
// Triggers - relationship to automation-triggers collection
|
|
83
93
|
{
|
|
84
94
|
name: 'triggers',
|
|
95
|
+
type: 'relationship',
|
|
96
|
+
admin: {
|
|
97
|
+
description: 'Triggers that can start this workflow. Uses OR logic - workflow runs if ANY trigger fires.'
|
|
98
|
+
},
|
|
99
|
+
hasMany: true,
|
|
100
|
+
relationTo: 'automation-triggers'
|
|
101
|
+
},
|
|
102
|
+
// Steps with workflow-specific configuration
|
|
103
|
+
{
|
|
104
|
+
name: 'steps',
|
|
85
105
|
type: 'array',
|
|
106
|
+
admin: {
|
|
107
|
+
description: 'Steps to execute when this workflow runs. Steps execute in order based on dependencies.'
|
|
108
|
+
},
|
|
86
109
|
fields: [
|
|
87
110
|
{
|
|
88
|
-
name: '
|
|
89
|
-
type: '
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
111
|
+
name: 'step',
|
|
112
|
+
type: 'relationship',
|
|
113
|
+
admin: {
|
|
114
|
+
description: 'Select a step from the step library'
|
|
115
|
+
},
|
|
116
|
+
relationTo: 'automation-steps',
|
|
117
|
+
required: true
|
|
93
118
|
},
|
|
94
119
|
{
|
|
95
|
-
name: '
|
|
120
|
+
name: 'stepName',
|
|
121
|
+
type: 'text',
|
|
122
|
+
admin: {
|
|
123
|
+
description: 'Override the step name for this workflow instance (optional)'
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: 'inputOverrides',
|
|
96
128
|
type: 'json',
|
|
97
129
|
admin: {
|
|
98
|
-
|
|
130
|
+
description: 'Override step configuration values for this workflow. Merged with step defaults.'
|
|
99
131
|
},
|
|
100
132
|
defaultValue: {}
|
|
101
133
|
},
|
|
102
|
-
// Virtual fields for custom triggers
|
|
103
|
-
...triggers.flatMap((t)=>(t.parameters || []).map((p)=>parameter(t.slug, p))),
|
|
104
134
|
{
|
|
105
135
|
name: 'condition',
|
|
106
|
-
type: '
|
|
136
|
+
type: 'code',
|
|
137
|
+
admin: {
|
|
138
|
+
description: 'JSONata expression that must evaluate to true for this step to execute. Leave empty to always run. Example: trigger.operation = "create"',
|
|
139
|
+
language: 'javascript'
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
name: 'dependencies',
|
|
144
|
+
type: 'array',
|
|
107
145
|
admin: {
|
|
108
|
-
description: '
|
|
146
|
+
description: 'Steps that must complete before this step can run'
|
|
109
147
|
},
|
|
110
|
-
|
|
148
|
+
fields: [
|
|
149
|
+
{
|
|
150
|
+
name: 'stepIndex',
|
|
151
|
+
type: 'number',
|
|
152
|
+
admin: {
|
|
153
|
+
description: 'Index of the dependent step (0-based)'
|
|
154
|
+
},
|
|
155
|
+
required: true
|
|
156
|
+
}
|
|
157
|
+
]
|
|
158
|
+
},
|
|
159
|
+
// Visual builder position
|
|
160
|
+
{
|
|
161
|
+
name: 'position',
|
|
162
|
+
type: 'point',
|
|
163
|
+
admin: {
|
|
164
|
+
description: 'Position in the visual workflow builder',
|
|
165
|
+
hidden: true
|
|
166
|
+
}
|
|
111
167
|
}
|
|
112
168
|
]
|
|
113
169
|
},
|
|
170
|
+
// Global workflow settings
|
|
114
171
|
{
|
|
115
|
-
|
|
116
|
-
|
|
172
|
+
type: 'collapsible',
|
|
173
|
+
label: 'Error Handling',
|
|
174
|
+
admin: {
|
|
175
|
+
initCollapsed: true
|
|
176
|
+
},
|
|
117
177
|
fields: [
|
|
118
178
|
{
|
|
119
|
-
name: '
|
|
120
|
-
type: 'text',
|
|
121
|
-
defaultValue: 'Unnamed Step'
|
|
122
|
-
},
|
|
123
|
-
{
|
|
124
|
-
name: 'type',
|
|
179
|
+
name: 'errorHandling',
|
|
125
180
|
type: 'select',
|
|
126
|
-
|
|
181
|
+
admin: {
|
|
182
|
+
description: 'How to handle step failures'
|
|
183
|
+
},
|
|
184
|
+
defaultValue: 'stop',
|
|
185
|
+
options: [
|
|
186
|
+
{
|
|
187
|
+
label: 'Stop workflow',
|
|
188
|
+
value: 'stop'
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
label: 'Continue to next step',
|
|
192
|
+
value: 'continue'
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
label: 'Retry failed step',
|
|
196
|
+
value: 'retry'
|
|
197
|
+
}
|
|
198
|
+
]
|
|
127
199
|
},
|
|
128
200
|
{
|
|
129
|
-
name: '
|
|
130
|
-
type: '
|
|
201
|
+
name: 'maxRetries',
|
|
202
|
+
type: 'number',
|
|
131
203
|
admin: {
|
|
132
|
-
|
|
204
|
+
condition: (_, siblingData)=>siblingData?.errorHandling === 'retry',
|
|
205
|
+
description: 'Maximum number of retry attempts'
|
|
133
206
|
},
|
|
134
|
-
defaultValue:
|
|
207
|
+
defaultValue: 3
|
|
135
208
|
},
|
|
136
209
|
{
|
|
137
|
-
name: '
|
|
138
|
-
type: '
|
|
210
|
+
name: 'retryDelay',
|
|
211
|
+
type: 'number',
|
|
139
212
|
admin: {
|
|
140
|
-
|
|
213
|
+
condition: (_, siblingData)=>siblingData?.errorHandling === 'retry',
|
|
214
|
+
description: 'Delay between retries in milliseconds'
|
|
141
215
|
},
|
|
142
|
-
|
|
143
|
-
required: false
|
|
216
|
+
defaultValue: 1000
|
|
144
217
|
},
|
|
145
218
|
{
|
|
146
|
-
name: '
|
|
147
|
-
type: '
|
|
219
|
+
name: 'timeout',
|
|
220
|
+
type: 'number',
|
|
148
221
|
admin: {
|
|
149
|
-
description: '
|
|
222
|
+
description: 'Maximum execution time in milliseconds (0 for no timeout)'
|
|
150
223
|
},
|
|
151
|
-
|
|
224
|
+
defaultValue: 300000
|
|
152
225
|
}
|
|
153
226
|
]
|
|
154
227
|
}
|
|
155
228
|
],
|
|
229
|
+
hooks: {
|
|
230
|
+
afterChange: [
|
|
231
|
+
// Update usage counts for triggers and steps
|
|
232
|
+
async ({ doc, req })=>{
|
|
233
|
+
const payload = req.payload;
|
|
234
|
+
// Update trigger usage counts
|
|
235
|
+
if (doc.triggers && Array.isArray(doc.triggers)) {
|
|
236
|
+
for (const triggerId of doc.triggers){
|
|
237
|
+
const id = typeof triggerId === 'object' ? triggerId.id : triggerId;
|
|
238
|
+
if (id) {
|
|
239
|
+
try {
|
|
240
|
+
// Count workflows using this trigger
|
|
241
|
+
const count = await payload.count({
|
|
242
|
+
collection: 'workflows',
|
|
243
|
+
where: {
|
|
244
|
+
triggers: {
|
|
245
|
+
contains: id
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
await payload.update({
|
|
250
|
+
collection: 'automation-triggers',
|
|
251
|
+
id,
|
|
252
|
+
data: {
|
|
253
|
+
usageCount: count.totalDocs
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
} catch {
|
|
257
|
+
// Ignore errors - trigger might have been deleted
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
// Update step usage counts
|
|
263
|
+
if (doc.steps && Array.isArray(doc.steps)) {
|
|
264
|
+
const stepIds = new Set();
|
|
265
|
+
for (const workflowStep of doc.steps){
|
|
266
|
+
const stepId = typeof workflowStep.step === 'object' ? workflowStep.step.id : workflowStep.step;
|
|
267
|
+
if (stepId) stepIds.add(stepId);
|
|
268
|
+
}
|
|
269
|
+
for (const stepId of stepIds){
|
|
270
|
+
try {
|
|
271
|
+
// Count workflows using this step
|
|
272
|
+
const count = await payload.count({
|
|
273
|
+
collection: 'workflows',
|
|
274
|
+
where: {
|
|
275
|
+
'steps.step': {
|
|
276
|
+
equals: stepId
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
await payload.update({
|
|
281
|
+
collection: 'automation-steps',
|
|
282
|
+
id: stepId,
|
|
283
|
+
data: {
|
|
284
|
+
usageCount: count.totalDocs
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
} catch {
|
|
288
|
+
// Ignore errors - step might have been deleted
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return doc;
|
|
293
|
+
}
|
|
294
|
+
]
|
|
295
|
+
},
|
|
156
296
|
versions: {
|
|
157
297
|
drafts: {
|
|
158
298
|
autosave: false
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/collections/Workflow.ts"],"sourcesContent":["import type {CollectionConfig} from 'payload'\n\nimport type {WorkflowsPluginConfig} from \"../plugin/config-types.js\"\n\nimport {parameter} from \"../fields/parameter.js\"\nimport {collectionTrigger, globalTrigger} from \"../triggers/index.js\"\n\nexport const createWorkflowCollection: <T extends string>(options: WorkflowsPluginConfig<T>) => CollectionConfig = (options) => {\n const steps = options.steps || []\n const triggers = (options.triggers || []).map(t => t(options)).concat(collectionTrigger(options), globalTrigger(options))\n return {\n slug: 'workflows',\n access: {\n create: () => true,\n delete: ({ req, data }) => {\n // Prevent deletion of read-only workflows\n if (data?.readOnly === true) {\n return false\n }\n return true\n },\n read: () => true,\n update: ({ req, data }) => {\n // Prevent updates to read-only workflows\n if (data?.readOnly === true) {\n return false\n }\n return true\n },\n },\n admin: {\n defaultColumns: ['name', 'slug', 'readOnly', 'updatedAt'],\n description: 'Create and manage automated workflows.',\n group: 'Automation',\n useAsTitle: 'name',\n },\n fields: [\n {\n name: 'name',\n type: 'text',\n admin: {\n description: 'Human-readable name for the workflow',\n },\n required: true,\n },\n {\n name: 'slug',\n type: 'text',\n admin: {\n description: 'URL-safe unique identifier for this workflow',\n position: 'sidebar',\n },\n index: true,\n unique: true,\n },\n {\n name: 'readOnly',\n type: 'checkbox',\n admin: {\n description: 'Read-only workflows cannot be edited or deleted. This is typically used for seeded template workflows.',\n position: 'sidebar',\n readOnly: true,\n },\n defaultValue: false,\n },\n {\n name: 'readOnlyBanner',\n type: 'ui',\n admin: {\n components: {\n Field: '@xtr-dev/payload-automation/client#ReadOnlyBanner',\n },\n condition: (data) => data?.readOnly === true,\n },\n },\n {\n name: 'description',\n type: 'textarea',\n admin: {\n description: 'Optional description of what this workflow does',\n },\n },\n {\n name: 'triggers',\n type: 'array',\n fields: [\n {\n name: 'type',\n type: 'select',\n options: [\n ...triggers.map(t => t.slug)\n ]\n },\n {\n name: 'parameters',\n type: 'json',\n admin: {\n hidden: true,\n },\n defaultValue: {}\n },\n // Virtual fields for custom triggers\n ...triggers.flatMap(t => (t.parameters || []).map(p => parameter(t.slug, p as any))),\n {\n name: 'condition',\n type: 'text',\n admin: {\n description: 'JSONPath expression that must evaluate to true for this trigger to execute the workflow (e.g., \"$.trigger.doc.status == \\'published\\'\")'\n },\n required: false\n },\n ]\n },\n {\n name: 'steps',\n type: 'array',\n fields: [\n {\n name: 'name',\n type: 'text',\n defaultValue: 'Unnamed Step'\n },\n {\n name: 'type',\n type: 'select',\n options: steps.map(t => t.slug)\n },\n {\n name: 'input',\n type: 'json',\n admin: {\n description: 'Step input configuration. Use JSONPath expressions to reference dynamic data (e.g., {\"url\": \"$.trigger.doc.webhookUrl\", \"data\": \"$.steps.previousStep.output.result\"})'\n },\n defaultValue: {}\n },\n {\n name: 'dependencies',\n type: 'text',\n admin: {\n description: 'Step names that must complete before this step can run'\n },\n hasMany: true,\n required: false\n },\n {\n name: 'condition',\n type: 'text',\n admin: {\n description: 'JSONPath expression that must evaluate to true for this step to execute (e.g., \"$.trigger.doc.status == \\'published\\'\")'\n },\n required: false\n },\n ],\n }\n ],\n versions: {\n drafts: {\n autosave: false,\n },\n maxPerDoc: 10,\n },\n }\n}\n"],"names":["parameter","collectionTrigger","globalTrigger","createWorkflowCollection","options","steps","triggers","map","t","concat","slug","access","create","delete","req","data","readOnly","read","update","admin","defaultColumns","description","group","useAsTitle","fields","name","type","required","position","index","unique","defaultValue","components","Field","condition","hidden","flatMap","parameters","p","hasMany","versions","drafts","autosave","maxPerDoc"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/collections/Workflow.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\n\n/**\n * Creates the workflows collection.\n * Workflows reference triggers and steps via relationships for reusability.\n */\nexport const createWorkflowCollection = (): CollectionConfig => {\n return {\n slug: 'workflows',\n access: {\n create: () => true,\n delete: ({ req, data }) => {\n // Prevent deletion of read-only workflows\n if (data?.readOnly === true) {\n return false\n }\n return true\n },\n read: () => true,\n update: ({ req, data }) => {\n // Prevent updates to read-only workflows\n if (data?.readOnly === true) {\n return false\n }\n return true\n },\n },\n admin: {\n defaultColumns: ['name', 'slug', 'readOnly', 'enabled', 'updatedAt'],\n description: 'Create and manage automated workflows.',\n group: 'Automation',\n useAsTitle: 'name',\n },\n fields: [\n {\n name: 'name',\n type: 'text',\n admin: {\n description: 'Human-readable name for the workflow',\n },\n required: true,\n },\n {\n name: 'slug',\n type: 'text',\n admin: {\n description: 'URL-safe unique identifier for this workflow',\n position: 'sidebar',\n },\n index: true,\n unique: true,\n },\n {\n name: 'readOnly',\n type: 'checkbox',\n admin: {\n description: 'Read-only workflows cannot be edited or deleted. This is typically used for seeded template workflows.',\n position: 'sidebar',\n readOnly: true,\n },\n defaultValue: false,\n },\n {\n name: 'readOnlyBanner',\n type: 'ui',\n admin: {\n components: {\n Field: '@xtr-dev/payload-automation/client#ReadOnlyBanner',\n },\n condition: (data) => data?.readOnly === true,\n },\n },\n {\n name: 'description',\n type: 'textarea',\n admin: {\n description: 'Optional description of what this workflow does',\n },\n },\n {\n name: 'enabled',\n type: 'checkbox',\n admin: {\n description: 'Enable or disable this workflow',\n position: 'sidebar',\n },\n defaultValue: true,\n },\n // Triggers - relationship to automation-triggers collection\n {\n name: 'triggers',\n type: 'relationship',\n admin: {\n description: 'Triggers that can start this workflow. Uses OR logic - workflow runs if ANY trigger fires.',\n },\n hasMany: true,\n relationTo: 'automation-triggers',\n },\n // Steps with workflow-specific configuration\n {\n name: 'steps',\n type: 'array',\n admin: {\n description: 'Steps to execute when this workflow runs. Steps execute in order based on dependencies.',\n },\n fields: [\n {\n name: 'step',\n type: 'relationship',\n admin: {\n description: 'Select a step from the step library',\n },\n relationTo: 'automation-steps',\n required: true,\n },\n {\n name: 'stepName',\n type: 'text',\n admin: {\n description: 'Override the step name for this workflow instance (optional)',\n },\n },\n {\n name: 'inputOverrides',\n type: 'json',\n admin: {\n description: 'Override step configuration values for this workflow. Merged with step defaults.',\n },\n defaultValue: {},\n },\n {\n name: 'condition',\n type: 'code',\n admin: {\n description: 'JSONata expression that must evaluate to true for this step to execute. Leave empty to always run. Example: trigger.operation = \"create\"',\n language: 'javascript',\n },\n },\n {\n name: 'dependencies',\n type: 'array',\n admin: {\n description: 'Steps that must complete before this step can run',\n },\n fields: [\n {\n name: 'stepIndex',\n type: 'number',\n admin: {\n description: 'Index of the dependent step (0-based)',\n },\n required: true,\n },\n ],\n },\n // Visual builder position\n {\n name: 'position',\n type: 'point',\n admin: {\n description: 'Position in the visual workflow builder',\n hidden: true,\n },\n },\n ],\n },\n // Global workflow settings\n {\n type: 'collapsible',\n label: 'Error Handling',\n admin: {\n initCollapsed: true,\n },\n fields: [\n {\n name: 'errorHandling',\n type: 'select',\n admin: {\n description: 'How to handle step failures',\n },\n defaultValue: 'stop',\n options: [\n { label: 'Stop workflow', value: 'stop' },\n { label: 'Continue to next step', value: 'continue' },\n { label: 'Retry failed step', value: 'retry' },\n ],\n },\n {\n name: 'maxRetries',\n type: 'number',\n admin: {\n condition: (_, siblingData) => siblingData?.errorHandling === 'retry',\n description: 'Maximum number of retry attempts',\n },\n defaultValue: 3,\n },\n {\n name: 'retryDelay',\n type: 'number',\n admin: {\n condition: (_, siblingData) => siblingData?.errorHandling === 'retry',\n description: 'Delay between retries in milliseconds',\n },\n defaultValue: 1000,\n },\n {\n name: 'timeout',\n type: 'number',\n admin: {\n description: 'Maximum execution time in milliseconds (0 for no timeout)',\n },\n defaultValue: 300000, // 5 minutes\n },\n ],\n },\n ],\n hooks: {\n afterChange: [\n // Update usage counts for triggers and steps\n async ({ doc, req }) => {\n const payload = req.payload\n\n // Update trigger usage counts\n if (doc.triggers && Array.isArray(doc.triggers)) {\n for (const triggerId of doc.triggers) {\n const id = typeof triggerId === 'object' ? triggerId.id : triggerId\n if (id) {\n try {\n // Count workflows using this trigger\n const count = await payload.count({\n collection: 'workflows',\n where: {\n triggers: { contains: id },\n },\n })\n await payload.update({\n collection: 'automation-triggers',\n id,\n data: { usageCount: count.totalDocs },\n })\n } catch {\n // Ignore errors - trigger might have been deleted\n }\n }\n }\n }\n\n // Update step usage counts\n if (doc.steps && Array.isArray(doc.steps)) {\n const stepIds = new Set<string>()\n for (const workflowStep of doc.steps) {\n const stepId = typeof workflowStep.step === 'object'\n ? workflowStep.step.id\n : workflowStep.step\n if (stepId) stepIds.add(stepId)\n }\n\n for (const stepId of stepIds) {\n try {\n // Count workflows using this step\n const count = await payload.count({\n collection: 'workflows',\n where: {\n 'steps.step': { equals: stepId },\n },\n })\n await payload.update({\n collection: 'automation-steps',\n id: stepId,\n data: { usageCount: count.totalDocs },\n })\n } catch {\n // Ignore errors - step might have been deleted\n }\n }\n }\n\n return doc\n },\n ],\n },\n versions: {\n drafts: {\n autosave: false,\n },\n maxPerDoc: 10,\n },\n }\n}\n"],"names":["createWorkflowCollection","slug","access","create","delete","req","data","readOnly","read","update","admin","defaultColumns","description","group","useAsTitle","fields","name","type","required","position","index","unique","defaultValue","components","Field","condition","hasMany","relationTo","language","hidden","label","initCollapsed","options","value","_","siblingData","errorHandling","hooks","afterChange","doc","payload","triggers","Array","isArray","triggerId","id","count","collection","where","contains","usageCount","totalDocs","steps","stepIds","Set","workflowStep","stepId","step","add","equals","versions","drafts","autosave","maxPerDoc"],"mappings":"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"}
|