@openlup/platform 0.17.0 → 0.19.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 +143 -0
- package/README.md +299 -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 +21 -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 +173 -0
- package/dist/catalog/bundleCatalogReadPort.js +36 -0
- package/dist/catalog/bundleCatalogStore.d.ts +5 -0
- package/dist/catalog/bundleCatalogStore.js +156 -0
- package/dist/catalog/bundlePriceSelection.d.ts +13 -0
- package/dist/catalog/bundlePriceSelection.js +26 -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 +241 -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/effectiveBundleCatalogQueries.d.ts +8 -0
- package/dist/catalog/effectiveBundleCatalogQueries.js +79 -0
- package/dist/catalog/effectiveBundleCatalogStore.d.ts +4 -0
- package/dist/catalog/effectiveBundleCatalogStore.js +156 -0
- package/dist/catalog/foundationSchemas.d.ts +98 -0
- package/dist/catalog/foundationSchemas.js +110 -0
- package/dist/catalog/index.d.ts +44 -0
- package/dist/catalog/index.js +26 -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 +3 -0
- package/dist/composition/index.js +3 -0
- package/dist/composition/inventoryContribution.d.ts +16 -0
- package/dist/composition/inventoryContribution.js +17 -0
- package/dist/composition/shippingContribution.d.ts +16 -0
- package/dist/composition/shippingContribution.js +17 -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/bindings.d.ts +35 -0
- package/dist/inventory/bindings.js +45 -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 +14 -0
- package/dist/inventory/index.js +11 -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 +28 -0
- package/dist/pricing/commercePriceAuthority.js +140 -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 +35 -0
- package/dist/pricing/commerceQuoteHandler.js +122 -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 +74 -0
- package/dist/pricing/dbBackedCommerceQuotePort.js +223 -0
- package/dist/pricing/dbBackedCommerceQuoteSnapshotVerifier.d.ts +4 -0
- package/dist/pricing/dbBackedCommerceQuoteSnapshotVerifier.js +25 -0
- package/dist/pricing/index.d.ts +52 -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/quoteShippingPromotions.d.ts +20 -0
- package/dist/pricing/quoteShippingPromotions.js +1 -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/commerceQuoteReadPreparation.d.ts +3 -0
- package/dist/promotions/commerceQuoteReadPreparation.js +27 -0
- package/dist/promotions/contracts/index.d.ts +387 -0
- package/dist/promotions/contracts/index.js +1 -0
- package/dist/promotions/index.d.ts +25 -0
- package/dist/promotions/index.js +22 -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 +70 -0
- package/dist/promotions/promotionQuoteAcceptance.js +202 -0
- package/dist/promotions/promotionQuoteBinding.d.ts +73 -0
- package/dist/promotions/promotionQuoteBinding.js +87 -0
- package/dist/promotions/quoteAcquisitionBinding.d.ts +6 -0
- package/dist/promotions/quoteAcquisitionBinding.js +1 -0
- package/dist/promotions/quoteAutomaticPromotions.d.ts +26 -0
- package/dist/promotions/quoteAutomaticPromotions.js +60 -0
- package/dist/promotions/quotePromoDiscounts.d.ts +36 -0
- package/dist/promotions/quotePromoDiscounts.js +146 -0
- package/dist/promotions/quotePromotionCodeAdjustments.d.ts +27 -0
- package/dist/promotions/quotePromotionCodeAdjustments.js +223 -0
- package/dist/promotions/quotePromotionV2.d.ts +30 -0
- package/dist/promotions/quotePromotionV2.js +186 -0
- package/dist/promotions/quoteShippingPolicy.d.ts +23 -0
- package/dist/promotions/quoteShippingPolicy.js +47 -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/bindings.d.ts +57 -0
- package/dist/shipping/bindings.js +46 -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 +4 -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/contracts/threshold.d.ts +22 -0
- package/dist/shipping/contracts/threshold.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 +20 -0
- package/dist/shipping/index.js +10 -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/dist/shipping/thresholdPolicy.d.ts +42 -0
- package/dist/shipping/thresholdPolicy.js +60 -0
- package/package.json +62 -2
- package/release-gates.json +263 -53
- 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 +263 -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 +181 -0
- package/src/catalog/bundleCatalogStore.ts +274 -0
- package/src/catalog/bundlePriceSelection.ts +25 -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/effectiveBundleCatalogQueries.ts +80 -0
- package/src/catalog/effectiveBundleCatalogStore.ts +208 -0
- package/src/catalog/foundationSchemas.ts +123 -0
- package/src/catalog/index.ts +44 -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 +3 -0
- package/src/composition/inventoryContribution.ts +24 -0
- package/src/composition/shippingContribution.ts +24 -0
- package/src/inventory/adminInventory.ts +251 -0
- package/src/inventory/bindings.ts +61 -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 +22 -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 +169 -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 +199 -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 +280 -0
- package/src/pricing/dbBackedCommerceQuoteSnapshotVerifier.ts +49 -0
- package/src/pricing/index.ts +53 -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/quoteShippingPromotions.ts +21 -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/commerceQuoteReadPreparation.ts +29 -0
- package/src/promotions/contracts/index.ts +387 -0
- package/src/promotions/index.ts +25 -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 +291 -0
- package/src/promotions/promotionQuoteBinding.ts +163 -0
- package/src/promotions/quoteAcquisitionBinding.ts +7 -0
- package/src/promotions/quoteAutomaticPromotions.ts +84 -0
- package/src/promotions/quotePromoDiscounts.ts +194 -0
- package/src/promotions/quotePromotionCodeAdjustments.ts +277 -0
- package/src/promotions/quotePromotionV2.ts +239 -0
- package/src/promotions/quoteShippingPolicy.ts +61 -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/bindings.ts +74 -0
- package/src/shipping/contracts/address.ts +71 -0
- package/src/shipping/contracts/delivery.ts +114 -0
- package/src/shipping/contracts/index.ts +19 -0
- package/src/shipping/contracts/shippingRate.ts +7 -0
- package/src/shipping/contracts/threshold.ts +22 -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 +26 -0
- package/src/shipping/pickupPointSearchPort.ts +8 -0
- package/src/shipping/readDeliverySelectionEvidence.ts +51 -0
- package/src/shipping/shippingRatePorts.ts +36 -0
- package/src/shipping/thresholdPolicy.ts +65 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
-- Catalog Wave 2a adds an inert, portable document-revision and price-policy foundation.
|
|
2
|
+
-- It deliberately carries no rows or runtime bindings: existing catalog and pricing authorities stay active.
|
|
3
|
+
-- Application-owned document payloads and SHA-256 digests are opaque here; this stock PostgreSQL forward
|
|
4
|
+
-- validates only the digest shape and relational invariants, never JSON content or jsonb text representation.
|
|
5
|
+
|
|
6
|
+
CREATE TABLE public.catalog_product_document_revisions (
|
|
7
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
8
|
+
product_id uuid NOT NULL REFERENCES public.catalog_products (id) ON DELETE RESTRICT,
|
|
9
|
+
revision_no integer NOT NULL CHECK (revision_no > 0),
|
|
10
|
+
schema_id text NOT NULL CHECK (btrim(schema_id) <> ''),
|
|
11
|
+
document_ref text NOT NULL CHECK (btrim(document_ref) <> ''),
|
|
12
|
+
document_payload jsonb NOT NULL,
|
|
13
|
+
source_ref text NOT NULL CHECK (btrim(source_ref) <> ''),
|
|
14
|
+
provenance jsonb NOT NULL,
|
|
15
|
+
digest text NOT NULL CHECK (digest ~ '^[0-9a-f]{64}$'),
|
|
16
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
17
|
+
CONSTRAINT catalog_product_document_revisions_product_revision_key UNIQUE (product_id, revision_no),
|
|
18
|
+
CONSTRAINT catalog_product_document_revisions_product_digest_key UNIQUE (product_id, digest),
|
|
19
|
+
CONSTRAINT catalog_product_document_revisions_product_id_id_key UNIQUE (product_id, id)
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
ALTER TABLE public.catalog_skus
|
|
23
|
+
ADD COLUMN asset_ref text CHECK (asset_ref IS NULL OR btrim(asset_ref) <> '');
|
|
24
|
+
|
|
25
|
+
-- catalog_skus.id is already a primary key, so every existing (product_id, id)
|
|
26
|
+
-- pair is unique. The production manifest runner is transactional, which makes
|
|
27
|
+
-- CONCURRENTLY unavailable; keep this exception scoped to this reference key.
|
|
28
|
+
-- squawk-ignore require-concurrent-index-creation
|
|
29
|
+
CREATE UNIQUE INDEX catalog_skus_product_id_id_key
|
|
30
|
+
ON public.catalog_skus (product_id, id);
|
|
31
|
+
|
|
32
|
+
ALTER TABLE public.catalog_products
|
|
33
|
+
ADD COLUMN primary_sku_id uuid,
|
|
34
|
+
ADD COLUMN current_document_revision_id uuid,
|
|
35
|
+
ADD CONSTRAINT catalog_products_primary_sku_same_product_fkey
|
|
36
|
+
FOREIGN KEY (id, primary_sku_id)
|
|
37
|
+
REFERENCES public.catalog_skus (product_id, id)
|
|
38
|
+
ON DELETE RESTRICT NOT VALID,
|
|
39
|
+
ADD CONSTRAINT catalog_products_current_document_revision_same_product_fkey
|
|
40
|
+
FOREIGN KEY (id, current_document_revision_id)
|
|
41
|
+
REFERENCES public.catalog_product_document_revisions (product_id, id)
|
|
42
|
+
ON DELETE RESTRICT NOT VALID;
|
|
43
|
+
|
|
44
|
+
CREATE TABLE public.catalog_sku_identifiers (
|
|
45
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
46
|
+
catalog_sku_id uuid NOT NULL REFERENCES public.catalog_skus (id) ON DELETE RESTRICT,
|
|
47
|
+
issuer text NOT NULL CHECK (btrim(issuer) <> ''),
|
|
48
|
+
identifier_kind text NOT NULL CHECK (identifier_kind IN ('gtin', 'other')),
|
|
49
|
+
normalized_value text NOT NULL CHECK (btrim(normalized_value) <> ''),
|
|
50
|
+
pack_kind text NOT NULL CHECK (pack_kind IN ('unit', 'collective')),
|
|
51
|
+
quantity integer NOT NULL CHECK (quantity > 0),
|
|
52
|
+
is_primary boolean NOT NULL DEFAULT false,
|
|
53
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
54
|
+
CONSTRAINT catalog_sku_identifiers_issuer_value_key UNIQUE (issuer, normalized_value),
|
|
55
|
+
CONSTRAINT catalog_sku_identifiers_pack_quantity_check
|
|
56
|
+
CHECK (
|
|
57
|
+
(pack_kind = 'unit' AND quantity = 1)
|
|
58
|
+
OR (pack_kind = 'collective' AND quantity > 1)
|
|
59
|
+
),
|
|
60
|
+
CONSTRAINT catalog_sku_identifiers_primary_gtin_unit_check
|
|
61
|
+
CHECK (
|
|
62
|
+
NOT is_primary
|
|
63
|
+
OR (identifier_kind = 'gtin' AND pack_kind = 'unit' AND quantity = 1)
|
|
64
|
+
)
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
CREATE UNIQUE INDEX catalog_sku_identifiers_one_primary_gtin_per_sku_key
|
|
68
|
+
ON public.catalog_sku_identifiers (catalog_sku_id)
|
|
69
|
+
WHERE is_primary AND identifier_kind = 'gtin';
|
|
70
|
+
|
|
71
|
+
CREATE INDEX catalog_sku_identifiers_sku_kind_primary_idx
|
|
72
|
+
ON public.catalog_sku_identifiers (catalog_sku_id, identifier_kind, is_primary);
|
|
73
|
+
|
|
74
|
+
-- price_lists.id is already a primary key, so every existing (id, region_code,
|
|
75
|
+
-- currency) tuple is unique. This is the transaction-safe repository pattern for
|
|
76
|
+
-- a reference index on an existing table.
|
|
77
|
+
-- squawk-ignore require-concurrent-index-creation
|
|
78
|
+
CREATE UNIQUE INDEX price_lists_id_region_currency_key
|
|
79
|
+
ON public.price_lists (id, region_code, currency);
|
|
80
|
+
|
|
81
|
+
CREATE TABLE public.subscription_price_policy_revisions (
|
|
82
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
83
|
+
price_list_id uuid NOT NULL,
|
|
84
|
+
region_code text NOT NULL,
|
|
85
|
+
currency text NOT NULL CHECK (currency ~ '^[A-Z]{3}$'),
|
|
86
|
+
channel text NOT NULL,
|
|
87
|
+
revision_no integer NOT NULL CHECK (revision_no > 0),
|
|
88
|
+
discount_bps integer NOT NULL CHECK (discount_bps BETWEEN 0 AND 10000),
|
|
89
|
+
rounding_quantum_minor integer NOT NULL CHECK (rounding_quantum_minor > 0),
|
|
90
|
+
rounding_rule text NOT NULL CHECK (rounding_rule = 'FLOOR_TO_QUANTUM'),
|
|
91
|
+
effective_from timestamptz NOT NULL,
|
|
92
|
+
effective_to timestamptz,
|
|
93
|
+
digest text NOT NULL CHECK (digest ~ '^[0-9a-f]{64}$'),
|
|
94
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
95
|
+
CONSTRAINT subscription_price_policy_revisions_effective_interval_check
|
|
96
|
+
CHECK (effective_to IS NULL OR effective_to > effective_from),
|
|
97
|
+
CONSTRAINT subscription_price_policy_revisions_region_code_check
|
|
98
|
+
CHECK (region_code ~ '^[A-Z]{2,3}$'),
|
|
99
|
+
CONSTRAINT subscription_price_policy_revisions_channel_check
|
|
100
|
+
CHECK (channel ~ '^[A-Za-z0-9._:-]+$'),
|
|
101
|
+
CONSTRAINT subscription_price_policy_revisions_price_list_context_fkey
|
|
102
|
+
FOREIGN KEY (price_list_id, region_code, currency)
|
|
103
|
+
REFERENCES public.price_lists (id, region_code, currency)
|
|
104
|
+
ON DELETE RESTRICT,
|
|
105
|
+
CONSTRAINT subscription_price_policy_revisions_context_revision_key
|
|
106
|
+
UNIQUE (price_list_id, region_code, currency, channel, revision_no),
|
|
107
|
+
CONSTRAINT subscription_price_policy_revisions_context_digest_key
|
|
108
|
+
UNIQUE (price_list_id, region_code, currency, channel, digest)
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
CREATE INDEX catalog_product_document_revisions_product_id_revision_no_idx
|
|
112
|
+
ON public.catalog_product_document_revisions (product_id, revision_no);
|
|
113
|
+
|
|
114
|
+
CREATE INDEX subscription_price_policy_revisions_context_effective_from_idx
|
|
115
|
+
ON public.subscription_price_policy_revisions (
|
|
116
|
+
price_list_id,
|
|
117
|
+
region_code,
|
|
118
|
+
currency,
|
|
119
|
+
channel,
|
|
120
|
+
effective_from
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
CREATE FUNCTION public.catalog_product_document_revisions_reject_mutation()
|
|
124
|
+
RETURNS trigger
|
|
125
|
+
LANGUAGE plpgsql
|
|
126
|
+
SET search_path = public, pg_catalog
|
|
127
|
+
AS $$
|
|
128
|
+
BEGIN
|
|
129
|
+
RAISE EXCEPTION 'catalog_product_document_revision_immutable'
|
|
130
|
+
USING ERRCODE = '55000';
|
|
131
|
+
END;
|
|
132
|
+
$$;
|
|
133
|
+
|
|
134
|
+
CREATE FUNCTION public.subscription_price_policy_revisions_guard_mutation()
|
|
135
|
+
RETURNS trigger
|
|
136
|
+
LANGUAGE plpgsql
|
|
137
|
+
SET search_path = public, pg_catalog
|
|
138
|
+
AS $$
|
|
139
|
+
BEGIN
|
|
140
|
+
IF TG_OP = 'DELETE' THEN
|
|
141
|
+
RAISE EXCEPTION 'subscription_price_policy_revision_immutable'
|
|
142
|
+
USING ERRCODE = '55000';
|
|
143
|
+
END IF;
|
|
144
|
+
|
|
145
|
+
IF OLD.effective_to IS NOT NULL
|
|
146
|
+
OR NEW.effective_to IS NULL
|
|
147
|
+
OR NEW.effective_to <= OLD.effective_from
|
|
148
|
+
OR NEW.id IS DISTINCT FROM OLD.id
|
|
149
|
+
OR NEW.price_list_id IS DISTINCT FROM OLD.price_list_id
|
|
150
|
+
OR NEW.region_code IS DISTINCT FROM OLD.region_code
|
|
151
|
+
OR NEW.currency IS DISTINCT FROM OLD.currency
|
|
152
|
+
OR NEW.channel IS DISTINCT FROM OLD.channel
|
|
153
|
+
OR NEW.revision_no IS DISTINCT FROM OLD.revision_no
|
|
154
|
+
OR NEW.discount_bps IS DISTINCT FROM OLD.discount_bps
|
|
155
|
+
OR NEW.rounding_quantum_minor IS DISTINCT FROM OLD.rounding_quantum_minor
|
|
156
|
+
OR NEW.rounding_rule IS DISTINCT FROM OLD.rounding_rule
|
|
157
|
+
OR NEW.effective_from IS DISTINCT FROM OLD.effective_from
|
|
158
|
+
OR NEW.digest IS DISTINCT FROM OLD.digest
|
|
159
|
+
OR NEW.created_at IS DISTINCT FROM OLD.created_at
|
|
160
|
+
THEN
|
|
161
|
+
RAISE EXCEPTION 'subscription_price_policy_revision_close_only'
|
|
162
|
+
USING ERRCODE = '55000';
|
|
163
|
+
END IF;
|
|
164
|
+
|
|
165
|
+
RETURN NEW;
|
|
166
|
+
END;
|
|
167
|
+
$$;
|
|
168
|
+
|
|
169
|
+
CREATE FUNCTION public.subscription_price_policy_revisions_prevent_overlap()
|
|
170
|
+
RETURNS trigger
|
|
171
|
+
LANGUAGE plpgsql
|
|
172
|
+
SET search_path = public, pg_catalog
|
|
173
|
+
AS $$
|
|
174
|
+
DECLARE
|
|
175
|
+
v_context_key text;
|
|
176
|
+
BEGIN
|
|
177
|
+
IF current_setting('transaction_isolation') <> 'read committed' THEN
|
|
178
|
+
RAISE EXCEPTION 'subscription_price_policy_revision_overlap_requires_read_committed'
|
|
179
|
+
USING ERRCODE = '0A000';
|
|
180
|
+
END IF;
|
|
181
|
+
|
|
182
|
+
v_context_key := concat_ws(
|
|
183
|
+
'|',
|
|
184
|
+
NEW.price_list_id::text,
|
|
185
|
+
NEW.region_code,
|
|
186
|
+
NEW.currency,
|
|
187
|
+
NEW.channel
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
PERFORM pg_catalog.pg_advisory_xact_lock(
|
|
191
|
+
pg_catalog.hashtextextended(v_context_key, 0)
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
IF EXISTS (
|
|
195
|
+
SELECT 1
|
|
196
|
+
FROM public.subscription_price_policy_revisions AS existing
|
|
197
|
+
WHERE existing.id <> NEW.id
|
|
198
|
+
AND existing.price_list_id = NEW.price_list_id
|
|
199
|
+
AND existing.region_code = NEW.region_code
|
|
200
|
+
AND existing.currency = NEW.currency
|
|
201
|
+
AND existing.channel = NEW.channel
|
|
202
|
+
AND tstzrange(existing.effective_from, existing.effective_to, '[)')
|
|
203
|
+
&& tstzrange(NEW.effective_from, NEW.effective_to, '[)')
|
|
204
|
+
) THEN
|
|
205
|
+
RAISE EXCEPTION 'subscription_price_policy_revision_overlaps'
|
|
206
|
+
USING ERRCODE = '23P01';
|
|
207
|
+
END IF;
|
|
208
|
+
|
|
209
|
+
RETURN NEW;
|
|
210
|
+
END;
|
|
211
|
+
$$;
|
|
212
|
+
|
|
213
|
+
CREATE TRIGGER catalog_product_document_revisions_immutable
|
|
214
|
+
BEFORE UPDATE OR DELETE ON public.catalog_product_document_revisions
|
|
215
|
+
FOR EACH ROW
|
|
216
|
+
EXECUTE FUNCTION public.catalog_product_document_revisions_reject_mutation();
|
|
217
|
+
|
|
218
|
+
CREATE TRIGGER subscription_price_policy_revisions_00_guard_mutation
|
|
219
|
+
BEFORE UPDATE OR DELETE ON public.subscription_price_policy_revisions
|
|
220
|
+
FOR EACH ROW
|
|
221
|
+
EXECUTE FUNCTION public.subscription_price_policy_revisions_guard_mutation();
|
|
222
|
+
|
|
223
|
+
CREATE TRIGGER subscription_price_policy_revisions_10_prevent_overlap
|
|
224
|
+
BEFORE INSERT OR UPDATE ON public.subscription_price_policy_revisions
|
|
225
|
+
FOR EACH ROW
|
|
226
|
+
EXECUTE FUNCTION public.subscription_price_policy_revisions_prevent_overlap();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
-- Catalog Wave 2a validates the same-product pointer foreign keys after their
|
|
2
|
+
-- NOT VALID installation in the immediately preceding transactional forward.
|
|
3
|
+
-- Both pointer columns were introduced nullable and without backfill, so this
|
|
4
|
+
-- scan confirms the current catalog before the constraints are marked valid.
|
|
5
|
+
|
|
6
|
+
ALTER TABLE public.catalog_products
|
|
7
|
+
VALIDATE CONSTRAINT catalog_products_primary_sku_same_product_fkey;
|
|
8
|
+
|
|
9
|
+
ALTER TABLE public.catalog_products
|
|
10
|
+
VALIDATE CONSTRAINT catalog_products_current_document_revision_same_product_fkey;
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
-- Draft foundation installs empty, isolated authoring tables and bounded command/read routines.
|
|
2
|
+
-- Immutable revisions and receipts retain original replay results; identity reservations survive abandonment.
|
|
3
|
+
-- No current catalog, document, pricing, stock, publication or subscription row is changed.
|
|
4
|
+
-- migration:allow-trigger: reject edits and deletion of immutable draft revision and command history.
|
|
5
|
+
-- migration:allow-rls: new tables default-deny all browser access; callers use bounded routines.
|
|
6
|
+
-- migration:allow-grant: revoke inherited table/routine privileges before granting the intended routine access.
|
|
7
|
+
-- migration:allow-dynamic-ddl: withdraw inherited nonowner ACL using catalog identities and quoted role names.
|
|
8
|
+
-- migration:allow-dml: transactional routines write only the four new draft-owned relations.
|
|
9
|
+
-- Owner-only INVOKER routines require externally verified operator context; no human-login or publication parity is claimed.
|
|
10
|
+
CREATE TABLE public.catalog_draft_heads (
|
|
11
|
+
draft_id uuid PRIMARY KEY, product_id uuid NOT NULL UNIQUE,
|
|
12
|
+
revision integer NOT NULL CHECK (revision > 0), status text NOT NULL CHECK (status IN ('open', 'abandoned'))
|
|
13
|
+
);
|
|
14
|
+
CREATE TABLE public.catalog_draft_revisions (
|
|
15
|
+
draft_id uuid NOT NULL REFERENCES public.catalog_draft_heads(draft_id), revision integer NOT NULL CHECK (revision > 0),
|
|
16
|
+
product_id uuid NOT NULL, status text NOT NULL CHECK (status IN ('open', 'abandoned')),
|
|
17
|
+
payload jsonb NOT NULL, command_key text NOT NULL, fingerprint text NOT NULL CHECK (fingerprint ~ '^[0-9a-f]{64}$'),
|
|
18
|
+
actor_id text NOT NULL CHECK (btrim(actor_id) <> ''), created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
19
|
+
PRIMARY KEY (draft_id, revision)
|
|
20
|
+
);
|
|
21
|
+
CREATE TABLE public.catalog_draft_command_receipts (
|
|
22
|
+
draft_id uuid NOT NULL, command_key text NOT NULL, fingerprint text NOT NULL CHECK (fingerprint ~ '^[0-9a-f]{64}$'),
|
|
23
|
+
revision integer NOT NULL, PRIMARY KEY (draft_id, command_key),
|
|
24
|
+
FOREIGN KEY (draft_id, revision) REFERENCES public.catalog_draft_revisions(draft_id, revision)
|
|
25
|
+
);
|
|
26
|
+
CREATE TABLE public.catalog_draft_sku_reservations (
|
|
27
|
+
sku_id uuid PRIMARY KEY, draft_id uuid NOT NULL REFERENCES public.catalog_draft_heads(draft_id),
|
|
28
|
+
product_id uuid NOT NULL, code text UNIQUE CHECK (code IS NULL OR (btrim(code) = code AND length(code) BETWEEN 1 AND 160))
|
|
29
|
+
);
|
|
30
|
+
CREATE FUNCTION public.catalog_draft_reject_mutation() RETURNS trigger
|
|
31
|
+
LANGUAGE plpgsql SET search_path = pg_catalog AS $$
|
|
32
|
+
BEGIN RAISE EXCEPTION 'catalog_draft_history_immutable' USING ERRCODE = '55000'; END;
|
|
33
|
+
$$;
|
|
34
|
+
CREATE TRIGGER catalog_draft_revisions_immutable BEFORE UPDATE OR DELETE ON public.catalog_draft_revisions
|
|
35
|
+
FOR EACH ROW EXECUTE FUNCTION public.catalog_draft_reject_mutation();
|
|
36
|
+
CREATE TRIGGER catalog_draft_receipts_immutable BEFORE UPDATE OR DELETE ON public.catalog_draft_command_receipts
|
|
37
|
+
FOR EACH ROW EXECUTE FUNCTION public.catalog_draft_reject_mutation();
|
|
38
|
+
ALTER TABLE public.catalog_draft_heads ENABLE ROW LEVEL SECURITY;
|
|
39
|
+
ALTER TABLE public.catalog_draft_revisions ENABLE ROW LEVEL SECURITY;
|
|
40
|
+
ALTER TABLE public.catalog_draft_command_receipts ENABLE ROW LEVEL SECURITY;
|
|
41
|
+
ALTER TABLE public.catalog_draft_sku_reservations ENABLE ROW LEVEL SECURITY;
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
CREATE FUNCTION public.catalog_draft_record(p_draft_id uuid, p_revision integer) RETURNS jsonb
|
|
45
|
+
LANGUAGE sql STABLE SET search_path = pg_catalog AS $$
|
|
46
|
+
SELECT jsonb_build_object('draftId', draft_id, 'productId', product_id, 'revision', revision,
|
|
47
|
+
'status', status, 'payload', payload, 'commandKey', command_key, 'fingerprint', fingerprint,
|
|
48
|
+
'actorId', actor_id, 'createdAt', created_at)
|
|
49
|
+
FROM public.catalog_draft_revisions WHERE draft_id = p_draft_id AND revision = p_revision
|
|
50
|
+
$$;
|
|
51
|
+
CREATE FUNCTION public.catalog_draft_apply(p_canonical_command text, p_fingerprint text, p_actor_id text) RETURNS jsonb
|
|
52
|
+
LANGUAGE plpgsql SECURITY INVOKER SET search_path = pg_catalog, public AS $$
|
|
53
|
+
DECLARE
|
|
54
|
+
c jsonb; p jsonb; s jsonb; h public.catalog_draft_heads%ROWTYPE;
|
|
55
|
+
receipt public.catalog_draft_command_receipts%ROWTYPE; reserved public.catalog_draft_sku_reservations%ROWTYPE;
|
|
56
|
+
d uuid; product uuid; sku uuid; key text; action text; expected integer; next_revision integer;
|
|
57
|
+
actor text; state text; code_value text;
|
|
58
|
+
BEGIN
|
|
59
|
+
actor := p_actor_id; IF actor IS NULL OR actor <> btrim(actor) OR length(actor) NOT BETWEEN 1 AND 240 THEN RAISE EXCEPTION 'catalog_draft_actor_required' USING ERRCODE='42501'; END IF;
|
|
60
|
+
IF p_canonical_command IS NULL OR octet_length(p_canonical_command) NOT BETWEEN 2 AND 1048576
|
|
61
|
+
OR p_fingerprint IS NULL OR p_fingerprint !~ '^[0-9a-f]{64}$'
|
|
62
|
+
OR encode(sha256(convert_to(p_canonical_command, 'UTF8')), 'hex') <> p_fingerprint THEN
|
|
63
|
+
RETURN jsonb_build_object('outcome','validation_issue','reason','command_fingerprint_invalid');
|
|
64
|
+
END IF;
|
|
65
|
+
c := p_canonical_command::jsonb;
|
|
66
|
+
IF jsonb_typeof(c) IS DISTINCT FROM 'object' OR c->'schemaVersion' IS DISTINCT FROM '1'::jsonb
|
|
67
|
+
OR c->>'action' IS NULL OR c->>'action' NOT IN ('create','revise','abandon')
|
|
68
|
+
OR jsonb_typeof(c->'draftId') IS DISTINCT FROM 'string'
|
|
69
|
+
OR jsonb_typeof(c->'commandKey') IS DISTINCT FROM 'string' OR length(c->>'commandKey') NOT BETWEEN 1 AND 240
|
|
70
|
+
OR btrim(c->>'commandKey') <> c->>'commandKey'
|
|
71
|
+
OR jsonb_typeof(c->'expectedRevision') IS DISTINCT FROM 'number'
|
|
72
|
+
OR c->>'expectedRevision' !~ '^(0|[1-9][0-9]*)$'
|
|
73
|
+
OR EXISTS (SELECT FROM jsonb_object_keys(c) k WHERE k NOT IN ('schemaVersion','action','draftId','commandKey','expectedRevision','payload')) THEN
|
|
74
|
+
RETURN jsonb_build_object('outcome','validation_issue','reason','command_invalid');
|
|
75
|
+
END IF;
|
|
76
|
+
d := (c->>'draftId')::uuid; key := c->>'commandKey'; action := c->>'action'; expected := (c->>'expectedRevision')::integer;
|
|
77
|
+
IF expected > 2147483646 THEN RETURN jsonb_build_object('outcome','validation_issue','reason','revision_limit'); END IF;
|
|
78
|
+
IF d::text <> c->>'draftId' THEN RETURN jsonb_build_object('outcome','validation_issue','reason','command_invalid'); END IF;
|
|
79
|
+
PERFORM pg_advisory_xact_lock(hashtextextended('catalog-draft:' || d::text, 0));
|
|
80
|
+
SELECT * INTO receipt FROM public.catalog_draft_command_receipts WHERE draft_id = d AND command_key = key;
|
|
81
|
+
IF FOUND THEN
|
|
82
|
+
IF receipt.fingerprint <> p_fingerprint THEN
|
|
83
|
+
RETURN jsonb_build_object('outcome','conflict','reason','command_key_reused');
|
|
84
|
+
END IF;
|
|
85
|
+
IF public.catalog_draft_record(d,receipt.revision)->>'actorId' <> actor THEN RETURN jsonb_build_object('outcome','unauthorized','reason','operator_mismatch'); END IF;
|
|
86
|
+
RETURN jsonb_build_object('outcome','replayed','record',public.catalog_draft_record(d, receipt.revision));
|
|
87
|
+
END IF;
|
|
88
|
+
SELECT * INTO h FROM public.catalog_draft_heads WHERE draft_id = d FOR UPDATE;
|
|
89
|
+
IF (action = 'create' AND (FOUND OR expected <> 0)) OR (action <> 'create' AND (NOT FOUND OR expected <> h.revision)) THEN
|
|
90
|
+
RETURN jsonb_build_object('outcome','conflict','reason','revision_conflict','currentRevision',coalesce(h.revision,0));
|
|
91
|
+
END IF;
|
|
92
|
+
IF h.status = 'abandoned' THEN RETURN jsonb_build_object('outcome','conflict','reason','draft_abandoned','currentRevision',h.revision); END IF;
|
|
93
|
+
next_revision := coalesce(h.revision,0) + 1;
|
|
94
|
+
state := CASE WHEN action = 'abandon' THEN 'abandoned' ELSE 'open' END;
|
|
95
|
+
IF action = 'abandon' THEN
|
|
96
|
+
IF c ? 'payload' THEN RETURN jsonb_build_object('outcome','validation_issue','reason','abandon_payload_invalid'); END IF;
|
|
97
|
+
SELECT payload INTO p FROM public.catalog_draft_revisions WHERE draft_id = d AND revision = h.revision;
|
|
98
|
+
product := h.product_id;
|
|
99
|
+
ELSE
|
|
100
|
+
p := c->'payload';
|
|
101
|
+
IF jsonb_typeof(p) IS DISTINCT FROM 'object' OR p->'schemaVersion' IS DISTINCT FROM '1'::jsonb
|
|
102
|
+
OR jsonb_typeof(p->'product') IS DISTINCT FROM 'object' OR jsonb_typeof(p->'skus') IS DISTINCT FROM 'array'
|
|
103
|
+
OR jsonb_typeof(p->'product'->'id') IS DISTINCT FROM 'string'
|
|
104
|
+
OR jsonb_typeof(p->'product'->'type') IS DISTINCT FROM 'object'
|
|
105
|
+
OR jsonb_typeof(p->'product'->'type'->'key') IS DISTINCT FROM 'string'
|
|
106
|
+
OR p->'product'->'type'->>'key' !~ '^[a-z][a-z0-9.-]*:[a-z][a-z0-9._-]*$'
|
|
107
|
+
OR jsonb_typeof(p->'product'->'type'->'version') IS DISTINCT FROM 'number'
|
|
108
|
+
OR p->'product'->'type'->>'version' !~ '^[1-9][0-9]*$'
|
|
109
|
+
OR jsonb_typeof(p->'product'->'dimensions') IS DISTINCT FROM 'array'
|
|
110
|
+
OR jsonb_array_length(p->'product'->'dimensions') > 16 OR jsonb_array_length(p->'skus') > 10000 THEN
|
|
111
|
+
RETURN jsonb_build_object('outcome','validation_issue','reason','payload_invalid');
|
|
112
|
+
END IF;
|
|
113
|
+
product := (p->'product'->>'id')::uuid;
|
|
114
|
+
IF product::text <> p->'product'->>'id' OR (h.product_id IS NOT NULL AND h.product_id <> product)
|
|
115
|
+
OR (p->'product' ? 'documentRevision' AND p->'product'->'documentRevision'->>'productId' IS DISTINCT FROM product::text)
|
|
116
|
+
OR (p->'product' ? 'primarySkuId' AND NOT EXISTS (SELECT FROM jsonb_array_elements(p->'skus') x WHERE x->>'id' = p->'product'->>'primarySkuId')) THEN
|
|
117
|
+
RETURN jsonb_build_object('outcome','validation_issue','reason','product_reference_invalid');
|
|
118
|
+
END IF;
|
|
119
|
+
IF EXISTS (SELECT FROM jsonb_array_elements(p->'skus') x GROUP BY x->>'id' HAVING count(*) > 1)
|
|
120
|
+
OR EXISTS (SELECT FROM jsonb_array_elements(p->'skus') x WHERE x ? 'code' GROUP BY x->>'code' HAVING count(*) > 1) THEN
|
|
121
|
+
RETURN jsonb_build_object('outcome','validation_issue','reason','sku_identity_duplicate');
|
|
122
|
+
END IF;
|
|
123
|
+
FOR s IN SELECT value FROM jsonb_array_elements(p->'skus') LOOP
|
|
124
|
+
IF jsonb_typeof(s) IS DISTINCT FROM 'object' OR jsonb_typeof(s->'id') IS DISTINCT FROM 'string'
|
|
125
|
+
OR s->>'productId' IS DISTINCT FROM product::text OR s ? 'sharedContent'
|
|
126
|
+
OR jsonb_typeof(s->'options') IS DISTINCT FROM 'object'
|
|
127
|
+
OR (s ? 'code' AND (jsonb_typeof(s->'code') IS DISTINCT FROM 'string' OR length(s->>'code') NOT BETWEEN 1 AND 160 OR btrim(s->>'code') <> s->>'code' OR s->>'code' !~ '^[A-Za-z0-9._:-]+$')) THEN
|
|
128
|
+
RETURN jsonb_build_object('outcome','validation_issue','reason','sku_reference_invalid');
|
|
129
|
+
END IF;
|
|
130
|
+
sku := (s->>'id')::uuid;
|
|
131
|
+
IF sku::text <> s->>'id' THEN RETURN jsonb_build_object('outcome','validation_issue','reason','sku_reference_invalid'); END IF;
|
|
132
|
+
SELECT * INTO reserved FROM public.catalog_draft_sku_reservations WHERE sku_id = sku;
|
|
133
|
+
IF FOUND AND (reserved.draft_id <> d OR reserved.product_id <> product OR (reserved.code IS NOT NULL AND reserved.code IS DISTINCT FROM s->>'code')) THEN
|
|
134
|
+
RETURN jsonb_build_object('outcome','conflict','reason','sku_identity_reserved');
|
|
135
|
+
END IF;
|
|
136
|
+
END LOOP;
|
|
137
|
+
END IF;
|
|
138
|
+
-- The exception block rolls back every write if any cross-draft reservation loses a race.
|
|
139
|
+
BEGIN
|
|
140
|
+
IF action = 'create' THEN INSERT INTO public.catalog_draft_heads VALUES(d,product,next_revision,state);
|
|
141
|
+
ELSE UPDATE public.catalog_draft_heads SET revision=next_revision,status=state WHERE draft_id=d; END IF;
|
|
142
|
+
IF action <> 'abandon' THEN
|
|
143
|
+
FOR s IN SELECT value FROM jsonb_array_elements(p->'skus') LOOP
|
|
144
|
+
INSERT INTO public.catalog_draft_sku_reservations(sku_id,draft_id,product_id,code)
|
|
145
|
+
VALUES((s->>'id')::uuid,d,product,s->>'code') ON CONFLICT(sku_id) DO NOTHING;
|
|
146
|
+
SELECT * INTO reserved FROM public.catalog_draft_sku_reservations WHERE sku_id=(s->>'id')::uuid FOR UPDATE;
|
|
147
|
+
IF reserved.draft_id <> d OR reserved.product_id <> product OR (reserved.code IS NOT NULL AND reserved.code IS DISTINCT FROM s->>'code') THEN
|
|
148
|
+
RAISE unique_violation USING MESSAGE='sku_identity_reserved';
|
|
149
|
+
END IF;
|
|
150
|
+
IF reserved.code IS NULL AND s ? 'code' THEN
|
|
151
|
+
UPDATE public.catalog_draft_sku_reservations SET code=s->>'code' WHERE sku_id=(s->>'id')::uuid;
|
|
152
|
+
END IF;
|
|
153
|
+
END LOOP;
|
|
154
|
+
END IF;
|
|
155
|
+
INSERT INTO public.catalog_draft_revisions(draft_id,revision,product_id,status,payload,command_key,fingerprint,actor_id)
|
|
156
|
+
VALUES(d,next_revision,product,state,p,key,p_fingerprint,actor);
|
|
157
|
+
INSERT INTO public.catalog_draft_command_receipts VALUES(d,key,p_fingerprint,next_revision);
|
|
158
|
+
EXCEPTION WHEN unique_violation THEN
|
|
159
|
+
RETURN jsonb_build_object('outcome','conflict','reason','identity_reserved');
|
|
160
|
+
END;
|
|
161
|
+
RETURN jsonb_build_object('outcome','committed','record',public.catalog_draft_record(d,next_revision));
|
|
162
|
+
EXCEPTION WHEN invalid_text_representation OR numeric_value_out_of_range THEN
|
|
163
|
+
RETURN jsonb_build_object('outcome','validation_issue','reason','command_invalid');
|
|
164
|
+
END;
|
|
165
|
+
$$;
|
|
166
|
+
CREATE FUNCTION public.catalog_draft_get(p_draft_id uuid, p_revision integer DEFAULT NULL, p_command_key text DEFAULT NULL) RETURNS jsonb
|
|
167
|
+
LANGUAGE plpgsql SECURITY INVOKER STABLE SET search_path = pg_catalog, public AS $$
|
|
168
|
+
DECLARE r integer;
|
|
169
|
+
BEGIN
|
|
170
|
+
-- Owner-only: verified operator context is supplied at the dark domain boundary.
|
|
171
|
+
IF p_revision IS NOT NULL AND p_command_key IS NOT NULL THEN RAISE EXCEPTION 'read_selector_invalid' USING ERRCODE='22023'; END IF;
|
|
172
|
+
IF p_command_key IS NOT NULL THEN
|
|
173
|
+
SELECT revision INTO r FROM public.catalog_draft_command_receipts WHERE draft_id=p_draft_id AND command_key=p_command_key;
|
|
174
|
+
ELSIF p_revision IS NOT NULL THEN r := p_revision;
|
|
175
|
+
ELSE SELECT revision INTO r FROM public.catalog_draft_heads WHERE draft_id=p_draft_id; END IF;
|
|
176
|
+
RETURN public.catalog_draft_record(p_draft_id,r);
|
|
177
|
+
END;
|
|
178
|
+
$$;
|
|
179
|
+
CREATE FUNCTION public.catalog_draft_list(p_after_draft_id uuid DEFAULT NULL, p_limit integer DEFAULT 100) RETURNS jsonb
|
|
180
|
+
LANGUAGE plpgsql SECURITY INVOKER STABLE SET search_path = pg_catalog, public AS $$
|
|
181
|
+
DECLARE result jsonb;
|
|
182
|
+
BEGIN
|
|
183
|
+
-- Owner-only: verified operator context is supplied at the dark domain boundary.
|
|
184
|
+
IF p_limit IS NULL OR p_limit NOT BETWEEN 1 AND 100 THEN RAISE EXCEPTION 'list_limit_invalid' USING ERRCODE='22023'; END IF;
|
|
185
|
+
WITH page AS (SELECT draft_id,revision FROM public.catalog_draft_heads
|
|
186
|
+
WHERE p_after_draft_id IS NULL OR draft_id > p_after_draft_id ORDER BY draft_id LIMIT p_limit+1),
|
|
187
|
+
visible AS (SELECT * FROM page ORDER BY draft_id LIMIT p_limit)
|
|
188
|
+
SELECT jsonb_build_object('items',coalesce(jsonb_agg(jsonb_build_object(
|
|
189
|
+
'draftId',r.draft_id,'productId',r.product_id,'revision',r.revision,'status',r.status,
|
|
190
|
+
'commandKey',r.command_key,'fingerprint',r.fingerprint,'actorId',r.actor_id,'createdAt',r.created_at) ORDER BY r.draft_id),'[]'::jsonb),
|
|
191
|
+
'nextCursor',CASE WHEN (SELECT count(*) FROM page)>p_limit THEN (SELECT draft_id::text FROM visible ORDER BY draft_id DESC LIMIT 1) END)
|
|
192
|
+
INTO result FROM visible v JOIN public.catalog_draft_revisions r ON r.draft_id=v.draft_id AND r.revision=v.revision;
|
|
193
|
+
RETURN result;
|
|
194
|
+
END;
|
|
195
|
+
$$;
|
|
196
|
+
REVOKE ALL ON TABLE public.catalog_draft_heads, public.catalog_draft_revisions,
|
|
197
|
+
public.catalog_draft_command_receipts, public.catalog_draft_sku_reservations FROM PUBLIC, anon, authenticated;
|
|
198
|
+
REVOKE ALL ON FUNCTION public.catalog_draft_reject_mutation() FROM PUBLIC, anon, authenticated;
|
|
199
|
+
REVOKE ALL ON FUNCTION public.catalog_draft_record(uuid,integer) FROM PUBLIC, anon, authenticated;
|
|
200
|
+
REVOKE ALL ON FUNCTION public.catalog_draft_apply(text,text,text) FROM PUBLIC, anon, authenticated;
|
|
201
|
+
REVOKE ALL ON FUNCTION public.catalog_draft_get(uuid,integer,text) FROM PUBLIC, anon, authenticated;
|
|
202
|
+
REVOKE ALL ON FUNCTION public.catalog_draft_list(uuid,integer) FROM PUBLIC, anon, authenticated;
|
|
203
|
+
|
|
204
|
+
DO $$ DECLARE observer record; BEGIN
|
|
205
|
+
FOR observer IN SELECT DISTINCT roles.rolname FROM pg_class c
|
|
206
|
+
CROSS JOIN LATERAL aclexplode(c.relacl) a JOIN pg_roles roles ON roles.oid=a.grantee
|
|
207
|
+
WHERE c.oid IN ('public.catalog_draft_heads'::regclass,'public.catalog_draft_revisions'::regclass,
|
|
208
|
+
'public.catalog_draft_command_receipts'::regclass,'public.catalog_draft_sku_reservations'::regclass)
|
|
209
|
+
AND a.grantee<>c.relowner LOOP
|
|
210
|
+
EXECUTE format('REVOKE ALL ON TABLE public.catalog_draft_heads, public.catalog_draft_revisions, public.catalog_draft_command_receipts, public.catalog_draft_sku_reservations FROM %I',observer.rolname);
|
|
211
|
+
END LOOP;
|
|
212
|
+
FOR observer IN SELECT p.oid::regprocedure routine,roles.rolname FROM pg_proc p
|
|
213
|
+
CROSS JOIN LATERAL aclexplode(p.proacl) a JOIN pg_roles roles ON roles.oid=a.grantee
|
|
214
|
+
WHERE p.pronamespace='public'::regnamespace AND p.proname LIKE 'catalog_draft_%' AND a.grantee<>p.proowner LOOP
|
|
215
|
+
EXECUTE format('REVOKE ALL ON FUNCTION %s FROM %I',observer.routine,observer.rolname);
|
|
216
|
+
END LOOP;
|
|
217
|
+
END $$;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { catalogSkuEnvelopeSelectorSchema } from "./foundationSchemas.js";
|
|
2
|
+
|
|
3
|
+
/** Shares exact-code parsing with detail reads; omission alone keeps discovery broad. */
|
|
4
|
+
export function parseCatalogActiveSkuSelection(value: unknown): readonly string[] | undefined {
|
|
5
|
+
if (value === undefined) return undefined;
|
|
6
|
+
if (!Array.isArray(value) || value.length < 1 || value.length > 100) {
|
|
7
|
+
throw new RangeError("catalog_active_sku_selection_invalid");
|
|
8
|
+
}
|
|
9
|
+
const codes: string[] = [];
|
|
10
|
+
const seen = new Set<string>();
|
|
11
|
+
for (const skuCode of value) {
|
|
12
|
+
const parsed = catalogSkuEnvelopeSelectorSchema.safeParse({ kind: "sku_code", skuCode });
|
|
13
|
+
if (!parsed.success || parsed.data.kind !== "sku_code" || seen.has(parsed.data.skuCode)) {
|
|
14
|
+
throw new RangeError("catalog_active_sku_selection_invalid");
|
|
15
|
+
}
|
|
16
|
+
codes.push(parsed.data.skuCode);
|
|
17
|
+
seen.add(parsed.data.skuCode);
|
|
18
|
+
}
|
|
19
|
+
return codes;
|
|
20
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { ZodType } from "zod";
|
|
2
|
+
import type { HttpRequest, HttpResponse } from "../http/index.js";
|
|
3
|
+
|
|
4
|
+
/** Structural inputs to the reference admin kit. Authorization and parsing stay with the host. */
|
|
5
|
+
export type AuthorizeAdmin = () => Promise<
|
|
6
|
+
| { ok: true; userId: string; role: "admin" | "distributor"; isMachineActor: boolean }
|
|
7
|
+
| { ok: false; code: "UNAUTHORIZED" | "FORBIDDEN"; message: string; identityCode?: string }
|
|
8
|
+
>;
|
|
9
|
+
export interface AgentDomainQuerySpec<T> {
|
|
10
|
+
readonly key: string;
|
|
11
|
+
readonly requestSchema: ZodType<T, unknown>;
|
|
12
|
+
readonly gate: "read";
|
|
13
|
+
readonly allowedActorKinds: readonly ("human" | "machine")[];
|
|
14
|
+
}
|
|
15
|
+
export interface AgentDomainMutationSpec<T> extends Omit<AgentDomainQuerySpec<T>, "gate"> {
|
|
16
|
+
readonly gate: "mutation" | "activation";
|
|
17
|
+
readonly lifecycleMarkers: readonly ("LIFECYCLE_ACTIVATE" | "LIFECYCLE_DEACTIVATE" | "HARD_DELETE")[];
|
|
18
|
+
}
|
|
19
|
+
export type BundleMutationKey = "create" | "update" | "setComposition" | "setTargetPrice" | "archive" | "restore" | "cloneDraft" | "activate" | "deactivate";
|
|
20
|
+
export type BundleQueryKey = "list" | "get" | "history" | "previewPrice";
|
|
21
|
+
export interface BundleResponseSchema {
|
|
22
|
+
safeParse(value: unknown): { success: boolean; data?: unknown };
|
|
23
|
+
}
|
|
24
|
+
type Handler = (req: HttpRequest, res: HttpResponse) => Promise<void>;
|
|
25
|
+
interface CommonOptions<T> {
|
|
26
|
+
authorizeAdmin: AuthorizeAdmin;
|
|
27
|
+
flagName?: string;
|
|
28
|
+
enabled?: boolean;
|
|
29
|
+
toResponse(value: T): Record<string, unknown>;
|
|
30
|
+
disabledMessage?: string;
|
|
31
|
+
invalidRequestMessage: string;
|
|
32
|
+
failureMessage: string;
|
|
33
|
+
}
|
|
34
|
+
export interface BundleAdminBindings {
|
|
35
|
+
bundleSpec: {
|
|
36
|
+
readonly mutations: Readonly<Record<string, AgentDomainMutationSpec<unknown>>>;
|
|
37
|
+
readonly queries?: Readonly<Record<string, AgentDomainQuerySpec<unknown>>>;
|
|
38
|
+
readonly mutationFlag: `${string}_MUTATIONS_ENABLED`;
|
|
39
|
+
readonly activationFlag: `${string}_ACTIVATION_ENABLED`;
|
|
40
|
+
readonly readFlag?: `${string}_READ_ENABLED`;
|
|
41
|
+
};
|
|
42
|
+
COMMERCE_CONTRACT_VERSION: "commerce.v0";
|
|
43
|
+
/** Passed by identity: instanceof and shared SQLSTATE mapping retain their owner. */
|
|
44
|
+
BundleRpcError: new (sqlstate: string | undefined, message: string) => Error & { sqlstate: string | undefined; pgMessage: string };
|
|
45
|
+
createAdminMutationHandler<Request, Result>(options: CommonOptions<Result> & {
|
|
46
|
+
mutation: AgentDomainMutationSpec<Request>;
|
|
47
|
+
invoke(actorId: string, request: Request): Promise<Result>;
|
|
48
|
+
}): Handler;
|
|
49
|
+
createAdminParameterizedReadHandler<Request, Result>(options: CommonOptions<Result> & {
|
|
50
|
+
query: AgentDomainQuerySpec<Request>;
|
|
51
|
+
load(actorId: string, request: Request): Promise<Result>;
|
|
52
|
+
responseSchema: BundleResponseSchema;
|
|
53
|
+
invalidResponseMessage: string;
|
|
54
|
+
}): Handler;
|
|
55
|
+
bundleHistoryResponseSchema: BundleResponseSchema;
|
|
56
|
+
getBundleResponseSchema: BundleResponseSchema;
|
|
57
|
+
listBundlesResponseSchema: BundleResponseSchema;
|
|
58
|
+
previewBundlePriceResponseSchema: BundleResponseSchema;
|
|
59
|
+
}
|