@openlup/platform 0.16.0 → 0.18.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 +180 -0
- package/README.md +564 -6
- package/dist/catalog/activeSkuSelection.d.ts +2 -0
- package/dist/catalog/activeSkuSelection.js +20 -0
- package/dist/catalog/adminBindings.d.ts +72 -0
- package/dist/catalog/adminBindings.js +1 -0
- package/dist/catalog/adminBundleHandler.d.ts +26 -0
- package/dist/catalog/adminBundleHandler.js +111 -0
- package/dist/catalog/adminBundleReadHandler.d.ts +20 -0
- package/dist/catalog/adminBundleReadHandler.js +185 -0
- package/dist/catalog/adminBundleWritePort.d.ts +85 -0
- package/dist/catalog/adminBundleWritePort.js +1 -0
- package/dist/catalog/bundleAvailability.d.ts +63 -0
- package/dist/catalog/bundleAvailability.js +78 -0
- package/dist/catalog/bundleAvailabilityService.d.ts +65 -0
- package/dist/catalog/bundleAvailabilityService.js +89 -0
- package/dist/catalog/bundleBindings.d.ts +33 -0
- package/dist/catalog/bundleBindings.js +1 -0
- package/dist/catalog/bundleCatalogQueries.d.ts +28 -0
- package/dist/catalog/bundleCatalogQueries.js +84 -0
- package/dist/catalog/bundleCatalogReadPort.d.ts +166 -0
- package/dist/catalog/bundleCatalogReadPort.js +36 -0
- package/dist/catalog/bundleCatalogStore.d.ts +3 -0
- package/dist/catalog/bundleCatalogStore.js +152 -0
- package/dist/catalog/bundleRuleCodes.d.ts +41 -0
- package/dist/catalog/bundleRuleCodes.js +110 -0
- package/dist/catalog/bundleRules.d.ts +69 -0
- package/dist/catalog/bundleRules.js +102 -0
- package/dist/catalog/catalogDraftStore.d.ts +4 -0
- package/dist/catalog/catalogDraftStore.js +63 -0
- package/dist/catalog/catalogSkuEnvelope.d.ts +7 -0
- package/dist/catalog/catalogSkuEnvelope.js +142 -0
- package/dist/catalog/catalogSkuEnvelopeMapper.d.ts +38 -0
- package/dist/catalog/catalogSkuEnvelopeMapper.js +88 -0
- package/dist/catalog/contracts/bundleAdmin.d.ts +143 -0
- package/dist/catalog/contracts/bundleAdmin.js +1 -0
- package/dist/catalog/contracts/bundleAvailability.d.ts +61 -0
- package/dist/catalog/contracts/bundleAvailability.js +1 -0
- package/dist/catalog/contracts/bundleRead.d.ts +223 -0
- package/dist/catalog/contracts/bundleRead.js +1 -0
- package/dist/catalog/contracts/draft.d.ts +79 -0
- package/dist/catalog/contracts/draft.js +47 -0
- package/dist/catalog/contracts/foundation.d.ts +78 -0
- package/dist/catalog/contracts/foundation.js +53 -0
- package/dist/catalog/contracts/index.d.ts +14 -0
- package/dist/catalog/contracts/index.js +6 -0
- package/dist/catalog/contracts/lifecycle.d.ts +77 -0
- package/dist/catalog/contracts/lifecycle.js +1 -0
- package/dist/catalog/contracts/productMediaContracts.d.ts +77 -0
- package/dist/catalog/contracts/productMediaContracts.js +149 -0
- package/dist/catalog/contracts/productTypes.d.ts +54 -0
- package/dist/catalog/contracts/productTypes.js +16 -0
- package/dist/catalog/contracts/resolveProductMedia.d.ts +3 -0
- package/dist/catalog/contracts/resolveProductMedia.js +78 -0
- package/dist/catalog/contracts/sellable.d.ts +23 -0
- package/dist/catalog/contracts/sellable.js +1 -0
- package/dist/catalog/draftHandler.d.ts +25 -0
- package/dist/catalog/draftHandler.js +118 -0
- package/dist/catalog/draftPorts.d.ts +18 -0
- package/dist/catalog/draftPorts.js +8 -0
- package/dist/catalog/draftSchemas.d.ts +423 -0
- package/dist/catalog/draftSchemas.js +89 -0
- package/dist/catalog/draftValidation.d.ts +21 -0
- package/dist/catalog/draftValidation.js +125 -0
- package/dist/catalog/foundationSchemas.d.ts +98 -0
- package/dist/catalog/foundationSchemas.js +110 -0
- package/dist/catalog/index.d.ts +43 -0
- package/dist/catalog/index.js +25 -0
- package/dist/catalog/mediaSourcePack.d.ts +51 -0
- package/dist/catalog/mediaSourcePack.js +247 -0
- package/dist/catalog/productTypeSchemas.d.ts +46 -0
- package/dist/catalog/productTypeSchemas.js +72 -0
- package/dist/catalog/publicationPorts.d.ts +42 -0
- package/dist/catalog/publicationPorts.js +1 -0
- package/dist/catalog/readPorts.d.ts +51 -0
- package/dist/catalog/readPorts.js +1 -0
- package/dist/catalog/referenceCatalogHandler.d.ts +7 -0
- package/dist/catalog/referenceCatalogHandler.js +27 -0
- package/dist/catalog/resolvedSkuProjection.d.ts +88 -0
- package/dist/catalog/resolvedSkuProjection.js +60 -0
- package/dist/catalog/sellableBundleHandler.d.ts +46 -0
- package/dist/catalog/sellableBundleHandler.js +84 -0
- package/dist/catalog/sellableSchemas.d.ts +46 -0
- package/dist/catalog/sellableSchemas.js +27 -0
- package/dist/catalog/sql.d.ts +46 -0
- package/dist/catalog/sql.js +13 -0
- package/dist/catalog/validation.d.ts +2 -0
- package/dist/catalog/validation.js +3 -0
- 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 +52 -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/marketingSync.d.ts +194 -0
- package/dist/communications/contracts/marketingSync.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 +47 -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/klaviyo/consentPush.d.ts +51 -0
- package/dist/communications/klaviyo/consentPush.js +19 -0
- package/dist/communications/klaviyo/index.d.ts +110 -0
- package/dist/communications/klaviyo/index.js +860 -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/resend/index.d.ts +40 -0
- package/dist/communications/resend/index.js +310 -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/composition/catalogContribution.d.ts +18 -0
- package/dist/composition/catalogContribution.js +21 -0
- package/dist/composition/index.d.ts +1 -0
- package/dist/composition/index.js +1 -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/customerContracts.d.ts +2 -2
- package/dist/inventory/adminInventory.d.ts +13 -0
- package/dist/inventory/adminInventory.js +214 -0
- package/dist/inventory/contracts/forecast.d.ts +86 -0
- package/dist/inventory/contracts/forecast.js +1 -0
- package/dist/inventory/contracts/index.d.ts +4 -0
- package/dist/inventory/contracts/index.js +1 -0
- package/dist/inventory/contracts/inventory.d.ts +157 -0
- package/dist/inventory/contracts/inventory.js +1 -0
- package/dist/inventory/contracts/newPurchaseStock.d.ts +20 -0
- package/dist/inventory/contracts/newPurchaseStock.js +1 -0
- package/dist/inventory/contracts/reservation.d.ts +18 -0
- package/dist/inventory/contracts/reservation.js +1 -0
- package/dist/inventory/forecast.d.ts +24 -0
- package/dist/inventory/forecast.js +200 -0
- package/dist/inventory/index.d.ts +13 -0
- package/dist/inventory/index.js +10 -0
- package/dist/inventory/inventoryHandlers.d.ts +53 -0
- package/dist/inventory/inventoryHandlers.js +151 -0
- package/dist/inventory/newPurchaseStockAvailability.d.ts +47 -0
- package/dist/inventory/newPurchaseStockAvailability.js +75 -0
- package/dist/inventory/ports.d.ts +21 -0
- package/dist/inventory/ports.js +16 -0
- package/dist/inventory/renewalReservation.d.ts +27 -0
- package/dist/inventory/renewalReservation.js +61 -0
- package/dist/inventory/reservationPolicy.d.ts +6 -0
- package/dist/inventory/reservationPolicy.js +19 -0
- package/dist/inventory/schemas.d.ts +137 -0
- package/dist/inventory/schemas.js +112 -0
- package/dist/inventory/sql.d.ts +14 -0
- package/dist/inventory/sql.js +5 -0
- package/dist/inventory/subscriptionForecastCalculation.d.ts +4 -0
- package/dist/inventory/subscriptionForecastCalculation.js +133 -0
- package/dist/pricing/acquisitionFinancialPolicy.d.ts +26 -0
- package/dist/pricing/acquisitionFinancialPolicy.js +1 -0
- package/dist/pricing/acquisitionOfferPricing.d.ts +27 -0
- package/dist/pricing/acquisitionOfferPricing.js +25 -0
- package/dist/pricing/acquisitionOfferPricingHandler.d.ts +20 -0
- package/dist/pricing/acquisitionOfferPricingHandler.js +105 -0
- package/dist/pricing/acquisitionOfferPricingSchemas.d.ts +31 -0
- package/dist/pricing/acquisitionOfferPricingSchemas.js +33 -0
- package/dist/pricing/acquisitionQuoteBinding.d.ts +12 -0
- package/dist/pricing/acquisitionQuoteBinding.js +12 -0
- package/dist/pricing/acquisitionReadiness.d.ts +31 -0
- package/dist/pricing/acquisitionReadiness.js +117 -0
- package/dist/pricing/acquisitionTerms.d.ts +19 -0
- package/dist/pricing/acquisitionTerms.js +126 -0
- package/dist/pricing/acquisitionTermsSchemas.d.ts +59 -0
- package/dist/pricing/acquisitionTermsSchemas.js +42 -0
- package/dist/pricing/catalogFactsProvenance.d.ts +33 -0
- package/dist/pricing/catalogFactsProvenance.js +64 -0
- package/dist/pricing/catalogFactsSchemas.d.ts +45 -0
- package/dist/pricing/catalogFactsSchemas.js +58 -0
- package/dist/pricing/commerceOfferPricing.d.ts +25 -0
- package/dist/pricing/commerceOfferPricing.js +139 -0
- package/dist/pricing/commerceOfferPricingCache.d.ts +10 -0
- package/dist/pricing/commerceOfferPricingCache.js +44 -0
- package/dist/pricing/commerceOfferPricingHandler.d.ts +21 -0
- package/dist/pricing/commerceOfferPricingHandler.js +78 -0
- package/dist/pricing/commercePriceAuthority.d.ts +26 -0
- package/dist/pricing/commercePriceAuthority.js +79 -0
- package/dist/pricing/commercePricingPolicyErrors.d.ts +40 -0
- package/dist/pricing/commercePricingPolicyErrors.js +67 -0
- package/dist/pricing/commerceQuoteBatchHandler.d.ts +37 -0
- package/dist/pricing/commerceQuoteBatchHandler.js +83 -0
- package/dist/pricing/commerceQuoteCatalogReadPort.d.ts +27 -0
- package/dist/pricing/commerceQuoteCatalogReadPort.js +8 -0
- package/dist/pricing/commerceQuoteHandler.d.ts +31 -0
- package/dist/pricing/commerceQuoteHandler.js +110 -0
- package/dist/pricing/commerceQuoteSnapshotExpect.d.ts +9 -0
- package/dist/pricing/commerceQuoteSnapshotExpect.js +22 -0
- package/dist/pricing/contracts/acquisitionTerms.d.ts +32 -0
- package/dist/pricing/contracts/acquisitionTerms.js +2 -0
- package/dist/pricing/contracts/authority.d.ts +27 -0
- package/dist/pricing/contracts/authority.js +1 -0
- package/dist/pricing/contracts/evidence.d.ts +33 -0
- package/dist/pricing/contracts/evidence.js +1 -0
- package/dist/pricing/contracts/index.d.ts +17 -0
- package/dist/pricing/contracts/index.js +7 -0
- package/dist/pricing/contracts/money.d.ts +27 -0
- package/dist/pricing/contracts/money.js +1 -0
- package/dist/pricing/contracts/offer.d.ts +52 -0
- package/dist/pricing/contracts/offer.js +4 -0
- package/dist/pricing/contracts/offerPolicy.d.ts +16 -0
- package/dist/pricing/contracts/offerPolicy.js +5 -0
- package/dist/pricing/contracts/policy.d.ts +22 -0
- package/dist/pricing/contracts/policy.js +3 -0
- package/dist/pricing/contracts/provenance.d.ts +24 -0
- package/dist/pricing/contracts/provenance.js +2 -0
- package/dist/pricing/contracts/quote.d.ts +94 -0
- package/dist/pricing/contracts/quote.js +1 -0
- package/dist/pricing/dbBackedCommerceQuoteHelpers.d.ts +78 -0
- package/dist/pricing/dbBackedCommerceQuoteHelpers.js +146 -0
- package/dist/pricing/dbBackedCommerceQuotePort.d.ts +66 -0
- package/dist/pricing/dbBackedCommerceQuotePort.js +206 -0
- package/dist/pricing/dbBackedCommerceQuoteSnapshotVerifier.d.ts +4 -0
- package/dist/pricing/dbBackedCommerceQuoteSnapshotVerifier.js +25 -0
- package/dist/pricing/index.d.ts +51 -0
- package/dist/pricing/index.js +36 -0
- package/dist/pricing/offerPolicyReadiness.d.ts +21 -0
- package/dist/pricing/offerPolicyReadiness.js +79 -0
- package/dist/pricing/offerPolicySchemas.d.ts +25 -0
- package/dist/pricing/offerPolicySchemas.js +15 -0
- package/dist/pricing/offerPricingSchemas.d.ts +315 -0
- package/dist/pricing/offerPricingSchemas.js +65 -0
- package/dist/pricing/ports.d.ts +13 -0
- package/dist/pricing/ports.js +9 -0
- package/dist/pricing/pricingPolicyToken.d.ts +39 -0
- package/dist/pricing/pricingPolicyToken.js +84 -0
- package/dist/pricing/quoteAcquisitionTerms.d.ts +2 -0
- package/dist/pricing/quoteAcquisitionTerms.js +4 -0
- package/dist/pricing/quoteCodeRejectionDetails.d.ts +20 -0
- package/dist/pricing/quoteCodeRejectionDetails.js +22 -0
- package/dist/pricing/quotePorts.d.ts +30 -0
- package/dist/pricing/quotePorts.js +20 -0
- package/dist/pricing/quotePrimitives.d.ts +13 -0
- package/dist/pricing/quotePrimitives.js +18 -0
- package/dist/pricing/quotePromotionEvidence.d.ts +39 -0
- package/dist/pricing/quotePromotionEvidence.js +73 -0
- package/dist/pricing/quoteSchemas.d.ts +1176 -0
- package/dist/pricing/quoteSchemas.js +196 -0
- package/dist/pricing/subscriptionPricePolicy.d.ts +52 -0
- package/dist/pricing/subscriptionPricePolicy.js +96 -0
- package/dist/pricing/taxProfileGuard.d.ts +2 -0
- package/dist/pricing/taxProfileGuard.js +24 -0
- package/dist/pricing/validation.d.ts +2 -0
- package/dist/pricing/validation.js +3 -0
- package/dist/promotions/adminBindings.d.ts +72 -0
- package/dist/promotions/adminBindings.js +1 -0
- package/dist/promotions/adminPromotionCodesContracts.d.ts +498 -0
- package/dist/promotions/adminPromotionCodesContracts.js +211 -0
- package/dist/promotions/adminPromotionCodesDataPort.d.ts +47 -0
- package/dist/promotions/adminPromotionCodesDataPort.js +1 -0
- package/dist/promotions/adminPromotionCodesHandler.d.ts +14 -0
- package/dist/promotions/adminPromotionCodesHandler.js +194 -0
- package/dist/promotions/adminPromotionsContracts.d.ts +235 -0
- package/dist/promotions/adminPromotionsContracts.js +99 -0
- package/dist/promotions/adminPromotionsDataPort.d.ts +42 -0
- package/dist/promotions/adminPromotionsDataPort.js +1 -0
- package/dist/promotions/adminPromotionsHandler.d.ts +16 -0
- package/dist/promotions/adminPromotionsHandler.js +47 -0
- package/dist/promotions/contracts/index.d.ts +387 -0
- package/dist/promotions/contracts/index.js +1 -0
- package/dist/promotions/index.d.ts +22 -0
- package/dist/promotions/index.js +21 -0
- package/dist/promotions/promoDataPort.d.ts +47 -0
- package/dist/promotions/promoDataPort.js +14 -0
- package/dist/promotions/promotionAcceptanceTelemetry.d.ts +29 -0
- package/dist/promotions/promotionAcceptanceTelemetry.js +45 -0
- package/dist/promotions/promotionBenefitDefinition.d.ts +37 -0
- package/dist/promotions/promotionBenefitDefinition.js +33 -0
- package/dist/promotions/promotionClaimSweepPort.d.ts +14 -0
- package/dist/promotions/promotionClaimSweepPort.js +8 -0
- package/dist/promotions/promotionClaims.d.ts +5 -0
- package/dist/promotions/promotionClaims.js +57 -0
- package/dist/promotions/promotionCodePreview.d.ts +11 -0
- package/dist/promotions/promotionCodePreview.js +154 -0
- package/dist/promotions/promotionCodeQuotePort.d.ts +25 -0
- package/dist/promotions/promotionCodeQuotePort.js +1 -0
- package/dist/promotions/promotionQuoteAcceptance.d.ts +65 -0
- package/dist/promotions/promotionQuoteAcceptance.js +202 -0
- package/dist/promotions/promotionQuoteBinding.d.ts +71 -0
- package/dist/promotions/promotionQuoteBinding.js +85 -0
- package/dist/promotions/quoteAutomaticPromotions.d.ts +24 -0
- package/dist/promotions/quoteAutomaticPromotions.js +36 -0
- package/dist/promotions/quotePromoDiscounts.d.ts +32 -0
- package/dist/promotions/quotePromoDiscounts.js +140 -0
- package/dist/promotions/quotePromotionCodeAdjustments.d.ts +27 -0
- package/dist/promotions/quotePromotionCodeAdjustments.js +223 -0
- package/dist/promotions/quotePromotionV2.d.ts +29 -0
- package/dist/promotions/quotePromotionV2.js +174 -0
- package/dist/promotions/sql.d.ts +14 -0
- package/dist/promotions/sql.js +5 -0
- package/dist/promotions/validation.d.ts +2 -0
- package/dist/promotions/validation.js +3 -0
- package/dist/shipping/addressCanonLookupHandler.d.ts +7 -0
- package/dist/shipping/addressCanonLookupHandler.js +105 -0
- package/dist/shipping/addressCanonical.d.ts +2 -0
- package/dist/shipping/addressCanonical.js +26 -0
- package/dist/shipping/addressPorts.d.ts +39 -0
- package/dist/shipping/addressPorts.js +1 -0
- package/dist/shipping/addressSchemas.d.ts +30 -0
- package/dist/shipping/addressSchemas.js +1 -0
- package/dist/shipping/adminShippingRateHandler.d.ts +4 -0
- package/dist/shipping/adminShippingRateHandler.js +64 -0
- package/dist/shipping/contracts/address.d.ts +144 -0
- package/dist/shipping/contracts/address.js +1 -0
- package/dist/shipping/contracts/delivery.d.ts +110 -0
- package/dist/shipping/contracts/delivery.js +1 -0
- package/dist/shipping/contracts/index.d.ts +3 -0
- package/dist/shipping/contracts/index.js +1 -0
- package/dist/shipping/contracts/shippingRate.d.ts +11 -0
- package/dist/shipping/contracts/shippingRate.js +1 -0
- package/dist/shipping/customerAddressesHandler.d.ts +5 -0
- package/dist/shipping/customerAddressesHandler.js +80 -0
- package/dist/shipping/customerSelfServiceAddressModels.d.ts +27 -0
- package/dist/shipping/customerSelfServiceAddressModels.js +101 -0
- package/dist/shipping/deliverySelectionHandler.d.ts +10 -0
- package/dist/shipping/deliverySelectionHandler.js +77 -0
- package/dist/shipping/deliverySelectionPort.d.ts +8 -0
- package/dist/shipping/deliverySelectionPort.js +1 -0
- package/dist/shipping/deliverySelectionSummary.d.ts +2 -0
- package/dist/shipping/deliverySelectionSummary.js +53 -0
- package/dist/shipping/handlerSchemas.d.ts +21 -0
- package/dist/shipping/handlerSchemas.js +1 -0
- package/dist/shipping/index.d.ts +17 -0
- package/dist/shipping/index.js +8 -0
- package/dist/shipping/pickupPointSearchPort.d.ts +4 -0
- package/dist/shipping/pickupPointSearchPort.js +1 -0
- package/dist/shipping/readDeliverySelectionEvidence.d.ts +2 -0
- package/dist/shipping/readDeliverySelectionEvidence.js +44 -0
- package/dist/shipping/shippingRatePorts.d.ts +52 -0
- package/dist/shipping/shippingRatePorts.js +1 -0
- package/package.json +72 -2
- package/release-gates.json +249 -8
- package/sql/20260807140000_read_parity_catalog.sql +94 -0
- package/sql/20260807190000_comms_outbox_rail.sql +329 -0
- package/sql/20260810120000_storefront_catalog_attributes.sql +51 -0
- package/sql/20260810120001_transactional_delivery_receipts.sql +209 -0
- package/sql/20260812140000_sellable_bundle_catalog.sql +124 -0
- package/sql/20260812210000_bundle_operator_write_boundary.sql +567 -0
- package/sql/20260813120000_communications_control_plane.sql +568 -0
- package/sql/20260814000000_fulfillment_stock_evidence.sql +239 -0
- package/sql/20260815120000_automatic_subscription_renewal.sql +1083 -0
- package/sql/20260817180000_promotion_claim_lifecycle.sql +591 -0
- package/sql/20260817180100_promotion_claim_lifecycle_validation.sql +23 -0
- package/sql/20260827220000_catalog_document_revision_foundation.sql +226 -0
- package/sql/20260827220001_catalog_document_revision_foundation_validate.sql +10 -0
- package/sql/20260908100000_catalog_draft_foundation.sql +217 -0
- package/sql/20261019200000_communications_native_operations.sql +68 -0
- package/src/catalog/activeSkuSelection.ts +20 -0
- package/src/catalog/adminBindings.ts +59 -0
- package/src/catalog/adminBundleHandler.ts +165 -0
- package/src/catalog/adminBundleReadHandler.ts +261 -0
- package/src/catalog/adminBundleWritePort.ts +102 -0
- package/src/catalog/bundleAvailability.ts +129 -0
- package/src/catalog/bundleAvailabilityService.ts +171 -0
- package/src/catalog/bundleBindings.ts +29 -0
- package/src/catalog/bundleCatalogQueries.ts +98 -0
- package/src/catalog/bundleCatalogReadPort.ts +177 -0
- package/src/catalog/bundleCatalogStore.ts +269 -0
- package/src/catalog/bundleRuleCodes.ts +134 -0
- package/src/catalog/bundleRules.ts +153 -0
- package/src/catalog/catalogDraftStore.ts +71 -0
- package/src/catalog/catalogSkuEnvelope.ts +214 -0
- package/src/catalog/catalogSkuEnvelopeMapper.ts +160 -0
- package/src/catalog/contracts/bundleAdmin.ts +20 -0
- package/src/catalog/contracts/bundleAvailability.ts +5 -0
- package/src/catalog/contracts/bundleRead.ts +13 -0
- package/src/catalog/contracts/draft.ts +69 -0
- package/src/catalog/contracts/foundation.ts +104 -0
- package/src/catalog/contracts/index.ts +14 -0
- package/src/catalog/contracts/lifecycle.ts +74 -0
- package/src/catalog/contracts/productMediaContracts.ts +170 -0
- package/src/catalog/contracts/productTypes.ts +35 -0
- package/src/catalog/contracts/resolveProductMedia.ts +85 -0
- package/src/catalog/contracts/sellable.ts +12 -0
- package/src/catalog/draftHandler.ts +105 -0
- package/src/catalog/draftPorts.ts +18 -0
- package/src/catalog/draftSchemas.ts +88 -0
- package/src/catalog/draftValidation.ts +100 -0
- package/src/catalog/foundationSchemas.ts +123 -0
- package/src/catalog/index.ts +43 -0
- package/src/catalog/mediaSourcePack.ts +219 -0
- package/src/catalog/productTypeSchemas.ts +70 -0
- package/src/catalog/publicationPorts.ts +44 -0
- package/src/catalog/readPorts.ts +61 -0
- package/src/catalog/referenceCatalogHandler.ts +39 -0
- package/src/catalog/resolvedSkuProjection.ts +76 -0
- package/src/catalog/sellableBundleHandler.ts +133 -0
- package/src/catalog/sellableSchemas.ts +33 -0
- package/src/catalog/sql.ts +14 -0
- package/src/catalog/validation.ts +3 -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 +52 -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/marketingSync.ts +109 -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 +47 -0
- package/src/communications/integrationEventSignature.ts +49 -0
- package/src/communications/klaviyo/consentPush.ts +19 -0
- package/src/communications/klaviyo/index.ts +649 -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/resend/index.ts +350 -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/composition/catalogContribution.ts +32 -0
- package/src/composition/index.ts +1 -0
- package/src/http/index.ts +1 -0
- package/src/http/webhookSignature.ts +96 -0
- package/src/inventory/adminInventory.ts +251 -0
- package/src/inventory/contracts/forecast.ts +96 -0
- package/src/inventory/contracts/index.ts +18 -0
- package/src/inventory/contracts/inventory.ts +14 -0
- package/src/inventory/contracts/newPurchaseStock.ts +23 -0
- package/src/inventory/contracts/reservation.ts +20 -0
- package/src/inventory/forecast.ts +239 -0
- package/src/inventory/index.ts +21 -0
- package/src/inventory/inventoryHandlers.ts +215 -0
- package/src/inventory/newPurchaseStockAvailability.ts +120 -0
- package/src/inventory/ports.ts +46 -0
- package/src/inventory/renewalReservation.ts +88 -0
- package/src/inventory/reservationPolicy.ts +27 -0
- package/src/inventory/schemas.ts +128 -0
- package/src/inventory/sql.ts +5 -0
- package/src/inventory/subscriptionForecastCalculation.ts +157 -0
- package/src/pricing/acquisitionFinancialPolicy.ts +26 -0
- package/src/pricing/acquisitionOfferPricing.ts +38 -0
- package/src/pricing/acquisitionOfferPricingHandler.ts +157 -0
- package/src/pricing/acquisitionOfferPricingSchemas.ts +35 -0
- package/src/pricing/acquisitionQuoteBinding.ts +12 -0
- package/src/pricing/acquisitionReadiness.ts +148 -0
- package/src/pricing/acquisitionTerms.ts +125 -0
- package/src/pricing/acquisitionTermsSchemas.ts +52 -0
- package/src/pricing/catalogFactsProvenance.ts +102 -0
- package/src/pricing/catalogFactsSchemas.ts +69 -0
- package/src/pricing/commerceOfferPricing.ts +224 -0
- package/src/pricing/commerceOfferPricingCache.ts +53 -0
- package/src/pricing/commerceOfferPricingHandler.ts +137 -0
- package/src/pricing/commercePriceAuthority.ts +104 -0
- package/src/pricing/commercePricingPolicyErrors.ts +88 -0
- package/src/pricing/commerceQuoteBatchHandler.ts +129 -0
- package/src/pricing/commerceQuoteCatalogReadPort.ts +42 -0
- package/src/pricing/commerceQuoteHandler.ts +184 -0
- package/src/pricing/commerceQuoteSnapshotExpect.ts +48 -0
- package/src/pricing/contracts/acquisitionTerms.ts +26 -0
- package/src/pricing/contracts/authority.ts +44 -0
- package/src/pricing/contracts/evidence.ts +25 -0
- package/src/pricing/contracts/index.ts +17 -0
- package/src/pricing/contracts/money.ts +32 -0
- package/src/pricing/contracts/offer.ts +42 -0
- package/src/pricing/contracts/offerPolicy.ts +17 -0
- package/src/pricing/contracts/policy.ts +26 -0
- package/src/pricing/contracts/provenance.ts +19 -0
- package/src/pricing/contracts/quote.ts +106 -0
- package/src/pricing/dbBackedCommerceQuoteHelpers.ts +256 -0
- package/src/pricing/dbBackedCommerceQuotePort.ts +260 -0
- package/src/pricing/dbBackedCommerceQuoteSnapshotVerifier.ts +49 -0
- package/src/pricing/index.ts +52 -0
- package/src/pricing/offerPolicyReadiness.ts +110 -0
- package/src/pricing/offerPolicySchemas.ts +23 -0
- package/src/pricing/offerPricingSchemas.ts +92 -0
- package/src/pricing/ports.ts +17 -0
- package/src/pricing/pricingPolicyToken.ts +119 -0
- package/src/pricing/quoteAcquisitionTerms.ts +10 -0
- package/src/pricing/quoteCodeRejectionDetails.ts +23 -0
- package/src/pricing/quotePorts.ts +58 -0
- package/src/pricing/quotePrimitives.ts +21 -0
- package/src/pricing/quotePromotionEvidence.ts +82 -0
- package/src/pricing/quoteSchemas.ts +230 -0
- package/src/pricing/subscriptionPricePolicy.ts +113 -0
- package/src/pricing/taxProfileGuard.ts +33 -0
- package/src/pricing/validation.ts +3 -0
- package/src/promotions/adminBindings.ts +53 -0
- package/src/promotions/adminPromotionCodesContracts.ts +244 -0
- package/src/promotions/adminPromotionCodesDataPort.ts +56 -0
- package/src/promotions/adminPromotionCodesHandler.ts +254 -0
- package/src/promotions/adminPromotionsContracts.ts +121 -0
- package/src/promotions/adminPromotionsDataPort.ts +48 -0
- package/src/promotions/adminPromotionsHandler.ts +72 -0
- package/src/promotions/contracts/index.ts +387 -0
- package/src/promotions/index.ts +22 -0
- package/src/promotions/promoDataPort.ts +65 -0
- package/src/promotions/promotionAcceptanceTelemetry.ts +73 -0
- package/src/promotions/promotionBenefitDefinition.ts +48 -0
- package/src/promotions/promotionClaimSweepPort.ts +23 -0
- package/src/promotions/promotionClaims.ts +70 -0
- package/src/promotions/promotionCodePreview.ts +183 -0
- package/src/promotions/promotionCodeQuotePort.ts +35 -0
- package/src/promotions/promotionQuoteAcceptance.ts +284 -0
- package/src/promotions/promotionQuoteBinding.ts +158 -0
- package/src/promotions/quoteAutomaticPromotions.ts +58 -0
- package/src/promotions/quotePromoDiscounts.ts +185 -0
- package/src/promotions/quotePromotionCodeAdjustments.ts +277 -0
- package/src/promotions/quotePromotionV2.ts +223 -0
- package/src/promotions/sql.ts +5 -0
- package/src/promotions/validation.ts +3 -0
- package/src/shipping/addressCanonLookupHandler.ts +114 -0
- package/src/shipping/addressCanonical.ts +31 -0
- package/src/shipping/addressPorts.ts +48 -0
- package/src/shipping/addressSchemas.ts +28 -0
- package/src/shipping/adminShippingRateHandler.ts +72 -0
- package/src/shipping/contracts/address.ts +71 -0
- package/src/shipping/contracts/delivery.ts +114 -0
- package/src/shipping/contracts/index.ts +18 -0
- package/src/shipping/contracts/shippingRate.ts +7 -0
- package/src/shipping/customerAddressesHandler.ts +100 -0
- package/src/shipping/customerSelfServiceAddressModels.ts +124 -0
- package/src/shipping/deliverySelectionHandler.ts +94 -0
- package/src/shipping/deliverySelectionPort.ts +10 -0
- package/src/shipping/deliverySelectionSummary.ts +56 -0
- package/src/shipping/handlerSchemas.ts +19 -0
- package/src/shipping/index.ts +23 -0
- package/src/shipping/pickupPointSearchPort.ts +8 -0
- package/src/shipping/readDeliverySelectionEvidence.ts +51 -0
- package/src/shipping/shippingRatePorts.ts +36 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { acquisitionQuoteBinding } from "../pricing/index.js";
|
|
2
|
+
/** The five sections the token hashes separately so a mismatch can name itself. */
|
|
3
|
+
export const PROMOTION_QUOTE_MONEY_SECTIONS = [
|
|
4
|
+
"lines",
|
|
5
|
+
"discounts",
|
|
6
|
+
"shipping",
|
|
7
|
+
"totals",
|
|
8
|
+
"currency",
|
|
9
|
+
];
|
|
10
|
+
/** `other` is the honest answer for a token minted before section bindings existed. */
|
|
11
|
+
export const PROMOTION_QUOTE_MISMATCH_FIELDS = [
|
|
12
|
+
...PROMOTION_QUOTE_MONEY_SECTIONS,
|
|
13
|
+
"other",
|
|
14
|
+
];
|
|
15
|
+
/**
|
|
16
|
+
* Discount order is preserved: the promotion engine emits its lanes
|
|
17
|
+
* deterministically, so a reordering is a real evaluation difference.
|
|
18
|
+
*/
|
|
19
|
+
export function projectPromotionQuoteMoney(quote, acquisitionTerms) {
|
|
20
|
+
return {
|
|
21
|
+
...acquisitionQuoteBinding(acquisitionTerms),
|
|
22
|
+
currency: quote.currency,
|
|
23
|
+
totalGross: quote.totalGross,
|
|
24
|
+
netTotal: quote.netTotal,
|
|
25
|
+
taxTotal: quote.taxTotal,
|
|
26
|
+
subtotalGross: quote.subtotalGross,
|
|
27
|
+
discountTotalGross: quote.discountTotalGross,
|
|
28
|
+
shippingGross: quote.shippingGross ?? null,
|
|
29
|
+
shippingDiscountGross: quote.shippingDiscountGross ?? null,
|
|
30
|
+
lines: quote.lines.map((line) => ({
|
|
31
|
+
sku: line.sku,
|
|
32
|
+
quantity: line.quantity,
|
|
33
|
+
unitPriceGross: line.unitPriceGross,
|
|
34
|
+
lineSubtotalGross: line.lineSubtotalGross,
|
|
35
|
+
tax: {
|
|
36
|
+
vatRateBps: line.tax.vatRateBps,
|
|
37
|
+
netAmount: line.tax.netAmount,
|
|
38
|
+
vatAmount: line.tax.vatAmount,
|
|
39
|
+
grossAmount: line.tax.grossAmount,
|
|
40
|
+
},
|
|
41
|
+
})),
|
|
42
|
+
discounts: quote.discounts.map((discount) => ({
|
|
43
|
+
promotionId: discount.promotionId,
|
|
44
|
+
appliesTo: discount.appliesTo,
|
|
45
|
+
amountOffMinor: discount.amountOffMinor,
|
|
46
|
+
promotionCodeId: discount.promotionCodeId ?? null,
|
|
47
|
+
promotionDefinitionFingerprint: discount.promotionDefinitionFingerprint ?? null,
|
|
48
|
+
promotionBenefitKind: discount.promotionBenefitKind ?? null,
|
|
49
|
+
promotionBenefitValueBps: discount.promotionBenefitValueBps ?? null,
|
|
50
|
+
promotionBenefitValueMinor: discount.promotionBenefitValueMinor ?? null,
|
|
51
|
+
})),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** Same projection, partitioned. The union of these five sections is the whole projection. */
|
|
55
|
+
export function promotionQuoteMoneySections(quote, acquisitionTerms) {
|
|
56
|
+
const money = projectPromotionQuoteMoney(quote, acquisitionTerms);
|
|
57
|
+
return {
|
|
58
|
+
lines: money.lines,
|
|
59
|
+
discounts: money.discounts,
|
|
60
|
+
shipping: {
|
|
61
|
+
shippingGross: money.shippingGross,
|
|
62
|
+
shippingDiscountGross: money.shippingDiscountGross,
|
|
63
|
+
},
|
|
64
|
+
totals: {
|
|
65
|
+
...acquisitionQuoteBinding(money.acquisitionTerms),
|
|
66
|
+
totalGross: money.totalGross,
|
|
67
|
+
netTotal: money.netTotal,
|
|
68
|
+
taxTotal: money.taxTotal,
|
|
69
|
+
subtotalGross: money.subtotalGross,
|
|
70
|
+
discountTotalGross: money.discountTotalGross,
|
|
71
|
+
},
|
|
72
|
+
currency: money.currency,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/** Key-sorted, `undefined`-dropping JSON. Every acceptance binding hashes this shape. */
|
|
76
|
+
export function stableJson(value) {
|
|
77
|
+
if (Array.isArray(value))
|
|
78
|
+
return `[${value.map(stableJson).join(",")}]`;
|
|
79
|
+
if (value && typeof value === "object") {
|
|
80
|
+
return `{${Object.entries(value).filter(([, item]) => item !== undefined)
|
|
81
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
82
|
+
.map(([key, item]) => `${JSON.stringify(key)}:${stableJson(item)}`).join(",")}}`;
|
|
83
|
+
}
|
|
84
|
+
return JSON.stringify(value);
|
|
85
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { CreateQuoteRequest } from "../pricing/index.js";
|
|
2
|
+
import type { CreateQuoteOptions } from "../pricing/index.js";
|
|
3
|
+
import type { CommercePromoDataPort } from "./promoDataPort.js";
|
|
4
|
+
import type { createQuotePromoDiscounts, QuoteDiscountResult } from "./quotePromoDiscounts.js";
|
|
5
|
+
import type { createQuotePromotionV2 } from "./quotePromotionV2.js";
|
|
6
|
+
export declare function createQuoteAutomaticPromotions<Request extends CreateQuoteRequest & {
|
|
7
|
+
visitorId?: string;
|
|
8
|
+
}>({ resolveEmailEligibility, evaluateOrderTotalDiscounts, evaluateAutomaticPromotionV2, legacyPromotionDataPort }: {
|
|
9
|
+
resolveEmailEligibility(request: Request): boolean;
|
|
10
|
+
evaluateOrderTotalDiscounts: ReturnType<typeof createQuotePromoDiscounts>["evaluateOrderTotalDiscounts"];
|
|
11
|
+
evaluateAutomaticPromotionV2: ReturnType<typeof createQuotePromotionV2>["evaluateAutomaticPromotionV2"];
|
|
12
|
+
legacyPromotionDataPort: ReturnType<typeof createQuotePromotionV2>["legacyPromotionDataPort"];
|
|
13
|
+
}): {
|
|
14
|
+
resolveAutomaticQuotePromotions: (input: {
|
|
15
|
+
promoDataPort?: CommercePromoDataPort;
|
|
16
|
+
request: Request;
|
|
17
|
+
options?: CreateQuoteOptions;
|
|
18
|
+
regionCode: string;
|
|
19
|
+
referenceProductMinor: number;
|
|
20
|
+
subtotalGrossMinor: number;
|
|
21
|
+
shippingGrossMinor: number;
|
|
22
|
+
atTime: string;
|
|
23
|
+
}) => Promise<QuoteDiscountResult>;
|
|
24
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { PROMOTION_ENGINE_V2 } from "../pricing/index.js";
|
|
2
|
+
export function createQuoteAutomaticPromotions({ resolveEmailEligibility, evaluateOrderTotalDiscounts, evaluateAutomaticPromotionV2, legacyPromotionDataPort }) {
|
|
3
|
+
async function resolveAutomaticQuotePromotions(input) {
|
|
4
|
+
if (!input.promoDataPort)
|
|
5
|
+
return { discounts: [], codeRejections: [] };
|
|
6
|
+
const emailEligibilityConfirmed = resolveEmailEligibility(input.request);
|
|
7
|
+
if (input.options?.pricingPolicy?.promotionEngineVersion === PROMOTION_ENGINE_V2) {
|
|
8
|
+
return evaluateAutomaticPromotionV2({
|
|
9
|
+
promoDataPort: input.promoDataPort,
|
|
10
|
+
clientId: input.options.clientId ?? null,
|
|
11
|
+
visitorId: input.request.visitorId ?? null,
|
|
12
|
+
mode: input.request.mode,
|
|
13
|
+
promoCodes: input.request.promoCodes,
|
|
14
|
+
regionCode: input.regionCode,
|
|
15
|
+
referenceProductMinor: input.referenceProductMinor,
|
|
16
|
+
currentProductMinor: input.subtotalGrossMinor,
|
|
17
|
+
shippingMinor: input.shippingGrossMinor,
|
|
18
|
+
atTime: input.atTime,
|
|
19
|
+
emailEligibilityConfirmed,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return evaluateOrderTotalDiscounts({
|
|
23
|
+
promoDataPort: legacyPromotionDataPort(input.promoDataPort),
|
|
24
|
+
clientId: input.options?.clientId ?? null,
|
|
25
|
+
visitorId: input.request.visitorId ?? null,
|
|
26
|
+
mode: input.request.mode,
|
|
27
|
+
promoCodes: input.request.promoCodes,
|
|
28
|
+
regionCode: input.regionCode,
|
|
29
|
+
subtotalGrossMinor: input.subtotalGrossMinor,
|
|
30
|
+
shippingGrossMinor: input.shippingGrossMinor,
|
|
31
|
+
atTime: input.atTime,
|
|
32
|
+
emailEligibilityConfirmed,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return { resolveAutomaticQuotePromotions };
|
|
36
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { CreateQuoteRequest } from "../pricing/index.js";
|
|
2
|
+
import type { CommerceCodeRejection, CommerceQuoteDiscount } from "../pricing/index.js";
|
|
3
|
+
import type { PromotionRow } from "@openlup/core/promo";
|
|
4
|
+
import type { CommercePromoDataPort } from "./promoDataPort.js";
|
|
5
|
+
export interface QuoteDiscountResult {
|
|
6
|
+
/** Applied discounts: order_total (clamped to subtotal) + at most one shipping discount. */
|
|
7
|
+
discounts: CommerceQuoteDiscount[];
|
|
8
|
+
/** Submitted coupon codes that produced no discount, with the reason. */
|
|
9
|
+
codeRejections: CommerceCodeRejection[];
|
|
10
|
+
}
|
|
11
|
+
export declare function createQuotePromoDiscounts({ promotionCustomerSemantic }: {
|
|
12
|
+
promotionCustomerSemantic: (row: PromotionRow) => CommerceQuoteDiscount["customerSemantic"];
|
|
13
|
+
}): {
|
|
14
|
+
evaluateOrderTotalDiscounts: (input: {
|
|
15
|
+
promoDataPort: CommercePromoDataPort;
|
|
16
|
+
clientId: string | null;
|
|
17
|
+
visitorId?: string | null;
|
|
18
|
+
mode: CreateQuoteRequest["mode"];
|
|
19
|
+
promoCodes: string[];
|
|
20
|
+
regionCode: string;
|
|
21
|
+
subtotalGrossMinor: number;
|
|
22
|
+
shippingGrossMinor?: number;
|
|
23
|
+
atTime: string;
|
|
24
|
+
/**
|
|
25
|
+
* True when a customer email drove eligibility resolution (the request carried
|
|
26
|
+
* `customerEligibilityContext.email`). Demotes the device-redemption guard to
|
|
27
|
+
* advisory so an email-eligible customer on a used device still gets the
|
|
28
|
+
* first-order price. See {@link PromoEvaluationCart.email_eligibility_confirmed}.
|
|
29
|
+
*/
|
|
30
|
+
emailEligibilityConfirmed?: boolean;
|
|
31
|
+
}) => Promise<QuoteDiscountResult>;
|
|
32
|
+
};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { evaluatePromos, promoEligibilityFailure } from "@openlup/core/promo";
|
|
2
|
+
export function createQuotePromoDiscounts({ promotionCustomerSemantic }) {
|
|
3
|
+
/**
|
|
4
|
+
* Evaluate active promotions for this cart and map them to quote discounts. Two lanes:
|
|
5
|
+
* `order_total` promos are clamped to the subtotal (so `discountTotal <= subtotal`), and
|
|
6
|
+
* a single `free_shipping` promo is emitted as an `appliesTo:"shipping"` discount clamped
|
|
7
|
+
* to `shippingGrossMinor` — it is NOT suppressed by an exclusive order-total promo and is
|
|
8
|
+
* NOT counted against the subtotal. Per-mode paid-order counts drive `first_subscription_purchase`
|
|
9
|
+
* and `first_onetime_purchase` independently; all counts are server-authoritative (resolved
|
|
10
|
+
* from the clientId, never client input), so eligibility cannot be forged.
|
|
11
|
+
*/
|
|
12
|
+
async function evaluateOrderTotalDiscounts(input) {
|
|
13
|
+
if (input.subtotalGrossMinor <= 0)
|
|
14
|
+
return { discounts: [], codeRejections: [] };
|
|
15
|
+
const byMode = input.clientId
|
|
16
|
+
? await input.promoDataPort.countPaidOrdersByMode(input.clientId)
|
|
17
|
+
: { oneTime: 0, subscription: 0 };
|
|
18
|
+
// Cap pre-filter: drop a promo that has reached its global or per-customer redemption
|
|
19
|
+
// limit BEFORE evaluation, so caps are enforced at quote time and a paid order is never
|
|
20
|
+
// failed on a cap. A per-customer cap can only be checked for an authenticated client.
|
|
21
|
+
const [allCandidates, redemptionCounts, deviceFirstOrderRedeemed] = await Promise.all([
|
|
22
|
+
input.promoDataPort.listActivePromotions(),
|
|
23
|
+
input.promoDataPort.redemptionCounts(input.clientId),
|
|
24
|
+
input.promoDataPort.deviceFirstOrderRedeemed(input.visitorId ?? null),
|
|
25
|
+
]);
|
|
26
|
+
const candidates = allCandidates.filter((promo) => !capReached(promo, redemptionCounts.get(promo.id), input.clientId));
|
|
27
|
+
const cart = {
|
|
28
|
+
region_code: input.regionCode,
|
|
29
|
+
cart_mode: input.mode === "subscription" ? "subscription" : "one_time",
|
|
30
|
+
cart_subtotal_minor: input.subtotalGrossMinor,
|
|
31
|
+
shipping_amount_minor: input.shippingGrossMinor ?? 0,
|
|
32
|
+
client_orders_count: byMode.oneTime + byMode.subscription,
|
|
33
|
+
client_subscription_orders_count: byMode.subscription,
|
|
34
|
+
client_onetime_orders_count: byMode.oneTime,
|
|
35
|
+
device_first_order_redeemed: deviceFirstOrderRedeemed,
|
|
36
|
+
email_eligibility_confirmed: input.emailEligibilityConfirmed === true,
|
|
37
|
+
// The engine compares code literals. Preserve the customer's submitted spelling
|
|
38
|
+
// for the public rejection, but feed known legacy codes in their canonical
|
|
39
|
+
// spelling so eligibility and applied-discount matching are case-insensitive.
|
|
40
|
+
applied_codes: canonicalSubmittedCodes(input.promoCodes, allCandidates),
|
|
41
|
+
};
|
|
42
|
+
const applied = evaluatePromos({ cart, candidates, now: input.atTime });
|
|
43
|
+
const discounts = [];
|
|
44
|
+
let running = 0;
|
|
45
|
+
for (const promo of applied) {
|
|
46
|
+
const source = candidates.find((candidate) => candidate.id === promo.promotion_id);
|
|
47
|
+
const customerSemantic = source ? promotionCustomerSemantic(source) : undefined;
|
|
48
|
+
const isShipping = promo.discount_type === "free_shipping";
|
|
49
|
+
const cap = isShipping ? cart.shipping_amount_minor : input.subtotalGrossMinor - running;
|
|
50
|
+
if (cap <= 0) {
|
|
51
|
+
if (isShipping)
|
|
52
|
+
continue;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
const amountOffMinor = Math.min(promo.amount_off_minor, cap);
|
|
56
|
+
if (amountOffMinor <= 0)
|
|
57
|
+
continue;
|
|
58
|
+
if (!isShipping)
|
|
59
|
+
running += amountOffMinor;
|
|
60
|
+
discounts.push({
|
|
61
|
+
promotionId: promo.promotion_id,
|
|
62
|
+
...(promo.code ? { code: promo.code } : {}),
|
|
63
|
+
// Human-facing label preserves the promotion name instead of its reason code.
|
|
64
|
+
...(promo.name ? { label: promo.name } : {}),
|
|
65
|
+
appliesTo: isShipping ? "shipping" : "order_total",
|
|
66
|
+
amountOffMinor,
|
|
67
|
+
reasonCode: promo.reason_code,
|
|
68
|
+
...(customerSemantic ? { customerSemantic } : {}),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
discounts,
|
|
73
|
+
codeRejections: classifyRejections(input, cart, allCandidates, redemptionCounts, discounts),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* For each submitted coupon code that produced no discount, classify why. Uses the
|
|
78
|
+
* pre-cap candidate set so an exhausted per-customer cap reads as `already_used` rather
|
|
79
|
+
* than `not_recognized`. An eligible code that was merely outranked by a larger promo
|
|
80
|
+
* is a neutral `better_price_exists`: it was not applied because the customer already
|
|
81
|
+
* has an equally good or better automatic price. Applied shipping-code discounts count
|
|
82
|
+
* as applied too.
|
|
83
|
+
*/
|
|
84
|
+
function classifyRejections(input, cart, allCandidates, redemptionCounts, discounts) {
|
|
85
|
+
const rejections = [];
|
|
86
|
+
for (const code of input.promoCodes) {
|
|
87
|
+
const promo = resolveSubmittedCoupon(code, allCandidates);
|
|
88
|
+
if (!promo) {
|
|
89
|
+
rejections.push({ code, reason: "not_recognized" });
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
// A legacy literal may be bound to more than one promotion/lane. A shipping
|
|
93
|
+
// benefit can legitimately apply even when the first matching product row
|
|
94
|
+
// fails scope, so the visible applied discount always wins over rejection.
|
|
95
|
+
if (discounts.some((discount) => sameCode(discount.code, code) || sameCode(discount.code, promo.code))) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (capReached(promo, redemptionCounts.get(promo.id), input.clientId)) {
|
|
99
|
+
rejections.push({ code, reason: "already_used" });
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const failure = promoEligibilityFailure(promo, cart, input.atTime);
|
|
103
|
+
if (failure)
|
|
104
|
+
rejections.push({ code, reason: failure });
|
|
105
|
+
else if (!discounts.some((discount) => discount.code === promo.code)) {
|
|
106
|
+
rejections.push({ code, reason: "better_price_exists" });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return rejections;
|
|
110
|
+
}
|
|
111
|
+
function canonicalSubmittedCodes(codes, candidates) {
|
|
112
|
+
return codes.map((code) => resolveSubmittedCoupon(code, candidates)?.code ?? code);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Legacy permits normalized-code collisions. Preserve its exact-literal
|
|
116
|
+
* matching first; case-insensitive recovery is safe only for one identity.
|
|
117
|
+
*/
|
|
118
|
+
function resolveSubmittedCoupon(code, candidates) {
|
|
119
|
+
const couponCandidates = candidates.filter((promo) => promo.trigger_type === "coupon_code" && promo.code !== null);
|
|
120
|
+
const exact = couponCandidates.find((promo) => promo.code === code);
|
|
121
|
+
if (exact)
|
|
122
|
+
return exact;
|
|
123
|
+
const normalized = couponCandidates.filter((promo) => sameCode(promo.code, code));
|
|
124
|
+
return normalized.length === 1 ? normalized[0] : undefined;
|
|
125
|
+
}
|
|
126
|
+
function sameCode(left, right) {
|
|
127
|
+
return typeof left === "string" && typeof right === "string" &&
|
|
128
|
+
left.trim().toUpperCase() === right.trim().toUpperCase();
|
|
129
|
+
}
|
|
130
|
+
function capReached(promo, counts, clientId) {
|
|
131
|
+
const { global, perCustomer } = counts ?? { global: 0, perCustomer: 0 };
|
|
132
|
+
if (promo.redemption_limit_global != null && global >= promo.redemption_limit_global)
|
|
133
|
+
return true;
|
|
134
|
+
if (clientId && promo.redemption_limit_per_customer != null && perCustomer >= promo.redemption_limit_per_customer) {
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
return { evaluateOrderTotalDiscounts };
|
|
140
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { PromotionCodeRejectionReason } from "@openlup/core/promo";
|
|
2
|
+
import type { CommerceCodeRejection, CommerceCodeRejectionDetail, CommerceCodeRejectionReason, CommerceQuoteDiscount } from "../pricing/index.js";
|
|
3
|
+
import type { PromotionCodeQuotePort } from "./promotionCodeQuotePort.js";
|
|
4
|
+
export interface ApplyPromotionCodesV2Input {
|
|
5
|
+
port?: PromotionCodeQuotePort;
|
|
6
|
+
preserveLegacyDiscountsWhenNoCandidates?: boolean;
|
|
7
|
+
clientId: string | null;
|
|
8
|
+
mode: "one_time" | "subscription";
|
|
9
|
+
promoCodes: readonly string[];
|
|
10
|
+
referenceProductMinor: number;
|
|
11
|
+
subtotalGrossMinor: number;
|
|
12
|
+
shippingGrossMinor: number;
|
|
13
|
+
discounts: CommerceQuoteDiscount[];
|
|
14
|
+
codeRejections: CommerceCodeRejection[];
|
|
15
|
+
codeRejectionDetails?: CommerceCodeRejectionDetail[];
|
|
16
|
+
atTime: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function createQuotePromotionCodeAdjustments({ minimumProductPayableMinor: MINIMUM_PRODUCT_PAYABLE_MINOR }: {
|
|
19
|
+
minimumProductPayableMinor: number;
|
|
20
|
+
}): {
|
|
21
|
+
applyPromotionCodesV2: (input: ApplyPromotionCodesV2Input) => Promise<{
|
|
22
|
+
discounts: CommerceQuoteDiscount[];
|
|
23
|
+
codeRejections: CommerceCodeRejection[];
|
|
24
|
+
codeRejectionDetails: CommerceCodeRejectionDetail[];
|
|
25
|
+
}>;
|
|
26
|
+
mapEngineRejectionReason: (reason: PromotionCodeRejectionReason) => CommerceCodeRejectionReason;
|
|
27
|
+
};
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { evaluatePromotionAdjustmentsV2 } from "@openlup/core/promo";
|
|
2
|
+
export function createQuotePromotionCodeAdjustments({ minimumProductPayableMinor: MINIMUM_PRODUCT_PAYABLE_MINOR }) {
|
|
3
|
+
async function applyPromotionCodesV2(input) {
|
|
4
|
+
const submittedV2Codes = firstSubmittedCodes(input.promoCodes);
|
|
5
|
+
if (!input.port || submittedV2Codes.length === 0) {
|
|
6
|
+
return {
|
|
7
|
+
discounts: input.discounts,
|
|
8
|
+
codeRejections: input.codeRejections,
|
|
9
|
+
codeRejectionDetails: input.codeRejectionDetails ?? [],
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
const firstSubmittedByIdentity = new Map(submittedV2Codes.map((code) => [normalize(code), code]));
|
|
13
|
+
const resolution = await input.port.resolve({
|
|
14
|
+
codes: submittedV2Codes,
|
|
15
|
+
clientId: input.clientId,
|
|
16
|
+
purchaseScope: input.mode === "subscription" ? "subscription_initial" : "one_time",
|
|
17
|
+
referenceProductMinor: input.referenceProductMinor,
|
|
18
|
+
atTime: input.atTime,
|
|
19
|
+
});
|
|
20
|
+
if (resolution.candidates.length === 0) {
|
|
21
|
+
const discounts = input.preserveLegacyDiscountsWhenNoCandidates
|
|
22
|
+
? input.discounts
|
|
23
|
+
: reduceProvisionalLegacyDiscounts(input.discounts, firstSubmittedByIdentity);
|
|
24
|
+
const appliedLegacyCodes = new Set(discounts.flatMap((discount) => discount.code ? [normalize(discount.code)] : []));
|
|
25
|
+
const codeRejections = dedupeRejections([
|
|
26
|
+
...input.codeRejections,
|
|
27
|
+
...resolution.codeRejections,
|
|
28
|
+
], firstSubmittedByIdentity, input.codeRejections.length).filter((rejection) => !appliedLegacyCodes.has(normalize(rejection.code)));
|
|
29
|
+
return {
|
|
30
|
+
discounts: input.preserveLegacyDiscountsWhenNoCandidates
|
|
31
|
+
? discounts
|
|
32
|
+
: orderSubmittedCodeSlots(projectSubmittedCodeSpelling(discounts, firstSubmittedByIdentity), firstSubmittedByIdentity),
|
|
33
|
+
codeRejections,
|
|
34
|
+
codeRejectionDetails: dedupeRejectionDetails([
|
|
35
|
+
...selectedRejectionDetails(input.codeRejectionDetails ?? [], input.codeRejections, codeRejections),
|
|
36
|
+
...selectedRejectionDetails(resolution.codeRejectionDetails, resolution.codeRejections, codeRejections),
|
|
37
|
+
], firstSubmittedByIdentity),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const provisionalLegacyDiscounts = reduceProvisionalLegacyDiscounts(input.discounts, firstSubmittedByIdentity);
|
|
41
|
+
const appliedLegacyCodes = new Set(provisionalLegacyDiscounts.flatMap((discount) => discount.code ? [normalize(discount.code)] : []));
|
|
42
|
+
const result = evaluatePromotionAdjustmentsV2(adjustmentContext(input, provisionalLegacyDiscounts), resolution.candidates);
|
|
43
|
+
const byAdjustmentIdentity = new Map(resolution.candidates.map((candidate) => [
|
|
44
|
+
`${candidate.promotionId}:${candidate.codeId}`,
|
|
45
|
+
candidate,
|
|
46
|
+
]));
|
|
47
|
+
const replacedLegacyLanes = new Set();
|
|
48
|
+
const effectiveAdjustments = result.adjustments.flatMap((adjustment) => {
|
|
49
|
+
const candidate = adjustment.codeId
|
|
50
|
+
? byAdjustmentIdentity.get(`${adjustment.promotionId}:${adjustment.codeId}`)
|
|
51
|
+
: undefined;
|
|
52
|
+
if (!candidate || candidate.source !== "code" || !candidate.code) {
|
|
53
|
+
throw new Error("promotion_code_quote_candidate_identity_missing");
|
|
54
|
+
}
|
|
55
|
+
const appliesTo = adjustment.lane === "shipping" ? "shipping" : "order_total";
|
|
56
|
+
const identity = normalize(candidate.code);
|
|
57
|
+
const matchingLegacy = provisionalLegacyDiscounts.filter((discount) => discount.appliesTo === appliesTo && discount.code && normalize(discount.code) === identity);
|
|
58
|
+
if (matchingLegacy.length === 0)
|
|
59
|
+
return [{ adjustment, candidate }];
|
|
60
|
+
// Re-score without the matching provisional legacy identity and retain the
|
|
61
|
+
// single adjustment that gives the customer the lower lane payable.
|
|
62
|
+
const withoutMatchingLegacy = provisionalLegacyDiscounts.filter((discount) => !(discount.appliesTo === appliesTo && discount.code && normalize(discount.code) === identity));
|
|
63
|
+
const replacement = evaluatePromotionAdjustmentsV2(adjustmentContext(input, withoutMatchingLegacy), [candidate]).adjustments[0];
|
|
64
|
+
const legacyAmount = matchingLegacy.reduce((sum, discount) => sum + discount.amountOffMinor, 0);
|
|
65
|
+
if (!replacement || replacement.amountOffMinor < legacyAmount)
|
|
66
|
+
return [];
|
|
67
|
+
replacedLegacyLanes.add(`${identity}:${appliesTo}`);
|
|
68
|
+
return [{ adjustment: replacement, candidate }];
|
|
69
|
+
});
|
|
70
|
+
const retainedDiscounts = projectSubmittedCodeSpelling(provisionalLegacyDiscounts.filter((discount) => !(discount.code && replacedLegacyLanes.has(`${normalize(discount.code)}:${discount.appliesTo}`))), firstSubmittedByIdentity);
|
|
71
|
+
const v2Discounts = effectiveAdjustments.map(({ adjustment, candidate }) => ({
|
|
72
|
+
promotionId: adjustment.promotionId,
|
|
73
|
+
code: firstSubmittedByIdentity.get(normalize(candidate.code)) ?? candidate.code,
|
|
74
|
+
label: adjustment.name,
|
|
75
|
+
appliesTo: adjustment.lane === "shipping" ? "shipping" : "order_total",
|
|
76
|
+
amountOffMinor: adjustment.amountOffMinor,
|
|
77
|
+
reasonCode: "promotion_code_v2",
|
|
78
|
+
promotionEngineVersion: "promotion-engine.v2",
|
|
79
|
+
promotionCodeId: candidate.codeId,
|
|
80
|
+
promotionCodeRevision: candidate.codeRevision,
|
|
81
|
+
promotionDefinitionFingerprint: candidate.definitionFingerprint,
|
|
82
|
+
promotionCodeScopes: [...candidate.scopes],
|
|
83
|
+
promotionMinimumReferenceMinor: candidate.minimumReferenceMinor,
|
|
84
|
+
promotionCodeValidTo: candidate.validTo,
|
|
85
|
+
promotionBenefitKind: candidate.kind,
|
|
86
|
+
promotionBenefitValueBps: candidate.valueBps,
|
|
87
|
+
promotionBenefitValueMinor: candidate.valueMinor,
|
|
88
|
+
floorApplied: adjustment.floorApplied,
|
|
89
|
+
}));
|
|
90
|
+
const appliedV2Codes = new Set(v2Discounts.flatMap((discount) => discount.code ? [normalize(discount.code)] : []));
|
|
91
|
+
const engineRejections = result.rejectedCodes.flatMap((rejection) => rejection.code && !appliedLegacyCodes.has(normalize(rejection.code))
|
|
92
|
+
? [{
|
|
93
|
+
code: firstSubmittedByIdentity.get(normalize(rejection.code)) ?? rejection.code,
|
|
94
|
+
reason: mapEngineRejectionReason(rejection.reason),
|
|
95
|
+
}]
|
|
96
|
+
: []);
|
|
97
|
+
const appliedCodes = new Set([...retainedDiscounts.flatMap((discount) => discount.code ? [normalize(discount.code)] : []), ...appliedV2Codes]);
|
|
98
|
+
const mergedRejections = dedupeRejections([
|
|
99
|
+
...input.codeRejections,
|
|
100
|
+
...resolution.codeRejections,
|
|
101
|
+
...engineRejections,
|
|
102
|
+
], firstSubmittedByIdentity, input.codeRejections.length).filter((rejection) => !appliedCodes.has(normalize(rejection.code)));
|
|
103
|
+
const codeRejectionDetails = dedupeRejectionDetails([
|
|
104
|
+
...selectedRejectionDetails(input.codeRejectionDetails ?? [], input.codeRejections, mergedRejections),
|
|
105
|
+
...selectedRejectionDetails(resolution.codeRejectionDetails, resolution.codeRejections, mergedRejections),
|
|
106
|
+
], firstSubmittedByIdentity);
|
|
107
|
+
return {
|
|
108
|
+
discounts: orderSubmittedCodeSlots([...retainedDiscounts, ...v2Discounts], firstSubmittedByIdentity),
|
|
109
|
+
codeRejections: mergedRejections,
|
|
110
|
+
codeRejectionDetails,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function adjustmentContext(input, discounts) {
|
|
114
|
+
return {
|
|
115
|
+
purchaseScope: input.mode === "subscription" ? "subscription_initial" : "one_time",
|
|
116
|
+
referenceProductMinor: input.referenceProductMinor,
|
|
117
|
+
currentProductMinor: input.subtotalGrossMinor - sumLane(discounts, false),
|
|
118
|
+
shippingMinor: input.shippingGrossMinor - sumLane(discounts, true),
|
|
119
|
+
minimumProductPayableMinor: MINIMUM_PRODUCT_PAYABLE_MINOR,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function sumLane(discounts, shipping) {
|
|
123
|
+
return discounts
|
|
124
|
+
.filter((discount) => (discount.appliesTo === "shipping") === shipping)
|
|
125
|
+
.reduce((sum, discount) => sum + discount.amountOffMinor, 0);
|
|
126
|
+
}
|
|
127
|
+
function dedupeRejections(rejections, firstSubmittedByIdentity = new Map(), authoritativeFrom = rejections.length) {
|
|
128
|
+
const byCode = new Map();
|
|
129
|
+
for (const [index, rejection] of rejections.entries()) {
|
|
130
|
+
const identity = normalize(rejection.code);
|
|
131
|
+
const current = byCode.get(identity);
|
|
132
|
+
if (!current || (index >= authoritativeFrom && current.reason === "not_recognized")) {
|
|
133
|
+
byCode.set(identity, { ...rejection, code: firstSubmittedByIdentity.get(identity) ?? rejection.code });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return orderSubmittedCodeSlots([...byCode.values()], firstSubmittedByIdentity);
|
|
137
|
+
}
|
|
138
|
+
function dedupeRejectionDetails(details, firstSubmittedByIdentity = new Map()) {
|
|
139
|
+
const byCode = new Map();
|
|
140
|
+
for (const detail of details) {
|
|
141
|
+
const identity = normalize(detail.code);
|
|
142
|
+
if (!byCode.has(identity)) {
|
|
143
|
+
byCode.set(identity, { ...detail, code: firstSubmittedByIdentity.get(identity) ?? detail.code });
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return orderSubmittedCodeSlots([...byCode.values()], firstSubmittedByIdentity);
|
|
147
|
+
}
|
|
148
|
+
function selectedRejectionDetails(details, sourceRejections, selectedRejections) {
|
|
149
|
+
const selected = new Map(selectedRejections.map((rejection) => [normalize(rejection.code), rejection.reason]));
|
|
150
|
+
const matchingSources = new Set(sourceRejections.flatMap((rejection) => selected.get(normalize(rejection.code)) === rejection.reason ? [normalize(rejection.code)] : []));
|
|
151
|
+
return details.filter((detail) => matchingSources.has(normalize(detail.code)));
|
|
152
|
+
}
|
|
153
|
+
function firstSubmittedCodes(codes) {
|
|
154
|
+
const seen = new Set();
|
|
155
|
+
const first = [];
|
|
156
|
+
for (const code of codes) {
|
|
157
|
+
const identity = normalize(code);
|
|
158
|
+
if (!identity || seen.has(identity))
|
|
159
|
+
continue;
|
|
160
|
+
seen.add(identity);
|
|
161
|
+
first.push(code);
|
|
162
|
+
}
|
|
163
|
+
return first;
|
|
164
|
+
}
|
|
165
|
+
/** Collapse legacy exact-literal collisions to one submitted identity per lane. */
|
|
166
|
+
function reduceProvisionalLegacyDiscounts(discounts, firstSubmittedByIdentity) {
|
|
167
|
+
const chosen = new Map();
|
|
168
|
+
for (const [index, discount] of discounts.entries()) {
|
|
169
|
+
if (!discount.code)
|
|
170
|
+
continue;
|
|
171
|
+
const identity = normalize(discount.code);
|
|
172
|
+
const firstSubmitted = firstSubmittedByIdentity.get(identity);
|
|
173
|
+
if (!firstSubmitted)
|
|
174
|
+
continue;
|
|
175
|
+
const key = `${identity}:${discount.appliesTo}`;
|
|
176
|
+
const exactFirstSpelling = discount.code === firstSubmitted;
|
|
177
|
+
const current = chosen.get(key);
|
|
178
|
+
if (!current || (exactFirstSpelling && !current.exactFirstSpelling)) {
|
|
179
|
+
chosen.set(key, { index, exactFirstSpelling });
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return discounts.filter((discount, index) => {
|
|
183
|
+
if (!discount.code)
|
|
184
|
+
return true;
|
|
185
|
+
const identity = normalize(discount.code);
|
|
186
|
+
if (!firstSubmittedByIdentity.has(identity))
|
|
187
|
+
return true;
|
|
188
|
+
return chosen.get(`${identity}:${discount.appliesTo}`)?.index === index;
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
function projectSubmittedCodeSpelling(discounts, firstSubmittedByIdentity) {
|
|
192
|
+
return discounts.map((discount) => {
|
|
193
|
+
if (!discount.code)
|
|
194
|
+
return discount;
|
|
195
|
+
const code = firstSubmittedByIdentity.get(normalize(discount.code));
|
|
196
|
+
return code ? { ...discount, code } : discount;
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
function submittedRank(code, firstSubmittedByIdentity) {
|
|
200
|
+
const rank = [...firstSubmittedByIdentity.keys()].indexOf(normalize(code));
|
|
201
|
+
return rank < 0 ? Number.MAX_SAFE_INTEGER : rank;
|
|
202
|
+
}
|
|
203
|
+
function orderSubmittedCodeSlots(values, firstSubmittedByIdentity) {
|
|
204
|
+
const ordered = values.map((value, index) => ({ value, index })).filter(({ value }) => value.code && submittedRank(value.code, firstSubmittedByIdentity) < Number.MAX_SAFE_INTEGER)
|
|
205
|
+
.sort((left, right) => submittedRank(left.value.code, firstSubmittedByIdentity) - submittedRank(right.value.code, firstSubmittedByIdentity) || left.index - right.index).map(({ value }) => value);
|
|
206
|
+
let index = 0;
|
|
207
|
+
return values.map((value) => value.code && submittedRank(value.code, firstSubmittedByIdentity) < Number.MAX_SAFE_INTEGER ? ordered[index++] : value);
|
|
208
|
+
}
|
|
209
|
+
function normalize(code) {
|
|
210
|
+
return code.trim().toUpperCase();
|
|
211
|
+
}
|
|
212
|
+
function mapEngineRejectionReason(reason) {
|
|
213
|
+
switch (reason) {
|
|
214
|
+
case "better_price_exists":
|
|
215
|
+
return "better_price_exists";
|
|
216
|
+
case "scope_not_applicable":
|
|
217
|
+
return "scope_not_applicable";
|
|
218
|
+
default:
|
|
219
|
+
return "not_eligible";
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return { applyPromotionCodesV2, mapEngineRejectionReason };
|
|
223
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { PromotionRow } from "@openlup/core/promo";
|
|
2
|
+
import type { CommerceCodeRejection, CommerceQuoteDiscount } from "../pricing/index.js";
|
|
3
|
+
import type { CreateQuoteRequest } from "../pricing/index.js";
|
|
4
|
+
import type { CommercePromoDataPort } from "./promoDataPort.js";
|
|
5
|
+
import type { createQuotePromoDiscounts } from "./quotePromoDiscounts.js";
|
|
6
|
+
export declare function createQuotePromotionV2({ promotionCustomerSemantic, minimumProductPayableMinor: MINIMUM_PRODUCT_PAYABLE_MINOR, evaluateOrderTotalDiscounts }: {
|
|
7
|
+
promotionCustomerSemantic: (row: PromotionRow) => CommerceQuoteDiscount["customerSemantic"];
|
|
8
|
+
minimumProductPayableMinor: number;
|
|
9
|
+
evaluateOrderTotalDiscounts: ReturnType<typeof createQuotePromoDiscounts>["evaluateOrderTotalDiscounts"];
|
|
10
|
+
}): {
|
|
11
|
+
evaluateAutomaticPromotionV2: (input: {
|
|
12
|
+
promoDataPort: CommercePromoDataPort;
|
|
13
|
+
clientId: string | null;
|
|
14
|
+
visitorId?: string | null;
|
|
15
|
+
mode: CreateQuoteRequest["mode"];
|
|
16
|
+
/** Raw request spellings stay intact for legacy exact-literal evaluation. */
|
|
17
|
+
promoCodes?: readonly string[];
|
|
18
|
+
regionCode: string;
|
|
19
|
+
referenceProductMinor: number;
|
|
20
|
+
currentProductMinor: number;
|
|
21
|
+
shippingMinor: number;
|
|
22
|
+
atTime: string;
|
|
23
|
+
emailEligibilityConfirmed?: boolean;
|
|
24
|
+
}) => Promise<{
|
|
25
|
+
discounts: CommerceQuoteDiscount[];
|
|
26
|
+
codeRejections: CommerceCodeRejection[];
|
|
27
|
+
}>;
|
|
28
|
+
legacyPromotionDataPort: (port: CommercePromoDataPort) => CommercePromoDataPort;
|
|
29
|
+
};
|