@xtr-dev/payload-automation 0.0.52 → 0.0.53

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 CHANGED
@@ -8,15 +8,13 @@ A workflow automation plugin for PayloadCMS 3.x. Build visual workflows triggere
8
8
 
9
9
  ## Features
10
10
 
11
- - **Visual Workflow Builder** - Drag-and-drop workflow editor in PayloadCMS admin
12
- - **Workflow Visualizer** - React component for displaying workflows with real-time execution status
13
- - **Collection Triggers** - Run workflows when documents are created, updated, or deleted
14
- - **Webhook Triggers** - Trigger workflows via HTTP endpoints
15
- - **Step Dependencies** - Define execution order with parallel and sequential steps
16
- - **Execution Tracking** - Full history with step-by-step results, duration, and logs
17
- - **Built-in Steps** - HTTP requests, document CRUD, email sending
18
- - **Custom Steps** - Create your own step types with the step factory
19
- - **JSONata Expressions** - Powerful data transformation between steps
11
+ - 🔄 Visual workflow builder in PayloadCMS admin
12
+ - Run workflows when documents are created/updated/deleted
13
+ - 🎯 Trigger workflows via webhooks
14
+ - 📊 Track workflow execution history
15
+ - 🔧 HTTP requests, document operations, email sending
16
+ - 🔗 Use data from previous steps in templates
17
+ - ⚙️ Step dependencies with parallel and sequential execution
20
18
 
21
19
  ## Installation
22
20
 
@@ -30,20 +28,20 @@ npm install @xtr-dev/payload-automation
30
28
 
31
29
  ```typescript
32
30
  import { buildConfig } from 'payload'
33
- import { workflowsPlugin } from '@xtr-dev/payload-automation'
31
+ import { workflowsPlugin } from '@xtr-dev/payload-automation/server'
34
32
 
35
33
  export default buildConfig({
34
+ // ... your config
36
35
  plugins: [
37
36
  workflowsPlugin({
38
- enabled: true,
39
37
  collectionTriggers: {
40
- orders: true, // Enable all hooks for orders
41
- users: {
42
- afterChange: true, // Only specific hooks
38
+ posts: true, // Enable all CRUD triggers for posts
39
+ users: {
40
+ create: true, // Only enable create trigger for users
41
+ update: true
43
42
  }
44
43
  },
45
- // Register custom steps
46
- steps: [myCustomStep],
44
+ enabled: true,
47
45
  }),
48
46
  ],
49
47
  })
@@ -52,10 +50,10 @@ export default buildConfig({
52
50
  ## Imports
53
51
 
54
52
  ```typescript
55
- // Main plugin
56
- import { workflowsPlugin } from '@xtr-dev/payload-automation'
53
+ // Server plugin
54
+ import { workflowsPlugin } from '@xtr-dev/payload-automation/server'
57
55
 
58
- // Client components
56
+ // Client components
59
57
  import { StatusCell, ErrorDisplay } from '@xtr-dev/payload-automation/client'
60
58
 
61
59
  // Types
@@ -89,13 +87,80 @@ const exampleWorkflows: SeedWorkflow[] = [
89
87
  name: 'Send Email',
90
88
  type: 'send-email',
91
89
  input: {
92
- to: '$.trigger.doc.email',
90
+ to: '{{trigger.doc.email}}',
93
91
  subject: 'Welcome!',
94
92
  text: 'Thanks for joining us!',
95
93
  },
96
94
  },
97
95
  ],
98
96
  },
