@endora-commerce/contracts 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 +34 -0
- package/dist/actor.d.ts +79 -0
- package/dist/actor.d.ts.map +1 -0
- package/dist/actor.js +41 -0
- package/dist/actor.js.map +1 -0
- package/dist/addresses.d.ts +134 -0
- package/dist/addresses.d.ts.map +1 -0
- package/dist/addresses.js +16 -0
- package/dist/addresses.js.map +1 -0
- package/dist/admin-actions.d.ts +367 -0
- package/dist/admin-actions.d.ts.map +1 -0
- package/dist/admin-actions.js +287 -0
- package/dist/admin-actions.js.map +1 -0
- package/dist/admin-contributions.d.ts +518 -0
- package/dist/admin-contributions.d.ts.map +1 -0
- package/dist/admin-contributions.js +495 -0
- package/dist/admin-contributions.js.map +1 -0
- package/dist/admin-i18n.d.ts +135 -0
- package/dist/admin-i18n.d.ts.map +1 -0
- package/dist/admin-i18n.js +72 -0
- package/dist/admin-i18n.js.map +1 -0
- package/dist/admin-notifications.d.ts +55 -0
- package/dist/admin-notifications.d.ts.map +1 -0
- package/dist/admin-notifications.js +16 -0
- package/dist/admin-notifications.js.map +1 -0
- package/dist/admin-roles.d.ts +125 -0
- package/dist/admin-roles.d.ts.map +1 -0
- package/dist/admin-roles.js +2 -0
- package/dist/admin-roles.js.map +1 -0
- package/dist/admin-users.d.ts +178 -0
- package/dist/admin-users.d.ts.map +1 -0
- package/dist/admin-users.js +14 -0
- package/dist/admin-users.js.map +1 -0
- package/dist/admin.d.ts +243 -0
- package/dist/admin.d.ts.map +1 -0
- package/dist/admin.js +246 -0
- package/dist/admin.js.map +1 -0
- package/dist/analytics.d.ts +123 -0
- package/dist/analytics.d.ts.map +1 -0
- package/dist/analytics.js +68 -0
- package/dist/analytics.js.map +1 -0
- package/dist/api-keys.d.ts +97 -0
- package/dist/api-keys.d.ts.map +1 -0
- package/dist/api-keys.js +64 -0
- package/dist/api-keys.js.map +1 -0
- package/dist/assets-library.d.ts +684 -0
- package/dist/assets-library.d.ts.map +1 -0
- package/dist/assets-library.js +181 -0
- package/dist/assets-library.js.map +1 -0
- package/dist/audit-logs.d.ts +141 -0
- package/dist/audit-logs.d.ts.map +1 -0
- package/dist/audit-logs.js +31 -0
- package/dist/audit-logs.js.map +1 -0
- package/dist/auth.d.ts +174 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +27 -0
- package/dist/auth.js.map +1 -0
- package/dist/blog.d.ts +669 -0
- package/dist/blog.d.ts.map +1 -0
- package/dist/blog.js +360 -0
- package/dist/blog.js.map +1 -0
- package/dist/capabilities.d.ts +40 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +38 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/carts.d.ts +1367 -0
- package/dist/carts.d.ts.map +1 -0
- package/dist/carts.js +405 -0
- package/dist/carts.js.map +1 -0
- package/dist/catalog.d.ts +2855 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/catalog.js +1543 -0
- package/dist/catalog.js.map +1 -0
- package/dist/cms.d.ts +872 -0
- package/dist/cms.d.ts.map +1 -0
- package/dist/cms.js +468 -0
- package/dist/cms.js.map +1 -0
- package/dist/common.d.ts +82 -0
- package/dist/common.d.ts.map +1 -0
- package/dist/common.js +72 -0
- package/dist/common.js.map +1 -0
- package/dist/comparisons.d.ts +487 -0
- package/dist/comparisons.d.ts.map +1 -0
- package/dist/comparisons.js +221 -0
- package/dist/comparisons.js.map +1 -0
- package/dist/credentials.d.ts +292 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +142 -0
- package/dist/credentials.js.map +1 -0
- package/dist/credit-limits.d.ts +111 -0
- package/dist/credit-limits.d.ts.map +1 -0
- package/dist/credit-limits.js +35 -0
- package/dist/credit-limits.js.map +1 -0
- package/dist/currencies.d.ts +127 -0
- package/dist/currencies.d.ts.map +1 -0
- package/dist/currencies.js +20 -0
- package/dist/currencies.js.map +1 -0
- package/dist/custom-fields.d.ts +345 -0
- package/dist/custom-fields.d.ts.map +1 -0
- package/dist/custom-fields.js +185 -0
- package/dist/custom-fields.js.map +1 -0
- package/dist/customer-accounts.d.ts +690 -0
- package/dist/customer-accounts.d.ts.map +1 -0
- package/dist/customer-accounts.js +41 -0
- package/dist/customer-accounts.js.map +1 -0
- package/dist/customers.d.ts +305 -0
- package/dist/customers.d.ts.map +1 -0
- package/dist/customers.js +158 -0
- package/dist/customers.js.map +1 -0
- package/dist/dictionary.d.ts +580 -0
- package/dist/dictionary.d.ts.map +1 -0
- package/dist/dictionary.js +297 -0
- package/dist/dictionary.js.map +1 -0
- package/dist/email-address.d.ts +62 -0
- package/dist/email-address.d.ts.map +1 -0
- package/dist/email-address.js +64 -0
- package/dist/email-address.js.map +1 -0
- package/dist/email.d.ts +175 -0
- package/dist/email.d.ts.map +1 -0
- package/dist/email.js +45 -0
- package/dist/email.js.map +1 -0
- package/dist/envelopes.d.ts +15 -0
- package/dist/envelopes.d.ts.map +1 -0
- package/dist/envelopes.js +16 -0
- package/dist/envelopes.js.map +1 -0
- package/dist/environment-inputs.d.ts +306 -0
- package/dist/environment-inputs.d.ts.map +1 -0
- package/dist/environment-inputs.js +277 -0
- package/dist/environment-inputs.js.map +1 -0
- package/dist/erp-connector.d.ts +52 -0
- package/dist/erp-connector.d.ts.map +1 -0
- package/dist/erp-connector.js +34 -0
- package/dist/erp-connector.js.map +1 -0
- package/dist/errors.d.ts +455 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +532 -0
- package/dist/errors.js.map +1 -0
- package/dist/google-analytics.d.ts +181 -0
- package/dist/google-analytics.d.ts.map +1 -0
- package/dist/google-analytics.js +176 -0
- package/dist/google-analytics.js.map +1 -0
- package/dist/google-tag-manager.d.ts +111 -0
- package/dist/google-tag-manager.d.ts.map +1 -0
- package/dist/google-tag-manager.js +129 -0
- package/dist/google-tag-manager.js.map +1 -0
- package/dist/i18n.d.ts +69 -0
- package/dist/i18n.d.ts.map +1 -0
- package/dist/i18n.js +59 -0
- package/dist/i18n.js.map +1 -0
- package/dist/import-export.d.ts +63 -0
- package/dist/import-export.d.ts.map +1 -0
- package/dist/import-export.js +37 -0
- package/dist/import-export.js.map +1 -0
- package/dist/index.d.ts +82 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +126 -0
- package/dist/index.js.map +1 -0
- package/dist/inventory.d.ts +673 -0
- package/dist/inventory.d.ts.map +1 -0
- package/dist/inventory.js +412 -0
- package/dist/inventory.js.map +1 -0
- package/dist/invoice-ledger.d.ts +366 -0
- package/dist/invoice-ledger.d.ts.map +1 -0
- package/dist/invoice-ledger.js +114 -0
- package/dist/invoice-ledger.js.map +1 -0
- package/dist/invoices.d.ts +845 -0
- package/dist/invoices.d.ts.map +1 -0
- package/dist/invoices.js +314 -0
- package/dist/invoices.js.map +1 -0
- package/dist/kernel.d.ts +49 -0
- package/dist/kernel.d.ts.map +1 -0
- package/dist/kernel.js +19 -0
- package/dist/kernel.js.map +1 -0
- package/dist/languages.d.ts +122 -0
- package/dist/languages.d.ts.map +1 -0
- package/dist/languages.js +24 -0
- package/dist/languages.js.map +1 -0
- package/dist/linkedin-ads.d.ts +167 -0
- package/dist/linkedin-ads.d.ts.map +1 -0
- package/dist/linkedin-ads.js +156 -0
- package/dist/linkedin-ads.js.map +1 -0
- package/dist/megamenu.d.ts +556 -0
- package/dist/megamenu.d.ts.map +1 -0
- package/dist/megamenu.js +186 -0
- package/dist/megamenu.js.map +1 -0
- package/dist/meta-ads.d.ts +126 -0
- package/dist/meta-ads.d.ts.map +1 -0
- package/dist/meta-ads.js +112 -0
- package/dist/meta-ads.js.map +1 -0
- package/dist/mfa.d.ts +274 -0
- package/dist/mfa.d.ts.map +1 -0
- package/dist/mfa.js +187 -0
- package/dist/mfa.js.map +1 -0
- package/dist/modules.d.ts +1706 -0
- package/dist/modules.d.ts.map +1 -0
- package/dist/modules.js +1390 -0
- package/dist/modules.js.map +1 -0
- package/dist/newsletter.d.ts +611 -0
- package/dist/newsletter.d.ts.map +1 -0
- package/dist/newsletter.js +345 -0
- package/dist/newsletter.js.map +1 -0
- package/dist/orders.d.ts +1175 -0
- package/dist/orders.d.ts.map +1 -0
- package/dist/orders.js +630 -0
- package/dist/orders.js.map +1 -0
- package/dist/organizations.d.ts +938 -0
- package/dist/organizations.d.ts.map +1 -0
- package/dist/organizations.js +418 -0
- package/dist/organizations.js.map +1 -0
- package/dist/pagination.d.ts +21 -0
- package/dist/pagination.d.ts.map +1 -0
- package/dist/pagination.js +22 -0
- package/dist/pagination.js.map +1 -0
- package/dist/payment-methods.d.ts +472 -0
- package/dist/payment-methods.d.ts.map +1 -0
- package/dist/payment-methods.js +175 -0
- package/dist/payment-methods.js.map +1 -0
- package/dist/payment-return-url.d.ts +53 -0
- package/dist/payment-return-url.d.ts.map +1 -0
- package/dist/payment-return-url.js +35 -0
- package/dist/payment-return-url.js.map +1 -0
- package/dist/payments.d.ts +386 -0
- package/dist/payments.d.ts.map +1 -0
- package/dist/payments.js +84 -0
- package/dist/payments.js.map +1 -0
- package/dist/pim-connector.d.ts +60 -0
- package/dist/pim-connector.d.ts.map +1 -0
- package/dist/pim-connector.js +43 -0
- package/dist/pim-connector.js.map +1 -0
- package/dist/pim-field-path.d.ts +6 -0
- package/dist/pim-field-path.d.ts.map +1 -0
- package/dist/pim-field-path.js +101 -0
- package/dist/pim-field-path.js.map +1 -0
- package/dist/platform-language.d.ts +20 -0
- package/dist/platform-language.d.ts.map +1 -0
- package/dist/platform-language.js +22 -0
- package/dist/platform-language.js.map +1 -0
- package/dist/price-lists.d.ts +685 -0
- package/dist/price-lists.d.ts.map +1 -0
- package/dist/price-lists.js +330 -0
- package/dist/price-lists.js.map +1 -0
- package/dist/product-feeds.d.ts +2837 -0
- package/dist/product-feeds.d.ts.map +1 -0
- package/dist/product-feeds.js +1504 -0
- package/dist/product-feeds.js.map +1 -0
- package/dist/product-scope-overrides.d.ts +134 -0
- package/dist/product-scope-overrides.d.ts.map +1 -0
- package/dist/product-scope-overrides.js +82 -0
- package/dist/product-scope-overrides.js.map +1 -0
- package/dist/product-value-resolver.d.ts +88 -0
- package/dist/product-value-resolver.d.ts.map +1 -0
- package/dist/product-value-resolver.js +128 -0
- package/dist/product-value-resolver.js.map +1 -0
- package/dist/promotions.d.ts +678 -0
- package/dist/promotions.d.ts.map +1 -0
- package/dist/promotions.js +479 -0
- package/dist/promotions.js.map +1 -0
- package/dist/prompt-actions.d.ts +582 -0
- package/dist/prompt-actions.d.ts.map +1 -0
- package/dist/prompt-actions.js +221 -0
- package/dist/prompt-actions.js.map +1 -0
- package/dist/pwa.d.ts +293 -0
- package/dist/pwa.d.ts.map +1 -0
- package/dist/pwa.js +204 -0
- package/dist/pwa.js.map +1 -0
- package/dist/quick-order.d.ts +340 -0
- package/dist/quick-order.d.ts.map +1 -0
- package/dist/quick-order.js +177 -0
- package/dist/quick-order.js.map +1 -0
- package/dist/quote-requests.d.ts +538 -0
- package/dist/quote-requests.d.ts.map +1 -0
- package/dist/quote-requests.js +308 -0
- package/dist/quote-requests.js.map +1 -0
- package/dist/returns.d.ts +774 -0
- package/dist/returns.d.ts.map +1 -0
- package/dist/returns.js +389 -0
- package/dist/returns.js.map +1 -0
- package/dist/sales-channels.d.ts +392 -0
- package/dist/sales-channels.d.ts.map +1 -0
- package/dist/sales-channels.js +285 -0
- package/dist/sales-channels.js.map +1 -0
- package/dist/scope-notice.d.ts +60 -0
- package/dist/scope-notice.d.ts.map +1 -0
- package/dist/scope-notice.js +56 -0
- package/dist/scope-notice.js.map +1 -0
- package/dist/search.d.ts +321 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +160 -0
- package/dist/search.js.map +1 -0
- package/dist/seo.d.ts +113 -0
- package/dist/seo.d.ts.map +1 -0
- package/dist/seo.js +63 -0
- package/dist/seo.js.map +1 -0
- package/dist/settings.d.ts +453 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +337 -0
- package/dist/settings.js.map +1 -0
- package/dist/shipments.d.ts +140 -0
- package/dist/shipments.d.ts.map +1 -0
- package/dist/shipments.js +14 -0
- package/dist/shipments.js.map +1 -0
- package/dist/shipping-methods.d.ts +350 -0
- package/dist/shipping-methods.d.ts.map +1 -0
- package/dist/shipping-methods.js +99 -0
- package/dist/shipping-methods.js.map +1 -0
- package/dist/shopping-lists.d.ts +122 -0
- package/dist/shopping-lists.d.ts.map +1 -0
- package/dist/shopping-lists.js +92 -0
- package/dist/shopping-lists.js.map +1 -0
- package/dist/taxes.d.ts +106 -0
- package/dist/taxes.d.ts.map +1 -0
- package/dist/taxes.js +80 -0
- package/dist/taxes.js.map +1 -0
- package/dist/text-normalization.d.ts +199 -0
- package/dist/text-normalization.d.ts.map +1 -0
- package/dist/text-normalization.js +205 -0
- package/dist/text-normalization.js.map +1 -0
- package/dist/transactional-emails.d.ts +459 -0
- package/dist/transactional-emails.d.ts.map +1 -0
- package/dist/transactional-emails.js +212 -0
- package/dist/transactional-emails.js.map +1 -0
- package/dist/webhooks.d.ts +69 -0
- package/dist/webhooks.d.ts.map +1 -0
- package/dist/webhooks.js +53 -0
- package/dist/webhooks.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { isoDateTimeSchema } from './common.js';
|
|
3
|
+
/**
|
|
4
|
+
* Dictionary contracts (feature 017).
|
|
5
|
+
*
|
|
6
|
+
* Single source of truth for the platform-wide registry of Countries, Currencies,
|
|
7
|
+
* and Languages, plus per-entry display-label translations and the typed
|
|
8
|
+
* cross-module DictionaryValidator port (see contracts/dictionary-validation.contract.md).
|
|
9
|
+
*
|
|
10
|
+
* Currency and Language codes follow the same regexes used by the legacy
|
|
11
|
+
* `i18n.ts` module (BCP-47 subset + ISO 4217). Country codes are ISO 3166-1
|
|
12
|
+
* alpha-2 — the convention already adopted by Address, Tax, Warehouse, and
|
|
13
|
+
* Organisation.
|
|
14
|
+
*/
|
|
15
|
+
const COUNTRY_CODE = z
|
|
16
|
+
.string()
|
|
17
|
+
.regex(/^[A-Z]{2}$/, 'ISO 3166-1 alpha-2 country code');
|
|
18
|
+
const CURRENCY_CODE = z
|
|
19
|
+
.string()
|
|
20
|
+
.regex(/^[A-Z]{3}$/, 'ISO 4217 currency code');
|
|
21
|
+
const LANGUAGE_CODE = z
|
|
22
|
+
.string()
|
|
23
|
+
.regex(/^[a-z]{2,3}(-[A-Z]{2})?$/, 'BCP-47 language tag');
|
|
24
|
+
const ALPHA3_CODE = z
|
|
25
|
+
.string()
|
|
26
|
+
.regex(/^[A-Z]{3}$/, 'ISO 3166-1 alpha-3 country code');
|
|
27
|
+
const NUMERIC_CODE = z
|
|
28
|
+
.string()
|
|
29
|
+
.regex(/^[0-9]{3}$/, 'ISO 3166-1 numeric country code (zero-padded)');
|
|
30
|
+
const DIAL_CODE = z
|
|
31
|
+
.string()
|
|
32
|
+
.regex(/^\+\d{1,4}$/, 'International dial code prefixed with "+"');
|
|
33
|
+
export const REGIONS = [
|
|
34
|
+
'Africa',
|
|
35
|
+
'Americas',
|
|
36
|
+
'Asia',
|
|
37
|
+
'Europe',
|
|
38
|
+
'Oceania',
|
|
39
|
+
'Antarctic',
|
|
40
|
+
];
|
|
41
|
+
export const regionSchema = z.enum(REGIONS);
|
|
42
|
+
export const symbolPositionSchema = z.enum(['prefix', 'suffix']);
|
|
43
|
+
export const dictionaryEntryTypeSchema = z.enum(['country', 'currency', 'language']);
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Country
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
export const countrySchema = z.object({
|
|
48
|
+
code: COUNTRY_CODE,
|
|
49
|
+
alpha3Code: ALPHA3_CODE,
|
|
50
|
+
numericCode: NUMERIC_CODE,
|
|
51
|
+
label: z.string().min(1).max(120),
|
|
52
|
+
region: regionSchema,
|
|
53
|
+
subregion: z.string().max(64).nullable(),
|
|
54
|
+
dialCode: DIAL_CODE.nullable(),
|
|
55
|
+
isEuMember: z.boolean(),
|
|
56
|
+
defaultCurrencyCode: CURRENCY_CODE.nullable(),
|
|
57
|
+
isActive: z.boolean(),
|
|
58
|
+
isDefault: z.boolean(),
|
|
59
|
+
sortOrder: z.number().int(),
|
|
60
|
+
createdAt: isoDateTimeSchema,
|
|
61
|
+
updatedAt: isoDateTimeSchema,
|
|
62
|
+
});
|
|
63
|
+
export const createCountryRequestSchema = z.object({
|
|
64
|
+
code: COUNTRY_CODE,
|
|
65
|
+
alpha3Code: ALPHA3_CODE,
|
|
66
|
+
numericCode: NUMERIC_CODE,
|
|
67
|
+
label: z.string().min(1).max(120),
|
|
68
|
+
region: regionSchema,
|
|
69
|
+
subregion: z.string().max(64).optional(),
|
|
70
|
+
dialCode: DIAL_CODE.optional(),
|
|
71
|
+
isEuMember: z.boolean().optional(),
|
|
72
|
+
defaultCurrencyCode: CURRENCY_CODE.optional(),
|
|
73
|
+
isActive: z.boolean().optional(),
|
|
74
|
+
sortOrder: z.number().int().optional(),
|
|
75
|
+
});
|
|
76
|
+
export const updateCountryRequestSchema = z.object({
|
|
77
|
+
alpha3Code: ALPHA3_CODE.optional(),
|
|
78
|
+
numericCode: NUMERIC_CODE.optional(),
|
|
79
|
+
label: z.string().min(1).max(120).optional(),
|
|
80
|
+
region: regionSchema.optional(),
|
|
81
|
+
subregion: z.string().max(64).nullable().optional(),
|
|
82
|
+
dialCode: DIAL_CODE.nullable().optional(),
|
|
83
|
+
isEuMember: z.boolean().optional(),
|
|
84
|
+
defaultCurrencyCode: CURRENCY_CODE.nullable().optional(),
|
|
85
|
+
isActive: z.boolean().optional(),
|
|
86
|
+
sortOrder: z.number().int().optional(),
|
|
87
|
+
});
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
// Currency (extended) — backwards compatible with i18n.ts `currencySchema`
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
export const dictionaryCurrencySchema = z.object({
|
|
92
|
+
code: CURRENCY_CODE,
|
|
93
|
+
label: z.string().min(1).max(64),
|
|
94
|
+
symbol: z.string().min(1).max(8),
|
|
95
|
+
symbolPosition: symbolPositionSchema,
|
|
96
|
+
decimalPlaces: z.number().int().min(0).max(6),
|
|
97
|
+
isDefault: z.boolean(),
|
|
98
|
+
isActive: z.boolean(),
|
|
99
|
+
sortOrder: z.number().int(),
|
|
100
|
+
createdAt: isoDateTimeSchema,
|
|
101
|
+
updatedAt: isoDateTimeSchema,
|
|
102
|
+
});
|
|
103
|
+
export const createDictionaryCurrencyRequestSchema = z.object({
|
|
104
|
+
code: CURRENCY_CODE,
|
|
105
|
+
label: z.string().min(1).max(64),
|
|
106
|
+
symbol: z.string().min(1).max(8),
|
|
107
|
+
symbolPosition: symbolPositionSchema.optional(),
|
|
108
|
+
decimalPlaces: z.number().int().min(0).max(6).optional(),
|
|
109
|
+
isActive: z.boolean().optional(),
|
|
110
|
+
sortOrder: z.number().int().optional(),
|
|
111
|
+
});
|
|
112
|
+
export const updateDictionaryCurrencyRequestSchema = z.object({
|
|
113
|
+
label: z.string().min(1).max(64).optional(),
|
|
114
|
+
symbol: z.string().min(1).max(8).optional(),
|
|
115
|
+
symbolPosition: symbolPositionSchema.optional(),
|
|
116
|
+
decimalPlaces: z.number().int().min(0).max(6).optional(),
|
|
117
|
+
isActive: z.boolean().optional(),
|
|
118
|
+
sortOrder: z.number().int().optional(),
|
|
119
|
+
});
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
// Language (extended) — backwards compatible with i18n.ts `languageSchema`
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
export const dictionaryLanguageSchema = z.object({
|
|
124
|
+
code: LANGUAGE_CODE,
|
|
125
|
+
label: z.string().min(1).max(64),
|
|
126
|
+
nativeLabel: z.string().min(1).max(64),
|
|
127
|
+
isRtl: z.boolean(),
|
|
128
|
+
fallbackCode: LANGUAGE_CODE.nullable(),
|
|
129
|
+
isDefault: z.boolean(),
|
|
130
|
+
isActive: z.boolean(),
|
|
131
|
+
sortOrder: z.number().int(),
|
|
132
|
+
countries: z.array(COUNTRY_CODE),
|
|
133
|
+
createdAt: isoDateTimeSchema,
|
|
134
|
+
updatedAt: isoDateTimeSchema,
|
|
135
|
+
});
|
|
136
|
+
export const createDictionaryLanguageRequestSchema = z.object({
|
|
137
|
+
code: LANGUAGE_CODE,
|
|
138
|
+
label: z.string().min(1).max(64),
|
|
139
|
+
nativeLabel: z.string().min(1).max(64),
|
|
140
|
+
isRtl: z.boolean().optional(),
|
|
141
|
+
fallbackCode: LANGUAGE_CODE.optional(),
|
|
142
|
+
isActive: z.boolean().optional(),
|
|
143
|
+
sortOrder: z.number().int().optional(),
|
|
144
|
+
});
|
|
145
|
+
export const updateDictionaryLanguageRequestSchema = z.object({
|
|
146
|
+
label: z.string().min(1).max(64).optional(),
|
|
147
|
+
nativeLabel: z.string().min(1).max(64).optional(),
|
|
148
|
+
isRtl: z.boolean().optional(),
|
|
149
|
+
fallbackCode: LANGUAGE_CODE.nullable().optional(),
|
|
150
|
+
isActive: z.boolean().optional(),
|
|
151
|
+
sortOrder: z.number().int().optional(),
|
|
152
|
+
});
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
// Translations
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
export const dictionaryTranslationSchema = z.object({
|
|
157
|
+
entryType: dictionaryEntryTypeSchema,
|
|
158
|
+
entryCode: z.string().min(1).max(12),
|
|
159
|
+
languageCode: LANGUAGE_CODE,
|
|
160
|
+
label: z.string().min(1).max(160),
|
|
161
|
+
createdAt: isoDateTimeSchema,
|
|
162
|
+
updatedAt: isoDateTimeSchema,
|
|
163
|
+
});
|
|
164
|
+
export const upsertTranslationRequestSchema = z.object({
|
|
165
|
+
label: z.string().min(1).max(160),
|
|
166
|
+
});
|
|
167
|
+
// ---------------------------------------------------------------------------
|
|
168
|
+
// Language ↔ Country association
|
|
169
|
+
// ---------------------------------------------------------------------------
|
|
170
|
+
export const languageCountrySchema = z.object({
|
|
171
|
+
languageCode: LANGUAGE_CODE,
|
|
172
|
+
countryCode: COUNTRY_CODE,
|
|
173
|
+
isPrimary: z.boolean(),
|
|
174
|
+
createdAt: isoDateTimeSchema,
|
|
175
|
+
});
|
|
176
|
+
export const upsertLanguageCountryRequestSchema = z.object({
|
|
177
|
+
isPrimary: z.boolean().optional(),
|
|
178
|
+
});
|
|
179
|
+
// ---------------------------------------------------------------------------
|
|
180
|
+
// Pagination wrappers (admin list endpoints)
|
|
181
|
+
// ---------------------------------------------------------------------------
|
|
182
|
+
const paginationMetaSchema = z.object({
|
|
183
|
+
page: z.number().int().min(1),
|
|
184
|
+
pageSize: z.number().int().min(1).max(250),
|
|
185
|
+
total: z.number().int().min(0),
|
|
186
|
+
});
|
|
187
|
+
export const dictionaryCountriesPageResponseSchema = z.object({
|
|
188
|
+
data: z.array(countrySchema),
|
|
189
|
+
pagination: paginationMetaSchema,
|
|
190
|
+
});
|
|
191
|
+
export const dictionaryCurrenciesPageResponseSchema = z.object({
|
|
192
|
+
data: z.array(dictionaryCurrencySchema),
|
|
193
|
+
pagination: paginationMetaSchema,
|
|
194
|
+
});
|
|
195
|
+
export const dictionaryLanguagesPageResponseSchema = z.object({
|
|
196
|
+
data: z.array(dictionaryLanguageSchema),
|
|
197
|
+
pagination: paginationMetaSchema,
|
|
198
|
+
});
|
|
199
|
+
// ---------------------------------------------------------------------------
|
|
200
|
+
// Storefront read facade — locale-resolved labels
|
|
201
|
+
// ---------------------------------------------------------------------------
|
|
202
|
+
export const resolvedCountrySchema = z.object({
|
|
203
|
+
code: COUNTRY_CODE,
|
|
204
|
+
label: z.string(),
|
|
205
|
+
alpha3Code: ALPHA3_CODE,
|
|
206
|
+
numericCode: NUMERIC_CODE,
|
|
207
|
+
region: regionSchema,
|
|
208
|
+
subregion: z.string().nullable(),
|
|
209
|
+
dialCode: z.string().nullable(),
|
|
210
|
+
isEuMember: z.boolean(),
|
|
211
|
+
defaultCurrencyCode: CURRENCY_CODE.nullable(),
|
|
212
|
+
sortOrder: z.number().int(),
|
|
213
|
+
});
|
|
214
|
+
export const resolvedCurrencySchema = z.object({
|
|
215
|
+
code: CURRENCY_CODE,
|
|
216
|
+
label: z.string(),
|
|
217
|
+
symbol: z.string(),
|
|
218
|
+
symbolPosition: symbolPositionSchema,
|
|
219
|
+
decimalPlaces: z.number().int().min(0).max(6),
|
|
220
|
+
sortOrder: z.number().int(),
|
|
221
|
+
});
|
|
222
|
+
export const resolvedLanguageSchema = z.object({
|
|
223
|
+
code: LANGUAGE_CODE,
|
|
224
|
+
label: z.string(),
|
|
225
|
+
nativeLabel: z.string(),
|
|
226
|
+
isRtl: z.boolean(),
|
|
227
|
+
fallbackCode: LANGUAGE_CODE.nullable(),
|
|
228
|
+
countries: z.array(COUNTRY_CODE),
|
|
229
|
+
sortOrder: z.number().int(),
|
|
230
|
+
});
|
|
231
|
+
export const dictionaryRegistryResponseSchema = z.object({
|
|
232
|
+
data: z.object({
|
|
233
|
+
countries: z.array(resolvedCountrySchema),
|
|
234
|
+
currencies: z.array(resolvedCurrencySchema),
|
|
235
|
+
languages: z.array(resolvedLanguageSchema),
|
|
236
|
+
defaults: z.object({
|
|
237
|
+
country: COUNTRY_CODE.nullable(),
|
|
238
|
+
currency: CURRENCY_CODE.nullable(),
|
|
239
|
+
language: LANGUAGE_CODE.nullable(),
|
|
240
|
+
}),
|
|
241
|
+
}),
|
|
242
|
+
});
|
|
243
|
+
const resolvedCountryWithFlagsSchema = resolvedCountrySchema.extend({ isActive: z.boolean() });
|
|
244
|
+
const resolvedCurrencyWithFlagsSchema = resolvedCurrencySchema.extend({ isActive: z.boolean() });
|
|
245
|
+
const resolvedLanguageWithFlagsSchema = resolvedLanguageSchema.extend({ isActive: z.boolean() });
|
|
246
|
+
export const dictionaryByCodeResponseSchema = z.object({
|
|
247
|
+
data: z.discriminatedUnion('entryType', [
|
|
248
|
+
z.object({ entryType: z.literal('country'), entry: resolvedCountryWithFlagsSchema }),
|
|
249
|
+
z.object({ entryType: z.literal('currency'), entry: resolvedCurrencyWithFlagsSchema }),
|
|
250
|
+
z.object({ entryType: z.literal('language'), entry: resolvedLanguageWithFlagsSchema }),
|
|
251
|
+
]),
|
|
252
|
+
});
|
|
253
|
+
/**
|
|
254
|
+
* Typed error thrown by the validator. Consumer modules' HTTP error handlers
|
|
255
|
+
* translate this into a 409 envelope per contracts/dictionary-validation.contract.md.
|
|
256
|
+
*/
|
|
257
|
+
export class DictionaryReferenceError extends Error {
|
|
258
|
+
code;
|
|
259
|
+
entryType;
|
|
260
|
+
entryCode;
|
|
261
|
+
constructor(args) {
|
|
262
|
+
super(args.message ??
|
|
263
|
+
`Dictionary ${args.entryType} ${args.entryCode} ${args.code === 'DICTIONARY_ENTRY_NOT_FOUND' ? 'is not registered.' : 'is inactive.'}`);
|
|
264
|
+
this.name = 'DictionaryReferenceError';
|
|
265
|
+
this.code = args.code;
|
|
266
|
+
this.entryType = args.entryType;
|
|
267
|
+
this.entryCode = args.entryCode;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Decide which write mode a dictionary-referencing field is being validated
|
|
272
|
+
* under: `create-or-change` when the field is new or its value moved,
|
|
273
|
+
* `unchanged` when it did not.
|
|
274
|
+
*
|
|
275
|
+
* Relocated here from `dictionaries/services/dispatch-validator-mode.ts`
|
|
276
|
+
* (feature 075, Phase P) and published as a **function, not a port** — which
|
|
277
|
+
* contradicts `contracts/port-publication.md` §1.5, and the code is why. That
|
|
278
|
+
* section cites this file as the worked example of "yes, it reads state the
|
|
279
|
+
* module owns, so make it a port"; it reads nothing. It is three comparisons
|
|
280
|
+
* over its two arguments, and switching `dictionaries` off cannot change the
|
|
281
|
+
* answer, so a gated port would answer 503 to a question about two strings the
|
|
282
|
+
* caller already holds (FR-013).
|
|
283
|
+
*
|
|
284
|
+
* Five modules call it — `addresses`, `inventory`, `promotions`,
|
|
285
|
+
* `sales_channels` and `taxes` — immediately before calling the validator
|
|
286
|
+
* port, which *is* gated and *does* read state. Splitting the pure decision
|
|
287
|
+
* from the stateful validation is what lets the second fail closed without the
|
|
288
|
+
* first inventing a mode.
|
|
289
|
+
*/
|
|
290
|
+
export function dispatchValidatorMode(currentValue, incomingValue) {
|
|
291
|
+
if (!currentValue)
|
|
292
|
+
return 'create-or-change';
|
|
293
|
+
if (!incomingValue)
|
|
294
|
+
return 'unchanged';
|
|
295
|
+
return currentValue === incomingValue ? 'unchanged' : 'create-or-change';
|
|
296
|
+
}
|
|
297
|
+
//# sourceMappingURL=dictionary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dictionary.js","sourceRoot":"","sources":["../src/dictionary.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one canonical form of an e-mail address the platform stores and compares.
|
|
3
|
+
*
|
|
4
|
+
* ## The defect it ends
|
|
5
|
+
*
|
|
6
|
+
* The account table's writes folded the address to lower case and its reads did
|
|
7
|
+
* not. Postgres' `=` on `text` is case-sensitive, so a buyer who registered as
|
|
8
|
+
* `Jan.Kowalski@example.pl` had `jan.kowalski@example.pl` on record and every
|
|
9
|
+
* login attempt with the address they had typed matched no row — answered with
|
|
10
|
+
* the generic anti-enumeration `INVALID_CREDENTIALS`, which tells neither the
|
|
11
|
+
* buyer nor support what happened. Two entrances reached it: organisation
|
|
12
|
+
* registration (a public storefront path) and the member admin surface.
|
|
13
|
+
*
|
|
14
|
+
* ## The decision
|
|
15
|
+
*
|
|
16
|
+
* **Stored folded, compared folded.** RFC 5321 makes the local part technically
|
|
17
|
+
* case-sensitive; no mail provider in practice treats it that way, and the tree
|
|
18
|
+
* had already chosen to fold on write. So the reads move to meet the writes.
|
|
19
|
+
*
|
|
20
|
+
* ## Why a function here rather than a `.toLowerCase()` in the Zod schemas
|
|
21
|
+
*
|
|
22
|
+
* A transform on `customerRegistrationSchema` and its siblings would normalise
|
|
23
|
+
* only the callers that went through that particular schema. Three entrances
|
|
24
|
+
* into the same table go through none of them: federated sign-in takes the
|
|
25
|
+
* address from an OIDC claim, the admin "send a reset link" action passes an
|
|
26
|
+
* address already on record, and the invitation flow re-reads the address off
|
|
27
|
+
* its own row. The invariant belongs to whoever owns the table, so it is
|
|
28
|
+
* applied at that module's own seam — every service and port method that
|
|
29
|
+
* accepts an address folds it as its first act — and a helper is what lets the
|
|
30
|
+
* eleven of them share one spelling of "the same address".
|
|
31
|
+
*
|
|
32
|
+
* It lives in `@endora-commerce/contracts` rather than inside `customer_accounts` because
|
|
33
|
+
* `organizations` writes an invitation row keyed by the same address and has to
|
|
34
|
+
* fold it the same way; a module may not import another module's internals, and
|
|
35
|
+
* a second private copy is exactly how `foldDiacritics` came to have six.
|
|
36
|
+
*
|
|
37
|
+
* Not a port: it is pure over its argument, so it cannot answer differently
|
|
38
|
+
* depending on which modules are switched on, and a gated port answering 503
|
|
39
|
+
* `MODULE_DISABLED` to "fold this address" would be a bug rather than a safety
|
|
40
|
+
* property.
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* Fold an e-mail address to the form the platform stores and compares.
|
|
44
|
+
*
|
|
45
|
+
* Two steps, and only two:
|
|
46
|
+
*
|
|
47
|
+
* 1. **Trim.** Surrounding whitespace is never part of an address. Zod's
|
|
48
|
+
* `.email()` already rejects it at every HTTP entrance, so this bites only
|
|
49
|
+
* where the address arrives from somewhere else — an identity provider's
|
|
50
|
+
* claim, an importer, a seed.
|
|
51
|
+
* 2. **Lowercase.** The whole point: `Jan.Kowalski@Example.PL` and
|
|
52
|
+
* `jan.kowalski@example.pl` are one account.
|
|
53
|
+
*
|
|
54
|
+
* Deliberately **not** a validator: an address that is not one comes back
|
|
55
|
+
* unchanged apart from those two steps, because rejecting it is the schema's
|
|
56
|
+
* job at the boundary and this function is also applied to addresses already
|
|
57
|
+
* persisted. And deliberately **not** a normaliser of provider-specific
|
|
58
|
+
* aliasing — Gmail's dots and `+tags` are still distinct addresses here, since
|
|
59
|
+
* collapsing them would silently merge accounts on some domains and not others.
|
|
60
|
+
*/
|
|
61
|
+
export declare function normalizeEmailAddress(email: string): string;
|
|
62
|
+
//# sourceMappingURL=email-address.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-address.d.ts","sourceRoot":"","sources":["../src/email-address.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE3D"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one canonical form of an e-mail address the platform stores and compares.
|
|
3
|
+
*
|
|
4
|
+
* ## The defect it ends
|
|
5
|
+
*
|
|
6
|
+
* The account table's writes folded the address to lower case and its reads did
|
|
7
|
+
* not. Postgres' `=` on `text` is case-sensitive, so a buyer who registered as
|
|
8
|
+
* `Jan.Kowalski@example.pl` had `jan.kowalski@example.pl` on record and every
|
|
9
|
+
* login attempt with the address they had typed matched no row — answered with
|
|
10
|
+
* the generic anti-enumeration `INVALID_CREDENTIALS`, which tells neither the
|
|
11
|
+
* buyer nor support what happened. Two entrances reached it: organisation
|
|
12
|
+
* registration (a public storefront path) and the member admin surface.
|
|
13
|
+
*
|
|
14
|
+
* ## The decision
|
|
15
|
+
*
|
|
16
|
+
* **Stored folded, compared folded.** RFC 5321 makes the local part technically
|
|
17
|
+
* case-sensitive; no mail provider in practice treats it that way, and the tree
|
|
18
|
+
* had already chosen to fold on write. So the reads move to meet the writes.
|
|
19
|
+
*
|
|
20
|
+
* ## Why a function here rather than a `.toLowerCase()` in the Zod schemas
|
|
21
|
+
*
|
|
22
|
+
* A transform on `customerRegistrationSchema` and its siblings would normalise
|
|
23
|
+
* only the callers that went through that particular schema. Three entrances
|
|
24
|
+
* into the same table go through none of them: federated sign-in takes the
|
|
25
|
+
* address from an OIDC claim, the admin "send a reset link" action passes an
|
|
26
|
+
* address already on record, and the invitation flow re-reads the address off
|
|
27
|
+
* its own row. The invariant belongs to whoever owns the table, so it is
|
|
28
|
+
* applied at that module's own seam — every service and port method that
|
|
29
|
+
* accepts an address folds it as its first act — and a helper is what lets the
|
|
30
|
+
* eleven of them share one spelling of "the same address".
|
|
31
|
+
*
|
|
32
|
+
* It lives in `@endora-commerce/contracts` rather than inside `customer_accounts` because
|
|
33
|
+
* `organizations` writes an invitation row keyed by the same address and has to
|
|
34
|
+
* fold it the same way; a module may not import another module's internals, and
|
|
35
|
+
* a second private copy is exactly how `foldDiacritics` came to have six.
|
|
36
|
+
*
|
|
37
|
+
* Not a port: it is pure over its argument, so it cannot answer differently
|
|
38
|
+
* depending on which modules are switched on, and a gated port answering 503
|
|
39
|
+
* `MODULE_DISABLED` to "fold this address" would be a bug rather than a safety
|
|
40
|
+
* property.
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* Fold an e-mail address to the form the platform stores and compares.
|
|
44
|
+
*
|
|
45
|
+
* Two steps, and only two:
|
|
46
|
+
*
|
|
47
|
+
* 1. **Trim.** Surrounding whitespace is never part of an address. Zod's
|
|
48
|
+
* `.email()` already rejects it at every HTTP entrance, so this bites only
|
|
49
|
+
* where the address arrives from somewhere else — an identity provider's
|
|
50
|
+
* claim, an importer, a seed.
|
|
51
|
+
* 2. **Lowercase.** The whole point: `Jan.Kowalski@Example.PL` and
|
|
52
|
+
* `jan.kowalski@example.pl` are one account.
|
|
53
|
+
*
|
|
54
|
+
* Deliberately **not** a validator: an address that is not one comes back
|
|
55
|
+
* unchanged apart from those two steps, because rejecting it is the schema's
|
|
56
|
+
* job at the boundary and this function is also applied to addresses already
|
|
57
|
+
* persisted. And deliberately **not** a normaliser of provider-specific
|
|
58
|
+
* aliasing — Gmail's dots and `+tags` are still distinct addresses here, since
|
|
59
|
+
* collapsing them would silently merge accounts on some domains and not others.
|
|
60
|
+
*/
|
|
61
|
+
export function normalizeEmailAddress(email) {
|
|
62
|
+
return email.trim().toLowerCase();
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=email-address.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-address.js","sourceRoot":"","sources":["../src/email-address.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAa;IACjD,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AACpC,CAAC"}
|
package/dist/email.d.ts
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* The `email` module's published contract (D-59, issue #114; extended by
|
|
4
|
+
* feature 075's Phase P).
|
|
5
|
+
*
|
|
6
|
+
* `email` owns no route, no entity and no worker: it is two registrations —
|
|
7
|
+
* the platform's outbound transport, and the durable record of what became of
|
|
8
|
+
* each message that left through it.
|
|
9
|
+
*
|
|
10
|
+
* The delivery recorder lives here rather than in the module because a second
|
|
11
|
+
* module reads it: `transactional_emails` decides `suppressed` above the
|
|
12
|
+
* transport and records that decision itself, so the shape is a boundary and
|
|
13
|
+
* not an internal. Feature 075 FR-001 — a type-only reach into another
|
|
14
|
+
* module's `services/` is still a reach.
|
|
15
|
+
*
|
|
16
|
+
* `EmailMailerPort` joins it for the same reason at a larger scale:
|
|
17
|
+
* thirty-two import sites across eight modules named `Mailer` /
|
|
18
|
+
* `MailerSendInput` at `email/services/mailer.ts`, which makes it the single
|
|
19
|
+
* largest contract in the sweep and the one with the smallest surface — two
|
|
20
|
+
* shapes and one method.
|
|
21
|
+
*/
|
|
22
|
+
/** What became of one outbound message. */
|
|
23
|
+
export declare const emailDeliveryStatusSchema: z.ZodEnum<{
|
|
24
|
+
failed: "failed";
|
|
25
|
+
sent: "sent";
|
|
26
|
+
suppressed: "suppressed";
|
|
27
|
+
}>;
|
|
28
|
+
export type EmailDeliveryStatus = z.infer<typeof emailDeliveryStatusSchema>;
|
|
29
|
+
/**
|
|
30
|
+
* Why a recorded message did not go out; absent for a delivered one.
|
|
31
|
+
*
|
|
32
|
+
* The split down the middle is the load-bearing part: the first two are the
|
|
33
|
+
* platform deliberately not sending, the last three are a message that was
|
|
34
|
+
* meant to go out and did not. An operator reading the record has to be able to
|
|
35
|
+
* tell a configuration from an outage, and since feature 074 shipped per-e-mail
|
|
36
|
+
* deactivation, "not sent" stopped being the same as "broken".
|
|
37
|
+
*/
|
|
38
|
+
export declare const emailDeliveryReasonSchema: z.ZodEnum<{
|
|
39
|
+
deactivated: "deactivated";
|
|
40
|
+
no_transport: "no_transport";
|
|
41
|
+
no_definition: "no_definition";
|
|
42
|
+
duplicate_message_id: "duplicate_message_id";
|
|
43
|
+
transport_error: "transport_error";
|
|
44
|
+
}>;
|
|
45
|
+
export type EmailDeliveryReason = z.infer<typeof emailDeliveryReasonSchema>;
|
|
46
|
+
export interface EmailDeliveryRecordInput {
|
|
47
|
+
messageId: string;
|
|
48
|
+
recipient: string;
|
|
49
|
+
kind: string;
|
|
50
|
+
status: EmailDeliveryStatus;
|
|
51
|
+
reason?: EmailDeliveryReason;
|
|
52
|
+
detail?: string;
|
|
53
|
+
subject?: string;
|
|
54
|
+
salesChannelId?: string | null;
|
|
55
|
+
documentType?: string;
|
|
56
|
+
documentId?: string;
|
|
57
|
+
context?: Record<string, unknown>;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Container name: `emailDeliveryRecorder`. Owner: `email`.
|
|
61
|
+
*
|
|
62
|
+
* **Never throws.** That is the contract, not an implementation detail: every
|
|
63
|
+
* caller reaches this after a message has already been handed to a transport or
|
|
64
|
+
* already been suppressed, so a failure here can only lose the record — it must
|
|
65
|
+
* never be able to lose, or appear to lose, the send. A caller therefore needs
|
|
66
|
+
* no `catch`, which is also what keeps a bare one from growing around a port.
|
|
67
|
+
*
|
|
68
|
+
* Answers the row id, or `null` when the row could not be written.
|
|
69
|
+
*
|
|
70
|
+
* **Owner off:** there is no such state, and that is why the name is an
|
|
71
|
+
* ordinary `ctx.di.register` rather than a `providePort` — `email` declares
|
|
72
|
+
* `activation.nonDeactivatable` (074, C3), so a gate over this registration
|
|
73
|
+
* could never close, and the never-throws promise above would be the first
|
|
74
|
+
* thing it broke. Publication and gating are separate questions;
|
|
75
|
+
* `paymentOrderStatusRegistry` is the other name published over a plain
|
|
76
|
+
* registration for the same kind of reason.
|
|
77
|
+
*/
|
|
78
|
+
export interface EmailDeliveryRecorder {
|
|
79
|
+
record(input: EmailDeliveryRecordInput): Promise<string | null>;
|
|
80
|
+
}
|
|
81
|
+
export interface EmailAttachment {
|
|
82
|
+
filename: string;
|
|
83
|
+
content: Uint8Array;
|
|
84
|
+
contentType?: string;
|
|
85
|
+
}
|
|
86
|
+
export interface EmailMailerSendInput {
|
|
87
|
+
/** Stable id, idempotency key for retries. Every send is idempotent on it. */
|
|
88
|
+
messageId: string;
|
|
89
|
+
to: string;
|
|
90
|
+
subject: string;
|
|
91
|
+
/** Plain-text body (always present; the readable alternative for HTML mail). */
|
|
92
|
+
text: string;
|
|
93
|
+
/** Optional HTML body (feature 047). When present, sent as multipart alternative. */
|
|
94
|
+
html?: string;
|
|
95
|
+
/** Optional binary attachments (feature 047 invoices — PDF delivery). */
|
|
96
|
+
attachments?: EmailAttachment[];
|
|
97
|
+
/**
|
|
98
|
+
* What this message is — the transactional-email code, or a legacy in-code
|
|
99
|
+
* builder's own name for it (D-59). Absent, the delivery record falls back to
|
|
100
|
+
* `meta.kind`, which every in-code builder in the tree already carries.
|
|
101
|
+
*/
|
|
102
|
+
kind?: string;
|
|
103
|
+
/**
|
|
104
|
+
* The business document this message delivers, when it delivers one (D-59).
|
|
105
|
+
* An invoice e-mail is the case the ruling was written for: the record has to
|
|
106
|
+
* be findable by the document an operator is asked about, not only by a
|
|
107
|
+
* recipient and a message id.
|
|
108
|
+
*/
|
|
109
|
+
document?: {
|
|
110
|
+
type: string;
|
|
111
|
+
id: string;
|
|
112
|
+
};
|
|
113
|
+
/** The channel the message was rendered for, or `null` for the platform-wide one. */
|
|
114
|
+
salesChannelId?: string | null;
|
|
115
|
+
/** Optional structured payload retained alongside the email for audit. */
|
|
116
|
+
meta?: Record<string, unknown>;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Why a transport did not send a message it was handed, without failing (D-59).
|
|
120
|
+
*
|
|
121
|
+
* Today there is one: a driver that deduplicates on `messageId` and has already
|
|
122
|
+
* accepted this one. The union exists so a second reason arrives as a value the
|
|
123
|
+
* delivery record already knows how to hold, rather than as a boolean nobody
|
|
124
|
+
* can interpret.
|
|
125
|
+
*/
|
|
126
|
+
export type EmailMailerSuppressionReason = 'duplicate_message_id';
|
|
127
|
+
/**
|
|
128
|
+
* What one transport call did with one message (D-59, issue #114).
|
|
129
|
+
*
|
|
130
|
+
* `send` answered `void`, so the layer that actually hands a message to SMTP
|
|
131
|
+
* was the one layer in the send path with nothing to say — while
|
|
132
|
+
* `TransactionalSendOutcome` and `OrderEmailResult`, computed above it, both
|
|
133
|
+
* distinguish a delivered message from a suppressed one. That is the gap this
|
|
134
|
+
* type closes, and the delivery record is what makes the answer outlive a log
|
|
135
|
+
* rotation.
|
|
136
|
+
*
|
|
137
|
+
* **A transport failure still throws.** Unchanged deliberately: every caller
|
|
138
|
+
* already contains its own send failure after a committed write, and turning
|
|
139
|
+
* the throw into a value would silently retire those tolerances. So this union
|
|
140
|
+
* enumerates the ways a message can fail to go out *without* an error, exactly
|
|
141
|
+
* as `TransactionalSendOutcome` does one layer up.
|
|
142
|
+
*/
|
|
143
|
+
export type EmailMailerSendOutcome =
|
|
144
|
+
/** Handed to the transport. */
|
|
145
|
+
{
|
|
146
|
+
status: 'sent';
|
|
147
|
+
}
|
|
148
|
+
/** The transport deliberately did not send it. */
|
|
149
|
+
| {
|
|
150
|
+
status: 'suppressed';
|
|
151
|
+
reason: EmailMailerSuppressionReason;
|
|
152
|
+
};
|
|
153
|
+
/**
|
|
154
|
+
* Container name: `emailMailer`. Owner: `email`.
|
|
155
|
+
*
|
|
156
|
+
* **Registered with `ctx.di.register`, not `providePort`, and that is
|
|
157
|
+
* deliberate.** `email`'s manifest declares it non-deactivatable — it is the
|
|
158
|
+
* transport every message leaves through, and a business configures a driver
|
|
159
|
+
* rather than switching the transport off — so a gate on its effective state
|
|
160
|
+
* could never close. Feature 075 does not change that registration; a port
|
|
161
|
+
* whose gate is unreachable would advertise a failure mode the platform cannot
|
|
162
|
+
* produce. (`auth` is also non-deactivatable and does use `providePort`; the
|
|
163
|
+
* two differ because `auth` had already made that call for `requireAdmin`, and
|
|
164
|
+
* one answer per module beats one answer per repository.)
|
|
165
|
+
*
|
|
166
|
+
* The driver decision — SMTP when the environment configures one, console
|
|
167
|
+
* otherwise — belongs to the module and is not part of this contract, and
|
|
168
|
+
* neither does the delivery record: `EmailDeliveryRecorder` above is a
|
|
169
|
+
* separate registration because `transactional_emails` writes to it without
|
|
170
|
+
* going through the transport at all.
|
|
171
|
+
*/
|
|
172
|
+
export interface EmailMailerPort {
|
|
173
|
+
send(input: EmailMailerSendInput): Promise<EmailMailerSendOutcome>;
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=email.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../src/email.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,2CAA2C;AAC3C,eAAO,MAAM,yBAAyB;;;;EAA2C,CAAC;AAClF,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB;;;;;;EAWpC,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,mBAAmB,CAAC;IAC5B,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CACjE;AAOD,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,UAAU,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,8EAA8E;IAC9E,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAC;IACb,qFAAqF;IACrF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;IAChC;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,QAAQ,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC,qFAAqF;IACrF,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,0EAA0E;IAC1E,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,4BAA4B,GAAG,sBAAsB,CAAC;AAElE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,sBAAsB;AAChC,+BAA+B;AAC7B;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE;AACpB,kDAAkD;GAChD;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,4BAA4B,CAAA;CAAE,CAAC;AAEnE;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;CACpE"}
|