@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,115 @@
1
+ import { createMagicLinkOriginResolver, normalizeMagicLinkReturnPath } from "./authVerifyUrl.js";
2
+ import { createIdentityResponder } from "./response.js";
3
+ import { resolveBundleId } from "../kernel/index.js";
4
+ const MAGIC_LINK_RESPONSE_FLOOR_MS = 400;
5
+ export function createCustomerMagicLinkHandler(options) {
6
+ const { sendBffError, sendBffSuccess, sendMethodNotAllowed } = createIdentityResponder(options);
7
+ const readRequestOrigin = options.resolveOrigin ?? createMagicLinkOriginResolver(options.redirectOrigins ?? []);
8
+ const { enabled: customerAuthUiEnabled, env: environment, readEnvironment: readCustomerSelfServiceEnv, createCustomerClient, createCustomerServiceClient, checkAttempt: checkAndRecordCustomerMagicLinkAttempt, extractClientIp, padResponseTime, requestSchema: customerMagicLinkRequestSchema, responseSchema: customerMagicLinkResponseSchema, resolveSession, callbackPath: customerAuthCallbackPath } = options;
9
+ return async (req, res) => {
10
+ if (!customerAuthUiEnabled()) {
11
+ sendBffError(res, "UPSTREAM_UNAVAILABLE", "Customer accounts are disabled", {
12
+ details: {
13
+ feature: "customer_auth",
14
+ ...options.disabledDetails,
15
+ reason: "feature_flag_disabled",
16
+ },
17
+ });
18
+ return;
19
+ }
20
+ if (req.method !== "POST") {
21
+ sendMethodNotAllowed(res, ["POST"]);
22
+ return;
23
+ }
24
+ const request = customerMagicLinkRequestSchema.safeParse(req.body);
25
+ if (!request.success) {
26
+ sendBffError(res, "BAD_REQUEST", "Invalid magic-link request", {
27
+ details: request.error.flatten(),
28
+ });
29
+ return;
30
+ }
31
+ const startedAt = Date.now();
32
+ const origin = readRequestOrigin(req);
33
+ if (!origin) {
34
+ await padResponseTime(startedAt, MAGIC_LINK_RESPONSE_FLOOR_MS);
35
+ sendBffError(res, "UPSTREAM_UNAVAILABLE", "Magic link origin is not configured", {
36
+ details: { reason: "magic_link_origin_not_configured" },
37
+ });
38
+ return;
39
+ }
40
+ if (resolveBundleId(environment) === "node-postgres") {
41
+ const resolved = resolveSession(environment);
42
+ if (!resolved.binding) {
43
+ await padResponseTime(startedAt, MAGIC_LINK_RESPONSE_FLOOR_MS);
44
+ sendBffError(res, "UPSTREAM_UNAVAILABLE", "Customer auth is not configured", {
45
+ details: { feature: "customer_auth", reason: resolved.error },
46
+ });
47
+ return;
48
+ }
49
+ await resolved.binding.requestChallenge(request.data.email);
50
+ await padResponseTime(startedAt, MAGIC_LINK_RESPONSE_FLOOR_MS);
51
+ sendAccepted(res);
52
+ return;
53
+ }
54
+ const env = readCustomerSelfServiceEnv();
55
+ if (!env) {
56
+ sendBffError(res, "UPSTREAM_UNAVAILABLE", "Customer auth is not configured", {
57
+ details: {
58
+ feature: "customer_auth",
59
+ requiredEnv: "SUPABASE_URL/SUPABASE_ANON_KEY/SUPABASE_SERVICE_ROLE_KEY",
60
+ },
61
+ });
62
+ return;
63
+ }
64
+ const anonymousAuthClient = createCustomerClient(env, null);
65
+ const rateLimitClient = createCustomerServiceClient(env);
66
+ const decision = await checkAndRecordCustomerMagicLinkAttempt({
67
+ client: rateLimitClient,
68
+ ip: extractClientIp(req.headers),
69
+ email: request.data.email,
70
+ });
71
+ if (!decision.allowed && decision.reason === "rpc_error") {
72
+ console.warn("customer_magic_link_rate_limit_unavailable", JSON.stringify({ reason: decision.reason }));
73
+ await padResponseTime(startedAt, MAGIC_LINK_RESPONSE_FLOOR_MS);
74
+ sendBffError(res, "UPSTREAM_UNAVAILABLE", "Magic link service temporarily unavailable");
75
+ return;
76
+ }
77
+ if (decision.allowed) {
78
+ const outcome = await sendMagicLink(anonymousAuthClient, request.data.email, origin, request.data.locale, normalizeMagicLinkReturnPath(request.data.returnTo, origin));
79
+ console.info("customer_magic_link_outcome", JSON.stringify({ outcome: outcome.status, code: outcome.code ?? null, locale: request.data.locale }));
80
+ }
81
+ else {
82
+ console.warn("customer_magic_link_outcome", JSON.stringify({ outcome: "rate_limited", reason: decision.reason ?? "unknown" }));
83
+ }
84
+ await padResponseTime(startedAt, MAGIC_LINK_RESPONSE_FLOOR_MS);
85
+ sendAccepted(res);
86
+ };
87
+ async function sendMagicLink(client, email, origin, locale, returnTo) {
88
+ try {
89
+ const callbackUrl = new URL(customerAuthCallbackPath(locale), origin);
90
+ if (returnTo)
91
+ callbackUrl.searchParams.set("returnTo", returnTo);
92
+ const { error } = await client.auth.signInWithOtp({
93
+ email,
94
+ options: {
95
+ // First login confirms the already provisioned principal through mailbox proof.
96
+ shouldCreateUser: false,
97
+ emailRedirectTo: callbackUrl.toString(),
98
+ },
99
+ });
100
+ if (error) {
101
+ console.warn("customer_magic_link_send_failed", JSON.stringify({ code: error.name }));
102
+ return { status: "auth_error", code: error.name };
103
+ }
104
+ return { status: "sent" };
105
+ }
106
+ catch (error) {
107
+ const code = error instanceof Error ? error.name : "unknown";
108
+ console.warn("customer_magic_link_send_threw", JSON.stringify({ error: code }));
109
+ return { status: "threw", code };
110
+ }
111
+ }
112
+ function sendAccepted(res) {
113
+ sendBffSuccess(res, customerMagicLinkResponseSchema.parse({ accepted: true }));
114
+ }
115
+ }
@@ -0,0 +1,11 @@
1
+ import { type IdentityResponseOptions } from "./response.js";
2
+ import type { HttpRequest, HttpResponse } from "../http/index.js";
3
+ import { customerMeResponseSchema } from "./customerContracts.js";
4
+ import type { CustomerMePort } from "./customerPorts.js";
5
+ import type { CustomerUserAuthenticationResult } from "./customerAuth.js";
6
+ export interface CustomerMeDeps extends IdentityResponseOptions {
7
+ responseSchema: typeof customerMeResponseSchema;
8
+ mePort: CustomerMePort;
9
+ authenticateUser: (req: HttpRequest) => Promise<CustomerUserAuthenticationResult>;
10
+ }
11
+ export declare function createCustomerMeHandler({ mePort, authenticateUser, responseSchema, responseRequestId }: CustomerMeDeps): (req: HttpRequest, res: HttpResponse) => Promise<void>;
@@ -0,0 +1,46 @@
1
+ import { createIdentityResponder } from "./response.js";
2
+ import { customerMeRequestSchema, } from "./customerContracts.js";
3
+ export function createCustomerMeHandler({ mePort, authenticateUser, responseSchema, responseRequestId }) {
4
+ const { sendBffError, sendBffSuccess, sendMethodNotAllowed } = createIdentityResponder({ responseRequestId });
5
+ return async function handler(req, res) {
6
+ if (req.method !== "GET") {
7
+ sendMethodNotAllowed(res, ["GET"]);
8
+ return;
9
+ }
10
+ let authentication;
11
+ try {
12
+ authentication = await authenticateUser(req);
13
+ }
14
+ catch {
15
+ sendBffError(res, "UPSTREAM_UNAVAILABLE", "Customer authentication failed");
16
+ return;
17
+ }
18
+ if (authentication.ok === false) {
19
+ sendBffError(res, authentication.code, authentication.message);
20
+ return;
21
+ }
22
+ const request = customerMeRequestSchema.safeParse(req.query ?? {});
23
+ if (!request.success) {
24
+ sendBffError(res, "BAD_REQUEST", "Invalid customer me request", {
25
+ details: request.error.flatten(),
26
+ });
27
+ return;
28
+ }
29
+ try {
30
+ const result = await mePort.getCustomerMe(authentication.userId);
31
+ if (!result) {
32
+ sendBffError(res, "FORBIDDEN", "No customer account linked to this session");
33
+ return;
34
+ }
35
+ const response = responseSchema.safeParse(result);
36
+ if (!response.success) {
37
+ sendBffError(res, "INVALID_RESPONSE", "Customer me returned invalid response");
38
+ return;
39
+ }
40
+ sendBffSuccess(res, response.data);
41
+ }
42
+ catch {
43
+ sendBffError(res, "UPSTREAM_UNAVAILABLE", "Customer me read failed");
44
+ }
45
+ };
46
+ }
@@ -0,0 +1,4 @@
1
+ import type { CustomerMeResponse } from "./customerContracts.js";
2
+ export interface CustomerMePort {
3
+ getCustomerMe(principalId: string): Promise<CustomerMeResponse | null>;
4
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,38 @@
1
+ import type { IdentityJwtLibraryLoader } from "./jwtLibrary.js";
2
+ import type { CustomerChallengeDeliveryPort, CustomerChallengeStore, CustomerSessionIssuerPort } from "./ports.js";
3
+ type Env = Record<string, string | undefined>;
4
+ export type CapturedCustomerChallenge = {
5
+ readonly email: string;
6
+ readonly token: string;
7
+ readonly expiresAt: string;
8
+ };
9
+ /** Deterministic no-egress reference delivery; deployments inject mail. */
10
+ export declare function createCapturedCustomerChallengeDelivery(sink?: (challenge: CapturedCustomerChallenge) => Promise<void>): CustomerChallengeDeliveryPort;
11
+ export interface CustomerSessionBinding {
12
+ requestChallenge(email: string): Promise<void>;
13
+ verifyChallenge(token: string): Promise<{
14
+ accessToken: string;
15
+ expiresAt: string;
16
+ user: {
17
+ id: string;
18
+ email: string;
19
+ };
20
+ } | null>;
21
+ }
22
+ export declare function resolveCustomerSessionBinding(env: Env, options?: {
23
+ createStore?: (connectionString: string) => CustomerChallengeStore & {
24
+ close(): Promise<void>;
25
+ };
26
+ delivery?: CustomerChallengeDeliveryPort;
27
+ issuer?: CustomerSessionIssuerPort;
28
+ loadJwt?: IdentityJwtLibraryLoader;
29
+ generateToken?: () => string;
30
+ now?: () => Date;
31
+ }): {
32
+ binding: CustomerSessionBinding;
33
+ error?: undefined;
34
+ } | {
35
+ binding?: undefined;
36
+ error: string;
37
+ };
38
+ export {};
@@ -0,0 +1,80 @@
1
+ import { createHash, randomBytes } from "node:crypto";
2
+ import { createCustomerSessionIssuer } from "./customerSessionIssuer.js";
3
+ import { createPostgresCustomerChallengeStore } from "./postgresCustomerRecovery.js";
4
+ import { resolveBundleId } from "../kernel/index.js";
5
+ /** Deterministic no-egress reference delivery; deployments inject mail. */
6
+ export function createCapturedCustomerChallengeDelivery(sink = async () => { }) {
7
+ return { deliver: sink };
8
+ }
9
+ export function resolveCustomerSessionBinding(env, options = {}) {
10
+ if (resolveBundleId(env) !== "node-postgres")
11
+ return { error: "customer_session_external_bundle" };
12
+ const connectionString = env.DATABASE_URL?.trim() ?? "";
13
+ if (!connectionString)
14
+ return { error: "database_url_required" };
15
+ let issuer;
16
+ try {
17
+ if (!options.issuer && !options.loadJwt)
18
+ throw new Error("identity_jwt_library_required");
19
+ issuer = options.issuer ?? createCustomerSessionIssuer({
20
+ audience: env.CUSTOMER_IDENTITY_AUDIENCE ?? "",
21
+ issuer: env.CUSTOMER_IDENTITY_ISSUER ?? "",
22
+ keyId: env.CUSTOMER_IDENTITY_KEY_ID ?? "",
23
+ privateJwk: env.CUSTOMER_IDENTITY_PRIVATE_JWK ?? "",
24
+ ttlSeconds: readTtl(env.CUSTOMER_IDENTITY_SESSION_TTL_SECONDS, 3_600),
25
+ }, options.loadJwt);
26
+ }
27
+ catch {
28
+ return { error: "customer_session_issuer_invalid" };
29
+ }
30
+ const createStore = options.createStore ?? ((url) => createPostgresCustomerChallengeStore({ connectionString: url }));
31
+ const delivery = options.delivery ?? createCapturedCustomerChallengeDelivery();
32
+ const generateToken = options.generateToken ?? (() => randomBytes(32).toString("base64url"));
33
+ const now = options.now ?? (() => new Date());
34
+ const challengeTtlSeconds = readTtl(env.CUSTOMER_IDENTITY_CHALLENGE_TTL_SECONDS, 600);
35
+ return { binding: {
36
+ async requestChallenge(email) {
37
+ const normalizedEmail = email.trim().toLowerCase();
38
+ const token = generateToken();
39
+ const requestedAt = now();
40
+ const expiresAt = new Date(requestedAt.getTime() + challengeTtlSeconds * 1000).toISOString();
41
+ const store = createStore(connectionString);
42
+ try {
43
+ const result = await store.issue({ email: normalizedEmail, tokenHash: hash(token), expiresAt });
44
+ if (result.deliverable)
45
+ await delivery.deliver({ email: normalizedEmail, token, expiresAt });
46
+ }
47
+ finally {
48
+ await store.close();
49
+ }
50
+ },
51
+ async verifyChallenge(token) {
52
+ const store = createStore(connectionString);
53
+ try {
54
+ const redeemed = await store.redeem({ tokenHash: hash(token), now: now().toISOString() });
55
+ if (!redeemed)
56
+ return null;
57
+ const session = await issuer.issue(redeemed);
58
+ return {
59
+ ...session,
60
+ user: { id: redeemed.principalId, email: redeemed.email },
61
+ };
62
+ }
63
+ finally {
64
+ await store.close();
65
+ }
66
+ },
67
+ } };
68
+ }
69
+ function hash(value) {
70
+ return createHash("sha256").update(value, "utf8").digest("hex");
71
+ }
72
+ function readTtl(value, fallback) {
73
+ if (!value)
74
+ return fallback;
75
+ const parsed = Number(value);
76
+ if (!Number.isInteger(parsed) || parsed < 300 || parsed > 86_400) {
77
+ throw new Error("customer identity ttl invalid");
78
+ }
79
+ return parsed;
80
+ }
@@ -0,0 +1,10 @@
1
+ import type { IdentityJwtLibraryLoader } from "./jwtLibrary.js";
2
+ import type { CustomerSessionIssuerPort } from "./ports.js";
3
+ export interface CustomerSessionIssuerConfig {
4
+ audience: string;
5
+ issuer: string;
6
+ keyId: string;
7
+ privateJwk: string;
8
+ ttlSeconds: number;
9
+ }
10
+ export declare function createCustomerSessionIssuer(config: CustomerSessionIssuerConfig, loadJwt: IdentityJwtLibraryLoader): CustomerSessionIssuerPort;
@@ -0,0 +1,57 @@
1
+ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
2
+ const KID = /^[A-Za-z0-9._-]{1,64}$/;
3
+ export function createCustomerSessionIssuer(config, loadJwt) {
4
+ const audience = claim(config.audience, "audience");
5
+ const issuer = claim(config.issuer, "issuer");
6
+ if (!KID.test(config.keyId))
7
+ throw new Error("customer session key id configuration is invalid");
8
+ if (!Number.isInteger(config.ttlSeconds) || config.ttlSeconds < 300 || config.ttlSeconds > 86_400) {
9
+ throw new Error("customer session ttl configuration is invalid");
10
+ }
11
+ const privateKey = readPrivateKey(config.privateJwk);
12
+ const library = loadJwt();
13
+ const key = library.then(({ importJWK }) => importJWK(privateKey, "ES256"));
14
+ return {
15
+ async issue({ principalId, email }) {
16
+ if (!UUID.test(principalId))
17
+ throw new Error("customer session principal is invalid");
18
+ const normalizedEmail = email.trim().toLowerCase();
19
+ if (!normalizedEmail || normalizedEmail.length > 320) {
20
+ throw new Error("customer session email is invalid");
21
+ }
22
+ const issuedAt = Math.floor(Date.now() / 1000);
23
+ const expiresAt = issuedAt + config.ttlSeconds;
24
+ const { SignJWT } = await library;
25
+ const accessToken = await new SignJWT({ email: normalizedEmail, email_verified: true })
26
+ .setProtectedHeader({ alg: "ES256", kid: config.keyId, typ: "JWT" })
27
+ .setSubject(principalId)
28
+ .setIssuer(issuer)
29
+ .setAudience(audience)
30
+ .setIssuedAt(issuedAt)
31
+ .setExpirationTime(expiresAt)
32
+ .sign(await key);
33
+ return { accessToken, expiresAt: new Date(expiresAt * 1000).toISOString() };
34
+ },
35
+ };
36
+ }
37
+ function readPrivateKey(value) {
38
+ if (!value || value.length > 16_384)
39
+ throw new Error("customer session private JWK configuration is invalid");
40
+ let parsed;
41
+ try {
42
+ parsed = JSON.parse(value);
43
+ }
44
+ catch {
45
+ throw new Error("customer session private JWK configuration is invalid");
46
+ }
47
+ if (parsed.kty !== "EC" || parsed.crv !== "P-256" || parsed.alg !== "ES256" || typeof parsed.d !== "string") {
48
+ throw new Error("customer session private JWK configuration is invalid");
49
+ }
50
+ return parsed;
51
+ }
52
+ function claim(value, label) {
53
+ if (!value || value.length > 256 || value.trim() !== value) {
54
+ throw new Error(`customer session ${label} configuration is invalid`);
55
+ }
56
+ return value;
57
+ }
@@ -0,0 +1,8 @@
1
+ import { type IdentityResponseOptions } from "./response.js";
2
+ import type { HttpRequest, HttpResponse } from "../http/index.js";
3
+ import { resolveCustomerSessionBinding } from "./customerSessionBinding.js";
4
+ export declare function createCustomerVerifyOtpHandler(options: IdentityResponseOptions & {
5
+ enabled: () => boolean;
6
+ env: Record<string, string | undefined>;
7
+ resolveSession: typeof resolveCustomerSessionBinding;
8
+ }): (req: HttpRequest, res: HttpResponse) => Promise<void>;
@@ -0,0 +1,41 @@
1
+ import { createIdentityResponder } from "./response.js";
2
+ import { z } from "zod";
3
+ z.config({ jitless: true });
4
+ const customerOtpVerifyRequestSchema = z.object({
5
+ email: z.string().trim().toLowerCase().email().max(160),
6
+ token: z.string().trim().min(16).max(256),
7
+ }).strict();
8
+ const customerOtpVerifyResponseSchema = z.object({
9
+ accessToken: z.string().min(1),
10
+ expiresAt: z.string().datetime({ offset: true }),
11
+ user: z.object({ id: z.guid(), email: z.string().email() }).strict(),
12
+ }).strict();
13
+ export function createCustomerVerifyOtpHandler(options) {
14
+ const { sendBffError, sendBffSuccess, sendMethodNotAllowed } = createIdentityResponder(options);
15
+ return async (req, res) => {
16
+ if (!options.enabled()) {
17
+ sendBffError(res, "UPSTREAM_UNAVAILABLE", "Customer accounts are disabled");
18
+ return;
19
+ }
20
+ if (req.method !== "POST")
21
+ return sendMethodNotAllowed(res, ["POST"]);
22
+ const request = customerOtpVerifyRequestSchema.safeParse(req.body);
23
+ if (!request.success) {
24
+ sendBffError(res, "BAD_REQUEST", "Invalid one-time code request", { details: request.error.flatten() });
25
+ return;
26
+ }
27
+ const resolved = options.resolveSession(options.env);
28
+ if (!resolved.binding) {
29
+ sendBffError(res, "UPSTREAM_UNAVAILABLE", "Customer auth is not configured", {
30
+ details: { feature: "customer_auth", reason: resolved.error },
31
+ });
32
+ return;
33
+ }
34
+ const session = await resolved.binding.verifyChallenge(request.data.token);
35
+ if (!session) {
36
+ sendBffError(res, "UNAUTHORIZED", "One-time code is invalid or expired");
37
+ return;
38
+ }
39
+ sendBffSuccess(res, customerOtpVerifyResponseSchema.parse(session));
40
+ };
41
+ }
@@ -0,0 +1,19 @@
1
+ import { type PgQueryExecutor, type PostgresDataGatewayEnv, type PostgresDataGatewayOptions } from "../data/index.js";
2
+ import type { OperatorInvitationStore } from "./operatorInvitationPorts.js";
3
+ /** Owner-authority lane, never the actor gateway. One transaction per command. */
4
+ export declare function createDirectOperatorInvitationStore(env: PostgresDataGatewayEnv, options?: PostgresDataGatewayOptions): OperatorInvitationStore & {
5
+ close(): Promise<void>;
6
+ };
7
+ /** Principal creation commits independently; an invitation failure leaves it reusable. */
8
+ export declare function createDirectOperatorIdentityDirectory(env: PostgresDataGatewayEnv, options?: PostgresDataGatewayOptions): {
9
+ ensurePrincipal(input: {
10
+ email: string;
11
+ redirectTo: string;
12
+ }): Promise<{
13
+ principalId: string;
14
+ created: boolean;
15
+ }>;
16
+ close: () => Promise<void>;
17
+ };
18
+ /** Trusted authentication only; caller owns the transaction, including challenge consumption. */
19
+ export declare function acceptDirectOperatorInvitationInTransaction(db: PgQueryExecutor, id: string): Promise<import("./contracts/operatorInvitations.js").OperatorInvitationAcceptance>;
@@ -0,0 +1,193 @@
1
+ import { createPostgresRoleFreeTransactionLane } from "../data/index.js";
2
+ import { assertOperatorInvitationTtl, invitationFailure } from "./operatorInvitationLifecycle.js";
3
+ const iso = (value) => value instanceof Date ? value.toISOString() : new Date(String(value)).toISOString();
4
+ const role = (value) => value === "distributor" ? "distributor" : "admin";
5
+ const snapshot = (row) => ({ principalId: String(row.principal_id), role: role(row.role) });
6
+ function result(row, code) {
7
+ return { ...snapshot(row), code, membershipState: "invited", invitationId: String(row.invitation_id), invitedAt: iso(row.invited_at), expiresAt: iso(row.expires_at) };
8
+ }
9
+ async function event(db, action, before, after, actor) {
10
+ await db.query(`INSERT INTO public.identity_operator_invitation_events
11
+ (event_id, invitation_id, principal_id, action, actor_kind, actor_principal_id, old_value, new_value, occurred_at)
12
+ VALUES (gen_random_uuid(), $1, $2, $3, $4, $5, $6::jsonb, $7::jsonb, clock_timestamp())`, [after.invitation_id, after.principal_id, action, actor ? "human" : "system", actor, JSON.stringify(before ?? {}), JSON.stringify(after)]);
13
+ }
14
+ async function lock(db, id) {
15
+ const principal = (await db.query("SELECT principal_id, email FROM public.platform_human_principals WHERE principal_id = $1", [id])).rows[0];
16
+ if (!principal)
17
+ return null;
18
+ await db.query("SELECT pg_advisory_xact_lock(hashtext($1)::bigint)", [`operator_invitation:${String(principal.email).trim().toLowerCase()}`]);
19
+ return principal;
20
+ }
21
+ async function membership(db, id) {
22
+ return (await db.query("SELECT * FROM public.platform_human_memberships WHERE principal_id = $1 FOR UPDATE", [id])).rows[0] ?? null;
23
+ }
24
+ async function machine(db, id) {
25
+ return (await db.query("SELECT EXISTS (SELECT 1 FROM public.platform_control_operators WHERE principal_id = $1) AS present", [id])).rows[0]?.present === true;
26
+ }
27
+ async function lockCommandMemberships(db, actor, target) {
28
+ // Lock both rows in the same order before taking either actor or target locks.
29
+ await db.query("SELECT principal_id FROM public.platform_human_memberships WHERE principal_id = ANY($1::uuid[]) ORDER BY principal_id FOR UPDATE", [[actor, target]]);
30
+ }
31
+ async function administrator(db, id) {
32
+ const row = (await db.query("SELECT role, membership_state FROM public.platform_human_memberships WHERE principal_id = $1 FOR SHARE", [id])).rows[0];
33
+ if (row?.role !== "admin" || row.membership_state !== "active" || await machine(db, id))
34
+ throw invitationFailure("HUMAN_ADMIN_REQUIRED", "FORBIDDEN");
35
+ }
36
+ async function invitation(db, id) {
37
+ const row = (await db.query("SELECT * FROM public.identity_operator_invitations WHERE principal_id = $1 FOR UPDATE", [id])).rows[0];
38
+ if (!row)
39
+ return null;
40
+ // Capture the database clock after serialization; equality is already expired.
41
+ const expiredNow = (await db.query("SELECT clock_timestamp() >= $1::timestamptz AS expired", [row.expires_at])).rows[0].expired;
42
+ if (row.phase === "invited" && expiredNow === true) {
43
+ const expired = (await db.query("UPDATE public.identity_operator_invitations SET phase = 'expired', closed_at = expires_at WHERE principal_id = $1 RETURNING *", [id])).rows[0];
44
+ await event(db, "expire", row, expired, null);
45
+ return expired;
46
+ }
47
+ return row;
48
+ }
49
+ /** Owner-authority lane, never the actor gateway. One transaction per command. */
50
+ export function createDirectOperatorInvitationStore(env, options = {}) {
51
+ const lane = createPostgresRoleFreeTransactionLane(env, options);
52
+ const run = (work) => lane.run((db) => work(db));
53
+ return {
54
+ issue(input) {
55
+ assertOperatorInvitationTtl(input.ttlSeconds);
56
+ return run(async (db) => {
57
+ // Serialize same-target commands, then lock both memberships in order.
58
+ const principal = await lock(db, input.targetPrincipalId);
59
+ await lockCommandMemberships(db, input.actorPrincipalId, input.targetPrincipalId);
60
+ await administrator(db, input.actorPrincipalId);
61
+ if ((await db.query("SELECT 1 FROM public.identity_privacy_erasures WHERE principal_id = $1", [input.targetPrincipalId])).rows.length)
62
+ return { code: "IDENTITY_CONFLICT" };
63
+ const email = input.email.trim().toLowerCase();
64
+ if (!principal || String(principal.email).trim().toLowerCase() !== email || await machine(db, input.targetPrincipalId))
65
+ return { code: "IDENTITY_CONFLICT" };
66
+ const member = await membership(db, input.targetPrincipalId);
67
+ if (member?.membership_state === "active")
68
+ return { code: "MEMBERSHIP_ALREADY_ACTIVE" };
69
+ const previous = await invitation(db, input.targetPrincipalId);
70
+ if (previous?.phase === "invited")
71
+ return previous.role === input.role ? result(previous, "ALREADY_INVITED") : { code: "INVITATION_ROLE_CONFLICT" };
72
+ const collision = (await db.query("SELECT principal_id FROM public.identity_operator_invitations WHERE email_key = $1", [email])).rows[0];
73
+ if (collision && collision.principal_id !== input.targetPrincipalId)
74
+ return { code: "IDENTITY_CONFLICT" };
75
+ const row = (await db.query(`WITH stamp AS (SELECT clock_timestamp() AS at)
76
+ INSERT INTO public.identity_operator_invitations
77
+ (principal_id, invitation_id, email_key, role, phase, invited_by, invited_at, expires_at)
78
+ SELECT $1, gen_random_uuid(), $2, $3, 'invited', $4, stamp.at, stamp.at + $5 * interval '1 second' FROM stamp WHERE true
79
+ ON CONFLICT (principal_id) DO UPDATE SET invitation_id = EXCLUDED.invitation_id, email_key = EXCLUDED.email_key,
80
+ role = EXCLUDED.role, phase = 'invited', invited_by = EXCLUDED.invited_by, invited_at = EXCLUDED.invited_at,
81
+ expires_at = EXCLUDED.expires_at, accepted_at = NULL, closed_at = NULL RETURNING *`, [input.targetPrincipalId, email, input.role, input.actorPrincipalId, input.ttlSeconds])).rows[0];
82
+ await event(db, previous || member ? "reissue" : "issue", previous, row, input.actorPrincipalId);
83
+ return result(row, previous || member ? "INVITATION_REISSUED" : "INVITATION_CREATED");
84
+ });
85
+ },
86
+ read(id) {
87
+ return run(async (db) => {
88
+ if (!await lock(db, id))
89
+ return null;
90
+ const member = await membership(db, id);
91
+ if (member?.membership_state === "active")
92
+ return { ...snapshot(member), membershipState: "active" };
93
+ const pending = await invitation(db, id);
94
+ if (pending && pending.phase !== "accepted")
95
+ return { ...snapshot(pending), membershipState: pending.phase === "invited" ? "invited" : pending.phase === "expired" ? "expired" : "revoked" };
96
+ return member ? { ...snapshot(member), membershipState: "revoked" } : null;
97
+ });
98
+ },
99
+ acceptOperatorInvitation(id) {
100
+ return run((db) => acceptDirectOperatorInvitationInTransaction(db, id));
101
+ },
102
+ remove(input) {
103
+ return run(async (db) => {
104
+ const principal = await lock(db, input.targetPrincipalId);
105
+ await lockCommandMemberships(db, input.actorPrincipalId, input.targetPrincipalId);
106
+ await administrator(db, input.actorPrincipalId);
107
+ if (input.actorPrincipalId === input.targetPrincipalId)
108
+ throw invitationFailure("SELF_REVOKE_FORBIDDEN", "FORBIDDEN");
109
+ if (!principal || await machine(db, input.targetPrincipalId))
110
+ throw invitationFailure("IDENTITY_CONFLICT");
111
+ const member = await membership(db, input.targetPrincipalId);
112
+ const pending = await invitation(db, input.targetPrincipalId);
113
+ if (pending?.phase === "invited") {
114
+ const cancelled = (await db.query("UPDATE public.identity_operator_invitations SET phase = 'cancelled', closed_at = clock_timestamp() WHERE principal_id = $1 RETURNING *", [input.targetPrincipalId])).rows[0];
115
+ await event(db, "cancel", pending, cancelled, input.actorPrincipalId);
116
+ }
117
+ if (member?.membership_state === "active")
118
+ await db.query("SELECT public.platform_revoke_human_membership($1, $2, $3)", [input.actorPrincipalId, input.targetPrincipalId, input.reason ?? null]);
119
+ if (!member && !pending)
120
+ throw invitationFailure("ADMIN_NOT_FOUND", "BAD_REQUEST");
121
+ return { revoked: true };
122
+ });
123
+ },
124
+ close: lane.close,
125
+ };
126
+ }
127
+ /** Principal creation commits independently; an invitation failure leaves it reusable. */
128
+ export function createDirectOperatorIdentityDirectory(env, options = {}) {
129
+ const lane = createPostgresRoleFreeTransactionLane(env, options);
130
+ return {
131
+ ensurePrincipal(input) {
132
+ const email = input.email.trim().toLowerCase();
133
+ return lane.run(async (executor) => {
134
+ const db = executor;
135
+ await db.query("SELECT pg_advisory_xact_lock(hashtext($1)::bigint)", [`operator_invitation:${email}`]);
136
+ const matches = (await db.query("SELECT principal_id FROM public.platform_human_principals WHERE lower(btrim(email)) = $1", [email])).rows;
137
+ if (matches.length > 1)
138
+ throw invitationFailure("IDENTITY_CONFLICT");
139
+ if (matches[0]) {
140
+ const principalId = String(matches[0].principal_id);
141
+ if (await machine(db, principalId))
142
+ throw invitationFailure("IDENTITY_CONFLICT");
143
+ return { principalId, created: false };
144
+ }
145
+ const row = (await db.query("INSERT INTO public.platform_human_principals (principal_id, email) VALUES (gen_random_uuid(), $1) RETURNING principal_id", [email])).rows[0];
146
+ return { principalId: String(row.principal_id), created: true };
147
+ });
148
+ },
149
+ close: lane.close,
150
+ };
151
+ }
152
+ /** Trusted authentication only; caller owns the transaction, including challenge consumption. */
153
+ export async function acceptDirectOperatorInvitationInTransaction(db, id) {
154
+ if (!await lock(db, id) || await machine(db, id))
155
+ return { code: "NO_INVITATION" };
156
+ if ((await db.query("SELECT 1 FROM public.identity_privacy_erasures WHERE principal_id = $1", [id])).rows.length)
157
+ return { code: "INVITATION_REVOKED" };
158
+ const member = await membership(db, id);
159
+ if (member?.membership_state === "active") {
160
+ if (member.role !== "admin" && member.role !== "distributor")
161
+ return { code: "NO_INVITATION" };
162
+ return { code: "ALREADY_ACTIVE", role: member.role };
163
+ }
164
+ const pending = await invitation(db, id);
165
+ if (!pending)
166
+ return { code: "NO_INVITATION" };
167
+ if (pending.phase === "expired")
168
+ return { code: "INVITATION_EXPIRED" };
169
+ if (pending.phase !== "invited" || (pending.role !== "admin" && pending.role !== "distributor"))
170
+ return { code: "INVITATION_REVOKED" };
171
+ const clock = (await db.query("WITH stamp AS (SELECT clock_timestamp() AS at) SELECT at, at >= $1::timestamptz AS expired FROM stamp", [pending.expires_at])).rows[0];
172
+ const at = clock.at;
173
+ if (clock.expired === true) {
174
+ const expired = (await db.query("UPDATE public.identity_operator_invitations SET phase = 'expired', closed_at = expires_at WHERE principal_id = $1 RETURNING *", [id])).rows[0];
175
+ await event(db, "expire", pending, expired, null);
176
+ return { code: "INVITATION_EXPIRED" };
177
+ }
178
+ if (member) {
179
+ const setting = (await db.query("SELECT current_setting('app.platform_human_membership_revoke', true) AS previous")).rows[0]?.previous;
180
+ await db.query("SELECT set_config('app.platform_human_membership_revoke', 'true', true)");
181
+ await db.query(`UPDATE public.platform_human_memberships SET membership_state = 'active', membership_provenance = 'invitation',
182
+ membership_accepted_at = $2, membership_revoked_at = NULL, membership_revoked_by = NULL, membership_revocation_reason = NULL WHERE principal_id = $1`, [id, at]);
183
+ await db.query("UPDATE public.platform_human_memberships SET role = $2 WHERE principal_id = $1", [id, pending.role]);
184
+ await db.query("SELECT set_config('app.platform_human_membership_revoke', $1, true)", [setting ?? ""]);
185
+ }
186
+ else {
187
+ await db.query(`INSERT INTO public.platform_human_memberships (principal_id, role, membership_state, membership_provenance, membership_accepted_at)
188
+ VALUES ($1, $2, 'active', 'invitation', $3)`, [id, pending.role, at]);
189
+ }
190
+ const accepted = (await db.query("UPDATE public.identity_operator_invitations SET phase = 'accepted', accepted_at = $2, closed_at = $2 WHERE principal_id = $1 RETURNING *", [id, at])).rows[0];
191
+ await event(db, "accept", pending, accepted, id);
192
+ return { code: "ACTIVATED", role: role(pending.role) };
193
+ }
@@ -0,0 +1,14 @@
1
+ import type { EmailTrustFailureCode } from "./contracts/emailTrust.js";
2
+ export type EmailTrustAssessment = {
3
+ trusted: true;
4
+ } | {
5
+ trusted: false;
6
+ code: EmailTrustFailureCode;
7
+ };
8
+ export interface EmailTrustPolicy {
9
+ assess(): Promise<EmailTrustAssessment>;
10
+ }
11
+ /** Explicitly retain verifier-only admission. This does not bypass mailbox verification. */
12
+ export declare function createVerifierResultOnlyEmailTrustPolicy(): EmailTrustPolicy;
13
+ /** Invalid, absent or throwing JavaScript bindings fail closed, including invalid results. */
14
+ export declare function assessEmailTrust(policy: EmailTrustPolicy): Promise<EmailTrustAssessment>;