@openlup/platform 0.17.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 +96 -0
- package/README.md +284 -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/contracts/index.d.ts +1 -0
- package/dist/communications/contracts/marketingSync.d.ts +194 -0
- package/dist/communications/contracts/marketingSync.js +1 -0
- package/dist/communications/index.d.ts +1 -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/newsletterIntegrationContracts.d.ts +3 -3
- package/dist/communications/resend/index.d.ts +40 -0
- package/dist/communications/resend/index.js +310 -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/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 +62 -2
- package/release-gates.json +218 -10
- package/sql/20260807140000_read_parity_catalog.sql +94 -0
- package/sql/20260810120000_storefront_catalog_attributes.sql +51 -0
- package/sql/20260812140000_sellable_bundle_catalog.sql +124 -0
- package/sql/20260812210000_bundle_operator_write_boundary.sql +567 -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/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/contracts/index.ts +1 -0
- package/src/communications/contracts/marketingSync.ts +109 -0
- package/src/communications/index.ts +1 -0
- package/src/communications/klaviyo/consentPush.ts +19 -0
- package/src/communications/klaviyo/index.ts +649 -0
- package/src/communications/resend/index.ts +350 -0
- package/src/composition/catalogContribution.ts +32 -0
- package/src/composition/index.ts +1 -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,111 @@
|
|
|
1
|
+
import { evaluateBundleComposition, evaluateBundleCompositionConstraint, evaluateBundleFulfillmentMode } from "./bundleRules.js";
|
|
2
|
+
export function createAdminBundleHandlers({ bundleSpec, BundleRpcError, COMMERCE_CONTRACT_VERSION, createAdminMutationHandler }) {
|
|
3
|
+
/**
|
|
4
|
+
* Bundle write handlers — each is the generic kit factory
|
|
5
|
+
* `createAdminMutationHandler` driven by one operation of `bundleSpec`. The
|
|
6
|
+
* factory owns the shared spine (POST-only, flag gate, admin authz, DB-derived
|
|
7
|
+
* actor-kind gate, contract parse, error map, success envelope); this module only
|
|
8
|
+
* injects the request-scoped port call, the envelope shape and the two pure rule
|
|
9
|
+
* passes that are decidable before the write.
|
|
10
|
+
*/
|
|
11
|
+
const BUNDLE_DISABLED = "Bundle writes are disabled";
|
|
12
|
+
const BUNDLE_INVALID = "Invalid bundle write request";
|
|
13
|
+
const BUNDLE_FAILURE = "Bundle write failed";
|
|
14
|
+
/** Bundle success envelope; only the fields the operation actually produced. */
|
|
15
|
+
function bundleResponse(result) {
|
|
16
|
+
return {
|
|
17
|
+
contractVersion: COMMERCE_CONTRACT_VERSION,
|
|
18
|
+
dryRun: result.dryRun,
|
|
19
|
+
idempotent: result.idempotent,
|
|
20
|
+
...(result.code ? { code: result.code } : {}),
|
|
21
|
+
...(result.bundleId ? { bundleId: result.bundleId } : {}),
|
|
22
|
+
...(result.componentCount === undefined ? {} : { componentCount: result.componentCount }),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/** A request-layer rule refusal, mapped onto the same envelope the boundary uses. */
|
|
26
|
+
function ruleRefusal(violations) {
|
|
27
|
+
return new BundleRpcError("P0001", violations.join(","));
|
|
28
|
+
}
|
|
29
|
+
function buildBundleHandler(deps, mutationKey, invoke) {
|
|
30
|
+
const mutation = bundleSpec.mutations[mutationKey];
|
|
31
|
+
const isActivation = mutation.gate === "activation";
|
|
32
|
+
return createAdminMutationHandler({
|
|
33
|
+
mutation,
|
|
34
|
+
flagName: isActivation ? bundleSpec.activationFlag : bundleSpec.mutationFlag,
|
|
35
|
+
enabled: isActivation ? deps.activationEnabled : deps.mutationsEnabled,
|
|
36
|
+
authorizeAdmin: deps.authorizeAdmin,
|
|
37
|
+
invoke: (actorId, input) => invoke(deps.writePort, actorId, input),
|
|
38
|
+
toResponse: bundleResponse,
|
|
39
|
+
disabledMessage: BUNDLE_DISABLED,
|
|
40
|
+
invalidRequestMessage: BUNDLE_INVALID,
|
|
41
|
+
failureMessage: BUNDLE_FAILURE,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
function createAdminBundleCreateDraftHandler(deps) {
|
|
45
|
+
return buildBundleHandler(deps, "create", (port, actorId, input) => {
|
|
46
|
+
const mode = evaluateBundleFulfillmentMode(input.bundle.fulfillmentMode);
|
|
47
|
+
if (!mode.ok)
|
|
48
|
+
return Promise.reject(ruleRefusal(mode.violations));
|
|
49
|
+
return port.upsertDraft(actorId, input);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function createAdminBundleUpdateDraftHandler(deps) {
|
|
53
|
+
return buildBundleHandler(deps, "update", (port, actorId, input) => {
|
|
54
|
+
const requested = input.updates.fulfillmentMode;
|
|
55
|
+
if (requested !== undefined) {
|
|
56
|
+
const mode = evaluateBundleFulfillmentMode(requested);
|
|
57
|
+
if (!mode.ok)
|
|
58
|
+
return Promise.reject(ruleRefusal(mode.violations));
|
|
59
|
+
}
|
|
60
|
+
return port.updateDraft(actorId, input);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Whole-set composition replace. Two pure passes run before the write: the set
|
|
65
|
+
* rules that are decidable from the request alone (at least one component, at
|
|
66
|
+
* least one non-add-on, no unit twice), and — when the bundle carries a non-empty
|
|
67
|
+
* constraint envelope — the adopter's own `CompositionRulesPort`. The write
|
|
68
|
+
* boundary re-derives everything it can see and refuses independently.
|
|
69
|
+
*/
|
|
70
|
+
function createAdminBundleSetCompositionHandler(deps) {
|
|
71
|
+
return buildBundleHandler(deps, "setComposition", async (port, actorId, input) => {
|
|
72
|
+
const composition = evaluateBundleComposition(input.components);
|
|
73
|
+
if (!composition.ok)
|
|
74
|
+
throw ruleRefusal(composition.violations);
|
|
75
|
+
const constraint = await evaluateBundleCompositionConstraint(deps.compositionRules, {
|
|
76
|
+
components: input.components,
|
|
77
|
+
constraint: await port.loadCompositionConstraint(input.code),
|
|
78
|
+
});
|
|
79
|
+
if (!constraint.ok)
|
|
80
|
+
throw ruleRefusal(constraint.violations);
|
|
81
|
+
return port.setComposition(actorId, input);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
function createAdminBundleSetTargetPriceHandler(deps) {
|
|
85
|
+
return buildBundleHandler(deps, "setTargetPrice", (port, actorId, input) => port.setTargetPrice(actorId, input));
|
|
86
|
+
}
|
|
87
|
+
function createAdminBundleArchiveHandler(deps) {
|
|
88
|
+
return buildBundleHandler(deps, "archive", (port, actorId, input) => port.archive(actorId, input));
|
|
89
|
+
}
|
|
90
|
+
function createAdminBundleRestoreHandler(deps) {
|
|
91
|
+
return buildBundleHandler(deps, "restore", (port, actorId, input) => port.restore(actorId, input));
|
|
92
|
+
}
|
|
93
|
+
function createAdminBundleCloneDraftHandler(deps) {
|
|
94
|
+
return buildBundleHandler(deps, "cloneDraft", (port, actorId, input) => port.cloneDraft(actorId, input));
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Activate (publish) — HUMAN-ONLY. The mutation's gate is `activation`, so the
|
|
98
|
+
* factory wires it to the activation flag and refuses a machine actor (403) from
|
|
99
|
+
* `allowedActorKinds` before the write is ever reached. The managed write routine
|
|
100
|
+
* also RAISEs 42501 as a second layer; the native routine has no independent
|
|
101
|
+
* actor-kind gate and relies on this handler gate.
|
|
102
|
+
*/
|
|
103
|
+
function createAdminBundleActivateHandler(deps) {
|
|
104
|
+
return buildBundleHandler(deps, "activate", (port, actorId, input) => port.activate(actorId, input));
|
|
105
|
+
}
|
|
106
|
+
/** Deactivate (unpublish) — HUMAN-ONLY, gated identically to activate. */
|
|
107
|
+
function createAdminBundleDeactivateHandler(deps) {
|
|
108
|
+
return buildBundleHandler(deps, "deactivate", (port, actorId, input) => port.deactivate(actorId, input));
|
|
109
|
+
}
|
|
110
|
+
return { createAdminBundleCreateDraftHandler, createAdminBundleUpdateDraftHandler, createAdminBundleSetCompositionHandler, createAdminBundleSetTargetPriceHandler, createAdminBundleArchiveHandler, createAdminBundleRestoreHandler, createAdminBundleCloneDraftHandler, createAdminBundleActivateHandler, createAdminBundleDeactivateHandler };
|
|
111
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { BundleAdminBindings, AuthorizeAdmin } from "./adminBindings.js";
|
|
2
|
+
import type { BundleAvailabilityService } from "./bundleAvailabilityService.js";
|
|
3
|
+
import type { BundleCatalogReadPort } from "./bundleCatalogReadPort.js";
|
|
4
|
+
export interface AdminBundleReadHandlerDeps {
|
|
5
|
+
readPort: BundleCatalogReadPort;
|
|
6
|
+
authorizeAdmin: AuthorizeAdmin;
|
|
7
|
+
readsEnabled: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Derived stock for the detail response. OPTIONAL, and absence is reported as a null
|
|
10
|
+
* `availability` rather than as an omitted field or a zero: a deployment with no stock rail has
|
|
11
|
+
* not counted anything, and saying so is different from saying nothing can ship.
|
|
12
|
+
*/
|
|
13
|
+
availabilityService?: BundleAvailabilityService;
|
|
14
|
+
}
|
|
15
|
+
export declare function createAdminBundleReadHandlers({ bundleSpec, BundleRpcError, COMMERCE_CONTRACT_VERSION, createAdminParameterizedReadHandler, bundleHistoryResponseSchema, getBundleResponseSchema, listBundlesResponseSchema, previewBundlePriceResponseSchema }: Pick<BundleAdminBindings, "bundleSpec" | "BundleRpcError" | "COMMERCE_CONTRACT_VERSION" | "createAdminParameterizedReadHandler" | "bundleHistoryResponseSchema" | "getBundleResponseSchema" | "listBundlesResponseSchema" | "previewBundlePriceResponseSchema">): {
|
|
16
|
+
createAdminBundleListHandler: (deps: AdminBundleReadHandlerDeps) => (req: import("../http/types.js").HttpRequest, res: import("../http/types.js").HttpResponse) => Promise<void>;
|
|
17
|
+
createAdminBundleGetHandler: (deps: AdminBundleReadHandlerDeps) => (req: import("../http/types.js").HttpRequest, res: import("../http/types.js").HttpResponse) => Promise<void>;
|
|
18
|
+
createAdminBundleHistoryHandler: (deps: AdminBundleReadHandlerDeps) => (req: import("../http/types.js").HttpRequest, res: import("../http/types.js").HttpResponse) => Promise<void>;
|
|
19
|
+
createAdminBundlePreviewPriceHandler: (deps: AdminBundleReadHandlerDeps) => (req: import("../http/types.js").HttpRequest, res: import("../http/types.js").HttpResponse) => Promise<void>;
|
|
20
|
+
};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { allocateBundleTargetPrice } from "@openlup/core/pricing";
|
|
2
|
+
export function createAdminBundleReadHandlers({ bundleSpec, BundleRpcError, COMMERCE_CONTRACT_VERSION, createAdminParameterizedReadHandler, bundleHistoryResponseSchema, getBundleResponseSchema, listBundlesResponseSchema, previewBundlePriceResponseSchema }) {
|
|
3
|
+
/**
|
|
4
|
+
* Bundle READ handlers — each is the generic kit factory
|
|
5
|
+
* `createAdminParameterizedReadHandler` driven by one query of `bundleSpec`. The
|
|
6
|
+
* factory owns the shared spine (GET-only, flag gate, admin authz, DB-derived
|
|
7
|
+
* actor-kind gate, query parse, error map, response validation, success
|
|
8
|
+
* envelope); this module only injects the read-port call and the response shape.
|
|
9
|
+
* It mirrors `adminBundleHandler.ts`, and the catalog reference
|
|
10
|
+
* (`server/domains/commerce/adminCatalogReadHandler.ts`) is the shape both follow.
|
|
11
|
+
*
|
|
12
|
+
* The price preview is the one read that computes: it runs the SAME pricing kernel
|
|
13
|
+
* the write boundary refuses against (`allocateBundleTargetPrice`) over TODAY's
|
|
14
|
+
* component prices, so an operator can see the money before committing to it. Its
|
|
15
|
+
* three refusal codes are mapped onto the same envelope a refused write produces,
|
|
16
|
+
* because they are the same three decisions.
|
|
17
|
+
*/
|
|
18
|
+
const BUNDLE_READ_DISABLED = "Bundle reads are disabled";
|
|
19
|
+
const BUNDLE_READ_INVALID = "Invalid bundle read request";
|
|
20
|
+
const BUNDLE_READ_FAILURE = "Bundle read failed";
|
|
21
|
+
function query(key) {
|
|
22
|
+
const spec = bundleSpec.queries?.[key];
|
|
23
|
+
if (!spec)
|
|
24
|
+
throw new Error(`bundleSpec.queries.${key} is not defined`);
|
|
25
|
+
return spec;
|
|
26
|
+
}
|
|
27
|
+
function gate(deps, key, load, toResponse, responseSchema, invalidResponseMessage) {
|
|
28
|
+
return createAdminParameterizedReadHandler({
|
|
29
|
+
query: query(key),
|
|
30
|
+
flagName: bundleSpec.readFlag,
|
|
31
|
+
enabled: deps.readsEnabled,
|
|
32
|
+
authorizeAdmin: deps.authorizeAdmin,
|
|
33
|
+
load: (_actorId, input) => load(deps.readPort, input),
|
|
34
|
+
toResponse,
|
|
35
|
+
responseSchema,
|
|
36
|
+
disabledMessage: BUNDLE_READ_DISABLED,
|
|
37
|
+
invalidRequestMessage: BUNDLE_READ_INVALID,
|
|
38
|
+
invalidResponseMessage,
|
|
39
|
+
failureMessage: BUNDLE_READ_FAILURE,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function createAdminBundleListHandler(deps) {
|
|
43
|
+
return gate(deps, "list", (port, input) => port.listBundles(input), (data) => ({
|
|
44
|
+
contractVersion: COMMERCE_CONTRACT_VERSION,
|
|
45
|
+
bundles: data.bundles,
|
|
46
|
+
total: data.total,
|
|
47
|
+
}), listBundlesResponseSchema, "Invalid bundle list response");
|
|
48
|
+
}
|
|
49
|
+
function createAdminBundleGetHandler(deps) {
|
|
50
|
+
return gate(deps, "get", async (port, input) => {
|
|
51
|
+
const bundle = await port.getBundle(input);
|
|
52
|
+
// Absent is NOT an empty detail: the same P0002 the write boundary raises,
|
|
53
|
+
// so a missing bundle answers 404 through one map rather than two.
|
|
54
|
+
if (!bundle)
|
|
55
|
+
throw new BundleRpcError("P0002", "bundle_not_found");
|
|
56
|
+
return { bundle, stock: await describeStock(deps, bundle) };
|
|
57
|
+
}, ({ bundle, stock }) => ({
|
|
58
|
+
contractVersion: COMMERCE_CONTRACT_VERSION,
|
|
59
|
+
bundle: toDetailView(bundle, stock),
|
|
60
|
+
}), getBundleResponseSchema, "Invalid bundle detail response");
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Fold stock into ONE bundle, through the same service the public feed uses.
|
|
64
|
+
*
|
|
65
|
+
* The service takes compositions and this is a detail, but the two carry the identical component
|
|
66
|
+
* row type, so the adaptation is a rename rather than a translation — and routing this read through
|
|
67
|
+
* the shared service is the point: `deriveBundleAvailability` stays the single place that folds
|
|
68
|
+
* units into bundles, so an operator's detail page and a shopper's feed can never disagree about
|
|
69
|
+
* the same bundle.
|
|
70
|
+
*
|
|
71
|
+
* A bundle whose status is not live is NOT skipped here. That is the whole gain over the feed: the
|
|
72
|
+
* feed is live-only by construction, so a draft had no derived stock anywhere, and an operator
|
|
73
|
+
* about to activate one could not see whether it would ship.
|
|
74
|
+
*/
|
|
75
|
+
async function describeStock(deps, bundle) {
|
|
76
|
+
if (!deps.availabilityService)
|
|
77
|
+
return null;
|
|
78
|
+
const [row] = await deps.availabilityService.describeCompositions([
|
|
79
|
+
{
|
|
80
|
+
code: bundle.code,
|
|
81
|
+
title: bundle.title,
|
|
82
|
+
fulfillmentMode: bundle.fulfillmentMode,
|
|
83
|
+
// Stated from what the detail resolved. Neither reaches the stock question — the service
|
|
84
|
+
// asks the availability port for units, never for money — but the shape is the contract and
|
|
85
|
+
// inventing a currency here would be a lie in a field somebody may later read.
|
|
86
|
+
currency: bundle.resolvedCurrency ?? "",
|
|
87
|
+
targetPriceMinor: 0,
|
|
88
|
+
mode: "any",
|
|
89
|
+
components: bundle.components,
|
|
90
|
+
},
|
|
91
|
+
]);
|
|
92
|
+
return row ?? null;
|
|
93
|
+
}
|
|
94
|
+
function createAdminBundleHistoryHandler(deps) {
|
|
95
|
+
return gate(deps, "history", (port, input) => port.listBundleHistory(input), (data) => ({
|
|
96
|
+
contractVersion: COMMERCE_CONTRACT_VERSION,
|
|
97
|
+
events: data.events,
|
|
98
|
+
total: data.total,
|
|
99
|
+
}), bundleHistoryResponseSchema, "Invalid bundle history response");
|
|
100
|
+
}
|
|
101
|
+
function createAdminBundlePreviewPriceHandler(deps) {
|
|
102
|
+
return gate(deps, "previewPrice", async (port, input) => {
|
|
103
|
+
const bundle = await port.getBundle({
|
|
104
|
+
code: input.code,
|
|
105
|
+
currency: input.currency,
|
|
106
|
+
mode: input.mode,
|
|
107
|
+
});
|
|
108
|
+
if (!bundle)
|
|
109
|
+
throw new BundleRpcError("P0002", "bundle_not_found");
|
|
110
|
+
return previewBundlePrice(bundle, input);
|
|
111
|
+
}, (preview) => ({ contractVersion: COMMERCE_CONTRACT_VERSION, preview }), previewBundlePriceResponseSchema, "Invalid bundle price preview response");
|
|
112
|
+
}
|
|
113
|
+
/** The detail the transport carries: the port's internal variant id stays server-side. */
|
|
114
|
+
function toDetailView(bundle, stock) {
|
|
115
|
+
const { components, resolvedPriceListId: _priceListId, ...rest } = bundle;
|
|
116
|
+
return {
|
|
117
|
+
...rest,
|
|
118
|
+
components: components.map(({ variantId: _variantId, ...component }) => component),
|
|
119
|
+
availability: stock
|
|
120
|
+
? {
|
|
121
|
+
sellableNow: stock.availability.sellableNow,
|
|
122
|
+
status: stock.availability.status,
|
|
123
|
+
reasonCode: stock.availability.reasonCode,
|
|
124
|
+
limitingSku: stock.availability.limitingSku,
|
|
125
|
+
components: stock.components.map((component) => ({
|
|
126
|
+
sku: component.sku,
|
|
127
|
+
quantity: component.quantity,
|
|
128
|
+
isAddon: component.isAddon,
|
|
129
|
+
sellableNow: component.sellableNow,
|
|
130
|
+
})),
|
|
131
|
+
}
|
|
132
|
+
: null,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Run the pricing kernel over the bundle's live component prices.
|
|
137
|
+
*
|
|
138
|
+
* Every refusal is stated as the same rule violation a write would produce, so an
|
|
139
|
+
* operator previewing a price and an operator setting it are told the same thing:
|
|
140
|
+
* no priced list (P0002), an unpriced component or an empty set (P0001), and the
|
|
141
|
+
* kernel's own three failure codes (P0001).
|
|
142
|
+
*/
|
|
143
|
+
function previewBundlePrice(bundle, input) {
|
|
144
|
+
if (!bundle.resolvedPriceListId || !bundle.resolvedCurrency) {
|
|
145
|
+
throw new BundleRpcError("P0002", "no_active_price_list");
|
|
146
|
+
}
|
|
147
|
+
const counted = bundle.components;
|
|
148
|
+
if (counted.length === 0)
|
|
149
|
+
throw new BundleRpcError("P0001", "min_components");
|
|
150
|
+
const unpriced = counted.filter((component) => component.referenceUnitPriceMinor === null);
|
|
151
|
+
if (unpriced.length > 0) {
|
|
152
|
+
throw new BundleRpcError("P0001", `component_unpriced:${unpriced[0].sku}`);
|
|
153
|
+
}
|
|
154
|
+
const activePrice = bundle.prices.find((price) => price.active && (!input.mode || price.mode === input.mode));
|
|
155
|
+
const targetPriceMinor = input.targetPriceMinor ?? activePrice?.targetPriceMinor;
|
|
156
|
+
if (targetPriceMinor === undefined)
|
|
157
|
+
throw new BundleRpcError("P0002", "no_active_target_price");
|
|
158
|
+
const allocation = allocateBundleTargetPrice({
|
|
159
|
+
components: counted.map((component) => ({
|
|
160
|
+
sku: component.sku,
|
|
161
|
+
quantity: component.quantity,
|
|
162
|
+
unitPriceMinor: component.referenceUnitPriceMinor,
|
|
163
|
+
})),
|
|
164
|
+
targetPriceMinor,
|
|
165
|
+
});
|
|
166
|
+
// The narrowing is written out rather than relied upon: the Node tsconfig runs
|
|
167
|
+
// without strictNullChecks, where a discriminated union does not narrow on `ok`.
|
|
168
|
+
if (!allocation.ok)
|
|
169
|
+
throw new BundleRpcError("P0001", allocation.code);
|
|
170
|
+
return {
|
|
171
|
+
bundleCode: bundle.code,
|
|
172
|
+
priceListId: bundle.resolvedPriceListId,
|
|
173
|
+
mode: input.mode ?? activePrice?.mode ?? "any",
|
|
174
|
+
currency: bundle.resolvedCurrency,
|
|
175
|
+
referenceTotalMinor: allocation.referenceTotalMinor,
|
|
176
|
+
targetPriceMinor: allocation.targetPriceMinor,
|
|
177
|
+
discountTotalMinor: allocation.discountTotalMinor,
|
|
178
|
+
discountBps: allocation.discountBps,
|
|
179
|
+
floorApplied: allocation.floorApplied,
|
|
180
|
+
componentTargets: allocation.components,
|
|
181
|
+
allocatedLines: allocation.lines,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
return { createAdminBundleListHandler, createAdminBundleGetHandler, createAdminBundleHistoryHandler, createAdminBundlePreviewPriceHandler };
|
|
185
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { CreateBundleDraftRequest, SetBundleCompositionRequest, SetBundleTargetPriceRequest, UpdateBundleDraftRequest } from "./contracts/bundleAdmin.js";
|
|
2
|
+
import type { ActivateBundleRequest, ArchiveBundleRequest, CloneBundleDraftRequest, DeactivateBundleRequest, RestoreBundleRequest } from "./contracts/bundleAdmin.js";
|
|
3
|
+
/**
|
|
4
|
+
* The semantic write port for the admin/agent bundle surface.
|
|
5
|
+
*
|
|
6
|
+
* This file names the CAPABILITY, never the storage. Both shipped write adapters
|
|
7
|
+
* implement exactly this interface and are proved against ONE shared scenario table, so a caller cannot
|
|
8
|
+
* tell them apart and neither can a rule. No data-client type, table name or
|
|
9
|
+
* routine name appears here; the domain layer opens no database handle of its own.
|
|
10
|
+
*
|
|
11
|
+
* Each method is one atomic operation on the security boundary: the boundary takes
|
|
12
|
+
* the actor explicitly; the managed chain re-derives actor kind, while native
|
|
13
|
+
* relies on the handler human-only gate. Each boundary writes its ledger row in the same transaction, short-circuits a replayed
|
|
14
|
+
* idempotency key, and validates a dry run without durable writes.
|
|
15
|
+
*/
|
|
16
|
+
/** Shape every bundle write returns. */
|
|
17
|
+
export interface BundleWriteResult {
|
|
18
|
+
idempotent: boolean;
|
|
19
|
+
dryRun: boolean;
|
|
20
|
+
code?: string;
|
|
21
|
+
bundleId?: string;
|
|
22
|
+
componentCount?: number;
|
|
23
|
+
}
|
|
24
|
+
/** The opaque rules envelope a bundle carries, as the boundary hands it back. */
|
|
25
|
+
export interface BundleCompositionConstraintEnvelope {
|
|
26
|
+
kind: string;
|
|
27
|
+
version: number;
|
|
28
|
+
data: Record<string, unknown>;
|
|
29
|
+
}
|
|
30
|
+
export interface AdminBundleWritePort {
|
|
31
|
+
/**
|
|
32
|
+
* Read the bundle's stored composition constraint. This is a WRITE-path read: the
|
|
33
|
+
* whole-set replace must be validated against the envelope the bundle already
|
|
34
|
+
* carries before anything is written, and the adopter's `CompositionRulesPort` is
|
|
35
|
+
* the only thing that can decide it. Returns `null` for an unconstrained bundle.
|
|
36
|
+
* Raises P0002 when the bundle does not exist.
|
|
37
|
+
*/
|
|
38
|
+
loadCompositionConstraint(code: string): Promise<BundleCompositionConstraintEnvelope | null>;
|
|
39
|
+
/** Create or edit a DRAFT bundle from a full structural payload. */
|
|
40
|
+
upsertDraft(actorId: string, input: CreateBundleDraftRequest): Promise<BundleWriteResult>;
|
|
41
|
+
/**
|
|
42
|
+
* Partial structural edit of a DRAFT bundle. The upsert is full-payload, so this
|
|
43
|
+
* reads the current bundle, overlays the supplied fields and replays the merged
|
|
44
|
+
* payload through the same boundary. Raises NOT_FOUND (P0002) when the bundle
|
|
45
|
+
* does not exist.
|
|
46
|
+
*/
|
|
47
|
+
updateDraft(actorId: string, input: UpdateBundleDraftRequest): Promise<BundleWriteResult>;
|
|
48
|
+
/**
|
|
49
|
+
* Replace the bill of materials as a WHOLE SET, atomically. Raises P0002 when the
|
|
50
|
+
* bundle or a named unit is missing, and P0001 when a unit is not sellable on its
|
|
51
|
+
* own or is priced in another currency.
|
|
52
|
+
*/
|
|
53
|
+
setComposition(actorId: string, input: SetBundleCompositionRequest): Promise<BundleWriteResult>;
|
|
54
|
+
/**
|
|
55
|
+
* Append a new active target price and close the previous one. Never mutates an
|
|
56
|
+
* existing row. Raises P0001 when the target sits above the sum of the parts or
|
|
57
|
+
* below the floor every component must clear.
|
|
58
|
+
*/
|
|
59
|
+
setTargetPrice(actorId: string, input: SetBundleTargetPriceRequest): Promise<BundleWriteResult>;
|
|
60
|
+
/** Remove a bundle from sale (status flip, never a hard delete). P0002 when missing. */
|
|
61
|
+
archive(actorId: string, input: ArchiveBundleRequest): Promise<BundleWriteResult>;
|
|
62
|
+
/**
|
|
63
|
+
* Restore an archived bundle to draft. Raises P0002 when missing and P0001
|
|
64
|
+
* (`restore_requires_archived`) when the bundle is not archived.
|
|
65
|
+
*/
|
|
66
|
+
restore(actorId: string, input: RestoreBundleRequest): Promise<BundleWriteResult>;
|
|
67
|
+
/**
|
|
68
|
+
* Clone any bundle into a NEW draft under a new code, including its composition.
|
|
69
|
+
* Raises P0002 (`clone_source_not_found`) when the source is missing and P0001
|
|
70
|
+
* when the target code is already taken.
|
|
71
|
+
*/
|
|
72
|
+
cloneDraft(actorId: string, input: CloneBundleDraftRequest): Promise<BundleWriteResult>;
|
|
73
|
+
/**
|
|
74
|
+
* Publish (draft -> active). HUMAN-ONLY: the managed chain raises 42501
|
|
75
|
+
* (`publish_requires_human`) for machine actors; native relies on the handler gate. Raises P0001 when the bundle has
|
|
76
|
+
* no composition or no active target price.
|
|
77
|
+
*/
|
|
78
|
+
activate(actorId: string, input: ActivateBundleRequest): Promise<BundleWriteResult>;
|
|
79
|
+
/**
|
|
80
|
+
* Unpublish (active -> draft). HUMAN-ONLY: the managed chain uses the same
|
|
81
|
+
* 42501 refusal; native relies on the handler gate. Raises P0001
|
|
82
|
+
* (`deactivate_requires_active`) when the bundle is not live.
|
|
83
|
+
*/
|
|
84
|
+
deactivate(actorId: string, input: DeactivateBundleRequest): Promise<BundleWriteResult>;
|
|
85
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { BundleRuleBindings, BundleStockStatus as CommerceOfferAvailabilityStatus } from "./bundleBindings.js";
|
|
2
|
+
/**
|
|
3
|
+
* Derived stock for a composed bundle — the pure kernel that folds many component
|
|
4
|
+
* stock figures into ONE answer about the bundle.
|
|
5
|
+
*
|
|
6
|
+
* A bundle is only as sellable as its scarcest counted part: with a bill of
|
|
7
|
+
* materials of `quantity` units per component, the number of whole bundles that
|
|
8
|
+
* can ship is `min(floor(component.sellableNow / component.quantity))`. That
|
|
9
|
+
* component is named (`limitingSku`) so an operator learns WHICH part is the
|
|
10
|
+
* constraint instead of only that there is one.
|
|
11
|
+
*
|
|
12
|
+
* FAIL TO UNKNOWN, NEVER GUESS. If any counted component's stock is itself
|
|
13
|
+
* unknown, the bundle's stock is unknown — not the minimum of the parts that did
|
|
14
|
+
* answer. Treating a missing figure as unlimited oversells; treating it as zero
|
|
15
|
+
* hides a sellable bundle. Both are decisions this module refuses to make, so it
|
|
16
|
+
* reports `unknown` and names the component whose figure is missing.
|
|
17
|
+
*
|
|
18
|
+
* The status ladder and the reason-code vocabulary are IMPORTED from the commerce
|
|
19
|
+
* domain's PUBLIC port seam rather than restated, so a bundle and a single unit
|
|
20
|
+
* can never disagree about what `low_stock` means.
|
|
21
|
+
*
|
|
22
|
+
* Pure: integer arithmetic, no clock, no randomness, no I/O.
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* Threshold below which a bundle reads as `low_stock`. Deliberately far under the
|
|
26
|
+
* single-unit default: a bundle consumes several units per sale, so the same raw
|
|
27
|
+
* stock represents far fewer bundles and a unit-scale threshold would report
|
|
28
|
+
* nearly every bundle as low.
|
|
29
|
+
*/
|
|
30
|
+
export declare const DEFAULT_BUNDLE_LOW_STOCK_THRESHOLD = 3;
|
|
31
|
+
/** A counted component answered with no figure at all. */
|
|
32
|
+
export declare const BUNDLE_COMPONENT_STOCK_UNKNOWN = "component_stock_unknown";
|
|
33
|
+
/** Nothing was counted — no non-add-on component to derive an answer from. */
|
|
34
|
+
export declare const BUNDLE_COMPOSITION_EMPTY = "bundle_composition_empty";
|
|
35
|
+
export interface BundleAvailabilityComponent {
|
|
36
|
+
sku: string;
|
|
37
|
+
/** Units of this component consumed by ONE bundle; always positive. */
|
|
38
|
+
quantity: number;
|
|
39
|
+
isAddon: boolean;
|
|
40
|
+
/** Sellable units of the component, or `null` when the figure is unknown. */
|
|
41
|
+
sellableNow: number | null;
|
|
42
|
+
}
|
|
43
|
+
export interface BundleAvailability {
|
|
44
|
+
/** Whole bundles that can ship now, or `null` when any counted part is unknown. */
|
|
45
|
+
sellableNow: number | null;
|
|
46
|
+
status: CommerceOfferAvailabilityStatus;
|
|
47
|
+
reasonCode: string;
|
|
48
|
+
/** The component that decided the answer; `null` only when nothing was counted. */
|
|
49
|
+
limitingSku: string | null;
|
|
50
|
+
}
|
|
51
|
+
export interface DeriveBundleAvailabilityInput {
|
|
52
|
+
components: readonly BundleAvailabilityComponent[];
|
|
53
|
+
lowStockThreshold?: number;
|
|
54
|
+
/**
|
|
55
|
+
* Add-ons are excluded by default: an add-on is priced into the bundle but
|
|
56
|
+
* optional in composition, so its stock must not make the bundle unsellable.
|
|
57
|
+
* An operator who sells the add-on as mandatory opts in here.
|
|
58
|
+
*/
|
|
59
|
+
includeAddonsInStock?: boolean;
|
|
60
|
+
}
|
|
61
|
+
export declare function createBundleAvailability({ offerAvailabilityReasonCode, offerAvailabilityStatusFor }: Pick<BundleRuleBindings, "offerAvailabilityReasonCode" | "offerAvailabilityStatusFor">): {
|
|
62
|
+
deriveBundleAvailability: (input: DeriveBundleAvailabilityInput) => BundleAvailability;
|
|
63
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derived stock for a composed bundle — the pure kernel that folds many component
|
|
3
|
+
* stock figures into ONE answer about the bundle.
|
|
4
|
+
*
|
|
5
|
+
* A bundle is only as sellable as its scarcest counted part: with a bill of
|
|
6
|
+
* materials of `quantity` units per component, the number of whole bundles that
|
|
7
|
+
* can ship is `min(floor(component.sellableNow / component.quantity))`. That
|
|
8
|
+
* component is named (`limitingSku`) so an operator learns WHICH part is the
|
|
9
|
+
* constraint instead of only that there is one.
|
|
10
|
+
*
|
|
11
|
+
* FAIL TO UNKNOWN, NEVER GUESS. If any counted component's stock is itself
|
|
12
|
+
* unknown, the bundle's stock is unknown — not the minimum of the parts that did
|
|
13
|
+
* answer. Treating a missing figure as unlimited oversells; treating it as zero
|
|
14
|
+
* hides a sellable bundle. Both are decisions this module refuses to make, so it
|
|
15
|
+
* reports `unknown` and names the component whose figure is missing.
|
|
16
|
+
*
|
|
17
|
+
* The status ladder and the reason-code vocabulary are IMPORTED from the commerce
|
|
18
|
+
* domain's PUBLIC port seam rather than restated, so a bundle and a single unit
|
|
19
|
+
* can never disagree about what `low_stock` means.
|
|
20
|
+
*
|
|
21
|
+
* Pure: integer arithmetic, no clock, no randomness, no I/O.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* Threshold below which a bundle reads as `low_stock`. Deliberately far under the
|
|
25
|
+
* single-unit default: a bundle consumes several units per sale, so the same raw
|
|
26
|
+
* stock represents far fewer bundles and a unit-scale threshold would report
|
|
27
|
+
* nearly every bundle as low.
|
|
28
|
+
*/
|
|
29
|
+
export const DEFAULT_BUNDLE_LOW_STOCK_THRESHOLD = 3;
|
|
30
|
+
/** A counted component answered with no figure at all. */
|
|
31
|
+
export const BUNDLE_COMPONENT_STOCK_UNKNOWN = "component_stock_unknown";
|
|
32
|
+
/** Nothing was counted — no non-add-on component to derive an answer from. */
|
|
33
|
+
export const BUNDLE_COMPOSITION_EMPTY = "bundle_composition_empty";
|
|
34
|
+
export function createBundleAvailability({ offerAvailabilityReasonCode, offerAvailabilityStatusFor }) {
|
|
35
|
+
function deriveBundleAvailability(input) {
|
|
36
|
+
const counted = input.components.filter((component) => input.includeAddonsInStock === true || !component.isAddon);
|
|
37
|
+
if (counted.length === 0) {
|
|
38
|
+
return {
|
|
39
|
+
sellableNow: null,
|
|
40
|
+
status: "unknown",
|
|
41
|
+
reasonCode: BUNDLE_COMPOSITION_EMPTY,
|
|
42
|
+
limitingSku: null,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
// Unknown wins over every figure that did answer, and the named component is the
|
|
46
|
+
// lowest sku among the unknown ones, so the same set always names the same part.
|
|
47
|
+
const unknown = counted.filter((component) => component.sellableNow === null);
|
|
48
|
+
if (unknown.length > 0) {
|
|
49
|
+
return {
|
|
50
|
+
sellableNow: null,
|
|
51
|
+
status: "unknown",
|
|
52
|
+
reasonCode: BUNDLE_COMPONENT_STOCK_UNKNOWN,
|
|
53
|
+
limitingSku: lowestSku(unknown),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
let limiting = counted[0];
|
|
57
|
+
let sellableNow = wholeBundles(counted[0]);
|
|
58
|
+
for (const component of counted.slice(1)) {
|
|
59
|
+
const candidate = wholeBundles(component);
|
|
60
|
+
// Strictly lower wins; an exact tie is broken by ascending sku code units, so
|
|
61
|
+
// the named component never depends on the order the composition was read in.
|
|
62
|
+
if (candidate < sellableNow || (candidate === sellableNow && component.sku < limiting.sku)) {
|
|
63
|
+
limiting = component;
|
|
64
|
+
sellableNow = candidate;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const status = offerAvailabilityStatusFor(sellableNow, input.lowStockThreshold ?? DEFAULT_BUNDLE_LOW_STOCK_THRESHOLD);
|
|
68
|
+
return { sellableNow, status, reasonCode: offerAvailabilityReasonCode(status), limitingSku: limiting.sku };
|
|
69
|
+
}
|
|
70
|
+
return { deriveBundleAvailability };
|
|
71
|
+
}
|
|
72
|
+
/** Whole bundles this component alone can carry. */
|
|
73
|
+
function wholeBundles(component) {
|
|
74
|
+
return Math.floor((component.sellableNow ?? 0) / component.quantity);
|
|
75
|
+
}
|
|
76
|
+
function lowestSku(components) {
|
|
77
|
+
return components.reduce((lowest, component) => (component.sku < lowest ? component.sku : lowest), components[0].sku);
|
|
78
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { BundleAvailability, BundleAvailabilityComponent } from "./bundleAvailability.js";
|
|
2
|
+
import type { BundleStockPort as CommerceOfferAvailabilityPort } from "./bundleBindings.js";
|
|
3
|
+
import type { ActiveBundleComposition, BundleCatalogReadPort } from "./bundleCatalogReadPort.js";
|
|
4
|
+
/**
|
|
5
|
+
* Derived bundle stock, composed from the two ports that already know the halves:
|
|
6
|
+
* the bundle read port knows the bill of materials, the EXISTING commerce offer
|
|
7
|
+
* availability port knows what a unit's stock is. Nothing here re-derives stock and
|
|
8
|
+
* nothing here opens a database handle.
|
|
9
|
+
*
|
|
10
|
+
* ONE BATCHED CALL, ALWAYS. Every component of every bundle in the request is
|
|
11
|
+
* asked for in a single `getAvailability`, deduplicated by sku, because the
|
|
12
|
+
* underlying port answers a set in three queries — asking per bundle would turn a
|
|
13
|
+
* catalogue listing into a query storm and would let two bundles sharing a unit
|
|
14
|
+
* disagree about its stock inside one response.
|
|
15
|
+
*
|
|
16
|
+
* A sku the availability port does not answer for is a MISSING figure, not zero:
|
|
17
|
+
* it is folded in as `sellableNow: null`, so the pure kernel reports the bundle as
|
|
18
|
+
* unknown and names the component rather than quietly overselling or hiding it.
|
|
19
|
+
*/
|
|
20
|
+
export interface BundleAvailabilityServiceDeps {
|
|
21
|
+
bundleCatalogReadPort: BundleCatalogReadPort;
|
|
22
|
+
offerAvailabilityPort: CommerceOfferAvailabilityPort;
|
|
23
|
+
/** Threshold handed to the pure kernel; its own default applies when omitted. */
|
|
24
|
+
lowStockThreshold?: number;
|
|
25
|
+
includeAddonsInStock?: boolean;
|
|
26
|
+
}
|
|
27
|
+
export interface BundleAvailabilityRow {
|
|
28
|
+
composition: ActiveBundleComposition;
|
|
29
|
+
availability: BundleAvailability;
|
|
30
|
+
/**
|
|
31
|
+
* The EXACT rows the kernel was given, carried out again rather than recomputed.
|
|
32
|
+
*
|
|
33
|
+
* The bundle-level answer names one limiting component; an operator staring at a bundle that
|
|
34
|
+
* cannot ship needs to know how far off every OTHER component is too, so they can tell "order one
|
|
35
|
+
* more of this" from "this bundle is months away". These rows are the only place that figure
|
|
36
|
+
* exists — the kernel folds them into a single number and the sellable feed's components carry
|
|
37
|
+
* price, never stock. Returning them costs nothing here and is the alternative to a second stock
|
|
38
|
+
* derivation somewhere that can disagree with this one.
|
|
39
|
+
*/
|
|
40
|
+
components: readonly BundleAvailabilityComponent[];
|
|
41
|
+
}
|
|
42
|
+
export interface BundleAvailabilityService {
|
|
43
|
+
/** Fold stock into a set of compositions the caller already holds. */
|
|
44
|
+
describeCompositions(compositions: readonly ActiveBundleComposition[]): Promise<BundleAvailabilityRow[]>;
|
|
45
|
+
/** Read the live sellable set and fold stock into it. */
|
|
46
|
+
describeActiveBundles(input?: {
|
|
47
|
+
currency?: string;
|
|
48
|
+
}): Promise<BundleAvailabilityRow[]>;
|
|
49
|
+
}
|
|
50
|
+
export declare function createBundleAvailabilityServices({ deriveBundleAvailability }: {
|
|
51
|
+
deriveBundleAvailability: (input: import("./bundleAvailability.js").DeriveBundleAvailabilityInput) => BundleAvailability;
|
|
52
|
+
}): {
|
|
53
|
+
createBundleAvailabilityService: (deps: BundleAvailabilityServiceDeps) => BundleAvailabilityService;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* The port to use where no stock source is bound at all.
|
|
57
|
+
*
|
|
58
|
+
* It answers every item with `sellableNow: null`, which the kernel reads as
|
|
59
|
+
* unknown — so a deployment without an inventory rail publishes bundles whose
|
|
60
|
+
* price is exact and whose stock is honestly unstated. The alternative, omitting
|
|
61
|
+
* the availability block, would have made "we do not know" indistinguishable from
|
|
62
|
+
* "we did not ask", and the alternative to THAT — defaulting to available —
|
|
63
|
+
* oversells on a deployment that never counted anything.
|
|
64
|
+
*/
|
|
65
|
+
export declare function createUnknownOfferAvailabilityPort(): CommerceOfferAvailabilityPort;
|