@openlup/platform 0.15.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 (531) hide show
  1. package/CHANGELOG.md +124 -0
  2. package/README.md +633 -6
  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/dist/identity/adminAuthBinding.d.ts +28 -0
  196. package/dist/identity/adminAuthBinding.js +66 -0
  197. package/dist/identity/adminAuthorization.d.ts +34 -0
  198. package/dist/identity/adminAuthorization.js +72 -0
  199. package/dist/identity/adminContracts.d.ts +70 -0
  200. package/dist/identity/adminContracts.js +43 -0
  201. package/dist/identity/adminMagicLinkHandler.d.ts +39 -0
  202. package/dist/identity/adminMagicLinkHandler.js +94 -0
  203. package/dist/identity/adminMeHandler.d.ts +15 -0
  204. package/dist/identity/adminMeHandler.js +49 -0
  205. package/dist/identity/adminPreflight.d.ts +16 -0
  206. package/dist/identity/adminPreflight.js +38 -0
  207. package/dist/identity/adminUserActionPorts.d.ts +31 -0
  208. package/dist/identity/adminUserActionPorts.js +51 -0
  209. package/dist/identity/adminUserHandlers.d.ts +38 -0
  210. package/dist/identity/adminUserHandlers.js +140 -0
  211. package/dist/identity/authVerifyUrl.d.ts +6 -0
  212. package/dist/identity/authVerifyUrl.js +54 -0
  213. package/dist/identity/contracts/contracts.d.ts +8 -0
  214. package/dist/identity/contracts/contracts.js +2 -0
  215. package/dist/identity/contracts/emailTrust.d.ts +3 -0
  216. package/dist/identity/contracts/emailTrust.js +4 -0
  217. package/dist/identity/contracts/index.d.ts +8 -0
  218. package/dist/identity/contracts/index.js +8 -0
  219. package/dist/identity/contracts/oidc.d.ts +3 -0
  220. package/dist/identity/contracts/oidc.js +9 -0
  221. package/dist/identity/contracts/operatorAuth.d.ts +35 -0
  222. package/dist/identity/contracts/operatorAuth.js +53 -0
  223. package/dist/identity/contracts/operatorInvitations.d.ts +37 -0
  224. package/dist/identity/contracts/operatorInvitations.js +1 -0
  225. package/dist/identity/contracts/ports.d.ts +22 -0
  226. package/dist/identity/contracts/ports.js +1 -0
  227. package/dist/identity/contracts/privacy.d.ts +62 -0
  228. package/dist/identity/contracts/privacy.js +99 -0
  229. package/dist/identity/contracts/providerList.d.ts +3 -0
  230. package/dist/identity/contracts/providerList.js +11 -0
  231. package/dist/identity/contracts/types.d.ts +16 -0
  232. package/dist/identity/contracts/types.js +4 -0
  233. package/dist/identity/contribution.d.ts +8 -0
  234. package/dist/identity/contribution.js +22 -0
  235. package/dist/identity/customerAuth.d.ts +8 -0
  236. package/dist/identity/customerAuth.js +1 -0
  237. package/dist/identity/customerAuthorization.d.ts +5 -0
  238. package/dist/identity/customerAuthorization.js +22 -0
  239. package/dist/identity/customerContracts.d.ts +42 -0
  240. package/dist/identity/customerContracts.js +43 -0
  241. package/dist/identity/customerIdentityBinding.d.ts +44 -0
  242. package/dist/identity/customerIdentityBinding.js +60 -0
  243. package/dist/identity/customerIdentityVerifier.d.ts +8 -0
  244. package/dist/identity/customerIdentityVerifier.js +101 -0
  245. package/dist/identity/customerMagicLinkHandler.d.ts +38 -0
  246. package/dist/identity/customerMagicLinkHandler.js +115 -0
  247. package/dist/identity/customerMeHandler.d.ts +11 -0
  248. package/dist/identity/customerMeHandler.js +46 -0
  249. package/dist/identity/customerPorts.d.ts +4 -0
  250. package/dist/identity/customerPorts.js +1 -0
  251. package/dist/identity/customerSessionBinding.d.ts +38 -0
  252. package/dist/identity/customerSessionBinding.js +80 -0
  253. package/dist/identity/customerSessionIssuer.d.ts +10 -0
  254. package/dist/identity/customerSessionIssuer.js +57 -0
  255. package/dist/identity/customerVerifyOtpHandler.d.ts +8 -0
  256. package/dist/identity/customerVerifyOtpHandler.js +41 -0
  257. package/dist/identity/directOperatorInvitationStore.d.ts +19 -0
  258. package/dist/identity/directOperatorInvitationStore.js +193 -0
  259. package/dist/identity/emailTrustPolicy.d.ts +14 -0
  260. package/dist/identity/emailTrustPolicy.js +16 -0
  261. package/dist/identity/identityPersonalDataContributor.d.ts +7 -0
  262. package/dist/identity/identityPersonalDataContributor.js +46 -0
  263. package/dist/identity/identityReadiness.d.ts +10 -0
  264. package/dist/identity/identityReadiness.js +25 -0
  265. package/dist/identity/index.d.ts +67 -0
  266. package/dist/identity/index.js +66 -0
  267. package/dist/identity/inviteAdminUserUseCase.d.ts +89 -0
  268. package/dist/identity/inviteAdminUserUseCase.js +75 -0
  269. package/dist/identity/jwtLibrary.d.ts +54 -0
  270. package/dist/identity/jwtLibrary.js +1 -0
  271. package/dist/identity/machineActorWriteFence.d.ts +4 -0
  272. package/dist/identity/machineActorWriteFence.js +13 -0
  273. package/dist/identity/magicLinkPorts.d.ts +28 -0
  274. package/dist/identity/magicLinkPorts.js +1 -0
  275. package/dist/identity/managedAccountLinkStore.d.ts +2 -0
  276. package/dist/identity/managedAccountLinkStore.js +93 -0
  277. package/dist/identity/managedAdminAuthVerifier.d.ts +7 -0
  278. package/dist/identity/managedAdminAuthVerifier.js +27 -0
  279. package/dist/identity/managedAdminUserGateways.d.ts +123 -0
  280. package/dist/identity/managedAdminUserGateways.js +127 -0
  281. package/dist/identity/managedClient.d.ts +90 -0
  282. package/dist/identity/managedClient.js +10 -0
  283. package/dist/identity/managedClientAuthLink.d.ts +3 -0
  284. package/dist/identity/managedClientAuthLink.js +115 -0
  285. package/dist/identity/managedCustomerMe.d.ts +13 -0
  286. package/dist/identity/managedCustomerMe.js +30 -0
  287. package/dist/identity/managedEmailTrustPolicy.d.ts +11 -0
  288. package/dist/identity/managedEmailTrustPolicy.js +105 -0
  289. package/dist/identity/managedIdentityVerifier.d.ts +2 -0
  290. package/dist/identity/managedIdentityVerifier.js +16 -0
  291. package/dist/identity/managedPrivacy.d.ts +4 -0
  292. package/dist/identity/managedPrivacy.js +39 -0
  293. package/dist/identity/messages.d.ts +29 -0
  294. package/dist/identity/messages.js +39 -0
  295. package/dist/identity/oidcIdentityVerifier.d.ts +45 -0
  296. package/dist/identity/oidcIdentityVerifier.js +309 -0
  297. package/dist/identity/oidcPrincipal.d.ts +2 -0
  298. package/dist/identity/oidcPrincipal.js +10 -0
  299. package/dist/identity/operatorAdminAuth.d.ts +6 -0
  300. package/dist/identity/operatorAdminAuth.js +13 -0
  301. package/dist/identity/operatorAuthHandlers.d.ts +18 -0
  302. package/dist/identity/operatorAuthHandlers.js +71 -0
  303. package/dist/identity/operatorIdentityVerifier.d.ts +8 -0
  304. package/dist/identity/operatorIdentityVerifier.js +19 -0
  305. package/dist/identity/operatorInvitationLifecycle.d.ts +7 -0
  306. package/dist/identity/operatorInvitationLifecycle.js +15 -0
  307. package/dist/identity/operatorInvitationPorts.d.ts +31 -0
  308. package/dist/identity/operatorInvitationPorts.js +1 -0
  309. package/dist/identity/operatorPorts.d.ts +37 -0
  310. package/dist/identity/operatorPorts.js +1 -0
  311. package/dist/identity/operatorSession.d.ts +13 -0
  312. package/dist/identity/operatorSession.js +31 -0
  313. package/dist/identity/operatorTokenAdminAuth.d.ts +15 -0
  314. package/dist/identity/operatorTokenAdminAuth.js +50 -0
  315. package/dist/identity/personalDataRegistry.d.ts +4 -0
  316. package/dist/identity/personalDataRegistry.js +11 -0
  317. package/dist/identity/platformAdminAuth.d.ts +15 -0
  318. package/dist/identity/platformAdminAuth.js +1 -0
  319. package/dist/identity/ports.d.ts +127 -0
  320. package/dist/identity/ports.js +38 -0
  321. package/dist/identity/postgresAccountLinkStore.d.ts +7 -0
  322. package/dist/identity/postgresAccountLinkStore.js +52 -0
  323. package/dist/identity/postgresCustomerMe.d.ts +3 -0
  324. package/dist/identity/postgresCustomerMe.js +21 -0
  325. package/dist/identity/postgresCustomerRecovery.d.ts +11 -0
  326. package/dist/identity/postgresCustomerRecovery.js +57 -0
  327. package/dist/identity/postgresOperatorIdentity.d.ts +5 -0
  328. package/dist/identity/postgresOperatorIdentity.js +137 -0
  329. package/dist/identity/postgresPrivacy.d.ts +7 -0
  330. package/dist/identity/postgresPrivacy.js +126 -0
  331. package/dist/identity/privacyHandlers.d.ts +10 -0
  332. package/dist/identity/privacyHandlers.js +35 -0
  333. package/dist/identity/privacyPorts.d.ts +72 -0
  334. package/dist/identity/privacyPorts.js +8 -0
  335. package/dist/identity/privacyUseCases.d.ts +5 -0
  336. package/dist/identity/privacyUseCases.js +55 -0
  337. package/dist/identity/reconcileAccountForPrincipal.d.ts +20 -0
  338. package/dist/identity/reconcileAccountForPrincipal.js +66 -0
  339. package/dist/identity/reconcileAccountHandler.d.ts +11 -0
  340. package/dist/identity/reconcileAccountHandler.js +30 -0
  341. package/dist/identity/removeAdminUserUseCase.d.ts +31 -0
  342. package/dist/identity/removeAdminUserUseCase.js +60 -0
  343. package/dist/identity/response.d.ts +3 -0
  344. package/dist/identity/response.js +2 -0
  345. package/dist/identity/sql.d.ts +44 -0
  346. package/dist/identity/sql.js +10 -0
  347. package/package.json +22 -2
  348. package/release-gates.json +63 -8
  349. package/sql/20260807190000_comms_outbox_rail.sql +329 -0
  350. package/sql/20260810120001_transactional_delivery_receipts.sql +209 -0
  351. package/sql/20260810180001_self_host_actor_auth.sql +40 -0
  352. package/sql/20260810204057_customer_identity_read.sql +31 -0
  353. package/sql/20260813120000_communications_control_plane.sql +568 -0
  354. package/sql/20260814090000_customer_selfservice_recovery.sql +477 -0
  355. package/sql/20260901143000_admin_membership_authority_foundation.sql +234 -0
  356. package/sql/20261018120000_identity_operator_invitations.sql +42 -0
  357. package/sql/20261018180000_identity_operator_challenges.sql +56 -0
  358. package/sql/20261018190000_identity_privacy_erasures.sql +15 -0
  359. package/sql/20261019200000_communications_native_operations.sql +68 -0
  360. package/src/communications/accountingDocumentDelivery.ts +63 -0
  361. package/src/communications/authEmailError.ts +10 -0
  362. package/src/communications/authEmailPorts.ts +2 -0
  363. package/src/communications/authSendEmailHook.ts +310 -0
  364. package/src/communications/capturedTransactionalDelivery.ts +29 -0
  365. package/src/communications/communicationSync.ts +96 -0
  366. package/src/communications/communicationSyncWorker.ts +302 -0
  367. package/src/communications/consentPolicy.ts +82 -0
  368. package/src/communications/consentPurposeRegistry.ts +31 -0
  369. package/src/communications/contracts/abandonedCart.ts +64 -0
  370. package/src/communications/contracts/authEmail.ts +73 -0
  371. package/src/communications/contracts/backInStock.ts +69 -0
  372. package/src/communications/contracts/blocks.ts +47 -0
  373. package/src/communications/contracts/checkoutExpired.ts +62 -0
  374. package/src/communications/contracts/checkoutRecovery.ts +68 -0
  375. package/src/communications/contracts/commerceEmailContent.ts +189 -0
  376. package/src/communications/contracts/commerceEngagementEmailContent.ts +106 -0
  377. package/src/communications/contracts/consent.ts +13 -0
  378. package/src/communications/contracts/email.ts +210 -0
  379. package/src/communications/contracts/guideCard.ts +34 -0
  380. package/src/communications/contracts/index.ts +51 -0
  381. package/src/communications/contracts/inquiryContent.ts +53 -0
  382. package/src/communications/contracts/invoiceDocumentContent.ts +57 -0
  383. package/src/communications/contracts/marketingAutomation.ts +45 -0
  384. package/src/communications/contracts/newsletter.ts +78 -0
  385. package/src/communications/contracts/newsletterWebhook.ts +23 -0
  386. package/src/communications/contracts/optOutContent.ts +23 -0
  387. package/src/communications/contracts/orderCanceled.ts +64 -0
  388. package/src/communications/contracts/orderDraft.ts +112 -0
  389. package/src/communications/contracts/orderPaid.ts +147 -0
  390. package/src/communications/contracts/orderRefunded.ts +63 -0
  391. package/src/communications/contracts/paymentFailed.ts +59 -0
  392. package/src/communications/contracts/publicIntake.ts +53 -0
  393. package/src/communications/contracts/render.ts +271 -0
  394. package/src/communications/contracts/reorderReminder.ts +59 -0
  395. package/src/communications/contracts/returnEmails.ts +113 -0
  396. package/src/communications/contracts/reviewEffects.ts +60 -0
  397. package/src/communications/contracts/reviewRequest.ts +59 -0
  398. package/src/communications/contracts/roleNotificationContent.ts +65 -0
  399. package/src/communications/contracts/shipmentDelivered.ts +59 -0
  400. package/src/communications/contracts/shipmentDispatched.ts +77 -0
  401. package/src/communications/contracts/shipmentException.ts +62 -0
  402. package/src/communications/contracts/subscriptionActivationActionRequired.ts +37 -0
  403. package/src/communications/contracts/subscriptionAddressChanged.ts +56 -0
  404. package/src/communications/contracts/subscriptionCancelled.ts +57 -0
  405. package/src/communications/contracts/subscriptionCycleSkipped.ts +80 -0
  406. package/src/communications/contracts/subscriptionDeliveryRescheduled.ts +79 -0
  407. package/src/communications/contracts/subscriptionEmailContent.ts +163 -0
  408. package/src/communications/contracts/subscriptionPackageChanged.ts +59 -0
  409. package/src/communications/contracts/subscriptionPauseReminder.ts +63 -0
  410. package/src/communications/contracts/subscriptionPaused.ts +58 -0
  411. package/src/communications/contracts/subscriptionPaymentExpired.ts +83 -0
  412. package/src/communications/contracts/subscriptionPaymentFailed.ts +117 -0
  413. package/src/communications/contracts/subscriptionPaymentRecovered.ts +50 -0
  414. package/src/communications/contracts/subscriptionRenewalAtRisk.ts +85 -0
  415. package/src/communications/contracts/subscriptionRenewalUpcoming.ts +103 -0
  416. package/src/communications/contracts/subscriptionResumed.ts +60 -0
  417. package/src/communications/contracts/subscriptionWelcome.ts +102 -0
  418. package/src/communications/contracts/subscriptionWinback.ts +62 -0
  419. package/src/communications/contracts/transactionalDelivery.ts +43 -0
  420. package/src/communications/contracts/types.ts +58 -0
  421. package/src/communications/contribution.ts +33 -0
  422. package/src/communications/deliveryReconciliation.ts +108 -0
  423. package/src/communications/emailEgress.ts +111 -0
  424. package/src/communications/emailHealthAssessment.ts +106 -0
  425. package/src/communications/emailHealthMetricsPort.ts +17 -0
  426. package/src/communications/emailNotificationControl.ts +28 -0
  427. package/src/communications/emailObservation.ts +43 -0
  428. package/src/communications/emailTransport.ts +167 -0
  429. package/src/communications/errors.ts +34 -0
  430. package/src/communications/index.ts +46 -0
  431. package/src/communications/integrationEventSignature.ts +49 -0
  432. package/src/communications/managedTransactionalDeliveryStore.ts +59 -0
  433. package/src/communications/nativeOperations.ts +40 -0
  434. package/src/communications/newsletterIntegrationContracts.ts +154 -0
  435. package/src/communications/newsletterWebhookPort.ts +104 -0
  436. package/src/communications/newsletterWebhookProcessing.ts +102 -0
  437. package/src/communications/originPolicy.ts +151 -0
  438. package/src/communications/postgresCommunicationSync.ts +127 -0
  439. package/src/communications/postgresConsentEvaluator.ts +49 -0
  440. package/src/communications/postgresDeliveryReconciliation.ts +101 -0
  441. package/src/communications/postgresNativeOperations.ts +55 -0
  442. package/src/communications/postgresNotificationRecipients.ts +57 -0
  443. package/src/communications/postgresTransactionalDeliveryStore.ts +79 -0
  444. package/src/communications/preferencesToken.ts +62 -0
  445. package/src/communications/publicPreferenceToken.ts +68 -0
  446. package/src/communications/shipmentNotifications.ts +78 -0
  447. package/src/communications/sql.ts +7 -0
  448. package/src/communications/standardWebhooks.ts +84 -0
  449. package/src/communications/status.ts +51 -0
  450. package/src/communications/transactionalDelivery.ts +114 -0
  451. package/src/communications/transactionalDeliveryIdentifier.ts +9 -0
  452. package/src/communications/unsubscribeToken.ts +186 -0
  453. package/src/communications/validation.ts +3 -0
  454. package/src/http/index.ts +1 -0
  455. package/src/http/webhookSignature.ts +96 -0
  456. package/src/identity/adminAuthBinding.ts +76 -0
  457. package/src/identity/adminAuthorization.ts +106 -0
  458. package/src/identity/adminContracts.ts +52 -0
  459. package/src/identity/adminMagicLinkHandler.ts +132 -0
  460. package/src/identity/adminMeHandler.ts +64 -0
  461. package/src/identity/adminPreflight.ts +46 -0
  462. package/src/identity/adminUserActionPorts.ts +78 -0
  463. package/src/identity/adminUserHandlers.ts +174 -0
  464. package/src/identity/authVerifyUrl.ts +56 -0
  465. package/src/identity/contracts/contracts.ts +15 -0
  466. package/src/identity/contracts/emailTrust.ts +5 -0
  467. package/src/identity/contracts/index.ts +8 -0
  468. package/src/identity/contracts/oidc.ts +10 -0
  469. package/src/identity/contracts/operatorAuth.ts +61 -0
  470. package/src/identity/contracts/operatorInvitations.ts +32 -0
  471. package/src/identity/contracts/ports.ts +25 -0
  472. package/src/identity/contracts/privacy.ts +86 -0
  473. package/src/identity/contracts/providerList.ts +13 -0
  474. package/src/identity/contracts/types.ts +25 -0
  475. package/src/identity/contribution.ts +29 -0
  476. package/src/identity/customerAuth.ts +3 -0
  477. package/src/identity/customerAuthorization.ts +38 -0
  478. package/src/identity/customerContracts.ts +52 -0
  479. package/src/identity/customerIdentityBinding.ts +125 -0
  480. package/src/identity/customerIdentityVerifier.ts +141 -0
  481. package/src/identity/customerMagicLinkHandler.ts +171 -0
  482. package/src/identity/customerMeHandler.ts +64 -0
  483. package/src/identity/customerPorts.ts +2 -0
  484. package/src/identity/customerSessionBinding.ts +114 -0
  485. package/src/identity/customerSessionIssuer.ts +71 -0
  486. package/src/identity/customerVerifyOtpHandler.ts +44 -0
  487. package/src/identity/directOperatorInvitationStore.ts +178 -0
  488. package/src/identity/emailTrustPolicy.ts +16 -0
  489. package/src/identity/identityPersonalDataContributor.ts +48 -0
  490. package/src/identity/identityReadiness.ts +26 -0
  491. package/src/identity/index.ts +69 -0
  492. package/src/identity/inviteAdminUserUseCase.ts +134 -0
  493. package/src/identity/jwtLibrary.ts +24 -0
  494. package/src/identity/machineActorWriteFence.ts +19 -0
  495. package/src/identity/magicLinkPorts.ts +7 -0
  496. package/src/identity/managedAccountLinkStore.ts +105 -0
  497. package/src/identity/managedAdminAuthVerifier.ts +40 -0
  498. package/src/identity/managedAdminUserGateways.ts +164 -0
  499. package/src/identity/managedClient.ts +58 -0
  500. package/src/identity/managedClientAuthLink.ts +120 -0
  501. package/src/identity/managedCustomerMe.ts +52 -0
  502. package/src/identity/managedEmailTrustPolicy.ts +90 -0
  503. package/src/identity/managedIdentityVerifier.ts +20 -0
  504. package/src/identity/managedPrivacy.ts +37 -0
  505. package/src/identity/messages.ts +41 -0
  506. package/src/identity/oidcIdentityVerifier.ts +229 -0
  507. package/src/identity/oidcPrincipal.ts +10 -0
  508. package/src/identity/operatorAdminAuth.ts +14 -0
  509. package/src/identity/operatorAuthHandlers.ts +52 -0
  510. package/src/identity/operatorIdentityVerifier.ts +19 -0
  511. package/src/identity/operatorInvitationLifecycle.ts +14 -0
  512. package/src/identity/operatorInvitationPorts.ts +11 -0
  513. package/src/identity/operatorPorts.ts +11 -0
  514. package/src/identity/operatorSession.ts +30 -0
  515. package/src/identity/operatorTokenAdminAuth.ts +79 -0
  516. package/src/identity/personalDataRegistry.ts +8 -0
  517. package/src/identity/platformAdminAuth.ts +7 -0
  518. package/src/identity/ports.ts +158 -0
  519. package/src/identity/postgresAccountLinkStore.ts +49 -0
  520. package/src/identity/postgresCustomerMe.ts +33 -0
  521. package/src/identity/postgresCustomerRecovery.ts +72 -0
  522. package/src/identity/postgresOperatorIdentity.ts +123 -0
  523. package/src/identity/postgresPrivacy.ts +117 -0
  524. package/src/identity/privacyHandlers.ts +33 -0
  525. package/src/identity/privacyPorts.ts +52 -0
  526. package/src/identity/privacyUseCases.ts +48 -0
  527. package/src/identity/reconcileAccountForPrincipal.ts +99 -0
  528. package/src/identity/reconcileAccountHandler.ts +36 -0
  529. package/src/identity/removeAdminUserUseCase.ts +85 -0
  530. package/src/identity/response.ts +3 -0
  531. package/src/identity/sql.ts +10 -0
