@openlup/platform 0.16.0 → 0.17.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 (296) hide show
  1. package/CHANGELOG.md +84 -0
  2. package/README.md +285 -5
  3. package/dist/communications/accountingDocumentDelivery.d.ts +35 -0
  4. package/dist/communications/accountingDocumentDelivery.js +33 -0
  5. package/dist/communications/authEmailError.d.ts +5 -0
  6. package/dist/communications/authEmailError.js +8 -0
  7. package/dist/communications/authEmailPorts.d.ts +2 -0
  8. package/dist/communications/authEmailPorts.js +1 -0
  9. package/dist/communications/authSendEmailHook.d.ts +35 -0
  10. package/dist/communications/authSendEmailHook.js +250 -0
  11. package/dist/communications/capturedTransactionalDelivery.d.ts +13 -0
  12. package/dist/communications/capturedTransactionalDelivery.js +14 -0
  13. package/dist/communications/communicationSync.d.ts +11 -0
  14. package/dist/communications/communicationSync.js +79 -0
  15. package/dist/communications/communicationSyncWorker.d.ts +68 -0
  16. package/dist/communications/communicationSyncWorker.js +218 -0
  17. package/dist/communications/consentPolicy.d.ts +19 -0
  18. package/dist/communications/consentPolicy.js +43 -0
  19. package/dist/communications/consentPurposeRegistry.d.ts +4 -0
  20. package/dist/communications/consentPurposeRegistry.js +24 -0
  21. package/dist/communications/contracts/abandonedCart.d.ts +27 -0
  22. package/dist/communications/contracts/abandonedCart.js +25 -0
  23. package/dist/communications/contracts/authEmail.d.ts +62 -0
  24. package/dist/communications/contracts/authEmail.js +1 -0
  25. package/dist/communications/contracts/backInStock.d.ts +28 -0
  26. package/dist/communications/contracts/backInStock.js +28 -0
  27. package/dist/communications/contracts/blocks.d.ts +24 -0
  28. package/dist/communications/contracts/blocks.js +23 -0
  29. package/dist/communications/contracts/checkoutExpired.d.ts +22 -0
  30. package/dist/communications/contracts/checkoutExpired.js +25 -0
  31. package/dist/communications/contracts/checkoutRecovery.d.ts +26 -0
  32. package/dist/communications/contracts/checkoutRecovery.js +37 -0
  33. package/dist/communications/contracts/commerceEmailContent.d.ts +176 -0
  34. package/dist/communications/contracts/commerceEmailContent.js +1 -0
  35. package/dist/communications/contracts/commerceEngagementEmailContent.d.ts +95 -0
  36. package/dist/communications/contracts/commerceEngagementEmailContent.js +1 -0
  37. package/dist/communications/contracts/consent.d.ts +12 -0
  38. package/dist/communications/contracts/consent.js +1 -0
  39. package/dist/communications/contracts/email.d.ts +182 -0
  40. package/dist/communications/contracts/email.js +1 -0
  41. package/dist/communications/contracts/guideCard.d.ts +3 -0
  42. package/dist/communications/contracts/guideCard.js +26 -0
  43. package/dist/communications/contracts/index.d.ts +51 -0
  44. package/dist/communications/contracts/index.js +42 -0
  45. package/dist/communications/contracts/inquiryContent.d.ts +32 -0
  46. package/dist/communications/contracts/inquiryContent.js +33 -0
  47. package/dist/communications/contracts/invoiceDocumentContent.d.ts +26 -0
  48. package/dist/communications/contracts/invoiceDocumentContent.js +36 -0
  49. package/dist/communications/contracts/marketingAutomation.d.ts +52 -0
  50. package/dist/communications/contracts/marketingAutomation.js +1 -0
  51. package/dist/communications/contracts/newsletter.d.ts +63 -0
  52. package/dist/communications/contracts/newsletter.js +1 -0
  53. package/dist/communications/contracts/newsletterWebhook.d.ts +26 -0
  54. package/dist/communications/contracts/newsletterWebhook.js +1 -0
  55. package/dist/communications/contracts/optOutContent.d.ts +11 -0
  56. package/dist/communications/contracts/optOutContent.js +17 -0
  57. package/dist/communications/contracts/orderCanceled.d.ts +25 -0
  58. package/dist/communications/contracts/orderCanceled.js +27 -0
  59. package/dist/communications/contracts/orderDraft.d.ts +46 -0
  60. package/dist/communications/contracts/orderDraft.js +46 -0
  61. package/dist/communications/contracts/orderPaid.d.ts +53 -0
  62. package/dist/communications/contracts/orderPaid.js +71 -0
  63. package/dist/communications/contracts/orderRefunded.d.ts +24 -0
  64. package/dist/communications/contracts/orderRefunded.js +27 -0
  65. package/dist/communications/contracts/paymentFailed.d.ts +24 -0
  66. package/dist/communications/contracts/paymentFailed.js +24 -0
  67. package/dist/communications/contracts/publicIntake.d.ts +49 -0
  68. package/dist/communications/contracts/publicIntake.js +1 -0
  69. package/dist/communications/contracts/render.d.ts +2 -0
  70. package/dist/communications/contracts/render.js +229 -0
  71. package/dist/communications/contracts/reorderReminder.d.ts +24 -0
  72. package/dist/communications/contracts/reorderReminder.js +24 -0
  73. package/dist/communications/contracts/returnEmails.d.ts +25 -0
  74. package/dist/communications/contracts/returnEmails.js +62 -0
  75. package/dist/communications/contracts/reviewEffects.d.ts +26 -0
  76. package/dist/communications/contracts/reviewEffects.js +25 -0
  77. package/dist/communications/contracts/reviewRequest.d.ts +26 -0
  78. package/dist/communications/contracts/reviewRequest.js +24 -0
  79. package/dist/communications/contracts/roleNotificationContent.d.ts +25 -0
  80. package/dist/communications/contracts/roleNotificationContent.js +46 -0
  81. package/dist/communications/contracts/shipmentDelivered.d.ts +23 -0
  82. package/dist/communications/contracts/shipmentDelivered.js +24 -0
  83. package/dist/communications/contracts/shipmentDispatched.d.ts +27 -0
  84. package/dist/communications/contracts/shipmentDispatched.js +35 -0
  85. package/dist/communications/contracts/shipmentException.d.ts +21 -0
  86. package/dist/communications/contracts/shipmentException.js +26 -0
  87. package/dist/communications/contracts/subscriptionActivationActionRequired.d.ts +17 -0
  88. package/dist/communications/contracts/subscriptionActivationActionRequired.js +22 -0
  89. package/dist/communications/contracts/subscriptionAddressChanged.d.ts +19 -0
  90. package/dist/communications/contracts/subscriptionAddressChanged.js +25 -0
  91. package/dist/communications/contracts/subscriptionCancelled.d.ts +19 -0
  92. package/dist/communications/contracts/subscriptionCancelled.js +25 -0
  93. package/dist/communications/contracts/subscriptionCycleSkipped.d.ts +25 -0
  94. package/dist/communications/contracts/subscriptionCycleSkipped.js +40 -0
  95. package/dist/communications/contracts/subscriptionDeliveryRescheduled.d.ts +25 -0
  96. package/dist/communications/contracts/subscriptionDeliveryRescheduled.js +39 -0
  97. package/dist/communications/contracts/subscriptionEmailContent.d.ts +145 -0
  98. package/dist/communications/contracts/subscriptionEmailContent.js +1 -0
  99. package/dist/communications/contracts/subscriptionPackageChanged.d.ts +20 -0
  100. package/dist/communications/contracts/subscriptionPackageChanged.js +26 -0
  101. package/dist/communications/contracts/subscriptionPauseReminder.d.ts +23 -0
  102. package/dist/communications/contracts/subscriptionPauseReminder.js +29 -0
  103. package/dist/communications/contracts/subscriptionPaused.d.ts +21 -0
  104. package/dist/communications/contracts/subscriptionPaused.js +25 -0
  105. package/dist/communications/contracts/subscriptionPaymentExpired.d.ts +34 -0
  106. package/dist/communications/contracts/subscriptionPaymentExpired.js +37 -0
  107. package/dist/communications/contracts/subscriptionPaymentFailed.d.ts +49 -0
  108. package/dist/communications/contracts/subscriptionPaymentFailed.js +56 -0
  109. package/dist/communications/contracts/subscriptionPaymentRecovered.d.ts +19 -0
  110. package/dist/communications/contracts/subscriptionPaymentRecovered.js +22 -0
  111. package/dist/communications/contracts/subscriptionRenewalAtRisk.d.ts +36 -0
  112. package/dist/communications/contracts/subscriptionRenewalAtRisk.js +36 -0
  113. package/dist/communications/contracts/subscriptionRenewalUpcoming.d.ts +49 -0
  114. package/dist/communications/contracts/subscriptionRenewalUpcoming.js +43 -0
  115. package/dist/communications/contracts/subscriptionResumed.d.ts +23 -0
  116. package/dist/communications/contracts/subscriptionResumed.js +25 -0
  117. package/dist/communications/contracts/subscriptionWelcome.d.ts +44 -0
  118. package/dist/communications/contracts/subscriptionWelcome.js +45 -0
  119. package/dist/communications/contracts/subscriptionWinback.d.ts +21 -0
  120. package/dist/communications/contracts/subscriptionWinback.js +28 -0
  121. package/dist/communications/contracts/transactionalDelivery.d.ts +42 -0
  122. package/dist/communications/contracts/transactionalDelivery.js +1 -0
  123. package/dist/communications/contracts/types.d.ts +46 -0
  124. package/dist/communications/contracts/types.js +1 -0
  125. package/dist/communications/contribution.d.ts +10 -0
  126. package/dist/communications/contribution.js +24 -0
  127. package/dist/communications/deliveryReconciliation.d.ts +64 -0
  128. package/dist/communications/deliveryReconciliation.js +60 -0
  129. package/dist/communications/emailEgress.d.ts +38 -0
  130. package/dist/communications/emailEgress.js +100 -0
  131. package/dist/communications/emailHealthAssessment.d.ts +39 -0
  132. package/dist/communications/emailHealthAssessment.js +59 -0
  133. package/dist/communications/emailHealthMetricsPort.d.ts +17 -0
  134. package/dist/communications/emailHealthMetricsPort.js +1 -0
  135. package/dist/communications/emailNotificationControl.d.ts +27 -0
  136. package/dist/communications/emailNotificationControl.js +10 -0
  137. package/dist/communications/emailObservation.d.ts +28 -0
  138. package/dist/communications/emailObservation.js +23 -0
  139. package/dist/communications/emailTransport.d.ts +82 -0
  140. package/dist/communications/emailTransport.js +95 -0
  141. package/dist/communications/errors.d.ts +15 -0
  142. package/dist/communications/errors.js +30 -0
  143. package/dist/communications/index.d.ts +46 -0
  144. package/dist/communications/index.js +38 -0
  145. package/dist/communications/integrationEventSignature.d.ts +15 -0
  146. package/dist/communications/integrationEventSignature.js +37 -0
  147. package/dist/communications/managedTransactionalDeliveryStore.d.ts +12 -0
  148. package/dist/communications/managedTransactionalDeliveryStore.js +49 -0
  149. package/dist/communications/nativeOperations.d.ts +79 -0
  150. package/dist/communications/nativeOperations.js +3 -0
  151. package/dist/communications/newsletterIntegrationContracts.d.ts +130 -0
  152. package/dist/communications/newsletterIntegrationContracts.js +128 -0
  153. package/dist/communications/newsletterWebhookPort.d.ts +11 -0
  154. package/dist/communications/newsletterWebhookPort.js +84 -0
  155. package/dist/communications/newsletterWebhookProcessing.d.ts +17 -0
  156. package/dist/communications/newsletterWebhookProcessing.js +71 -0
  157. package/dist/communications/originPolicy.d.ts +40 -0
  158. package/dist/communications/originPolicy.js +100 -0
  159. package/dist/communications/postgresCommunicationSync.d.ts +79 -0
  160. package/dist/communications/postgresCommunicationSync.js +111 -0
  161. package/dist/communications/postgresConsentEvaluator.d.ts +19 -0
  162. package/dist/communications/postgresConsentEvaluator.js +43 -0
  163. package/dist/communications/postgresDeliveryReconciliation.d.ts +3 -0
  164. package/dist/communications/postgresDeliveryReconciliation.js +113 -0
  165. package/dist/communications/postgresNativeOperations.d.ts +13 -0
  166. package/dist/communications/postgresNativeOperations.js +63 -0
  167. package/dist/communications/postgresNotificationRecipients.d.ts +37 -0
  168. package/dist/communications/postgresNotificationRecipients.js +60 -0
  169. package/dist/communications/postgresTransactionalDeliveryStore.d.ts +7 -0
  170. package/dist/communications/postgresTransactionalDeliveryStore.js +55 -0
  171. package/dist/communications/preferencesToken.d.ts +10 -0
  172. package/dist/communications/preferencesToken.js +40 -0
  173. package/dist/communications/publicPreferenceToken.d.ts +24 -0
  174. package/dist/communications/publicPreferenceToken.js +53 -0
  175. package/dist/communications/shipmentNotifications.d.ts +34 -0
  176. package/dist/communications/shipmentNotifications.js +61 -0
  177. package/dist/communications/sql.d.ts +26 -0
  178. package/dist/communications/sql.js +7 -0
  179. package/dist/communications/standardWebhooks.d.ts +14 -0
  180. package/dist/communications/standardWebhooks.js +73 -0
  181. package/dist/communications/status.d.ts +11 -0
  182. package/dist/communications/status.js +36 -0
  183. package/dist/communications/transactionalDelivery.d.ts +19 -0
  184. package/dist/communications/transactionalDelivery.js +96 -0
  185. package/dist/communications/transactionalDeliveryIdentifier.d.ts +3 -0
  186. package/dist/communications/transactionalDeliveryIdentifier.js +6 -0
  187. package/dist/communications/unsubscribeToken.d.ts +77 -0
  188. package/dist/communications/unsubscribeToken.js +150 -0
  189. package/dist/communications/validation.d.ts +2 -0
  190. package/dist/communications/validation.js +3 -0
  191. package/dist/http/index.d.ts +1 -0
  192. package/dist/http/index.js +1 -0
  193. package/dist/http/webhookSignature.d.ts +42 -0
  194. package/dist/http/webhookSignature.js +86 -0
  195. package/package.json +12 -2
  196. package/release-gates.json +41 -8
  197. package/sql/20260807190000_comms_outbox_rail.sql +329 -0
  198. package/sql/20260810120001_transactional_delivery_receipts.sql +209 -0
  199. package/sql/20260813120000_communications_control_plane.sql +568 -0
  200. package/sql/20261019200000_communications_native_operations.sql +68 -0
  201. package/src/communications/accountingDocumentDelivery.ts +63 -0
  202. package/src/communications/authEmailError.ts +10 -0
  203. package/src/communications/authEmailPorts.ts +2 -0
  204. package/src/communications/authSendEmailHook.ts +310 -0
  205. package/src/communications/capturedTransactionalDelivery.ts +29 -0
  206. package/src/communications/communicationSync.ts +96 -0
  207. package/src/communications/communicationSyncWorker.ts +302 -0
  208. package/src/communications/consentPolicy.ts +82 -0
  209. package/src/communications/consentPurposeRegistry.ts +31 -0
  210. package/src/communications/contracts/abandonedCart.ts +64 -0
  211. package/src/communications/contracts/authEmail.ts +73 -0
  212. package/src/communications/contracts/backInStock.ts +69 -0
  213. package/src/communications/contracts/blocks.ts +47 -0
  214. package/src/communications/contracts/checkoutExpired.ts +62 -0
  215. package/src/communications/contracts/checkoutRecovery.ts +68 -0
  216. package/src/communications/contracts/commerceEmailContent.ts +189 -0
  217. package/src/communications/contracts/commerceEngagementEmailContent.ts +106 -0
  218. package/src/communications/contracts/consent.ts +13 -0
  219. package/src/communications/contracts/email.ts +210 -0
  220. package/src/communications/contracts/guideCard.ts +34 -0
  221. package/src/communications/contracts/index.ts +51 -0
  222. package/src/communications/contracts/inquiryContent.ts +53 -0
  223. package/src/communications/contracts/invoiceDocumentContent.ts +57 -0
  224. package/src/communications/contracts/marketingAutomation.ts +45 -0
  225. package/src/communications/contracts/newsletter.ts +78 -0
  226. package/src/communications/contracts/newsletterWebhook.ts +23 -0
  227. package/src/communications/contracts/optOutContent.ts +23 -0
  228. package/src/communications/contracts/orderCanceled.ts +64 -0
  229. package/src/communications/contracts/orderDraft.ts +112 -0
  230. package/src/communications/contracts/orderPaid.ts +147 -0
  231. package/src/communications/contracts/orderRefunded.ts +63 -0
  232. package/src/communications/contracts/paymentFailed.ts +59 -0
  233. package/src/communications/contracts/publicIntake.ts +53 -0
  234. package/src/communications/contracts/render.ts +271 -0
  235. package/src/communications/contracts/reorderReminder.ts +59 -0
  236. package/src/communications/contracts/returnEmails.ts +113 -0
  237. package/src/communications/contracts/reviewEffects.ts +60 -0
  238. package/src/communications/contracts/reviewRequest.ts +59 -0
  239. package/src/communications/contracts/roleNotificationContent.ts +65 -0
  240. package/src/communications/contracts/shipmentDelivered.ts +59 -0
  241. package/src/communications/contracts/shipmentDispatched.ts +77 -0
  242. package/src/communications/contracts/shipmentException.ts +62 -0
  243. package/src/communications/contracts/subscriptionActivationActionRequired.ts +37 -0
  244. package/src/communications/contracts/subscriptionAddressChanged.ts +56 -0
  245. package/src/communications/contracts/subscriptionCancelled.ts +57 -0
  246. package/src/communications/contracts/subscriptionCycleSkipped.ts +80 -0
  247. package/src/communications/contracts/subscriptionDeliveryRescheduled.ts +79 -0
  248. package/src/communications/contracts/subscriptionEmailContent.ts +163 -0
  249. package/src/communications/contracts/subscriptionPackageChanged.ts +59 -0
  250. package/src/communications/contracts/subscriptionPauseReminder.ts +63 -0
  251. package/src/communications/contracts/subscriptionPaused.ts +58 -0
  252. package/src/communications/contracts/subscriptionPaymentExpired.ts +83 -0
  253. package/src/communications/contracts/subscriptionPaymentFailed.ts +117 -0
  254. package/src/communications/contracts/subscriptionPaymentRecovered.ts +50 -0
  255. package/src/communications/contracts/subscriptionRenewalAtRisk.ts +85 -0
  256. package/src/communications/contracts/subscriptionRenewalUpcoming.ts +103 -0
  257. package/src/communications/contracts/subscriptionResumed.ts +60 -0
  258. package/src/communications/contracts/subscriptionWelcome.ts +102 -0
  259. package/src/communications/contracts/subscriptionWinback.ts +62 -0
  260. package/src/communications/contracts/transactionalDelivery.ts +43 -0
  261. package/src/communications/contracts/types.ts +58 -0
  262. package/src/communications/contribution.ts +33 -0
  263. package/src/communications/deliveryReconciliation.ts +108 -0
  264. package/src/communications/emailEgress.ts +111 -0
  265. package/src/communications/emailHealthAssessment.ts +106 -0
  266. package/src/communications/emailHealthMetricsPort.ts +17 -0
  267. package/src/communications/emailNotificationControl.ts +28 -0
  268. package/src/communications/emailObservation.ts +43 -0
  269. package/src/communications/emailTransport.ts +167 -0
  270. package/src/communications/errors.ts +34 -0
  271. package/src/communications/index.ts +46 -0
  272. package/src/communications/integrationEventSignature.ts +49 -0
  273. package/src/communications/managedTransactionalDeliveryStore.ts +59 -0
  274. package/src/communications/nativeOperations.ts +40 -0
  275. package/src/communications/newsletterIntegrationContracts.ts +154 -0
  276. package/src/communications/newsletterWebhookPort.ts +104 -0
  277. package/src/communications/newsletterWebhookProcessing.ts +102 -0
  278. package/src/communications/originPolicy.ts +151 -0
  279. package/src/communications/postgresCommunicationSync.ts +127 -0
  280. package/src/communications/postgresConsentEvaluator.ts +49 -0
  281. package/src/communications/postgresDeliveryReconciliation.ts +101 -0
  282. package/src/communications/postgresNativeOperations.ts +55 -0
  283. package/src/communications/postgresNotificationRecipients.ts +57 -0
  284. package/src/communications/postgresTransactionalDeliveryStore.ts +79 -0
  285. package/src/communications/preferencesToken.ts +62 -0
  286. package/src/communications/publicPreferenceToken.ts +68 -0
  287. package/src/communications/shipmentNotifications.ts +78 -0
  288. package/src/communications/sql.ts +7 -0
  289. package/src/communications/standardWebhooks.ts +84 -0
  290. package/src/communications/status.ts +51 -0
  291. package/src/communications/transactionalDelivery.ts +114 -0
  292. package/src/communications/transactionalDeliveryIdentifier.ts +9 -0
  293. package/src/communications/unsubscribeToken.ts +186 -0
  294. package/src/communications/validation.ts +3 -0
  295. package/src/http/index.ts +1 -0
  296. package/src/http/webhookSignature.ts +96 -0
