@xtr-dev/payload-automation 0.0.22 → 0.0.24

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 (59) hide show
  1. package/README.md +73 -2
  2. package/dist/collections/Workflow.js +187 -18
  3. package/dist/collections/Workflow.js.map +1 -1
  4. package/dist/collections/WorkflowRuns.js +14 -7
  5. package/dist/collections/WorkflowRuns.js.map +1 -1
  6. package/dist/components/StatusCell.d.ts +6 -0
  7. package/dist/components/StatusCell.js +75 -0
  8. package/dist/components/StatusCell.js.map +1 -0
  9. package/dist/components/WorkflowExecutionStatus.d.ts +6 -0
  10. package/dist/components/WorkflowExecutionStatus.js +287 -0
  11. package/dist/components/WorkflowExecutionStatus.js.map +1 -0
  12. package/dist/core/workflow-executor.d.ts +42 -2
  13. package/dist/core/workflow-executor.js +169 -14
  14. package/dist/core/workflow-executor.js.map +1 -1
  15. package/dist/exports/client.d.ts +2 -0
  16. package/dist/exports/client.js +4 -1
  17. package/dist/exports/client.js.map +1 -1
  18. package/dist/exports/helpers.d.ts +26 -0
  19. package/dist/exports/helpers.js +30 -0
  20. package/dist/exports/helpers.js.map +1 -0
  21. package/dist/index.d.ts +1 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/plugin/cron-scheduler.js +27 -27
  24. package/dist/plugin/cron-scheduler.js.map +1 -1
  25. package/dist/plugin/index.js +134 -43
  26. package/dist/plugin/index.js.map +1 -1
  27. package/dist/plugin/init-step-tasks.js +15 -1
  28. package/dist/plugin/init-step-tasks.js.map +1 -1
  29. package/dist/plugin/init-webhook.js +1 -1
  30. package/dist/plugin/init-webhook.js.map +1 -1
  31. package/dist/steps/create-document.js +1 -1
  32. package/dist/steps/create-document.js.map +1 -1
  33. package/dist/steps/delete-document.js +2 -2
  34. package/dist/steps/delete-document.js.map +1 -1
  35. package/dist/steps/http-request-handler.js +229 -10
  36. package/dist/steps/http-request-handler.js.map +1 -1
  37. package/dist/steps/http-request.d.ts +147 -4
  38. package/dist/steps/http-request.js +189 -3
  39. package/dist/steps/http-request.js.map +1 -1
  40. package/dist/steps/read-document.js +2 -2
  41. package/dist/steps/read-document.js.map +1 -1
  42. package/dist/steps/send-email.js +5 -5
  43. package/dist/steps/send-email.js.map +1 -1
  44. package/dist/steps/update-document.js +2 -2
  45. package/dist/steps/update-document.js.map +1 -1
  46. package/dist/test/create-document-step.test.js +378 -0
  47. package/dist/test/create-document-step.test.js.map +1 -0
  48. package/dist/test/http-request-step.test.js +361 -0
  49. package/dist/test/http-request-step.test.js.map +1 -0
  50. package/dist/test/workflow-executor.test.js +530 -0
  51. package/dist/test/workflow-executor.test.js.map +1 -0
  52. package/dist/utils/trigger-helpers.d.ts +46 -0
  53. package/dist/utils/trigger-helpers.js +100 -0
  54. package/dist/utils/trigger-helpers.js.map +1 -0
  55. package/dist/utils/trigger-presets.d.ts +60 -0
  56. package/dist/utils/trigger-presets.js +172 -0
  57. package/dist/utils/trigger-presets.js.map +1 -0
  58. package/package.json +9 -1
  59. package/dist/test/basic.test.d.ts +0 -1
@@ -15,14 +15,14 @@ export const DeleteDocumentStepTask = {
15
15
  name: 'id',
16
16
  type: 'text',
17
17
  admin: {
18
- description: 'The ID of a specific document to delete (leave empty to delete multiple)'
18
+ description: 'The ID of a specific document to delete. Use JSONPath (e.g., "$.trigger.doc.id"). Leave empty to delete multiple.'
19
19
  }
20
20
  },
21
21
  {
22
22
  name: 'where',
23
23
  type: 'json',
24
24
  admin: {
25
- description: 'Query conditions to find documents to delete (used when ID is not provided)'
25
+ description: 'Query conditions to find documents to delete when ID is not provided. Use JSONPath in values (e.g., {"author": "$.trigger.doc.author"})'
26
26
  }
27
27
  }
