@endora-commerce/mod-newsletter 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 +66 -0
- package/dist/admin/api/newsletter-client.d.ts +64 -0
- package/dist/admin/api/newsletter-client.d.ts.map +1 -0
- package/dist/admin/api/newsletter-client.js +71 -0
- package/dist/admin/api/newsletter-client.js.map +1 -0
- package/dist/admin/email-variables.d.ts +21 -0
- package/dist/admin/email-variables.d.ts.map +1 -0
- package/dist/admin/email-variables.js +48 -0
- package/dist/admin/email-variables.js.map +1 -0
- package/dist/admin/index.d.ts +44 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +149 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/AutomationBuilder.d.ts +8 -0
- package/dist/admin/pages/AutomationBuilder.d.ts.map +1 -0
- package/dist/admin/pages/AutomationBuilder.js +131 -0
- package/dist/admin/pages/AutomationBuilder.js.map +1 -0
- package/dist/admin/pages/AutomationsPage.d.ts +8 -0
- package/dist/admin/pages/AutomationsPage.d.ts.map +1 -0
- package/dist/admin/pages/AutomationsPage.js +43 -0
- package/dist/admin/pages/AutomationsPage.js.map +1 -0
- package/dist/admin/pages/BlocksPage.d.ts +8 -0
- package/dist/admin/pages/BlocksPage.d.ts.map +1 -0
- package/dist/admin/pages/BlocksPage.js +110 -0
- package/dist/admin/pages/BlocksPage.js.map +1 -0
- package/dist/admin/pages/CampaignEditor.d.ts +8 -0
- package/dist/admin/pages/CampaignEditor.d.ts.map +1 -0
- package/dist/admin/pages/CampaignEditor.js +144 -0
- package/dist/admin/pages/CampaignEditor.js.map +1 -0
- package/dist/admin/pages/CampaignStats.d.ts +8 -0
- package/dist/admin/pages/CampaignStats.d.ts.map +1 -0
- package/dist/admin/pages/CampaignStats.js +45 -0
- package/dist/admin/pages/CampaignStats.js.map +1 -0
- package/dist/admin/pages/CampaignsPage.d.ts +8 -0
- package/dist/admin/pages/CampaignsPage.d.ts.map +1 -0
- package/dist/admin/pages/CampaignsPage.js +28 -0
- package/dist/admin/pages/CampaignsPage.js.map +1 -0
- package/dist/admin/pages/ProviderSettingsPage.d.ts +8 -0
- package/dist/admin/pages/ProviderSettingsPage.d.ts.map +1 -0
- package/dist/admin/pages/ProviderSettingsPage.js +64 -0
- package/dist/admin/pages/ProviderSettingsPage.js.map +1 -0
- package/dist/admin/pages/SubscribersPage.d.ts +8 -0
- package/dist/admin/pages/SubscribersPage.d.ts.map +1 -0
- package/dist/admin/pages/SubscribersPage.js +55 -0
- package/dist/admin/pages/SubscribersPage.js.map +1 -0
- package/dist/admin/pages/TagsPage.d.ts +8 -0
- package/dist/admin/pages/TagsPage.d.ts.map +1 -0
- package/dist/admin/pages/TagsPage.js +114 -0
- package/dist/admin/pages/TagsPage.js.map +1 -0
- package/dist/backend/entities/newsletter-automation-run.entity.d.ts +19 -0
- package/dist/backend/entities/newsletter-automation-run.entity.d.ts.map +1 -0
- package/dist/backend/entities/newsletter-automation-run.entity.js +72 -0
- package/dist/backend/entities/newsletter-automation-run.entity.js.map +1 -0
- package/dist/backend/entities/newsletter-automation.entity.d.ts +22 -0
- package/dist/backend/entities/newsletter-automation.entity.d.ts.map +1 -0
- package/dist/backend/entities/newsletter-automation.entity.js +86 -0
- package/dist/backend/entities/newsletter-automation.entity.js.map +1 -0
- package/dist/backend/entities/newsletter-campaign-subscriber.entity.d.ts +6 -0
- package/dist/backend/entities/newsletter-campaign-subscriber.entity.d.ts.map +1 -0
- package/dist/backend/entities/newsletter-campaign-subscriber.entity.js +30 -0
- package/dist/backend/entities/newsletter-campaign-subscriber.entity.js.map +1 -0
- package/dist/backend/entities/newsletter-campaign.entity.d.ts +22 -0
- package/dist/backend/entities/newsletter-campaign.entity.d.ts.map +1 -0
- package/dist/backend/entities/newsletter-campaign.entity.js +102 -0
- package/dist/backend/entities/newsletter-campaign.entity.js.map +1 -0
- package/dist/backend/entities/newsletter-custom-field.entity.d.ts +12 -0
- package/dist/backend/entities/newsletter-custom-field.entity.d.ts.map +1 -0
- package/dist/backend/entities/newsletter-custom-field.entity.js +53 -0
- package/dist/backend/entities/newsletter-custom-field.entity.js.map +1 -0
- package/dist/backend/entities/newsletter-email-block-sales-channel.entity.d.ts +7 -0
- package/dist/backend/entities/newsletter-email-block-sales-channel.entity.d.ts.map +1 -0
- package/dist/backend/entities/newsletter-email-block-sales-channel.entity.js +35 -0
- package/dist/backend/entities/newsletter-email-block-sales-channel.entity.js.map +1 -0
- package/dist/backend/entities/newsletter-email-block.entity.d.ts +21 -0
- package/dist/backend/entities/newsletter-email-block.entity.d.ts.map +1 -0
- package/dist/backend/entities/newsletter-email-block.entity.js +78 -0
- package/dist/backend/entities/newsletter-email-block.entity.js.map +1 -0
- package/dist/backend/entities/newsletter-engagement-event.entity.d.ts +12 -0
- package/dist/backend/entities/newsletter-engagement-event.entity.d.ts.map +1 -0
- package/dist/backend/entities/newsletter-engagement-event.entity.js +54 -0
- package/dist/backend/entities/newsletter-engagement-event.entity.js.map +1 -0
- package/dist/backend/entities/newsletter-send-record.entity.d.ts +23 -0
- package/dist/backend/entities/newsletter-send-record.entity.d.ts.map +1 -0
- package/dist/backend/entities/newsletter-send-record.entity.js +89 -0
- package/dist/backend/entities/newsletter-send-record.entity.js.map +1 -0
- package/dist/backend/entities/newsletter-subscriber-tag.entity.d.ts +6 -0
- package/dist/backend/entities/newsletter-subscriber-tag.entity.d.ts.map +1 -0
- package/dist/backend/entities/newsletter-subscriber-tag.entity.js +31 -0
- package/dist/backend/entities/newsletter-subscriber-tag.entity.js.map +1 -0
- package/dist/backend/entities/newsletter-subscriber.entity.d.ts +63 -0
- package/dist/backend/entities/newsletter-subscriber.entity.d.ts.map +1 -0
- package/dist/backend/entities/newsletter-subscriber.entity.js +147 -0
- package/dist/backend/entities/newsletter-subscriber.entity.js.map +1 -0
- package/dist/backend/entities/newsletter-suppression.entity.d.ts +14 -0
- package/dist/backend/entities/newsletter-suppression.entity.d.ts.map +1 -0
- package/dist/backend/entities/newsletter-suppression.entity.js +51 -0
- package/dist/backend/entities/newsletter-suppression.entity.js.map +1 -0
- package/dist/backend/entities/newsletter-tag.entity.d.ts +12 -0
- package/dist/backend/entities/newsletter-tag.entity.d.ts.map +1 -0
- package/dist/backend/entities/newsletter-tag.entity.js +53 -0
- package/dist/backend/entities/newsletter-tag.entity.js.map +1 -0
- package/dist/backend/index.d.ts +120 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +139 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/plugin.d.ts +114 -0
- package/dist/backend/plugin.d.ts.map +1 -0
- package/dist/backend/plugin.js +171 -0
- package/dist/backend/plugin.js.map +1 -0
- package/dist/backend/routes.admin.d.ts +27 -0
- package/dist/backend/routes.admin.d.ts.map +1 -0
- package/dist/backend/routes.admin.js +118 -0
- package/dist/backend/routes.admin.js.map +1 -0
- package/dist/backend/routes.self.d.ts +15 -0
- package/dist/backend/routes.self.d.ts.map +1 -0
- package/dist/backend/routes.self.js +28 -0
- package/dist/backend/routes.self.js.map +1 -0
- package/dist/backend/routes.storefront.d.ts +24 -0
- package/dist/backend/routes.storefront.d.ts.map +1 -0
- package/dist/backend/routes.storefront.js +79 -0
- package/dist/backend/routes.storefront.js.map +1 -0
- package/dist/backend/services/audience-resolver.d.ts +20 -0
- package/dist/backend/services/audience-resolver.d.ts.map +1 -0
- package/dist/backend/services/audience-resolver.js +41 -0
- package/dist/backend/services/audience-resolver.js.map +1 -0
- package/dist/backend/services/automation.service.d.ts +58 -0
- package/dist/backend/services/automation.service.d.ts.map +1 -0
- package/dist/backend/services/automation.service.js +301 -0
- package/dist/backend/services/automation.service.js.map +1 -0
- package/dist/backend/services/campaign-dispatch.service.d.ts +50 -0
- package/dist/backend/services/campaign-dispatch.service.d.ts.map +1 -0
- package/dist/backend/services/campaign-dispatch.service.js +130 -0
- package/dist/backend/services/campaign-dispatch.service.js.map +1 -0
- package/dist/backend/services/campaign.service.d.ts +40 -0
- package/dist/backend/services/campaign.service.d.ts.map +1 -0
- package/dist/backend/services/campaign.service.js +195 -0
- package/dist/backend/services/campaign.service.js.map +1 -0
- package/dist/backend/services/consent-block-seeder.d.ts +30 -0
- package/dist/backend/services/consent-block-seeder.d.ts.map +1 -0
- package/dist/backend/services/consent-block-seeder.js +52 -0
- package/dist/backend/services/consent-block-seeder.js.map +1 -0
- package/dist/backend/services/content.service.d.ts +52 -0
- package/dist/backend/services/content.service.d.ts.map +1 -0
- package/dist/backend/services/content.service.js +48 -0
- package/dist/backend/services/content.service.js.map +1 -0
- package/dist/backend/services/custom-field.service.d.ts +16 -0
- package/dist/backend/services/custom-field.service.d.ts.map +1 -0
- package/dist/backend/services/custom-field.service.js +67 -0
- package/dist/backend/services/custom-field.service.js.map +1 -0
- package/dist/backend/services/email-block.service.d.ts +26 -0
- package/dist/backend/services/email-block.service.d.ts.map +1 -0
- package/dist/backend/services/email-block.service.js +120 -0
- package/dist/backend/services/email-block.service.js.map +1 -0
- package/dist/backend/services/opt-in.service.d.ts +25 -0
- package/dist/backend/services/opt-in.service.d.ts.map +1 -0
- package/dist/backend/services/opt-in.service.js +46 -0
- package/dist/backend/services/opt-in.service.js.map +1 -0
- package/dist/backend/services/provider/console-provider.d.ts +32 -0
- package/dist/backend/services/provider/console-provider.d.ts.map +1 -0
- package/dist/backend/services/provider/console-provider.js +31 -0
- package/dist/backend/services/provider/console-provider.js.map +1 -0
- package/dist/backend/services/provider/provider-registry.d.ts +51 -0
- package/dist/backend/services/provider/provider-registry.d.ts.map +1 -0
- package/dist/backend/services/provider/provider-registry.js +84 -0
- package/dist/backend/services/provider/provider-registry.js.map +1 -0
- package/dist/backend/services/provider/smtp-provider.d.ts +30 -0
- package/dist/backend/services/provider/smtp-provider.d.ts.map +1 -0
- package/dist/backend/services/provider/smtp-provider.js +42 -0
- package/dist/backend/services/provider/smtp-provider.js.map +1 -0
- package/dist/backend/services/provider-admin.service.d.ts +39 -0
- package/dist/backend/services/provider-admin.service.d.ts.map +1 -0
- package/dist/backend/services/provider-admin.service.js +59 -0
- package/dist/backend/services/provider-admin.service.js.map +1 -0
- package/dist/backend/services/provider-admin.types.d.ts +6 -0
- package/dist/backend/services/provider-admin.types.d.ts.map +1 -0
- package/dist/backend/services/provider-admin.types.js +2 -0
- package/dist/backend/services/provider-admin.types.js.map +1 -0
- package/dist/backend/services/queues/newsletter-queues.d.ts +34 -0
- package/dist/backend/services/queues/newsletter-queues.d.ts.map +1 -0
- package/dist/backend/services/queues/newsletter-queues.js +58 -0
- package/dist/backend/services/queues/newsletter-queues.js.map +1 -0
- package/dist/backend/services/self.service.d.ts +17 -0
- package/dist/backend/services/self.service.d.ts.map +1 -0
- package/dist/backend/services/self.service.js +44 -0
- package/dist/backend/services/self.service.js.map +1 -0
- package/dist/backend/services/stats.service.d.ts +10 -0
- package/dist/backend/services/stats.service.d.ts.map +1 -0
- package/dist/backend/services/stats.service.js +45 -0
- package/dist/backend/services/stats.service.js.map +1 -0
- package/dist/backend/services/subscriber-admin.service.d.ts +26 -0
- package/dist/backend/services/subscriber-admin.service.d.ts.map +1 -0
- package/dist/backend/services/subscriber-admin.service.js +163 -0
- package/dist/backend/services/subscriber-admin.service.js.map +1 -0
- package/dist/backend/services/subscriber.service.d.ts +91 -0
- package/dist/backend/services/subscriber.service.d.ts.map +1 -0
- package/dist/backend/services/subscriber.service.js +280 -0
- package/dist/backend/services/subscriber.service.js.map +1 -0
- package/dist/backend/services/tag.service.d.ts +16 -0
- package/dist/backend/services/tag.service.d.ts.map +1 -0
- package/dist/backend/services/tag.service.js +79 -0
- package/dist/backend/services/tag.service.js.map +1 -0
- package/dist/backend/services/token.helper.d.ts +32 -0
- package/dist/backend/services/token.helper.d.ts.map +1 -0
- package/dist/backend/services/token.helper.js +76 -0
- package/dist/backend/services/token.helper.js.map +1 -0
- package/dist/backend/services/tracking.service.d.ts +14 -0
- package/dist/backend/services/tracking.service.d.ts.map +1 -0
- package/dist/backend/services/tracking.service.js +45 -0
- package/dist/backend/services/tracking.service.js.map +1 -0
- package/dist/manifest.d.ts +192 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +211 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260629T200954_newsletter_init.d.ts +6 -0
- package/dist/migrations/20260629T200954_newsletter_init.d.ts.map +1 -0
- package/dist/migrations/20260629T200954_newsletter_init.js +261 -0
- package/dist/migrations/20260629T200954_newsletter_init.js.map +1 -0
- package/dist/migrations/20260830T212736_newsletter_organization_attribution.d.ts +116 -0
- package/dist/migrations/20260830T212736_newsletter_organization_attribution.d.ts.map +1 -0
- package/dist/migrations/20260830T212736_newsletter_organization_attribution.js +186 -0
- package/dist/migrations/20260830T212736_newsletter_organization_attribution.js.map +1 -0
- package/dist/migrations/20260903T101752_newsletter_namespace_block_names.d.ts +6 -0
- package/dist/migrations/20260903T101752_newsletter_namespace_block_names.d.ts.map +1 -0
- package/dist/migrations/20260903T101752_newsletter_namespace_block_names.js +58 -0
- package/dist/migrations/20260903T101752_newsletter_namespace_block_names.js.map +1 -0
- package/dist/migrations/20260912T125702_newsletter_subscriber_tenant_scope_index.d.ts +23 -0
- package/dist/migrations/20260912T125702_newsletter_subscriber_tenant_scope_index.d.ts.map +1 -0
- package/dist/migrations/20260912T125702_newsletter_subscriber_tenant_scope_index.js +27 -0
- package/dist/migrations/20260912T125702_newsletter_subscriber_tenant_scope_index.js.map +1 -0
- package/dist/migrations/index.d.ts +30 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +35 -0
- package/dist/migrations/index.js.map +1 -0
- package/docs/newsletter.md +94 -0
- package/i18n/en.json +27 -0
- package/i18n/pl.json +27 -0
- package/package.json +119 -0
- package/tailwind.css +14 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { ERROR_CODES } from '@endora-commerce/contracts';
|
|
2
|
+
import { HttpError } from '@endora-commerce/platform/http';
|
|
3
|
+
import { NewsletterTag } from '../entities/newsletter-tag.entity.js';
|
|
4
|
+
import { recordAuditFromContext } from '@endora-commerce/platform/commands';
|
|
5
|
+
/** Newsletter tag CRUD (feature 048, US3). */
|
|
6
|
+
export class NewsletterTagService {
|
|
7
|
+
emFactory;
|
|
8
|
+
auditLog;
|
|
9
|
+
constructor(emFactory, auditLog) {
|
|
10
|
+
this.emFactory = emFactory;
|
|
11
|
+
this.auditLog = auditLog;
|
|
12
|
+
}
|
|
13
|
+
#audit(em, action, objectId, stateAfter) {
|
|
14
|
+
if (this.auditLog) {
|
|
15
|
+
recordAuditFromContext(this.auditLog, em, {
|
|
16
|
+
action,
|
|
17
|
+
objectType: 'newsletter_tag',
|
|
18
|
+
objectId,
|
|
19
|
+
stateBefore: null,
|
|
20
|
+
stateAfter,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async list() {
|
|
25
|
+
const em = this.emFactory();
|
|
26
|
+
const rows = await em.find(NewsletterTag, {}, { orderBy: { code: 'asc' } });
|
|
27
|
+
return rows.map((t) => this.toDto(t));
|
|
28
|
+
}
|
|
29
|
+
async create(input) {
|
|
30
|
+
const em = this.emFactory();
|
|
31
|
+
const existing = await em.findOne(NewsletterTag, { code: input.code });
|
|
32
|
+
if (existing)
|
|
33
|
+
throw new HttpError(409, ERROR_CODES.VALIDATION_FAILED, 'Tag code already exists.');
|
|
34
|
+
const tag = em.create(NewsletterTag, {
|
|
35
|
+
code: input.code,
|
|
36
|
+
name: input.name,
|
|
37
|
+
description: input.description ?? null,
|
|
38
|
+
});
|
|
39
|
+
em.persist(tag);
|
|
40
|
+
this.#audit(em, 'newsletter_tag.create', tag.id, { code: tag.code });
|
|
41
|
+
await em.flush();
|
|
42
|
+
return this.toDto(tag);
|
|
43
|
+
}
|
|
44
|
+
async update(id, input) {
|
|
45
|
+
const em = this.emFactory();
|
|
46
|
+
const tag = await em.findOne(NewsletterTag, { id });
|
|
47
|
+
if (!tag)
|
|
48
|
+
throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Tag not found.');
|
|
49
|
+
if (input.name !== undefined)
|
|
50
|
+
tag.name = input.name;
|
|
51
|
+
if (input.description !== undefined)
|
|
52
|
+
tag.description = input.description;
|
|
53
|
+
this.#audit(em, 'newsletter_tag.update', tag.id, { name: tag.name });
|
|
54
|
+
await em.persistAndFlush(tag);
|
|
55
|
+
return this.toDto(tag);
|
|
56
|
+
}
|
|
57
|
+
async remove(id) {
|
|
58
|
+
const em = this.emFactory();
|
|
59
|
+
const tag = await em.findOne(NewsletterTag, { id });
|
|
60
|
+
if (!tag)
|
|
61
|
+
throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Tag not found.');
|
|
62
|
+
// Guard: block delete if referenced by a campaign target or automation trigger.
|
|
63
|
+
const refs = (await em.getConnection().execute(`select 1 from newsletter_campaigns where target_tag_ids @> ?::jsonb
|
|
64
|
+
union all
|
|
65
|
+
select 1 from newsletter_automations where trigger_tag_ids @> ?::jsonb limit 1`, [JSON.stringify([id]), JSON.stringify([id])], 'all', em.getTransactionContext()));
|
|
66
|
+
if (refs.length > 0) {
|
|
67
|
+
throw new HttpError(409, ERROR_CODES.VALIDATION_FAILED, 'Tag is used by a campaign or automation.', {
|
|
68
|
+
code: 'tag_in_use',
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
// Subscriber-tag bridge rows cascade via FK.
|
|
72
|
+
this.#audit(em, 'newsletter_tag.delete', tag.id, null);
|
|
73
|
+
await em.removeAndFlush(tag);
|
|
74
|
+
}
|
|
75
|
+
toDto(t) {
|
|
76
|
+
return { id: t.id, code: t.code, name: t.name, description: t.description };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=tag.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tag.service.js","sourceRoot":"","sources":["../../../src/backend/services/tag.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signed, TTL-bounded tokens for the newsletter public surface (feature 048,
|
|
3
|
+
* research R5/R10). One helper mints/verifies all four token purposes so the
|
|
4
|
+
* confirm/unsubscribe/open/click links share a single HMAC secret and format.
|
|
5
|
+
*
|
|
6
|
+
* Token format: `base64url(payloadJson).base64url(hmacSha256(payloadJson))`.
|
|
7
|
+
* The payload always carries `{ p: purpose, exp: epochSeconds, ...claims }`.
|
|
8
|
+
*/
|
|
9
|
+
export type NewsletterTokenPurpose = 'confirm' | 'unsubscribe' | 'open' | 'click';
|
|
10
|
+
export interface NewsletterTokenClaims {
|
|
11
|
+
/** Subscriber id (confirm/unsubscribe) or send-record id (open/click). */
|
|
12
|
+
id: string;
|
|
13
|
+
/** Optional link id for click tracking. */
|
|
14
|
+
linkId?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare class NewsletterTokenHelper {
|
|
17
|
+
private readonly secret;
|
|
18
|
+
constructor(secret: string);
|
|
19
|
+
private sign;
|
|
20
|
+
/**
|
|
21
|
+
* Mint a token. `ttlSeconds` bounds validity; `nowSeconds` is injectable for
|
|
22
|
+
* deterministic tests (defaults to the current time).
|
|
23
|
+
*/
|
|
24
|
+
mint(purpose: NewsletterTokenPurpose, claims: NewsletterTokenClaims, ttlSeconds: number, nowSeconds?: number): string;
|
|
25
|
+
/**
|
|
26
|
+
* Verify a token against the expected purpose. Returns the claims on success,
|
|
27
|
+
* or `null` when the signature is invalid, the purpose mismatches, the token
|
|
28
|
+
* is malformed, or it has expired.
|
|
29
|
+
*/
|
|
30
|
+
verify(token: string, purpose: NewsletterTokenPurpose, nowSeconds?: number): NewsletterTokenClaims | null;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=token.helper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token.helper.d.ts","sourceRoot":"","sources":["../../../src/backend/services/token.helper.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,MAAM,MAAM,sBAAsB,GAAG,SAAS,GAAG,aAAa,GAAG,MAAM,GAAG,OAAO,CAAC;AAElF,MAAM,WAAW,qBAAqB;IACpC,0EAA0E;IAC1E,EAAE,EAAE,MAAM,CAAC;IACX,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAgBD,qBAAa,qBAAqB;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,MAAM;IAM3C,OAAO,CAAC,IAAI;IAIZ;;;OAGG;IACH,IAAI,CACF,OAAO,EAAE,sBAAsB,EAC/B,MAAM,EAAE,qBAAqB,EAC7B,UAAU,EAAE,MAAM,EAClB,UAAU,GAAE,MAAsC,GACjD,MAAM;IAYT;;;;OAIG;IACH,MAAM,CACJ,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,sBAAsB,EAC/B,UAAU,GAAE,MAAsC,GACjD,qBAAqB,GAAG,IAAI;CAgChC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { createHmac, timingSafeEqual } from 'node:crypto';
|
|
2
|
+
function b64urlEncode(buf) {
|
|
3
|
+
return buf.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
4
|
+
}
|
|
5
|
+
function b64urlDecode(s) {
|
|
6
|
+
const pad = s.length % 4 === 0 ? '' : '='.repeat(4 - (s.length % 4));
|
|
7
|
+
return Buffer.from(s.replace(/-/g, '+').replace(/_/g, '/') + pad, 'base64');
|
|
8
|
+
}
|
|
9
|
+
export class NewsletterTokenHelper {
|
|
10
|
+
secret;
|
|
11
|
+
constructor(secret) {
|
|
12
|
+
this.secret = secret;
|
|
13
|
+
if (!secret) {
|
|
14
|
+
throw new Error('[newsletter] token secret must be a non-empty string');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
sign(payloadJson) {
|
|
18
|
+
return b64urlEncode(createHmac('sha256', this.secret).update(payloadJson).digest());
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Mint a token. `ttlSeconds` bounds validity; `nowSeconds` is injectable for
|
|
22
|
+
* deterministic tests (defaults to the current time).
|
|
23
|
+
*/
|
|
24
|
+
mint(purpose, claims, ttlSeconds, nowSeconds = Math.floor(Date.now() / 1000)) {
|
|
25
|
+
const payload = {
|
|
26
|
+
p: purpose,
|
|
27
|
+
exp: nowSeconds + ttlSeconds,
|
|
28
|
+
id: claims.id,
|
|
29
|
+
...(claims.linkId !== undefined ? { linkId: claims.linkId } : {}),
|
|
30
|
+
};
|
|
31
|
+
const payloadJson = JSON.stringify(payload);
|
|
32
|
+
const body = b64urlEncode(Buffer.from(payloadJson, 'utf8'));
|
|
33
|
+
return `${body}.${this.sign(payloadJson)}`;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Verify a token against the expected purpose. Returns the claims on success,
|
|
37
|
+
* or `null` when the signature is invalid, the purpose mismatches, the token
|
|
38
|
+
* is malformed, or it has expired.
|
|
39
|
+
*/
|
|
40
|
+
verify(token, purpose, nowSeconds = Math.floor(Date.now() / 1000)) {
|
|
41
|
+
const dot = token.indexOf('.');
|
|
42
|
+
if (dot <= 0)
|
|
43
|
+
return null;
|
|
44
|
+
const body = token.slice(0, dot);
|
|
45
|
+
const sig = token.slice(dot + 1);
|
|
46
|
+
let payloadJson;
|
|
47
|
+
try {
|
|
48
|
+
payloadJson = b64urlDecode(body).toString('utf8');
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
const expectedSig = this.sign(payloadJson);
|
|
54
|
+
const a = Buffer.from(sig);
|
|
55
|
+
const b = Buffer.from(expectedSig);
|
|
56
|
+
if (a.length !== b.length || !timingSafeEqual(a, b))
|
|
57
|
+
return null;
|
|
58
|
+
let payload;
|
|
59
|
+
try {
|
|
60
|
+
payload = JSON.parse(payloadJson);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
if (payload.p !== purpose)
|
|
66
|
+
return null;
|
|
67
|
+
if (typeof payload.exp !== 'number' || payload.exp < nowSeconds)
|
|
68
|
+
return null;
|
|
69
|
+
if (typeof payload.id !== 'string' || payload.id.length === 0)
|
|
70
|
+
return null;
|
|
71
|
+
return payload.linkId !== undefined
|
|
72
|
+
? { id: payload.id, linkId: payload.linkId }
|
|
73
|
+
: { id: payload.id };
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=token.helper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token.helper.js","sourceRoot":"","sources":["../../../src/backend/services/token.helper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
/**
|
|
3
|
+
* Engagement tracking (feature 048, US6). Records open + click events against a
|
|
4
|
+
* send record and maintains the denormalized counters the stats service reads.
|
|
5
|
+
* Token minting/verification lives in NewsletterTokenHelper; the routes verify
|
|
6
|
+
* the signed token then call these methods with the resolved send-record id.
|
|
7
|
+
*/
|
|
8
|
+
export declare class NewsletterTrackingService {
|
|
9
|
+
private readonly emFactory;
|
|
10
|
+
constructor(emFactory: () => EntityManager);
|
|
11
|
+
recordOpen(sendRecordId: string): Promise<void>;
|
|
12
|
+
recordClick(sendRecordId: string, linkId: string | null, url: string | null): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=tracking.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracking.service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/tracking.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAI3D;;;;;GAKG;AACH,qBAAa,yBAAyB;IACxB,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,MAAM,aAAa;IAErD,UAAU,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW/C,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;CAgBlG"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { NewsletterSendRecord } from '../entities/newsletter-send-record.entity.js';
|
|
2
|
+
import { NewsletterEngagementEvent } from '../entities/newsletter-engagement-event.entity.js';
|
|
3
|
+
/**
|
|
4
|
+
* Engagement tracking (feature 048, US6). Records open + click events against a
|
|
5
|
+
* send record and maintains the denormalized counters the stats service reads.
|
|
6
|
+
* Token minting/verification lives in NewsletterTokenHelper; the routes verify
|
|
7
|
+
* the signed token then call these methods with the resolved send-record id.
|
|
8
|
+
*/
|
|
9
|
+
export class NewsletterTrackingService {
|
|
10
|
+
emFactory;
|
|
11
|
+
constructor(emFactory) {
|
|
12
|
+
this.emFactory = emFactory;
|
|
13
|
+
}
|
|
14
|
+
async recordOpen(sendRecordId) {
|
|
15
|
+
// command-coverage-ignore: engagement telemetry — records an email open,
|
|
16
|
+
// high-volume delivery tracking, not an audited domain-state mutation.
|
|
17
|
+
const em = this.emFactory();
|
|
18
|
+
const record = await em.findOne(NewsletterSendRecord, { id: sendRecordId });
|
|
19
|
+
if (!record)
|
|
20
|
+
return;
|
|
21
|
+
em.create(NewsletterEngagementEvent, { sendRecordId, type: 'open' });
|
|
22
|
+
if (record.openedAt === null)
|
|
23
|
+
record.openedAt = new Date();
|
|
24
|
+
await em.flush();
|
|
25
|
+
}
|
|
26
|
+
async recordClick(sendRecordId, linkId, url) {
|
|
27
|
+
// command-coverage-ignore: engagement telemetry — records a link click,
|
|
28
|
+
// high-volume delivery tracking, not an audited domain-state mutation.
|
|
29
|
+
const em = this.emFactory();
|
|
30
|
+
const record = await em.findOne(NewsletterSendRecord, { id: sendRecordId });
|
|
31
|
+
if (!record)
|
|
32
|
+
return;
|
|
33
|
+
em.create(NewsletterEngagementEvent, {
|
|
34
|
+
sendRecordId,
|
|
35
|
+
type: 'click',
|
|
36
|
+
linkId,
|
|
37
|
+
url,
|
|
38
|
+
});
|
|
39
|
+
record.clickCount += 1;
|
|
40
|
+
if (record.openedAt === null)
|
|
41
|
+
record.openedAt = new Date();
|
|
42
|
+
await em.flush();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=tracking.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracking.service.js","sourceRoot":"","sources":["../../../src/backend/services/tracking.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,8CAA8C,CAAC;AACpF,OAAO,EAAE,yBAAyB,EAAE,MAAM,mDAAmD,CAAC;AAE9F;;;;;GAKG;AACH,MAAM,OAAO,yBAAyB;IACP;IAA7B,YAA6B,SAA8B;QAA9B,cAAS,GAAT,SAAS,CAAqB;IAAG,CAAC;IAE/D,KAAK,CAAC,UAAU,CAAC,YAAoB;QACnC,yEAAyE;QACzE,uEAAuE;QACvE,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,oBAAoB,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;QAC5E,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,EAAE,CAAC,MAAM,CAAC,yBAAyB,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QACrE,IAAI,MAAM,CAAC,QAAQ,KAAK,IAAI;YAAE,MAAM,CAAC,QAAQ,GAAG,IAAI,IAAI,EAAE,CAAC;QAC3D,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,YAAoB,EAAE,MAAqB,EAAE,GAAkB;QAC/E,wEAAwE;QACxE,uEAAuE;QACvE,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,oBAAoB,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;QAC5E,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,EAAE,CAAC,MAAM,CAAC,yBAAyB,EAAE;YACnC,YAAY;YACZ,IAAI,EAAE,OAAO;YACb,MAAM;YACN,GAAG;SACJ,CAAC,CAAC;QACH,MAAM,CAAC,UAAU,IAAI,CAAC,CAAC;QACvB,IAAI,MAAM,CAAC,QAAQ,KAAK,IAAI;YAAE,MAAM,CAAC,QAAQ,GAAG,IAAI,IAAI,EAAE,CAAC;QAC3D,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC;CACF"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Newsletter module (feature 048). Owns the subscriber list, tags, custom
|
|
3
|
+
* fields, campaigns, linear automations, reusable email-safe blocks, the
|
|
4
|
+
* bulk-sending provider (SMTP / Amazon SES via nodemailer), and engagement
|
|
5
|
+
* tracking. Bulk delivery is independent of the transactional-email transport.
|
|
6
|
+
* The SMTP connection + credentials come from a reusable `email_adapter`
|
|
7
|
+
* credential configuration referenced by `newsletter.email_credentials`
|
|
8
|
+
* (feature 058); when unset, dispatch uses the console (dev) sink.
|
|
9
|
+
*/
|
|
10
|
+
export declare const newsletterSettingsManifest: {
|
|
11
|
+
moduleCode: string;
|
|
12
|
+
groups: {
|
|
13
|
+
code: string;
|
|
14
|
+
name: string;
|
|
15
|
+
salesChannelCodes?: string[] | undefined;
|
|
16
|
+
isSystemProtected?: boolean | undefined;
|
|
17
|
+
}[];
|
|
18
|
+
settings: {
|
|
19
|
+
code: string;
|
|
20
|
+
name: string;
|
|
21
|
+
valueType: "string" | "number" | "boolean" | "json" | "string_list" | "secret" | "credential_ref";
|
|
22
|
+
defaultValue: unknown;
|
|
23
|
+
description?: string | undefined;
|
|
24
|
+
groupCode?: string | undefined;
|
|
25
|
+
previousDefaultValues?: unknown[] | undefined;
|
|
26
|
+
salesChannelCodes?: string[] | undefined;
|
|
27
|
+
enumOptions?: string[] | undefined;
|
|
28
|
+
configurationType?: string | undefined;
|
|
29
|
+
hidden?: boolean | undefined;
|
|
30
|
+
}[];
|
|
31
|
+
};
|
|
32
|
+
export declare const manifest: {
|
|
33
|
+
id: string;
|
|
34
|
+
name: string;
|
|
35
|
+
version: string;
|
|
36
|
+
dependencies: string[];
|
|
37
|
+
description?: string | undefined;
|
|
38
|
+
acknowledgedDependencies?: {
|
|
39
|
+
moduleId: string;
|
|
40
|
+
port: string;
|
|
41
|
+
reason: string;
|
|
42
|
+
}[] | undefined;
|
|
43
|
+
nonBindingDependencies?: {
|
|
44
|
+
moduleId: string;
|
|
45
|
+
name: string;
|
|
46
|
+
kind: "contributes-to" | "degrades-without" | "refuses-without";
|
|
47
|
+
reason: string;
|
|
48
|
+
whenAbsent?: string | undefined;
|
|
49
|
+
}[] | undefined;
|
|
50
|
+
activation?: {
|
|
51
|
+
settingCode: string;
|
|
52
|
+
default: boolean;
|
|
53
|
+
} | {
|
|
54
|
+
nonDeactivatable: true;
|
|
55
|
+
reason: string;
|
|
56
|
+
} | undefined;
|
|
57
|
+
settings?: {
|
|
58
|
+
moduleCode: string;
|
|
59
|
+
groups: {
|
|
60
|
+
code: string;
|
|
61
|
+
name: string;
|
|
62
|
+
salesChannelCodes?: string[] | undefined;
|
|
63
|
+
isSystemProtected?: boolean | undefined;
|
|
64
|
+
}[];
|
|
65
|
+
settings: {
|
|
66
|
+
code: string;
|
|
67
|
+
name: string;
|
|
68
|
+
valueType: "string" | "number" | "boolean" | "json" | "string_list" | "secret" | "credential_ref";
|
|
69
|
+
defaultValue: unknown;
|
|
70
|
+
description?: string | undefined;
|
|
71
|
+
groupCode?: string | undefined;
|
|
72
|
+
previousDefaultValues?: unknown[] | undefined;
|
|
73
|
+
salesChannelCodes?: string[] | undefined;
|
|
74
|
+
enumOptions?: string[] | undefined;
|
|
75
|
+
configurationType?: string | undefined;
|
|
76
|
+
hidden?: boolean | undefined;
|
|
77
|
+
}[];
|
|
78
|
+
} | undefined;
|
|
79
|
+
i18n?: {
|
|
80
|
+
bundlesDir: string;
|
|
81
|
+
} | undefined;
|
|
82
|
+
docs?: false | {
|
|
83
|
+
dir: string;
|
|
84
|
+
} | undefined;
|
|
85
|
+
demo?: false | {
|
|
86
|
+
summary: string;
|
|
87
|
+
seed: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoSeedResult>;
|
|
88
|
+
reset: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoResetResult>;
|
|
89
|
+
after?: readonly string[] | undefined;
|
|
90
|
+
package?: string | undefined;
|
|
91
|
+
} | undefined;
|
|
92
|
+
actions?: {
|
|
93
|
+
id: string;
|
|
94
|
+
labelKey: string;
|
|
95
|
+
icon: "Plus" | "Sparkles" | "Settings" | "Search" | "Boxes" | "Layers" | "Menu" | "PlusCircle" | "PlusSquare" | "FilePlus" | "FolderPlus" | "Upload" | "FileUp" | "CloudUpload" | "Download" | "FileDown" | "FileText" | "BookOpen" | "Rss" | "Package" | "Tag" | "ShoppingCart" | "Receipt" | "CreditCard" | "Users" | "UserPlus" | "Inbox" | "ListChecks" | "ClipboardList" | "Image" | "Video" | "LayoutDashboard" | "PanelLeft" | "KeyRound" | "ShieldCheck" | "Edit" | "Archive" | "Box" | "Truck" | "CircleDollarSign" | "Activity" | "LineChart" | "Smartphone" | "Webhook" | "Scale" | "PlugZap" | "PercentDiamond" | "Newspaper" | "Languages" | "Eraser" | "Warehouse" | "TrendingDown" | "Bell" | "PackageOpen" | "Building2" | "Store" | "ClipboardCheck";
|
|
96
|
+
targetRoute: string;
|
|
97
|
+
keywords: string[];
|
|
98
|
+
weight: number;
|
|
99
|
+
descriptionKey?: string | undefined;
|
|
100
|
+
requiredPermission?: string | undefined;
|
|
101
|
+
}[] | undefined;
|
|
102
|
+
permissions?: {
|
|
103
|
+
code: string;
|
|
104
|
+
label: string;
|
|
105
|
+
module?: string | undefined;
|
|
106
|
+
description?: string | undefined;
|
|
107
|
+
requires?: string[] | undefined;
|
|
108
|
+
}[] | undefined;
|
|
109
|
+
transactionalEmails?: {
|
|
110
|
+
code: string;
|
|
111
|
+
name: string;
|
|
112
|
+
variables: {
|
|
113
|
+
key: string;
|
|
114
|
+
label: string;
|
|
115
|
+
sampleValue?: string | undefined;
|
|
116
|
+
description?: string | undefined;
|
|
117
|
+
}[];
|
|
118
|
+
description?: string | undefined;
|
|
119
|
+
group?: string | undefined;
|
|
120
|
+
}[] | undefined;
|
|
121
|
+
capabilities?: string[] | undefined;
|
|
122
|
+
exclusiveCapabilities?: {
|
|
123
|
+
key: string;
|
|
124
|
+
errorCode: string;
|
|
125
|
+
}[] | undefined;
|
|
126
|
+
errorCodes?: {
|
|
127
|
+
code: string;
|
|
128
|
+
tokens?: string[] | undefined;
|
|
129
|
+
}[] | undefined;
|
|
130
|
+
blocks?: {
|
|
131
|
+
name: string;
|
|
132
|
+
labelKey: string;
|
|
133
|
+
category: string;
|
|
134
|
+
contexts: ("newsletter" | "cms" | "email" | "invoice")[];
|
|
135
|
+
fields: Record<string, {
|
|
136
|
+
type: "number" | "object" | "array" | "text" | "textarea" | "select" | "radio" | "external" | "uuid" | "richtext";
|
|
137
|
+
label?: string | undefined;
|
|
138
|
+
required?: boolean | undefined;
|
|
139
|
+
options?: {
|
|
140
|
+
label: string;
|
|
141
|
+
value: string | number;
|
|
142
|
+
}[] | undefined;
|
|
143
|
+
refKind?: string | undefined;
|
|
144
|
+
}>;
|
|
145
|
+
descriptionKey?: string | undefined;
|
|
146
|
+
defaultProps?: Record<string, unknown> | undefined;
|
|
147
|
+
responsiveFields?: string[] | undefined;
|
|
148
|
+
previewIcon?: string | undefined;
|
|
149
|
+
weight?: number | undefined;
|
|
150
|
+
}[] | undefined;
|
|
151
|
+
blockCategories?: {
|
|
152
|
+
key: string;
|
|
153
|
+
titleKey: string;
|
|
154
|
+
contexts: ("newsletter" | "cms" | "email" | "invoice")[];
|
|
155
|
+
weight?: number | undefined;
|
|
156
|
+
visible?: boolean | undefined;
|
|
157
|
+
}[] | undefined;
|
|
158
|
+
env?: {
|
|
159
|
+
name: string;
|
|
160
|
+
describes: {
|
|
161
|
+
en: string;
|
|
162
|
+
pl: string;
|
|
163
|
+
};
|
|
164
|
+
requirement: {
|
|
165
|
+
kind: "required";
|
|
166
|
+
} | {
|
|
167
|
+
kind: "requiredWhen";
|
|
168
|
+
input: string;
|
|
169
|
+
equals: string;
|
|
170
|
+
} | {
|
|
171
|
+
kind: "optional";
|
|
172
|
+
without: {
|
|
173
|
+
en: string;
|
|
174
|
+
pl: string;
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
secret: boolean;
|
|
178
|
+
generable: boolean;
|
|
179
|
+
owner: {
|
|
180
|
+
kind: "platform";
|
|
181
|
+
} | {
|
|
182
|
+
kind: "application";
|
|
183
|
+
application: "admin" | "backend" | "storefront";
|
|
184
|
+
} | {
|
|
185
|
+
kind: "module";
|
|
186
|
+
moduleId: string;
|
|
187
|
+
};
|
|
188
|
+
consumers: ("admin" | "backend" | "storefront")[];
|
|
189
|
+
addressOf: "admin" | "backend" | "storefront" | null;
|
|
190
|
+
}[] | undefined;
|
|
191
|
+
};
|
|
192
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;CAqErC,CAAC;AAEH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmInB,CAAC"}
|