@@ -0,0 +1,43 @@
1
+ const MARKETING_PURPOSES = new Set([
2
+ "marketing_launch_offer",
3
+ "marketing_newsletter",
4
+ ]);
5
+ export function evaluateCommunicationPolicy(input, registry) {
6
+ if (registry && !registry.has(input.purpose))
7
+ return { decision: "blocked", reason: "unsupported_purpose" };
8
+ const state = input.permission?.state ?? "unknown";
9
+ if (input.purpose === "admin_notification") {
10
+ return { decision: "allowed", reason: "admin_notification" };
11
+ }
12
+ if (input.purpose === "transactional" || input.purpose === "subscription_dunning") {
13
+ return { decision: "allowed", reason: "purpose_allowed_without_marketing_consent" };
14
+ }
15
+ if (input.purpose === "tester_program") {
16
+ if (input.permission?.legacySequencePaused) {
17
+ return { decision: "blocked", reason: "sequence_paused" };
18
+ }
19
+ if (state === "denied" || state === "suppressed") {
20
+ return { decision: "blocked", reason: `permission_${state}` };
21
+ }
22
+ if (state !== "granted") {
23
+ return { decision: "blocked", reason: "missing_tester_program_permission" };
24
+ }
25
+ return { decision: "allowed", reason: "tester_program_granted" };
26
+ }
27
+ if (registry ? registry.classification(input.purpose) === "marketing" : MARKETING_PURPOSES.has(input.purpose)) {
28
+ if (state === "suppressed") {
29
+ return { decision: "blocked", reason: "permission_suppressed" };
30
+ }
31
+ if (state !== "granted") {
32
+ return { decision: "blocked", reason: "missing_marketing_permission" };
33
+ }
34
+ return { decision: "allowed", reason: "marketing_permission_granted" };
35
+ }
36
+ if (registry?.classification(input.purpose) === "non-marketing") {
37
+ return { decision: "allowed", reason: "purpose_allowed_without_marketing_consent" };
38
+ }
39
+ return { decision: "blocked", reason: "unsupported_purpose" };
40
+ }
41
+ export function consentStateFromBoolean(value) {
42
+ return value === true ? "granted" : "denied";
43
+ }
@@ -0,0 +1,4 @@
1
+ import type { CommunicationPurpose } from "./contracts/types.js";
2
+ import type { CommunicationPurposeDefinition, CommunicationPurposeRegistry } from "./contracts/consent.js";
3
+ export declare function createCommunicationPurposeRegistry<const Purpose extends string = never>(additions?: readonly CommunicationPurposeDefinition<Purpose>[]): CommunicationPurposeRegistry<CommunicationPurpose | Purpose>;
4
+ export declare const defaultCommunicationPurposeRegistry: CommunicationPurposeRegistry<"transactional" | "marketing_launch_offer" | "marketing_newsletter" | "tester_program" | "subscription_dunning" | "admin_notification">;
@@ -0,0 +1,24 @@
1
+ const builtins = [
2
+ { id: "transactional", classification: "non-marketing" },
3
+ { id: "tester_program", classification: "non-marketing" },
4
+ { id: "marketing_launch_offer", classification: "marketing" },
5
+ { id: "marketing_newsletter", classification: "marketing" },
6
+ { id: "subscription_dunning", classification: "non-marketing" },
7
+ { id: "admin_notification", classification: "non-marketing" },
8
+ ];
9
+ export function createCommunicationPurposeRegistry(additions = []) {
10
+ const definitions = new Map();
11
+ for (const definition of [...builtins, ...additions]) {
12
+ if (typeof definition.id !== "string" || !definition.id.trim() || definition.id !== definition.id.trim()
13
+ || !["marketing", "non-marketing"].includes(definition.classification) || definitions.has(definition.id)) {
14
+ throw new Error("communication_purpose_definition_invalid");
15
+ }
16
+ definitions.set(definition.id, Object.freeze({ id: definition.id, classification: definition.classification }));
17
+ }
18
+ return Object.freeze({
19
+ purposes: Object.freeze([...definitions.values()]),
20
+ has: (value) => typeof value === "string" && definitions.has(value),
21
+ classification: (purpose) => definitions.get(purpose)?.classification,
22
+ });
23
+ }
24
+ export const defaultCommunicationPurposeRegistry = createCommunicationPurposeRegistry();
@@ -0,0 +1,27 @@
1
+ import type { CommerceEmailContent } from "./commerceEmailContent.js";
2
+ import type { Locale } from "./email.js";
3
+ import { type EmailBlock } from "./blocks.js";
4
+ /** 1 = light same-session nudge, 24 = gentle nudge, 72 = final last-nudge. */
5
+ export type AbandonedCartReminderHours = 1 | 24 | 72;
6
+ export interface AbandonedCartEmailVars {
7
+ firstName: string | null;
8
+ /** Consumer-facing brand label injected by the app/composition root. */
9
+ brandName: string;
10
+ reminderHours: AbandonedCartReminderHours;
11
+ /** Absolute CTA URL back to the configurator. */
12
+ ctaUrl: string;
13
+ /** Absolute signed unsubscribe URL (purpose 'marketing_newsletter'). */
14
+ unsubscribeUrl: string;
15
+ }
16
+ export interface AbandonedCartEmailContent {
17
+ subject: string;
18
+ preheader: string;
19
+ blocks: EmailBlock[];
20
+ }
21
+ /** Bind host-selected copy and pure helpers without provider or environment access. */
22
+ export declare function createAbandonedCartContentFactory(input: {
23
+ commerceEmailContent: Pick<CommerceEmailContent, "abandonedCart">;
24
+ marketingUnsubscribeFooter: (locale: Locale, unsubscribeUrl: string) => EmailBlock[];
25
+ }): {
26
+ abandonedCartEmailContent: (locale: Locale, vars: AbandonedCartEmailVars, signoff: string) => AbandonedCartEmailContent;
27
+ };
@@ -0,0 +1,25 @@
1
+ import { button, heading, paragraph, } from "./blocks.js";
2
+ /** Bind host-selected copy and pure helpers without provider or environment access. */
3
+ export function createAbandonedCartContentFactory(input) {
4
+ const { commerceEmailContent, marketingUnsubscribeFooter } = input;
5
+ function abandonedCartEmailContent(locale, vars, signoff) {
6
+ const copy = commerceEmailContent.abandonedCart[locale];
7
+ const tone = vars.reminderHours === 72 ? copy.last : vars.reminderHours === 1 ? copy.first : copy.gentle;
8
+ const withBrand = (text) => text.split("{{brand}}").join(vars.brandName);
9
+ const blocks = [
10
+ heading(withBrand(tone.heading)),
11
+ paragraph(copy.greeting(vars.firstName)),
12
+ paragraph(withBrand(tone.intro)),
13
+ paragraph(withBrand(tone.body)),
14
+ button(tone.cta, vars.ctaUrl),
15
+ paragraph(signoff),
16
+ ...marketingUnsubscribeFooter(locale, vars.unsubscribeUrl),
17
+ ];
18
+ return {
19
+ subject: withBrand(tone.subject),
20
+ preheader: withBrand(tone.preheader),
21
+ blocks,
22
+ };
23
+ }
24
+ return { abandonedCartEmailContent };
25
+ }
@@ -0,0 +1,62 @@
1
+ import type { EmailBlock, Locale } from "./email.js";
2
+ export type AuthEmailLocale = Locale;
3
+ export interface AuthEmailContentPort {
4
+ buildAuthEmailContent(input: {
5
+ actionType: string;
6
+ userMetadata: Record<string, unknown> | null | undefined;
7
+ redirectTo: string;
8
+ actionLink: string;
9
+ otp: string | null;
10
+ }): {
11
+ locale: AuthEmailLocale;
12
+ subject: string;
13
+ preheader: string;
14
+ blocks: EmailBlock[];
15
+ };
16
+ }
17
+ export type AuthEmailPersistenceSkipReason = "admin_disabled" | "egress_suppressed";
18
+ export interface AuthEmailMessage {
19
+ sender: string;
20
+ recipient: string;
21
+ subject: string;
22
+ html: string;
23
+ text?: string;
24
+ }
25
+ export interface AuthEmailTransportResult {
26
+ outcome: {
27
+ ok: boolean;
28
+ messageId: string | null;
29
+ providerError: string | null;
30
+ aborted?: boolean;
31
+ adminDisabled?: boolean;
32
+ suppressed?: "drop" | null;
33
+ egressMode?: "production" | "sandbox_direct" | "sandbox_sink" | "drop";
34
+ };
35
+ providerResponse: Record<string, unknown>;
36
+ }
37
+ /** Provider-neutral outbound seam supplied by the API composition root. */
38
+ export interface AuthEmailTransportPort {
39
+ readonly providerKind: string;
40
+ send(message: AuthEmailMessage): Promise<AuthEmailTransportResult>;
41
+ }
42
+ /** Provider-neutral send observation owned by the Auth email candidate. */
43
+ export interface AuthEmailPersistencePort {
44
+ isTemplateEnabled(templateSlug: string, signal: AbortSignal): Promise<boolean>;
45
+ recordSend(input: {
46
+ templateSlug: string;
47
+ recipientEmail: string;
48
+ authUserId: string | null;
49
+ aggregateId: string;
50
+ dedupeKey: string;
51
+ providerKind: string;
52
+ outcome: {
53
+ ok: boolean;
54
+ messageId: string | null;
55
+ providerError: string | null;
56
+ aborted?: boolean;
57
+ skipReason?: AuthEmailPersistenceSkipReason | null;
58
+ };
59
+ providerResponse: Record<string, unknown>;
60
+ metadata: Record<string, unknown>;
61
+ }): Promise<void>;
62
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,28 @@
1
+ import type { CommerceEmailContent } from "./commerceEmailContent.js";
2
+ import type { Locale } from "./email.js";
3
+ import { type EmailBlock } from "./blocks.js";
4
+ export interface BackInStockEmailVars {
5
+ /** Consumer-facing brand label injected by the app/composition root. */
6
+ brandName: string;
7
+ /** The sku that came back. Used for context/keying – never shown in copy. */
8
+ sku: string;
9
+ /**
10
+ * Resolved, localized product name. When unknown (catalog miss / failure) the
11
+ * copy falls back to a generic localized label – a raw sku must never surface
12
+ * in a customer-facing subject or body.
13
+ */
14
+ productName?: string | null;
15
+ /** Absolute CTA URL (product page or configurator). null/absent → no button. */
16
+ ctaUrl?: string | null;
17
+ }
18
+ export interface BackInStockEmailContent {
19
+ subject: string;
20
+ preheader: string;
21
+ blocks: EmailBlock[];
22
+ }
23
+ /** Bind host-selected copy and pure helpers without provider or environment access. */
24
+ export declare function createBackInStockContentFactory(input: {
25
+ commerceEmailContent: Pick<CommerceEmailContent, "backInStock">;
26
+ }): {
27
+ backInStockEmailContent: (locale: Locale, vars: BackInStockEmailVars, signoff: string) => BackInStockEmailContent;
28
+ };
@@ -0,0 +1,28 @@
1
+ import { button, heading, paragraph, } from "./blocks.js";
2
+ /** Bind host-selected copy and pure helpers without provider or environment access. */
3
+ export function createBackInStockContentFactory(input) {
4
+ const { commerceEmailContent } = input;
5
+ function backInStockEmailContent(locale, vars, signoff) {
6
+ const copy = commerceEmailContent.backInStock[locale];
7
+ const productLabel = vars.productName && vars.productName.trim() !== ""
8
+ ? vars.productName.trim()
9
+ : copy.genericLabel;
10
+ const withBrand = (text) => text.split("{{brand}}").join(vars.brandName);
11
+ const blocks = [
12
+ heading(copy.heading(productLabel)),
13
+ paragraph(copy.greeting),
14
+ paragraph(copy.intro(productLabel)),
15
+ paragraph(withBrand(copy.hook)),
16
+ ];
17
+ if (vars.ctaUrl) {
18
+ blocks.push(button(copy.cta, vars.ctaUrl));
19
+ }
20
+ blocks.push(paragraph(signoff));
21
+ return {
22
+ subject: withBrand(copy.subject(productLabel)),
23
+ preheader: copy.preheader,
24
+ blocks,
25
+ };
26
+ }
27
+ return { backInStockEmailContent };
28
+ }
@@ -0,0 +1,24 @@
1
+ import type { ParagraphBlock, HeadingBlock, ListBlock, AccentBoxBlock, DataRow, DataTableBlock, FeatureCardBlock, ButtonBlock, LinkParagraphBlock, DividerBlock } from "./email.js";
2
+ export type { ParagraphBlock, HeadingBlock, ListBlock, AccentBoxBlock, DataRow, DataTableBlock, FeatureCardImage, FeatureCardBlock, ButtonBlock, LinkParagraphBlock, DividerBlock, EmailBlock } from "./email.js";
3
+ export declare const paragraph: (text: string, opts?: {
4
+ muted?: boolean;
5
+ afterCard?: boolean;
6
+ }) => ParagraphBlock;
7
+ export declare const heading: (text: string) => HeadingBlock;
8
+ export declare const list: (items: string[]) => ListBlock;
9
+ export declare const accentBox: (lines: string[], opts?: {
10
+ label?: string;
11
+ }) => AccentBoxBlock;
12
+ export declare const dataTable: (rows: DataRow[], opts?: {
13
+ link?: {
14
+ label: string;
15
+ href: string;
16
+ };
17
+ }) => DataTableBlock;
18
+ export declare const featureCard: (card: Omit<FeatureCardBlock, "kind">) => FeatureCardBlock;
19
+ export declare const button: (label: string, href: string) => ButtonBlock;
20
+ export declare const linkParagraph: (text: string, href: string, opts?: {
21
+ muted?: boolean;
22
+ afterCard?: boolean;
23
+ }) => LinkParagraphBlock;
24
+ export declare const divider: () => DividerBlock;
@@ -0,0 +1,23 @@
1
+ export const paragraph = (text, opts = {}) => ({
2
+ kind: "paragraph",
3
+ text,
4
+ muted: opts.muted ?? false,
5
+ ...(opts.afterCard ? { afterCard: true } : {}),
6
+ });
7
+ export const heading = (text) => ({ kind: "heading", text });
8
+ export const list = (items) => ({ kind: "list", items });
9
+ export const accentBox = (lines, opts = {}) => ({ kind: "accentBox", label: opts.label, lines });
10
+ export const dataTable = (rows, opts = {}) => ({ kind: "dataTable", rows, ...(opts.link ? { link: opts.link } : {}) });
11
+ export const featureCard = (card) => ({
12
+ kind: "featureCard",
13
+ ...card,
14
+ });
15
+ export const button = (label, href) => ({ kind: "button", label, href });
16
+ export const linkParagraph = (text, href, opts = {}) => ({
17
+ kind: "linkParagraph",
18
+ text,
19
+ href,
20
+ muted: opts.muted ?? false,
21
+ ...(opts.afterCard ? { afterCard: true } : {}),
22
+ });
23
+ export const divider = () => ({ kind: "divider" });
@@ -0,0 +1,22 @@
1
+ import type { CommerceEmailContent } from "./commerceEmailContent.js";
2
+ import type { Locale } from "./email.js";
3
+ import { type EmailBlock } from "./blocks.js";
4
+ export interface CheckoutExpiredEmailVars {
5
+ firstName: string | null;
6
+ orderId: string;
7
+ amountLabel: string | null;
8
+ ctaUrl?: string | null;
9
+ ctaKind?: "recovery" | "compose";
10
+ }
11
+ export interface CheckoutExpiredEmailContent {
12
+ subject: string;
13
+ preheader: string;
14
+ blocks: EmailBlock[];
15
+ }
16
+ /** Bind host-selected copy and pure helpers without provider or environment access. */
17
+ export declare function createCheckoutExpiredContentFactory(input: {
18
+ commerceEmailContent: Pick<CommerceEmailContent, "checkoutExpired" | "orderRefPrefix">;
19
+ formatCustomerOrderReference: (orderId: string, prefix: string) => string;
20
+ }): {
21
+ checkoutExpiredEmailContent: (locale: Locale, vars: CheckoutExpiredEmailVars, signoff: string) => CheckoutExpiredEmailContent;
22
+ };
@@ -0,0 +1,25 @@
1
+ import { accentBox, button, heading, paragraph, } from "./blocks.js";
2
+ /** Bind host-selected copy and pure helpers without provider or environment access. */
3
+ export function createCheckoutExpiredContentFactory(input) {
4
+ const { commerceEmailContent, formatCustomerOrderReference } = input;
5
+ function checkoutExpiredEmailContent(locale, vars, signoff) {
6
+ const copy = commerceEmailContent.checkoutExpired[vars.ctaKind === "recovery" ? "recovery" : "expired"][locale];
7
+ const orderRef = formatCustomerOrderReference(vars.orderId, commerceEmailContent.orderRefPrefix);
8
+ const blocks = [
9
+ heading(copy.heading),
10
+ paragraph(copy.greeting(vars.firstName)),
11
+ paragraph(copy.intro(orderRef)),
12
+ accentBox(copy.details(vars.amountLabel), { label: copy.detailsLabel }),
13
+ ];
14
+ if (vars.ctaUrl)
15
+ blocks.push(button(copy.cta, vars.ctaUrl));
16
+ blocks.push(paragraph(copy.outro));
17
+ blocks.push(paragraph(signoff));
18
+ return {
19
+ subject: copy.subject(orderRef),
20
+ preheader: copy.preheader,
21
+ blocks,
22
+ };
23
+ }
24
+ return { checkoutExpiredEmailContent };
25
+ }
@@ -0,0 +1,26 @@
1
+ import type { CommerceEmailContent } from "./commerceEmailContent.js";
2
+ import type { Locale } from "./email.js";
3
+ import { type EmailBlock } from "./blocks.js";
4
+ export type CheckoutRecoveryReminderHours = 1 | 20;
5
+ export type CheckoutRecoveryMode = "subscription_cycle" | "one_time";
6
+ export interface CheckoutRecoveryEmailVars {
7
+ firstName: string | null;
8
+ brandName: string;
9
+ reminderHours: CheckoutRecoveryReminderHours;
10
+ mode: CheckoutRecoveryMode;
11
+ /** Absolute signed deep-link to complete payment for this order. */
12
+ recoveryUrl: string;
13
+ /** Optional deployment-specific context consumed by the selected copy pack. */
14
+ contextName?: string | null;
15
+ }
16
+ export interface CheckoutRecoveryEmailContent {
17
+ subject: string;
18
+ preheader: string;
19
+ blocks: EmailBlock[];
20
+ }
21
+ /** Bind host-selected copy and pure helpers without provider or environment access. */
22
+ export declare function createCheckoutRecoveryContentFactory(input: {
23
+ commerceEmailContent: Pick<CommerceEmailContent, "checkoutRecovery">;
24
+ }): {
25
+ checkoutRecoveryEmailContent: (locale: Locale, vars: CheckoutRecoveryEmailVars, signoff: string) => CheckoutRecoveryEmailContent;
26
+ };
@@ -0,0 +1,37 @@
1
+ import { button, heading, paragraph } from "./blocks.js";
2
+ /** Bind host-selected copy and pure helpers without provider or environment access. */
3
+ export function createCheckoutRecoveryContentFactory(input) {
4
+ const { commerceEmailContent } = input;
5
+ function checkoutRecoveryEmailContent(locale, vars, signoff) {
6
+ const copy = commerceEmailContent.checkoutRecovery[locale];
7
+ const isFinal = vars.reminderHours === 20;
8
+ // The optional selected-pack context note runs only on the first nudge (1h).
9
+ // The final reminder (20h) stays businesslike, so it never sits next to the
10
+ // "order will be canceled" note.
11
+ const showCheer = !isFinal;
12
+ const isSubscription = vars.mode === "subscription_cycle";
13
+ const intro = isSubscription
14
+ ? copy.introSubscription
15
+ : showCheer
16
+ ? `${copy.introOneTime} ${copy.contextCheer(vars.contextName ?? null)}`
17
+ : copy.introOneTime;
18
+ const blocks = [
19
+ heading(isFinal ? copy.headingFinal : copy.headingFirst),
20
+ paragraph(copy.greeting(vars.firstName)),
21
+ paragraph(intro),
22
+ button(copy.cta, vars.recoveryUrl),
23
+ ];
24
+ if (isSubscription && showCheer) {
25
+ blocks.push(paragraph(copy.subscriptionCheer(vars.contextName ?? null, vars.brandName)));
26
+ }
27
+ if (isFinal)
28
+ blocks.push(paragraph(copy.finalNote));
29
+ blocks.push(paragraph(signoff));
30
+ return {
31
+ subject: isFinal ? copy.subjectFinal : copy.subjectFirst,
32
+ preheader: copy.preheader,
33
+ blocks,
34
+ };
35
+ }
36
+ return { checkoutRecoveryEmailContent };
37
+ }
@@ -0,0 +1,176 @@
1
+ import type { Locale } from "./email.js";
2
+ import type { CommerceEngagementEmailContent, CommerceRecoveryEmailContent } from "./commerceEngagementEmailContent.js";
3
+ export interface OrderDraftEmailCopy {
4
+ subject: (orderId: string) => string;
5
+ preheader: string;
6
+ heading: string;
7
+ greeting: (firstName: string | null) => string;
8
+ intro: (orderId: string) => string;
9
+ itemsLabel: string;
10
+ noItems: string;
11
+ subtotalLabel: string;
12
+ discountRowLabel: string;
13
+ totalLabel: string;
14
+ cta: string;
15
+ outro: string;
16
+ personalizedClosing: (name: string) => string;
17
+ }
18
+ export interface OrderPaidEmailCopy {
19
+ subject: (orderId: string) => string;
20
+ preheader: string;
21
+ heading: string;
22
+ greeting: (firstName: string | null) => string;
23
+ introOneTime: (orderId: string) => string;
24
+ introSubscription: (orderId: string) => string;
25
+ noItems: string;
26
+ subtotalLabel: string;
27
+ catalogProductsLabel: string;
28
+ discountRowLabel: string;
29
+ firstSubscriptionDiscountRowLabel: string;
30
+ productPayableLabel: string;
31
+ shippingLabel: string;
32
+ shippingFree: string;
33
+ totalLabel: string;
34
+ finalPaidLabel: string;
35
+ cta: string;
36
+ receiptNote: (name: string | null) => string;
37
+ outro: string;
38
+ withdrawalNotice: string;
39
+ withdrawalLinkLabel: string;
40
+ }
41
+ export interface OrderCanceledEmailCopy {
42
+ subject: (orderId: string) => string;
43
+ preheader: string;
44
+ heading: string;
45
+ greeting: (firstName: string | null) => string;
46
+ intro: (orderId: string) => string;
47
+ detailsLabel: string;
48
+ details: (amountLabel: string | null) => string[];
49
+ returnNote: (name: string | null) => string;
50
+ cta: string;
51
+ outro: string;
52
+ }
53
+ export interface OrderRefundedEmailCopy {
54
+ subject: (orderId: string) => string;
55
+ preheader: string;
56
+ heading: string;
57
+ greeting: (firstName: string | null) => string;
58
+ intro: (orderId: string) => string;
59
+ detailsLabel: string;
60
+ details: (amountLabel: string | null) => string[];
61
+ returnNote: (brandName: string) => string;
62
+ cta: string;
63
+ outro: string;
64
+ }
65
+ export interface PaymentFailedEmailCopy {
66
+ subject: (orderId: string) => string;
67
+ preheader: string;
68
+ heading: string;
69
+ greeting: (firstName: string | null) => string;
70
+ intro: (orderId: string) => string;
71
+ detailsLabel: string;
72
+ details: (amountLabel: string | null, mode: "subscription_cycle" | "one_time") => string[];
73
+ cta: string;
74
+ outro: string;
75
+ }
76
+ /**
77
+ * A guide the mail points the customer at: a document the deployment hosts
78
+ * under its own site origin, so `path` is site-relative and the content
79
+ * module joins it with the origin the sender passes in.
80
+ */
81
+ export interface EmailGuideCardCopy {
82
+ eyebrow: string;
83
+ title: string;
84
+ text: (contextName: string | null) => string;
85
+ cta: string;
86
+ /** Site-relative path of the document (leading slash). */
87
+ path: string;
88
+ /** Optional illustration, also site-relative. */
89
+ image?: {
90
+ path: string;
91
+ alt: string;
92
+ widthPx: number;
93
+ heightPx: number;
94
+ };
95
+ }
96
+ export interface ShipmentDispatchedEmailCopy {
97
+ subject: (orderRef: string) => string;
98
+ preheader: string;
99
+ heading: string;
100
+ greeting: (firstName: string | null) => string;
101
+ intro: (orderRef: string) => string;
102
+ trackingLabel: string;
103
+ trackParcel: string;
104
+ noTracking: string[];
105
+ noTrackingLabel: string;
106
+ /** The account guide the customer can read while the parcel travels. */
107
+ accountGuide: EmailGuideCardCopy;
108
+ outro: (contextName: string | null) => string;
109
+ }
110
+ export interface ShipmentDeliveredEmailCopy {
111
+ subject: (orderRef: string) => string;
112
+ preheader: string;
113
+ heading: string;
114
+ greeting: (firstName: string | null) => string;
115
+ intro: (orderRef: string, contextName: string | null) => string;
116
+ /** The getting-started guide to read before the first serving. */
117
+ startGuide: EmailGuideCardCopy;
118
+ outro: string;
119
+ }
120
+ export interface ShipmentExceptionEmailCopy {
121
+ subject: (orderRef: string) => string;
122
+ preheader: string;
123
+ heading: string;
124
+ greeting: (firstName: string | null) => string;
125
+ intro: (orderRef: string) => string;
126
+ reassuranceLabel: string;
127
+ reassurance: string[];
128
+ cta: string;
129
+ outro: string;
130
+ }
131
+ export interface ReturnApprovedEmailCopy {
132
+ subject: (orderRef: string) => string;
133
+ preheader: string;
134
+ heading: string;
135
+ greeting: (firstName: string | null) => string;
136
+ intro: (orderRef: string) => string;
137
+ returnAddressNotice: string;
138
+ cta: string;
139
+ }
140
+ export interface ReturnRejectedEmailCopy {
141
+ subject: (orderRef: string) => string;
142
+ preheader: string;
143
+ heading: string;
144
+ greeting: (firstName: string | null) => string;
145
+ intro: (orderRef: string) => string;
146
+ cta: string;
147
+ }
148
+ export interface ReturnRefundedEmailCopy {
149
+ subject: (orderRef: string) => string;
150
+ preheader: string;
151
+ heading: string;
152
+ greeting: (firstName: string | null) => string;
153
+ amountKnown: (amountLabel: string, orderRef: string) => string;
154
+ amountUnknown: (orderRef: string) => string;
155
+ returnNote: string;
156
+ cta: string;
157
+ }
158
+ /** Build-time selected transactional-copy owner for commerce mail. */
159
+ export interface CommerceEmailContent extends CommerceRecoveryEmailContent, CommerceEngagementEmailContent {
160
+ readonly id: string;
161
+ readonly orderRefPrefix: string;
162
+ readonly orderDraft: Record<Locale, OrderDraftEmailCopy>;
163
+ readonly orderPaid: Record<Locale, OrderPaidEmailCopy>;
164
+ readonly orderCanceled: Record<Locale, OrderCanceledEmailCopy>;
165
+ readonly orderRefunded: Record<Locale, OrderRefundedEmailCopy>;
166
+ readonly paymentFailed: Record<Locale, PaymentFailedEmailCopy>;
167
+ readonly shipmentDispatched: Record<Locale, ShipmentDispatchedEmailCopy>;
168
+ readonly shipmentDelivered: Record<Locale, ShipmentDeliveredEmailCopy>;
169
+ readonly shipmentException: Record<Locale, ShipmentExceptionEmailCopy>;
170
+ readonly returns: {
171
+ readonly approved: Record<Locale, ReturnApprovedEmailCopy>;
172
+ readonly rejected: Record<Locale, ReturnRejectedEmailCopy>;
173
+ readonly refunded: Record<Locale, ReturnRefundedEmailCopy>;
174
+ };
175
+ }
176
+ export type CommerceFulfillmentEmailContent = Pick<CommerceEmailContent, "shipmentDispatched" | "shipmentDelivered" | "shipmentException" | "returns">;