@@ -0,0 +1,63 @@
1
+ import type { CommerceEmailContent } from "./commerceEmailContent.js";
2
+ import type { Locale } from "./email.js";
3
+ import {
4
+ accentBox,
5
+ button,
6
+ heading,
7
+ paragraph,
8
+ type EmailBlock,
9
+ } from "./blocks.js";
10
+
11
+ export interface OrderRefundedEmailVars {
12
+ firstName: string | null;
13
+ brandName: string;
14
+ orderId: string;
15
+ /** Formatted gross refund total, e.g. "129,99 zł", or null when unknown. */
16
+ amountLabel: string | null;
17
+ /** Absolute CTA URL (back to shop); null/absent → no button. */
18
+ ctaUrl?: string | null;
19
+ }
20
+
21
+ export interface OrderRefundedEmailContent {
22
+ subject: string;
23
+ preheader: string;
24
+ blocks: EmailBlock[];
25
+ }
26
+
27
+ /** Bind host-selected copy and pure helpers without provider or environment access. */
28
+ export function createOrderRefundedContentFactory(input: {
29
+ commerceEmailContent: Pick<CommerceEmailContent, "orderRefunded" | "orderRefPrefix">;
30
+ formatCustomerOrderReference: (orderId: string, prefix: string) => string;
31
+ }) {
32
+ const { commerceEmailContent, formatCustomerOrderReference } = input;
33
+
34
+ function orderRefundedEmailContent(
35
+ locale: Locale,
36
+ vars: OrderRefundedEmailVars,
37
+ signoff: string,
38
+ ): OrderRefundedEmailContent {
39
+ const copy = commerceEmailContent.orderRefunded[locale];
40
+ const orderRef = formatCustomerOrderReference(vars.orderId, commerceEmailContent.orderRefPrefix);
41
+ const blocks: EmailBlock[] = [
42
+ heading(copy.heading),
43
+ paragraph(copy.greeting(vars.firstName)),
44
+ paragraph(copy.intro(orderRef)),
45
+ accentBox(copy.details(vars.amountLabel), { label: copy.detailsLabel }),
46
+ paragraph(copy.returnNote(vars.brandName)),
47
+ ];
48
+
49
+ if (vars.ctaUrl) {
50
+ blocks.push(button(copy.cta, vars.ctaUrl));
51
+ }
52
+
53
+ blocks.push(paragraph(copy.outro));
54
+ blocks.push(paragraph(signoff));
55
+
56
+ return {
57
+ subject: copy.subject(orderRef),
58
+ preheader: copy.preheader,
59
+ blocks,
60
+ };
61
+ }
62
+ return { orderRefundedEmailContent };
63
+ }
@@ -0,0 +1,59 @@
1
+ import type { CommerceEmailContent } from "./commerceEmailContent.js";
2
+ import type { Locale } from "./email.js";
3
+ import {
4
+ accentBox,
5
+ button,
6
+ heading,
7
+ paragraph,
8
+ type EmailBlock,
9
+ } from "./blocks.js";
10
+
11
+ export interface PaymentFailedEmailVars {
12
+ firstName: string | null;
13
+ orderId: string;
14
+ /** Formatted gross total, e.g. "129,99 zł", or null when unknown. */
15
+ amountLabel: string | null;
16
+ mode: "subscription_cycle" | "one_time";
17
+ /** Absolute tokenized /konto/... recovery URL for the same order context. */
18
+ recoveryUrl: string;
19
+ }
20
+
21
+ export interface PaymentFailedEmailContent {
22
+ subject: string;
23
+ preheader: string;
24
+ blocks: EmailBlock[];
25
+ }
26
+
27
+ /** Bind host-selected copy and pure helpers without provider or environment access. */
28
+ export function createPaymentFailedContentFactory(input: {
29
+ commerceEmailContent: Pick<CommerceEmailContent, "paymentFailed" | "orderRefPrefix">;
30
+ formatCustomerOrderReference: (orderId: string, prefix: string) => string;
31
+ }) {
32
+ const { commerceEmailContent, formatCustomerOrderReference } = input;
33
+
34
+ function paymentFailedEmailContent(
35
+ locale: Locale,
36
+ vars: PaymentFailedEmailVars,
37
+ signoff: string,
38
+ ): PaymentFailedEmailContent {
39
+ const copy = commerceEmailContent.paymentFailed[locale];
40
+ const orderRef = formatCustomerOrderReference(vars.orderId, commerceEmailContent.orderRefPrefix);
41
+ const blocks: EmailBlock[] = [
42
+ heading(copy.heading),
43
+ paragraph(copy.greeting(vars.firstName)),
44
+ paragraph(copy.intro(orderRef)),
45
+ accentBox(copy.details(vars.amountLabel, vars.mode), { label: copy.detailsLabel }),
46
+ button(copy.cta, vars.recoveryUrl),
47
+ ];
48
+
49
+ blocks.push(paragraph(copy.outro));
50
+ blocks.push(paragraph(signoff));
51
+
52
+ return {
53
+ subject: copy.subject(orderRef),
54
+ preheader: copy.preheader,
55
+ blocks,
56
+ };
57
+ }
58
+ return { paymentFailedEmailContent };
59
+ }
@@ -0,0 +1,53 @@
1
+ /** Delivery seam for public intake capabilities. */
2
+ export type PublicIntakeDeliveryPurpose = "transactional" | "admin_notification";
3
+ export type PublicIntakeRecipientKind = "external_contact" | "admin_internal";
4
+
5
+ export interface PublicIntakePolicyDecision {
6
+ allowed: boolean;
7
+ reason: string;
8
+ decisionId: string | null;
9
+ }
10
+
11
+ export interface PublicIntakeDeliveryResult {
12
+ /** The delivery implementation returned a durable message identifier. */
13
+ accepted: boolean;
14
+ messageId: string | null;
15
+ /** A policy or explicit no-egress terminal outcome. */
16
+ skip: string | null;
17
+ /** A non-sent terminal outcome, including an ambiguous provider acceptance. */
18
+ error: string | null;
19
+ }
20
+
21
+ export interface PublicIntakeDeliveryMessage {
22
+ sender: string;
23
+ to: string;
24
+ subject: string;
25
+ html: string;
26
+ text?: string;
27
+ replyTo?: string;
28
+ }
29
+
30
+ export interface PublicIntakeDeliveryPort {
31
+ evaluatePolicy(input: {
32
+ email: string | null | undefined;
33
+ purpose: PublicIntakeDeliveryPurpose;
34
+ source: string;
35
+ recipientKind: PublicIntakeRecipientKind;
36
+ sourceTable?: "b2b_inquiries";
37
+ sourceId?: string | null;
38
+ metadata: Record<string, unknown>;
39
+ }): Promise<PublicIntakePolicyDecision>;
40
+ deliver(input: {
41
+ source: string;
42
+ templateSlug: string;
43
+ policyDecisionId: string | null;
44
+ dedupeKey: string;
45
+ purpose: PublicIntakeDeliveryPurpose;
46
+ triggerEvent: string;
47
+ recipientEmail: string;
48
+ aggregateType: string;
49
+ aggregateId: string;
50
+ metadata?: Record<string, unknown>;
51
+ message: PublicIntakeDeliveryMessage;
52
+ }): Promise<PublicIntakeDeliveryResult>;
53
+ }
@@ -0,0 +1,271 @@
1
+ // Pure branded HTML/text rendering; all presentation is supplied by the host.
2
+ import type { EmailBlock, EmailTheme, EmailRenderer, RenderEmailInput } from "./email.js";
3
+
4
+ function escapeHtml(value: string): string {
5
+ return value
6
+ .replace(/&/g, "&amp;")
7
+ .replace(/</g, "&lt;")
8
+ .replace(/>/g, "&gt;")
9
+ .replace(/"/g, "&quot;");
10
+ }
11
+
12
+ /**
13
+ * Escape, turn intentional newlines into <br>, then apply the only two inline
14
+ * markers the renderer understands: `**bold**` and `[text](href)`.
15
+ *
16
+ * Both run AFTER escaping, so the markers can never be used to inject markup:
17
+ * by the time they match, every `<`, `>`, `&` and `"` in the source string is
18
+ * already an entity. The link marker writes its href from the captured group,
19
+ * which cannot contain `)` or `"`, so it cannot break out of the attribute.
20
+ *
21
+ * They exist because a content module otherwise cannot emphasise or link ONE
22
+ * WORD of a sentence — only a whole block. Two places need exactly that: the
23
+ * pet's name in body copy, and the "Wypisz się" link inside the marketing
24
+ * fine print. Keep this list at two; a third marker means the email body has
25
+ * become a markup language and belongs in a block kind instead.
26
+ */
27
+ function inlineHtml(value: string): string {
28
+ return escapeHtml(value)
29
+ .replace(/\n/g, "<br>")
30
+ .replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
31
+ }
32
+
33
+ /**
34
+ * Schemes an inline link may use. Everything else — `javascript:`, `data:`, a
35
+ * bare path — renders as literal text instead of an anchor.
36
+ *
37
+ * This allowlist is the actual guard, not the character class in the pattern
38
+ * below: by the time the marker matches, escaping has already turned any quote
39
+ * into `&quot;`, so excluding a raw `"` would exclude nothing. Without the
40
+ * allowlist, `[x](javascript:…)` produced a working javascript: anchor.
41
+ */
42
+ const INLINE_LINK_SAFE_SCHEME = /^(?:https?:\/\/|mailto:)/i;
43
+
44
+ /** inlineHtml plus link support; separate because links need the theme colour. */
45
+ function inlineHtmlWithLinks(value: string, theme: EmailTheme, color: string): string {
46
+ return inlineHtml(value).replace(
47
+ /\[([^\]]+)\]\(([^)]+)\)/g,
48
+ (match, text: string, href: string) =>
49
+ INLINE_LINK_SAFE_SCHEME.test(href)
50
+ ? `<a href="${href}" style="color:${color};text-decoration:underline;">${text}</a>`
51
+ : match,
52
+ );
53
+ }
54
+
55
+ /**
56
+ * The two block kinds that may leave the content card, derived from EmailBlock
57
+ * rather than imported by name. The constraint that forced this - a generated
58
+ * Deno mirror whose fixed import prologue brought in EmailBlock and nothing
59
+ * else, so naming ParagraphBlock/LinkParagraphBlock here compiled under Node
60
+ * and failed under Deno - retired with the hosted-function tree on 2026-09-05.
61
+ * The Extract stays because it is still the narrower, self-maintaining
62
+ * spelling.
63
+ */
64
+ type AfterCardBlock = Extract<EmailBlock, { kind: "paragraph" | "linkParagraph" }>;
65
+
66
+ /**
67
+ * True when a block opts out of the content card. Local (not exported from
68
+ * blocks.ts) because the generated mirror's prologue carried types only, so a
69
+ * runtime import from blocks would not have survived generation; that generator
70
+ * retired with the hosted-function tree on 2026-09-05. It stays local because
71
+ * nothing outside this file needs it.
72
+ */
73
+ function isAfterCardBlock(block: EmailBlock): block is AfterCardBlock {
74
+ return (block.kind === "paragraph" || block.kind === "linkParagraph")
75
+ && block.afterCard === true;
76
+ }
77
+
78
+ function renderBlockHtml(block: EmailBlock, theme: EmailTheme): string {
79
+ switch (block.kind) {
80
+ case "heading":
81
+ return `<p style="color:${theme.textHeading};font-size:22px;font-weight:700;margin:0 0 20px 0;line-height:1.3;">${inlineHtml(block.text)}</p>`;
82
+ case "paragraph": {
83
+ const color = block.muted ? theme.textMuted : theme.textBody;
84
+ // Below the card the copy is fine print: smaller and centred, because it
85
+ // sits on the page background rather than inside the content card.
86
+ const size = block.afterCard ? "11px" : block.muted ? "13px" : "15px";
87
+ const box = block.afterCard
88
+ ? "line-height:1.6;text-align:center;margin:0 0 8px 0;"
89
+ : "line-height:1.7;margin:0 0 16px 0;";
90
+ return `<p style="color:${color};font-size:${size};${box}">${inlineHtmlWithLinks(block.text, theme, color)}</p>`;
91
+ }
92
+ case "list": {
93
+ const items = block.items
94
+ .map(
95
+ (item) =>
96
+ `<li style="margin:0 0 6px 0;">${inlineHtml(item)}</li>`,
97
+ )
98
+ .join("");
99
+ return `<ul style="color:${theme.textBody};font-size:15px;line-height:1.7;margin:0 0 16px 0;padding-left:22px;">${items}</ul>`;
100
+ }
101
+ case "accentBox": {
102
+ const label = block.label
103
+ ? `<p style="color:${theme.brand};font-size:12px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;margin:0 0 8px 0;">${inlineHtml(block.label)}</p>`
104
+ : "";
105
+ const body = block.lines.map((line) => inlineHtml(line)).join("<br>");
106
+ return `<table width="100%" cellpadding="0" cellspacing="0" style="border-radius:12px;border-left:4px solid ${theme.brand};background-color:${theme.accentBg};margin:0 0 20px 0;"><tr><td style="padding:20px 24px;">${label}<p style="color:${theme.textBody};font-size:14px;line-height:1.7;margin:0;">${body}</p></td></tr></table>`;
107
+ }
108
+ case "dataTable": {
109
+ const rows = block.rows
110
+ .map(
111
+ (row) =>
112
+ `${escapeHtml(row.label)}: <strong style="color:${theme.textHeading};">${escapeHtml(row.value)}</strong>`,
113
+ )
114
+ .join("<br>");
115
+ const link = block.link
116
+ ? `<br><a href="${escapeHtml(block.link.href)}" style="color:${theme.brand};text-decoration:underline;font-weight:600;">${escapeHtml(block.link.label)}</a>`
117
+ : "";
118
+ return `<table width="100%" cellpadding="0" cellspacing="0" style="border-radius:12px;border-left:4px solid ${theme.brand};background-color:${theme.accentBg};margin:0 0 20px 0;"><tr><td style="padding:20px 24px;"><p style="color:${theme.textBody};font-size:14px;line-height:1.8;margin:0;">${rows}${link}</p></td></tr></table>`;
119
+ }
120
+ case "featureCard": {
121
+ const accent = theme.featureCardAccent ?? theme.brand;
122
+ const eyebrow = block.eyebrow
123
+ ? `<p style="margin:0 0 6px 0;font-size:11px;letter-spacing:0.16em;text-transform:uppercase;color:${accent};font-weight:700;">${inlineHtml(block.eyebrow)}</p>`
124
+ : "";
125
+ // Two inline-blocks that fit side by side at the full card width and wrap
126
+ // to one column when the client is narrower: the image keeps its width,
127
+ // the copy takes the remaining room, and no media query is involved.
128
+ const image = block.image
129
+ ? `<div style="display:inline-block;width:100%;max-width:${block.image.widthPx}px;vertical-align:middle;"><img src="${escapeHtml(block.image.src)}" alt="${escapeHtml(block.image.alt)}" width="${block.image.widthPx}" height="${block.image.heightPx}" style="display:block;width:100%;max-width:${block.image.widthPx}px;height:auto;margin:0 auto;color:${accent};font-size:13px;font-weight:700;font-family:${theme.fontFamily};"></div>`
130
+ : "";
131
+ // Copy column cap: the container width minus the content card's and this
132
+ // card's side paddings (2x36 + 2x20 = 112), the image, and 28px of slack so
133
+ // client rounding never wraps the pair at full width.
134
+ const copyWidth = block.image ? `max-width:${theme.maxWidthPx - 112 - block.image.widthPx - 28}px;` : "";
135
+ const copy = `<div style="display:inline-block;width:100%;${copyWidth}vertical-align:middle;text-align:left;padding:8px 0 0 0;">${eyebrow}<p style="margin:0 0 8px 0;font-size:18px;line-height:1.3;font-weight:700;color:${theme.textHeading};">${inlineHtml(block.title)}</p><p style="margin:0 0 14px 0;font-size:14px;line-height:1.55;color:${theme.textBody};">${inlineHtmlWithLinks(block.text, theme, theme.textBody)}</p><a href="${escapeHtml(block.cta.href)}" style="display:inline-block;background-color:${theme.brand};color:${theme.onBrand};text-decoration:none;font-weight:700;font-size:14px;padding:12px 22px;border-radius:${theme.buttonRadiusPx}px;">${escapeHtml(block.cta.label)}</a></div>`;
136
+ return `<table width="100%" cellpadding="0" cellspacing="0" style="border-radius:${theme.cardRadiusPx}px;background-color:${theme.featureCardBg ?? theme.accentBg};margin:0 0 20px 0;"><tr><td style="padding:18px 20px;text-align:center;font-size:0;">${image}${copy}</td></tr></table>`;
137
+ }
138
+ case "button":
139
+ return `<table cellpadding="0" cellspacing="0" style="margin:0 0 20px 0;"><tr><td style="background-color:${theme.brand};border-radius:${theme.buttonRadiusPx}px;"><a href="${escapeHtml(block.href)}" style="display:inline-block;padding:14px 32px;color:${theme.onBrand};font-size:14px;font-weight:700;text-decoration:none;">${escapeHtml(block.label)}</a></td></tr></table>`;
140
+ case "linkParagraph": {
141
+ const color = block.muted ? theme.textMuted : theme.brand;
142
+ const size = block.afterCard ? "11px" : block.muted ? "13px" : "15px";
143
+ const box = block.afterCard
144
+ ? "line-height:1.6;text-align:center;margin:0 0 8px 0;"
145
+ : "line-height:1.7;margin:0 0 16px 0;";
146
+ return `<p style="color:${color};font-size:${size};${box}"><a href="${escapeHtml(block.href)}" style="color:${color};text-decoration:underline;">${inlineHtml(block.text)}</a></p>`;
147
+ }
148
+ case "divider":
149
+ return `<table width="100%" cellpadding="0" cellspacing="0" style="margin:0 0 20px 0;"><tr><td style="border-top:1px solid ${theme.hairline};font-size:0;line-height:0;height:1px;">&nbsp;</td></tr></table>`;
150
+ }
151
+ }
152
+
153
+ function renderBlockText(block: EmailBlock): string {
154
+ switch (block.kind) {
155
+ case "heading":
156
+ case "paragraph":
157
+ return block.text;
158
+ case "list":
159
+ return block.items.map((item) => `- ${item}`).join("\n");
160
+ case "accentBox": {
161
+ const label = block.label ? `${block.label.toUpperCase()}\n` : "";
162
+ return `${label}${block.lines.join("\n")}`;
163
+ }
164
+ case "dataTable": {
165
+ const rows = block.rows.map((row) => `${row.label}: ${row.value}`);
166
+ if (block.link) rows.push(`${block.link.label}: ${block.link.href}`);
167
+ return rows.join("\n");
168
+ }
169
+ case "featureCard": {
170
+ const eyebrow = block.eyebrow ? `${block.eyebrow.toUpperCase()}\n` : "";
171
+ return `${eyebrow}${block.title}\n${block.text}\n${block.cta.label}: ${block.cta.href}`;
172
+ }
173
+ case "button":
174
+ return `${block.label}: ${block.href}`;
175
+ case "linkParagraph":
176
+ return `${block.text}: ${block.href}`;
177
+ case "divider":
178
+ return "—";
179
+ }
180
+ }
181
+
182
+ function renderHtml(input: RenderEmailInput): string {
183
+ const { locale } = input;
184
+ const { theme } = input.brand;
185
+ const chrome = input.brand.chrome[locale];
186
+ const preheader = input.preheader
187
+ ? `<span style="display:none;max-height:0;overflow:hidden;opacity:0;mso-hide:all;">${escapeHtml(input.preheader)}</span>`
188
+ : "";
189
+ // Blocks marked afterCard render BELOW the white card, above the footer: the
190
+ // durable-medium legal notices that must be delivered but must not compete
191
+ // with the message itself.
192
+ const cardBlocks = input.blocks.filter((block) => !isAfterCardBlock(block));
193
+ const postCardBlocks = input.blocks.filter((block) => isAfterCardBlock(block));
194
+ const body = cardBlocks.map((block) => renderBlockHtml(block, theme)).join("");
195
+ const postCardRow = postCardBlocks.length
196
+ ? `<tr><td style="padding:18px 14px 0 14px;">${postCardBlocks
197
+ .map((block) => renderBlockHtml({ ...block, muted: true }, theme))
198
+ .join("")}</td></tr>`
199
+ : "";
200
+
201
+ // Header: image logo when the theme provides one (text logoText is the alt /
202
+ // blocked-image fallback); otherwise the text wordmark.
203
+ // Wordmark fallback, shared by both branches: this is what the header looks
204
+ // like with no image at all, and — via the text styling copied onto the <img>
205
+ // below — what it degrades to when a client blocks remote images. Gmail,
206
+ // Outlook and Apple Mail all block by default for unknown senders, so the
207
+ // blocked state is the common case, not the edge case.
208
+ const wordmark = `<span style="font-size:32px;font-weight:900;color:${theme.brand};letter-spacing:2px;">${escapeHtml(theme.logoText)}</span>`;
209
+ const header = theme.logoImageUrl
210
+ // The text properties on the <img> are not decoration: mail clients apply
211
+ // them to the alt text, so a blocked image renders "openlup" in the brand
212
+ // colour and weight rather than small grey default text. Outlook desktop
213
+ // ignores styled alt entirely, hence the mso conditional wordmark.
214
+ ? `<!--[if !mso]><!--><img src="${escapeHtml(theme.logoImageUrl)}" alt="${escapeHtml(chrome.logoAlt)}"${theme.logoImageWidthPx ? ` width="${theme.logoImageWidthPx}"` : ""}${theme.logoImageHeightPx ? ` height="${theme.logoImageHeightPx}"` : ""} style="display:inline-block;border:0;${theme.logoImageHeightPx ? `height:${theme.logoImageHeightPx}px;` : ""}${theme.logoImageWidthPx ? `width:${theme.logoImageWidthPx}px;` : ""}color:${theme.brand};font-size:32px;font-weight:900;letter-spacing:2px;font-family:${theme.fontFamily};"><!--<![endif]--><!--[if mso]>${wordmark}<![endif]-->`
215
+ : wordmark;
216
+
217
+ // Optional full-width hero banner under the header (per-locale artwork).
218
+ const bannerRow = chrome.bannerImageUrl
219
+ ? `<tr><td style="padding:0 0 24px 0;"><img src="${escapeHtml(chrome.bannerImageUrl)}" alt="${escapeHtml(chrome.bannerImageAlt ?? chrome.logoAlt)}" width="${theme.maxWidthPx}" style="display:block;width:100%;max-width:${theme.maxWidthPx}px;height:auto;border:0;border-radius:12px;"></td></tr>`
220
+ : "";
221
+
222
+ // Footer: optional tagline + optional legal fine print. Omitted entirely when
223
+ // the brand supplies neither.
224
+ const footerParts: string[] = [];
225
+ if (chrome.footerTagline) {
226
+ footerParts.push(
227
+ `<p style="color:${theme.textMuted};font-size:12px;margin:0 0 10px 0;">${escapeHtml(chrome.footerTagline)}</p>`,
228
+ );
229
+ }
230
+ if (chrome.footerLegalLines?.length) {
231
+ footerParts.push(
232
+ `<p style="color:${theme.textMuted};font-size:11px;line-height:1.6;margin:0;">${chrome.footerLegalLines.map((line) => escapeHtml(line)).join("<br>")}</p>`,
233
+ );
234
+ }
235
+ const footerRow = footerParts.length
236
+ ? `<tr><td style="padding:24px 0 0 0;text-align:center;">${footerParts.join("")}</td></tr>`
237
+ : "";
238
+
239
+ return [
240
+ '<!DOCTYPE html><html><head><meta charset="utf-8">',
241
+ '<meta name="viewport" content="width=device-width,initial-scale=1"></head>',
242
+ `<body style="margin:0;padding:0;background-color:${theme.bodyBg};font-family:${theme.fontFamily};">`,
243
+ preheader,
244
+ `<table width="100%" cellpadding="0" cellspacing="0" style="background-color:${theme.bodyBg};"><tr><td align="center" style="padding:40px 20px;">`,
245
+ `<table width="${theme.maxWidthPx}" cellpadding="0" cellspacing="0" style="max-width:${theme.maxWidthPx}px;width:100%;">`,
246
+ `<tr><td style="padding:0 0 24px 0;text-align:center;">${header}</td></tr>`,
247
+ bannerRow,
248
+ `<tr><td style="background-color:${theme.cardBg};border-radius:${theme.cardRadiusPx}px;padding:36px 36px 40px 36px;">${body}</td></tr>`,
249
+ postCardRow,
250
+ footerRow,
251
+ "</table></td></tr></table></body></html>",
252
+ ].join("");
253
+ }
254
+
255
+ function renderText(input: RenderEmailInput): string {
256
+ const chrome = input.brand.chrome[input.locale];
257
+ const body = input.blocks
258
+ .map((block) => renderBlockText(block))
259
+ .filter((part) => part.length > 0)
260
+ .join("\n\n");
261
+ const footerLines = [chrome.footerTagline, ...(chrome.footerLegalLines ?? [])].filter(
262
+ (line): line is string => typeof line === "string" && line.length > 0,
263
+ );
264
+ return footerLines.length ? `${body}\n\n${footerLines.join("\n")}` : body;
265
+ }
266
+
267
+ export const renderEmail: EmailRenderer = (input) => ({
268
+ subject: input.subject,
269
+ html: renderHtml(input),
270
+ text: renderText(input),
271
+ });
@@ -0,0 +1,59 @@
1
+ import type { CommerceEmailContent } from "./commerceEmailContent.js";
2
+ import type { Locale } from "./email.js";
3
+ import {
4
+ accentBox,
5
+ button,
6
+ heading,
7
+ paragraph,
8
+ type EmailBlock,
9
+ } from "./blocks.js";
10
+
11
+ export interface ReorderReminderEmailVars {
12
+ firstName: string | null;
13
+ /** Brand label used in body copy; callers must inject the app/tenant label. */
14
+ brandName: string;
15
+ /** Absolute CTA URL back to the configurator (compose/reorder the next box). */
16
+ ctaUrl: string;
17
+ /** Absolute signed unsubscribe URL (purpose 'marketing_newsletter'). */
18
+ unsubscribeUrl: string;
19
+ }
20
+
21
+ export interface ReorderReminderEmailContent {
22
+ subject: string;
23
+ preheader: string;
24
+ blocks: EmailBlock[];
25
+ }
26
+
27
+ /** Bind host-selected copy and pure helpers without provider or environment access. */
28
+ export function createReorderReminderContentFactory(input: {
29
+ commerceEmailContent: Pick<CommerceEmailContent, "reorderReminder">;
30
+ marketingUnsubscribeFooter: (locale: Locale, unsubscribeUrl: string) => EmailBlock[];
31
+ }) {
32
+ const { commerceEmailContent, marketingUnsubscribeFooter } = input;
33
+
34
+ function reorderReminderEmailContent(
35
+ locale: Locale,
36
+ vars: ReorderReminderEmailVars,
37
+ signoff: string,
38
+ ): ReorderReminderEmailContent {
39
+ const copy = commerceEmailContent.reorderReminder[locale];
40
+
41
+ const blocks: EmailBlock[] = [
42
+ heading(copy.heading),
43
+ paragraph(copy.greeting(vars.firstName)),
44
+ paragraph(copy.intro(vars.brandName)),
45
+ paragraph(copy.consistencyNote),
46
+ accentBox(copy.upsellLines, { label: copy.upsellLabel }),
47
+ button(copy.cta, vars.ctaUrl),
48
+ paragraph(signoff),
49
+ ...marketingUnsubscribeFooter(locale, vars.unsubscribeUrl),
50
+ ];
51
+
52
+ return {
53
+ subject: copy.subject,
54
+ preheader: copy.preheader,
55
+ blocks,
56
+ };
57
+ }
58
+ return { reorderReminderEmailContent };
59
+ }
@@ -0,0 +1,113 @@
1
+ import type { CommerceEmailContent } from "./commerceEmailContent.js";
2
+ import type { Locale } from "./email.js";
3
+ import {
4
+ accentBox,
5
+ button,
6
+ heading,
7
+ paragraph,
8
+ type EmailBlock,
9
+ } from "./blocks.js";
10
+
11
+ export interface ReturnEmailVars {
12
+ firstName: string | null;
13
+ orderId: string;
14
+ /** Formatted gross refund total, e.g. "129,99 zł", or null when unknown. */
15
+ amountLabel?: string | null;
16
+ /** Absolute CTA URL appropriate for the state (returns guide / account / shop). */
17
+ ctaUrl?: string | null;
18
+ }
19
+
20
+ export interface ReturnEmailContent {
21
+ subject: string;
22
+ preheader: string;
23
+ blocks: EmailBlock[];
24
+ }
25
+
26
+ /** Bind host-selected copy and pure helpers without provider or environment access. */
27
+ export function createReturnEmailsContentFactory(input: {
28
+ commerceEmailContent: Pick<CommerceEmailContent, "returns" | "orderRefPrefix">;
29
+ formatCustomerOrderReference: (orderId: string, prefix: string) => string;
30
+ }) {
31
+ const { commerceEmailContent, formatCustomerOrderReference } = input;
32
+
33
+ function returnApprovedEmailContent(
34
+ locale: Locale,
35
+ vars: ReturnEmailVars,
36
+ signoff: string,
37
+ ): ReturnEmailContent {
38
+ const copy = commerceEmailContent.returns.approved[locale];
39
+ const orderRef = formatCustomerOrderReference(
40
+ vars.orderId,
41
+ commerceEmailContent.orderRefPrefix,
42
+ );
43
+ const blocks: EmailBlock[] = [
44
+ heading(copy.heading),
45
+ paragraph(copy.greeting(vars.firstName)),
46
+ paragraph(copy.intro(orderRef)),
47
+ accentBox([copy.returnAddressNotice]),
48
+ ];
49
+ if (vars.ctaUrl) {
50
+ blocks.push(button(copy.cta, vars.ctaUrl));
51
+ }
52
+ blocks.push(paragraph(signoff));
53
+ return {
54
+ subject: copy.subject(orderRef),
55
+ preheader: copy.preheader,
56
+ blocks,
57
+ };
58
+ }
59
+
60
+ function returnRejectedEmailContent(
61
+ locale: Locale,
62
+ vars: ReturnEmailVars,
63
+ signoff: string,
64
+ ): ReturnEmailContent {
65
+ const copy = commerceEmailContent.returns.rejected[locale];
66
+ const orderRef = formatCustomerOrderReference(
67
+ vars.orderId,
68
+ commerceEmailContent.orderRefPrefix,
69
+ );
70
+ const blocks: EmailBlock[] = [
71
+ heading(copy.heading),
72
+ paragraph(copy.greeting(vars.firstName)),
73
+ paragraph(copy.intro(orderRef)),
74
+ ];
75
+ if (vars.ctaUrl) blocks.push(button(copy.cta, vars.ctaUrl));
76
+ blocks.push(paragraph(signoff));
77
+ return {
78
+ subject: copy.subject(orderRef),
79
+ preheader: copy.preheader,
80
+ blocks,
81
+ };
82
+ }
83
+
84
+ function returnRefundedEmailContent(
85
+ locale: Locale,
86
+ vars: ReturnEmailVars,
87
+ signoff: string,
88
+ ): ReturnEmailContent {
89
+ const copy = commerceEmailContent.returns.refunded[locale];
90
+ const orderRef = formatCustomerOrderReference(
91
+ vars.orderId,
92
+ commerceEmailContent.orderRefPrefix,
93
+ );
94
+ const blocks: EmailBlock[] = [
95
+ heading(copy.heading),
96
+ paragraph(copy.greeting(vars.firstName)),
97
+ paragraph(
98
+ vars.amountLabel
99
+ ? copy.amountKnown(vars.amountLabel, orderRef)
100
+ : copy.amountUnknown(orderRef),
101
+ ),
102
+ paragraph(copy.returnNote),
103
+ ];
104
+ if (vars.ctaUrl) blocks.push(button(copy.cta, vars.ctaUrl));
105
+ blocks.push(paragraph(signoff));
106
+ return {
107
+ subject: copy.subject(orderRef),
108
+ preheader: copy.preheader,
109
+ blocks,
110
+ };
111
+ }
112
+ return { returnApprovedEmailContent, returnRejectedEmailContent, returnRefundedEmailContent };
113
+ }
@@ -0,0 +1,60 @@
1
+ import type { CommerceEmailContent } from "./commerceEmailContent.js";
2
+ import type { Locale } from "./email.js";
3
+ import {
4
+ button,
5
+ heading,
6
+ paragraph,
7
+ type EmailBlock,
8
+ } from "./blocks.js";
9
+
10
+ export interface ReviewEffectsEmailVars {
11
+ firstName: string | null;
12
+ /** Consumer-facing brand label injected by the app/composition root. */
13
+ brandName: string;
14
+ /** Absolute CTA URL to the review form (baseUrl + locale review path + token). */
15
+ reviewUrl: string;
16
+ /** Absolute signed unsubscribe URL (purpose = 'marketing_newsletter'). */
17
+ unsubscribeUrl: string;
18
+ /** Optional product, service, or other review context label. */
19
+ contextName?: string | null;
20
+ }
21
+
22
+ export interface ReviewEffectsEmailContent {
23
+ subject: string;
24
+ preheader: string;
25
+ blocks: EmailBlock[];
26
+ }
27
+
28
+ /** Bind host-selected copy and pure helpers without provider or environment access. */
29
+ export function createReviewEffectsContentFactory(input: {
30
+ commerceEmailContent: Pick<CommerceEmailContent, "reviewEffects">;
31
+ marketingUnsubscribeFooter: (locale: Locale, unsubscribeUrl: string) => EmailBlock[];
32
+ }) {
33
+ const { commerceEmailContent, marketingUnsubscribeFooter } = input;
34
+
35
+ function reviewEffectsEmailContent(
36
+ locale: Locale,
37
+ vars: ReviewEffectsEmailVars,
38
+ signoff: string,
39
+ ): ReviewEffectsEmailContent {
40
+ const copy = commerceEmailContent.reviewEffects[locale];
41
+ const withBrand = (text: string) => text.split("{{brand}}").join(vars.brandName);
42
+ const blocks: EmailBlock[] = [
43
+ heading(copy.heading),
44
+ paragraph(copy.greeting(vars.firstName)),
45
+ paragraph(copy.intro(vars.contextName ?? null, vars.brandName)),
46
+ paragraph(copy.body),
47
+ button(copy.cta, vars.reviewUrl),
48
+ paragraph(copy.outro),
49
+ paragraph(signoff),
50
+ ...marketingUnsubscribeFooter(locale, vars.unsubscribeUrl),
51
+ ];
52
+
53
+ return {
54
+ subject: withBrand(copy.subject),
55
+ preheader: copy.preheader,
56
+ blocks,
57
+ };
58
+ }
59
+ return { reviewEffectsEmailContent };
60
+ }