tzmail 1.0.0

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 (56) hide show
  1. package/README.md +5091 -0
  2. package/dist/core/enums/template-part.enum.d.ts +7 -0
  3. package/dist/core/enums/template-part.enum.d.ts.map +1 -0
  4. package/dist/core/enums/template-part.enum.js +10 -0
  5. package/dist/core/enums/theme.enum.d.ts +8 -0
  6. package/dist/core/enums/theme.enum.d.ts.map +1 -0
  7. package/dist/core/enums/theme.enum.js +11 -0
  8. package/dist/core/interfaces/email.interface.d.ts +32 -0
  9. package/dist/core/interfaces/email.interface.d.ts.map +1 -0
  10. package/dist/core/interfaces/email.interface.js +2 -0
  11. package/dist/core/interfaces/template.interface.d.ts +56 -0
  12. package/dist/core/interfaces/template.interface.d.ts.map +1 -0
  13. package/dist/core/interfaces/template.interface.js +2 -0
  14. package/dist/factories/email-factory.d.ts +34 -0
  15. package/dist/factories/email-factory.d.ts.map +1 -0
  16. package/dist/factories/email-factory.js +52 -0
  17. package/dist/factories/template-factory.d.ts +15 -0
  18. package/dist/factories/template-factory.d.ts.map +1 -0
  19. package/dist/factories/template-factory.js +96 -0
  20. package/dist/index.d.ts +15 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +265 -0
  23. package/dist/services/attachment.service.d.ts +48 -0
  24. package/dist/services/attachment.service.d.ts.map +1 -0
  25. package/dist/services/attachment.service.js +93 -0
  26. package/dist/services/email.service.d.ts +31 -0
  27. package/dist/services/email.service.d.ts.map +1 -0
  28. package/dist/services/email.service.js +58 -0
  29. package/dist/services/template.service.d.ts +180 -0
  30. package/dist/services/template.service.d.ts.map +1 -0
  31. package/dist/services/template.service.js +507 -0
  32. package/dist/templates/base/template-builder.d.ts +23 -0
  33. package/dist/templates/base/template-builder.d.ts.map +1 -0
  34. package/dist/templates/base/template-builder.js +676 -0
  35. package/dist/templates/themes/corporate.theme.d.ts +65 -0
  36. package/dist/templates/themes/corporate.theme.d.ts.map +1 -0
  37. package/dist/templates/themes/corporate.theme.js +109 -0
  38. package/dist/templates/themes/minimal.theme.d.ts +70 -0
  39. package/dist/templates/themes/minimal.theme.d.ts.map +1 -0
  40. package/dist/templates/themes/minimal.theme.js +114 -0
  41. package/dist/templates/themes/modern.theme.d.ts +38 -0
  42. package/dist/templates/themes/modern.theme.d.ts.map +1 -0
  43. package/dist/templates/themes/modern.theme.js +81 -0
  44. package/dist/templates/themes/monokai.theme.d.ts +29 -0
  45. package/dist/templates/themes/monokai.theme.d.ts.map +1 -0
  46. package/dist/templates/themes/monokai.theme.js +73 -0
  47. package/dist/templates/themes/system.theme.d.ts +50 -0
  48. package/dist/templates/themes/system.theme.d.ts.map +1 -0
  49. package/dist/templates/themes/system.theme.js +54 -0
  50. package/dist/templates/themes/theme.interface.d.ts +42 -0
  51. package/dist/templates/themes/theme.interface.d.ts.map +1 -0
  52. package/dist/templates/themes/theme.interface.js +2 -0
  53. package/dist/tests/template.service.test.d.ts +1 -0
  54. package/dist/tests/template.service.test.d.ts.map +1 -0
  55. package/dist/tests/template.service.test.js +1 -0
  56. package/package.json +46 -0
