@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,106 @@
1
+ import type { EmailHealthMetrics } from "./emailHealthMetricsPort.js";
2
+
3
+ /**
4
+ * Pure email-health assessment layer: env-derived thresholds plus the
5
+ * healthy/unhealthy verdict over the delivery metrics. Extracted from
6
+ * `api/_cron/emailHealthWatchdogJob.ts` (which sat at the 300-LOC source cap)
7
+ * so the incident-driven `permanent_delivery_failure` breach could land here
8
+ * with unit coverage and no database in the loop.
9
+ */
10
+
11
+ type Env = Record<string, string | undefined>;
12
+
13
+ export interface EmailHealthThresholds {
14
+ windowMinutes: number;
15
+ stuckMinutes: number;
16
+ minAttemptsForRatio: number;
17
+ maxFailureRatio: number;
18
+ maxStuck: number;
19
+ /**
20
+ * Absolute ceiling on in-window deliveries that ended `status='failed'`
21
+ * (handler-final loss of a customer notification: outbox discard,
22
+ * max-attempts exhaustion). Default 0 — a single lost email breaches.
23
+ * Bounces/complaints are separate statuses and stay ratio-only.
24
+ */
25
+ maxPermanentFailures: number;
26
+ }
27
+
28
+ export interface EmailHealthBreach {
29
+ code: string;
30
+ detail: string;
31
+ }
32
+
33
+ export interface EmailHealthAssessment {
34
+ healthy: boolean;
35
+ failureRatio: number;
36
+ breaches: EmailHealthBreach[];
37
+ }
38
+
39
+ export function readEmailHealthThresholds(env: Env): EmailHealthThresholds {
40
+ return {
41
+ windowMinutes: positiveInt(env.EMAIL_HEALTH_WINDOW_MINUTES, 60),
42
+ stuckMinutes: positiveInt(env.EMAIL_HEALTH_STUCK_MINUTES, 30),
43
+ minAttemptsForRatio: positiveInt(env.EMAIL_HEALTH_MIN_ATTEMPTS_FOR_RATIO, 5),
44
+ maxFailureRatio: clampRatio(env.EMAIL_HEALTH_MAX_FAILURE_RATIO, 0.5),
45
+ maxStuck: nonNegativeInt(env.EMAIL_HEALTH_MAX_STUCK, 0),
46
+ maxPermanentFailures: nonNegativeInt(env.EMAIL_HEALTH_MAX_PERMANENT_FAILURES, 0),
47
+ };
48
+ }
49
+
50
+ /**
51
+ * Pure decision: turn raw counts into a healthy/unhealthy verdict. Kept side
52
+ * effect-free so the thresholds logic is unit-tested without a database.
53
+ */
54
+ export function evaluateEmailHealth(
55
+ metrics: EmailHealthMetrics,
56
+ thresholds: EmailHealthThresholds,
57
+ ): EmailHealthAssessment {
58
+ const failureRatio = metrics.attempted > 0 ? metrics.failed / metrics.attempted : 0;
59
+ const breaches: EmailHealthBreach[] = [];
60
+
61
+ if (metrics.attempted >= thresholds.minAttemptsForRatio && failureRatio > thresholds.maxFailureRatio) {
62
+ breaches.push({
63
+ code: "failure_ratio",
64
+ detail: `${metrics.failed}/${metrics.attempted} (${(failureRatio * 100).toFixed(1)}%) > ${(thresholds.maxFailureRatio * 100).toFixed(1)}%`,
65
+ });
66
+ }
67
+ // Absolute, ratio-independent: one terminally-failed delivery is one
68
+ // customer notification we finally did not send. Incident 2026-07-24: a
69
+ // single discarded payment-recovery email stayed invisible because the
70
+ // ratio breach needs minAttemptsForRatio sends in the window.
71
+ if (metrics.permanentFailures > thresholds.maxPermanentFailures) {
72
+ breaches.push({
73
+ code: "permanent_delivery_failure",
74
+ detail: `${metrics.permanentFailures} delivery(ies) terminally failed > ${thresholds.maxPermanentFailures}`,
75
+ });
76
+ }
77
+ if (metrics.stuckProcessing > thresholds.maxStuck) {
78
+ breaches.push({
79
+ code: "stuck_processing",
80
+ detail: `${metrics.stuckProcessing} email(s) stuck in 'processing' > ${thresholds.stuckMinutes}m`,
81
+ });
82
+ }
83
+ if (metrics.stuckScheduled > thresholds.maxStuck) {
84
+ breaches.push({
85
+ code: "stuck_scheduled",
86
+ detail: `${metrics.stuckScheduled} scheduled email(s) overdue and undispatched`,
87
+ });
88
+ }
89
+
90
+ return { healthy: breaches.length === 0, failureRatio, breaches };
91
+ }
92
+
93
+ function positiveInt(raw: string | undefined, fallback: number): number {
94
+ const n = Number(raw);
95
+ return Number.isFinite(n) && n > 0 ? Math.floor(n) : fallback;
96
+ }
97
+
98
+ function nonNegativeInt(raw: string | undefined, fallback: number): number {
99
+ const n = Number(raw);
100
+ return Number.isFinite(n) && n >= 0 ? Math.floor(n) : fallback;
101
+ }
102
+
103
+ function clampRatio(raw: string | undefined, fallback: number): number {
104
+ const n = Number(raw);
105
+ return Number.isFinite(n) && n >= 0 && n <= 1 ? n : fallback;
106
+ }
@@ -0,0 +1,17 @@
1
+ export interface EmailHealthMetrics {
2
+ attempted: number;
3
+ sent: number;
4
+ failed: number;
5
+ delayed: number;
6
+ /**
7
+ * In-window rows that ended `status='failed'` — handler-final loss of a
8
+ * customer notification (outbox discard / max-attempts). Distinct from
9
+ * `failed` above, which also counts bounces/complaints/missed for the
10
+ * ratio signal.
11
+ */
12
+ permanentFailures: number;
13
+ /** `processing` rows whose last transition is older than the stuck threshold. */
14
+ stuckProcessing: number;
15
+ /** `planned`/`queued` rows past their `expected_send_at` but still not sent. */
16
+ stuckScheduled: number;
17
+ }
@@ -0,0 +1,28 @@
1
+ export interface EmailNotificationControlPort {
2
+ isEnabled(slug: string, signal: AbortSignal): Promise<boolean>;
3
+ }
4
+
5
+ export interface EmailNotificationControlClient {
6
+ from(table: string): EmailNotificationControlQueryBuilder;
7
+ }
8
+
9
+ interface EmailNotificationControlQueryBuilder {
10
+ select(columns: string): EmailNotificationControlQueryBuilder;
11
+ eq(column: string, value: unknown): EmailNotificationControlQueryBuilder;
12
+ maybeSingle(): PromiseLike<{ data: unknown; error: { code?: string; message?: string } | null }>;
13
+ }
14
+
15
+ export function createEmailNotificationControlPort(
16
+ client: EmailNotificationControlClient,
17
+ createPolicy: (client: EmailNotificationControlClient) => { shouldDeliver(input: { slug: string; signal: AbortSignal }): Promise<{ allow: boolean }> },
18
+ ): EmailNotificationControlPort {
19
+ const policy = createPolicy(client);
20
+ return {
21
+ isEnabled(slug: string, signal: AbortSignal): Promise<boolean> {
22
+ return policy.shouldDeliver({ slug, signal }).then((decision) => decision.allow);
23
+ },
24
+ };
25
+ }
26
+
27
+ // Compatibility alias for the pre-neutralization lifecycle consumer.
28
+ export const createSupabaseEmailNotificationControlPort = createEmailNotificationControlPort;
@@ -0,0 +1,43 @@
1
+ /** An authenticated, decoded, environment-admitted observation. Evidence is opaque. */
2
+ export interface EmailObservation {
3
+ eventType: string;
4
+ eventAt: string;
5
+ metadata: Record<string, unknown>;
6
+ }
7
+
8
+ export interface EmailObservationPort {
9
+ findSend(messageId: string): Promise<{ id: string } | null>;
10
+ applyObservation(observation: EmailObservation & { sendId: string; messageId: string }): Promise<void>;
11
+ recordAttempt(attempt: EmailObservationDisposition): Promise<void>;
12
+ }
13
+
14
+ export type EmailObservationDisposition =
15
+ | { outcome: "send_not_found" }
16
+ | { outcome: "processed"; sendId: string };
17
+
18
+ /** Lookup/apply failures propagate; attempt telemetry cannot overturn authoritative success. */
19
+ export async function processEmailObservation(input: {
20
+ port: EmailObservationPort;
21
+ messageId: string;
22
+ // Construct evidence only after a local send is found (including clock reads).
23
+ observation: () => EmailObservation;
24
+ }): Promise<EmailObservationDisposition> {
25
+ const send = await input.port.findSend(input.messageId);
26
+ let disposition: EmailObservationDisposition;
27
+ if (!send) {
28
+ disposition = { outcome: "send_not_found" };
29
+ } else {
30
+ await input.port.applyObservation({
31
+ sendId: send.id,
32
+ messageId: input.messageId,
33
+ ...input.observation(),
34
+ });
35
+ disposition = { outcome: "processed", sendId: send.id };
36
+ }
37
+ try {
38
+ await input.port.recordAttempt(disposition);
39
+ } catch {
40
+ // Best-effort telemetry is separate from durable convergence.
41
+ }
42
+ return disposition;
43
+ }
@@ -0,0 +1,167 @@
1
+ import { admitPolicyEnforcedEmailTransport } from "./emailEgress.js";
2
+ /** Provider-agnostic transport contract, outcome projection and capture transport. */
3
+ export interface EmailMessage {
4
+ from: string;
5
+ to: string;
6
+ subject: string;
7
+ html: string;
8
+ /** Optional plain-text alternative; omitted from the outbound payload when absent. */
9
+ text?: string;
10
+ /** Optional reply mailbox; omitted entirely when the caller does not set it. */
11
+ replyTo?: string;
12
+ /** Stable outbound request key retained by the concrete transport for a limited window. */
13
+ idempotencyKey?: string;
14
+ /** Base64 content only; remote URLs and filesystem paths are not supported. */
15
+ attachments?: ReadonlyArray<{ filename: string; content: string }>;
16
+ /** Cancels the in-flight send when the worker's per-row timeout fires. */
17
+ signal?: AbortSignal;
18
+ }
19
+
20
+ /** Transport results preserve existing reference wire shapes. */
21
+ export interface EmailSendOutcome {
22
+ ok: boolean;
23
+ /** Legacy identifier retained for reference result compatibility. */
24
+ resendId: string | null;
25
+ httpStatus: number;
26
+ providerError: string | null;
27
+ providerErrorCode?: string | null;
28
+ aborted: boolean;
29
+ suppressed?: "drop" | null;
30
+ egressMode?: "production" | "sandbox_direct" | "sandbox_sink" | "drop";
31
+ egressReason?: string;
32
+ }
33
+ export interface EmailSendResult {
34
+ outcome: EmailSendOutcome;
35
+ providerResponse: Record<string, unknown>;
36
+ }
37
+
38
+ export type EmailSendSkipReason = "admin_disabled" | "egress_suppressed";
39
+
40
+ export interface EmailSendOutcomeLike {
41
+ ok: boolean;
42
+ resendId: string | null;
43
+ providerError: string | null;
44
+ aborted?: boolean;
45
+ adminDisabled?: boolean;
46
+ suppressed?: "drop" | null;
47
+ skipReason?: EmailSendSkipReason | null;
48
+ }
49
+
50
+ /** Converts the transport's legacy identifier field at the transport boundary. */
51
+ export function emailTransportMessageId(outcome: EmailSendOutcomeLike): string | null {
52
+ return outcome.resendId;
53
+ }
54
+
55
+ /** Maps the neutral message identifier onto the deployed ledger's legacy field. */
56
+ export function emailTransportLedgerIdentifier(messageId: string | null): Record<string, string | null> {
57
+ return { resend_id: messageId };
58
+ }
59
+
60
+ /** Synthesizes the canonical terminal transport shape when `send` throws. */
61
+ export function failedEmailTransportResult(): EmailSendResult {
62
+ return {
63
+ outcome: { ok: false, resendId: null, httpStatus: 0, providerError: "provider_transport_threw", aborted: false },
64
+ providerResponse: { exception: true },
65
+ };
66
+ }
67
+
68
+ export interface EmailSendOutcomeProjection {
69
+ skipReason: EmailSendSkipReason | null;
70
+ timelineStatus: "sent" | "skipped" | "delivery_delayed" | "failed";
71
+ ledgerStatus: "sent" | "skipped" | "failed";
72
+ sentAtEligible: boolean;
73
+ lastErrorCode: string | null;
74
+ }
75
+
76
+ /** One truth projection for every Node email ledger, timeline and caller. */
77
+ export function classifyEmailSendOutcome(outcome: EmailSendOutcomeLike): EmailSendOutcomeProjection {
78
+ const skipReason = outcome.skipReason
79
+ ?? (outcome.adminDisabled ? "admin_disabled" : null)
80
+ ?? (outcome.suppressed === "drop" ? "egress_suppressed" : null);
81
+ if (skipReason) {
82
+ return {
83
+ skipReason,
84
+ timelineStatus: "skipped",
85
+ ledgerStatus: "skipped",
86
+ sentAtEligible: false,
87
+ lastErrorCode: skipReason,
88
+ };
89
+ }
90
+ // `ok` deliberately remains the transport-acceptance contract. In particular,
91
+ // this wave does not turn a historical 2xx-without-id answer into a retry.
92
+ if (outcome.ok) {
93
+ return {
94
+ skipReason: null,
95
+ timelineStatus: "sent",
96
+ ledgerStatus: "sent",
97
+ sentAtEligible: true,
98
+ lastErrorCode: null,
99
+ };
100
+ }
101
+ if (outcome.aborted) {
102
+ return {
103
+ skipReason: null,
104
+ timelineStatus: "delivery_delayed",
105
+ ledgerStatus: "failed",
106
+ sentAtEligible: false,
107
+ lastErrorCode: "provider_request_aborted",
108
+ };
109
+ }
110
+ return {
111
+ skipReason: null,
112
+ timelineStatus: "failed",
113
+ ledgerStatus: "failed",
114
+ sentAtEligible: false,
115
+ lastErrorCode: normalizeEmailFailureCode(outcome.providerError),
116
+ };
117
+ }
118
+
119
+ /** Adds a provider-neutral skip reason without changing ordinary wire shapes. */
120
+ export function withEmailSendSkipReason<T extends EmailSendOutcomeLike>(outcome: T): T {
121
+ const skipReason = classifyEmailSendOutcome(outcome).skipReason;
122
+ return skipReason ? { ...outcome, skipReason } : outcome;
123
+ }
124
+
125
+ function normalizeEmailFailureCode(value: string | null): string {
126
+ if (!value) return "provider_send_failed";
127
+ const normalized = value
128
+ .toLowerCase()
129
+ .replace(/[^a-z0-9]+/g, "_")
130
+ .replace(/^_+|_+$/g, "")
131
+ .slice(0, 80);
132
+ return normalized || "provider_send_failed";
133
+ }
134
+
135
+ export interface EmailTransport {
136
+ /** Stable provider tag for ledgers/observability (e.g. "mailProvider", "in-memory"). */
137
+ readonly providerKind: string;
138
+ send(message: EmailMessage): Promise<EmailSendResult>;
139
+ }
140
+
141
+ export interface InMemoryEmailTransport extends EmailTransport {
142
+ /** Every message handed to send(), in order — for tests and dry runs. */
143
+ readonly sent: ReadonlyArray<EmailMessage>;
144
+ }
145
+
146
+ /**
147
+ * Reference capture transport: captures sends in memory and never makes a
148
+ * network call. Proves the seam is real — a port wired to this sends email with
149
+ * no HTTP client — and doubles as the substitute an OSS fork starts from
150
+ * before writing its own SES/SMTP adapter.
151
+ */
152
+ export function createInMemoryEmailTransport(opts?: { idPrefix?: string }): InMemoryEmailTransport {
153
+ const sent: EmailMessage[] = [];
154
+ const prefix = opts?.idPrefix ?? "in-memory";
155
+ return admitPolicyEnforcedEmailTransport({
156
+ providerKind: "in-memory",
157
+ sent,
158
+ async send(message) {
159
+ sent.push(message);
160
+ const id = `${prefix}-${sent.length}`;
161
+ return {
162
+ outcome: { ok: true, resendId: id, httpStatus: 200, providerError: null, aborted: false },
163
+ providerResponse: { id },
164
+ };
165
+ },
166
+ });
167
+ }
@@ -0,0 +1,34 @@
1
+ export class CommunicationRecipientNotFoundError extends Error {
2
+ constructor(message = "Communication recipient not found") {
3
+ super(message);
4
+ this.name = "CommunicationRecipientNotFoundError";
5
+ }
6
+ }
7
+
8
+ export class CommunicationTemplateNotFoundError extends Error {
9
+ constructor(message = "Communication template not found") {
10
+ super(message);
11
+ this.name = "CommunicationTemplateNotFoundError";
12
+ }
13
+ }
14
+
15
+ export class CommunicationConflictError extends Error {
16
+ constructor(message = "Communication request conflicts with current state") {
17
+ super(message);
18
+ this.name = "CommunicationConflictError";
19
+ }
20
+ }
21
+
22
+ export class CommunicationValidationError extends Error {
23
+ constructor(message = "Communication request is invalid") {
24
+ super(message);
25
+ this.name = "CommunicationValidationError";
26
+ }
27
+ }
28
+
29
+ export class CommunicationUnavailableError extends Error {
30
+ constructor(message = "Communication capability is unavailable") {
31
+ super(message);
32
+ this.name = "CommunicationUnavailableError";
33
+ }
34
+ }
@@ -0,0 +1,46 @@
1
+ export * from "./accountingDocumentDelivery.js";
2
+ export * from "./authEmailError.js";
3
+ export * from "./authSendEmailHook.js";
4
+ export * from "./capturedTransactionalDelivery.js";
5
+ export * from "./communicationSync.js";
6
+ export * from "./communicationSyncWorker.js";
7
+ export * from "./consentPolicy.js";
8
+ export * from "./deliveryReconciliation.js";
9
+ export * from "./contribution.js";
10
+ export * from "./emailHealthAssessment.js";
11
+ export * from "./emailHealthMetricsPort.js";
12
+ export * from "./emailNotificationControl.js";
13
+ export * from "./emailObservation.js";
14
+ export * from "./emailTransport.js";
15
+ export * from "./emailEgress.js";
16
+ export * from "./errors.js";
17
+ export * from "./integrationEventSignature.js";
18
+ export * from "./managedTransactionalDeliveryStore.js";
19
+ export * from "./newsletterIntegrationContracts.js";
20
+ export * from "./newsletterWebhookPort.js";
21
+ export * from "./originPolicy.js";
22
+ export * from "./postgresTransactionalDeliveryStore.js";
23
+ export * from "./preferencesToken.js";
24
+ export * from "./publicPreferenceToken.js";
25
+ export * from "./shipmentNotifications.js";
26
+ export * from "./sql.js";
27
+ export * from "./standardWebhooks.js";
28
+ export * from "./status.js";
29
+ export * from "./transactionalDelivery.js";
30
+ export * from "./transactionalDeliveryIdentifier.js";
31
+ export * from "./unsubscribeToken.js";
32
+ export type * from "./contracts/authEmail.js";
33
+ export type * from "./contracts/email.js";
34
+ export type * from "./contracts/publicIntake.js";
35
+ export type * from "./contracts/transactionalDelivery.js";
36
+ export type * from "./contracts/newsletterWebhook.js";
37
+ export type { NewsletterSyncProviderPort, NewsletterSyncOutcome, NewsletterSyncReconcileOutcome } from "./contracts/newsletter.js";
38
+ export * from "./consentPurposeRegistry.js";
39
+ export type * from "./contracts/consent.js";
40
+ export type * from "./contracts/marketingAutomation.js";
41
+ export * from "./newsletterWebhookProcessing.js";
42
+ export * from "./nativeOperations.js";
43
+ export * from "./postgresDeliveryReconciliation.js";
44
+ export * from "./postgresCommunicationSync.js";
45
+ export * from "./postgresNotificationRecipients.js";
46
+ export * from "./postgresConsentEvaluator.js";
@@ -0,0 +1,49 @@
1
+ import { createHmac, timingSafeEqual } from "node:crypto";
2
+
3
+ export const COMMUNICATION_INTEGRATION_SIGNATURE_TOLERANCE_SECONDS = 300;
4
+ export const COMMUNICATION_INTEGRATION_SIGNATURE_HEADER = "x-openlup-communications-signature";
5
+ export const COMMUNICATION_INTEGRATION_TIMESTAMP_HEADER = "x-openlup-communications-timestamp";
6
+
7
+ export function verifyCommunicationIntegrationEventSignature(input: {
8
+ rawBody: string;
9
+ signatureHeader: string | undefined;
10
+ timestampHeader: string | undefined;
11
+ secret: string;
12
+ now?: () => number;
13
+ }): boolean {
14
+ if (!input.secret || !input.signatureHeader || !input.timestampHeader) return false;
15
+ const timestamp = Number(input.timestampHeader);
16
+ if (!Number.isFinite(timestamp)) return false;
17
+ const now = input.now ?? (() => Math.floor(Date.now() / 1000));
18
+ const age = now() - timestamp;
19
+ if (age > COMMUNICATION_INTEGRATION_SIGNATURE_TOLERANCE_SECONDS) return false;
20
+ if (age < -COMMUNICATION_INTEGRATION_SIGNATURE_TOLERANCE_SECONDS) return false;
21
+
22
+ const signature = normalizeSignature(input.signatureHeader);
23
+ const expected = signCommunicationIntegrationEvent({
24
+ rawBody: input.rawBody,
25
+ timestamp,
26
+ secret: input.secret,
27
+ });
28
+ return safeEqual(signature, expected);
29
+ }
30
+
31
+ export function signCommunicationIntegrationEvent(input: {
32
+ rawBody: string;
33
+ timestamp: number;
34
+ secret: string;
35
+ }): string {
36
+ return createHmac("sha256", input.secret)
37
+ .update(`${input.timestamp}.${input.rawBody}`)
38
+ .digest("hex");
39
+ }
40
+
41
+ function normalizeSignature(value: string): string {
42
+ return value.trim().replace(/^sha256=/i, "");
43
+ }
44
+
45
+ function safeEqual(left: string, right: string): boolean {
46
+ const leftBuffer = Buffer.from(left);
47
+ const rightBuffer = Buffer.from(right);
48
+ return leftBuffer.length === rightBuffer.length && timingSafeEqual(leftBuffer, rightBuffer);
49
+ }
@@ -0,0 +1,59 @@
1
+ import type { TransactionalDeliveryReceipt, TransactionalDeliveryReceiptStore } from "./contracts/transactionalDelivery.js";
2
+ export interface ManagedTransactionalDeliveryStoreClient {
3
+ rpc(
4
+ functionName: string,
5
+ args: Record<string, unknown>,
6
+ ): PromiseLike<{ data: unknown; error: { code?: string; message?: string } | null }>;
7
+ }
8
+
9
+ function failure(name: string, error: { code?: string; message?: string }): Error & { code?: string } {
10
+ const result = new Error(`${name}_failed: ${error.message ?? error.code ?? "unknown"}`) as Error & { code?: string };
11
+ result.code = error.code;
12
+ return result;
13
+ }
14
+
15
+ function receipt(data: unknown): TransactionalDeliveryReceipt {
16
+ const value = (Array.isArray(data) ? data[0] : data) as Record<string, unknown> | undefined;
17
+ if (!value || typeof value !== "object") throw new Error("transactional_delivery_receipt_invalid");
18
+ const common = {
19
+ idempotencyKey: String(value.idempotency_key ?? ""),
20
+ commandFingerprint: String(value.command_fingerprint ?? ""),
21
+ attemptCount: Number(value.attempt_count ?? 0),
22
+ };
23
+ if (value.state === "accepted" && typeof value.delivery_reference === "string" && value.error_code == null) {
24
+ return { ...common, state: "accepted", deliveryReference: value.delivery_reference, errorCode: null };
25
+ }
26
+ if (value.state === "failed" && value.delivery_reference == null && typeof value.error_code === "string") {
27
+ return { ...common, state: "failed", deliveryReference: null, errorCode: value.error_code };
28
+ }
29
+ throw new Error("transactional_delivery_receipt_invalid");
30
+ }
31
+
32
+ /** Managed adapter over the three receipt RPCs; access scope belongs to its binding. */
33
+ export function createManagedTransactionalDeliveryStore(
34
+ client: ManagedTransactionalDeliveryStoreClient,
35
+ ): TransactionalDeliveryReceiptStore {
36
+ const call = async (name: string, args: Record<string, unknown>): Promise<TransactionalDeliveryReceipt> => {
37
+ const { data, error } = await client.rpc(name, args);
38
+ if (error) throw failure(name, error);
39
+ return receipt(data);
40
+ };
41
+ return {
42
+ async readReceipt(idempotencyKey) {
43
+ const { data, error } = await client.rpc("transactional_delivery_read_receipt", { p_idempotency_key: idempotencyKey });
44
+ if (error) throw failure("transactional_delivery_read_receipt", error);
45
+ return Array.isArray(data) && data.length === 0 ? null : receipt(data);
46
+ },
47
+ recordAccepted: (input) => call("transactional_delivery_record_accepted", {
48
+ p_idempotency_key: input.idempotencyKey,
49
+ p_command_fingerprint: input.commandFingerprint,
50
+ p_delivery_reference: input.deliveryReference,
51
+ }),
52
+ recordFailed: (input) => call("transactional_delivery_record_failed", {
53
+ p_idempotency_key: input.idempotencyKey,
54
+ p_command_fingerprint: input.commandFingerprint,
55
+ p_error_code: input.errorCode,
56
+ }),
57
+ };
58
+ }
59
+
@@ -0,0 +1,40 @@
1
+ import type { PostgresDataGatewayOptions } from "../data/index.js";
2
+ import type { DeliveryReconciliationPort } from "./deliveryReconciliation.js";
3
+ import type { CommunicationRecipientKind } from "./consentPolicy.js";
4
+
5
+ export interface NativeCommunicationsOptions extends PostgresDataGatewayOptions { binding: string }
6
+ export type NativeObservationKind = "sent" | "delayed" | "delivered" | "bounced" | "complained" | "failed";
7
+ export interface NativeNetworkTarget {
8
+ sourceKey: string;
9
+ messageId: string;
10
+ recipientEmail: string;
11
+ templateReference: string;
12
+ acceptedAt: string;
13
+ /** Only a host that observed a real network acceptance may register this fact. */
14
+ evidence: "network_accepted";
15
+ }
16
+ export interface NativeDeliveryEvidence {
17
+ id: string; sourceKey: string; messageId: string; acceptedAt: string;
18
+ observedState: NativeObservationKind | null; observedAt: string | null;
19
+ lastPollAt: string | null; notFoundCount: number; abandonedAt: string | null;
20
+ observations: { key: string; kind: NativeObservationKind; occurredAt: string; source: "poll" | "callback" }[];
21
+ }
22
+ export interface NativeDeliveryStore extends DeliveryReconciliationPort {
23
+ registerNetworkTarget(input: NativeNetworkTarget): Promise<{ id: string }>;
24
+ applyObservation(input: { sendId: string; messageId: string; observationKey: string; kind: NativeObservationKind; occurredAt: string; recipientEmail: string | null; source: "poll" | "callback" }): Promise<void>;
25
+ findByMessageId(messageId: string): Promise<{ id: string } | null>;
26
+ listEvidence(input: { limit: number }): Promise<NativeDeliveryEvidence[]>;
27
+ readEvidence(sendId: string): Promise<NativeDeliveryEvidence | null>;
28
+ close(): Promise<void>;
29
+ }
30
+ export type NativeNotificationType = "packaging_digest" | "new_signup" | "commerce_payment_critical";
31
+ export interface NativeNotificationRecipient { id: string; email: string; name: string | null; active: boolean; notification_type: NativeNotificationType; created_at: string }
32
+ export interface NativeConsentInput {
33
+ email: string; purpose: string; recipientKind?: CommunicationRecipientKind;
34
+ sourceTable?: "testers" | "waitlist" | "clients" | "b2b_inquiries" | "notification_recipients";
35
+ sourceId?: string | null;
36
+ }
37
+ export interface NativeConsentDecision { allowed: boolean; reason: string; decisionId: string }
38
+ export class NativeConsentEvaluationUnavailableError extends Error {
39
+ constructor(cause: unknown) { super("native_consent_evaluation_unavailable", { cause }); this.name = "ConsentEvaluationUnavailableError"; }
40
+ }