@pixelplex/mail-service 0.1.16 β 0.1.20
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/.gitlab-ci.yml +39 -0
- package/.husky/pre-commit +9 -0
- package/README.md +24 -13
- package/dist/connect/app.service.js +4 -2
- package/dist/connect/app.service.js.map +1 -1
- package/dist/connect/retry.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/service/app.controller.d.ts +1 -1
- package/dist/service/app.controller.js.map +1 -1
- package/dist/service/sender.service.js +18 -7
- package/dist/service/sender.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +954 -1005
- package/package.json +18 -16
package/.gitlab-ci.yml
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
stages:
|
|
2
|
+
- π¦ test
|
|
3
|
+
- π¦ package
|
|
4
|
+
- π deploy
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
##############################
|
|
9
|
+
# π¦ PACKAGE π¦ #
|
|
10
|
+
##############################
|
|
11
|
+
|
|
12
|
+
π¦ image:
|
|
13
|
+
stage: π¦ package
|
|
14
|
+
tags:
|
|
15
|
+
- pp-builder
|
|
16
|
+
script:
|
|
17
|
+
- docker login -u $DOCKERHUB_USER -p $DOCKERHUB_PASSWORD
|
|
18
|
+
- docker pull pixelplex/mail-service:latest || true
|
|
19
|
+
- docker build -t pixelplex/mail-service:$CI_COMMIT_REF_NAME
|
|
20
|
+
-t pixelplex/mail-service:latest .
|
|
21
|
+
- docker push pixelplex/mail-service:$CI_COMMIT_REF_NAME
|
|
22
|
+
- docker push pixelplex/mail-service:latest
|
|
23
|
+
- docker logout
|
|
24
|
+
only:
|
|
25
|
+
- tags
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
π¦ package:
|
|
30
|
+
stage: π¦ package
|
|
31
|
+
tags:
|
|
32
|
+
- docker
|
|
33
|
+
script:
|
|
34
|
+
- echo "//registry.yarnpkg.com/:_authToken=$NPM_TOKEN" >>~/.npmrc
|
|
35
|
+
- yarn
|
|
36
|
+
- yarn build
|
|
37
|
+
- yarn publish --non-interactive
|
|
38
|
+
only:
|
|
39
|
+
- tags
|
package/README.md
CHANGED
|
@@ -22,6 +22,18 @@ TL;DR
|
|
|
22
22
|
|
|
23
23
|
Didn't understand anything? Okay, let's see more details
|
|
24
24
|
|
|
25
|
+
### Local development
|
|
26
|
+
|
|
27
|
+
To use the package locally, pass the follow config:
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
- MAIL_SERVICE_SMTP_HOST=smtp.ethereal.email
|
|
31
|
+
- MAIL_SERVICE_SMTP_PORT=587
|
|
32
|
+
- MAIL_SERVICE_SMTP_SENDER=postmail@pixelplex.io
|
|
33
|
+
- MAIL_SERVICE_SMTP_USERNAME=
|
|
34
|
+
- MAIL_SERVICE_SMTP_PASSWORD=
|
|
35
|
+
```
|
|
36
|
+
|
|
25
37
|
### Prepare templates
|
|
26
38
|
|
|
27
39
|
The service uses Handlebars as template engine. So all your email templates should be prepared as handlebars templates.
|
|
@@ -32,7 +44,7 @@ For sending message your templates folder should include at least 3 files:
|
|
|
32
44
|
- `{type}.handlebars` - body of your email where `{type}` is just your unique key for a unique type of email. For example `welcome.handlebars` for the welcome use email
|
|
33
45
|
- `{type}.subject.handlebars` - subject (title) of yur email
|
|
34
46
|
|
|
35
|
-
For example our project will store files in `templates` folder.
|
|
47
|
+
For example our project will store files in `templates` folder.
|
|
36
48
|
|
|
37
49
|
```
|
|
38
50
|
βββ docker-compose.yml
|
|
@@ -44,6 +56,7 @@ For example our project will store files in `templates` folder.
|
|
|
44
56
|
|
|
45
57
|
```handlebars
|
|
46
58
|
# main.handlebars
|
|
59
|
+
|
|
47
60
|
<html>
|
|
48
61
|
<head></head>
|
|
49
62
|
<body>{{{body}}}</body>
|
|
@@ -51,12 +64,12 @@ For example our project will store files in `templates` folder.
|
|
|
51
64
|
```
|
|
52
65
|
|
|
53
66
|
```handlebars
|
|
54
|
-
# welcome.subject.handlebars
|
|
55
|
-
Welcome, {{name}}!
|
|
67
|
+
# welcome.subject.handlebars Welcome, {{name}}!
|
|
56
68
|
```
|
|
57
69
|
|
|
58
70
|
```handlebars
|
|
59
71
|
# welcome.handlebars
|
|
72
|
+
|
|
60
73
|
<p>Welcome, {{name}}!</p>
|
|
61
74
|
<p>How are you?</p>
|
|
62
75
|
```
|
|
@@ -66,16 +79,16 @@ Welcome, {{name}}!
|
|
|
66
79
|
Then you need to run this service and connect it to RMQ. For example, you can use Docker Compose:
|
|
67
80
|
|
|
68
81
|
```yaml
|
|
69
|
-
version:
|
|
82
|
+
version: '3'
|
|
70
83
|
services:
|
|
71
84
|
rabbitmq:
|
|
72
|
-
image:
|
|
85
|
+
image: 'rabbitmq:3-management'
|
|
73
86
|
ports:
|
|
74
|
-
-
|
|
75
|
-
-
|
|
87
|
+
- '127.0.0.1:5672:5672'
|
|
88
|
+
- '127.0.0.1:15672:15672'
|
|
76
89
|
|
|
77
90
|
mail-service:
|
|
78
|
-
image:
|
|
91
|
+
image: 'pixelplex/mail-service:0.1.18'
|
|
79
92
|
environment:
|
|
80
93
|
- MAIL_SERVICE_RMQ_URL=amqp://rabbitmq:5672
|
|
81
94
|
- MAIL_SERVICE_RMQ_QUEUE=test_queue
|
|
@@ -86,8 +99,8 @@ services:
|
|
|
86
99
|
- MAIL_SERVICE_SMTP_PASSWORD=$MAIL_SERVICE_SMTP_PASSWORD
|
|
87
100
|
- MAIL_SERVICE_SENTRY_DNS=$MAIL_SERVICE_SENTRY_DNS
|
|
88
101
|
volumes:
|
|
89
|
-
- ./templates:/app/templates # note that we attach our template folder as /app/templates
|
|
90
|
-
```
|
|
102
|
+
- ./templates:/app/templates # note that we attach our template folder as /app/templates
|
|
103
|
+
```
|
|
91
104
|
|
|
92
105
|
### Trigger new message sending
|
|
93
106
|
|
|
@@ -147,13 +160,11 @@ import { MailService } from '@pixelplex/mail-service';
|
|
|
147
160
|
|
|
148
161
|
@Injectable()
|
|
149
162
|
export class AppService {
|
|
150
|
-
|
|
151
163
|
constructor(private mailService: MailService) {}
|
|
152
164
|
|
|
153
165
|
async onModuleInit() {
|
|
154
166
|
this.mailService.sendMail('test@gmail.com', 'welcome', { name: 'ΠΡΠ°Π½Π°ΡΠΈΠΉ' }, 20);
|
|
155
167
|
}
|
|
156
|
-
|
|
157
168
|
}
|
|
158
169
|
```
|
|
159
170
|
|
|
@@ -161,5 +172,5 @@ In the example below we'll send email to `test@gmail.com` with type `welcome` (`
|
|
|
161
172
|
|
|
162
173
|
`{name: 'ΠΡΠ°Π½Π°ΡΠΈΠΉ'}` is the object with data which will used as payload data during processing your handlebars template.
|
|
163
174
|
|
|
164
|
-
The last argument, `20`, is the number of seconds after which the message will be rejected from the queue if, but only if it was processed with an error.
|
|
175
|
+
The last argument, `20`, is the number of seconds after which the message will be rejected from the queue if, but only if it was processed with an error.
|
|
165
176
|
If you think your message should live until it is successfully sent use `0`.
|
|
@@ -13,8 +13,8 @@ exports.MailService = void 0;
|
|
|
13
13
|
const common_1 = require("@nestjs/common");
|
|
14
14
|
const microservices_1 = require("@nestjs/microservices");
|
|
15
15
|
const rxjs_1 = require("rxjs");
|
|
16
|
-
const retry_1 = require("./retry");
|
|
17
16
|
const connection_1 = require("../connection");
|
|
17
|
+
const retry_1 = require("./retry");
|
|
18
18
|
let MailService = class MailService {
|
|
19
19
|
constructor() {
|
|
20
20
|
this.logger = new common_1.Logger('Mail::MailService');
|
|
@@ -23,7 +23,9 @@ let MailService = class MailService {
|
|
|
23
23
|
await rxjs_1.defer(async () => {
|
|
24
24
|
await this.client.connect();
|
|
25
25
|
await this.clientRecovery.connect();
|
|
26
|
-
})
|
|
26
|
+
})
|
|
27
|
+
.pipe(retry_1.default(10, 5000))
|
|
28
|
+
.toPromise();
|
|
27
29
|
this.logger.log('RMQ Connected');
|
|
28
30
|
}
|
|
29
31
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.service.js","sourceRoot":"","sources":["../../src/connect/app.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4E;AAC5E,yDAA4D;AAC5D,+BAAyC;AACzC,
|
|
1
|
+
{"version":3,"file":"app.service.js","sourceRoot":"","sources":["../../src/connect/app.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4E;AAC5E,yDAA4D;AAC5D,+BAAyC;AACzC,8CAAwD;AAExD,mCAA4B;AAG5B,IAAa,WAAW,GAAxB,MAAa,WAAW;IAAxB;QACU,WAAM,GAAW,IAAI,eAAM,CAAC,mBAAmB,CAAC,CAAC;IAwC3D,CAAC;IAhCC,KAAK,CAAC,sBAAsB;QAC1B,MAAM,YAAK,CAAC,KAAK,IAAI,EAAE;YACrB,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YAC5B,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC;QACtC,CAAC,CAAC;aACC,IAAI,CAAC,eAAK,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;aACrB,SAAS,EAAE,CAAC;QACf,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IACnC,CAAC;IAED;;;;;;;OAOG;IACH,QAAQ,CACN,EAAU,EACV,IAAY,EACZ,OAAoC,EACpC,MAAc,CAAC;QAEf,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAuB,UAAU,EAAE;YACxD,IAAI;YACJ,OAAO;YACP,EAAE;YACF,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE;YAC/B,GAAG;SACJ,CAAC,CAAC;IACL,CAAC;CACF,CAAA;AArCC;IADC,sBAAM,CAAC,0BAAa,CAAC;8BACd,2BAAW;2CAAC;AAGpB;IADC,sBAAM,CAAC,qBAAQ,CAAC;8BACD,2BAAW;mDAAC;AAPjB,WAAW;IADvB,mBAAU,EAAE;GACA,WAAW,CAyCvB;AAzCY,kCAAW"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../../src/connect/retry.ts"],"names":[],"mappings":";;AACA,8CAAwD;AAExD,SAAwB,KAAK,
|
|
1
|
+
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../../src/connect/retry.ts"],"names":[],"mappings":";;AACA,8CAAwD;AAExD,SAAwB,KAAK,CAAC,aAAa,GAAG,EAAE,EAAE,UAAU,GAAG,KAAK;IAClE,OAAO,CAAI,MAAqB,EAAE,EAAE,CAClC,MAAM,CAAC,IAAI,CACT,qBAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CACd,CAAC,CAAC,IAAI,CACJ,gBAAI,CAAC,CAAC,GAAW,EAAE,KAAY,EAAE,EAAE;QACjC,IAAI,GAAG,GAAG,CAAC,IAAI,aAAa,EAAE;YAC5B,MAAM,KAAK,CAAC;SACb;QACD,OAAO,GAAG,GAAG,CAAC,CAAC;IACjB,CAAC,EAAE,CAAC,CAAC,EACL,iBAAK,CAAC,UAAU,CAAC,CAClB,CACF,CACF,CAAC;AACN,CAAC;AAfD,wBAeC"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,qDAAkD;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,qDAAkD;AAGzC,2FAHA,uBAAU,OAGA;AAFnB,uDAAoD;AAE/B,4FAFZ,yBAAW,OAEY"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RmqContext } from '@nestjs/microservices';
|
|
2
|
-
import { SenderService } from './sender.service';
|
|
3
2
|
import { MailMessage } from '../mail';
|
|
3
|
+
import { SenderService } from './sender.service';
|
|
4
4
|
export declare class AppController {
|
|
5
5
|
private readonly senderService;
|
|
6
6
|
private logger;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.controller.js","sourceRoot":"","sources":["../../src/service/app.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAoD;AACpD,yDAA+E;AAC/E,uCAAuC;
|
|
1
|
+
{"version":3,"file":"app.controller.js","sourceRoot":"","sources":["../../src/service/app.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAoD;AACpD,yDAA+E;AAC/E,uCAAuC;AAEvC,qDAAiD;AAGjD,IAAa,aAAa,GAA1B,MAAa,aAAa;IAExB,YAA6B,aAA4B;QAA5B,kBAAa,GAAb,aAAa,CAAe;QADjD,WAAM,GAAG,IAAI,eAAM,CAAC,qBAAqB,CAAC,CAAC;IACS,CAAC;IAG7D,KAAK,CAAC,QAAQ,CAAY,IAAiB,EAAS,OAAmB;QACrE,MAAM,OAAO,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;QACxC,MAAM,WAAW,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;QACzC,IAAI;YACF,MAAM,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YACpE,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;SAC1B;QAAC,OAAO,CAAC,EAAE;YACV,IAAI,IAAI,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE;gBAC7E,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,eAAe,IAAI,CAAC,EAAE,oBAAoB,IAAI,CAAC,IAAI,gDAAgD,CACpG,CAAC;gBACF,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;gBACzB,OAAO;aACR;YACD,MAAM,CAAC,gBAAgB,CAAC,CAAC,EAAE;gBACzB,KAAK,EAAE,IAAI;aACZ,CAAC,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;YACtC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACrB,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;SACzC;IACH,CAAC;CACF,CAAA;AAtBC;IADC,4BAAY,CAAC,UAAU,CAAC;IACT,WAAA,uBAAO,EAAE,CAAA,EAAqB,WAAA,mBAAG,EAAE,CAAA;;6CAAU,0BAAU;;6CAqBtE;AA1BU,aAAa;IADzB,mBAAU,EAAE;qCAGiC,8BAAa;GAF9C,aAAa,CA2BzB;AA3BY,sCAAa"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SenderService = void 0;
|
|
4
|
+
const fs = require("fs");
|
|
4
5
|
const common_1 = require("@nestjs/common");
|
|
5
6
|
const config_1 = require("config");
|
|
6
|
-
const fs = require("fs");
|
|
7
7
|
const Handlebars = require("handlebars");
|
|
8
8
|
const nodemailer_1 = require("nodemailer");
|
|
9
9
|
const hbs = require("nodemailer-express-handlebars");
|
|
@@ -21,16 +21,21 @@ class SenderService {
|
|
|
21
21
|
await this.connect();
|
|
22
22
|
}
|
|
23
23
|
async connect() {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
const isEthereal = config_1.MAIL_SERVICE.SMTP.HOST === 'smtp.ethereal.email';
|
|
25
|
+
if (!config_1.MAIL_SERVICE.SMTP.PASSWORD && !isEthereal) {
|
|
26
|
+
throw new Error(`[NodemailerConnector Error] Password wasn\'t provided.
|
|
27
|
+
You should provide the password in ENV Variable or in local.json config file`);
|
|
28
|
+
}
|
|
29
|
+
let testAccount;
|
|
30
|
+
if (isEthereal) {
|
|
31
|
+
testAccount = await nodemailer_1.createTestAccount();
|
|
27
32
|
}
|
|
28
33
|
this.transporter = nodemailer_1.createTransport({
|
|
29
34
|
host: config_1.MAIL_SERVICE.SMTP.HOST,
|
|
30
35
|
port: config_1.MAIL_SERVICE.SMTP.PORT,
|
|
31
36
|
auth: {
|
|
32
|
-
user: config_1.MAIL_SERVICE.SMTP.USERNAME,
|
|
33
|
-
pass: config_1.MAIL_SERVICE.SMTP.PASSWORD,
|
|
37
|
+
user: config_1.MAIL_SERVICE.SMTP.USERNAME || testAccount.user,
|
|
38
|
+
pass: config_1.MAIL_SERVICE.SMTP.PASSWORD || testAccount.pass,
|
|
34
39
|
},
|
|
35
40
|
});
|
|
36
41
|
this.transporter.use('compile', hbs(hbsOptions));
|
|
@@ -39,7 +44,13 @@ class SenderService {
|
|
|
39
44
|
async sendMail(to, template, context = {}) {
|
|
40
45
|
this.logger.debug(`New message to "${to}" with template "${template}"`);
|
|
41
46
|
const subjectTpl = Handlebars.compile(fs.readFileSync(`templates/${template}.subject.handlebars`).toString());
|
|
42
|
-
const info = await this.transporter.sendMail({
|
|
47
|
+
const info = await this.transporter.sendMail({
|
|
48
|
+
from: config_1.MAIL_SERVICE.SMTP.SENDER,
|
|
49
|
+
to,
|
|
50
|
+
subject: subjectTpl(context),
|
|
51
|
+
template,
|
|
52
|
+
context,
|
|
53
|
+
});
|
|
43
54
|
this.logger.debug(`Message sent: ${info.messageId}, ${info.response}`);
|
|
44
55
|
}
|
|
45
56
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sender.service.js","sourceRoot":"","sources":["../../src/service/sender.service.ts"],"names":[],"mappings":";;;AAAA,2CAAwC;AACxC,mCAAsC;AACtC,
|
|
1
|
+
{"version":3,"file":"sender.service.js","sourceRoot":"","sources":["../../src/service/sender.service.ts"],"names":[],"mappings":";;;AAAA,yBAAyB;AACzB,2CAAwC;AACxC,mCAAsC;AACtC,yCAAyC;AACzC,2CAA6E;AAC7E,qDAAqD;AAErD,MAAM,UAAU,GAAG;IACjB,UAAU,EAAE;QACV,UAAU,EAAE,YAAY;KACzB;IACD,QAAQ,EAAE,YAAY;CACvB,CAAC;AAEF,MAAa,aAAa;IAA1B;QAEmB,WAAM,GAAW,IAAI,eAAM,CAAC,qBAAqB,CAAC,CAAC;IA2CtE,CAAC;IAzCC,KAAK,CAAC,YAAY;QAChB,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,UAAU,GAAG,qBAAY,CAAC,IAAI,CAAC,IAAI,KAAK,qBAAqB,CAAC;QACpE,IAAI,CAAC,qBAAY,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,UAAU,EAAE;YAC9C,MAAM,IAAI,KAAK,CACb;qFAC6E,CAC9E,CAAC;SACH;QAED,IAAI,WAAW,CAAC;QAChB,IAAI,UAAU,EAAE;YACd,WAAW,GAAG,MAAM,8BAAiB,EAAE,CAAC;SACzC;QACD,IAAI,CAAC,WAAW,GAAG,4BAAe,CAAC;YACjC,IAAI,EAAE,qBAAY,CAAC,IAAI,CAAC,IAAI;YAC5B,IAAI,EAAE,qBAAY,CAAC,IAAI,CAAC,IAAI;YAC5B,IAAI,EAAE;gBACJ,IAAI,EAAE,qBAAY,CAAC,IAAI,CAAC,QAAQ,IAAI,WAAW,CAAC,IAAI;gBACpD,IAAI,EAAE,qBAAY,CAAC,IAAI,CAAC,QAAQ,IAAI,WAAW,CAAC,IAAI;aACrD;SACF,CAAC,CAAC;QACH,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC;QACjD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,EAAU,EAAE,QAAgB,EAAE,OAAO,GAAG,EAAE;QACvD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,EAAE,oBAAoB,QAAQ,GAAG,CAAC,CAAC;QACxE,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC,YAAY,CAAC,aAAa,QAAQ,qBAAqB,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC9G,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC;YAC3C,IAAI,EAAE,qBAAY,CAAC,IAAI,CAAC,MAAM;YAC9B,EAAE;YACF,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC;YAC5B,QAAQ;YACR,OAAO;SACR,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAiB,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IACzE,CAAC;CACF;AA7CD,sCA6CC"}
|