@@ -0,0 +1,507 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TemplateService = void 0;
4
+ const template_factory_1 = require("../factories/template-factory");
5
+ const theme_enum_1 = require("../core/enums/theme.enum");
6
+ /**
7
+ * TemplateService - Gerencia a criação, renderização e gerenciamento de templates de email
8
+ *
9
+ * Métodos:
10
+ * - createTemplate(themeType, variant, config, options): ITemplate
11
+ * Cria um novo template com as configurações especificadas.
12
+ *
13
+ * - cloneTemplate(template, modifications): ITemplate
14
+ * Cria um template a partir de um template existente (clone).
15
+ *
16
+ * - renderTemplate(template, data, options): Promise<string>
17
+ * Renderiza um template com os dados fornecidos.
18
+ *
19
+ * - previewTemplate(themeType, variant, config, data): Promise<string>
20
+ * Pré-visualiza um template sem enviar email.
21
+ *
22
+ * - getThemeInfo(themeType): object
23
+ * Obtém informações sobre um tema específico.
24
+ *
25
+ * - listThemes(): ThemeType[]
26
+ * Lista todos os temas disponíveis.
27
+ *
28
+ * - getTemplateStats(): object
29
+ * Obtém estatísticas de uso dos templates.
30
+ *
31
+ * - clearCache(themeType?): number
32
+ * Limpa o cache de templates, opcionalmente por tema.
33
+ *
34
+ * - getTemplateHistory(templateId): ITemplate[]
35
+ * Obtém histórico de versões de um template.
36
+ *
37
+ * - restoreTemplateVersion(templateId, versionIndex): ITemplate | null
38
+ * Restaura uma versão anterior do template.
39
+ *
40
+ * Exemplo de uso:
41
+ *
42
+ * const templateService = new TemplateService();
43
+ * const myTemplate = templateService.createTemplate(ThemeType.MODERN, 'light', { header: { show: true } });
44
+ * const html = await templateService.renderTemplate(myTemplate, { username: 'John' });
45
+ * console.log(html);
46
+ */
47
+ class TemplateService {
48
+ constructor(options = {}) {
49
+ this.options = options;
50
+ this.templateCache = new Map();
51
+ this.defaultTTL = 3600;
52
+ this.templateHistory = new Map();
53
+ this.options = {
54
+ cache: true,
55
+ cacheTTL: 3600,
56
+ validateConfig: true,
57
+ minify: true,
58
+ preview: false,
59
+ ...options
60
+ };
61
+ setInterval(() => this.cleanExpiredCache(), 3600000);
62
+ }
63
+ /**
64
+ * Cria um novo template com as configurações especificadas
65
+ */
66
+ createTemplate(themeType, variant, config, options) {
67
+ // Validar configuração se necessário
68
+ if (this.options.validateConfig || (options === null || options === void 0 ? void 0 : options.validateConfig)) {
69
+ this.validateTemplateConfig(config);
70
+ }
71
+ // Gerar ID único para o template
72
+ const templateId = this.generateTemplateId(themeType, variant, config);
73
+ // ckeck cache
74
+ if ((this.options.cache || (options === null || options === void 0 ? void 0 : options.cache)) && this.templateCache.has(templateId)) {
75
+ const cached = this.templateCache.get(templateId);
76
+ cached.hits++;
77
+ return cached.template;
78
+ }
79
+ // Criar template
80
+ const template = template_factory_1.TemplateFactory.createTemplate(themeType, variant, config);
81
+ // add métodos adicionais ao template
82
+ const enhancedTemplate = this.enhanceTemplate(template, templateId);
83
+ // store em cache
84
+ if (this.options.cache || (options === null || options === void 0 ? void 0 : options.cache)) {
85
+ const ttl = ((options === null || options === void 0 ? void 0 : options.cacheTTL) || this.options.cacheTTL || this.defaultTTL) * 1000;
86
+ this.templateCache.set(templateId, {
87
+ template: enhancedTemplate,
88
+ createdAt: new Date(),
89
+ expiresAt: new Date(Date.now() + ttl),
90
+ hits: 1
91
+ });
92
+ }
93
+ // reg no histórico
94
+ this.addToHistory(templateId, enhancedTemplate);
95
+ return enhancedTemplate;
96
+ }
97
+ /**
98
+ * Cria um template a partir de um template existente (clone)
99
+ */
100
+ cloneTemplate(template, modifications) {
101
+ const newConfig = {
102
+ ...template.config,
103
+ ...modifications
104
+ };
105
+ return this.createTemplate(template.theme, template.variant, newConfig);
106
+ }
107
+ /**
108
+ * Renderiza um template com os dados fornecidos
109
+ */
110
+ async renderTemplate(template, data, options) {
111
+ try {
112
+ // add metadados para preview se necessário
113
+ const renderData = {
114
+ ...data,
115
+ _meta: {
116
+ isPreview: (options === null || options === void 0 ? void 0 : options.preview) || this.options.preview,
117
+ renderDate: new Date().toISOString(),
118
+ templateName: template.name,
119
+ theme: template.theme,
120
+ variant: template.variant
121
+ }
122
+ };
123
+ // render template
124
+ let html = await template.render(renderData);
125
+ // Minificar HTML se necessário
126
+ if (this.options.minify) {
127
+ html = this.minifyHtml(html);
128
+ }
129
+ console.log(`Template ${template.name} rendered successfully.`);
130
+ console.log(`Render metadata:`, renderData._meta);
131
+ console.log(html);
132
+ return html;
133
+ }
134
+ catch (error) {
135
+ throw new Error(`Failed to render template: ${error}`);
136
+ }
137
+ }
138
+ /**
139
+ * Pré-visualiza um template sem enviar email
140
+ */
141
+ async previewTemplate(themeType, variant, config, data) {
142
+ const template = this.createTemplate(themeType, variant, config, { cache: false });
143
+ return this.renderTemplate(template, data, { preview: true });
144
+ }
145
+ /**
146
+ * Obtém informações sobre um tema específico
147
+ */
148
+ getThemeInfo(themeType) {
149
+ const info = template_factory_1.TemplateFactory.getThemeInfo(themeType);
150
+ const theme = template_factory_1.TemplateFactory.getTheme(themeType);
151
+ return {
152
+ ...info,
153
+ availableVariants: ['light', 'dark'],
154
+ defaultConfig: this.getDefaultConfigForTheme(themeType, theme)
155
+ };
156
+ }
157
+ /**
158
+ * Lista todos os temas disponíveis
159
+ */
160
+ listThemes() {
161
+ return template_factory_1.TemplateFactory.listThemes();
162
+ }
163
+ /**
164
+ * Obtém estatísticas de uso dos templates
165
+ */
166
+ getTemplateStats() {
167
+ const templatesByTheme = {
168
+ [theme_enum_1.ThemeType.SYSTEM]: 0,
169
+ [theme_enum_1.ThemeType.MONOKAI]: 0,
170
+ [theme_enum_1.ThemeType.MODERN]: 0,
171
+ [theme_enum_1.ThemeType.CORPORATE]: 0,
172
+ [theme_enum_1.ThemeType.MINIMAL]: 0
173
+ };
174
+ let totalHits = 0;
175
+ for (const cache of this.templateCache.values()) {
176
+ const theme = cache.template.theme;
177
+ templatesByTheme[theme]++;
178
+ totalHits += cache.hits;
179
+ }
180
+ const totalCached = this.templateCache.size;
181
+ const totalInHistory = this.templateHistory.size;
182
+ return {
183
+ totalTemplates: totalInHistory,
184
+ cachedTemplates: totalCached,
185
+ totalHits,
186
+ templatesByTheme,
187
+ cacheHitRate: totalCached > 0 ? (totalHits / totalCached) * 100 : 0
188
+ };
189
+ }
190
+ /**
191
+ * Limpa o cache de templates
192
+ */
193
+ clearCache(themeType) {
194
+ let cleared = 0;
195
+ if (themeType) {
196
+ for (const [id, cache] of this.templateCache.entries()) {
197
+ if (cache.template.theme === themeType) {
198
+ this.templateCache.delete(id);
199
+ cleared++;
200
+ }
201
+ }
202
+ }
203
+ else {
204
+ cleared = this.templateCache.size;
205
+ this.templateCache.clear();
206
+ }
207
+ return cleared;
208
+ }
209
+ /**
210
+ * Obtém histórico de versões de um template
211
+ */
212
+ getTemplateHistory(templateId) {
213
+ return this.templateHistory.get(templateId) || [];
214
+ }
215
+ /**
216
+ * Restaura uma versão anterior do template
217
+ */
218
+ restoreTemplateVersion(templateId, versionIndex) {
219
+ const history = this.templateHistory.get(templateId);
220
+ if (!history || versionIndex >= history.length) {
221
+ return null;
222
+ }
223
+ const oldTemplate = history[versionIndex];
224
+ return this.createTemplate(oldTemplate.theme, oldTemplate.variant, oldTemplate.config, { cache: false });
225
+ }
226
+ /**
227
+ * Valida a configuração do template
228
+ */
229
+ validateTemplateConfig(config) {
230
+ var _a, _b, _c;
231
+ const errors = [];
232
+ // Validar header
233
+ if (((_a = config.header) === null || _a === void 0 ? void 0 : _a.show) && ((_b = config.header) === null || _b === void 0 ? void 0 : _b.logo)) {
234
+ if (config.header.logo.type === 'image' && !config.header.logo.imageUrl) {
235
+ errors.push('Logo image requires imageUrl');
236
+ }
237
+ if (config.header.logo.type === 'text' && !config.header.logo.text) {
238
+ errors.push('Logo text requires text content');
239
+ }
240
+ }
241
+ // Validar footer
242
+ if ((_c = config.footer) === null || _c === void 0 ? void 0 : _c.show) {
243
+ if (config.footer.links) {
244
+ for (const link of config.footer.links) {
245
+ if (!link.text || !link.url) {
246
+ errors.push('Footer links require both text and url');
247
+ }
248
+ }
249
+ }
250
+ }
251
+ // Validar layout
252
+ const validLayouts = ['full', 'minimal'];
253
+ if (config.layout && !validLayouts.includes(config.layout)) {
254
+ errors.push(`Invalid layout: ${config.layout}. Must be one of: ${validLayouts.join(', ')}`);
255
+ }
256
+ // Validar spacing
257
+ const validSpacing = ['compact', 'normal', 'relaxed'];
258
+ if (config.spacing && !validSpacing.includes(config.spacing)) {
259
+ errors.push(`Invalid spacing: ${config.spacing}. Must be one of: ${validSpacing.join(', ')}`);
260
+ }
261
+ // Validar borderRadius
262
+ const validBorderRadius = ['none', 'small', 'medium', 'large'];
263
+ if (config.borderRadius && !validBorderRadius.includes(config.borderRadius)) {
264
+ errors.push(`Invalid borderRadius: ${config.borderRadius}. Must be one of: ${validBorderRadius.join(', ')}`);
265
+ }
266
+ if (errors.length > 0) {
267
+ throw new Error(`Template configuration validation failed:\n${errors.join('\n')}`);
268
+ }
269
+ }
270
+ /**
271
+ * Gera um ID único para o template baseado na configuração
272
+ */
273
+ generateTemplateId(themeType, variant, config) {
274
+ const configString = JSON.stringify({
275
+ theme: themeType,
276
+ variant,
277
+ config
278
+ });
279
+ // Hash simples para ID
280
+ let hash = 0;
281
+ for (let i = 0; i < configString.length; i++) {
282
+ const char = configString.charCodeAt(i);
283
+ hash = ((hash << 5) - hash) + char;
284
+ hash = hash & hash;
285
+ }
286
+ return `${themeType}_${variant}_${Math.abs(hash)}`;
287
+ }
288
+ /**
289
+ * Adiciona funcionalidades extras ao template
290
+ */
291
+ enhanceTemplate(template, templateId) {
292
+ const enhanced = { ...template };
293
+ // add método para obter versão
294
+ enhanced.getVersion = () => {
295
+ const history = this.templateHistory.get(templateId);
296
+ return history ? history.length : 1;
297
+ };
298
+ // add método para obter ID
299
+ enhanced.getId = () => templateId;
300
+ // add método para clonar
301
+ enhanced.clone = (modifications) => {
302
+ return this.cloneTemplate(enhanced, modifications);
303
+ };
304
+ return enhanced;
305
+ }
306
+ /**
307
+ * Adiciona template ao histórico
308
+ */
309
+ addToHistory(templateId, template) {
310
+ if (!this.templateHistory.has(templateId)) {
311
+ this.templateHistory.set(templateId, []);
312
+ }
313
+ const history = this.templateHistory.get(templateId);
314
+ history.push(template);
315
+ // Manter apenas últimas 10 versões
316
+ if (history.length > 10) {
317
+ history.shift();
318
+ }
319
+ }
320
+ /**
321
+ * Remove templates expirados do cache
322
+ */
323
+ cleanExpiredCache() {
324
+ const now = Date.now();
325
+ for (const [id, cache] of this.templateCache.entries()) {
326
+ if (cache.expiresAt.getTime() < now) {
327
+ this.templateCache.delete(id);
328
+ }
329
+ }
330
+ }
331
+ /**
332
+ * Minifica o HTML para reduzir tamanho
333
+ */
334
+ minifyHtml(html) {
335
+ return html
336
+ .replace(/\s+/g, ' ')
337
+ .replace(/>\s+</g, '><')
338
+ .replace(/<!--.*?-->/g, '')
339
+ .trim();
340
+ }
341
+ /**
342
+ * Obtém configuração padrão para um tema
343
+ */
344
+ getDefaultConfigForTheme(themeType, theme) {
345
+ const baseConfig = {
346
+ header: {
347
+ show: true,
348
+ logo: {
349
+ type: 'text',
350
+ text: 'MyApp',
351
+ size: 'medium'
352
+ }
353
+ },
354
+ footer: {
355
+ show: true,
356
+ copyrightText: `© ${new Date().getFullYear()} MyApp. All rights reserved.`
357
+ },
358
+ layout: 'full',
359
+ spacing: 'normal',
360
+ borderRadius: 'medium'
361
+ };
362
+ // Ajustes específicos por tema
363
+ switch (themeType) {
364
+ case theme_enum_1.ThemeType.MONOKAI:
365
+ return {
366
+ ...baseConfig,
367
+ borderRadius: 'small',
368
+ spacing: 'normal'
369
+ };
370
+ case theme_enum_1.ThemeType.MODERN:
371
+ return {
372
+ ...baseConfig,
373
+ borderRadius: 'large',
374
+ spacing: 'relaxed'
375
+ };
376
+ case theme_enum_1.ThemeType.CORPORATE:
377
+ return {
378
+ ...baseConfig,
379
+ borderRadius: 'small',
380
+ spacing: 'normal'
381
+ };
382
+ case theme_enum_1.ThemeType.MINIMAL:
383
+ return {
384
+ ...baseConfig,
385
+ borderRadius: 'none',
386
+ spacing: 'relaxed'
387
+ };
388
+ default:
389
+ return baseConfig;
390
+ }
391
+ }
392
+ /**
393
+ * Pré-carrega templates no cache
394
+ */
395
+ async preloadTemplates(templates) {
396
+ const promises = templates.map(async ({ themeType, variant, config }) => {
397
+ this.createTemplate(themeType, variant, config);
398
+ });
399
+ await Promise.all(promises);
400
+ }
401
+ /**
402
+ * Exporta template para formato JSON
403
+ */
404
+ exportTemplate(template) {
405
+ var _a, _b;
406
+ return JSON.stringify({
407
+ name: template.name,
408
+ theme: template.theme,
409
+ variant: template.variant,
410
+ config: template.config,
411
+ exportedAt: new Date().toISOString(),
412
+ version: ((_b = (_a = template).getVersion) === null || _b === void 0 ? void 0 : _b.call(_a)) || 1
413
+ }, null, 2);
414
+ }
415
+ /**
416
+ * Importa template de formato JSON
417
+ */
418
+ importTemplate(jsonData) {
419
+ try {
420
+ const data = JSON.parse(jsonData);
421
+ if (!data.theme || !data.variant || !data.config) {
422
+ throw new Error('Invalid template data: missing required fields');
423
+ }
424
+ return this.createTemplate(data.theme, data.variant, data.config);
425
+ }
426
+ catch (error) {
427
+ throw new Error(`Failed to import template: ${error}`);
428
+ }
429
+ }
430
+ /**
431
+ * Gera um template de exemplo para demonstração
432
+ */
433
+ generateExampleTemplate(themeType = theme_enum_1.ThemeType.MODERN, variant = 'light') {
434
+ const exampleConfig = {
435
+ header: {
436
+ show: true,
437
+ logo: {
438
+ type: 'text',
439
+ text: 'ExampleApp',
440
+ size: 'medium'
441
+ },
442
+ backgroundColor: variant === 'light' ? '#ffffff' : '#1a1a1a',
443
+ textColor: variant === 'light' ? '#111827' : '#f9fafb'
444
+ },
445
+ footer: {
446
+ show: true,
447
+ links: [
448
+ { text: 'Privacy Policy', url: 'https://example.com/privacy' },
449
+ { text: 'Terms of Service', url: 'https://example.com/terms' },
450
+ { text: 'Contact', url: 'https://example.com/contact' }
451
+ ],
452
+ socialLinks: [
453
+ { platform: 'twitter', url: 'https://twitter.com/example' },
454
+ { platform: 'github', url: 'https://github.com/example' },
455
+ { platform: 'linkedin', url: 'https://linkedin.com/company/example' }
456
+ ],
457
+ copyrightText: `© ${new Date().getFullYear()} ExampleApp. All rights reserved.`,
458
+ unsubscribeText: 'Unsubscribe',
459
+ backgroundColor: variant === 'light' ? '#f9fafb' : '#111827',
460
+ textColor: variant === 'light' ? '#6b7280' : '#9ca3af'
461
+ },
462
+ layout: 'full',
463
+ spacing: 'normal',
464
+ borderRadius: themeType === theme_enum_1.ThemeType.MINIMAL ? 'none' : 'medium'
465
+ };
466
+ return this.createTemplate(themeType, variant, exampleConfig);
467
+ }
468
+ /**
469
+ * Valida se um template é válido
470
+ */
471
+ isValidTemplate(template) {
472
+ try {
473
+ // Verificar propriedades obrigatórias
474
+ if (!template || typeof template !== 'object')
475
+ return false;
476
+ if (!template.name || typeof template.name !== 'string')
477
+ return false;
478
+ if (!template.theme || !Object.values(theme_enum_1.ThemeType).includes(template.theme))
479
+ return false;
480
+ if (!template.variant || !['light', 'dark'].includes(template.variant))
481
+ return false;
482
+ if (!template.config || typeof template.config !== 'object')
483
+ return false;
484
+ if (typeof template.render !== 'function')
485
+ return false;
486
+ return true;
487
+ }
488
+ catch {
489
+ return false;
490
+ }
491
+ }
492
+ /**
493
+ * Obtém estatísticas detalhadas de um template específico
494
+ */
495
+ getTemplateDetails(templateId) {
496
+ const cacheInfo = this.templateCache.get(templateId) || null;
497
+ const history = this.templateHistory.get(templateId) || [];
498
+ const usageCount = (cacheInfo === null || cacheInfo === void 0 ? void 0 : cacheInfo.hits) || 0;
499
+ return {
500
+ template: (cacheInfo === null || cacheInfo === void 0 ? void 0 : cacheInfo.template) || history[history.length - 1] || null,
501
+ history,
502
+ cacheInfo,
503
+ usageCount
504
+ };
505
+ }
506
+ }
507
+ exports.TemplateService = TemplateService;
@@ -0,0 +1,23 @@
1
+ import { ITemplateConfig } from '../../core/interfaces/template.interface';
2
+ import { ITheme } from '../themes/theme.interface';
3
+ export declare class TemplateBuilder {
4
+ private template;
5
+ private theme;
6
+ private config;
7
+ private variant;
8
+ constructor(theme: ITheme, config: ITemplateConfig, variant: 'light' | 'dark');
9
+ buildHeader(content?: string): this;
10
+ private buildLogo;
11
+ buildBody(content: string): this;
12
+ private formatCorporateContent;
13
+ private formatMinimalContent;
14
+ private highlightCode;
15
+ private applySyntaxHighlighting;
16
+ buildButton(text: string, url: string, variant?: 'primary' | 'secondary'): this;
17
+ buildFooter(): this;
18
+ private buildFooterLinks;
19
+ private buildSocialLinks;
20
+ private getSocialIcon;
21
+ build(): string;
22
+ }
23
+ //# sourceMappingURL=template-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-builder.d.ts","sourceRoot":"","sources":["../../../src/templates/base/template-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,0CAA0C,CAAC;AAC3E,OAAO,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAC;AAMnD,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAc;IAC9B,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,OAAO,CAAmB;gBAEtB,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM;IAM7E,WAAW,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI;IAoFnC,OAAO,CAAC,SAAS;IA8EjB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAwEhC,OAAO,CAAC,sBAAsB;IAuB9B,OAAO,CAAC,oBAAoB;IA0B5B,OAAO,CAAC,aAAa;IAiBrB,OAAO,CAAC,uBAAuB;IAuB/B,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,SAAS,GAAG,WAAuB,GAAG,IAAI;IAsG1F,WAAW,IAAI,IAAI;IA0GnB,OAAO,CAAC,gBAAgB;IA8DxB,OAAO,CAAC,gBAAgB;IAgCxB,OAAO,CAAC,aAAa;IAsBrB,KAAK,IAAI,MAAM;CA6DhB"}