28
28
  ],
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/steps/delete-document.ts"],"sourcesContent":["import type { TaskConfig } from \"payload\"\n\nimport { deleteDocumentHandler } from \"./delete-document-handler.js\"\n\nexport const DeleteDocumentStepTask = {\n slug: 'delete-document',\n handler: deleteDocumentHandler,\n inputSchema: [\n {\n name: 'collectionSlug',\n type: 'text',\n admin: {\n description: 'The collection slug to delete from'\n },\n required: true\n },\n {\n name: 'id',\n type: 'text',\n admin: {\n description: 'The ID of a specific document to delete (leave empty to delete multiple)'\n }\n },\n {\n name: 'where',\n type: 'json',\n admin: {\n description: 'Query conditions to find documents to delete (used when ID is not provided)'\n }\n }\n ],\n outputSchema: [\n {\n name: 'doc',\n type: 'json',\n admin: {\n description: 'The deleted document(s)'\n }\n },\n {\n name: 'deletedCount',\n type: 'number',\n admin: {\n description: 'Number of documents deleted'\n }\n }\n ]\n} satisfies TaskConfig<'delete-document'>"],"names":["deleteDocumentHandler","DeleteDocumentStepTask","slug","handler","inputSchema","name","type","admin","description","required","outputSchema"],"mappings":"AAEA,SAASA,qBAAqB,QAAQ,+BAA8B;AAEpE,OAAO,MAAMC,yBAAyB;IACpCC,MAAM;IACNC,SAASH;IACTI,aAAa;QACX;YACEC,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLC,aAAa;YACf;YACAC,UAAU;QACZ;QACA;YACEJ,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLC,aAAa;YACf;QACF;QACA;YACEH,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLC,aAAa;YACf;QACF;KACD;IACDE,cAAc;QACZ;YACEL,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLC,aAAa;YACf;QACF;QACA;YACEH,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLC,aAAa;YACf;QACF;KACD;AACH,EAAyC"}
1
+ {"version":3,"sources":["../../src/steps/delete-document.ts"],"sourcesContent":["import type { TaskConfig } from \"payload\"\n\nimport { deleteDocumentHandler } from \"./delete-document-handler.js\"\n\nexport const DeleteDocumentStepTask = {\n slug: 'delete-document',\n handler: deleteDocumentHandler,\n inputSchema: [\n {\n name: 'collectionSlug',\n type: 'text',\n admin: {\n description: 'The collection slug to delete from'\n },\n required: true\n },\n {\n name: 'id',\n type: 'text',\n admin: {\n description: 'The ID of a specific document to delete. Use JSONPath (e.g., \"$.trigger.doc.id\"). Leave empty to delete multiple.'\n }\n },\n {\n name: 'where',\n type: 'json',\n admin: {\n description: 'Query conditions to find documents to delete when ID is not provided. Use JSONPath in values (e.g., {\"author\": \"$.trigger.doc.author\"})'\n }\n }\n ],\n outputSchema: [\n {\n name: 'doc',\n type: 'json',\n admin: {\n description: 'The deleted document(s)'\n }\n },\n {\n name: 'deletedCount',\n type: 'number',\n admin: {\n description: 'Number of documents deleted'\n }\n }\n ]\n} satisfies TaskConfig<'delete-document'>"],"names":["deleteDocumentHandler","DeleteDocumentStepTask","slug","handler","inputSchema","name","type","admin","description","required","outputSchema"],"mappings":"AAEA,SAASA,qBAAqB,QAAQ,+BAA8B;AAEpE,OAAO,MAAMC,yBAAyB;IACpCC,MAAM;IACNC,SAASH;IACTI,aAAa;QACX;YACEC,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLC,aAAa;YACf;YACAC,UAAU;QACZ;QACA;YACEJ,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLC,aAAa;YACf;QACF;QACA;YACEH,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLC,aAAa;YACf;QACF;KACD;IACDE,cAAc;QACZ;YACEL,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLC,aAAa;YACf;QACF;QACA;YACEH,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLC,aAAa;YACf;QACF;KACD;AACH,EAAyC"}
@@ -1,14 +1,233 @@
1
- export const httpStepHandler = async ({ input })=>{
2
- if (!input) {
3
- throw new Error('No input provided');
1
+ export const httpStepHandler = async ({ input, req })=>{
2
+ const startTime = Date.now() // Move startTime to outer scope
3
+ ;
4
+ try {
5
+ if (!input || !input.url) {
6
+ return {
7
+ output: {
8
+ status: 0,
9
+ statusText: 'Invalid Input',
10
+ headers: {},
11
+ body: '',
12
+ data: null,
13
+ duration: 0,
14
+ error: 'URL is required for HTTP request'
15
+ },
16
+ state: 'failed'
17
+ };
18
+ }
19
+ const typedInput = input;
20
+ // Validate URL
21
+ try {
22
+ new URL(typedInput.url);
23
+ } catch (error) {
24
+ return {
25
+ output: {
26
+ status: 0,
27
+ statusText: 'Invalid URL',
28
+ headers: {},
29
+ body: '',
30
+ data: null,
31
+ duration: 0,
32
+ error: `Invalid URL: ${typedInput.url}`
33
+ },
34
+ state: 'failed'
35
+ };
36
+ }
37
+ // Prepare request options
38
+ const method = (typedInput.method || 'GET').toUpperCase();
39
+ const timeout = typedInput.timeout || 30000;
40
+ const headers = {
41
+ 'User-Agent': 'PayloadCMS-Automation/1.0',
42
+ ...typedInput.headers
43
+ };
44
+ // Handle authentication
45
+ if (typedInput.authentication) {
46
+ switch(typedInput.authentication.type){
47
+ case 'bearer':
48
+ if (typedInput.authentication.token) {
49
+ headers['Authorization'] = `Bearer ${typedInput.authentication.token}`;
50
+ }
51
+ break;
52
+ case 'basic':
53
+ if (typedInput.authentication.username && typedInput.authentication.password) {
54
+ const credentials = btoa(`${typedInput.authentication.username}:${typedInput.authentication.password}`);
55
+ headers['Authorization'] = `Basic ${credentials}`;
56
+ }
57
+ break;
58
+ case 'apikey':
59
+ if (typedInput.authentication.headerName && typedInput.authentication.headerValue) {
60
+ headers[typedInput.authentication.headerName] = typedInput.authentication.headerValue;
61
+ }
62
+ break;
63
+ }
64
+ }
65
+ // Prepare request body
66
+ let requestBody;
67
+ if ([
68
+ 'POST',
69
+ 'PUT',
70
+ 'PATCH'
71
+ ].includes(method) && typedInput.body) {
72
+ if (typeof typedInput.body === 'string') {
73
+ requestBody = typedInput.body;
74
+ } else {
75
+ requestBody = JSON.stringify(typedInput.body);
76
+ if (!headers['Content-Type']) {
77
+ headers['Content-Type'] = 'application/json';
78
+ }
79
+ }
80
+ }
81
+ // Create abort controller for timeout
82
+ const abortController = new AbortController();
83
+ const timeoutId = setTimeout(()=>abortController.abort(), timeout);
84
+ // Retry logic
85
+ const maxRetries = Math.min(Math.max(typedInput.retries || 0, 0), 5);
86
+ const retryDelay = Math.max(typedInput.retryDelay || 1000, 100);
87
+ let lastError = null;
88
+ for(let attempt = 0; attempt <= maxRetries; attempt++){
89
+ try {
90
+ // Add delay for retry attempts
91
+ if (attempt > 0) {
92
+ req?.payload?.logger?.info({
93
+ attempt: attempt + 1,
94
+ maxRetries: maxRetries + 1,
95
+ url: typedInput.url,
96
+ delay: retryDelay
97
+ }, 'HTTP request retry attempt');
98
+ await new Promise((resolve)=>setTimeout(resolve, retryDelay));
99
+ }
100
+ const response = await fetch(typedInput.url, {
101
+ method,
102
+ headers,
103
+ body: requestBody,
104
+ signal: abortController.signal
105
+ });
106
+ clearTimeout(timeoutId);
107
+ const duration = Date.now() - startTime;
108
+ // Parse response
109
+ const responseText = await response.text();
110
+ let parsedData = null;
111
+ try {
112
+ const contentType = response.headers.get('content-type') || '';
113
+ if (contentType.includes('application/json') || contentType.includes('text/json')) {
114
+ parsedData = JSON.parse(responseText);
115
+ }
116
+ } catch (parseError) {
117
+ // Not JSON, that's fine
118
+ }
119
+ // Convert headers to plain object
120
+ const responseHeaders = {};
121
+ response.headers.forEach((value, key)=>{
122
+ responseHeaders[key] = value;
123
+ });
124
+ const output = {
125
+ status: response.status,
126
+ statusText: response.statusText,
127
+ headers: responseHeaders,
128
+ body: responseText,
129
+ data: parsedData,
130
+ duration
131
+ };
132
+ req?.payload?.logger?.info({
133
+ url: typedInput.url,
134
+ method,
135
+ status: response.status,
136
+ duration,
137
+ attempt: attempt + 1
138
+ }, 'HTTP request completed');
139
+ return {
140
+ output,
141
+ // Always return 'succeeded' for completed HTTP requests, even with error status codes (4xx/5xx).
142
+ // This preserves error information in the output for workflow conditional logic.
143
+ // Only network errors, timeouts, and connection failures should result in 'failed' state.
144
+ // This design allows workflows to handle HTTP errors gracefully rather than failing completely.
145
+ state: 'succeeded'
146
+ };
147
+ } catch (error) {
148
+ lastError = error instanceof Error ? error : new Error('Unknown error');
149
+ // Handle specific error types
150
+ if (error instanceof Error) {
151
+ if (error.name === 'AbortError') {
152
+ lastError = new Error(`Request timeout after ${timeout}ms`);
153
+ } else if (error.message.includes('fetch')) {
154
+ lastError = new Error(`Network error: ${error.message}`);
155
+ }
156
+ }
157
+ req?.payload?.logger?.warn({
158
+ url: typedInput.url,
159
+ method,
160
+ attempt: attempt + 1,
161
+ maxRetries: maxRetries + 1,
162
+ error: lastError.message
163
+ }, 'HTTP request attempt failed');
164
+ // Don't retry on certain errors
165
+ if (lastError.message.includes('Invalid URL') || lastError.message.includes('TypeError') || attempt >= maxRetries) {
166
+ break;
167
+ }
168
+ }
169
+ }
170
+ clearTimeout(timeoutId);
171
+ const duration = Date.now() - startTime;
172
+ // All retries exhausted
173
+ const finalError = lastError || new Error('HTTP request failed');
174
+ req?.payload?.logger?.error({
175
+ url: typedInput.url,
176
+ method,
177
+ totalAttempts: maxRetries + 1,
178
+ duration,
179
+ error: finalError.message
180
+ }, 'HTTP request failed after all retries');
181
+ // Include detailed error information in the output
182
+ // Even though PayloadCMS will discard this for failed tasks,
183
+ // we include it here for potential future PayloadCMS improvements
184
+ const errorDetails = {
185
+ errorType: finalError.message.includes('timeout') ? 'timeout' : finalError.message.includes('ENOTFOUND') ? 'dns' : finalError.message.includes('ECONNREFUSED') ? 'connection' : 'network',
186
+ duration,
187
+ attempts: maxRetries + 1,
188
+ finalError: finalError.message,
189
+ context: {
190
+ url: typedInput.url,
191
+ method,
192
+ timeout: typedInput.timeout,
193
+ headers: typedInput.headers
194
+ }
195
+ };
196
+ // Return comprehensive output (PayloadCMS will discard it for failed state, but we try anyway)
197
+ return {
198
+ output: {
199
+ status: 0,
200
+ statusText: 'Request Failed',
201
+ headers: {},
202
+ body: '',
203
+ data: null,
204
+ duration,
205
+ error: finalError.message,
206
+ errorDetails
207
+ },
208
+ state: 'failed'
209
+ };
210
+ } catch (unexpectedError) {
211
+ // Handle any unexpected errors that weren't caught above
212
+ const error = unexpectedError instanceof Error ? unexpectedError : new Error('Unexpected error');
213
+ req?.payload?.logger?.error({
214
+ error: error.message,
215
+ stack: error.stack,
216
+ input: input?.url || 'unknown'
217
+ }, 'Unexpected error in HTTP request handler');
218
+ return {
219
+ output: {
220
+ status: 0,
221
+ statusText: 'Handler Error',
222
+ headers: {},
223
+ body: '',
224
+ data: null,
225
+ duration: Date.now() - startTime,
226
+ error: `HTTP request handler error: ${error.message}`
227
+ },
228
+ state: 'failed'
229
+ };
4
230
  }
5
- const response = await fetch(input.url);
6
- return {
7
- output: {
8
- response: await response.text()
9
- },
10
- state: response.ok ? 'succeeded' : undefined
11
- };
12
231
  };
13
232
 
14
233
  //# sourceMappingURL=http-request-handler.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/steps/http-request-handler.ts"],"sourcesContent":["import type {TaskHandler} from \"payload\"\n\nexport const httpStepHandler: TaskHandler<'http-request-step'> = async ({input}) => {\n if (!input) {\n throw new Error('No input provided')\n }\n const response = await fetch(input.url)\n return {\n output: {\n response: await response.text()\n },\n state: response.ok ? 'succeeded' : undefined\n }\n}\n"],"names":["httpStepHandler","input","Error","response","fetch","url","output","text","state","ok","undefined"],"mappings":"AAEA,OAAO,MAAMA,kBAAoD,OAAO,EAACC,KAAK,EAAC;IAC7E,IAAI,CAACA,OAAO;QACV,MAAM,IAAIC,MAAM;IAClB;IACA,MAAMC,WAAW,MAAMC,MAAMH,MAAMI,GAAG;IACtC,OAAO;QACLC,QAAQ;YACNH,UAAU,MAAMA,SAASI,IAAI;QAC/B;QACAC,OAAOL,SAASM,EAAE,GAAG,cAAcC;IACrC;AACF,EAAC"}
1
+ {"version":3,"sources":["../../src/steps/http-request-handler.ts"],"sourcesContent":["import type {TaskHandler} from \"payload\"\n\ninterface HttpRequestInput {\n url: string\n method?: string\n headers?: Record<string, string>\n body?: any\n timeout?: number\n authentication?: {\n type?: 'none' | 'bearer' | 'basic' | 'apikey'\n token?: string\n username?: string\n password?: string\n headerName?: string\n headerValue?: string\n }\n retries?: number\n retryDelay?: number\n}\n\nexport const httpStepHandler: TaskHandler<'http-request-step'> = async ({input, req}) => {\n const startTime = Date.now() // Move startTime to outer scope\n \n try {\n if (!input || !input.url) {\n return {\n output: {\n status: 0,\n statusText: 'Invalid Input',\n headers: {},\n body: '',\n data: null,\n duration: 0,\n error: 'URL is required for HTTP request'\n },\n state: 'failed'\n }\n }\n\n const typedInput = input as HttpRequestInput\n \n // Validate URL\n try {\n new URL(typedInput.url)\n } catch (error) {\n return {\n output: {\n status: 0,\n statusText: 'Invalid URL',\n headers: {},\n body: '',\n data: null,\n duration: 0,\n error: `Invalid URL: ${typedInput.url}`\n },\n state: 'failed'\n }\n }\n\n // Prepare request options\n const method = (typedInput.method || 'GET').toUpperCase()\n const timeout = typedInput.timeout || 30000\n const headers: Record<string, string> = {\n 'User-Agent': 'PayloadCMS-Automation/1.0',\n ...typedInput.headers\n }\n\n // Handle authentication\n if (typedInput.authentication) {\n switch (typedInput.authentication.type) {\n case 'bearer':\n if (typedInput.authentication.token) {\n headers['Authorization'] = `Bearer ${typedInput.authentication.token}`\n }\n break\n case 'basic':\n if (typedInput.authentication.username && typedInput.authentication.password) {\n const credentials = btoa(`${typedInput.authentication.username}:${typedInput.authentication.password}`)\n headers['Authorization'] = `Basic ${credentials}`\n }\n break\n case 'apikey':\n if (typedInput.authentication.headerName && typedInput.authentication.headerValue) {\n headers[typedInput.authentication.headerName] = typedInput.authentication.headerValue\n }\n break\n }\n }\n\n // Prepare request body\n let requestBody: string | undefined\n if (['POST', 'PUT', 'PATCH'].includes(method) && typedInput.body) {\n if (typeof typedInput.body === 'string') {\n requestBody = typedInput.body\n } else {\n requestBody = JSON.stringify(typedInput.body)\n if (!headers['Content-Type']) {\n headers['Content-Type'] = 'application/json'\n }\n }\n }\n\n // Create abort controller for timeout\n const abortController = new AbortController()\n const timeoutId = setTimeout(() => abortController.abort(), timeout)\n\n // Retry logic\n const maxRetries = Math.min(Math.max(typedInput.retries || 0, 0), 5)\n const retryDelay = Math.max(typedInput.retryDelay || 1000, 100)\n\n let lastError: Error | null = null\n \n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n // Add delay for retry attempts\n if (attempt > 0) {\n req?.payload?.logger?.info({\n attempt: attempt + 1,\n maxRetries: maxRetries + 1,\n url: typedInput.url,\n delay: retryDelay\n }, 'HTTP request retry attempt')\n \n await new Promise(resolve => setTimeout(resolve, retryDelay))\n }\n\n const response = await fetch(typedInput.url, {\n method,\n headers,\n body: requestBody,\n signal: abortController.signal\n })\n\n clearTimeout(timeoutId)\n const duration = Date.now() - startTime\n\n // Parse response\n const responseText = await response.text()\n let parsedData: any = null\n\n try {\n const contentType = response.headers.get('content-type') || ''\n if (contentType.includes('application/json') || contentType.includes('text/json')) {\n parsedData = JSON.parse(responseText)\n }\n } catch (parseError) {\n // Not JSON, that's fine\n }\n\n // Convert headers to plain object\n const responseHeaders: Record<string, string> = {}\n response.headers.forEach((value, key) => {\n responseHeaders[key] = value\n })\n\n const output = {\n status: response.status,\n statusText: response.statusText,\n headers: responseHeaders,\n body: responseText,\n data: parsedData,\n duration\n }\n\n req?.payload?.logger?.info({\n url: typedInput.url,\n method,\n status: response.status,\n duration,\n attempt: attempt + 1\n }, 'HTTP request completed')\n\n return {\n output,\n // Always return 'succeeded' for completed HTTP requests, even with error status codes (4xx/5xx).\n // This preserves error information in the output for workflow conditional logic.\n // Only network errors, timeouts, and connection failures should result in 'failed' state.\n // This design allows workflows to handle HTTP errors gracefully rather than failing completely.\n state: 'succeeded'\n }\n\n } catch (error) {\n lastError = error instanceof Error ? error : new Error('Unknown error')\n \n // Handle specific error types\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n lastError = new Error(`Request timeout after ${timeout}ms`)\n } else if (error.message.includes('fetch')) {\n lastError = new Error(`Network error: ${error.message}`)\n }\n }\n\n req?.payload?.logger?.warn({\n url: typedInput.url,\n method,\n attempt: attempt + 1,\n maxRetries: maxRetries + 1,\n error: lastError.message\n }, 'HTTP request attempt failed')\n\n // Don't retry on certain errors\n if (lastError.message.includes('Invalid URL') || \n lastError.message.includes('TypeError') ||\n attempt >= maxRetries) {\n break\n }\n }\n }\n\n clearTimeout(timeoutId)\n const duration = Date.now() - startTime\n\n // All retries exhausted\n const finalError = lastError || new Error('HTTP request failed')\n \n req?.payload?.logger?.error({\n url: typedInput.url,\n method,\n totalAttempts: maxRetries + 1,\n duration,\n error: finalError.message\n }, 'HTTP request failed after all retries')\n\n // Include detailed error information in the output\n // Even though PayloadCMS will discard this for failed tasks,\n // we include it here for potential future PayloadCMS improvements\n const errorDetails = {\n errorType: finalError.message.includes('timeout') ? 'timeout' : \n finalError.message.includes('ENOTFOUND') ? 'dns' :\n finalError.message.includes('ECONNREFUSED') ? 'connection' : 'network',\n duration,\n attempts: maxRetries + 1,\n finalError: finalError.message,\n context: {\n url: typedInput.url,\n method,\n timeout: typedInput.timeout,\n headers: typedInput.headers\n }\n }\n\n // Return comprehensive output (PayloadCMS will discard it for failed state, but we try anyway)\n return {\n output: {\n status: 0,\n statusText: 'Request Failed',\n headers: {},\n body: '',\n data: null,\n duration,\n error: finalError.message,\n errorDetails // Include detailed error info (will be discarded by PayloadCMS)\n },\n state: 'failed'\n }\n } catch (unexpectedError) {\n // Handle any unexpected errors that weren't caught above\n const error = unexpectedError instanceof Error ? unexpectedError : new Error('Unexpected error')\n \n req?.payload?.logger?.error({\n error: error.message,\n stack: error.stack,\n input: (input as any)?.url || 'unknown'\n }, 'Unexpected error in HTTP request handler')\n\n return {\n output: {\n status: 0,\n statusText: 'Handler Error',\n headers: {},\n body: '',\n data: null,\n duration: Date.now() - startTime,\n error: `HTTP request handler error: ${error.message}`\n },\n state: 'failed'\n }\n }\n}\n"],"names":["httpStepHandler","input","req","startTime","Date","now","url","output","status","statusText","headers","body","data","duration","error","state","typedInput","URL","method","toUpperCase","timeout","authentication","type","token","username","password","credentials","btoa","headerName","headerValue","requestBody","includes","JSON","stringify","abortController","AbortController","timeoutId","setTimeout","abort","maxRetries","Math","min","max","retries","retryDelay","lastError","attempt","payload","logger","info","delay","Promise","resolve","response","fetch","signal","clearTimeout","responseText","text","parsedData","contentType","get","parse","parseError","responseHeaders","forEach","value","key","Error","name","message","warn","finalError","totalAttempts","errorDetails","errorType","attempts","context","unexpectedError","stack"],"mappings":"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"}
@@ -1,12 +1,155 @@
1
1
  export declare const HttpRequestStepTask: {
2
2
  slug: "http-request-step";
3
3
  handler: import("payload").TaskHandler<"http-request-step">;
4
- inputSchema: {
4
+ inputSchema: ({
5
5
  name: string;
6
6
  type: "text";
7
- }[];
8
- outputSchema: {
7
+ admin: {
8
+ description: string;
9
+ condition?: undefined;
10
+ };
11
+ required: true;
12
+ options?: undefined;
13
+ defaultValue?: undefined;
14
+ fields?: undefined;
15
+ min?: undefined;
16
+ max?: undefined;
17
+ } | {
18
+ name: string;
19
+ type: "select";
20
+ options: {
21
+ label: string;
22
+ value: string;
23
+ }[];
24
+ defaultValue: string;
25
+ admin: {
26
+ description: string;
27
+ condition?: undefined;
28
+ };
29
+ required?: undefined;
30
+ fields?: undefined;
31
+ min?: undefined;
32
+ max?: undefined;
33
+ } | {
34
+ name: string;
35
+ type: "json";
36
+ admin: {
37
+ description: string;
38
+ condition?: undefined;
39
+ };
40
+ required?: undefined;
41
+ options?: undefined;
42
+ defaultValue?: undefined;
43
+ fields?: undefined;
44
+ min?: undefined;
45
+ max?: undefined;
46
+ } | {
47
+ name: string;
48
+ type: "json";
49
+ admin: {
50
+ condition: (_: Partial<any>, siblingData: Partial<any>) => boolean;
51
+ description: string;
52
+ };
53
+ required?: undefined;
54
+ options?: undefined;
55
+ defaultValue?: undefined;
56
+ fields?: undefined;
57
+ min?: undefined;
58
+ max?: undefined;
59
+ } | {
60
+ name: string;
61
+ type: "number";
62
+ defaultValue: number;
63
+ admin: {
64
+ description: string;
65
+ condition?: undefined;
66
+ };
67
+ required?: undefined;
68
+ options?: undefined;
69
+ fields?: undefined;
70
+ min?: undefined;
71
+ max?: undefined;
72
+ } | {
73
+ name: string;
74
+ type: "group";
75
+ fields: ({
76
+ name: string;
77
+ type: "select";
78
+ options: {
79
+ label: string;
80
+ value: string;
81
+ }[];
82
+ defaultValue: string;
83
+ admin: {
84
+ description: string;
85
+ condition?: undefined;
86
+ };
87
+ } | {
88
+ name: string;
89
+ type: "text";
90
+ admin: {
91
+ condition: (_: Partial<any>, siblingData: Partial<any>) => boolean;
92
+ description: string;
93
+ };
94
+ options?: undefined;
95
+ defaultValue?: undefined;
96
+ })[];
97
+ admin?: undefined;
98
+ required?: undefined;
99
+ options?: undefined;
100
+ defaultValue?: undefined;
101
+ min?: undefined;
102
+ max?: undefined;
103
+ } | {
104
+ name: string;
105
+ type: "number";
106
+ defaultValue: number;
107
+ min: number;
108
+ max: number;
109
+ admin: {
110
+ description: string;
111
+ condition?: undefined;
112
+ };
113
+ required?: undefined;
114
+ options?: undefined;
115
+ fields?: undefined;
116
+ } | {
117
+ name: string;
118
+ type: "number";
119
+ defaultValue: number;
120
+ admin: {
121
+ condition: (_: Partial<any>, siblingData: Partial<any>) => boolean;
122
+ description: string;
123
+ };
124
+ required?: undefined;
125
+ options?: undefined;
126
+ fields?: undefined;
127
+ min?: undefined;
128
+ max?: undefined;
129
+ })[];
130
+ outputSchema: ({
131
+ name: string;
132
+ type: "number";
133
+ admin: {
134
+ description: string;
135
+ };
136
+ } | {
137
+ name: string;
138
+ type: "text";
139
+ admin: {
140
+ description: string;
141
+ };
142
+ } | {
143
+ name: string;
144
+ type: "json";
145
+ admin: {
146
+ description: string;
147
+ };
148
+ } | {
9
149
  name: string;
10
150
  type: "textarea";
11
- }[];
151
+ admin: {
152
+ description: string;
153
+ };
154
+ })[];
12
155
  };
@@ -5,13 +5,199 @@ export const HttpRequestStepTask = {
5
5
  inputSchema: [
6
6
  {
7
7
  name: 'url',
8
- type: 'text'
8
+ type: 'text',
9
+ admin: {
10
+ description: 'The URL to make the HTTP request to'
11
+ },
12
+ required: true
13
+ },
14
+ {
15
+ name: 'method',
16
+ type: 'select',
17
+ options: [
18
+ {
19
+ label: 'GET',
20
+ value: 'GET'
21
+ },
22
+ {
23
+ label: 'POST',
24
+ value: 'POST'
25
+ },
26
+ {
27
+ label: 'PUT',
28
+ value: 'PUT'
29
+ },
30
+ {
31
+ label: 'DELETE',
32
+ value: 'DELETE'
33
+ },
34
+ {
35
+ label: 'PATCH',
36
+ value: 'PATCH'
37
+ }
38
+ ],
39
+ defaultValue: 'GET',
40
+ admin: {
41
+ description: 'HTTP method to use'
42
+ }
43
+ },
44
+ {
45
+ name: 'headers',
46
+ type: 'json',
47
+ admin: {
48
+ description: 'HTTP headers as JSON object (e.g., {"Content-Type": "application/json"})'
49
+ }
50
+ },
51
+ {
52
+ name: 'body',
53
+ type: 'json',
54
+ admin: {
55
+ condition: (_, siblingData)=>siblingData?.method !== 'GET' && siblingData?.method !== 'DELETE',
56
+ description: 'Request body data. Use JSONPath to reference values (e.g., {"postId": "$.trigger.doc.id", "title": "$.trigger.doc.title"})'
57
+ }
58
+ },
59
+ {
60
+ name: 'timeout',
61
+ type: 'number',
62
+ defaultValue: 30000,
63
+ admin: {
64
+ description: 'Request timeout in milliseconds (default: 30000)'
65
+ }
66
+ },
67
+ {
68
+ name: 'authentication',
69
+ type: 'group',
70
+ fields: [
71
+ {
72
+ name: 'type',
73
+ type: 'select',
74
+ options: [
75
+ {
76
+ label: 'None',
77
+ value: 'none'
78
+ },
79
+ {
80
+ label: 'Bearer Token',
81
+ value: 'bearer'
82
+ },
83
+ {
84
+ label: 'Basic Auth',
85
+ value: 'basic'
86
+ },
87
+ {
88
+ label: 'API Key Header',
89
+ value: 'apikey'
90
+ }
91
+ ],
92
+ defaultValue: 'none',
93
+ admin: {
94
+ description: 'Authentication method'
95
+ }
96
+ },
97
+ {
98
+ name: 'token',
99
+ type: 'text',
100
+ admin: {
101
+ condition: (_, siblingData)=>siblingData?.type === 'bearer',
102
+ description: 'Bearer token value'
103
+ }
104
+ },
105
+ {
106
+ name: 'username',
107
+ type: 'text',
108
+ admin: {
109
+ condition: (_, siblingData)=>siblingData?.type === 'basic',
110
+ description: 'Basic auth username'
111
+ }
112
+ },
113
+ {
114
+ name: 'password',
115
+ type: 'text',
116
+ admin: {
117
+ condition: (_, siblingData)=>siblingData?.type === 'basic',
118
+ description: 'Basic auth password'
119
+ }
120
+ },
121
+ {
122
+ name: 'headerName',
123
+ type: 'text',
124
+ admin: {
125
+ condition: (_, siblingData)=>siblingData?.type === 'apikey',
126
+ description: 'API key header name (e.g., "X-API-Key")'
127
+ }
128
+ },
129
+ {
130
+ name: 'headerValue',
131
+ type: 'text',
132
+ admin: {
133
+ condition: (_, siblingData)=>siblingData?.type === 'apikey',
134
+ description: 'API key value'
135
+ }
136
+ }
137
+ ]
138
+ },
139
+ {
140
+ name: 'retries',
141
+ type: 'number',
142
+ defaultValue: 0,
143
+ min: 0,
144
+ max: 5,
145
+ admin: {
146
+ description: 'Number of retry attempts on failure (max: 5)'
147
+ }
148
+ },
149
+ {
150
+ name: 'retryDelay',
151
+ type: 'number',
152
+ defaultValue: 1000,
153
+ admin: {
154
+ condition: (_, siblingData)=>(siblingData?.retries || 0) > 0,
155
+ description: 'Delay between retries in milliseconds'
156
+ }
9
157
  }
10
158
  ],
11
159
  outputSchema: [
12
160
  {
13
- name: 'response',
14
- type: 'textarea'
161
+ name: 'status',
162
+ type: 'number',
163
+ admin: {
164
+ description: 'HTTP status code'
165
+ }
166
+ },
167
+ {
168
+ name: 'statusText',
169
+ type: 'text',
170
+ admin: {
171
+ description: 'HTTP status text'
172
+ }
173
+ },
174
+ {
175
+ name: 'headers',
176
+ type: 'json',
177
+ admin: {
178
+ description: 'Response headers'
179
+ }
180
+ },
181
+ {
182
+ name: 'body',
183
+ type: 'textarea',
184
+ admin: {
185
+ description: 'Response body'
186
+ }
187
+ },
188
+ {
189
+ name: 'data',
190
+ type: 'json',
191
+ admin: {
192
+ description: 'Parsed response data (if JSON)'
193
+ }
194
+ },
195
+ {
196
+ name: 'duration',
197
+ type: 'number',
198
+ admin: {
199
+ description: 'Request duration in milliseconds'
200
+ }
15
201
  }
16
202
  ]
17
203
  };