@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,46 @@
|
|
|
1
|
+
import type { HttpRequest, HttpResponse } from "../http/index.js";
|
|
2
|
+
import type { BffResponder } from "../bff/index.js";
|
|
3
|
+
import type { BundleResponseSchema } from "./adminBindings.js";
|
|
4
|
+
import type { SellableBundle } from "./contracts/bundleAvailability.js";
|
|
5
|
+
import type { BundleAvailability } from "./bundleAvailability.js";
|
|
6
|
+
import type { ActiveBundleComposition } from "./bundleCatalogReadPort.js";
|
|
7
|
+
import type { BundleAvailabilityService } from "./bundleAvailabilityService.js";
|
|
8
|
+
/**
|
|
9
|
+
* The public sellable-bundle feed handler, shaped after the reference catalog
|
|
10
|
+
* handler: GET-only, one
|
|
11
|
+
* port call, validate the body before sending, and never serve an unvalidated
|
|
12
|
+
* shape.
|
|
13
|
+
*
|
|
14
|
+
* It composes rather than computes twice: the availability service already folds
|
|
15
|
+
* one batched stock answer into the live compositions, and the pricing kernel
|
|
16
|
+
* turns the stored target price into per-component money. This module only decides
|
|
17
|
+
* what a storefront is allowed to see.
|
|
18
|
+
*
|
|
19
|
+
* A bundle whose money cannot be derived is DROPPED from the feed, not published
|
|
20
|
+
* with a guessed figure. Three cases reach that door — an unpriced component, an
|
|
21
|
+
* empty composition, and a target price the kernel refuses — and all three mean
|
|
22
|
+
* the same thing to a storefront: this is not sellable today. Publishing it with a
|
|
23
|
+
* partial price would put a number on a shelf that no order line could reproduce.
|
|
24
|
+
*/
|
|
25
|
+
export interface SellableBundleHandlerDeps {
|
|
26
|
+
availabilityService: BundleAvailabilityService;
|
|
27
|
+
/** Narrow the feed to one currency; omitted means every currency an operator priced. */
|
|
28
|
+
currency?: string;
|
|
29
|
+
enabled: boolean;
|
|
30
|
+
}
|
|
31
|
+
export declare function createSellableBundleHandlers({ responder, sellableBundleListResponseSchema, SELLABLE_BUNDLE_CONTRACT_VERSION }: {
|
|
32
|
+
responder: BffResponder;
|
|
33
|
+
sellableBundleListResponseSchema: BundleResponseSchema;
|
|
34
|
+
SELLABLE_BUNDLE_CONTRACT_VERSION: "catalog.sellable-bundle.v1";
|
|
35
|
+
}): {
|
|
36
|
+
createSellableBundleHandler: (deps: SellableBundleHandlerDeps) => (req: HttpRequest, res: HttpResponse) => Promise<void>;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Derive the row a storefront sees, or `null` when the money cannot be stated.
|
|
40
|
+
*
|
|
41
|
+
* The allocator emits TWO lines for a quantity whose subtotal does not divide
|
|
42
|
+
* evenly, which is exactly why the feed carries lines rather than a unit price per
|
|
43
|
+
* component: `unitPrice x quantity === lineSubtotal` holds on every line, and the
|
|
44
|
+
* lines sum to the bundle's price with no remainder.
|
|
45
|
+
*/
|
|
46
|
+
export declare function toSellableBundle(composition: ActiveBundleComposition, availability: BundleAvailability): SellableBundle | null;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { allocateBundleTargetPrice } from "@openlup/core/pricing";
|
|
2
|
+
export function createSellableBundleHandlers({ responder, sellableBundleListResponseSchema, SELLABLE_BUNDLE_CONTRACT_VERSION }) {
|
|
3
|
+
const { sendBffError, sendBffSuccess, sendMethodNotAllowed } = responder;
|
|
4
|
+
function createSellableBundleHandler(deps) {
|
|
5
|
+
return async function handler(req, res) {
|
|
6
|
+
if (req.method !== "GET") {
|
|
7
|
+
sendMethodNotAllowed(res, ["GET"]);
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
if (!deps.enabled) {
|
|
11
|
+
sendBffError(res, "UPSTREAM_UNAVAILABLE", "Bundle reads are disabled", {
|
|
12
|
+
details: { reason: "feature_flag_disabled", featureFlag: "COMMERCE_BUNDLE_READ_ENABLED" },
|
|
13
|
+
});
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
const rows = await deps.availabilityService.describeActiveBundles(deps.currency ? { currency: deps.currency } : {});
|
|
18
|
+
const response = sellableBundleListResponseSchema.safeParse({
|
|
19
|
+
contractVersion: SELLABLE_BUNDLE_CONTRACT_VERSION,
|
|
20
|
+
bundles: rows
|
|
21
|
+
.map((row) => toSellableBundle(row.composition, row.availability))
|
|
22
|
+
.filter((bundle) => bundle !== null),
|
|
23
|
+
});
|
|
24
|
+
if (!response.success) {
|
|
25
|
+
sendBffError(res, "INVALID_RESPONSE", "Sellable bundle feed returned an invalid shape");
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
sendBffSuccess(res, response.data, { contractVersion: SELLABLE_BUNDLE_CONTRACT_VERSION }, { cacheControl: "no-store" });
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
sendBffError(res, "UPSTREAM_UNAVAILABLE", "Sellable bundles are unavailable");
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return { createSellableBundleHandler };
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Derive the row a storefront sees, or `null` when the money cannot be stated.
|
|
39
|
+
*
|
|
40
|
+
* The allocator emits TWO lines for a quantity whose subtotal does not divide
|
|
41
|
+
* evenly, which is exactly why the feed carries lines rather than a unit price per
|
|
42
|
+
* component: `unitPrice x quantity === lineSubtotal` holds on every line, and the
|
|
43
|
+
* lines sum to the bundle's price with no remainder.
|
|
44
|
+
*/
|
|
45
|
+
export function toSellableBundle(composition, availability) {
|
|
46
|
+
if (composition.components.length === 0)
|
|
47
|
+
return null;
|
|
48
|
+
if (composition.components.some((component) => component.referenceUnitPriceMinor === null)) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
const allocation = allocateBundleTargetPrice({
|
|
52
|
+
components: composition.components.map((component) => ({
|
|
53
|
+
sku: component.sku,
|
|
54
|
+
quantity: component.quantity,
|
|
55
|
+
unitPriceMinor: component.referenceUnitPriceMinor,
|
|
56
|
+
})),
|
|
57
|
+
targetPriceMinor: composition.targetPriceMinor,
|
|
58
|
+
});
|
|
59
|
+
if (!allocation.ok)
|
|
60
|
+
return null;
|
|
61
|
+
// Basis points come from the per-component allocation, so a component split
|
|
62
|
+
// across two lines reports the same discount on both halves rather than two
|
|
63
|
+
// rounded figures that disagree.
|
|
64
|
+
const discountBpsBySku = new Map(allocation.components.map((component) => [component.sku, component.discountBps]));
|
|
65
|
+
return {
|
|
66
|
+
code: composition.code,
|
|
67
|
+
title: composition.title,
|
|
68
|
+
fulfillmentMode: composition.fulfillmentMode,
|
|
69
|
+
price: { amountMinor: composition.targetPriceMinor, currency: composition.currency },
|
|
70
|
+
components: allocation.lines.map((line) => ({
|
|
71
|
+
sku: line.sku,
|
|
72
|
+
quantity: line.quantity,
|
|
73
|
+
effectiveUnitPriceMinor: line.effectiveUnitPriceMinor,
|
|
74
|
+
lineSubtotalMinor: line.lineSubtotalMinor,
|
|
75
|
+
discountBps: discountBpsBySku.get(line.sku) ?? 0,
|
|
76
|
+
})),
|
|
77
|
+
availability: {
|
|
78
|
+
status: availability.status,
|
|
79
|
+
sellableNow: availability.sellableNow,
|
|
80
|
+
limitingSku: availability.limitingSku,
|
|
81
|
+
reasonCode: availability.reasonCode,
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { z } from "./validation.js";
|
|
2
|
+
export { SELLABLE_CATALOG_CONTRACT_VERSION } from "./contracts/sellable.js";
|
|
3
|
+
export type { SellableCatalogItem, SellableCatalogProfile, SellableCatalogListResponse } from "./contracts/sellable.js";
|
|
4
|
+
export declare const sellableCatalogItemSchema: z.ZodObject<{
|
|
5
|
+
sku: z.ZodString;
|
|
6
|
+
title: z.ZodString;
|
|
7
|
+
unitPrice: z.ZodObject<{
|
|
8
|
+
amountMinor: z.ZodNumber;
|
|
9
|
+
currency: z.ZodString;
|
|
10
|
+
}, z.core.$strict>;
|
|
11
|
+
permittedPurchaseModes: z.ZodArray<z.ZodEnum<{
|
|
12
|
+
subscription: "subscription";
|
|
13
|
+
one_time: "one_time";
|
|
14
|
+
}>>;
|
|
15
|
+
}, z.core.$strict>;
|
|
16
|
+
export declare const sellableCatalogProfileSchema: z.ZodObject<{
|
|
17
|
+
id: z.ZodString;
|
|
18
|
+
brand: z.ZodString;
|
|
19
|
+
country: z.ZodString;
|
|
20
|
+
currency: z.ZodString;
|
|
21
|
+
locale: z.ZodString;
|
|
22
|
+
timezone: z.ZodString;
|
|
23
|
+
}, z.core.$strict>;
|
|
24
|
+
export declare const sellableCatalogListResponseSchema: z.ZodObject<{
|
|
25
|
+
contractVersion: z.ZodLiteral<"catalog.sellable.v1">;
|
|
26
|
+
profile: z.ZodObject<{
|
|
27
|
+
id: z.ZodString;
|
|
28
|
+
brand: z.ZodString;
|
|
29
|
+
country: z.ZodString;
|
|
30
|
+
currency: z.ZodString;
|
|
31
|
+
locale: z.ZodString;
|
|
32
|
+
timezone: z.ZodString;
|
|
33
|
+
}, z.core.$strict>;
|
|
34
|
+
items: z.ZodArray<z.ZodObject<{
|
|
35
|
+
sku: z.ZodString;
|
|
36
|
+
title: z.ZodString;
|
|
37
|
+
unitPrice: z.ZodObject<{
|
|
38
|
+
amountMinor: z.ZodNumber;
|
|
39
|
+
currency: z.ZodString;
|
|
40
|
+
}, z.core.$strict>;
|
|
41
|
+
permittedPurchaseModes: z.ZodArray<z.ZodEnum<{
|
|
42
|
+
subscription: "subscription";
|
|
43
|
+
one_time: "one_time";
|
|
44
|
+
}>>;
|
|
45
|
+
}, z.core.$strict>>;
|
|
46
|
+
}, z.core.$strict>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { z } from "./validation.js";
|
|
2
|
+
import { SELLABLE_CATALOG_CONTRACT_VERSION } from "./contracts/sellable.js";
|
|
3
|
+
export { SELLABLE_CATALOG_CONTRACT_VERSION } from "./contracts/sellable.js";
|
|
4
|
+
/** Minimal neutral storefront wire schema. */
|
|
5
|
+
const sellableCatalogMoneySchema = z.object({
|
|
6
|
+
amountMinor: z.number().int().nonnegative(),
|
|
7
|
+
currency: z.string().regex(/^[A-Z]{3}$/),
|
|
8
|
+
}).strict();
|
|
9
|
+
export const sellableCatalogItemSchema = z.object({
|
|
10
|
+
sku: z.string().trim().min(1).max(128).regex(/^[a-zA-Z0-9._:-]+$/),
|
|
11
|
+
title: z.string().trim().min(1).max(160),
|
|
12
|
+
unitPrice: sellableCatalogMoneySchema,
|
|
13
|
+
permittedPurchaseModes: z.array(z.enum(["one_time", "subscription"])).min(1).max(2),
|
|
14
|
+
}).strict();
|
|
15
|
+
export const sellableCatalogProfileSchema = z.object({
|
|
16
|
+
id: z.string().trim().min(1).max(120),
|
|
17
|
+
brand: z.string().trim().min(1).max(160),
|
|
18
|
+
country: z.string().regex(/^[A-Z]{2}$/),
|
|
19
|
+
currency: z.string().regex(/^[A-Z]{3}$/),
|
|
20
|
+
locale: z.string().trim().min(2).max(35),
|
|
21
|
+
timezone: z.string().trim().min(1).max(80),
|
|
22
|
+
}).strict();
|
|
23
|
+
export const sellableCatalogListResponseSchema = z.object({
|
|
24
|
+
contractVersion: z.literal(SELLABLE_CATALOG_CONTRACT_VERSION),
|
|
25
|
+
profile: sellableCatalogProfileSchema,
|
|
26
|
+
items: z.array(sellableCatalogItemSchema).min(1),
|
|
27
|
+
}).strict();
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** Sparse projections of canonical units, never an install chain. */
|
|
2
|
+
export declare const BUNDLE_MIGRATIONS: readonly [{
|
|
3
|
+
readonly id: "20260812140000_sellable_bundle_catalog";
|
|
4
|
+
readonly catalogPath: "db/platform/migrations/20260812140000_sellable_bundle_catalog.sql";
|
|
5
|
+
readonly position: 16;
|
|
6
|
+
readonly sha256: "62fbf0910a3a7a2ee47e2d798933fe0a5a273fd1242cae7cc78ff237ba39e394";
|
|
7
|
+
readonly url: URL;
|
|
8
|
+
}, {
|
|
9
|
+
readonly id: "20260812210000_bundle_operator_write_boundary";
|
|
10
|
+
readonly catalogPath: "db/platform/migrations/20260812210000_bundle_operator_write_boundary.sql";
|
|
11
|
+
readonly position: 19;
|
|
12
|
+
readonly sha256: "d0ec6674d7eedf4cd3ce0d8ccd7345ebe2f93da635316a68a6bd178ec55b2679";
|
|
13
|
+
readonly url: URL;
|
|
14
|
+
}];
|
|
15
|
+
/** Catalog foundation projections; install the complete canonical catalogue. */
|
|
16
|
+
export declare const CATALOG_MIGRATIONS: readonly [{
|
|
17
|
+
readonly id: "20260807140000_read_parity_catalog";
|
|
18
|
+
readonly catalogPath: "db/platform/migrations/20260807140000_read_parity_catalog.sql";
|
|
19
|
+
readonly position: 1;
|
|
20
|
+
readonly sha256: "fcf3827028c1651ae42a7c7d7472153825a3121c936d58a3d105befc7dece9ea";
|
|
21
|
+
readonly url: URL;
|
|
22
|
+
}, {
|
|
23
|
+
readonly id: "20260810120000_storefront_catalog_attributes";
|
|
24
|
+
readonly catalogPath: "db/platform/migrations/20260810120000_storefront_catalog_attributes.sql";
|
|
25
|
+
readonly position: 8;
|
|
26
|
+
readonly sha256: "2bf938f5d0076193557bea834f86dbd9bcb7f3e2de00f99fb91cc1229b93a14b";
|
|
27
|
+
readonly url: URL;
|
|
28
|
+
}, {
|
|
29
|
+
readonly id: "20260827220000_catalog_document_revision_foundation";
|
|
30
|
+
readonly catalogPath: "db/platform/migrations/20260827220000_catalog_document_revision_foundation.sql";
|
|
31
|
+
readonly position: 58;
|
|
32
|
+
readonly sha256: "715e74d655b76d35b8a49e0d2a5625594a6c4856863f369cd1eadcd1fb1ae90e";
|
|
33
|
+
readonly url: URL;
|
|
34
|
+
}, {
|
|
35
|
+
readonly id: "20260827220001_catalog_document_revision_foundation_validate";
|
|
36
|
+
readonly catalogPath: "db/platform/migrations/20260827220001_catalog_document_revision_foundation_validate.sql";
|
|
37
|
+
readonly position: 59;
|
|
38
|
+
readonly sha256: "f3cd7fb61dcebac1cf0468cf2d7c1f9dd036817ab0094c72864266e307b269de";
|
|
39
|
+
readonly url: URL;
|
|
40
|
+
}, {
|
|
41
|
+
readonly id: "20260908100000_catalog_draft_foundation";
|
|
42
|
+
readonly catalogPath: "db/platform/migrations/20260908100000_catalog_draft_foundation.sql";
|
|
43
|
+
readonly position: 61;
|
|
44
|
+
readonly sha256: "ac0d614cdb2874df8fbdbbbda4f9455986ef95241fe1334b0b54a1fa6478ab2e";
|
|
45
|
+
readonly url: URL;
|
|
46
|
+
}];
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Sparse projections of canonical units, never an install chain. */
|
|
2
|
+
export const BUNDLE_MIGRATIONS = [
|
|
3
|
+
{ id: "20260812140000_sellable_bundle_catalog", catalogPath: "db/platform/migrations/20260812140000_sellable_bundle_catalog.sql", position: 16, sha256: "62fbf0910a3a7a2ee47e2d798933fe0a5a273fd1242cae7cc78ff237ba39e394", url: new URL("../../sql/20260812140000_sellable_bundle_catalog.sql", import.meta.url) },
|
|
4
|
+
{ id: "20260812210000_bundle_operator_write_boundary", catalogPath: "db/platform/migrations/20260812210000_bundle_operator_write_boundary.sql", position: 19, sha256: "d0ec6674d7eedf4cd3ce0d8ccd7345ebe2f93da635316a68a6bd178ec55b2679", url: new URL("../../sql/20260812210000_bundle_operator_write_boundary.sql", import.meta.url) },
|
|
5
|
+
];
|
|
6
|
+
/** Catalog foundation projections; install the complete canonical catalogue. */
|
|
7
|
+
export const CATALOG_MIGRATIONS = [
|
|
8
|
+
{ id: "20260807140000_read_parity_catalog", catalogPath: "db/platform/migrations/20260807140000_read_parity_catalog.sql", position: 1, sha256: "fcf3827028c1651ae42a7c7d7472153825a3121c936d58a3d105befc7dece9ea", url: new URL("../../sql/20260807140000_read_parity_catalog.sql", import.meta.url) },
|
|
9
|
+
{ id: "20260810120000_storefront_catalog_attributes", catalogPath: "db/platform/migrations/20260810120000_storefront_catalog_attributes.sql", position: 8, sha256: "2bf938f5d0076193557bea834f86dbd9bcb7f3e2de00f99fb91cc1229b93a14b", url: new URL("../../sql/20260810120000_storefront_catalog_attributes.sql", import.meta.url) },
|
|
10
|
+
{ id: "20260827220000_catalog_document_revision_foundation", catalogPath: "db/platform/migrations/20260827220000_catalog_document_revision_foundation.sql", position: 58, sha256: "715e74d655b76d35b8a49e0d2a5625594a6c4856863f369cd1eadcd1fb1ae90e", url: new URL("../../sql/20260827220000_catalog_document_revision_foundation.sql", import.meta.url) },
|
|
11
|
+
{ id: "20260827220001_catalog_document_revision_foundation_validate", catalogPath: "db/platform/migrations/20260827220001_catalog_document_revision_foundation_validate.sql", position: 59, sha256: "f3cd7fb61dcebac1cf0468cf2d7c1f9dd036817ab0094c72864266e307b269de", url: new URL("../../sql/20260827220001_catalog_document_revision_foundation_validate.sql", import.meta.url) },
|
|
12
|
+
{ id: "20260908100000_catalog_draft_foundation", catalogPath: "db/platform/migrations/20260908100000_catalog_draft_foundation.sql", position: 61, sha256: "ac0d614cdb2874df8fbdbbbda4f9455986ef95241fe1334b0b54a1fa6478ab2e", url: new URL("../../sql/20260908100000_catalog_draft_foundation.sql", import.meta.url) },
|
|
13
|
+
];
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
declare const subjectBrand: unique symbol, feedbackBrand: unique symbol, receiptBrand: unique symbol, readBrand: unique symbol;
|
|
2
|
+
type Operation = "profile.sync" | "consent.read" | "erasure.request" | "erasure.read";
|
|
3
|
+
type Mutation = "profile.sync" | "erasure.request";
|
|
4
|
+
type SubjectReference = string & {
|
|
5
|
+
readonly [subjectBrand]: "opaque-subject";
|
|
6
|
+
};
|
|
7
|
+
export type SyncBinding = Readonly<{
|
|
8
|
+
account: string;
|
|
9
|
+
environment: string;
|
|
10
|
+
namespace: string;
|
|
11
|
+
mappingVersion: string;
|
|
12
|
+
}>;
|
|
13
|
+
export type SyncAudience = Readonly<{
|
|
14
|
+
kind: "account-wide";
|
|
15
|
+
} | {
|
|
16
|
+
kind: "audience";
|
|
17
|
+
reference: string;
|
|
18
|
+
}>;
|
|
19
|
+
export type ProfileProjection = Readonly<{
|
|
20
|
+
email: string;
|
|
21
|
+
externalReference: string;
|
|
22
|
+
fields: Readonly<Partial<Record<"givenName" | "familyName" | "locale", string>>>;
|
|
23
|
+
properties: Readonly<Record<string, string | number | boolean>>;
|
|
24
|
+
}>;
|
|
25
|
+
type Payload = {
|
|
26
|
+
"profile.sync": {
|
|
27
|
+
projection: ProfileProjection;
|
|
28
|
+
consentEvidenceReference: string;
|
|
29
|
+
};
|
|
30
|
+
"consent.read": Readonly<Record<string, never>>;
|
|
31
|
+
"erasure.request": {
|
|
32
|
+
caseReference: string;
|
|
33
|
+
authorityReference: string;
|
|
34
|
+
};
|
|
35
|
+
"erasure.read": {
|
|
36
|
+
receipt: SyncReceipt<"erasure.request">;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
export type SyncRequest<O extends Operation = Operation> = {
|
|
40
|
+
[K in O]: Readonly<{
|
|
41
|
+
binding: SyncBinding;
|
|
42
|
+
audience: SyncAudience;
|
|
43
|
+
subjectReference: SubjectReference;
|
|
44
|
+
requestId: string;
|
|
45
|
+
generation: number;
|
|
46
|
+
deadlineMs: number;
|
|
47
|
+
authorizationReference: string;
|
|
48
|
+
operation: K;
|
|
49
|
+
payload: Readonly<Payload[K]>;
|
|
50
|
+
}>;
|
|
51
|
+
}[O];
|
|
52
|
+
export type NegativeFeedback = Readonly<{
|
|
53
|
+
[feedbackBrand]: "provider-negative";
|
|
54
|
+
sourceReference: string;
|
|
55
|
+
occurredAtMs: number | null;
|
|
56
|
+
audience: SyncAudience;
|
|
57
|
+
}> & Readonly<{
|
|
58
|
+
kind: "unsubscribe" | "complaint" | "manual_block";
|
|
59
|
+
outcome: "deny_permission";
|
|
60
|
+
} | {
|
|
61
|
+
kind: "hard_bounce" | "invalid_address";
|
|
62
|
+
outcome: "deny_delivery";
|
|
63
|
+
}>;
|
|
64
|
+
export type ConsentReadback = Readonly<{
|
|
65
|
+
audience: SyncAudience;
|
|
66
|
+
observedAtMs: number;
|
|
67
|
+
remoteConsent: "subscribed" | "unsubscribed" | "pending" | "never_subscribed" | "unknown";
|
|
68
|
+
feedback: readonly NegativeFeedback[];
|
|
69
|
+
coverage: "complete" | "partial" | "unsupported";
|
|
70
|
+
}>;
|
|
71
|
+
export type SyncFailure<O extends Operation = Operation> = {
|
|
72
|
+
kind: "refused";
|
|
73
|
+
reason: "invalid_request" | "binding" | "provenance" | "stale" | "deadline" | "unsupported" | "verification_pending" | "restricted" | "identity_conflict" | "provider_refusal";
|
|
74
|
+
} | {
|
|
75
|
+
kind: "retryable";
|
|
76
|
+
reason: "cooling" | "host" | "provider";
|
|
77
|
+
afterSeconds: number;
|
|
78
|
+
} | ({
|
|
79
|
+
kind: "unknown";
|
|
80
|
+
reason: "dispatch_unresolved" | "acceptance_ambiguous" | "record_failed";
|
|
81
|
+
} & (O extends Mutation ? {
|
|
82
|
+
receipt?: SyncReceipt<O>;
|
|
83
|
+
} : {
|
|
84
|
+
receipt?: never;
|
|
85
|
+
}));
|
|
86
|
+
export type SyncReceipt<O extends Mutation> = Readonly<{
|
|
87
|
+
[receiptBrand]: O;
|
|
88
|
+
operation: O;
|
|
89
|
+
request: SyncRequest<O>;
|
|
90
|
+
}>;
|
|
91
|
+
type MutationSuccess<O extends Mutation> = {
|
|
92
|
+
kind: "accepted" | "acknowledged";
|
|
93
|
+
receipt: SyncReceipt<O>;
|
|
94
|
+
journal: "confirmed" | "unknown";
|
|
95
|
+
};
|
|
96
|
+
type Success = {
|
|
97
|
+
"profile.sync": MutationSuccess<"profile.sync">;
|
|
98
|
+
"consent.read": {
|
|
99
|
+
kind: "completed";
|
|
100
|
+
value: ConsentReadback;
|
|
101
|
+
journal: "confirmed" | "unknown";
|
|
102
|
+
};
|
|
103
|
+
"erasure.request": MutationSuccess<"erasure.request">;
|
|
104
|
+
"erasure.read": MutationSuccess<"erasure.request"> | {
|
|
105
|
+
kind: "completed";
|
|
106
|
+
receipt: SyncReceipt<"erasure.request">;
|
|
107
|
+
journal: "confirmed" | "unknown";
|
|
108
|
+
evidence: Readonly<{
|
|
109
|
+
[readBrand]: "provider-read";
|
|
110
|
+
readRequestId: string;
|
|
111
|
+
observedAtMs: number;
|
|
112
|
+
evidenceReference: string;
|
|
113
|
+
assurance: "provider_record_deleted" | "provider_erasure_confirmed";
|
|
114
|
+
}>;
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
export type SyncResult<O extends Operation = Operation> = {
|
|
118
|
+
[K in O]: Readonly<Pick<SyncRequest<K>, "binding" | "operation" | "subjectReference" | "requestId" | "generation">> & Readonly<Success[K] | SyncFailure<K>>;
|
|
119
|
+
}[O];
|
|
120
|
+
export type SyncCapabilities = Readonly<{
|
|
121
|
+
binding: SyncBinding;
|
|
122
|
+
audience: SyncAudience;
|
|
123
|
+
operations: Readonly<Record<Operation, {
|
|
124
|
+
state: "supported";
|
|
125
|
+
evidenceReference: string;
|
|
126
|
+
maxRequestBytes: number;
|
|
127
|
+
maxFeedbackItems: number;
|
|
128
|
+
} | {
|
|
129
|
+
state: "unsupported" | "verification_pending";
|
|
130
|
+
}>>;
|
|
131
|
+
}>;
|
|
132
|
+
export interface ProfileSyncPort {
|
|
133
|
+
sync(request: SyncRequest<"profile.sync">): Promise<SyncResult<"profile.sync">>;
|
|
134
|
+
}
|
|
135
|
+
export interface ConsentReadPort {
|
|
136
|
+
read(request: SyncRequest<"consent.read">): Promise<SyncResult<"consent.read">>;
|
|
137
|
+
}
|
|
138
|
+
export interface ErasurePort {
|
|
139
|
+
request(request: SyncRequest<"erasure.request">): Promise<SyncResult<"erasure.request">>;
|
|
140
|
+
read(request: SyncRequest<"erasure.read">): Promise<SyncResult<"erasure.read">>;
|
|
141
|
+
}
|
|
142
|
+
export interface MarketingSyncProvider {
|
|
143
|
+
readonly capabilities: SyncCapabilities;
|
|
144
|
+
readonly profiles: ProfileSyncPort;
|
|
145
|
+
readonly consent: ConsentReadPort;
|
|
146
|
+
readonly erasure: ErasurePort;
|
|
147
|
+
}
|
|
148
|
+
export type ConsentPushRequest = Readonly<Omit<SyncRequest<"profile.sync">, "operation" | "audience" | "payload"> & {
|
|
149
|
+
operation: "consent.push";
|
|
150
|
+
} & ({
|
|
151
|
+
audience: Extract<SyncAudience, {
|
|
152
|
+
kind: "audience";
|
|
153
|
+
}>;
|
|
154
|
+
payload: Readonly<{
|
|
155
|
+
action: "grant";
|
|
156
|
+
consentEvidenceReference: string;
|
|
157
|
+
}>;
|
|
158
|
+
} | {
|
|
159
|
+
audience: SyncAudience;
|
|
160
|
+
payload: Readonly<{
|
|
161
|
+
action: "withdraw";
|
|
162
|
+
consentEvidenceReference: string;
|
|
163
|
+
}>;
|
|
164
|
+
})>;
|
|
165
|
+
declare const pushReceiptBrand: unique symbol;
|
|
166
|
+
type PushReceipt = Readonly<{
|
|
167
|
+
[pushReceiptBrand]: "consent.push";
|
|
168
|
+
operation: "consent.push";
|
|
169
|
+
request: ConsentPushRequest;
|
|
170
|
+
}>;
|
|
171
|
+
type PushFailure = Exclude<SyncFailure, {
|
|
172
|
+
kind: "unknown";
|
|
173
|
+
}> | (Omit<Extract<SyncFailure, {
|
|
174
|
+
kind: "unknown";
|
|
175
|
+
}>, "receipt"> & {
|
|
176
|
+
receipt?: PushReceipt;
|
|
177
|
+
});
|
|
178
|
+
export type ConsentPushResult = Readonly<Pick<ConsentPushRequest, "binding" | "operation" | "subjectReference" | "requestId" | "generation">> & Readonly<{
|
|
179
|
+
kind: "accepted" | "acknowledged";
|
|
180
|
+
receipt: PushReceipt;
|
|
181
|
+
journal: "confirmed" | "unknown";
|
|
182
|
+
reconcileRequired: boolean;
|
|
183
|
+
} | PushFailure>;
|
|
184
|
+
export interface ConsentPushPort {
|
|
185
|
+
readonly capabilities: SyncCapabilities & Readonly<{
|
|
186
|
+
operations: SyncCapabilities["operations"] & Readonly<{
|
|
187
|
+
"consent.push": SyncCapabilities["operations"]["profile.sync"] & Readonly<{
|
|
188
|
+
grantEffect: "audience" | "account-wide";
|
|
189
|
+
}>;
|
|
190
|
+
}>;
|
|
191
|
+
}>;
|
|
192
|
+
push(request: ConsentPushRequest): Promise<ConsentPushResult>;
|
|
193
|
+
}
|
|
194
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { ConsentPushRequest } from "../contracts/marketingSync.js";
|
|
2
|
+
/** Private singleton translation. Local evidence is deliberately absent from wire attributes. */
|
|
3
|
+
export declare function consentCommand(request: ConsentPushRequest, target: {
|
|
4
|
+
id: string;
|
|
5
|
+
email: string;
|
|
6
|
+
}, listId: string | undefined): {
|
|
7
|
+
method: string;
|
|
8
|
+
path: string;
|
|
9
|
+
expected: number[];
|
|
10
|
+
body: {
|
|
11
|
+
data: {
|
|
12
|
+
type: string;
|
|
13
|
+
id: string;
|
|
14
|
+
}[];
|
|
15
|
+
};
|
|
16
|
+
} | {
|
|
17
|
+
method: string;
|
|
18
|
+
path: string;
|
|
19
|
+
expected: number[];
|
|
20
|
+
body: {
|
|
21
|
+
data: {
|
|
22
|
+
relationships?: {
|
|
23
|
+
list: {
|
|
24
|
+
data: {
|
|
25
|
+
type: string;
|
|
26
|
+
id: string | undefined;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
} | undefined;
|
|
30
|
+
type: string;
|
|
31
|
+
attributes: {
|
|
32
|
+
profiles: {
|
|
33
|
+
data: {
|
|
34
|
+
attributes: {
|
|
35
|
+
email: string;
|
|
36
|
+
subscriptions: {
|
|
37
|
+
email: {
|
|
38
|
+
marketing: {
|
|
39
|
+
consent: string;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
id?: string | undefined;
|
|
45
|
+
type: string;
|
|
46
|
+
}[];
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Private singleton translation. Local evidence is deliberately absent from wire attributes. */
|
|
2
|
+
export function consentCommand(request, target, listId) {
|
|
3
|
+
if (request.payload.action === "withdraw" && request.audience.kind === "audience") {
|
|
4
|
+
if (!listId)
|
|
5
|
+
throw new Error("scope");
|
|
6
|
+
return { method: "DELETE", path: `/api/lists/${listId}/relationships/profiles`, expected: [204],
|
|
7
|
+
body: { data: [{ type: "profile", id: target.id }] } };
|
|
8
|
+
}
|
|
9
|
+
const grant = request.payload.action === "grant";
|
|
10
|
+
const type = grant ? "profile-subscription-bulk-create-job" : "profile-subscription-bulk-delete-job";
|
|
11
|
+
if (grant && !listId)
|
|
12
|
+
throw new Error("scope");
|
|
13
|
+
return { method: "POST", path: `/api/${type}s`, expected: [202], body: { data: { type,
|
|
14
|
+
attributes: { profiles: { data: [{ type: "profile", ...(grant ? { id: target.id } : {}), attributes: {
|
|
15
|
+
email: target.email, subscriptions: { email: { marketing: { consent: grant ? "SUBSCRIBED" : "UNSUBSCRIBED" } } },
|
|
16
|
+
} }] } },
|
|
17
|
+
...(grant ? { relationships: { list: { data: { type: "list", id: listId } } } } : {}),
|
|
18
|
+
} } };
|
|
19
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { ConsentPushPort, ConsentPushRequest, MarketingSyncProvider, ProfileProjection, SyncAudience, SyncBinding, SyncRequest } from "../contracts/marketingSync.js";
|
|
2
|
+
import type { MarketingAutomationClaim, MarketingAutomationProviderPort, MarketingAutomationScope } from "../contracts/marketingAutomation.js";
|
|
3
|
+
import type { NewsletterSyncProviderPort } from "../contracts/newsletter.js";
|
|
4
|
+
type Request = SyncRequest | ConsentPushRequest;
|
|
5
|
+
type Operation = Request["operation"];
|
|
6
|
+
type Subject = {
|
|
7
|
+
binding: SyncBinding;
|
|
8
|
+
subjectReference: string;
|
|
9
|
+
generation: number;
|
|
10
|
+
erased: boolean;
|
|
11
|
+
id?: string;
|
|
12
|
+
email: string;
|
|
13
|
+
externalReference: string;
|
|
14
|
+
ownedFields: readonly (keyof ProfileProjection["fields"])[];
|
|
15
|
+
ownedProperties: readonly string[];
|
|
16
|
+
};
|
|
17
|
+
type LocalEvidence = {
|
|
18
|
+
binding: SyncBinding;
|
|
19
|
+
subjectReference: string;
|
|
20
|
+
audience: SyncAudience;
|
|
21
|
+
purpose: string;
|
|
22
|
+
action: "profile" | "grant" | "withdraw" | "erase";
|
|
23
|
+
current: boolean;
|
|
24
|
+
projection?: ProfileProjection;
|
|
25
|
+
caseReference?: string;
|
|
26
|
+
authorityReference?: string;
|
|
27
|
+
allPurposes?: boolean;
|
|
28
|
+
};
|
|
29
|
+
type Authorization = {
|
|
30
|
+
binding: SyncBinding;
|
|
31
|
+
subjectReference: string;
|
|
32
|
+
audience: SyncAudience;
|
|
33
|
+
operation: Operation;
|
|
34
|
+
expiresAtMs: number;
|
|
35
|
+
};
|
|
36
|
+
type Verification = {
|
|
37
|
+
binding: SyncBinding;
|
|
38
|
+
audience: SyncAudience;
|
|
39
|
+
evidenceReference: string;
|
|
40
|
+
revision: string;
|
|
41
|
+
operations: readonly Exclude<Operation, "erasure.read">[];
|
|
42
|
+
scopes: readonly string[];
|
|
43
|
+
noTrigger: {
|
|
44
|
+
profile: boolean;
|
|
45
|
+
accountWideGrant: boolean;
|
|
46
|
+
doi: boolean;
|
|
47
|
+
};
|
|
48
|
+
safeCreate: boolean;
|
|
49
|
+
safeListRemoval: boolean;
|
|
50
|
+
suppressionSafeAtEffect: boolean;
|
|
51
|
+
negativeSourcesComplete: boolean;
|
|
52
|
+
listMappings: Readonly<Record<string, SyncAudience>>;
|
|
53
|
+
};
|
|
54
|
+
type Storage = {
|
|
55
|
+
/** Shared plain mutable record for this account/environment, independent of CAS failure.
|
|
56
|
+
* Start quarantined after host restart; only explicit host reconciliation clears it.
|
|
57
|
+
* Every adapter namespace/instance uses the same record. Never reset it at construction. */
|
|
58
|
+
availability: {
|
|
59
|
+
account: string;
|
|
60
|
+
environment: string;
|
|
61
|
+
busy: boolean;
|
|
62
|
+
quarantined: boolean;
|
|
63
|
+
};
|
|
64
|
+
/** Shared across every adapter for the account. false proves no commit; throws may have committed. */
|
|
65
|
+
get(key: string): Promise<string | null>;
|
|
66
|
+
compareAndSwap(key: string, expected: string | null, value: string): Promise<boolean>;
|
|
67
|
+
};
|
|
68
|
+
type Configuration = {
|
|
69
|
+
binding: SyncBinding;
|
|
70
|
+
audience: SyncAudience;
|
|
71
|
+
/** Synchronous current host registry lookup; never parses an opaque subject reference. */
|
|
72
|
+
subjectMapper(subjectReference: string): Subject | undefined;
|
|
73
|
+
resolveCredential(): {
|
|
74
|
+
binding: SyncBinding;
|
|
75
|
+
key: string;
|
|
76
|
+
} | undefined;
|
|
77
|
+
authorizations: ReadonlyMap<string, Authorization>;
|
|
78
|
+
localEvidence: ReadonlyMap<string, LocalEvidence>;
|
|
79
|
+
storage: Storage;
|
|
80
|
+
verification?: Verification;
|
|
81
|
+
/** Private authenticated settlement facts from the host's existing reconciliation journal. */
|
|
82
|
+
grantSettlements?: ReadonlyMap<string, {
|
|
83
|
+
binding: SyncBinding;
|
|
84
|
+
subjectReference: string;
|
|
85
|
+
requestId: string;
|
|
86
|
+
source: "provider-read";
|
|
87
|
+
evidenceReference: string;
|
|
88
|
+
settledAtMs: number;
|
|
89
|
+
}>;
|
|
90
|
+
fetch?: typeof fetch;
|
|
91
|
+
now?: () => number;
|
|
92
|
+
maxRequestBytes?: number;
|
|
93
|
+
maxResponseBytes?: number;
|
|
94
|
+
maxFeedbackItems?: number;
|
|
95
|
+
timeoutMs?: number;
|
|
96
|
+
rateIntervalMs?: number;
|
|
97
|
+
legacy?: {
|
|
98
|
+
scope: MarketingAutomationScope;
|
|
99
|
+
claims: ReadonlyMap<string, {
|
|
100
|
+
claim: MarketingAutomationClaim;
|
|
101
|
+
request: Request;
|
|
102
|
+
}>;
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
/** Opt-in Node adapter; owner TEST evidence is required before any bound operation is enabled. */
|
|
106
|
+
export declare function createKlaviyoMarketingAdapter(input: Configuration): MarketingSyncProvider & MarketingAutomationProviderPort & {
|
|
107
|
+
readonly consentPush: ConsentPushPort;
|
|
108
|
+
readonly newsletter: NewsletterSyncProviderPort;
|
|
109
|
+
};
|
|
110
|
+
export {};
|