97
+ {
98
+ slug: 'example-order-processing',
99
+ name: 'Example: Order Processing Pipeline',
100
+ description: 'Process order with validation, inventory check, and notifications',
101
+ triggers: [
102
+ {
103
+ type: 'collection-hook',
104
+ parameters: {
105
+ collectionSlug: 'orders',
106
+ hook: 'afterChange',
107
+ },
108
+ condition: 'trigger.operation = "create"',
109
+ },
110
+ ],
111
+ steps: [
112
+ {
113
+ name: 'Validate Order',
114
+ type: 'http-request-step',
115
+ input: {
116
+ url: 'https://api.example.com/validate',
117
+ method: 'POST',
118
+ body: {
119
+ orderId: '{{trigger.doc.id}}',
120
+ items: '{{trigger.doc.items}}',
121
+ },
122
+ },
123
+ },
124
+ {
125
+ name: 'Check Inventory',
126
+ type: 'http-request-step',
127
+ input: {
128
+ url: 'https://api.example.com/inventory/check',
129
+ method: 'POST',
130
+ body: {
131
+ items: '{{trigger.doc.items}}',
132
+ },
133
+ },
134
+ // This step runs only after Validate Order succeeds
135
+ dependencies: ['Validate Order'],
136
+ },
137
+ {
138
+ name: 'Create Shipment',
139
+ type: 'create-document',
140
+ input: {
141
+ collection: 'shipments',
142
+ data: {
143
+ orderId: '{{trigger.doc.id}}',
144
+ status: 'pending',
145
+ items: '{{trigger.doc.items}}',
146
+ },
147
+ },
148
+ // This step waits for both validation and inventory check
149
+ dependencies: ['Validate Order', 'Check Inventory'],
150
+ },
151
+ {
152
+ name: 'Send Confirmation Email',
153
+ type: 'send-email',
154
+ input: {
155
+ to: '{{trigger.doc.customer.email}}',
156
+ subject: 'Order Confirmed',
157
+ text: 'Your order {{trigger.doc.id}} has been confirmed!',
158
+ },
159
+ // Only send email after shipment is created
160
+ dependencies: ['Create Shipment'],
161
+ },
162
+ ],
163
+ },
99
164
  ]
100
165
 
