@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.
- package/CHANGELOG.md +124 -0
- package/README.md +633 -6
- package/dist/communications/accountingDocumentDelivery.d.ts +35 -0
- package/dist/communications/accountingDocumentDelivery.js +33 -0
- package/dist/communications/authEmailError.d.ts +5 -0
- package/dist/communications/authEmailError.js +8 -0
- package/dist/communications/authEmailPorts.d.ts +2 -0
- package/dist/communications/authEmailPorts.js +1 -0
- package/dist/communications/authSendEmailHook.d.ts +35 -0
- package/dist/communications/authSendEmailHook.js +250 -0
- package/dist/communications/capturedTransactionalDelivery.d.ts +13 -0
- package/dist/communications/capturedTransactionalDelivery.js +14 -0
- package/dist/communications/communicationSync.d.ts +11 -0
- package/dist/communications/communicationSync.js +79 -0
- package/dist/communications/communicationSyncWorker.d.ts +68 -0
- package/dist/communications/communicationSyncWorker.js +218 -0
- package/dist/communications/consentPolicy.d.ts +19 -0
- package/dist/communications/consentPolicy.js +43 -0
- package/dist/communications/consentPurposeRegistry.d.ts +4 -0
- package/dist/communications/consentPurposeRegistry.js +24 -0
- package/dist/communications/contracts/abandonedCart.d.ts +27 -0
- package/dist/communications/contracts/abandonedCart.js +25 -0
- package/dist/communications/contracts/authEmail.d.ts +62 -0
- package/dist/communications/contracts/authEmail.js +1 -0
- package/dist/communications/contracts/backInStock.d.ts +28 -0
- package/dist/communications/contracts/backInStock.js +28 -0
- package/dist/communications/contracts/blocks.d.ts +24 -0
- package/dist/communications/contracts/blocks.js +23 -0
- package/dist/communications/contracts/checkoutExpired.d.ts +22 -0
- package/dist/communications/contracts/checkoutExpired.js +25 -0
- package/dist/communications/contracts/checkoutRecovery.d.ts +26 -0
- package/dist/communications/contracts/checkoutRecovery.js +37 -0
- package/dist/communications/contracts/commerceEmailContent.d.ts +176 -0
- package/dist/communications/contracts/commerceEmailContent.js +1 -0
- package/dist/communications/contracts/commerceEngagementEmailContent.d.ts +95 -0
- package/dist/communications/contracts/commerceEngagementEmailContent.js +1 -0
- package/dist/communications/contracts/consent.d.ts +12 -0
- package/dist/communications/contracts/consent.js +1 -0
- package/dist/communications/contracts/email.d.ts +182 -0
- package/dist/communications/contracts/email.js +1 -0
- package/dist/communications/contracts/guideCard.d.ts +3 -0
- package/dist/communications/contracts/guideCard.js +26 -0
- package/dist/communications/contracts/index.d.ts +51 -0
- package/dist/communications/contracts/index.js +42 -0
- package/dist/communications/contracts/inquiryContent.d.ts +32 -0
- package/dist/communications/contracts/inquiryContent.js +33 -0
- package/dist/communications/contracts/invoiceDocumentContent.d.ts +26 -0
- package/dist/communications/contracts/invoiceDocumentContent.js +36 -0
- package/dist/communications/contracts/marketingAutomation.d.ts +52 -0
- package/dist/communications/contracts/marketingAutomation.js +1 -0
- package/dist/communications/contracts/newsletter.d.ts +63 -0
- package/dist/communications/contracts/newsletter.js +1 -0
- package/dist/communications/contracts/newsletterWebhook.d.ts +26 -0
- package/dist/communications/contracts/newsletterWebhook.js +1 -0
- package/dist/communications/contracts/optOutContent.d.ts +11 -0
- package/dist/communications/contracts/optOutContent.js +17 -0
- package/dist/communications/contracts/orderCanceled.d.ts +25 -0
- package/dist/communications/contracts/orderCanceled.js +27 -0
- package/dist/communications/contracts/orderDraft.d.ts +46 -0
- package/dist/communications/contracts/orderDraft.js +46 -0
- package/dist/communications/contracts/orderPaid.d.ts +53 -0
- package/dist/communications/contracts/orderPaid.js +71 -0
- package/dist/communications/contracts/orderRefunded.d.ts +24 -0
- package/dist/communications/contracts/orderRefunded.js +27 -0
- package/dist/communications/contracts/paymentFailed.d.ts +24 -0
- package/dist/communications/contracts/paymentFailed.js +24 -0
- package/dist/communications/contracts/publicIntake.d.ts +49 -0
- package/dist/communications/contracts/publicIntake.js +1 -0
- package/dist/communications/contracts/render.d.ts +2 -0
- package/dist/communications/contracts/render.js +229 -0
- package/dist/communications/contracts/reorderReminder.d.ts +24 -0
- package/dist/communications/contracts/reorderReminder.js +24 -0
- package/dist/communications/contracts/returnEmails.d.ts +25 -0
- package/dist/communications/contracts/returnEmails.js +62 -0
- package/dist/communications/contracts/reviewEffects.d.ts +26 -0
- package/dist/communications/contracts/reviewEffects.js +25 -0
- package/dist/communications/contracts/reviewRequest.d.ts +26 -0
- package/dist/communications/contracts/reviewRequest.js +24 -0
- package/dist/communications/contracts/roleNotificationContent.d.ts +25 -0
- package/dist/communications/contracts/roleNotificationContent.js +46 -0
- package/dist/communications/contracts/shipmentDelivered.d.ts +23 -0
- package/dist/communications/contracts/shipmentDelivered.js +24 -0
- package/dist/communications/contracts/shipmentDispatched.d.ts +27 -0
- package/dist/communications/contracts/shipmentDispatched.js +35 -0
- package/dist/communications/contracts/shipmentException.d.ts +21 -0
- package/dist/communications/contracts/shipmentException.js +26 -0
- package/dist/communications/contracts/subscriptionActivationActionRequired.d.ts +17 -0
- package/dist/communications/contracts/subscriptionActivationActionRequired.js +22 -0
- package/dist/communications/contracts/subscriptionAddressChanged.d.ts +19 -0
- package/dist/communications/contracts/subscriptionAddressChanged.js +25 -0
- package/dist/communications/contracts/subscriptionCancelled.d.ts +19 -0
- package/dist/communications/contracts/subscriptionCancelled.js +25 -0
- package/dist/communications/contracts/subscriptionCycleSkipped.d.ts +25 -0
- package/dist/communications/contracts/subscriptionCycleSkipped.js +40 -0
- package/dist/communications/contracts/subscriptionDeliveryRescheduled.d.ts +25 -0
- package/dist/communications/contracts/subscriptionDeliveryRescheduled.js +39 -0
- package/dist/communications/contracts/subscriptionEmailContent.d.ts +145 -0
- package/dist/communications/contracts/subscriptionEmailContent.js +1 -0
- package/dist/communications/contracts/subscriptionPackageChanged.d.ts +20 -0
- package/dist/communications/contracts/subscriptionPackageChanged.js +26 -0
- package/dist/communications/contracts/subscriptionPauseReminder.d.ts +23 -0
- package/dist/communications/contracts/subscriptionPauseReminder.js +29 -0
- package/dist/communications/contracts/subscriptionPaused.d.ts +21 -0
- package/dist/communications/contracts/subscriptionPaused.js +25 -0
- package/dist/communications/contracts/subscriptionPaymentExpired.d.ts +34 -0
- package/dist/communications/contracts/subscriptionPaymentExpired.js +37 -0
- package/dist/communications/contracts/subscriptionPaymentFailed.d.ts +49 -0
- package/dist/communications/contracts/subscriptionPaymentFailed.js +56 -0
- package/dist/communications/contracts/subscriptionPaymentRecovered.d.ts +19 -0
- package/dist/communications/contracts/subscriptionPaymentRecovered.js +22 -0
- package/dist/communications/contracts/subscriptionRenewalAtRisk.d.ts +36 -0
- package/dist/communications/contracts/subscriptionRenewalAtRisk.js +36 -0
- package/dist/communications/contracts/subscriptionRenewalUpcoming.d.ts +49 -0
- package/dist/communications/contracts/subscriptionRenewalUpcoming.js +43 -0
- package/dist/communications/contracts/subscriptionResumed.d.ts +23 -0
- package/dist/communications/contracts/subscriptionResumed.js +25 -0
- package/dist/communications/contracts/subscriptionWelcome.d.ts +44 -0
- package/dist/communications/contracts/subscriptionWelcome.js +45 -0
- package/dist/communications/contracts/subscriptionWinback.d.ts +21 -0
- package/dist/communications/contracts/subscriptionWinback.js +28 -0
- package/dist/communications/contracts/transactionalDelivery.d.ts +42 -0
- package/dist/communications/contracts/transactionalDelivery.js +1 -0
- package/dist/communications/contracts/types.d.ts +46 -0
- package/dist/communications/contracts/types.js +1 -0
- package/dist/communications/contribution.d.ts +10 -0
- package/dist/communications/contribution.js +24 -0
- package/dist/communications/deliveryReconciliation.d.ts +64 -0
- package/dist/communications/deliveryReconciliation.js +60 -0
- package/dist/communications/emailEgress.d.ts +38 -0
- package/dist/communications/emailEgress.js +100 -0
- package/dist/communications/emailHealthAssessment.d.ts +39 -0
- package/dist/communications/emailHealthAssessment.js +59 -0
- package/dist/communications/emailHealthMetricsPort.d.ts +17 -0
- package/dist/communications/emailHealthMetricsPort.js +1 -0
- package/dist/communications/emailNotificationControl.d.ts +27 -0
- package/dist/communications/emailNotificationControl.js +10 -0
- package/dist/communications/emailObservation.d.ts +28 -0
- package/dist/communications/emailObservation.js +23 -0
- package/dist/communications/emailTransport.d.ts +82 -0
- package/dist/communications/emailTransport.js +95 -0
- package/dist/communications/errors.d.ts +15 -0
- package/dist/communications/errors.js +30 -0
- package/dist/communications/index.d.ts +46 -0
- package/dist/communications/index.js +38 -0
- package/dist/communications/integrationEventSignature.d.ts +15 -0
- package/dist/communications/integrationEventSignature.js +37 -0
- package/dist/communications/managedTransactionalDeliveryStore.d.ts +12 -0
- package/dist/communications/managedTransactionalDeliveryStore.js +49 -0
- package/dist/communications/nativeOperations.d.ts +79 -0
- package/dist/communications/nativeOperations.js +3 -0
- package/dist/communications/newsletterIntegrationContracts.d.ts +130 -0
- package/dist/communications/newsletterIntegrationContracts.js +128 -0
- package/dist/communications/newsletterWebhookPort.d.ts +11 -0
- package/dist/communications/newsletterWebhookPort.js +84 -0
- package/dist/communications/newsletterWebhookProcessing.d.ts +17 -0
- package/dist/communications/newsletterWebhookProcessing.js +71 -0
- package/dist/communications/originPolicy.d.ts +40 -0
- package/dist/communications/originPolicy.js +100 -0
- package/dist/communications/postgresCommunicationSync.d.ts +79 -0
- package/dist/communications/postgresCommunicationSync.js +111 -0
- package/dist/communications/postgresConsentEvaluator.d.ts +19 -0
- package/dist/communications/postgresConsentEvaluator.js +43 -0
- package/dist/communications/postgresDeliveryReconciliation.d.ts +3 -0
- package/dist/communications/postgresDeliveryReconciliation.js +113 -0
- package/dist/communications/postgresNativeOperations.d.ts +13 -0
- package/dist/communications/postgresNativeOperations.js +63 -0
- package/dist/communications/postgresNotificationRecipients.d.ts +37 -0
- package/dist/communications/postgresNotificationRecipients.js +60 -0
- package/dist/communications/postgresTransactionalDeliveryStore.d.ts +7 -0
- package/dist/communications/postgresTransactionalDeliveryStore.js +55 -0
- package/dist/communications/preferencesToken.d.ts +10 -0
- package/dist/communications/preferencesToken.js +40 -0
- package/dist/communications/publicPreferenceToken.d.ts +24 -0
- package/dist/communications/publicPreferenceToken.js +53 -0
- package/dist/communications/shipmentNotifications.d.ts +34 -0
- package/dist/communications/shipmentNotifications.js +61 -0
- package/dist/communications/sql.d.ts +26 -0
- package/dist/communications/sql.js +7 -0
- package/dist/communications/standardWebhooks.d.ts +14 -0
- package/dist/communications/standardWebhooks.js +73 -0
- package/dist/communications/status.d.ts +11 -0
- package/dist/communications/status.js +36 -0
- package/dist/communications/transactionalDelivery.d.ts +19 -0
- package/dist/communications/transactionalDelivery.js +96 -0
- package/dist/communications/transactionalDeliveryIdentifier.d.ts +3 -0
- package/dist/communications/transactionalDeliveryIdentifier.js +6 -0
- package/dist/communications/unsubscribeToken.d.ts +77 -0
- package/dist/communications/unsubscribeToken.js +150 -0
- package/dist/communications/validation.d.ts +2 -0
- package/dist/communications/validation.js +3 -0
- package/dist/http/index.d.ts +1 -0
- package/dist/http/index.js +1 -0
- package/dist/http/webhookSignature.d.ts +42 -0
- package/dist/http/webhookSignature.js +86 -0
- package/dist/identity/adminAuthBinding.d.ts +28 -0
- package/dist/identity/adminAuthBinding.js +66 -0
- package/dist/identity/adminAuthorization.d.ts +34 -0
- package/dist/identity/adminAuthorization.js +72 -0
- package/dist/identity/adminContracts.d.ts +70 -0
- package/dist/identity/adminContracts.js +43 -0
- package/dist/identity/adminMagicLinkHandler.d.ts +39 -0
- package/dist/identity/adminMagicLinkHandler.js +94 -0
- package/dist/identity/adminMeHandler.d.ts +15 -0
- package/dist/identity/adminMeHandler.js +49 -0
- package/dist/identity/adminPreflight.d.ts +16 -0
- package/dist/identity/adminPreflight.js +38 -0
- package/dist/identity/adminUserActionPorts.d.ts +31 -0
- package/dist/identity/adminUserActionPorts.js +51 -0
- package/dist/identity/adminUserHandlers.d.ts +38 -0
- package/dist/identity/adminUserHandlers.js +140 -0
- package/dist/identity/authVerifyUrl.d.ts +6 -0
- package/dist/identity/authVerifyUrl.js +54 -0
- package/dist/identity/contracts/contracts.d.ts +8 -0
- package/dist/identity/contracts/contracts.js +2 -0
- package/dist/identity/contracts/emailTrust.d.ts +3 -0
- package/dist/identity/contracts/emailTrust.js +4 -0
- package/dist/identity/contracts/index.d.ts +8 -0
- package/dist/identity/contracts/index.js +8 -0
- package/dist/identity/contracts/oidc.d.ts +3 -0
- package/dist/identity/contracts/oidc.js +9 -0
- package/dist/identity/contracts/operatorAuth.d.ts +35 -0
- package/dist/identity/contracts/operatorAuth.js +53 -0
- package/dist/identity/contracts/operatorInvitations.d.ts +37 -0
- package/dist/identity/contracts/operatorInvitations.js +1 -0
- package/dist/identity/contracts/ports.d.ts +22 -0
- package/dist/identity/contracts/ports.js +1 -0
- package/dist/identity/contracts/privacy.d.ts +62 -0
- package/dist/identity/contracts/privacy.js +99 -0
- package/dist/identity/contracts/providerList.d.ts +3 -0
- package/dist/identity/contracts/providerList.js +11 -0
- package/dist/identity/contracts/types.d.ts +16 -0
- package/dist/identity/contracts/types.js +4 -0
- package/dist/identity/contribution.d.ts +8 -0
- package/dist/identity/contribution.js +22 -0
- package/dist/identity/customerAuth.d.ts +8 -0
- package/dist/identity/customerAuth.js +1 -0
- package/dist/identity/customerAuthorization.d.ts +5 -0
- package/dist/identity/customerAuthorization.js +22 -0
- package/dist/identity/customerContracts.d.ts +42 -0
- package/dist/identity/customerContracts.js +43 -0
- package/dist/identity/customerIdentityBinding.d.ts +44 -0
- package/dist/identity/customerIdentityBinding.js +60 -0
- package/dist/identity/customerIdentityVerifier.d.ts +8 -0
- package/dist/identity/customerIdentityVerifier.js +101 -0
- package/dist/identity/customerMagicLinkHandler.d.ts +38 -0
- package/dist/identity/customerMagicLinkHandler.js +115 -0
- package/dist/identity/customerMeHandler.d.ts +11 -0
- package/dist/identity/customerMeHandler.js +46 -0
- package/dist/identity/customerPorts.d.ts +4 -0
- package/dist/identity/customerPorts.js +1 -0
- package/dist/identity/customerSessionBinding.d.ts +38 -0
- package/dist/identity/customerSessionBinding.js +80 -0
- package/dist/identity/customerSessionIssuer.d.ts +10 -0
- package/dist/identity/customerSessionIssuer.js +57 -0
- package/dist/identity/customerVerifyOtpHandler.d.ts +8 -0
- package/dist/identity/customerVerifyOtpHandler.js +41 -0
- package/dist/identity/directOperatorInvitationStore.d.ts +19 -0
- package/dist/identity/directOperatorInvitationStore.js +193 -0
- package/dist/identity/emailTrustPolicy.d.ts +14 -0
- package/dist/identity/emailTrustPolicy.js +16 -0
- package/dist/identity/identityPersonalDataContributor.d.ts +7 -0
- package/dist/identity/identityPersonalDataContributor.js +46 -0
- package/dist/identity/identityReadiness.d.ts +10 -0
- package/dist/identity/identityReadiness.js +25 -0
- package/dist/identity/index.d.ts +67 -0
- package/dist/identity/index.js +66 -0
- package/dist/identity/inviteAdminUserUseCase.d.ts +89 -0
- package/dist/identity/inviteAdminUserUseCase.js +75 -0
- package/dist/identity/jwtLibrary.d.ts +54 -0
- package/dist/identity/jwtLibrary.js +1 -0
- package/dist/identity/machineActorWriteFence.d.ts +4 -0
- package/dist/identity/machineActorWriteFence.js +13 -0
- package/dist/identity/magicLinkPorts.d.ts +28 -0
- package/dist/identity/magicLinkPorts.js +1 -0
- package/dist/identity/managedAccountLinkStore.d.ts +2 -0
- package/dist/identity/managedAccountLinkStore.js +93 -0
- package/dist/identity/managedAdminAuthVerifier.d.ts +7 -0
- package/dist/identity/managedAdminAuthVerifier.js +27 -0
- package/dist/identity/managedAdminUserGateways.d.ts +123 -0
- package/dist/identity/managedAdminUserGateways.js +127 -0
- package/dist/identity/managedClient.d.ts +90 -0
- package/dist/identity/managedClient.js +10 -0
- package/dist/identity/managedClientAuthLink.d.ts +3 -0
- package/dist/identity/managedClientAuthLink.js +115 -0
- package/dist/identity/managedCustomerMe.d.ts +13 -0
- package/dist/identity/managedCustomerMe.js +30 -0
- package/dist/identity/managedEmailTrustPolicy.d.ts +11 -0
- package/dist/identity/managedEmailTrustPolicy.js +105 -0
- package/dist/identity/managedIdentityVerifier.d.ts +2 -0
- package/dist/identity/managedIdentityVerifier.js +16 -0
- package/dist/identity/managedPrivacy.d.ts +4 -0
- package/dist/identity/managedPrivacy.js +39 -0
- package/dist/identity/messages.d.ts +29 -0
- package/dist/identity/messages.js +39 -0
- package/dist/identity/oidcIdentityVerifier.d.ts +45 -0
- package/dist/identity/oidcIdentityVerifier.js +309 -0
- package/dist/identity/oidcPrincipal.d.ts +2 -0
- package/dist/identity/oidcPrincipal.js +10 -0
- package/dist/identity/operatorAdminAuth.d.ts +6 -0
- package/dist/identity/operatorAdminAuth.js +13 -0
- package/dist/identity/operatorAuthHandlers.d.ts +18 -0
- package/dist/identity/operatorAuthHandlers.js +71 -0
- package/dist/identity/operatorIdentityVerifier.d.ts +8 -0
- package/dist/identity/operatorIdentityVerifier.js +19 -0
- package/dist/identity/operatorInvitationLifecycle.d.ts +7 -0
- package/dist/identity/operatorInvitationLifecycle.js +15 -0
- package/dist/identity/operatorInvitationPorts.d.ts +31 -0
- package/dist/identity/operatorInvitationPorts.js +1 -0
- package/dist/identity/operatorPorts.d.ts +37 -0
- package/dist/identity/operatorPorts.js +1 -0
- package/dist/identity/operatorSession.d.ts +13 -0
- package/dist/identity/operatorSession.js +31 -0
- package/dist/identity/operatorTokenAdminAuth.d.ts +15 -0
- package/dist/identity/operatorTokenAdminAuth.js +50 -0
- package/dist/identity/personalDataRegistry.d.ts +4 -0
- package/dist/identity/personalDataRegistry.js +11 -0
- package/dist/identity/platformAdminAuth.d.ts +15 -0
- package/dist/identity/platformAdminAuth.js +1 -0
- package/dist/identity/ports.d.ts +127 -0
- package/dist/identity/ports.js +38 -0
- package/dist/identity/postgresAccountLinkStore.d.ts +7 -0
- package/dist/identity/postgresAccountLinkStore.js +52 -0
- package/dist/identity/postgresCustomerMe.d.ts +3 -0
- package/dist/identity/postgresCustomerMe.js +21 -0
- package/dist/identity/postgresCustomerRecovery.d.ts +11 -0
- package/dist/identity/postgresCustomerRecovery.js +57 -0
- package/dist/identity/postgresOperatorIdentity.d.ts +5 -0
- package/dist/identity/postgresOperatorIdentity.js +137 -0
- package/dist/identity/postgresPrivacy.d.ts +7 -0
- package/dist/identity/postgresPrivacy.js +126 -0
- package/dist/identity/privacyHandlers.d.ts +10 -0
- package/dist/identity/privacyHandlers.js +35 -0
- package/dist/identity/privacyPorts.d.ts +72 -0
- package/dist/identity/privacyPorts.js +8 -0
- package/dist/identity/privacyUseCases.d.ts +5 -0
- package/dist/identity/privacyUseCases.js +55 -0
- package/dist/identity/reconcileAccountForPrincipal.d.ts +20 -0
- package/dist/identity/reconcileAccountForPrincipal.js +66 -0
- package/dist/identity/reconcileAccountHandler.d.ts +11 -0
- package/dist/identity/reconcileAccountHandler.js +30 -0
- package/dist/identity/removeAdminUserUseCase.d.ts +31 -0
- package/dist/identity/removeAdminUserUseCase.js +60 -0
- package/dist/identity/response.d.ts +3 -0
- package/dist/identity/response.js +2 -0
- package/dist/identity/sql.d.ts +44 -0
- package/dist/identity/sql.js +10 -0
- package/package.json +22 -2
- package/release-gates.json +63 -8
- package/sql/20260807190000_comms_outbox_rail.sql +329 -0
- package/sql/20260810120001_transactional_delivery_receipts.sql +209 -0
- package/sql/20260810180001_self_host_actor_auth.sql +40 -0
- package/sql/20260810204057_customer_identity_read.sql +31 -0
- package/sql/20260813120000_communications_control_plane.sql +568 -0
- package/sql/20260814090000_customer_selfservice_recovery.sql +477 -0
- package/sql/20260901143000_admin_membership_authority_foundation.sql +234 -0
- package/sql/20261018120000_identity_operator_invitations.sql +42 -0
- package/sql/20261018180000_identity_operator_challenges.sql +56 -0
- package/sql/20261018190000_identity_privacy_erasures.sql +15 -0
- package/sql/20261019200000_communications_native_operations.sql +68 -0
- package/src/communications/accountingDocumentDelivery.ts +63 -0
- package/src/communications/authEmailError.ts +10 -0
- package/src/communications/authEmailPorts.ts +2 -0
- package/src/communications/authSendEmailHook.ts +310 -0
- package/src/communications/capturedTransactionalDelivery.ts +29 -0
- package/src/communications/communicationSync.ts +96 -0
- package/src/communications/communicationSyncWorker.ts +302 -0
- package/src/communications/consentPolicy.ts +82 -0
- package/src/communications/consentPurposeRegistry.ts +31 -0
- package/src/communications/contracts/abandonedCart.ts +64 -0
- package/src/communications/contracts/authEmail.ts +73 -0
- package/src/communications/contracts/backInStock.ts +69 -0
- package/src/communications/contracts/blocks.ts +47 -0
- package/src/communications/contracts/checkoutExpired.ts +62 -0
- package/src/communications/contracts/checkoutRecovery.ts +68 -0
- package/src/communications/contracts/commerceEmailContent.ts +189 -0
- package/src/communications/contracts/commerceEngagementEmailContent.ts +106 -0
- package/src/communications/contracts/consent.ts +13 -0
- package/src/communications/contracts/email.ts +210 -0
- package/src/communications/contracts/guideCard.ts +34 -0
- package/src/communications/contracts/index.ts +51 -0
- package/src/communications/contracts/inquiryContent.ts +53 -0
- package/src/communications/contracts/invoiceDocumentContent.ts +57 -0
- package/src/communications/contracts/marketingAutomation.ts +45 -0
- package/src/communications/contracts/newsletter.ts +78 -0
- package/src/communications/contracts/newsletterWebhook.ts +23 -0
- package/src/communications/contracts/optOutContent.ts +23 -0
- package/src/communications/contracts/orderCanceled.ts +64 -0
- package/src/communications/contracts/orderDraft.ts +112 -0
- package/src/communications/contracts/orderPaid.ts +147 -0
- package/src/communications/contracts/orderRefunded.ts +63 -0
- package/src/communications/contracts/paymentFailed.ts +59 -0
- package/src/communications/contracts/publicIntake.ts +53 -0
- package/src/communications/contracts/render.ts +271 -0
- package/src/communications/contracts/reorderReminder.ts +59 -0
- package/src/communications/contracts/returnEmails.ts +113 -0
- package/src/communications/contracts/reviewEffects.ts +60 -0
- package/src/communications/contracts/reviewRequest.ts +59 -0
- package/src/communications/contracts/roleNotificationContent.ts +65 -0
- package/src/communications/contracts/shipmentDelivered.ts +59 -0
- package/src/communications/contracts/shipmentDispatched.ts +77 -0
- package/src/communications/contracts/shipmentException.ts +62 -0
- package/src/communications/contracts/subscriptionActivationActionRequired.ts +37 -0
- package/src/communications/contracts/subscriptionAddressChanged.ts +56 -0
- package/src/communications/contracts/subscriptionCancelled.ts +57 -0
- package/src/communications/contracts/subscriptionCycleSkipped.ts +80 -0
- package/src/communications/contracts/subscriptionDeliveryRescheduled.ts +79 -0
- package/src/communications/contracts/subscriptionEmailContent.ts +163 -0
- package/src/communications/contracts/subscriptionPackageChanged.ts +59 -0
- package/src/communications/contracts/subscriptionPauseReminder.ts +63 -0
- package/src/communications/contracts/subscriptionPaused.ts +58 -0
- package/src/communications/contracts/subscriptionPaymentExpired.ts +83 -0
- package/src/communications/contracts/subscriptionPaymentFailed.ts +117 -0
- package/src/communications/contracts/subscriptionPaymentRecovered.ts +50 -0
- package/src/communications/contracts/subscriptionRenewalAtRisk.ts +85 -0
- package/src/communications/contracts/subscriptionRenewalUpcoming.ts +103 -0
- package/src/communications/contracts/subscriptionResumed.ts +60 -0
- package/src/communications/contracts/subscriptionWelcome.ts +102 -0
- package/src/communications/contracts/subscriptionWinback.ts +62 -0
- package/src/communications/contracts/transactionalDelivery.ts +43 -0
- package/src/communications/contracts/types.ts +58 -0
- package/src/communications/contribution.ts +33 -0
- package/src/communications/deliveryReconciliation.ts +108 -0
- package/src/communications/emailEgress.ts +111 -0
- package/src/communications/emailHealthAssessment.ts +106 -0
- package/src/communications/emailHealthMetricsPort.ts +17 -0
- package/src/communications/emailNotificationControl.ts +28 -0
- package/src/communications/emailObservation.ts +43 -0
- package/src/communications/emailTransport.ts +167 -0
- package/src/communications/errors.ts +34 -0
- package/src/communications/index.ts +46 -0
- package/src/communications/integrationEventSignature.ts +49 -0
- package/src/communications/managedTransactionalDeliveryStore.ts +59 -0
- package/src/communications/nativeOperations.ts +40 -0
- package/src/communications/newsletterIntegrationContracts.ts +154 -0
- package/src/communications/newsletterWebhookPort.ts +104 -0
- package/src/communications/newsletterWebhookProcessing.ts +102 -0
- package/src/communications/originPolicy.ts +151 -0
- package/src/communications/postgresCommunicationSync.ts +127 -0
- package/src/communications/postgresConsentEvaluator.ts +49 -0
- package/src/communications/postgresDeliveryReconciliation.ts +101 -0
- package/src/communications/postgresNativeOperations.ts +55 -0
- package/src/communications/postgresNotificationRecipients.ts +57 -0
- package/src/communications/postgresTransactionalDeliveryStore.ts +79 -0
- package/src/communications/preferencesToken.ts +62 -0
- package/src/communications/publicPreferenceToken.ts +68 -0
- package/src/communications/shipmentNotifications.ts +78 -0
- package/src/communications/sql.ts +7 -0
- package/src/communications/standardWebhooks.ts +84 -0
- package/src/communications/status.ts +51 -0
- package/src/communications/transactionalDelivery.ts +114 -0
- package/src/communications/transactionalDeliveryIdentifier.ts +9 -0
- package/src/communications/unsubscribeToken.ts +186 -0
- package/src/communications/validation.ts +3 -0
- package/src/http/index.ts +1 -0
- package/src/http/webhookSignature.ts +96 -0
- package/src/identity/adminAuthBinding.ts +76 -0
- package/src/identity/adminAuthorization.ts +106 -0
- package/src/identity/adminContracts.ts +52 -0
- package/src/identity/adminMagicLinkHandler.ts +132 -0
- package/src/identity/adminMeHandler.ts +64 -0
- package/src/identity/adminPreflight.ts +46 -0
- package/src/identity/adminUserActionPorts.ts +78 -0
- package/src/identity/adminUserHandlers.ts +174 -0
- package/src/identity/authVerifyUrl.ts +56 -0
- package/src/identity/contracts/contracts.ts +15 -0
- package/src/identity/contracts/emailTrust.ts +5 -0
- package/src/identity/contracts/index.ts +8 -0
- package/src/identity/contracts/oidc.ts +10 -0
- package/src/identity/contracts/operatorAuth.ts +61 -0
- package/src/identity/contracts/operatorInvitations.ts +32 -0
- package/src/identity/contracts/ports.ts +25 -0
- package/src/identity/contracts/privacy.ts +86 -0
- package/src/identity/contracts/providerList.ts +13 -0
- package/src/identity/contracts/types.ts +25 -0
- package/src/identity/contribution.ts +29 -0
- package/src/identity/customerAuth.ts +3 -0
- package/src/identity/customerAuthorization.ts +38 -0
- package/src/identity/customerContracts.ts +52 -0
- package/src/identity/customerIdentityBinding.ts +125 -0
- package/src/identity/customerIdentityVerifier.ts +141 -0
- package/src/identity/customerMagicLinkHandler.ts +171 -0
- package/src/identity/customerMeHandler.ts +64 -0
- package/src/identity/customerPorts.ts +2 -0
- package/src/identity/customerSessionBinding.ts +114 -0
- package/src/identity/customerSessionIssuer.ts +71 -0
- package/src/identity/customerVerifyOtpHandler.ts +44 -0
- package/src/identity/directOperatorInvitationStore.ts +178 -0
- package/src/identity/emailTrustPolicy.ts +16 -0
- package/src/identity/identityPersonalDataContributor.ts +48 -0
- package/src/identity/identityReadiness.ts +26 -0
- package/src/identity/index.ts +69 -0
- package/src/identity/inviteAdminUserUseCase.ts +134 -0
- package/src/identity/jwtLibrary.ts +24 -0
- package/src/identity/machineActorWriteFence.ts +19 -0
- package/src/identity/magicLinkPorts.ts +7 -0
- package/src/identity/managedAccountLinkStore.ts +105 -0
- package/src/identity/managedAdminAuthVerifier.ts +40 -0
- package/src/identity/managedAdminUserGateways.ts +164 -0
- package/src/identity/managedClient.ts +58 -0
- package/src/identity/managedClientAuthLink.ts +120 -0
- package/src/identity/managedCustomerMe.ts +52 -0
- package/src/identity/managedEmailTrustPolicy.ts +90 -0
- package/src/identity/managedIdentityVerifier.ts +20 -0
- package/src/identity/managedPrivacy.ts +37 -0
- package/src/identity/messages.ts +41 -0
- package/src/identity/oidcIdentityVerifier.ts +229 -0
- package/src/identity/oidcPrincipal.ts +10 -0
- package/src/identity/operatorAdminAuth.ts +14 -0
- package/src/identity/operatorAuthHandlers.ts +52 -0
- package/src/identity/operatorIdentityVerifier.ts +19 -0
- package/src/identity/operatorInvitationLifecycle.ts +14 -0
- package/src/identity/operatorInvitationPorts.ts +11 -0
- package/src/identity/operatorPorts.ts +11 -0
- package/src/identity/operatorSession.ts +30 -0
- package/src/identity/operatorTokenAdminAuth.ts +79 -0
- package/src/identity/personalDataRegistry.ts +8 -0
- package/src/identity/platformAdminAuth.ts +7 -0
- package/src/identity/ports.ts +158 -0
- package/src/identity/postgresAccountLinkStore.ts +49 -0
- package/src/identity/postgresCustomerMe.ts +33 -0
- package/src/identity/postgresCustomerRecovery.ts +72 -0
- package/src/identity/postgresOperatorIdentity.ts +123 -0
- package/src/identity/postgresPrivacy.ts +117 -0
- package/src/identity/privacyHandlers.ts +33 -0
- package/src/identity/privacyPorts.ts +52 -0
- package/src/identity/privacyUseCases.ts +48 -0
- package/src/identity/reconcileAccountForPrincipal.ts +99 -0
- package/src/identity/reconcileAccountHandler.ts +36 -0
- package/src/identity/removeAdminUserUseCase.ts +85 -0
- package/src/identity/response.ts +3 -0
- package/src/identity/sql.ts +10 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { OperatorInvitationAcceptance, OperatorInvitationConflict, OperatorInvitationResult, OperatorMembershipSnapshot, OperatorRole } from "./contracts/operatorInvitations.js";
|
|
2
|
+
export interface OperatorIdentityDirectoryPort {
|
|
3
|
+
ensurePrincipal(input: {
|
|
4
|
+
email: string;
|
|
5
|
+
redirectTo: string;
|
|
6
|
+
}): Promise<{
|
|
7
|
+
principalId: string;
|
|
8
|
+
created: boolean;
|
|
9
|
+
}>;
|
|
10
|
+
}
|
|
11
|
+
export interface OperatorInvitationStore {
|
|
12
|
+
issue(input: {
|
|
13
|
+
actorPrincipalId: string;
|
|
14
|
+
targetPrincipalId: string;
|
|
15
|
+
email: string;
|
|
16
|
+
role: OperatorRole;
|
|
17
|
+
ttlSeconds: number;
|
|
18
|
+
}): Promise<OperatorInvitationResult | {
|
|
19
|
+
code: OperatorInvitationConflict;
|
|
20
|
+
}>;
|
|
21
|
+
read(principalId: string): Promise<OperatorMembershipSnapshot | null>;
|
|
22
|
+
/** Trusted server input only: principal must come from successful authentication. */
|
|
23
|
+
acceptOperatorInvitation(principalId: string): Promise<OperatorInvitationAcceptance>;
|
|
24
|
+
remove(input: {
|
|
25
|
+
actorPrincipalId: string;
|
|
26
|
+
targetPrincipalId: string;
|
|
27
|
+
reason?: string;
|
|
28
|
+
}): Promise<{
|
|
29
|
+
revoked: true;
|
|
30
|
+
}>;
|
|
31
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface OperatorIdentity {
|
|
2
|
+
principalId: string;
|
|
3
|
+
email: string;
|
|
4
|
+
}
|
|
5
|
+
export interface OperatorMembership extends OperatorIdentity {
|
|
6
|
+
membershipState: string;
|
|
7
|
+
role: unknown;
|
|
8
|
+
}
|
|
9
|
+
export interface OperatorIdentityStore {
|
|
10
|
+
issue(input: {
|
|
11
|
+
email: string;
|
|
12
|
+
tokenHash: string;
|
|
13
|
+
}): Promise<{
|
|
14
|
+
deliverable: false;
|
|
15
|
+
} | {
|
|
16
|
+
deliverable: true;
|
|
17
|
+
email: string;
|
|
18
|
+
expiresAt: string;
|
|
19
|
+
}>;
|
|
20
|
+
/** Activation and consumption commit together before signing. */
|
|
21
|
+
redeem(input: {
|
|
22
|
+
tokenHash: string;
|
|
23
|
+
}): Promise<OperatorIdentity | null>;
|
|
24
|
+
findMembership(principalId: string): Promise<OperatorMembership | null>;
|
|
25
|
+
bootstrapFirstAdministrator(input: {
|
|
26
|
+
email: string;
|
|
27
|
+
}): Promise<{
|
|
28
|
+
principalId: string;
|
|
29
|
+
membershipState: "invited";
|
|
30
|
+
}>;
|
|
31
|
+
pruneChallenges(input: {
|
|
32
|
+
limit: number;
|
|
33
|
+
}): Promise<{
|
|
34
|
+
deleted: number;
|
|
35
|
+
}>;
|
|
36
|
+
close(): Promise<void>;
|
|
37
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CustomerChallengeDeliveryPort, CustomerSessionIssuerPort } from "./ports.js";
|
|
2
|
+
import type { OperatorIdentityStore } from "./operatorPorts.js";
|
|
3
|
+
import type { OperatorSession } from "./contracts/operatorAuth.js";
|
|
4
|
+
export declare function createOperatorSessionService(options: {
|
|
5
|
+
store: OperatorIdentityStore;
|
|
6
|
+
delivery: CustomerChallengeDeliveryPort;
|
|
7
|
+
issuer: CustomerSessionIssuerPort;
|
|
8
|
+
generateToken?: () => string;
|
|
9
|
+
deliveryFailed?: (code: "operator_challenge_delivery_failed") => void;
|
|
10
|
+
}): {
|
|
11
|
+
requestChallenge(email: string, accept?: () => Promise<void>): Promise<void>;
|
|
12
|
+
verifyChallenge(token: string): Promise<OperatorSession | null>;
|
|
13
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
2
|
+
export function createOperatorSessionService(options) {
|
|
3
|
+
const hash = (value) => createHash("sha256").update(value, "utf8").digest("hex");
|
|
4
|
+
return {
|
|
5
|
+
async requestChallenge(email, accept) {
|
|
6
|
+
const token = options.generateToken?.() ?? randomBytes(32).toString("base64url");
|
|
7
|
+
if (!/^[A-Za-z0-9_-]{43}$/.test(token))
|
|
8
|
+
throw new Error("operator_challenge_generator_invalid");
|
|
9
|
+
const result = await options.store.issue({ email: email.trim().toLowerCase(), tokenHash: hash(token) });
|
|
10
|
+
// Send the generic acceptance before mailbox transport, for every eligibility result.
|
|
11
|
+
await accept?.();
|
|
12
|
+
if (!result.deliverable)
|
|
13
|
+
return;
|
|
14
|
+
try {
|
|
15
|
+
await options.delivery.deliver({ email: result.email, expiresAt: result.expiresAt, token });
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
options.deliveryFailed?.("operator_challenge_delivery_failed");
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
async verifyChallenge(token) {
|
|
22
|
+
if (!/^[A-Za-z0-9_-]{43}$/.test(token))
|
|
23
|
+
return null;
|
|
24
|
+
const identity = await options.store.redeem({ tokenHash: hash(token) });
|
|
25
|
+
if (!identity)
|
|
26
|
+
return null;
|
|
27
|
+
const session = await options.issuer.issue(identity);
|
|
28
|
+
return { ...session, user: { id: identity.principalId, email: identity.email } };
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AdminAuthPort } from "./ports.js";
|
|
2
|
+
export type PlatformOperatorAuthConfig = {
|
|
3
|
+
readonly operatorToken: string;
|
|
4
|
+
readonly operatorId: string;
|
|
5
|
+
};
|
|
6
|
+
export type PlatformOperatorAllowlistCheck = (principalId: string) => Promise<boolean>;
|
|
7
|
+
export type PlatformOperatorAuthConfigResolution = {
|
|
8
|
+
readonly config: PlatformOperatorAuthConfig;
|
|
9
|
+
readonly error?: undefined;
|
|
10
|
+
} | {
|
|
11
|
+
readonly config?: undefined;
|
|
12
|
+
readonly error: "platform_operator_token_required" | "platform_operator_token_invalid" | "platform_operator_id_required" | "platform_operator_id_invalid";
|
|
13
|
+
};
|
|
14
|
+
export declare function resolvePlatformOperatorAuthConfig(env: Record<string, string | undefined>): PlatformOperatorAuthConfigResolution;
|
|
15
|
+
export declare function createOperatorTokenAdminAuth(config: PlatformOperatorAuthConfig, isOperatorAllowed: PlatformOperatorAllowlistCheck): AdminAuthPort;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { createHash, timingSafeEqual } from "node:crypto";
|
|
2
|
+
const MIN_TOKEN_BYTES = 32;
|
|
3
|
+
const MAX_TOKEN_BYTES = 512;
|
|
4
|
+
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
5
|
+
function byteLength(value) {
|
|
6
|
+
return Buffer.byteLength(value, "utf8");
|
|
7
|
+
}
|
|
8
|
+
export function resolvePlatformOperatorAuthConfig(env) {
|
|
9
|
+
const operatorToken = env.PLATFORM_OPERATOR_TOKEN?.trim() ?? "";
|
|
10
|
+
if (!operatorToken)
|
|
11
|
+
return { error: "platform_operator_token_required" };
|
|
12
|
+
const tokenBytes = byteLength(operatorToken);
|
|
13
|
+
if (tokenBytes < MIN_TOKEN_BYTES || tokenBytes > MAX_TOKEN_BYTES) {
|
|
14
|
+
return { error: "platform_operator_token_invalid" };
|
|
15
|
+
}
|
|
16
|
+
const operatorId = env.PLATFORM_OPERATOR_ID?.trim() ?? "";
|
|
17
|
+
if (!operatorId)
|
|
18
|
+
return { error: "platform_operator_id_required" };
|
|
19
|
+
if (!UUID_PATTERN.test(operatorId))
|
|
20
|
+
return { error: "platform_operator_id_invalid" };
|
|
21
|
+
return { config: { operatorToken, operatorId: operatorId.toLowerCase() } };
|
|
22
|
+
}
|
|
23
|
+
function matchesOperatorToken(candidate, expected) {
|
|
24
|
+
if (!candidate || byteLength(candidate) > MAX_TOKEN_BYTES)
|
|
25
|
+
return false;
|
|
26
|
+
const actualDigest = createHash("sha256").update(candidate, "utf8").digest();
|
|
27
|
+
const expectedDigest = createHash("sha256").update(expected, "utf8").digest();
|
|
28
|
+
return timingSafeEqual(actualDigest, expectedDigest);
|
|
29
|
+
}
|
|
30
|
+
export function createOperatorTokenAdminAuth(config, isOperatorAllowed) {
|
|
31
|
+
return {
|
|
32
|
+
async authorize(accessToken, options = {}) {
|
|
33
|
+
if (!matchesOperatorToken(accessToken, config.operatorToken)) {
|
|
34
|
+
return { ok: false, code: "UNAUTHORIZED", message: "Admin session required" };
|
|
35
|
+
}
|
|
36
|
+
if (options.allowedRoles && !options.allowedRoles.includes("admin")) {
|
|
37
|
+
return { ok: false, code: "FORBIDDEN", message: "Admin role required" };
|
|
38
|
+
}
|
|
39
|
+
if (!(await isOperatorAllowed(config.operatorId))) {
|
|
40
|
+
return { ok: false, code: "FORBIDDEN", message: "Admin role required" };
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
ok: true,
|
|
44
|
+
principalId: config.operatorId,
|
|
45
|
+
role: "admin",
|
|
46
|
+
isMachineActor: true,
|
|
47
|
+
};
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type PersonalDataContributor, type PersonalDataContributorRegistry } from "./privacyPorts.js";
|
|
2
|
+
export declare function createPersonalDataContributorRegistry<Context>(contributors: readonly PersonalDataContributor<Context>[], options: {
|
|
3
|
+
requiredContributorIds: readonly string[];
|
|
4
|
+
}): PersonalDataContributorRegistry<Context>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { PrivacyRefusal } from "./privacyPorts.js";
|
|
2
|
+
export function createPersonalDataContributorRegistry(contributors, options) {
|
|
3
|
+
const ids = contributors.map(c => c.id);
|
|
4
|
+
if (new Set(ids).size !== ids.length)
|
|
5
|
+
throw new PrivacyRefusal("PRIVACY_CONTRIBUTOR_DUPLICATE");
|
|
6
|
+
if (contributors.some(c => !/^[a-z][a-z0-9.-]{0,99}$/.test(c.id) || !/^[a-z][a-z0-9.-]*\.v[1-9][0-9]*$/.test(c.version)))
|
|
7
|
+
throw new PrivacyRefusal("PRIVACY_OWNERSHIP_UNDECLARED");
|
|
8
|
+
if (contributors.some(c => !Array.isArray(c.ownedResources) || c.ownedResources.length === 0 || c.ownedResources.some(resource => !resource.trim())))
|
|
9
|
+
throw new PrivacyRefusal("PRIVACY_OWNERSHIP_UNDECLARED");
|
|
10
|
+
return Object.freeze({ contributors: Object.freeze([...contributors]), missingContributors: Object.freeze(options.requiredContributorIds.filter(id => !ids.includes(id))) });
|
|
11
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type PlatformAdminAuthorizationResult = {
|
|
2
|
+
ok: true;
|
|
3
|
+
} | {
|
|
4
|
+
ok: false;
|
|
5
|
+
code: "UNAUTHORIZED" | "FORBIDDEN";
|
|
6
|
+
message: string;
|
|
7
|
+
};
|
|
8
|
+
export type PlatformUserAuthenticationResult = {
|
|
9
|
+
ok: true;
|
|
10
|
+
userId: string;
|
|
11
|
+
} | {
|
|
12
|
+
ok: false;
|
|
13
|
+
code: "UNAUTHORIZED";
|
|
14
|
+
message: string;
|
|
15
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { type EmailTrustPolicy } from "./emailTrustPolicy.js";
|
|
2
|
+
import type { EmailTrustFailureCode } from "./contracts/emailTrust.js";
|
|
3
|
+
export interface VerifiedPrincipal {
|
|
4
|
+
principalId: string;
|
|
5
|
+
email: string | null;
|
|
6
|
+
emailVerified: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface IdentityVerifierPort {
|
|
9
|
+
verifyAccessToken(accessToken: string): Promise<VerifiedPrincipal | null>;
|
|
10
|
+
}
|
|
11
|
+
export interface CustomerChallengeStore {
|
|
12
|
+
issue(input: {
|
|
13
|
+
email: string;
|
|
14
|
+
tokenHash: string;
|
|
15
|
+
expiresAt: string;
|
|
16
|
+
}): Promise<{
|
|
17
|
+
deliverable: boolean;
|
|
18
|
+
}>;
|
|
19
|
+
redeem(input: {
|
|
20
|
+
tokenHash: string;
|
|
21
|
+
now: string;
|
|
22
|
+
}): Promise<{
|
|
23
|
+
principalId: string;
|
|
24
|
+
email: string;
|
|
25
|
+
} | null>;
|
|
26
|
+
}
|
|
27
|
+
export interface CustomerChallengeDeliveryPort {
|
|
28
|
+
deliver(input: {
|
|
29
|
+
email: string;
|
|
30
|
+
token: string;
|
|
31
|
+
expiresAt: string;
|
|
32
|
+
}): Promise<void>;
|
|
33
|
+
}
|
|
34
|
+
export interface CustomerSessionIssuerPort {
|
|
35
|
+
issue(input: {
|
|
36
|
+
principalId: string;
|
|
37
|
+
email: string;
|
|
38
|
+
}): Promise<{
|
|
39
|
+
accessToken: string;
|
|
40
|
+
expiresAt: string;
|
|
41
|
+
}>;
|
|
42
|
+
}
|
|
43
|
+
export type LinkClientAuthUserResult = {
|
|
44
|
+
ok: true;
|
|
45
|
+
clientId: string;
|
|
46
|
+
authUserId: string;
|
|
47
|
+
created: boolean;
|
|
48
|
+
alreadyLinked: boolean;
|
|
49
|
+
adminSelfLink: boolean;
|
|
50
|
+
} | {
|
|
51
|
+
ok: false;
|
|
52
|
+
code: EmailTrustFailureCode | "CLIENT_NOT_FOUND" | "MACHINE_ACTOR_REFUSED" | "CLIENT_AUTH_LINK_CONFLICT";
|
|
53
|
+
message: string;
|
|
54
|
+
};
|
|
55
|
+
export type SetClientAuthUserIdResult = {
|
|
56
|
+
ok: true;
|
|
57
|
+
alreadyLinked: boolean;
|
|
58
|
+
} | {
|
|
59
|
+
ok: false;
|
|
60
|
+
code: "CLIENT_AUTH_LINK_CONFLICT";
|
|
61
|
+
existingAuthUserId: string | null;
|
|
62
|
+
};
|
|
63
|
+
export interface ClientAuthLinkStore {
|
|
64
|
+
emailTrustPolicy: EmailTrustPolicy;
|
|
65
|
+
findClientByEmail(email: string): Promise<{
|
|
66
|
+
id: string;
|
|
67
|
+
authUserId: string | null;
|
|
68
|
+
} | null>;
|
|
69
|
+
findAuthUserIdByEmail(email: string): Promise<string | null>;
|
|
70
|
+
findAuthUserEmailById(authUserId: string): Promise<string | null>;
|
|
71
|
+
/** Provision an unconfirmed principal; null means an existing identity needs lookup. */
|
|
72
|
+
createAuthUser(email: string): Promise<string | null>;
|
|
73
|
+
findAdminIdentity(authUserId: string): Promise<{
|
|
74
|
+
isMachineActor: boolean;
|
|
75
|
+
} | null>;
|
|
76
|
+
setClientAuthUserId(clientId: string, authUserId: string): Promise<SetClientAuthUserIdResult>;
|
|
77
|
+
}
|
|
78
|
+
/** Link a commerce contact without proving mailbox ownership; failed links never delete identities. */
|
|
79
|
+
export declare function linkClientAuthUser(store: ClientAuthLinkStore, rawEmail: string): Promise<LinkClientAuthUserResult>;
|
|
80
|
+
export type AdminPrincipalRole = "admin" | "distributor";
|
|
81
|
+
export type AdminAuthorizationResult = {
|
|
82
|
+
ok: true;
|
|
83
|
+
principalId: string;
|
|
84
|
+
role: AdminPrincipalRole;
|
|
85
|
+
isMachineActor: boolean;
|
|
86
|
+
} | {
|
|
87
|
+
ok: false;
|
|
88
|
+
code: "UNAUTHORIZED" | "FORBIDDEN";
|
|
89
|
+
message: string;
|
|
90
|
+
};
|
|
91
|
+
export interface AdminAuthPort {
|
|
92
|
+
authorize(accessToken: string | null, options?: {
|
|
93
|
+
allowedRoles?: readonly AdminPrincipalRole[];
|
|
94
|
+
}): Promise<AdminAuthorizationResult>;
|
|
95
|
+
}
|
|
96
|
+
export interface CustomerAccount {
|
|
97
|
+
id: string;
|
|
98
|
+
email: string | null;
|
|
99
|
+
principalId: string | null;
|
|
100
|
+
}
|
|
101
|
+
export interface CreateAccountInput {
|
|
102
|
+
email: string;
|
|
103
|
+
principalId: string;
|
|
104
|
+
acquisitionSource: string;
|
|
105
|
+
lifecycleStage: string;
|
|
106
|
+
}
|
|
107
|
+
export type LinkPrincipalResult = {
|
|
108
|
+
ok: true;
|
|
109
|
+
alreadyLinked: boolean;
|
|
110
|
+
} | {
|
|
111
|
+
ok: false;
|
|
112
|
+
existingPrincipalId: string | null;
|
|
113
|
+
};
|
|
114
|
+
export interface AccountLinkStore {
|
|
115
|
+
findAccountByPrincipalId(principalId: string): Promise<CustomerAccount | null>;
|
|
116
|
+
findAccountByEmail(email: string): Promise<CustomerAccount | null>;
|
|
117
|
+
isReservedPrincipal(principalId: string): Promise<boolean>;
|
|
118
|
+
linkPrincipalToAccount(accountId: string, principalId: string): Promise<LinkPrincipalResult>;
|
|
119
|
+
createAccount(input: CreateAccountInput): Promise<{
|
|
120
|
+
id: string;
|
|
121
|
+
}>;
|
|
122
|
+
}
|
|
123
|
+
/** Attempt creation before scanning the existing identity directory. */
|
|
124
|
+
export declare function findOrCreateAuthUserId(store: Pick<ClientAuthLinkStore, "createAuthUser" | "findAuthUserIdByEmail">, email: string): Promise<{
|
|
125
|
+
authUserId: string;
|
|
126
|
+
created: boolean;
|
|
127
|
+
}>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { assessEmailTrust } from "./emailTrustPolicy.js";
|
|
2
|
+
/** Link a commerce contact without proving mailbox ownership; failed links never delete identities. */
|
|
3
|
+
export async function linkClientAuthUser(store, rawEmail) {
|
|
4
|
+
const email = rawEmail.trim().toLowerCase();
|
|
5
|
+
const client = await store.findClientByEmail(email);
|
|
6
|
+
if (!client)
|
|
7
|
+
return { ok: false, code: "CLIENT_NOT_FOUND", message: "No client row exists for this email; provision the client first." };
|
|
8
|
+
if (client.authUserId) {
|
|
9
|
+
const linkedEmail = await store.findAuthUserEmailById(client.authUserId);
|
|
10
|
+
if (linkedEmail?.trim().toLowerCase() !== email) {
|
|
11
|
+
return { ok: false, code: "CLIENT_AUTH_LINK_CONFLICT", message: "Client account is linked to a different auth identity." };
|
|
12
|
+
}
|
|
13
|
+
return { ok: true, clientId: client.id, authUserId: client.authUserId, created: false, alreadyLinked: true, adminSelfLink: false };
|
|
14
|
+
}
|
|
15
|
+
const trust = await assessEmailTrust(store.emailTrustPolicy);
|
|
16
|
+
if (!trust.trusted)
|
|
17
|
+
return { ok: false, code: trust.code, message: "Soft-account provisioning requires a trusted email envelope." };
|
|
18
|
+
const { authUserId, created } = await findOrCreateAuthUserId(store, email);
|
|
19
|
+
const adminIdentity = await store.findAdminIdentity(authUserId);
|
|
20
|
+
if (adminIdentity?.isMachineActor) {
|
|
21
|
+
return { ok: false, code: "MACHINE_ACTOR_REFUSED", message: "Refusing to link a machine/service admin identity to a customer client row." };
|
|
22
|
+
}
|
|
23
|
+
const link = await store.setClientAuthUserId(client.id, authUserId);
|
|
24
|
+
if (!link.ok) {
|
|
25
|
+
return { ok: false, code: "CLIENT_AUTH_LINK_CONFLICT", message: "Client account was linked to another auth identity concurrently." };
|
|
26
|
+
}
|
|
27
|
+
return { ok: true, clientId: client.id, authUserId, created, alreadyLinked: link.alreadyLinked, adminSelfLink: adminIdentity !== null };
|
|
28
|
+
}
|
|
29
|
+
/** Attempt creation before scanning the existing identity directory. */
|
|
30
|
+
export async function findOrCreateAuthUserId(store, email) {
|
|
31
|
+
const createdId = await store.createAuthUser(email);
|
|
32
|
+
if (createdId)
|
|
33
|
+
return { authUserId: createdId, created: true };
|
|
34
|
+
const existingId = await store.findAuthUserIdByEmail(email);
|
|
35
|
+
if (!existingId)
|
|
36
|
+
throw new Error("auth_user_registered_but_unresolved");
|
|
37
|
+
return { authUserId: existingId, created: false };
|
|
38
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AccountLinkStore } from "./ports.js";
|
|
2
|
+
/** Trusted contact-link capability; conditional writes never replace an existing principal. */
|
|
3
|
+
export declare function createPostgresAccountLinkStore(env: {
|
|
4
|
+
connectionString: string;
|
|
5
|
+
}): AccountLinkStore & {
|
|
6
|
+
close(): Promise<void>;
|
|
7
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { createPostgresRoleFreeTransactionLane } from "../data/index.js";
|
|
2
|
+
/** Trusted contact-link capability; conditional writes never replace an existing principal. */
|
|
3
|
+
export function createPostgresAccountLinkStore(env) {
|
|
4
|
+
const lane = createPostgresRoleFreeTransactionLane(env);
|
|
5
|
+
const run = (work) => lane.run(db => work(db));
|
|
6
|
+
const account = (row) => row
|
|
7
|
+
? { id: String(row.id), email: row.email === null ? null : String(row.email), principalId: row.principal_id === null ? null : String(row.principal_id) } : null;
|
|
8
|
+
async function writable(db, principalId, clientId = null) {
|
|
9
|
+
// Same receipt-before-contact lock order as privacy erasure; no concurrent resurrection.
|
|
10
|
+
await db.query("LOCK TABLE public.identity_privacy_erasures IN SHARE MODE");
|
|
11
|
+
if ((await db.query("SELECT 1 FROM public.identity_privacy_erasures WHERE principal_id = $1 OR client_id = $2 LIMIT 1", [principalId, clientId])).rows.length)
|
|
12
|
+
throw new Error("account_erased_refused");
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
findAccountByPrincipalId(id) {
|
|
16
|
+
return run(async (db) => account((await db.query("SELECT id, email, principal_id FROM public.clients WHERE principal_id = $1", [id])).rows[0]));
|
|
17
|
+
},
|
|
18
|
+
findAccountByEmail(email) {
|
|
19
|
+
return run(async (db) => {
|
|
20
|
+
const rows = (await db.query("SELECT id, email, principal_id FROM public.clients WHERE lower(btrim(email)) = $1 LIMIT 2", [email.trim().toLowerCase()])).rows;
|
|
21
|
+
if (rows.length > 1)
|
|
22
|
+
throw new Error("account_normalized_email_ambiguous");
|
|
23
|
+
return account(rows[0]);
|
|
24
|
+
});
|
|
25
|
+
},
|
|
26
|
+
isReservedPrincipal(id) {
|
|
27
|
+
return run(async (db) => (await db.query("SELECT 1 FROM public.platform_control_operators WHERE principal_id = $1 UNION ALL SELECT 1 FROM public.identity_privacy_erasures WHERE principal_id = $1 LIMIT 1", [id])).rows.length > 0);
|
|
28
|
+
},
|
|
29
|
+
linkPrincipalToAccount(id, principalId) {
|
|
30
|
+
return run(async (db) => {
|
|
31
|
+
await writable(db, principalId, id);
|
|
32
|
+
const updated = await db.query("UPDATE public.clients SET principal_id = $2 WHERE id = $1 AND principal_id IS NULL RETURNING id", [id, principalId]);
|
|
33
|
+
if (updated.rows.length)
|
|
34
|
+
return { ok: true, alreadyLinked: false };
|
|
35
|
+
const row = (await db.query("SELECT principal_id FROM public.clients WHERE id = $1", [id])).rows[0];
|
|
36
|
+
return row?.principal_id === principalId ? { ok: true, alreadyLinked: true }
|
|
37
|
+
: { ok: false, existingPrincipalId: row?.principal_id == null ? null : String(row.principal_id) };
|
|
38
|
+
});
|
|
39
|
+
},
|
|
40
|
+
createAccount(input) {
|
|
41
|
+
return run(async (db) => {
|
|
42
|
+
await writable(db, input.principalId);
|
|
43
|
+
const row = (await db.query(`INSERT INTO public.clients (email, principal_id, acquisition_source, lifecycle_stage)
|
|
44
|
+
VALUES ($1, $2, $3, $4) RETURNING id`, [input.email, input.principalId, input.acquisitionSource, input.lifecycleStage])).rows[0];
|
|
45
|
+
if (!row)
|
|
46
|
+
throw new Error("account_insert_failed");
|
|
47
|
+
return { id: String(row.id) };
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
close: lane.close,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function createPostgresCustomerMePort(gateway) {
|
|
2
|
+
return {
|
|
3
|
+
async getCustomerMe() {
|
|
4
|
+
const { data, error } = await gateway
|
|
5
|
+
.from("clients")
|
|
6
|
+
.select("id, email, first_name, last_name, lifecycle_stage")
|
|
7
|
+
.maybeSingle();
|
|
8
|
+
if (error)
|
|
9
|
+
throw error;
|
|
10
|
+
if (!data)
|
|
11
|
+
return null;
|
|
12
|
+
return {
|
|
13
|
+
clientId: data.id,
|
|
14
|
+
email: data.email,
|
|
15
|
+
firstName: data.first_name,
|
|
16
|
+
lastName: data.last_name,
|
|
17
|
+
lifecycleStage: data.lifecycle_stage,
|
|
18
|
+
};
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CustomerChallengeStore } from "./ports.js";
|
|
2
|
+
import { type PostgresDataGatewayEnv, type PostgresDataGatewayOptions } from "../data/index.js";
|
|
3
|
+
export type PostgresCustomerChallengeStore = CustomerChallengeStore & {
|
|
4
|
+
close(): Promise<void>;
|
|
5
|
+
pruneChallenges(input: {
|
|
6
|
+
limit: number;
|
|
7
|
+
}): Promise<{
|
|
8
|
+
deleted: number;
|
|
9
|
+
}>;
|
|
10
|
+
};
|
|
11
|
+
export declare function createPostgresCustomerChallengeStore(env: PostgresDataGatewayEnv, options?: PostgresDataGatewayOptions): PostgresCustomerChallengeStore;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { createPostgresRoleFreeTransactionLane, } from "../data/index.js";
|
|
2
|
+
export function createPostgresCustomerChallengeStore(env, options = {}) {
|
|
3
|
+
const lane = createPostgresRoleFreeTransactionLane(env, options);
|
|
4
|
+
return {
|
|
5
|
+
async issue(input) {
|
|
6
|
+
return lane.run(async (executor) => {
|
|
7
|
+
const db = executor;
|
|
8
|
+
// EXCLUSIVE conflicts with erasure's EXCLUSIVE lock and stays held through the RPC.
|
|
9
|
+
// Receipt reads use ACCESS SHARE, compatible with erasure's receipt-table lock.
|
|
10
|
+
await db.query("LOCK TABLE public.customer_identity_challenges IN EXCLUSIVE MODE");
|
|
11
|
+
const erased = await db.query(`SELECT 1 FROM public.clients c JOIN public.identity_privacy_erasures e ON e.client_id = c.id OR e.principal_id = c.principal_id
|
|
12
|
+
WHERE lower(c.email) = lower(btrim($1))`, [input.email]);
|
|
13
|
+
if (erased.rows.length)
|
|
14
|
+
return { deliverable: false };
|
|
15
|
+
const { rows } = await db.query("SELECT public.customer_identity_challenge_issue($1,$2,$3::timestamptz,now()) AS result", [input.email, input.tokenHash, input.expiresAt]);
|
|
16
|
+
const result = rows[0]?.result;
|
|
17
|
+
if (!result || typeof result !== "object")
|
|
18
|
+
throw new Error("customer challenge issue response invalid");
|
|
19
|
+
return { deliverable: result.deliverable === true };
|
|
20
|
+
});
|
|
21
|
+
},
|
|
22
|
+
async redeem(input) {
|
|
23
|
+
return lane.run(async (executor) => {
|
|
24
|
+
const db = executor;
|
|
25
|
+
// EXCLUSIVE conflicts with erasure's EXCLUSIVE lock and stays held through the RPC.
|
|
26
|
+
// Receipt reads use ACCESS SHARE, compatible with erasure's receipt-table lock.
|
|
27
|
+
await db.query("LOCK TABLE public.customer_identity_challenges IN EXCLUSIVE MODE");
|
|
28
|
+
const erased = await db.query(`SELECT 1 FROM public.customer_identity_challenges c
|
|
29
|
+
JOIN public.identity_privacy_erasures e ON e.client_id = c.client_id OR e.principal_id = c.principal_id WHERE c.token_hash = $1`, [input.tokenHash]);
|
|
30
|
+
if (erased.rows.length)
|
|
31
|
+
return null;
|
|
32
|
+
const { rows } = await db.query("SELECT public.customer_identity_challenge_redeem($1,$2::timestamptz) AS result", [input.tokenHash, input.now]);
|
|
33
|
+
const result = rows[0]?.result;
|
|
34
|
+
if (result === null || result === undefined)
|
|
35
|
+
return null;
|
|
36
|
+
if (!result || typeof result !== "object")
|
|
37
|
+
throw new Error("customer challenge redeem response invalid");
|
|
38
|
+
const value = result;
|
|
39
|
+
if (typeof value.principalId !== "string" || typeof value.email !== "string") {
|
|
40
|
+
throw new Error("customer challenge redeem response invalid");
|
|
41
|
+
}
|
|
42
|
+
return { principalId: value.principalId, email: value.email };
|
|
43
|
+
});
|
|
44
|
+
},
|
|
45
|
+
async pruneChallenges({ limit }) {
|
|
46
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 500)
|
|
47
|
+
throw new Error("customer_prune_limit_invalid");
|
|
48
|
+
return lane.run(async (executor) => {
|
|
49
|
+
const deleted = await executor.query(`WITH batch AS (SELECT id FROM public.customer_identity_challenges
|
|
50
|
+
WHERE expires_at < clock_timestamp() - interval '24 hours' ORDER BY expires_at, id LIMIT $1 FOR UPDATE SKIP LOCKED)
|
|
51
|
+
DELETE FROM public.customer_identity_challenges c USING batch WHERE c.id = batch.id RETURNING c.id`, [limit]);
|
|
52
|
+
return { deleted: deleted.rows.length };
|
|
53
|
+
});
|
|
54
|
+
},
|
|
55
|
+
close: lane.close,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { OperatorIdentityStore } from "./operatorPorts.js";
|
|
2
|
+
/** Dedicated owner lane: no actor gateway, customer writes or machine grants. */
|
|
3
|
+
export declare function createPostgresOperatorIdentityStore(env: {
|
|
4
|
+
connectionString: string;
|
|
5
|
+
}): OperatorIdentityStore;
|