@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.
Files changed (151) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +63 -0
  3. package/dist/admin/api/transactional-emails-client.d.ts +43 -0
  4. package/dist/admin/api/transactional-emails-client.d.ts.map +1 -0
  5. package/dist/admin/api/transactional-emails-client.js +47 -0
  6. package/dist/admin/api/transactional-emails-client.js.map +1 -0
  7. package/dist/admin/components/BrandingPanel.d.ts +10 -0
  8. package/dist/admin/components/BrandingPanel.d.ts.map +1 -0
  9. package/dist/admin/components/BrandingPanel.js +58 -0
  10. package/dist/admin/components/BrandingPanel.js.map +1 -0
  11. package/dist/admin/components/EmailEditorPane.d.ts +7 -0
  12. package/dist/admin/components/EmailEditorPane.d.ts.map +1 -0
  13. package/dist/admin/components/EmailEditorPane.js +48 -0
  14. package/dist/admin/components/EmailEditorPane.js.map +1 -0
  15. package/dist/admin/index.d.ts +46 -0
  16. package/dist/admin/index.d.ts.map +1 -0
  17. package/dist/admin/index.js +92 -0
  18. package/dist/admin/index.js.map +1 -0
  19. package/dist/admin/pages/EmailBlockEditorPage.d.ts +16 -0
  20. package/dist/admin/pages/EmailBlockEditorPage.d.ts.map +1 -0
  21. package/dist/admin/pages/EmailBlockEditorPage.js +19 -0
  22. package/dist/admin/pages/EmailBlockEditorPage.js.map +1 -0
  23. package/dist/admin/pages/EmailBlocksPage.d.ts +8 -0
  24. package/dist/admin/pages/EmailBlocksPage.d.ts.map +1 -0
  25. package/dist/admin/pages/EmailBlocksPage.js +55 -0
  26. package/dist/admin/pages/EmailBlocksPage.js.map +1 -0
  27. package/dist/admin/pages/EmailEditor.d.ts +8 -0
  28. package/dist/admin/pages/EmailEditor.d.ts.map +1 -0
  29. package/dist/admin/pages/EmailEditor.js +126 -0
  30. package/dist/admin/pages/EmailEditor.js.map +1 -0
  31. package/dist/admin/pages/EmailFragmentEditor.d.ts +9 -0
  32. package/dist/admin/pages/EmailFragmentEditor.d.ts.map +1 -0
  33. package/dist/admin/pages/EmailFragmentEditor.js +93 -0
  34. package/dist/admin/pages/EmailFragmentEditor.js.map +1 -0
  35. package/dist/admin/pages/EmailTemplateEditorPage.d.ts +12 -0
  36. package/dist/admin/pages/EmailTemplateEditorPage.d.ts.map +1 -0
  37. package/dist/admin/pages/EmailTemplateEditorPage.js +15 -0
  38. package/dist/admin/pages/EmailTemplateEditorPage.js.map +1 -0
  39. package/dist/admin/pages/EmailTemplatesPage.d.ts +8 -0
  40. package/dist/admin/pages/EmailTemplatesPage.d.ts.map +1 -0
  41. package/dist/admin/pages/EmailTemplatesPage.js +55 -0
  42. package/dist/admin/pages/EmailTemplatesPage.js.map +1 -0
  43. package/dist/admin/pages/EmailsList.d.ts +8 -0
  44. package/dist/admin/pages/EmailsList.d.ts.map +1 -0
  45. package/dist/admin/pages/EmailsList.js +85 -0
  46. package/dist/admin/pages/EmailsList.js.map +1 -0
  47. package/dist/backend/commands/email-activation.commands.d.ts +42 -0
  48. package/dist/backend/commands/email-activation.commands.d.ts.map +1 -0
  49. package/dist/backend/commands/email-activation.commands.js +51 -0
  50. package/dist/backend/commands/email-activation.commands.js.map +1 -0
  51. package/dist/backend/entities/email-block-sales-channel.entity.d.ts +11 -0
  52. package/dist/backend/entities/email-block-sales-channel.entity.d.ts.map +1 -0
  53. package/dist/backend/entities/email-block-sales-channel.entity.js +39 -0
  54. package/dist/backend/entities/email-block-sales-channel.entity.js.map +1 -0
  55. package/dist/backend/entities/email-block.entity.d.ts +24 -0
  56. package/dist/backend/entities/email-block.entity.d.ts.map +1 -0
  57. package/dist/backend/entities/email-block.entity.js +85 -0
  58. package/dist/backend/entities/email-block.entity.js.map +1 -0
  59. package/dist/backend/entities/email-template-sales-channel.entity.d.ts +10 -0
  60. package/dist/backend/entities/email-template-sales-channel.entity.d.ts.map +1 -0
  61. package/dist/backend/entities/email-template-sales-channel.entity.js +38 -0
  62. package/dist/backend/entities/email-template-sales-channel.entity.js.map +1 -0
  63. package/dist/backend/entities/email-template.entity.d.ts +19 -0
  64. package/dist/backend/entities/email-template.entity.d.ts.map +1 -0
  65. package/dist/backend/entities/email-template.entity.js +76 -0
  66. package/dist/backend/entities/email-template.entity.js.map +1 -0
  67. package/dist/backend/entities/transactional-email-content.entity.d.ts +25 -0
  68. package/dist/backend/entities/transactional-email-content.entity.d.ts.map +1 -0
  69. package/dist/backend/entities/transactional-email-content.entity.js +77 -0
  70. package/dist/backend/entities/transactional-email-content.entity.js.map +1 -0
  71. package/dist/backend/entities/transactional-email.entity.d.ts +32 -0
  72. package/dist/backend/entities/transactional-email.entity.d.ts.map +1 -0
  73. package/dist/backend/entities/transactional-email.entity.js +100 -0
  74. package/dist/backend/entities/transactional-email.entity.js.map +1 -0
  75. package/dist/backend/index.d.ts +105 -0
  76. package/dist/backend/index.d.ts.map +1 -0
  77. package/dist/backend/index.js +126 -0
  78. package/dist/backend/index.js.map +1 -0
  79. package/dist/backend/plugin.d.ts +49 -0
  80. package/dist/backend/plugin.d.ts.map +1 -0
  81. package/dist/backend/plugin.js +54 -0
  82. package/dist/backend/plugin.js.map +1 -0
  83. package/dist/backend/routes.admin.d.ts +23 -0
  84. package/dist/backend/routes.admin.d.ts.map +1 -0
  85. package/dist/backend/routes.admin.js +133 -0
  86. package/dist/backend/routes.admin.js.map +1 -0
  87. package/dist/backend/services/branding.service.d.ts +63 -0
  88. package/dist/backend/services/branding.service.d.ts.map +1 -0
  89. package/dist/backend/services/branding.service.js +119 -0
  90. package/dist/backend/services/branding.service.js.map +1 -0
  91. package/dist/backend/services/content-resolver.d.ts +28 -0
  92. package/dist/backend/services/content-resolver.d.ts.map +1 -0
  93. package/dist/backend/services/content-resolver.js +64 -0
  94. package/dist/backend/services/content-resolver.js.map +1 -0
  95. package/dist/backend/services/email-block.service.d.ts +25 -0
  96. package/dist/backend/services/email-block.service.d.ts.map +1 -0
  97. package/dist/backend/services/email-block.service.js +162 -0
  98. package/dist/backend/services/email-block.service.js.map +1 -0
  99. package/dist/backend/services/email-builder-registry.d.ts +9 -0
  100. package/dist/backend/services/email-builder-registry.d.ts.map +1 -0
  101. package/dist/backend/services/email-builder-registry.js +14 -0
  102. package/dist/backend/services/email-builder-registry.js.map +1 -0
  103. package/dist/backend/services/email-defaults-registry.d.ts +63 -0
  104. package/dist/backend/services/email-defaults-registry.d.ts.map +1 -0
  105. package/dist/backend/services/email-defaults-registry.js +60 -0
  106. package/dist/backend/services/email-defaults-registry.js.map +1 -0
  107. package/dist/backend/services/email-template.service.d.ts +23 -0
  108. package/dist/backend/services/email-template.service.d.ts.map +1 -0
  109. package/dist/backend/services/email-template.service.js +155 -0
  110. package/dist/backend/services/email-template.service.js.map +1 -0
  111. package/dist/backend/services/embed-resolver.d.ts +21 -0
  112. package/dist/backend/services/embed-resolver.d.ts.map +1 -0
  113. package/dist/backend/services/embed-resolver.js +68 -0
  114. package/dist/backend/services/embed-resolver.js.map +1 -0
  115. package/dist/backend/services/manifest-reconciler.d.ts +28 -0
  116. package/dist/backend/services/manifest-reconciler.d.ts.map +1 -0
  117. package/dist/backend/services/manifest-reconciler.js +98 -0
  118. package/dist/backend/services/manifest-reconciler.js.map +1 -0
  119. package/dist/backend/services/template-email.d.ts +26 -0
  120. package/dist/backend/services/template-email.d.ts.map +1 -0
  121. package/dist/backend/services/template-email.js +69 -0
  122. package/dist/backend/services/template-email.js.map +1 -0
  123. package/dist/backend/services/transactional-email.service.d.ts +138 -0
  124. package/dist/backend/services/transactional-email.service.d.ts.map +1 -0
  125. package/dist/backend/services/transactional-email.service.js +410 -0
  126. package/dist/backend/services/transactional-email.service.js.map +1 -0
  127. package/dist/manifest.d.ts +195 -0
  128. package/dist/manifest.d.ts.map +1 -0
  129. package/dist/manifest.js +367 -0
  130. package/dist/manifest.js.map +1 -0
  131. package/dist/migrations/20260629T113442_transactional_emails_init.d.ts +6 -0
  132. package/dist/migrations/20260629T113442_transactional_emails_init.d.ts.map +1 -0
  133. package/dist/migrations/20260629T113442_transactional_emails_init.js +133 -0
  134. package/dist/migrations/20260629T113442_transactional_emails_init.js.map +1 -0
  135. package/dist/migrations/20260801T111001_transactional_emails_email_defaults_reseed.d.ts +6 -0
  136. package/dist/migrations/20260801T111001_transactional_emails_email_defaults_reseed.d.ts.map +1 -0
  137. package/dist/migrations/20260801T111001_transactional_emails_email_defaults_reseed.js +49 -0
  138. package/dist/migrations/20260801T111001_transactional_emails_email_defaults_reseed.js.map +1 -0
  139. package/dist/migrations/20260903T101748_transactional_emails_namespace_block_names.d.ts +6 -0
  140. package/dist/migrations/20260903T101748_transactional_emails_namespace_block_names.d.ts.map +1 -0
  141. package/dist/migrations/20260903T101748_transactional_emails_namespace_block_names.js +60 -0
  142. package/dist/migrations/20260903T101748_transactional_emails_namespace_block_names.js.map +1 -0
  143. package/dist/migrations/index.d.ts +29 -0
  144. package/dist/migrations/index.d.ts.map +1 -0
  145. package/dist/migrations/index.js +33 -0
  146. package/dist/migrations/index.js.map +1 -0
  147. package/docs/transactional-emails.md +176 -0
  148. package/i18n/en.json +92 -0
  149. package/i18n/pl.json +92 -0
  150. package/package.json +113 -0
  151. 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"}