@endora-commerce/mod-transactional-emails 0.100.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.
- package/LICENSE +21 -0
- package/README.md +63 -0
- package/dist/admin/api/transactional-emails-client.d.ts +43 -0
- package/dist/admin/api/transactional-emails-client.d.ts.map +1 -0
- package/dist/admin/api/transactional-emails-client.js +47 -0
- package/dist/admin/api/transactional-emails-client.js.map +1 -0
- package/dist/admin/components/BrandingPanel.d.ts +10 -0
- package/dist/admin/components/BrandingPanel.d.ts.map +1 -0
- package/dist/admin/components/BrandingPanel.js +58 -0
- package/dist/admin/components/BrandingPanel.js.map +1 -0
- package/dist/admin/components/EmailEditorPane.d.ts +7 -0
- package/dist/admin/components/EmailEditorPane.d.ts.map +1 -0
- package/dist/admin/components/EmailEditorPane.js +48 -0
- package/dist/admin/components/EmailEditorPane.js.map +1 -0
- package/dist/admin/index.d.ts +46 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +92 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/EmailBlockEditorPage.d.ts +16 -0
- package/dist/admin/pages/EmailBlockEditorPage.d.ts.map +1 -0
- package/dist/admin/pages/EmailBlockEditorPage.js +19 -0
- package/dist/admin/pages/EmailBlockEditorPage.js.map +1 -0
- package/dist/admin/pages/EmailBlocksPage.d.ts +8 -0
- package/dist/admin/pages/EmailBlocksPage.d.ts.map +1 -0
- package/dist/admin/pages/EmailBlocksPage.js +55 -0
- package/dist/admin/pages/EmailBlocksPage.js.map +1 -0
- package/dist/admin/pages/EmailEditor.d.ts +8 -0
- package/dist/admin/pages/EmailEditor.d.ts.map +1 -0
- package/dist/admin/pages/EmailEditor.js +126 -0
- package/dist/admin/pages/EmailEditor.js.map +1 -0
- package/dist/admin/pages/EmailFragmentEditor.d.ts +9 -0
- package/dist/admin/pages/EmailFragmentEditor.d.ts.map +1 -0
- package/dist/admin/pages/EmailFragmentEditor.js +93 -0
- package/dist/admin/pages/EmailFragmentEditor.js.map +1 -0
- package/dist/admin/pages/EmailTemplateEditorPage.d.ts +12 -0
- package/dist/admin/pages/EmailTemplateEditorPage.d.ts.map +1 -0
- package/dist/admin/pages/EmailTemplateEditorPage.js +15 -0
- package/dist/admin/pages/EmailTemplateEditorPage.js.map +1 -0
- package/dist/admin/pages/EmailTemplatesPage.d.ts +8 -0
- package/dist/admin/pages/EmailTemplatesPage.d.ts.map +1 -0
- package/dist/admin/pages/EmailTemplatesPage.js +55 -0
- package/dist/admin/pages/EmailTemplatesPage.js.map +1 -0
- package/dist/admin/pages/EmailsList.d.ts +8 -0
- package/dist/admin/pages/EmailsList.d.ts.map +1 -0
- package/dist/admin/pages/EmailsList.js +85 -0
- package/dist/admin/pages/EmailsList.js.map +1 -0
- package/dist/backend/commands/email-activation.commands.d.ts +42 -0
- package/dist/backend/commands/email-activation.commands.d.ts.map +1 -0
- package/dist/backend/commands/email-activation.commands.js +51 -0
- package/dist/backend/commands/email-activation.commands.js.map +1 -0
- package/dist/backend/entities/email-block-sales-channel.entity.d.ts +11 -0
- package/dist/backend/entities/email-block-sales-channel.entity.d.ts.map +1 -0
- package/dist/backend/entities/email-block-sales-channel.entity.js +39 -0
- package/dist/backend/entities/email-block-sales-channel.entity.js.map +1 -0
- package/dist/backend/entities/email-block.entity.d.ts +24 -0
- package/dist/backend/entities/email-block.entity.d.ts.map +1 -0
- package/dist/backend/entities/email-block.entity.js +85 -0
- package/dist/backend/entities/email-block.entity.js.map +1 -0
- package/dist/backend/entities/email-template-sales-channel.entity.d.ts +10 -0
- package/dist/backend/entities/email-template-sales-channel.entity.d.ts.map +1 -0
- package/dist/backend/entities/email-template-sales-channel.entity.js +38 -0
- package/dist/backend/entities/email-template-sales-channel.entity.js.map +1 -0
- package/dist/backend/entities/email-template.entity.d.ts +19 -0
- package/dist/backend/entities/email-template.entity.d.ts.map +1 -0
- package/dist/backend/entities/email-template.entity.js +76 -0
- package/dist/backend/entities/email-template.entity.js.map +1 -0
- package/dist/backend/entities/transactional-email-content.entity.d.ts +25 -0
- package/dist/backend/entities/transactional-email-content.entity.d.ts.map +1 -0
- package/dist/backend/entities/transactional-email-content.entity.js +77 -0
- package/dist/backend/entities/transactional-email-content.entity.js.map +1 -0
- package/dist/backend/entities/transactional-email.entity.d.ts +32 -0
- package/dist/backend/entities/transactional-email.entity.d.ts.map +1 -0
- package/dist/backend/entities/transactional-email.entity.js +100 -0
- package/dist/backend/entities/transactional-email.entity.js.map +1 -0
- package/dist/backend/index.d.ts +105 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +126 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/plugin.d.ts +49 -0
- package/dist/backend/plugin.d.ts.map +1 -0
- package/dist/backend/plugin.js +54 -0
- package/dist/backend/plugin.js.map +1 -0
- package/dist/backend/routes.admin.d.ts +23 -0
- package/dist/backend/routes.admin.d.ts.map +1 -0
- package/dist/backend/routes.admin.js +133 -0
- package/dist/backend/routes.admin.js.map +1 -0
- package/dist/backend/services/branding.service.d.ts +63 -0
- package/dist/backend/services/branding.service.d.ts.map +1 -0
- package/dist/backend/services/branding.service.js +119 -0
- package/dist/backend/services/branding.service.js.map +1 -0
- package/dist/backend/services/content-resolver.d.ts +28 -0
- package/dist/backend/services/content-resolver.d.ts.map +1 -0
- package/dist/backend/services/content-resolver.js +64 -0
- package/dist/backend/services/content-resolver.js.map +1 -0
- package/dist/backend/services/email-block.service.d.ts +25 -0
- package/dist/backend/services/email-block.service.d.ts.map +1 -0
- package/dist/backend/services/email-block.service.js +162 -0
- package/dist/backend/services/email-block.service.js.map +1 -0
- package/dist/backend/services/email-builder-registry.d.ts +9 -0
- package/dist/backend/services/email-builder-registry.d.ts.map +1 -0
- package/dist/backend/services/email-builder-registry.js +14 -0
- package/dist/backend/services/email-builder-registry.js.map +1 -0
- package/dist/backend/services/email-defaults-registry.d.ts +63 -0
- package/dist/backend/services/email-defaults-registry.d.ts.map +1 -0
- package/dist/backend/services/email-defaults-registry.js +60 -0
- package/dist/backend/services/email-defaults-registry.js.map +1 -0
- package/dist/backend/services/email-template.service.d.ts +23 -0
- package/dist/backend/services/email-template.service.d.ts.map +1 -0
- package/dist/backend/services/email-template.service.js +155 -0
- package/dist/backend/services/email-template.service.js.map +1 -0
- package/dist/backend/services/embed-resolver.d.ts +21 -0
- package/dist/backend/services/embed-resolver.d.ts.map +1 -0
- package/dist/backend/services/embed-resolver.js +68 -0
- package/dist/backend/services/embed-resolver.js.map +1 -0
- package/dist/backend/services/manifest-reconciler.d.ts +28 -0
- package/dist/backend/services/manifest-reconciler.d.ts.map +1 -0
- package/dist/backend/services/manifest-reconciler.js +98 -0
- package/dist/backend/services/manifest-reconciler.js.map +1 -0
- package/dist/backend/services/template-email.d.ts +26 -0
- package/dist/backend/services/template-email.d.ts.map +1 -0
- package/dist/backend/services/template-email.js +69 -0
- package/dist/backend/services/template-email.js.map +1 -0
- package/dist/backend/services/transactional-email.service.d.ts +138 -0
- package/dist/backend/services/transactional-email.service.d.ts.map +1 -0
- package/dist/backend/services/transactional-email.service.js +410 -0
- package/dist/backend/services/transactional-email.service.js.map +1 -0
- package/dist/manifest.d.ts +195 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +367 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260629T113442_transactional_emails_init.d.ts +6 -0
- package/dist/migrations/20260629T113442_transactional_emails_init.d.ts.map +1 -0
- package/dist/migrations/20260629T113442_transactional_emails_init.js +133 -0
- package/dist/migrations/20260629T113442_transactional_emails_init.js.map +1 -0
- package/dist/migrations/20260801T111001_transactional_emails_email_defaults_reseed.d.ts +6 -0
- package/dist/migrations/20260801T111001_transactional_emails_email_defaults_reseed.d.ts.map +1 -0
- package/dist/migrations/20260801T111001_transactional_emails_email_defaults_reseed.js +49 -0
- package/dist/migrations/20260801T111001_transactional_emails_email_defaults_reseed.js.map +1 -0
- package/dist/migrations/20260903T101748_transactional_emails_namespace_block_names.d.ts +6 -0
- package/dist/migrations/20260903T101748_transactional_emails_namespace_block_names.d.ts.map +1 -0
- package/dist/migrations/20260903T101748_transactional_emails_namespace_block_names.js +60 -0
- package/dist/migrations/20260903T101748_transactional_emails_namespace_block_names.js.map +1 -0
- package/dist/migrations/index.d.ts +29 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +33 -0
- package/dist/migrations/index.js.map +1 -0
- package/docs/transactional-emails.md +176 -0
- package/i18n/en.json +92 -0
- package/i18n/pl.json +92 -0
- package/package.json +113 -0
- package/tailwind.css +14 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TransactionalEmailService — feature 047 (US1/US2/US4).
|
|
3
|
+
*
|
|
4
|
+
* Implements the TransactionalEmailSender port: resolve effective subject +
|
|
5
|
+
* content (channel -> global -> default), resolve embeds, render email-safe HTML
|
|
6
|
+
* + plain text, substitute variables (Magento-style directives, HTML-escaped in
|
|
7
|
+
* HTML), and send through the shared Mailer. Also powers the admin list/detail,
|
|
8
|
+
* save/reset content, and preview endpoints.
|
|
9
|
+
*/
|
|
10
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
11
|
+
import { type PreviewEmailResponse, type ResolvedEmailContent, type TransactionalEmailDetail, type TransactionalEmailSender, type TransactionalEmailSendInput, type TransactionalEmailSummary, type TransactionalSendOutcome } from '@endora-commerce/contracts';
|
|
12
|
+
import type { PuckDataTree } from '@endora-commerce/email-components/schema/envelope';
|
|
13
|
+
import type { EmailDeliveryRecorder, EmailMailerPort } from '@endora-commerce/contracts';
|
|
14
|
+
import type { AuditPort } from '@endora-commerce/platform/kernel';
|
|
15
|
+
import type { ContentResolver } from './content-resolver.js';
|
|
16
|
+
import type { BrandingService } from './branding.service.js';
|
|
17
|
+
import type { EmbedResolver } from './embed-resolver.js';
|
|
18
|
+
import type { EmailDefaultsRegistry } from './email-defaults-registry.js';
|
|
19
|
+
export interface SaveContentInput {
|
|
20
|
+
subject: string;
|
|
21
|
+
content: PuckDataTree;
|
|
22
|
+
expectedVersion?: number;
|
|
23
|
+
}
|
|
24
|
+
export interface RenderResult {
|
|
25
|
+
subject: string;
|
|
26
|
+
html: string;
|
|
27
|
+
text: string;
|
|
28
|
+
}
|
|
29
|
+
export interface TransactionalEmailServiceDeps {
|
|
30
|
+
emFactory: () => EntityManager;
|
|
31
|
+
contentResolver: ContentResolver;
|
|
32
|
+
branding: BrandingService;
|
|
33
|
+
embeds: EmbedResolver;
|
|
34
|
+
/**
|
|
35
|
+
* The owner ledger the admin projection reads for the per-email protection
|
|
36
|
+
* (issue #89). Required rather than optional: an absent registry would make
|
|
37
|
+
* every email report itself deactivatable, which is the one wrong answer —
|
|
38
|
+
* the admin would offer a switch for `email_verification` and the write path
|
|
39
|
+
* would refuse it.
|
|
40
|
+
*/
|
|
41
|
+
defaults: EmailDefaultsRegistry;
|
|
42
|
+
/**
|
|
43
|
+
* The transport, named by `email`'s published contract since feature 075's
|
|
44
|
+
* Phase C. `email` registers it ungated and declares itself
|
|
45
|
+
* non-deactivatable, so this edge has no absent state to fail closed onto —
|
|
46
|
+
* the `undefined` below is a composition that wired no transport at all,
|
|
47
|
+
* which `send` reports as `no_transport`.
|
|
48
|
+
*/
|
|
49
|
+
mailer?: EmailMailerPort;
|
|
50
|
+
/**
|
|
51
|
+
* Where the three outcomes decided **before** the transport are recorded
|
|
52
|
+
* (D-59). Optional: a composition with no recorder loses the row, not the
|
|
53
|
+
* send, which is the same tolerance the recorder itself states.
|
|
54
|
+
*/
|
|
55
|
+
deliveryRecorder?: EmailDeliveryRecorder;
|
|
56
|
+
auditLog?: AuditPort;
|
|
57
|
+
}
|
|
58
|
+
export declare class TransactionalEmailService implements TransactionalEmailSender {
|
|
59
|
+
private readonly emFactory;
|
|
60
|
+
private readonly contentResolver;
|
|
61
|
+
private readonly branding;
|
|
62
|
+
private readonly embeds;
|
|
63
|
+
private readonly defaults;
|
|
64
|
+
private readonly mailer;
|
|
65
|
+
private readonly deliveryRecorder;
|
|
66
|
+
private readonly auditLog;
|
|
67
|
+
constructor(deps: TransactionalEmailServiceDeps);
|
|
68
|
+
/**
|
|
69
|
+
* Delivers the email and reports what happened. The three non-`sent` outcomes
|
|
70
|
+
* used to be one silent `return`, which left every caller unable to tell an
|
|
71
|
+
* operator's "off" from a code with no template — and suppressing its own
|
|
72
|
+
* fallback for both.
|
|
73
|
+
*
|
|
74
|
+
* Since D-59 each of those three also leaves a **row**. They are the outcomes
|
|
75
|
+
* decided before the transport is reached, so the record `RecordingMailer`
|
|
76
|
+
* writes never gets the chance — and one of them, `deactivated`, is the whole
|
|
77
|
+
* reason the record has to tell a deliberate configuration from an outage.
|
|
78
|
+
* A delivered message is deliberately *not* recorded here: the transport
|
|
79
|
+
* records it, so one send stays one row.
|
|
80
|
+
*/
|
|
81
|
+
send(input: TransactionalEmailSendInput): Promise<TransactionalSendOutcome>;
|
|
82
|
+
/**
|
|
83
|
+
* One row for a message this layer decided not to send.
|
|
84
|
+
*
|
|
85
|
+
* No `catch`: the recorder answers `null` for a row it could not write and
|
|
86
|
+
* never throws (its own contract), so there is nothing here to contain — and
|
|
87
|
+
* a `catch` around a collaborator another module owns is how a presence
|
|
88
|
+
* answer stops travelling.
|
|
89
|
+
*/
|
|
90
|
+
private recordNotSent;
|
|
91
|
+
private renderWith;
|
|
92
|
+
/**
|
|
93
|
+
* Whether an operator may switch this one email off (issue #89).
|
|
94
|
+
*
|
|
95
|
+
* Resolved from the registry on every read rather than persisted on the row:
|
|
96
|
+
* the declaration is shipped code owned by the sending module, so a stored
|
|
97
|
+
* copy would go stale the moment that module changed its mind, and the
|
|
98
|
+
* reconciler would have one more column to fight over.
|
|
99
|
+
*/
|
|
100
|
+
private protectionOf;
|
|
101
|
+
private summarize;
|
|
102
|
+
list(): Promise<TransactionalEmailSummary[]>;
|
|
103
|
+
/**
|
|
104
|
+
* One email's summary, re-read from the database.
|
|
105
|
+
*
|
|
106
|
+
* The activation route answers with this rather than with what it just
|
|
107
|
+
* wrote — the flip runs on the Command Bus's own forked EM, so the only
|
|
108
|
+
* honest report of the committed state is a fresh read. Hence `refresh`
|
|
109
|
+
* rather than `em.clear()`: the request-scoped identity map may hold entities
|
|
110
|
+
* the auth guard put there, and discarding those to re-read one row is a
|
|
111
|
+
* wider blast radius than the question needs.
|
|
112
|
+
*/
|
|
113
|
+
summary(code: string): Promise<TransactionalEmailSummary>;
|
|
114
|
+
private loadEmailOrThrow;
|
|
115
|
+
getDetail(code: string, salesChannelId: string | null, language?: string): Promise<TransactionalEmailDetail>;
|
|
116
|
+
private validateContent;
|
|
117
|
+
saveContent(code: string, scope: {
|
|
118
|
+
salesChannelId: string | null;
|
|
119
|
+
language: string;
|
|
120
|
+
}, input: SaveContentInput, actor?: {
|
|
121
|
+
adminUserId?: string | null;
|
|
122
|
+
}): Promise<ResolvedEmailContent>;
|
|
123
|
+
resetContent(code: string, scope: {
|
|
124
|
+
salesChannelId: string | null;
|
|
125
|
+
language: string;
|
|
126
|
+
}, actor?: {
|
|
127
|
+
adminUserId?: string | null;
|
|
128
|
+
}): Promise<ResolvedEmailContent>;
|
|
129
|
+
preview(code: string, input: {
|
|
130
|
+
salesChannelId: string | null;
|
|
131
|
+
language?: string;
|
|
132
|
+
draftSubject?: string;
|
|
133
|
+
draftContent?: PuckDataTree;
|
|
134
|
+
}): Promise<PreviewEmailResponse>;
|
|
135
|
+
private sampleVariables;
|
|
136
|
+
private audit;
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=transactional-email.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transactional-email.service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/transactional-email.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TransactionalEmailService — feature 047 (US1/US2/US4).
|
|
3
|
+
*
|
|
4
|
+
* Implements the TransactionalEmailSender port: resolve effective subject +
|
|
5
|
+
* content (channel -> global -> default), resolve embeds, render email-safe HTML
|
|
6
|
+
* + plain text, substitute variables (Magento-style directives, HTML-escaped in
|
|
7
|
+
* HTML), and send through the shared Mailer. Also powers the admin list/detail,
|
|
8
|
+
* save/reset content, and preview endpoints.
|
|
9
|
+
*/
|
|
10
|
+
import { ERROR_CODES, } from '@endora-commerce/contracts';
|
|
11
|
+
import { EMAIL_SAFE_COMPONENT_NAMES } from '@endora-commerce/email-components/schema/component-types';
|
|
12
|
+
import { walkUnknownComponents } from '@endora-commerce/email-components/tree/walk-embeds';
|
|
13
|
+
import { renderEmailHtml } from '@endora-commerce/email-components/render/render-email-html';
|
|
14
|
+
import { renderEmailText } from '@endora-commerce/email-components/render/render-email-text';
|
|
15
|
+
import { renderDirectives } from '@endora-commerce/email-components/directives/directive-engine';
|
|
16
|
+
import { HttpError } from '@endora-commerce/platform/http';
|
|
17
|
+
import { TransactionalEmail } from '../entities/transactional-email.entity.js';
|
|
18
|
+
import { TransactionalEmailContent } from '../entities/transactional-email-content.entity.js';
|
|
19
|
+
import {} from './content-resolver.js';
|
|
20
|
+
import {} from './branding.service.js';
|
|
21
|
+
const KNOWN_COMPONENTS = new Set(EMAIL_SAFE_COMPONENT_NAMES);
|
|
22
|
+
/**
|
|
23
|
+
* A missing transport is a deployment fact, not a per-message one: it holds for
|
|
24
|
+
* every send until someone fixes the composition. The guard is deliberately
|
|
25
|
+
* **per process** (module scope, never reset) so a misconfigured deployment
|
|
26
|
+
* gets one line instead of one per email.
|
|
27
|
+
*/
|
|
28
|
+
let noTransportWarned = false;
|
|
29
|
+
export class TransactionalEmailService {
|
|
30
|
+
emFactory;
|
|
31
|
+
contentResolver;
|
|
32
|
+
branding;
|
|
33
|
+
embeds;
|
|
34
|
+
defaults;
|
|
35
|
+
mailer;
|
|
36
|
+
deliveryRecorder;
|
|
37
|
+
auditLog;
|
|
38
|
+
constructor(deps) {
|
|
39
|
+
this.emFactory = deps.emFactory;
|
|
40
|
+
this.contentResolver = deps.contentResolver;
|
|
41
|
+
this.branding = deps.branding;
|
|
42
|
+
this.embeds = deps.embeds;
|
|
43
|
+
this.defaults = deps.defaults;
|
|
44
|
+
this.mailer = deps.mailer;
|
|
45
|
+
this.deliveryRecorder = deps.deliveryRecorder;
|
|
46
|
+
this.auditLog = deps.auditLog;
|
|
47
|
+
}
|
|
48
|
+
// --- Sending (port) -----------------------------------------------------
|
|
49
|
+
/**
|
|
50
|
+
* Delivers the email and reports what happened. The three non-`sent` outcomes
|
|
51
|
+
* used to be one silent `return`, which left every caller unable to tell an
|
|
52
|
+
* operator's "off" from a code with no template — and suppressing its own
|
|
53
|
+
* fallback for both.
|
|
54
|
+
*
|
|
55
|
+
* Since D-59 each of those three also leaves a **row**. They are the outcomes
|
|
56
|
+
* decided before the transport is reached, so the record `RecordingMailer`
|
|
57
|
+
* writes never gets the chance — and one of them, `deactivated`, is the whole
|
|
58
|
+
* reason the record has to tell a deliberate configuration from an outage.
|
|
59
|
+
* A delivered message is deliberately *not* recorded here: the transport
|
|
60
|
+
* records it, so one send stays one row.
|
|
61
|
+
*/
|
|
62
|
+
async send(input) {
|
|
63
|
+
if (!this.mailer) {
|
|
64
|
+
if (!noTransportWarned) {
|
|
65
|
+
noTransportWarned = true;
|
|
66
|
+
console.warn('[transactional_emails] no mailer configured — transactional emails are not being delivered (logged once per process).');
|
|
67
|
+
}
|
|
68
|
+
await this.recordNotSent(input, 'failed', 'no_transport');
|
|
69
|
+
return { status: 'no_transport' };
|
|
70
|
+
}
|
|
71
|
+
const em = this.emFactory();
|
|
72
|
+
const email = await em.findOne(TransactionalEmail, { code: input.code });
|
|
73
|
+
// No definition: the caller may still have a legacy in-code builder for
|
|
74
|
+
// this code. Deactivated: the operator chose silence, so nothing goes out.
|
|
75
|
+
if (!email) {
|
|
76
|
+
await this.recordNotSent(input, 'failed', 'no_definition');
|
|
77
|
+
return { status: 'no_definition' };
|
|
78
|
+
}
|
|
79
|
+
if (!email.active) {
|
|
80
|
+
await this.recordNotSent(input, 'suppressed', 'deactivated');
|
|
81
|
+
return { status: 'deactivated' };
|
|
82
|
+
}
|
|
83
|
+
const fallbackLanguage = email.languages[0];
|
|
84
|
+
const resolved = await this.contentResolver.resolve(em, email, {
|
|
85
|
+
salesChannelId: input.salesChannelId,
|
|
86
|
+
language: input.language,
|
|
87
|
+
...(fallbackLanguage ? { fallbackLanguage } : {}),
|
|
88
|
+
});
|
|
89
|
+
const branding = await this.branding.resolve(input.salesChannelId);
|
|
90
|
+
const rendered = await this.renderWith(em, resolved, branding, input.variables, {
|
|
91
|
+
salesChannelId: input.salesChannelId,
|
|
92
|
+
language: resolved.language,
|
|
93
|
+
...(fallbackLanguage ? { fallbackLanguage } : {}),
|
|
94
|
+
});
|
|
95
|
+
// The transport's own answer (`sent` / `suppressed`, D-59) is deliberately
|
|
96
|
+
// not carried further, and this is the decision rather than an oversight:
|
|
97
|
+
// its one suppression reason is `duplicate_message_id`, meaning this exact
|
|
98
|
+
// message id was already accepted, so the message did go out. What
|
|
99
|
+
// `TransactionalSendOutcome` exists to tell a caller is whether it may fall
|
|
100
|
+
// back to its own in-code builder, and neither transport answer permits
|
|
101
|
+
// that. The row is not lost either — `RecordingMailer` writes the
|
|
102
|
+
// `suppressed` delivery record before returning. Widening the union here
|
|
103
|
+
// would publish a distinction no caller can act on.
|
|
104
|
+
await this.mailer.send({
|
|
105
|
+
messageId: input.messageId,
|
|
106
|
+
to: input.to,
|
|
107
|
+
subject: rendered.subject,
|
|
108
|
+
text: rendered.text,
|
|
109
|
+
html: rendered.html,
|
|
110
|
+
// What the delivery record needs and only this layer holds: the code the
|
|
111
|
+
// message was rendered from, the channel it was rendered for, and the
|
|
112
|
+
// document it delivers.
|
|
113
|
+
kind: input.code,
|
|
114
|
+
salesChannelId: input.salesChannelId,
|
|
115
|
+
...(input.document ? { document: input.document } : {}),
|
|
116
|
+
...(input.attachments ? { attachments: input.attachments } : {}),
|
|
117
|
+
...(input.meta ? { meta: input.meta } : {}),
|
|
118
|
+
});
|
|
119
|
+
return { status: 'sent' };
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* One row for a message this layer decided not to send.
|
|
123
|
+
*
|
|
124
|
+
* No `catch`: the recorder answers `null` for a row it could not write and
|
|
125
|
+
* never throws (its own contract), so there is nothing here to contain — and
|
|
126
|
+
* a `catch` around a collaborator another module owns is how a presence
|
|
127
|
+
* answer stops travelling.
|
|
128
|
+
*/
|
|
129
|
+
async recordNotSent(input, status, reason) {
|
|
130
|
+
await this.deliveryRecorder?.record({
|
|
131
|
+
messageId: input.messageId,
|
|
132
|
+
recipient: input.to,
|
|
133
|
+
kind: input.code,
|
|
134
|
+
status,
|
|
135
|
+
reason,
|
|
136
|
+
salesChannelId: input.salesChannelId,
|
|
137
|
+
...(input.document
|
|
138
|
+
? { documentType: input.document.type, documentId: input.document.id }
|
|
139
|
+
: {}),
|
|
140
|
+
...(input.meta ? { context: input.meta } : {}),
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
// --- Rendering ----------------------------------------------------------
|
|
144
|
+
async renderWith(em, resolved, branding, variables, scope) {
|
|
145
|
+
const embeds = await this.embeds.resolve(em, resolved.content, scope);
|
|
146
|
+
const ctx = {
|
|
147
|
+
...variables,
|
|
148
|
+
branding: { logoUrl: branding.logoUrl, accentColor: branding.accentColor },
|
|
149
|
+
};
|
|
150
|
+
const html = renderDirectives(renderEmailHtml(resolved.content, {
|
|
151
|
+
embeds,
|
|
152
|
+
accentColor: branding.accentColor,
|
|
153
|
+
language: scope.language,
|
|
154
|
+
}), ctx, { escape: true });
|
|
155
|
+
const text = renderDirectives(renderEmailText(resolved.content, { embeds }), ctx);
|
|
156
|
+
const subject = renderDirectives(resolved.subject, ctx);
|
|
157
|
+
return { subject, html, text };
|
|
158
|
+
}
|
|
159
|
+
// --- Admin: list / detail ----------------------------------------------
|
|
160
|
+
/**
|
|
161
|
+
* Whether an operator may switch this one email off (issue #89).
|
|
162
|
+
*
|
|
163
|
+
* Resolved from the registry on every read rather than persisted on the row:
|
|
164
|
+
* the declaration is shipped code owned by the sending module, so a stored
|
|
165
|
+
* copy would go stale the moment that module changed its mind, and the
|
|
166
|
+
* reconciler would have one more column to fight over.
|
|
167
|
+
*/
|
|
168
|
+
protectionOf(code) {
|
|
169
|
+
const reason = this.defaults.nonDeactivatableReasonOf(code);
|
|
170
|
+
return { deactivatable: reason === null, nonDeactivatableReason: reason };
|
|
171
|
+
}
|
|
172
|
+
async summarize(em, email) {
|
|
173
|
+
const globalCount = await em.count(TransactionalEmailContent, { emailId: email.id, salesChannelId: null });
|
|
174
|
+
const channelCount = await em.count(TransactionalEmailContent, { emailId: email.id, salesChannelId: { $ne: null } });
|
|
175
|
+
return {
|
|
176
|
+
code: email.code,
|
|
177
|
+
name: email.name,
|
|
178
|
+
ownerModule: email.ownerModule,
|
|
179
|
+
group: email.groupCode,
|
|
180
|
+
active: email.active,
|
|
181
|
+
languages: email.languages,
|
|
182
|
+
hasGlobalOverride: globalCount > 0,
|
|
183
|
+
hasChannelOverride: channelCount > 0,
|
|
184
|
+
...this.protectionOf(email.code),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
async list() {
|
|
188
|
+
const em = this.emFactory();
|
|
189
|
+
const emails = await em.find(TransactionalEmail, {}, { orderBy: { name: 'asc' } });
|
|
190
|
+
const out = [];
|
|
191
|
+
for (const e of emails) {
|
|
192
|
+
out.push(await this.summarize(em, e));
|
|
193
|
+
}
|
|
194
|
+
return out;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* One email's summary, re-read from the database.
|
|
198
|
+
*
|
|
199
|
+
* The activation route answers with this rather than with what it just
|
|
200
|
+
* wrote — the flip runs on the Command Bus's own forked EM, so the only
|
|
201
|
+
* honest report of the committed state is a fresh read. Hence `refresh`
|
|
202
|
+
* rather than `em.clear()`: the request-scoped identity map may hold entities
|
|
203
|
+
* the auth guard put there, and discarding those to re-read one row is a
|
|
204
|
+
* wider blast radius than the question needs.
|
|
205
|
+
*/
|
|
206
|
+
async summary(code) {
|
|
207
|
+
const em = this.emFactory();
|
|
208
|
+
const email = await em.findOne(TransactionalEmail, { code }, { refresh: true });
|
|
209
|
+
if (!email)
|
|
210
|
+
throw new HttpError(404, ERROR_CODES.NOT_FOUND, `Transactional email "${code}" not found.`);
|
|
211
|
+
return this.summarize(em, email);
|
|
212
|
+
}
|
|
213
|
+
async loadEmailOrThrow(em, code) {
|
|
214
|
+
const email = await em.findOne(TransactionalEmail, { code });
|
|
215
|
+
if (!email)
|
|
216
|
+
throw new HttpError(404, ERROR_CODES.NOT_FOUND, `Transactional email "${code}" not found.`);
|
|
217
|
+
return email;
|
|
218
|
+
}
|
|
219
|
+
async getDetail(code, salesChannelId, language) {
|
|
220
|
+
const em = this.emFactory();
|
|
221
|
+
const email = await this.loadEmailOrThrow(em, code);
|
|
222
|
+
const lang = language ?? email.languages[0] ?? 'en-US';
|
|
223
|
+
const fallbackLanguage = email.languages[0];
|
|
224
|
+
const resolved = await this.contentResolver.resolve(em, email, {
|
|
225
|
+
salesChannelId,
|
|
226
|
+
language: lang,
|
|
227
|
+
...(fallbackLanguage ? { fallbackLanguage } : {}),
|
|
228
|
+
});
|
|
229
|
+
const globalCount = await em.count(TransactionalEmailContent, { emailId: email.id, salesChannelId: null });
|
|
230
|
+
const channelCount = await em.count(TransactionalEmailContent, { emailId: email.id, salesChannelId: { $ne: null } });
|
|
231
|
+
const defaultContentTree = email.defaultContent.languages?.[lang] ?? { root: { props: {} }, content: [] };
|
|
232
|
+
const effective = {
|
|
233
|
+
subject: resolved.subject,
|
|
234
|
+
content: resolved.content,
|
|
235
|
+
source: resolved.source,
|
|
236
|
+
version: resolved.version,
|
|
237
|
+
};
|
|
238
|
+
return {
|
|
239
|
+
code: email.code,
|
|
240
|
+
name: email.name,
|
|
241
|
+
ownerModule: email.ownerModule,
|
|
242
|
+
group: email.groupCode,
|
|
243
|
+
description: email.description,
|
|
244
|
+
active: email.active,
|
|
245
|
+
languages: email.languages,
|
|
246
|
+
variables: email.variables,
|
|
247
|
+
scope: { salesChannelId, language: lang },
|
|
248
|
+
effective,
|
|
249
|
+
default: { subject: email.defaultSubject[lang] ?? '', content: defaultContentTree },
|
|
250
|
+
hasGlobalOverride: globalCount > 0,
|
|
251
|
+
hasChannelOverride: channelCount > 0,
|
|
252
|
+
...this.protectionOf(email.code),
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
// --- Admin: save / reset -----------------------------------------------
|
|
256
|
+
validateContent(content) {
|
|
257
|
+
const unknown = walkUnknownComponents(content, KNOWN_COMPONENTS);
|
|
258
|
+
if (unknown.size > 0) {
|
|
259
|
+
throw new HttpError(400, ERROR_CODES.VALIDATION_FAILED, `Email content contains non-email-safe components: ${[...unknown].join(', ')}`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
async saveContent(code, scope, input, actor = {}) {
|
|
263
|
+
if (!input.subject.trim()) {
|
|
264
|
+
throw new HttpError(422, ERROR_CODES.VALIDATION_FAILED, 'Subject is required.');
|
|
265
|
+
}
|
|
266
|
+
this.validateContent(input.content);
|
|
267
|
+
const em = this.emFactory();
|
|
268
|
+
const email = await this.loadEmailOrThrow(em, code);
|
|
269
|
+
let row = await em.findOne(TransactionalEmailContent, {
|
|
270
|
+
emailId: email.id,
|
|
271
|
+
salesChannelId: scope.salesChannelId,
|
|
272
|
+
language: scope.language,
|
|
273
|
+
});
|
|
274
|
+
if (row) {
|
|
275
|
+
if (input.expectedVersion !== undefined && input.expectedVersion !== row.version) {
|
|
276
|
+
throw new HttpError(409, ERROR_CODES.VERSION_CONFLICT, 'Email content was modified by someone else.');
|
|
277
|
+
}
|
|
278
|
+
row.subject = input.subject;
|
|
279
|
+
row.content = input.content;
|
|
280
|
+
row.version += 1;
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
row = em.create(TransactionalEmailContent, {
|
|
284
|
+
emailId: email.id,
|
|
285
|
+
salesChannelId: scope.salesChannelId,
|
|
286
|
+
language: scope.language,
|
|
287
|
+
subject: input.subject,
|
|
288
|
+
content: input.content,
|
|
289
|
+
version: 1,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
await em.flush();
|
|
293
|
+
await this.audit(actor, 'transactional_email.content.saved', email.code, { scope });
|
|
294
|
+
return { subject: row.subject, content: row.content, source: scope.salesChannelId ? 'channel' : 'global', version: row.version };
|
|
295
|
+
}
|
|
296
|
+
async resetContent(code, scope, actor = {}) {
|
|
297
|
+
const em = this.emFactory();
|
|
298
|
+
const email = await this.loadEmailOrThrow(em, code);
|
|
299
|
+
const row = await em.findOne(TransactionalEmailContent, {
|
|
300
|
+
emailId: email.id,
|
|
301
|
+
salesChannelId: scope.salesChannelId,
|
|
302
|
+
language: scope.language,
|
|
303
|
+
});
|
|
304
|
+
if (row) {
|
|
305
|
+
await em.removeAndFlush(row);
|
|
306
|
+
await this.audit(actor, 'transactional_email.content.reset', email.code, { scope });
|
|
307
|
+
}
|
|
308
|
+
const fallbackLanguage = email.languages[0];
|
|
309
|
+
const resolved = await this.contentResolver.resolve(em, email, {
|
|
310
|
+
salesChannelId: scope.salesChannelId,
|
|
311
|
+
language: scope.language,
|
|
312
|
+
...(fallbackLanguage ? { fallbackLanguage } : {}),
|
|
313
|
+
});
|
|
314
|
+
return { subject: resolved.subject, content: resolved.content, source: resolved.source, version: resolved.version };
|
|
315
|
+
}
|
|
316
|
+
// --- Admin: preview -----------------------------------------------------
|
|
317
|
+
async preview(code, input) {
|
|
318
|
+
const em = this.emFactory();
|
|
319
|
+
const email = await this.loadEmailOrThrow(em, code);
|
|
320
|
+
const lang = input.language ?? email.languages[0] ?? 'en-US';
|
|
321
|
+
const fallbackLanguage = email.languages[0];
|
|
322
|
+
let resolved;
|
|
323
|
+
if (input.draftContent !== undefined || input.draftSubject !== undefined) {
|
|
324
|
+
const base = await this.contentResolver.resolve(em, email, {
|
|
325
|
+
salesChannelId: input.salesChannelId,
|
|
326
|
+
language: lang,
|
|
327
|
+
...(fallbackLanguage ? { fallbackLanguage } : {}),
|
|
328
|
+
});
|
|
329
|
+
resolved = {
|
|
330
|
+
subject: input.draftSubject ?? base.subject,
|
|
331
|
+
content: input.draftContent ?? base.content,
|
|
332
|
+
source: base.source,
|
|
333
|
+
version: base.version,
|
|
334
|
+
language: lang,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
resolved = await this.contentResolver.resolve(em, email, {
|
|
339
|
+
salesChannelId: input.salesChannelId,
|
|
340
|
+
language: lang,
|
|
341
|
+
...(fallbackLanguage ? { fallbackLanguage } : {}),
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
const branding = await this.branding.resolve(input.salesChannelId);
|
|
345
|
+
// Sample data drives the preview (FR-024).
|
|
346
|
+
const variables = this.sampleVariables(email.variables);
|
|
347
|
+
const rendered = await this.renderWith(em, resolved, branding, variables, {
|
|
348
|
+
salesChannelId: input.salesChannelId,
|
|
349
|
+
language: lang,
|
|
350
|
+
...(fallbackLanguage ? { fallbackLanguage } : {}),
|
|
351
|
+
});
|
|
352
|
+
return { subject: rendered.subject, html: rendered.html, text: rendered.text };
|
|
353
|
+
}
|
|
354
|
+
sampleVariables(variables) {
|
|
355
|
+
// Prefer shared helper (JSON sampleValue for lists like order.items).
|
|
356
|
+
// Local inline fallback mirrors packages/email-components sample-variables.
|
|
357
|
+
const ctx = {};
|
|
358
|
+
for (const v of variables) {
|
|
359
|
+
const parts = v.key.split('.');
|
|
360
|
+
let cur = ctx;
|
|
361
|
+
for (let i = 0; i < parts.length - 1; i += 1) {
|
|
362
|
+
const part = parts[i];
|
|
363
|
+
if (!part)
|
|
364
|
+
continue;
|
|
365
|
+
if (typeof cur[part] !== 'object' || cur[part] === null || Array.isArray(cur[part])) {
|
|
366
|
+
cur[part] = {};
|
|
367
|
+
}
|
|
368
|
+
cur = cur[part];
|
|
369
|
+
}
|
|
370
|
+
const leaf = parts[parts.length - 1];
|
|
371
|
+
if (!leaf)
|
|
372
|
+
continue;
|
|
373
|
+
const raw = v.sampleValue;
|
|
374
|
+
if (raw == null || raw === '') {
|
|
375
|
+
cur[leaf] = `{${v.key}}`;
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
const trimmed = raw.trim();
|
|
379
|
+
if ((trimmed.startsWith('{') && trimmed.endsWith('}')) ||
|
|
380
|
+
(trimmed.startsWith('[') && trimmed.endsWith(']'))) {
|
|
381
|
+
try {
|
|
382
|
+
cur[leaf] = JSON.parse(trimmed);
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
catch {
|
|
386
|
+
/* fall through */
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
cur[leaf] = raw;
|
|
390
|
+
}
|
|
391
|
+
return ctx;
|
|
392
|
+
}
|
|
393
|
+
async audit(actor, action, objectId, meta) {
|
|
394
|
+
if (!this.auditLog)
|
|
395
|
+
return;
|
|
396
|
+
try {
|
|
397
|
+
await this.auditLog.record({
|
|
398
|
+
actorAdminUserId: actor.adminUserId ?? null,
|
|
399
|
+
action,
|
|
400
|
+
objectType: 'transactional_email',
|
|
401
|
+
objectId,
|
|
402
|
+
stateAfter: meta,
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
catch {
|
|
406
|
+
// Audit is best-effort; never block the operation.
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
//# sourceMappingURL=transactional-email.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transactional-email.service.js","sourceRoot":"","sources":["../../../src/backend/services/transactional-email.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EACL,WAAW,GAQZ,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EAAE,0BAA0B,EAAE,MAAM,0DAA0D,CAAC;AACtG,OAAO,EAAE,qBAAqB,EAAE,MAAM,oDAAoD,CAAC;AAC3F,OAAO,EAAE,eAAe,EAAE,MAAM,4DAA4D,CAAC;AAC7F,OAAO,EAAE,eAAe,EAAE,MAAM,4DAA4D,CAAC;AAC7F,OAAO,EAAE,gBAAgB,EAAE,MAAM,+DAA+D,CAAC;AACjG,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAO3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAC;AAC/E,OAAO,EAAE,yBAAyB,EAAE,MAAM,mDAAmD,CAAC;AAE9F,OAAO,EAAwB,MAAM,uBAAuB,CAAC;AAE7D,OAAO,EAAyB,MAAM,uBAAuB,CAAC;AAI9D,MAAM,gBAAgB,GAAwB,IAAI,GAAG,CAAC,0BAA0B,CAAC,CAAC;AAElF;;;;;GAKG;AACH,IAAI,iBAAiB,GAAG,KAAK,CAAC;AA4C9B,MAAM,OAAO,yBAAyB;IACnB,SAAS,CAAsB;IAC/B,eAAe,CAAkB;IACjC,QAAQ,CAAkB;IAC1B,MAAM,CAAgB;IACtB,QAAQ,CAAwB;IAChC,MAAM,CAA8B;IACpC,gBAAgB,CAAoC;IACpD,QAAQ,CAAwB;IAEjD,YAAY,IAAmC;QAC7C,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;QAChC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,eAAe,CAAC;QAC5C,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC9B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC9B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,CAAC;QAC9C,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;IAChC,CAAC;IAED,2EAA2E;IAE3E;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,IAAI,CAAC,KAAkC;QAC3C,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,IAAI,CAAC,iBAAiB,EAAE,CAAC;gBACvB,iBAAiB,GAAG,IAAI,CAAC;gBACzB,OAAO,CAAC,IAAI,CACV,uHAAuH,CACxH,CAAC;YACJ,CAAC;YACD,MAAM,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC;YAC1D,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;QACpC,CAAC;QACD,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACzE,wEAAwE;QACxE,2EAA2E;QAC3E,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;YAC3D,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC;QACrC,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YAClB,MAAM,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC;YAC7D,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;QACnC,CAAC;QAED,MAAM,gBAAgB,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAC5C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE;YAC7D,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAClD,CAAC,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QACnE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,EAAE;YAC9E,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,QAAQ,EAAE,QAAQ,CAAC,QAAQ;YAC3B,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAClD,CAAC,CAAC;QAEH,2EAA2E;QAC3E,0EAA0E;QAC1E,2EAA2E;QAC3E,mEAAmE;QACnE,4EAA4E;QAC5E,wEAAwE;QACxE,kEAAkE;QAClE,yEAAyE;QACzE,oDAAoD;QACpD,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;YACrB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,yEAAyE;YACzE,sEAAsE;YACtE,wBAAwB;YACxB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACvD,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAChE,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC5C,CAAC,CAAC;QACH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAC5B,CAAC;IAED;;;;;;;OAOG;IACK,KAAK,CAAC,aAAa,CACzB,KAAkC,EAClC,MAA+B,EAC/B,MAA2B;QAE3B,MAAM,IAAI,CAAC,gBAAgB,EAAE,MAAM,CAAC;YAClC,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,SAAS,EAAE,KAAK,CAAC,EAAE;YACnB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,MAAM;YACN,MAAM;YACN,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,GAAG,CAAC,KAAK,CAAC,QAAQ;gBAChB,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,EAAE,EAAE;gBACtE,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC/C,CAAC,CAAC;IACL,CAAC;IAED,2EAA2E;IAEnE,KAAK,CAAC,UAAU,CACtB,EAAiB,EACjB,QAAyB,EACzB,QAA0B,EAC1B,SAAkC,EAClC,KAAqF;QAErF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACtE,MAAM,GAAG,GAA4B;YACnC,GAAG,SAAS;YACZ,QAAQ,EAAE,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,CAAC,WAAW,EAAE;SAC3E,CAAC;QACF,MAAM,IAAI,GAAG,gBAAgB,CAC3B,eAAe,CAAC,QAAQ,CAAC,OAAO,EAAE;YAChC,MAAM;YACN,WAAW,EAAE,QAAQ,CAAC,WAAW;YACjC,QAAQ,EAAE,KAAK,CAAC,QAAQ;SACzB,CAAC,EACF,GAAG,EACH,EAAE,MAAM,EAAE,IAAI,EAAE,CACjB,CAAC;QACF,MAAM,IAAI,GAAG,gBAAgB,CAAC,eAAe,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC;QAClF,MAAM,OAAO,GAAG,gBAAgB,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QACxD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACjC,CAAC;IAED,0EAA0E;IAE1E;;;;;;;OAOG;IACK,YAAY,CAAC,IAAY;QAI/B,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,wBAAwB,CAAC,IAAI,CAAC,CAAC;QAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,IAAI,EAAE,sBAAsB,EAAE,MAAM,EAAE,CAAC;IAC5E,CAAC;IAEO,KAAK,CAAC,SAAS,CACrB,EAAiB,EACjB,KAAyB;QAEzB,MAAM,WAAW,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,yBAAyB,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3G,MAAM,YAAY,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,yBAAyB,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE,cAAc,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QACrH,OAAO;YACL,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,KAAK,EAAE,KAAK,CAAC,SAAS;YACtB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,iBAAiB,EAAE,WAAW,GAAG,CAAC;YAClC,kBAAkB,EAAE,YAAY,GAAG,CAAC;YACpC,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC;SACjC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,IAAI;QACR,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QACnF,MAAM,GAAG,GAAgC,EAAE,CAAC;QAC5C,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACvB,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QACxC,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,OAAO,CAAC,IAAY;QACxB,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAChF,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,WAAW,CAAC,SAAS,EAAE,wBAAwB,IAAI,cAAc,CAAC,CAAC;QACxG,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IACnC,CAAC;IAEO,KAAK,CAAC,gBAAgB,CAAC,EAAiB,EAAE,IAAY;QAC5D,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7D,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,WAAW,CAAC,SAAS,EAAE,wBAAwB,IAAI,cAAc,CAAC,CAAC;QACxG,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,IAAY,EAAE,cAA6B,EAAE,QAAiB;QAC5E,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QACpD,MAAM,IAAI,GAAG,QAAQ,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC;QACvD,MAAM,gBAAgB,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAC5C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE;YAC7D,cAAc;YACd,QAAQ,EAAE,IAAI;YACd,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAClD,CAAC,CAAC;QACH,MAAM,WAAW,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,yBAAyB,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3G,MAAM,YAAY,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,yBAAyB,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE,cAAc,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QACrH,MAAM,kBAAkB,GACrB,KAAK,CAAC,cAA0D,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;QAE9H,MAAM,SAAS,GAAyB;YACtC,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,OAAO,EAAE,QAAQ,CAAC,OAAO;SAC1B,CAAC;QAEF,OAAO;YACL,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,KAAK,EAAE,KAAK,CAAC,SAAS;YACtB,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,KAAK,EAAE,EAAE,cAAc,EAAE,QAAQ,EAAE,IAAI,EAAE;YACzC,SAAS;YACT,OAAO,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,kBAAkC,EAAE;YACnG,iBAAiB,EAAE,WAAW,GAAG,CAAC;YAClC,kBAAkB,EAAE,YAAY,GAAG,CAAC;YACpC,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC;SACjC,CAAC;IACJ,CAAC;IAED,0EAA0E;IAElE,eAAe,CAAC,OAAqB;QAC3C,MAAM,OAAO,GAAG,qBAAqB,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC;QACjE,IAAI,OAAO,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;YACrB,MAAM,IAAI,SAAS,CACjB,GAAG,EACH,WAAW,CAAC,iBAAiB,EAC7B,qDAAqD,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC/E,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,WAAW,CACf,IAAY,EACZ,KAA0D,EAC1D,KAAuB,EACvB,QAAyC,EAAE;QAE3C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;YAC1B,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,WAAW,CAAC,iBAAiB,EAAE,sBAAsB,CAAC,CAAC;QAClF,CAAC;QACD,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAEpC,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QACpD,IAAI,GAAG,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,yBAAyB,EAAE;YACpD,OAAO,EAAE,KAAK,CAAC,EAAE;YACjB,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,QAAQ,EAAE,KAAK,CAAC,QAAQ;SACzB,CAAC,CAAC;QACH,IAAI,GAAG,EAAE,CAAC;YACR,IAAI,KAAK,CAAC,eAAe,KAAK,SAAS,IAAI,KAAK,CAAC,eAAe,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC;gBACjF,MAAM,IAAI,SAAS,CAAC,GAAG,EAAE,WAAW,CAAC,gBAAgB,EAAE,6CAA6C,CAAC,CAAC;YACxG,CAAC;YACD,GAAG,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;YAC5B,GAAG,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;YAC5B,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC;QACnB,CAAC;aAAM,CAAC;YACN,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,yBAAyB,EAAE;gBACzC,OAAO,EAAE,KAAK,CAAC,EAAE;gBACjB,cAAc,EAAE,KAAK,CAAC,cAAc;gBACpC,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,OAAO,EAAE,CAAC;aACX,CAAC,CAAC;QACL,CAAC;QACD,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;QACjB,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,mCAAmC,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAEpF,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,OAAuB,EAAE,MAAM,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC;IACnJ,CAAC;IAED,KAAK,CAAC,YAAY,CAChB,IAAY,EACZ,KAA0D,EAC1D,QAAyC,EAAE;QAE3C,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QACpD,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,yBAAyB,EAAE;YACtD,OAAO,EAAE,KAAK,CAAC,EAAE;YACjB,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,QAAQ,EAAE,KAAK,CAAC,QAAQ;SACzB,CAAC,CAAC;QACH,IAAI,GAAG,EAAE,CAAC;YACR,MAAM,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;YAC7B,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,mCAAmC,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QACtF,CAAC;QACD,MAAM,gBAAgB,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAC5C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE;YAC7D,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAClD,CAAC,CAAC;QACH,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC;IACtH,CAAC;IAED,2EAA2E;IAE3E,KAAK,CAAC,OAAO,CACX,IAAY,EACZ,KAA+G;QAE/G,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QACpD,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC;QAC7D,MAAM,gBAAgB,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAE5C,IAAI,QAAyB,CAAC;QAC9B,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;YACzE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE;gBACzD,cAAc,EAAE,KAAK,CAAC,cAAc;gBACpC,QAAQ,EAAE,IAAI;gBACd,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAClD,CAAC,CAAC;YACH,QAAQ,GAAG;gBACT,OAAO,EAAE,KAAK,CAAC,YAAY,IAAI,IAAI,CAAC,OAAO;gBAC3C,OAAO,EAAE,KAAK,CAAC,YAAY,IAAI,IAAI,CAAC,OAAO;gBAC3C,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,QAAQ,EAAE,IAAI;aACf,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,QAAQ,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE;gBACvD,cAAc,EAAE,KAAK,CAAC,cAAc;gBACpC,QAAQ,EAAE,IAAI;gBACd,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAClD,CAAC,CAAC;QACL,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QACnE,2CAA2C;QAC3C,MAAM,SAAS,GAAG,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACxD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE;YACxE,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,QAAQ,EAAE,IAAI;YACd,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAClD,CAAC,CAAC;QACH,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC;IACjF,CAAC;IAEO,eAAe,CACrB,SAA2E;QAE3E,sEAAsE;QACtE,4EAA4E;QAC5E,MAAM,GAAG,GAA4B,EAAE,CAAC;QACxC,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;YAC1B,MAAM,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC/B,IAAI,GAAG,GAAG,GAAG,CAAC;YACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC7C,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;gBACtB,IAAI,CAAC,IAAI;oBAAE,SAAS;gBACpB,IAAI,OAAO,GAAG,CAAC,IAAI,CAAC,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;oBACpF,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;gBACjB,CAAC;gBACD,GAAG,GAAG,GAAG,CAAC,IAAI,CAA4B,CAAC;YAC7C,CAAC;YACD,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YACrC,IAAI,CAAC,IAAI;gBAAE,SAAS;YACpB,MAAM,GAAG,GAAG,CAAC,CAAC,WAAW,CAAC;YAC1B,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,KAAK,EAAE,EAAE,CAAC;gBAC9B,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC;gBACzB,SAAS;YACX,CAAC;YACD,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;YAC3B,IACE,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;gBAClD,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAClD,CAAC;gBACD,IAAI,CAAC;oBACH,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAY,CAAC;oBAC3C,SAAS;gBACX,CAAC;gBAAC,MAAM,CAAC;oBACP,kBAAkB;gBACpB,CAAC;YACH,CAAC;YACD,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC;QAClB,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAEO,KAAK,CAAC,KAAK,CACjB,KAAsC,EACtC,MAAc,EACd,QAAgB,EAChB,IAA6B;QAE7B,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC3B,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;gBACzB,gBAAgB,EAAE,KAAK,CAAC,WAAW,IAAI,IAAI;gBAC3C,MAAM;gBACN,UAAU,EAAE,qBAAqB;gBACjC,QAAQ;gBACR,UAAU,EAAE,IAAI;aACjB,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,mDAAmD;QACrD,CAAC;IACH,CAAC;CACF"}
|