@mobilizehub/payload-plugin 0.3.0 → 0.3.2
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 +57 -185
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,218 +1,90 @@
|
|
|
1
|
-
# Payload Plugin
|
|
1
|
+
# MobilizeHub Payload Plugin
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A comprehensive email advocacy and contact management plugin for [Payload CMS](https://payloadcms.com). Build powerful advocacy campaigns, manage contacts, send targeted email broadcasts, and track engagement—all within your Payload admin.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/@mobilizehub/payload-plugin)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
> **Alpha Release**: This plugin is currently in alpha. Expect breaking changes between releases until we reach a stable 1.0 version.
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
- And some JavaScript/Typescript experience
|
|
10
|
+
## Features
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
- Contact database with tag-based segmentation and opt-in management
|
|
13
|
+
- Email broadcast campaigns with draft-to-sent workflow and audience targeting
|
|
14
|
+
- Background task processing for scalable email delivery
|
|
15
|
+
- Webhook integration for delivery status tracking (delivered, bounced, opened, clicked)
|
|
16
|
+
- Secure unsubscribe tokens with HMAC-SHA256 signing
|
|
17
|
+
- Pluggable email adapter system with built-in Resend support
|
|
18
|
+
- Customizable pages collection with content blocks
|
|
13
19
|
|
|
14
|
-
|
|
20
|
+
## Installation
|
|
15
21
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
export const config = buildConfig({
|
|
24
|
-
plugins: [
|
|
25
|
-
// You can pass options to the plugin
|
|
26
|
-
myPlugin({
|
|
27
|
-
enabled: true,
|
|
28
|
-
}),
|
|
29
|
-
],
|
|
30
|
-
})
|
|
22
|
+
```bash
|
|
23
|
+
npm install @mobilizehub/payload-plugin
|
|
24
|
+
# or
|
|
25
|
+
yarn add @mobilizehub/payload-plugin
|
|
26
|
+
# or
|
|
27
|
+
pnpm add @mobilizehub/payload-plugin
|
|
31
28
|
```
|
|
32
29
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
The initialization process goes in the following order:
|
|
36
|
-
|
|
37
|
-
1. Incoming config is validated
|
|
38
|
-
2. **Plugins execute**
|
|
39
|
-
3. Default options are integrated
|
|
40
|
-
4. Sanitization cleans and validates data
|
|
41
|
-
5. Final config gets initialized
|
|
42
|
-
|
|
43
|
-
## Building the Plugin
|
|
44
|
-
|
|
45
|
-
When you build a plugin, you are purely building a feature for your project and then abstracting it outside of the project.
|
|
46
|
-
|
|
47
|
-
### Template Files
|
|
48
|
-
|
|
49
|
-
In the Payload [plugin template](https://github.com/payloadcms/payload/tree/main/templates/plugin), you will see a common file structure that is used across all plugins:
|
|
50
|
-
|
|
51
|
-
1. root folder
|
|
52
|
-
2. /src folder
|
|
53
|
-
3. /dev folder
|
|
54
|
-
|
|
55
|
-
#### Root
|
|
30
|
+
## Development
|
|
56
31
|
|
|
57
|
-
|
|
32
|
+
### Setup
|
|
58
33
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
- .**prettierrc**.json - Configuration for Prettier code formatting.
|
|
64
|
-
- **tsconfig**.json - Configures the compiler options for TypeScript
|
|
65
|
-
- .**swcrc** - Configuration for SWC, a fast compiler that transpiles and bundles TypeScript.
|
|
66
|
-
- **vitest**.config.js - Config file for Vitest, defining how tests are run and how modules are resolved
|
|
34
|
+
```bash
|
|
35
|
+
# Clone the repository
|
|
36
|
+
git clone https://github.com/mobilizehub/payload-plugin.git
|
|
37
|
+
cd payload-plugin
|
|
67
38
|
|
|
68
|
-
|
|
39
|
+
# Install dependencies
|
|
40
|
+
pnpm install
|
|
69
41
|
|
|
70
|
-
|
|
42
|
+
# Create environment file
|
|
43
|
+
cp dev/.env.example dev/.env
|
|
44
|
+
# Edit dev/.env with your configuration
|
|
71
45
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
**IMPORTANT**: Make a copy of the `.env.example` file and rename it to `.env`. Update the `DATABASE_URI` to match the database you are using and your plugin name. Update `PAYLOAD_SECRET` to a unique string.
|
|
75
|
-
**You will not be able to run `pnpm/yarn dev` until you have created this `.env` file.**
|
|
76
|
-
|
|
77
|
-
`myPlugin` has already been added to the `payload.config()` file in this project.
|
|
78
|
-
|
|
79
|
-
```ts
|
|
80
|
-
plugins: [
|
|
81
|
-
myPlugin({
|
|
82
|
-
collections: {
|
|
83
|
-
posts: true,
|
|
84
|
-
},
|
|
85
|
-
}),
|
|
86
|
-
]
|
|
46
|
+
# Start development server
|
|
47
|
+
pnpm dev
|
|
87
48
|
```
|
|
88
49
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
You may wish to add collections or expand the test project depending on the purpose of your plugin. Just make sure to keep this dev environment as simplified as possible - users should be able to install your plugin without additional configuration required.
|
|
92
|
-
|
|
93
|
-
When you’re ready to start development, initiate the project with `pnpm/npm/yarn dev` and pull up [http://localhost:3000](http://localhost:3000) in your browser.
|
|
94
|
-
|
|
95
|
-
#### Src
|
|
50
|
+
### Testing
|
|
96
51
|
|
|
97
|
-
|
|
52
|
+
```bash
|
|
53
|
+
# Run all tests
|
|
54
|
+
pnpm test
|
|
98
55
|
|
|
99
|
-
|
|
56
|
+
# Run integration tests
|
|
57
|
+
pnpm test:int
|
|
100
58
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
```ts
|
|
104
|
-
export const myPlugin =
|
|
105
|
-
(pluginOptions: MyPluginConfig) =>
|
|
106
|
-
(config: Config): Config => {
|
|
107
|
-
// do cool stuff with the config here
|
|
108
|
-
|
|
109
|
-
return config
|
|
110
|
-
}
|
|
59
|
+
# Run E2E tests
|
|
60
|
+
pnpm test:e2e
|
|
111
61
|
```
|
|
112
62
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
From here, you can extend the config as you wish.
|
|
116
|
-
|
|
117
|
-
Finally, you return the config and that is it!
|
|
118
|
-
|
|
119
|
-
##### Spread Syntax
|
|
63
|
+
### Building
|
|
120
64
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
Let’s say you want to build a plugin that adds a new collection:
|
|
126
|
-
|
|
127
|
-
```ts
|
|
128
|
-
config.collections = [
|
|
129
|
-
...(config.collections || []),
|
|
130
|
-
// Add additional collections here
|
|
131
|
-
]
|
|
65
|
+
```bash
|
|
66
|
+
# Build the plugin
|
|
67
|
+
pnpm build
|
|
132
68
|
```
|
|
133
69
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
This same logic is applied to other properties like admin, hooks, globals:
|
|
137
|
-
|
|
138
|
-
```ts
|
|
139
|
-
config.globals = [
|
|
140
|
-
...(config.globals || []),
|
|
141
|
-
// Add additional globals here
|
|
142
|
-
]
|
|
143
|
-
|
|
144
|
-
config.hooks = {
|
|
145
|
-
...(incomingConfig.hooks || {}),
|
|
146
|
-
// Add additional hooks here
|
|
147
|
-
}
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
Some properties will be slightly different to extend, for instance the onInit property:
|
|
151
|
-
|
|
152
|
-
```ts
|
|
153
|
-
import { onInitExtension } from './onInitExtension' // example file
|
|
154
|
-
|
|
155
|
-
config.onInit = async (payload) => {
|
|
156
|
-
if (incomingConfig.onInit) await incomingConfig.onInit(payload)
|
|
157
|
-
// Add additional onInit code by defining an onInitExtension function
|
|
158
|
-
onInitExtension(pluginOptions, payload)
|
|
159
|
-
}
|
|
160
|
-
```
|
|
70
|
+
## Requirements
|
|
161
71
|
|
|
162
|
-
|
|
72
|
+
- Node.js: `^18.20.2 || >=20.9.0`
|
|
73
|
+
- Payload CMS: `^3.68.5`
|
|
74
|
+
- pnpm: `^9 || ^10`
|
|
163
75
|
|
|
164
|
-
|
|
76
|
+
## Contributing
|
|
165
77
|
|
|
166
|
-
|
|
78
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
167
79
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
```ts
|
|
171
|
-
export type MyPluginConfig = {
|
|
172
|
-
/**
|
|
173
|
-
* List of collections to add a custom field
|
|
174
|
-
*/
|
|
175
|
-
collections?: Partial<Record<CollectionSlug, true>>
|
|
176
|
-
/**
|
|
177
|
-
* Disable the plugin
|
|
178
|
-
*/
|
|
179
|
-
disabled?: boolean
|
|
180
|
-
}
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
If possible, include JSDoc comments to describe the options and their types. This allows a developer to see details about the options in their editor.
|
|
184
|
-
|
|
185
|
-
##### Testing
|
|
186
|
-
|
|
187
|
-
Having a test suite for your plugin is essential to ensure quality and stability. **Vitest** is a fast, modern testing framework that works seamlessly with Vite and supports TypeScript out of the box.
|
|
188
|
-
|
|
189
|
-
Vitest organizes tests into test suites and cases, similar to other testing frameworks. We recommend creating individual tests based on the expected behavior of your plugin from start to finish.
|
|
190
|
-
|
|
191
|
-
Writing tests with Vitest is very straightforward, and you can learn more about how it works in the [Vitest documentation.](https://vitest.dev/)
|
|
192
|
-
|
|
193
|
-
For this template, we stubbed out `int.spec.ts` in the `dev` folder where you can write your tests.
|
|
194
|
-
|
|
195
|
-
```ts
|
|
196
|
-
describe('Plugin tests', () => {
|
|
197
|
-
// Create tests to ensure expected behavior from the plugin
|
|
198
|
-
it('some condition that must be met', () => {
|
|
199
|
-
// Write your test logic here
|
|
200
|
-
expect(...)
|
|
201
|
-
})
|
|
202
|
-
})
|
|
203
|
-
```
|
|
80
|
+
## License
|
|
204
81
|
|
|
205
|
-
|
|
82
|
+
MIT © MobilizeHub
|
|
206
83
|
|
|
207
|
-
|
|
208
|
-
In addition to the setup, here are other best practices aim we follow:
|
|
84
|
+
## Support
|
|
209
85
|
|
|
210
|
-
|
|
211
|
-
- **Include tests in your GitHub CI workflow**: If you’ve configured tests for your package, integrate them into your workflow to run the tests each time you commit to the plugin repository. Learn more about [how to configure tests into your GitHub CI workflow.](https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs)
|
|
212
|
-
- **Publish your finished plugin to NPM**: The best way to share and allow others to use your plugin once it is complete is to publish an NPM package. This process is straightforward and well documented, find out more [creating and publishing a NPM package here.](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages/).
|
|
213
|
-
- **Add payload-plugin topic tag**: Apply the tag **payload-plugin **to your GitHub repository. This will boost the visibility of your plugin and ensure it gets listed with [existing payload plugins](https://github.com/topics/payload-plugin).
|
|
214
|
-
- **Use [Semantic Versioning](https://semver.org/) (SemVar)** - With the SemVar system you release version numbers that reflect the nature of changes (major, minor, patch). Ensure all major versions reference their Payload compatibility.
|
|
86
|
+
For issues, questions, or feature requests, please [open an issue](https://github.com/mobilizehub/payload-plugin/issues) on GitHub.
|
|
215
87
|
|
|
216
|
-
|
|
88
|
+
## Changelog
|
|
217
89
|
|
|
218
|
-
|
|
90
|
+
See [CHANGELOG.md](./CHANGELOG.md) for version history and release notes.
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { generateBroadcastsCollection } from './collections/broadcasts/generateBroadcastsCollection.js';
|
|
2
2
|
import { generateContactsCollection } from './collections/contacts/generateContactsCollection.js';
|
|
3
3
|
import { generateEmailsCollection } from './collections/emails/generateEmailsCollection.js';
|
|
4
|
+
import { generatePagesCollection } from './collections/pages/generatePagesCollection.js';
|
|
4
5
|
import { generateTagsCollection } from './collections/tags/generateTagsCollection.js';
|
|
5
6
|
import { generateUnsubscribeTokensCollection } from './collections/unsubscribe-tokens/generateUnsubscribeTokens.js';
|
|
6
7
|
import { sendBroadcastHandler } from './endpoints/sendBroadcastHandler.js';
|
|
@@ -16,7 +17,7 @@ export const mobilizehubPlugin = (pluginOptions)=>(config)=>{
|
|
|
16
17
|
if (!config.collections) {
|
|
17
18
|
config.collections = [];
|
|
18
19
|
}
|
|
19
|
-
config.collections.push(generateTagsCollection(pluginOptions), generateContactsCollection(pluginOptions), generateBroadcastsCollection(pluginOptions), generateEmailsCollection(pluginOptions), generateUnsubscribeTokensCollection());
|
|
20
|
+
config.collections.push(generateTagsCollection(pluginOptions), generateContactsCollection(pluginOptions), generateBroadcastsCollection(pluginOptions), generateEmailsCollection(pluginOptions), generatePagesCollection(pluginOptions), generateUnsubscribeTokensCollection());
|
|
20
21
|
if (!config.endpoints) {
|
|
21
22
|
config.endpoints = [];
|
|
22
23
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Config, Endpoint, TaskConfig } from 'payload'\n\nimport type { MobilizehubPluginConfig } from './types/index.js'\n\nimport { generateBroadcastsCollection } from './collections/broadcasts/generateBroadcastsCollection.js'\nimport { generateContactsCollection } from './collections/contacts/generateContactsCollection.js'\nimport { generateEmailsCollection } from './collections/emails/generateEmailsCollection.js'\nimport { generateTagsCollection } from './collections/tags/generateTagsCollection.js'\nimport { generateUnsubscribeTokensCollection } from './collections/unsubscribe-tokens/generateUnsubscribeTokens.js'\nimport { sendBroadcastHandler } from './endpoints/sendBroadcastHandler.js'\nimport { sendTestEmailHandler } from './endpoints/sendTestBroadcastHandler.js'\nimport { unsubscribeHandler } from './endpoints/unsubscribeHandler.js'\nimport { createSendBroadcastsTask } from './tasks/sendBroadcastsTask.js'\nimport { createSendEmailTask } from './tasks/sendEmailTask.js'\n\nexport * from './types/index.js'\n\nexport const mobilizehubPlugin =\n (pluginOptions: MobilizehubPluginConfig) =>\n (config: Config): Config => {\n if (pluginOptions.disabled) {\n return config\n }\n\n if (!config.collections) {\n config.collections = []\n }\n\n config.collections.push(\n generateTagsCollection(pluginOptions),\n generateContactsCollection(pluginOptions),\n generateBroadcastsCollection(pluginOptions),\n generateEmailsCollection(pluginOptions),\n generateUnsubscribeTokensCollection(),\n )\n\n if (!config.endpoints) {\n config.endpoints = []\n }\n\n const endpoints: Endpoint[] = [\n {\n handler: sendBroadcastHandler(),\n method: 'post',\n path: '/send-broadcast',\n },\n {\n handler: sendTestEmailHandler(pluginOptions),\n method: 'post',\n path: '/send-test-email',\n },\n {\n handler: unsubscribeHandler(),\n method: 'post',\n path: '/unsubscribe',\n },\n ]\n\n config.endpoints = [...config.endpoints, ...endpoints]\n\n if (!config.jobs) {\n config.jobs = {\n tasks: [],\n }\n }\n\n const tasks: TaskConfig[] = [\n createSendBroadcastsTask(pluginOptions),\n createSendEmailTask(pluginOptions),\n ]\n\n config.jobs.tasks = [...(config.jobs.tasks ?? []), ...tasks]\n\n const incomingOnInit = config.onInit\n\n config.onInit = async (payload) => {\n if (incomingOnInit) {\n await incomingOnInit(payload)\n }\n }\n\n return config\n }\n"],"names":["generateBroadcastsCollection","generateContactsCollection","generateEmailsCollection","generateTagsCollection","generateUnsubscribeTokensCollection","sendBroadcastHandler","sendTestEmailHandler","unsubscribeHandler","createSendBroadcastsTask","createSendEmailTask","mobilizehubPlugin","pluginOptions","config","disabled","collections","push","endpoints","handler","method","path","jobs","tasks","incomingOnInit","onInit","payload"],"mappings":"AAIA,SAASA,4BAA4B,QAAQ,2DAA0D;AACvG,SAASC,0BAA0B,QAAQ,uDAAsD;AACjG,SAASC,wBAAwB,QAAQ,mDAAkD;AAC3F,SAASC,sBAAsB,QAAQ,+CAA8C;AACrF,SAASC,mCAAmC,QAAQ,gEAA+D;AACnH,SAASC,oBAAoB,QAAQ,sCAAqC;AAC1E,SAASC,oBAAoB,QAAQ,0CAAyC;AAC9E,SAASC,kBAAkB,QAAQ,oCAAmC;AACtE,SAASC,wBAAwB,QAAQ,gCAA+B;AACxE,SAASC,mBAAmB,QAAQ,2BAA0B;AAE9D,cAAc,mBAAkB;AAEhC,OAAO,MAAMC,oBACX,CAACC,gBACD,CAACC;QACC,IAAID,cAAcE,QAAQ,EAAE;YAC1B,OAAOD;QACT;QAEA,IAAI,CAACA,OAAOE,WAAW,EAAE;YACvBF,OAAOE,WAAW,GAAG,EAAE;QACzB;QAEAF,OAAOE,WAAW,CAACC,IAAI,CACrBZ,uBAAuBQ,
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Config, Endpoint, TaskConfig } from 'payload'\n\nimport type { MobilizehubPluginConfig } from './types/index.js'\n\nimport { generateBroadcastsCollection } from './collections/broadcasts/generateBroadcastsCollection.js'\nimport { generateContactsCollection } from './collections/contacts/generateContactsCollection.js'\nimport { generateEmailsCollection } from './collections/emails/generateEmailsCollection.js'\nimport { generatePagesCollection } from './collections/pages/generatePagesCollection.js'\nimport { generateTagsCollection } from './collections/tags/generateTagsCollection.js'\nimport { generateUnsubscribeTokensCollection } from './collections/unsubscribe-tokens/generateUnsubscribeTokens.js'\nimport { sendBroadcastHandler } from './endpoints/sendBroadcastHandler.js'\nimport { sendTestEmailHandler } from './endpoints/sendTestBroadcastHandler.js'\nimport { unsubscribeHandler } from './endpoints/unsubscribeHandler.js'\nimport { createSendBroadcastsTask } from './tasks/sendBroadcastsTask.js'\nimport { createSendEmailTask } from './tasks/sendEmailTask.js'\n\nexport * from './types/index.js'\n\nexport const mobilizehubPlugin =\n (pluginOptions: MobilizehubPluginConfig) =>\n (config: Config): Config => {\n if (pluginOptions.disabled) {\n return config\n }\n\n if (!config.collections) {\n config.collections = []\n }\n\n config.collections.push(\n generateTagsCollection(pluginOptions),\n generateContactsCollection(pluginOptions),\n generateBroadcastsCollection(pluginOptions),\n generateEmailsCollection(pluginOptions),\n generatePagesCollection(pluginOptions),\n generateUnsubscribeTokensCollection(),\n )\n\n if (!config.endpoints) {\n config.endpoints = []\n }\n\n const endpoints: Endpoint[] = [\n {\n handler: sendBroadcastHandler(),\n method: 'post',\n path: '/send-broadcast',\n },\n {\n handler: sendTestEmailHandler(pluginOptions),\n method: 'post',\n path: '/send-test-email',\n },\n {\n handler: unsubscribeHandler(),\n method: 'post',\n path: '/unsubscribe',\n },\n ]\n\n config.endpoints = [...config.endpoints, ...endpoints]\n\n if (!config.jobs) {\n config.jobs = {\n tasks: [],\n }\n }\n\n const tasks: TaskConfig[] = [\n createSendBroadcastsTask(pluginOptions),\n createSendEmailTask(pluginOptions),\n ]\n\n config.jobs.tasks = [...(config.jobs.tasks ?? []), ...tasks]\n\n const incomingOnInit = config.onInit\n\n config.onInit = async (payload) => {\n if (incomingOnInit) {\n await incomingOnInit(payload)\n }\n }\n\n return config\n }\n"],"names":["generateBroadcastsCollection","generateContactsCollection","generateEmailsCollection","generatePagesCollection","generateTagsCollection","generateUnsubscribeTokensCollection","sendBroadcastHandler","sendTestEmailHandler","unsubscribeHandler","createSendBroadcastsTask","createSendEmailTask","mobilizehubPlugin","pluginOptions","config","disabled","collections","push","endpoints","handler","method","path","jobs","tasks","incomingOnInit","onInit","payload"],"mappings":"AAIA,SAASA,4BAA4B,QAAQ,2DAA0D;AACvG,SAASC,0BAA0B,QAAQ,uDAAsD;AACjG,SAASC,wBAAwB,QAAQ,mDAAkD;AAC3F,SAASC,uBAAuB,QAAQ,iDAAgD;AACxF,SAASC,sBAAsB,QAAQ,+CAA8C;AACrF,SAASC,mCAAmC,QAAQ,gEAA+D;AACnH,SAASC,oBAAoB,QAAQ,sCAAqC;AAC1E,SAASC,oBAAoB,QAAQ,0CAAyC;AAC9E,SAASC,kBAAkB,QAAQ,oCAAmC;AACtE,SAASC,wBAAwB,QAAQ,gCAA+B;AACxE,SAASC,mBAAmB,QAAQ,2BAA0B;AAE9D,cAAc,mBAAkB;AAEhC,OAAO,MAAMC,oBACX,CAACC,gBACD,CAACC;QACC,IAAID,cAAcE,QAAQ,EAAE;YAC1B,OAAOD;QACT;QAEA,IAAI,CAACA,OAAOE,WAAW,EAAE;YACvBF,OAAOE,WAAW,GAAG,EAAE;QACzB;QAEAF,OAAOE,WAAW,CAACC,IAAI,CACrBZ,uBAAuBQ,gBACvBX,2BAA2BW,gBAC3BZ,6BAA6BY,gBAC7BV,yBAAyBU,gBACzBT,wBAAwBS,gBACxBP;QAGF,IAAI,CAACQ,OAAOI,SAAS,EAAE;YACrBJ,OAAOI,SAAS,GAAG,EAAE;QACvB;QAEA,MAAMA,YAAwB;YAC5B;gBACEC,SAASZ;gBACTa,QAAQ;gBACRC,MAAM;YACR;YACA;gBACEF,SAASX,qBAAqBK;gBAC9BO,QAAQ;gBACRC,MAAM;YACR;YACA;gBACEF,SAASV;gBACTW,QAAQ;gBACRC,MAAM;YACR;SACD;QAEDP,OAAOI,SAAS,GAAG;eAAIJ,OAAOI,SAAS;eAAKA;SAAU;QAEtD,IAAI,CAACJ,OAAOQ,IAAI,EAAE;YAChBR,OAAOQ,IAAI,GAAG;gBACZC,OAAO,EAAE;YACX;QACF;QAEA,MAAMA,QAAsB;YAC1Bb,yBAAyBG;YACzBF,oBAAoBE;SACrB;QAEDC,OAAOQ,IAAI,CAACC,KAAK,GAAG;eAAKT,OAAOQ,IAAI,CAACC,KAAK,IAAI,EAAE;eAAMA;SAAM;QAE5D,MAAMC,iBAAiBV,OAAOW,MAAM;QAEpCX,OAAOW,MAAM,GAAG,OAAOC;YACrB,IAAIF,gBAAgB;gBAClB,MAAMA,eAAeE;YACvB;QACF;QAEA,OAAOZ;IACT,EAAC"}
|