101
166
  workflowsPlugin({
@@ -114,8 +179,73 @@ Seeded workflows:
114
179
 
115
180
  See [docs/SEEDING_WORKFLOWS.md](./docs/SEEDING_WORKFLOWS.md) for detailed documentation.
116
181
 
182
+ ## Step Dependencies
183
+
184
+ Steps can declare dependencies on other steps to control execution order. The workflow executor uses topological sorting to determine the optimal execution order.
185
+
186
+ ### How Dependencies Work
187
+
188
+ - **Parallel Execution**: Steps without dependencies run in parallel
189
+ - **Sequential Execution**: Steps with dependencies wait for their dependencies to complete successfully
190
+ - **Multiple Dependencies**: A step can depend on multiple other steps (all must succeed)
191
+ - **Failure Handling**: If a dependency fails, the dependent step is skipped
192
+
193
+ ### Example: Parallel and Sequential Steps
194
+
195
+ ```typescript
196
+ steps: [
197
+ {
198
+ name: 'Fetch User Data',
199
+ type: 'http-request-step',
200
+ // No dependencies - runs immediately
201
+ },
202
+ {
203
+ name: 'Fetch Order Data',
204
+ type: 'http-request-step',
205
+ // No dependencies - runs in parallel with Fetch User Data
206
+ },
207
+ {
208
+ name: 'Generate Report',
209
+ type: 'http-request-step',
210
+ dependencies: ['Fetch User Data', 'Fetch Order Data'],
211
+ // Waits for both API calls to complete
212
+ input: {
213
+ url: 'https://api.example.com/reports',
214
+ body: {
215
+ user: '{{steps.FetchUserData.output.user}}',
216
+ orders: '{{steps.FetchOrderData.output.orders}}',
217
+ },
218
+ },
219
+ },
220
+ ]
221
+ ```
222
+
223
+ ### Accessing Step Output
224
+
225
+ Use `{{steps.<stepName>.output.<field>}}` to reference data from completed steps:
226
+
227
+ ```typescript
228
+ {
229
+ name: 'Process Result',
230
+ type: 'create-document',
231
+ dependencies: ['API Call'],
232
+ input: {
233
+ collection: 'results',
234
+ data: {
235
+ // Access output from the "API Call" step
236
+ apiResponse: '{{steps.APICall.output.data}}',
237
+ status: '{{steps.APICall.output.status}}',
238
+ },
239
+ },
240
+ }
241
+ ```
242
+
117
243
  ## Step Types
118
244
 
245
+ > **Note:** Steps are just regular [PayloadCMS tasks](https://payloadcms.com/docs/jobs/overview). This plugin uses Payload's built-in job queue system, so you can leverage all of Payload's task features including retries, scheduling, and monitoring. Any `TaskConfig` you create is a valid step.
246
+
247
+ The plugin comes with a few built-in step types found below.
248
+
119
249
  ### HTTP Request
120
250
  Call external APIs with full configuration:
121
251
 
@@ -156,12 +286,13 @@ Send notifications via PayloadCMS email adapter:
156
286
 
157
287
  ## Custom Steps
158
288
 
159
- Create reusable step types with the `createStep` factory:
289
+ Steps are standard PayloadCMS tasks, so creating custom steps is just defining a `TaskConfig`. No proprietary APIs to learn:
160
290
 
161
291
  ```typescript
162
- import { createStep } from '@xtr-dev/payload-automation/steps'
292
+ import type { TaskConfig } from 'payload'
163
293
 
164
- export const SlackNotificationStep = createStep({
294
+ // Define the step configuration
295
+ export const SlackNotificationStep: TaskConfig<'slack-notification'> = {
165
296
  slug: 'slack-notification',
166
297
  label: 'Send Slack Message',
167
298
 
@@ -175,42 +306,40 @@ export const SlackNotificationStep = createStep({
175
306
  { name: 'timestamp', type: 'text' },
176
307
  ],
177
308
 
178
- visual: {
179
- icon: '💬',
180
- color: '#4A154B',
181
- },
182
-
183
- validate: (input) => {
184
- if (!input.channel?.startsWith('#')) {
185
- throw new Error('Channel must start with #')
186
- }
187
- },
188
-
189
- execute: async (input, req) => {
190
- const response = await fetch('https://slack.com/api/chat.postMessage', {
191
- method: 'POST',
192
- headers: {
193
- 'Authorization': `Bearer ${process.env.SLACK_TOKEN}`,
194
- 'Content-Type': 'application/json',
195
- },
196
- body: JSON.stringify({
197
- channel: input.channel,
198
- text: input.message,
199
- }),
200
- })
201
-
202
- const data = await response.json()
203
- return {
204
- messageId: data.message.ts,
205
- timestamp: new Date().toISOString(),
309
+ handler: async ({ input, req }) => {
310
+ try {
311
+ const response = await fetch('https://slack.com/api/chat.postMessage', {
312
+ method: 'POST',
313
+ headers: {
314
+ 'Authorization': `Bearer ${process.env.SLACK_TOKEN}`,
315
+ 'Content-Type': 'application/json',
316
+ },
317
+ body: JSON.stringify({
318
+ channel: input.channel,
319
+ text: input.message,
320
+ }),
321
+ })
322
+
323
+ const data = await response.json()
324
+
325
+ return {
326
+ output: {
327
+ messageId: data.message.ts,
328
+ timestamp: new Date().toISOString(),
329
+ },
330
+ state: 'succeeded'
331
+ }
332
+ } catch (error) {
333
+ return {
334
+ output: {},
335
+ state: 'failed',
336
+ errorMessage: error instanceof Error ? error.message : 'Unknown error'
337
+ }
206
338
  }
207
339
  },
208
- })
209
- ```
210
-
211
- Register custom steps in the plugin config:
340
+ }
212
341
 
213
- ```typescript
342
+ // Register in plugin config
214
343
  workflowsPlugin({
215
344
  steps: [SlackNotificationStep],
216
345
  })
@@ -1,5 +1,3 @@
1
- export { createStep } from './create-step.js';
2
- export type { StepDefinition, StepTask } from './create-step.js';
3
1
  export { CreateDocumentStepTask } from './create-document.js';
4
2
  export { createDocumentHandler } from './create-document-handler.js';
5
3
  export { DeleteDocumentStepTask } from './delete-document.js';
@@ -1,5 +1,3 @@
1
- // Step factory for creating new steps
2
- export { createStep } from './create-step.js';
3
1
  // Built-in steps
4
2
  export { CreateDocumentStepTask } from './create-document.js';
5
3
  export { createDocumentHandler } from './create-document-handler.js';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/steps/index.ts"],"sourcesContent":["// Step factory for creating new steps\nexport { createStep } from './create-step.js'\nexport type { StepDefinition, StepTask } from './create-step.js'\n\n// Built-in steps\nexport { CreateDocumentStepTask } from './create-document.js'\nexport { createDocumentHandler } from './create-document-handler.js'\nexport { DeleteDocumentStepTask } from './delete-document.js'\nexport { deleteDocumentHandler } from './delete-document-handler.js'\nexport { HttpRequestStepTask } from './http-request.js'\nexport { httpStepHandler } from './http-request-handler.js'\n\nexport { ReadDocumentStepTask } from './read-document.js'\nexport { readDocumentHandler } from './read-document-handler.js'\nexport { SendEmailStepTask } from './send-email.js'\nexport { sendEmailHandler } from './send-email-handler.js'\nexport { UpdateDocumentStepTask } from './update-document.js'\nexport { updateDocumentHandler } from './update-document-handler.js'\n"],"names":["createStep","CreateDocumentStepTask","createDocumentHandler","DeleteDocumentStepTask","deleteDocumentHandler","HttpRequestStepTask","httpStepHandler","ReadDocumentStepTask","readDocumentHandler","SendEmailStepTask","sendEmailHandler","UpdateDocumentStepTask","updateDocumentHandler"],"mappings":"AAAA,sCAAsC;AACtC,SAASA,UAAU,QAAQ,mBAAkB;AAG7C,iBAAiB;AACjB,SAASC,sBAAsB,QAAQ,uBAAsB;AAC7D,SAASC,qBAAqB,QAAQ,+BAA8B;AACpE,SAASC,sBAAsB,QAAQ,uBAAsB;AAC7D,SAASC,qBAAqB,QAAQ,+BAA8B;AACpE,SAASC,mBAAmB,QAAQ,oBAAmB;AACvD,SAASC,eAAe,QAAQ,4BAA2B;AAE3D,SAASC,oBAAoB,QAAQ,qBAAoB;AACzD,SAASC,mBAAmB,QAAQ,6BAA4B;AAChE,SAASC,iBAAiB,QAAQ,kBAAiB;AACnD,SAASC,gBAAgB,QAAQ,0BAAyB;AAC1D,SAASC,sBAAsB,QAAQ,uBAAsB;AAC7D,SAASC,qBAAqB,QAAQ,+BAA8B"}
1
+ {"version":3,"sources":["../../src/steps/index.ts"],"sourcesContent":["// Built-in steps\nexport { CreateDocumentStepTask } from './create-document.js'\nexport { createDocumentHandler } from './create-document-handler.js'\nexport { DeleteDocumentStepTask } from './delete-document.js'\nexport { deleteDocumentHandler } from './delete-document-handler.js'\nexport { HttpRequestStepTask } from './http-request.js'\nexport { httpStepHandler } from './http-request-handler.js'\n\nexport { ReadDocumentStepTask } from './read-document.js'\nexport { readDocumentHandler } from './read-document-handler.js'\nexport { SendEmailStepTask } from './send-email.js'\nexport { sendEmailHandler } from './send-email-handler.js'\nexport { UpdateDocumentStepTask } from './update-document.js'\nexport { updateDocumentHandler } from './update-document-handler.js'\n"],"names":["CreateDocumentStepTask","createDocumentHandler","DeleteDocumentStepTask","deleteDocumentHandler","HttpRequestStepTask","httpStepHandler","ReadDocumentStepTask","readDocumentHandler","SendEmailStepTask","sendEmailHandler","UpdateDocumentStepTask","updateDocumentHandler"],"mappings":"AAAA,iBAAiB;AACjB,SAASA,sBAAsB,QAAQ,uBAAsB;AAC7D,SAASC,qBAAqB,QAAQ,+BAA8B;AACpE,SAASC,sBAAsB,QAAQ,uBAAsB;AAC7D,SAASC,qBAAqB,QAAQ,+BAA8B;AACpE,SAASC,mBAAmB,QAAQ,oBAAmB;AACvD,SAASC,eAAe,QAAQ,4BAA2B;AAE3D,SAASC,oBAAoB,QAAQ,qBAAoB;AACzD,SAASC,mBAAmB,QAAQ,6BAA4B;AAChE,SAASC,iBAAiB,QAAQ,kBAAiB;AACnD,SAASC,gBAAgB,QAAQ,0BAAyB;AAC1D,SAASC,sBAAsB,QAAQ,uBAAsB;AAC7D,SAASC,qBAAqB,QAAQ,+BAA8B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xtr-dev/payload-automation",
3
- "version": "0.0.52",
3
+ "version": "0.0.53",
4
4
  "description": "PayloadCMS Automation Plugin - Comprehensive workflow automation system with visual workflow building, execution tracking, and step types",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,66 +0,0 @@
1
- import type { Field, JsonObject, PayloadRequest, TaskConfig } from 'payload';
2
- /**
3
- * Configuration for creating a step with the factory.
4
- */
5
- export interface StepDefinition<TSlug extends string> {
6
- /** Unique identifier for the step */
7
- slug: TSlug;
8
- /** Human-readable label for the step (optional, defaults to slug) */
9
- label?: string;
10
- /** Input fields schema */
11
- inputSchema: Field[];
12
- /** Output fields schema */
13
- outputSchema: Field[];
14
- /**
15
- * Optional validation function. Throw an error if validation fails.
16
- * Runs before the execute function.
17
- */
18
- validate?: (input: JsonObject) => void;
19
- /**
20
- * The main execution function for the step.
21
- * Should return the output data on success, or throw an error on failure.
22
- */
23
- execute: (input: JsonObject, req: PayloadRequest) => Promise<JsonObject>;
24
- }
25
- /**
26
- * The result type returned by createStep.
27
- * Combines TaskConfig with the handler for convenience.
28
- */
29
- export type StepTask<TSlug extends string> = TaskConfig<TSlug>;
30
- /**
31
- * Creates a step definition that combines TaskConfig and handler in one place.
32
- *
33
- * This factory eliminates the need for separate `{step}.ts` and `{step}-handler.ts` files
34
- * by providing a single unified definition.
35
- *
36
- * @example
37
- * ```typescript
38
- * export const myStep = createStep({
39
- * slug: 'my-step',
40
- * inputSchema: [
41
- * { name: 'url', type: 'text', required: true }
42
- * ],
43
- * outputSchema: [
44
- * { name: 'result', type: 'json' }
45
- * ],
46
- * validate: (input) => {
47
- * if (!input.url) throw new Error('URL is required')
48
- * },
49
- * execute: async (input, req) => {
50
- * const response = await fetch(input.url)
51
- * return { result: await response.json() }
52
- * }
53
- * })
54
- * ```
55
- */
56
- export declare function createStep<TSlug extends string>(definition: StepDefinition<TSlug>): StepTask<TSlug>;
57
- /**
58
- * Helper type to extract input type from a step's inputSchema.
59
- * Note: This provides a basic type based on field names, not full type inference.
60
- */
61
- export type StepInput<T extends StepTask<string>> = T extends StepTask<infer _> ? Record<string, unknown> : never;
62
- /**
63
- * Helper type to extract output type from a step's outputSchema.
64
- * Note: This provides a basic type based on field names, not full type inference.
65
- */
66
- export type StepOutput<T extends StepTask<string>> = T extends StepTask<infer _> ? Record<string, unknown> : never;
@@ -1,59 +0,0 @@
1
- /**
2
- * Creates a step definition that combines TaskConfig and handler in one place.
3
- *
4
- * This factory eliminates the need for separate `{step}.ts` and `{step}-handler.ts` files
5
- * by providing a single unified definition.
6
- *
7
- * @example
8
- * ```typescript
9
- * export const myStep = createStep({
10
- * slug: 'my-step',
11
- * inputSchema: [
12
- * { name: 'url', type: 'text', required: true }
13
- * ],
14
- * outputSchema: [
15
- * { name: 'result', type: 'json' }
16
- * ],
17
- * validate: (input) => {
18
- * if (!input.url) throw new Error('URL is required')
19
- * },
20
- * execute: async (input, req) => {
21
- * const response = await fetch(input.url)
22
- * return { result: await response.json() }
23
- * }
24
- * })
25
- * ```
26
- */ export function createStep(definition) {
27
- const { slug, label, inputSchema, outputSchema, validate, execute } = definition;
28
- // Create the handler that wraps validation and execution
29
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
- const handler = async ({ input, req })=>{
31
- try {
32
- const jsonInput = input ?? {};
33
- // Run validation if provided
34
- if (validate) {
35
- validate(jsonInput);
36
- }
37
- // Execute the step
38
- const output = await execute(jsonInput, req);
39
- return {
40
- output,
41
- state: 'succeeded'
42
- };
43
- } catch (error) {
44
- return {
45
- errorMessage: error instanceof Error ? error.message : 'Unknown error',
46
- state: 'failed'
47
- };
48
- }
49
- };
50
- return {
51
- slug,
52
- label,
53
- handler,
54
- inputSchema,
55
- outputSchema
56
- };
57
- }
58
-
59
- //# sourceMappingURL=create-step.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/steps/create-step.ts"],"sourcesContent":["import type { Field, JsonObject, PayloadRequest, TaskConfig, TaskHandler } from 'payload'\n\n/**\n * Configuration for creating a step with the factory.\n */\nexport interface StepDefinition<TSlug extends string> {\n /** Unique identifier for the step */\n slug: TSlug\n /** Human-readable label for the step (optional, defaults to slug) */\n label?: string\n /** Input fields schema */\n inputSchema: Field[]\n /** Output fields schema */\n outputSchema: Field[]\n /**\n * Optional validation function. Throw an error if validation fails.\n * Runs before the execute function.\n */\n validate?: (input: JsonObject) => void\n /**\n * The main execution function for the step.\n * Should return the output data on success, or throw an error on failure.\n */\n execute: (input: JsonObject, req: PayloadRequest) => Promise<JsonObject>\n}\n\n/**\n * The result type returned by createStep.\n * Combines TaskConfig with the handler for convenience.\n */\nexport type StepTask<TSlug extends string> = TaskConfig<TSlug>\n\n/**\n * Creates a step definition that combines TaskConfig and handler in one place.\n *\n * This factory eliminates the need for separate `{step}.ts` and `{step}-handler.ts` files\n * by providing a single unified definition.\n *\n * @example\n * ```typescript\n * export const myStep = createStep({\n * slug: 'my-step',\n * inputSchema: [\n * { name: 'url', type: 'text', required: true }\n * ],\n * outputSchema: [\n * { name: 'result', type: 'json' }\n * ],\n * validate: (input) => {\n * if (!input.url) throw new Error('URL is required')\n * },\n * execute: async (input, req) => {\n * const response = await fetch(input.url)\n * return { result: await response.json() }\n * }\n * })\n * ```\n */\nexport function createStep<TSlug extends string>(\n definition: StepDefinition<TSlug>\n): StepTask<TSlug> {\n const { slug, label, inputSchema, outputSchema, validate, execute } = definition\n\n // Create the handler that wraps validation and execution\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const handler = async ({ input, req }: any) => {\n try {\n const jsonInput = (input ?? {}) as JsonObject\n\n // Run validation if provided\n if (validate) {\n validate(jsonInput)\n }\n\n // Execute the step\n const output = await execute(jsonInput, req)\n return {\n output,\n state: 'succeeded'\n }\n } catch (error) {\n return {\n errorMessage: error instanceof Error ? error.message : 'Unknown error',\n state: 'failed'\n }\n }\n }\n\n return {\n slug,\n label,\n handler,\n inputSchema,\n outputSchema\n } as StepTask<TSlug>\n}\n\n/**\n * Helper type to extract input type from a step's inputSchema.\n * Note: This provides a basic type based on field names, not full type inference.\n */\nexport type StepInput<T extends StepTask<string>> = T extends StepTask<infer _>\n ? Record<string, unknown>\n : never\n\n/**\n * Helper type to extract output type from a step's outputSchema.\n * Note: This provides a basic type based on field names, not full type inference.\n */\nexport type StepOutput<T extends StepTask<string>> = T extends StepTask<infer _>\n ? Record<string, unknown>\n : never\n"],"names":["createStep","definition","slug","label","inputSchema","outputSchema","validate","execute","handler","input","req","jsonInput","output","state","error","errorMessage","Error","message"],"mappings":"AAgCA;;;;;;;;;;;;;;;;;;;;;;;;;CAyBC,GACD,OAAO,SAASA,WACdC,UAAiC;IAEjC,MAAM,EAAEC,IAAI,EAAEC,KAAK,EAAEC,WAAW,EAAEC,YAAY,EAAEC,QAAQ,EAAEC,OAAO,EAAE,GAAGN;IAEtE,yDAAyD;IACzD,8DAA8D;IAC9D,MAAMO,UAAU,OAAO,EAAEC,KAAK,EAAEC,GAAG,EAAO;QACxC,IAAI;YACF,MAAMC,YAAaF,SAAS,CAAC;YAE7B,6BAA6B;YAC7B,IAAIH,UAAU;gBACZA,SAASK;YACX;YAEA,mBAAmB;YACnB,MAAMC,SAAS,MAAML,QAAQI,WAAWD;YACxC,OAAO;gBACLE;gBACAC,OAAO;YACT;QACF,EAAE,OAAOC,OAAO;YACd,OAAO;gBACLC,cAAcD,iBAAiBE,QAAQF,MAAMG,OAAO,GAAG;gBACvDJ,OAAO;YACT;QACF;IACF;IAEA,OAAO;QACLX;QACAC;QACAK;QACAJ;QACAC;IACF;AACF"}