@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.
Files changed (525) hide show
  1. package/CHANGELOG.md +143 -0
  2. package/README.md +299 -6
  3. package/dist/catalog/activeSkuSelection.d.ts +2 -0
  4. package/dist/catalog/activeSkuSelection.js +20 -0
  5. package/dist/catalog/adminBindings.d.ts +72 -0
  6. package/dist/catalog/adminBindings.js +1 -0
  7. package/dist/catalog/adminBundleHandler.d.ts +26 -0
  8. package/dist/catalog/adminBundleHandler.js +111 -0
  9. package/dist/catalog/adminBundleReadHandler.d.ts +21 -0
  10. package/dist/catalog/adminBundleReadHandler.js +185 -0
  11. package/dist/catalog/adminBundleWritePort.d.ts +85 -0
  12. package/dist/catalog/adminBundleWritePort.js +1 -0
  13. package/dist/catalog/bundleAvailability.d.ts +63 -0
  14. package/dist/catalog/bundleAvailability.js +78 -0
  15. package/dist/catalog/bundleAvailabilityService.d.ts +65 -0
  16. package/dist/catalog/bundleAvailabilityService.js +89 -0
  17. package/dist/catalog/bundleBindings.d.ts +33 -0
  18. package/dist/catalog/bundleBindings.js +1 -0
  19. package/dist/catalog/bundleCatalogQueries.d.ts +28 -0
  20. package/dist/catalog/bundleCatalogQueries.js +84 -0
  21. package/dist/catalog/bundleCatalogReadPort.d.ts +173 -0
  22. package/dist/catalog/bundleCatalogReadPort.js +36 -0
  23. package/dist/catalog/bundleCatalogStore.d.ts +5 -0
  24. package/dist/catalog/bundleCatalogStore.js +156 -0
  25. package/dist/catalog/bundlePriceSelection.d.ts +13 -0
  26. package/dist/catalog/bundlePriceSelection.js +26 -0
  27. package/dist/catalog/bundleRuleCodes.d.ts +41 -0
  28. package/dist/catalog/bundleRuleCodes.js +110 -0
  29. package/dist/catalog/bundleRules.d.ts +69 -0
  30. package/dist/catalog/bundleRules.js +102 -0
  31. package/dist/catalog/catalogDraftStore.d.ts +4 -0
  32. package/dist/catalog/catalogDraftStore.js +63 -0
  33. package/dist/catalog/catalogSkuEnvelope.d.ts +7 -0
  34. package/dist/catalog/catalogSkuEnvelope.js +142 -0
  35. package/dist/catalog/catalogSkuEnvelopeMapper.d.ts +38 -0
  36. package/dist/catalog/catalogSkuEnvelopeMapper.js +88 -0
  37. package/dist/catalog/contracts/bundleAdmin.d.ts +143 -0
  38. package/dist/catalog/contracts/bundleAdmin.js +1 -0
  39. package/dist/catalog/contracts/bundleAvailability.d.ts +61 -0
  40. package/dist/catalog/contracts/bundleAvailability.js +1 -0
  41. package/dist/catalog/contracts/bundleRead.d.ts +241 -0
  42. package/dist/catalog/contracts/bundleRead.js +1 -0
  43. package/dist/catalog/contracts/draft.d.ts +79 -0
  44. package/dist/catalog/contracts/draft.js +47 -0
  45. package/dist/catalog/contracts/foundation.d.ts +78 -0
  46. package/dist/catalog/contracts/foundation.js +53 -0
  47. package/dist/catalog/contracts/index.d.ts +14 -0
  48. package/dist/catalog/contracts/index.js +6 -0
  49. package/dist/catalog/contracts/lifecycle.d.ts +77 -0
  50. package/dist/catalog/contracts/lifecycle.js +1 -0
  51. package/dist/catalog/contracts/productMediaContracts.d.ts +77 -0
  52. package/dist/catalog/contracts/productMediaContracts.js +149 -0
  53. package/dist/catalog/contracts/productTypes.d.ts +54 -0
  54. package/dist/catalog/contracts/productTypes.js +16 -0
  55. package/dist/catalog/contracts/resolveProductMedia.d.ts +3 -0
  56. package/dist/catalog/contracts/resolveProductMedia.js +78 -0
  57. package/dist/catalog/contracts/sellable.d.ts +23 -0
  58. package/dist/catalog/contracts/sellable.js +1 -0
  59. package/dist/catalog/draftHandler.d.ts +25 -0
  60. package/dist/catalog/draftHandler.js +118 -0
  61. package/dist/catalog/draftPorts.d.ts +18 -0
  62. package/dist/catalog/draftPorts.js +8 -0
  63. package/dist/catalog/draftSchemas.d.ts +423 -0
  64. package/dist/catalog/draftSchemas.js +89 -0
  65. package/dist/catalog/draftValidation.d.ts +21 -0
  66. package/dist/catalog/draftValidation.js +125 -0
  67. package/dist/catalog/effectiveBundleCatalogQueries.d.ts +8 -0
  68. package/dist/catalog/effectiveBundleCatalogQueries.js +79 -0
  69. package/dist/catalog/effectiveBundleCatalogStore.d.ts +4 -0
  70. package/dist/catalog/effectiveBundleCatalogStore.js +156 -0
  71. package/dist/catalog/foundationSchemas.d.ts +98 -0
  72. package/dist/catalog/foundationSchemas.js +110 -0
  73. package/dist/catalog/index.d.ts +44 -0
  74. package/dist/catalog/index.js +26 -0
  75. package/dist/catalog/mediaSourcePack.d.ts +51 -0
  76. package/dist/catalog/mediaSourcePack.js +247 -0
  77. package/dist/catalog/productTypeSchemas.d.ts +46 -0
  78. package/dist/catalog/productTypeSchemas.js +72 -0
  79. package/dist/catalog/publicationPorts.d.ts +42 -0
  80. package/dist/catalog/publicationPorts.js +1 -0
  81. package/dist/catalog/readPorts.d.ts +51 -0
  82. package/dist/catalog/readPorts.js +1 -0
  83. package/dist/catalog/referenceCatalogHandler.d.ts +7 -0
  84. package/dist/catalog/referenceCatalogHandler.js +27 -0
  85. package/dist/catalog/resolvedSkuProjection.d.ts +88 -0
  86. package/dist/catalog/resolvedSkuProjection.js +60 -0
  87. package/dist/catalog/sellableBundleHandler.d.ts +46 -0
  88. package/dist/catalog/sellableBundleHandler.js +84 -0
  89. package/dist/catalog/sellableSchemas.d.ts +46 -0
  90. package/dist/catalog/sellableSchemas.js +27 -0
  91. package/dist/catalog/sql.d.ts +46 -0
  92. package/dist/catalog/sql.js +13 -0
  93. package/dist/catalog/validation.d.ts +2 -0
  94. package/dist/catalog/validation.js +3 -0
  95. package/dist/communications/contracts/index.d.ts +1 -0
  96. package/dist/communications/contracts/marketingSync.d.ts +194 -0
  97. package/dist/communications/contracts/marketingSync.js +1 -0
  98. package/dist/communications/index.d.ts +1 -0
  99. package/dist/communications/klaviyo/consentPush.d.ts +51 -0
  100. package/dist/communications/klaviyo/consentPush.js +19 -0
  101. package/dist/communications/klaviyo/index.d.ts +110 -0
  102. package/dist/communications/klaviyo/index.js +860 -0
  103. package/dist/communications/newsletterIntegrationContracts.d.ts +3 -3
  104. package/dist/communications/resend/index.d.ts +40 -0
  105. package/dist/communications/resend/index.js +310 -0
  106. package/dist/composition/catalogContribution.d.ts +18 -0
  107. package/dist/composition/catalogContribution.js +21 -0
  108. package/dist/composition/index.d.ts +3 -0
  109. package/dist/composition/index.js +3 -0
  110. package/dist/composition/inventoryContribution.d.ts +16 -0
  111. package/dist/composition/inventoryContribution.js +17 -0
  112. package/dist/composition/shippingContribution.d.ts +16 -0
  113. package/dist/composition/shippingContribution.js +17 -0
  114. package/dist/identity/customerContracts.d.ts +2 -2
  115. package/dist/inventory/adminInventory.d.ts +13 -0
  116. package/dist/inventory/adminInventory.js +214 -0
  117. package/dist/inventory/bindings.d.ts +35 -0
  118. package/dist/inventory/bindings.js +45 -0
  119. package/dist/inventory/contracts/forecast.d.ts +86 -0
  120. package/dist/inventory/contracts/forecast.js +1 -0
  121. package/dist/inventory/contracts/index.d.ts +4 -0
  122. package/dist/inventory/contracts/index.js +1 -0
  123. package/dist/inventory/contracts/inventory.d.ts +157 -0
  124. package/dist/inventory/contracts/inventory.js +1 -0
  125. package/dist/inventory/contracts/newPurchaseStock.d.ts +20 -0
  126. package/dist/inventory/contracts/newPurchaseStock.js +1 -0
  127. package/dist/inventory/contracts/reservation.d.ts +18 -0
  128. package/dist/inventory/contracts/reservation.js +1 -0
  129. package/dist/inventory/forecast.d.ts +24 -0
  130. package/dist/inventory/forecast.js +200 -0
  131. package/dist/inventory/index.d.ts +14 -0
  132. package/dist/inventory/index.js +11 -0
  133. package/dist/inventory/inventoryHandlers.d.ts +53 -0
  134. package/dist/inventory/inventoryHandlers.js +151 -0
  135. package/dist/inventory/newPurchaseStockAvailability.d.ts +47 -0
  136. package/dist/inventory/newPurchaseStockAvailability.js +75 -0
  137. package/dist/inventory/ports.d.ts +21 -0
  138. package/dist/inventory/ports.js +16 -0
  139. package/dist/inventory/renewalReservation.d.ts +27 -0
  140. package/dist/inventory/renewalReservation.js +61 -0
  141. package/dist/inventory/reservationPolicy.d.ts +6 -0
  142. package/dist/inventory/reservationPolicy.js +19 -0
  143. package/dist/inventory/schemas.d.ts +137 -0
  144. package/dist/inventory/schemas.js +112 -0
  145. package/dist/inventory/sql.d.ts +14 -0
  146. package/dist/inventory/sql.js +5 -0
  147. package/dist/inventory/subscriptionForecastCalculation.d.ts +4 -0
  148. package/dist/inventory/subscriptionForecastCalculation.js +133 -0
  149. package/dist/pricing/acquisitionFinancialPolicy.d.ts +26 -0
  150. package/dist/pricing/acquisitionFinancialPolicy.js +1 -0
  151. package/dist/pricing/acquisitionOfferPricing.d.ts +27 -0
  152. package/dist/pricing/acquisitionOfferPricing.js +25 -0
  153. package/dist/pricing/acquisitionOfferPricingHandler.d.ts +20 -0
  154. package/dist/pricing/acquisitionOfferPricingHandler.js +105 -0
  155. package/dist/pricing/acquisitionOfferPricingSchemas.d.ts +31 -0
  156. package/dist/pricing/acquisitionOfferPricingSchemas.js +33 -0
  157. package/dist/pricing/acquisitionQuoteBinding.d.ts +12 -0
  158. package/dist/pricing/acquisitionQuoteBinding.js +12 -0
  159. package/dist/pricing/acquisitionReadiness.d.ts +31 -0
  160. package/dist/pricing/acquisitionReadiness.js +117 -0
  161. package/dist/pricing/acquisitionTerms.d.ts +19 -0
  162. package/dist/pricing/acquisitionTerms.js +126 -0
  163. package/dist/pricing/acquisitionTermsSchemas.d.ts +59 -0
  164. package/dist/pricing/acquisitionTermsSchemas.js +42 -0
  165. package/dist/pricing/catalogFactsProvenance.d.ts +33 -0
  166. package/dist/pricing/catalogFactsProvenance.js +64 -0
  167. package/dist/pricing/catalogFactsSchemas.d.ts +45 -0
  168. package/dist/pricing/catalogFactsSchemas.js +58 -0
  169. package/dist/pricing/commerceOfferPricing.d.ts +25 -0
  170. package/dist/pricing/commerceOfferPricing.js +139 -0
  171. package/dist/pricing/commerceOfferPricingCache.d.ts +10 -0
  172. package/dist/pricing/commerceOfferPricingCache.js +44 -0
  173. package/dist/pricing/commerceOfferPricingHandler.d.ts +21 -0
  174. package/dist/pricing/commerceOfferPricingHandler.js +78 -0
  175. package/dist/pricing/commercePriceAuthority.d.ts +28 -0
  176. package/dist/pricing/commercePriceAuthority.js +140 -0
  177. package/dist/pricing/commercePricingPolicyErrors.d.ts +40 -0
  178. package/dist/pricing/commercePricingPolicyErrors.js +67 -0
  179. package/dist/pricing/commerceQuoteBatchHandler.d.ts +37 -0
  180. package/dist/pricing/commerceQuoteBatchHandler.js +83 -0
  181. package/dist/pricing/commerceQuoteCatalogReadPort.d.ts +27 -0
  182. package/dist/pricing/commerceQuoteCatalogReadPort.js +8 -0
  183. package/dist/pricing/commerceQuoteHandler.d.ts +35 -0
  184. package/dist/pricing/commerceQuoteHandler.js +122 -0
  185. package/dist/pricing/commerceQuoteSnapshotExpect.d.ts +9 -0
  186. package/dist/pricing/commerceQuoteSnapshotExpect.js +22 -0
  187. package/dist/pricing/contracts/acquisitionTerms.d.ts +32 -0
  188. package/dist/pricing/contracts/acquisitionTerms.js +2 -0
  189. package/dist/pricing/contracts/authority.d.ts +27 -0
  190. package/dist/pricing/contracts/authority.js +1 -0
  191. package/dist/pricing/contracts/evidence.d.ts +33 -0
  192. package/dist/pricing/contracts/evidence.js +1 -0
  193. package/dist/pricing/contracts/index.d.ts +17 -0
  194. package/dist/pricing/contracts/index.js +7 -0
  195. package/dist/pricing/contracts/money.d.ts +27 -0
  196. package/dist/pricing/contracts/money.js +1 -0
  197. package/dist/pricing/contracts/offer.d.ts +52 -0
  198. package/dist/pricing/contracts/offer.js +4 -0
  199. package/dist/pricing/contracts/offerPolicy.d.ts +16 -0
  200. package/dist/pricing/contracts/offerPolicy.js +5 -0
  201. package/dist/pricing/contracts/policy.d.ts +22 -0
  202. package/dist/pricing/contracts/policy.js +3 -0
  203. package/dist/pricing/contracts/provenance.d.ts +24 -0
  204. package/dist/pricing/contracts/provenance.js +2 -0
  205. package/dist/pricing/contracts/quote.d.ts +94 -0
  206. package/dist/pricing/contracts/quote.js +1 -0
  207. package/dist/pricing/dbBackedCommerceQuoteHelpers.d.ts +78 -0
  208. package/dist/pricing/dbBackedCommerceQuoteHelpers.js +146 -0
  209. package/dist/pricing/dbBackedCommerceQuotePort.d.ts +74 -0
  210. package/dist/pricing/dbBackedCommerceQuotePort.js +223 -0
  211. package/dist/pricing/dbBackedCommerceQuoteSnapshotVerifier.d.ts +4 -0
  212. package/dist/pricing/dbBackedCommerceQuoteSnapshotVerifier.js +25 -0
  213. package/dist/pricing/index.d.ts +52 -0
  214. package/dist/pricing/index.js +36 -0
  215. package/dist/pricing/offerPolicyReadiness.d.ts +21 -0
  216. package/dist/pricing/offerPolicyReadiness.js +79 -0
  217. package/dist/pricing/offerPolicySchemas.d.ts +25 -0
  218. package/dist/pricing/offerPolicySchemas.js +15 -0
  219. package/dist/pricing/offerPricingSchemas.d.ts +315 -0
  220. package/dist/pricing/offerPricingSchemas.js +65 -0
  221. package/dist/pricing/ports.d.ts +13 -0
  222. package/dist/pricing/ports.js +9 -0
  223. package/dist/pricing/pricingPolicyToken.d.ts +39 -0
  224. package/dist/pricing/pricingPolicyToken.js +84 -0
  225. package/dist/pricing/quoteAcquisitionTerms.d.ts +2 -0
  226. package/dist/pricing/quoteAcquisitionTerms.js +4 -0
  227. package/dist/pricing/quoteCodeRejectionDetails.d.ts +20 -0
  228. package/dist/pricing/quoteCodeRejectionDetails.js +22 -0
  229. package/dist/pricing/quotePorts.d.ts +30 -0
  230. package/dist/pricing/quotePorts.js +20 -0
  231. package/dist/pricing/quotePrimitives.d.ts +13 -0
  232. package/dist/pricing/quotePrimitives.js +18 -0
  233. package/dist/pricing/quotePromotionEvidence.d.ts +39 -0
  234. package/dist/pricing/quotePromotionEvidence.js +73 -0
  235. package/dist/pricing/quoteSchemas.d.ts +1176 -0
  236. package/dist/pricing/quoteSchemas.js +196 -0
  237. package/dist/pricing/quoteShippingPromotions.d.ts +20 -0
  238. package/dist/pricing/quoteShippingPromotions.js +1 -0
  239. package/dist/pricing/subscriptionPricePolicy.d.ts +52 -0
  240. package/dist/pricing/subscriptionPricePolicy.js +96 -0
  241. package/dist/pricing/taxProfileGuard.d.ts +2 -0
  242. package/dist/pricing/taxProfileGuard.js +24 -0
  243. package/dist/pricing/validation.d.ts +2 -0
  244. package/dist/pricing/validation.js +3 -0
  245. package/dist/promotions/adminBindings.d.ts +72 -0
  246. package/dist/promotions/adminBindings.js +1 -0
  247. package/dist/promotions/adminPromotionCodesContracts.d.ts +498 -0
  248. package/dist/promotions/adminPromotionCodesContracts.js +211 -0
  249. package/dist/promotions/adminPromotionCodesDataPort.d.ts +47 -0
  250. package/dist/promotions/adminPromotionCodesDataPort.js +1 -0
  251. package/dist/promotions/adminPromotionCodesHandler.d.ts +14 -0
  252. package/dist/promotions/adminPromotionCodesHandler.js +194 -0
  253. package/dist/promotions/adminPromotionsContracts.d.ts +235 -0
  254. package/dist/promotions/adminPromotionsContracts.js +99 -0
  255. package/dist/promotions/adminPromotionsDataPort.d.ts +42 -0
  256. package/dist/promotions/adminPromotionsDataPort.js +1 -0
  257. package/dist/promotions/adminPromotionsHandler.d.ts +16 -0
  258. package/dist/promotions/adminPromotionsHandler.js +47 -0
  259. package/dist/promotions/commerceQuoteReadPreparation.d.ts +3 -0
  260. package/dist/promotions/commerceQuoteReadPreparation.js +27 -0
  261. package/dist/promotions/contracts/index.d.ts +387 -0
  262. package/dist/promotions/contracts/index.js +1 -0
  263. package/dist/promotions/index.d.ts +25 -0
  264. package/dist/promotions/index.js +22 -0
  265. package/dist/promotions/promoDataPort.d.ts +47 -0
  266. package/dist/promotions/promoDataPort.js +14 -0
  267. package/dist/promotions/promotionAcceptanceTelemetry.d.ts +29 -0
  268. package/dist/promotions/promotionAcceptanceTelemetry.js +45 -0
  269. package/dist/promotions/promotionBenefitDefinition.d.ts +37 -0
  270. package/dist/promotions/promotionBenefitDefinition.js +33 -0
  271. package/dist/promotions/promotionClaimSweepPort.d.ts +14 -0
  272. package/dist/promotions/promotionClaimSweepPort.js +8 -0
  273. package/dist/promotions/promotionClaims.d.ts +5 -0
  274. package/dist/promotions/promotionClaims.js +57 -0
  275. package/dist/promotions/promotionCodePreview.d.ts +11 -0
  276. package/dist/promotions/promotionCodePreview.js +154 -0
  277. package/dist/promotions/promotionCodeQuotePort.d.ts +25 -0
  278. package/dist/promotions/promotionCodeQuotePort.js +1 -0
  279. package/dist/promotions/promotionQuoteAcceptance.d.ts +70 -0
  280. package/dist/promotions/promotionQuoteAcceptance.js +202 -0
  281. package/dist/promotions/promotionQuoteBinding.d.ts +73 -0
  282. package/dist/promotions/promotionQuoteBinding.js +87 -0
  283. package/dist/promotions/quoteAcquisitionBinding.d.ts +6 -0
  284. package/dist/promotions/quoteAcquisitionBinding.js +1 -0
  285. package/dist/promotions/quoteAutomaticPromotions.d.ts +26 -0
  286. package/dist/promotions/quoteAutomaticPromotions.js +60 -0
  287. package/dist/promotions/quotePromoDiscounts.d.ts +36 -0
  288. package/dist/promotions/quotePromoDiscounts.js +146 -0
  289. package/dist/promotions/quotePromotionCodeAdjustments.d.ts +27 -0
  290. package/dist/promotions/quotePromotionCodeAdjustments.js +223 -0
  291. package/dist/promotions/quotePromotionV2.d.ts +30 -0
  292. package/dist/promotions/quotePromotionV2.js +186 -0
  293. package/dist/promotions/quoteShippingPolicy.d.ts +23 -0
  294. package/dist/promotions/quoteShippingPolicy.js +47 -0
  295. package/dist/promotions/sql.d.ts +14 -0
  296. package/dist/promotions/sql.js +5 -0
  297. package/dist/promotions/validation.d.ts +2 -0
  298. package/dist/promotions/validation.js +3 -0
  299. package/dist/shipping/addressCanonLookupHandler.d.ts +7 -0
  300. package/dist/shipping/addressCanonLookupHandler.js +105 -0
  301. package/dist/shipping/addressCanonical.d.ts +2 -0
  302. package/dist/shipping/addressCanonical.js +26 -0
  303. package/dist/shipping/addressPorts.d.ts +39 -0
  304. package/dist/shipping/addressPorts.js +1 -0
  305. package/dist/shipping/addressSchemas.d.ts +30 -0
  306. package/dist/shipping/addressSchemas.js +1 -0
  307. package/dist/shipping/adminShippingRateHandler.d.ts +4 -0
  308. package/dist/shipping/adminShippingRateHandler.js +64 -0
  309. package/dist/shipping/bindings.d.ts +57 -0
  310. package/dist/shipping/bindings.js +46 -0
  311. package/dist/shipping/contracts/address.d.ts +144 -0
  312. package/dist/shipping/contracts/address.js +1 -0
  313. package/dist/shipping/contracts/delivery.d.ts +110 -0
  314. package/dist/shipping/contracts/delivery.js +1 -0
  315. package/dist/shipping/contracts/index.d.ts +4 -0
  316. package/dist/shipping/contracts/index.js +1 -0
  317. package/dist/shipping/contracts/shippingRate.d.ts +11 -0
  318. package/dist/shipping/contracts/shippingRate.js +1 -0
  319. package/dist/shipping/contracts/threshold.d.ts +22 -0
  320. package/dist/shipping/contracts/threshold.js +1 -0
  321. package/dist/shipping/customerAddressesHandler.d.ts +5 -0
  322. package/dist/shipping/customerAddressesHandler.js +80 -0
  323. package/dist/shipping/customerSelfServiceAddressModels.d.ts +27 -0
  324. package/dist/shipping/customerSelfServiceAddressModels.js +101 -0
  325. package/dist/shipping/deliverySelectionHandler.d.ts +10 -0
  326. package/dist/shipping/deliverySelectionHandler.js +77 -0
  327. package/dist/shipping/deliverySelectionPort.d.ts +8 -0
  328. package/dist/shipping/deliverySelectionPort.js +1 -0
  329. package/dist/shipping/deliverySelectionSummary.d.ts +2 -0
  330. package/dist/shipping/deliverySelectionSummary.js +53 -0
  331. package/dist/shipping/handlerSchemas.d.ts +21 -0
  332. package/dist/shipping/handlerSchemas.js +1 -0
  333. package/dist/shipping/index.d.ts +20 -0
  334. package/dist/shipping/index.js +10 -0
  335. package/dist/shipping/pickupPointSearchPort.d.ts +4 -0
  336. package/dist/shipping/pickupPointSearchPort.js +1 -0
  337. package/dist/shipping/readDeliverySelectionEvidence.d.ts +2 -0
  338. package/dist/shipping/readDeliverySelectionEvidence.js +44 -0
  339. package/dist/shipping/shippingRatePorts.d.ts +52 -0
  340. package/dist/shipping/shippingRatePorts.js +1 -0
  341. package/dist/shipping/thresholdPolicy.d.ts +42 -0
  342. package/dist/shipping/thresholdPolicy.js +60 -0
  343. package/package.json +62 -2
  344. package/release-gates.json +263 -53
  345. package/sql/20260807140000_read_parity_catalog.sql +94 -0
  346. package/sql/20260810120000_storefront_catalog_attributes.sql +51 -0
  347. package/sql/20260812140000_sellable_bundle_catalog.sql +124 -0
  348. package/sql/20260812210000_bundle_operator_write_boundary.sql +567 -0
  349. package/sql/20260814000000_fulfillment_stock_evidence.sql +239 -0
  350. package/sql/20260815120000_automatic_subscription_renewal.sql +1083 -0
  351. package/sql/20260817180000_promotion_claim_lifecycle.sql +591 -0
  352. package/sql/20260817180100_promotion_claim_lifecycle_validation.sql +23 -0
  353. package/sql/20260827220000_catalog_document_revision_foundation.sql +226 -0
  354. package/sql/20260827220001_catalog_document_revision_foundation_validate.sql +10 -0
  355. package/sql/20260908100000_catalog_draft_foundation.sql +217 -0
  356. package/src/catalog/activeSkuSelection.ts +20 -0
  357. package/src/catalog/adminBindings.ts +59 -0
  358. package/src/catalog/adminBundleHandler.ts +165 -0
  359. package/src/catalog/adminBundleReadHandler.ts +263 -0
  360. package/src/catalog/adminBundleWritePort.ts +102 -0
  361. package/src/catalog/bundleAvailability.ts +129 -0
  362. package/src/catalog/bundleAvailabilityService.ts +171 -0
  363. package/src/catalog/bundleBindings.ts +29 -0
  364. package/src/catalog/bundleCatalogQueries.ts +98 -0
  365. package/src/catalog/bundleCatalogReadPort.ts +181 -0
  366. package/src/catalog/bundleCatalogStore.ts +274 -0
  367. package/src/catalog/bundlePriceSelection.ts +25 -0
  368. package/src/catalog/bundleRuleCodes.ts +134 -0
  369. package/src/catalog/bundleRules.ts +153 -0
  370. package/src/catalog/catalogDraftStore.ts +71 -0
  371. package/src/catalog/catalogSkuEnvelope.ts +214 -0
  372. package/src/catalog/catalogSkuEnvelopeMapper.ts +160 -0
  373. package/src/catalog/contracts/bundleAdmin.ts +20 -0
  374. package/src/catalog/contracts/bundleAvailability.ts +5 -0
  375. package/src/catalog/contracts/bundleRead.ts +13 -0
  376. package/src/catalog/contracts/draft.ts +69 -0
  377. package/src/catalog/contracts/foundation.ts +104 -0
  378. package/src/catalog/contracts/index.ts +14 -0
  379. package/src/catalog/contracts/lifecycle.ts +74 -0
  380. package/src/catalog/contracts/productMediaContracts.ts +170 -0
  381. package/src/catalog/contracts/productTypes.ts +35 -0
  382. package/src/catalog/contracts/resolveProductMedia.ts +85 -0
  383. package/src/catalog/contracts/sellable.ts +12 -0
  384. package/src/catalog/draftHandler.ts +105 -0
  385. package/src/catalog/draftPorts.ts +18 -0
  386. package/src/catalog/draftSchemas.ts +88 -0
  387. package/src/catalog/draftValidation.ts +100 -0
  388. package/src/catalog/effectiveBundleCatalogQueries.ts +80 -0
  389. package/src/catalog/effectiveBundleCatalogStore.ts +208 -0
  390. package/src/catalog/foundationSchemas.ts +123 -0
  391. package/src/catalog/index.ts +44 -0
  392. package/src/catalog/mediaSourcePack.ts +219 -0
  393. package/src/catalog/productTypeSchemas.ts +70 -0
  394. package/src/catalog/publicationPorts.ts +44 -0
  395. package/src/catalog/readPorts.ts +61 -0
  396. package/src/catalog/referenceCatalogHandler.ts +39 -0
  397. package/src/catalog/resolvedSkuProjection.ts +76 -0
  398. package/src/catalog/sellableBundleHandler.ts +133 -0
  399. package/src/catalog/sellableSchemas.ts +33 -0
  400. package/src/catalog/sql.ts +14 -0
  401. package/src/catalog/validation.ts +3 -0
  402. package/src/communications/contracts/index.ts +1 -0
  403. package/src/communications/contracts/marketingSync.ts +109 -0
  404. package/src/communications/index.ts +1 -0
  405. package/src/communications/klaviyo/consentPush.ts +19 -0
  406. package/src/communications/klaviyo/index.ts +649 -0
  407. package/src/communications/resend/index.ts +350 -0
  408. package/src/composition/catalogContribution.ts +32 -0
  409. package/src/composition/index.ts +3 -0
  410. package/src/composition/inventoryContribution.ts +24 -0
  411. package/src/composition/shippingContribution.ts +24 -0
  412. package/src/inventory/adminInventory.ts +251 -0
  413. package/src/inventory/bindings.ts +61 -0
  414. package/src/inventory/contracts/forecast.ts +96 -0
  415. package/src/inventory/contracts/index.ts +18 -0
  416. package/src/inventory/contracts/inventory.ts +14 -0
  417. package/src/inventory/contracts/newPurchaseStock.ts +23 -0
  418. package/src/inventory/contracts/reservation.ts +20 -0
  419. package/src/inventory/forecast.ts +239 -0
  420. package/src/inventory/index.ts +22 -0
  421. package/src/inventory/inventoryHandlers.ts +215 -0
  422. package/src/inventory/newPurchaseStockAvailability.ts +120 -0
  423. package/src/inventory/ports.ts +46 -0
  424. package/src/inventory/renewalReservation.ts +88 -0
  425. package/src/inventory/reservationPolicy.ts +27 -0
  426. package/src/inventory/schemas.ts +128 -0
  427. package/src/inventory/sql.ts +5 -0
  428. package/src/inventory/subscriptionForecastCalculation.ts +157 -0
  429. package/src/pricing/acquisitionFinancialPolicy.ts +26 -0
  430. package/src/pricing/acquisitionOfferPricing.ts +38 -0
  431. package/src/pricing/acquisitionOfferPricingHandler.ts +157 -0
  432. package/src/pricing/acquisitionOfferPricingSchemas.ts +35 -0
  433. package/src/pricing/acquisitionQuoteBinding.ts +12 -0
  434. package/src/pricing/acquisitionReadiness.ts +148 -0
  435. package/src/pricing/acquisitionTerms.ts +125 -0
  436. package/src/pricing/acquisitionTermsSchemas.ts +52 -0
  437. package/src/pricing/catalogFactsProvenance.ts +102 -0
  438. package/src/pricing/catalogFactsSchemas.ts +69 -0
  439. package/src/pricing/commerceOfferPricing.ts +224 -0
  440. package/src/pricing/commerceOfferPricingCache.ts +53 -0
  441. package/src/pricing/commerceOfferPricingHandler.ts +137 -0
  442. package/src/pricing/commercePriceAuthority.ts +169 -0
  443. package/src/pricing/commercePricingPolicyErrors.ts +88 -0
  444. package/src/pricing/commerceQuoteBatchHandler.ts +129 -0
  445. package/src/pricing/commerceQuoteCatalogReadPort.ts +42 -0
  446. package/src/pricing/commerceQuoteHandler.ts +199 -0
  447. package/src/pricing/commerceQuoteSnapshotExpect.ts +48 -0
  448. package/src/pricing/contracts/acquisitionTerms.ts +26 -0
  449. package/src/pricing/contracts/authority.ts +44 -0
  450. package/src/pricing/contracts/evidence.ts +25 -0
  451. package/src/pricing/contracts/index.ts +17 -0
  452. package/src/pricing/contracts/money.ts +32 -0
  453. package/src/pricing/contracts/offer.ts +42 -0
  454. package/src/pricing/contracts/offerPolicy.ts +17 -0
  455. package/src/pricing/contracts/policy.ts +26 -0
  456. package/src/pricing/contracts/provenance.ts +19 -0
  457. package/src/pricing/contracts/quote.ts +106 -0
  458. package/src/pricing/dbBackedCommerceQuoteHelpers.ts +256 -0
  459. package/src/pricing/dbBackedCommerceQuotePort.ts +280 -0
  460. package/src/pricing/dbBackedCommerceQuoteSnapshotVerifier.ts +49 -0
  461. package/src/pricing/index.ts +53 -0
  462. package/src/pricing/offerPolicyReadiness.ts +110 -0
  463. package/src/pricing/offerPolicySchemas.ts +23 -0
  464. package/src/pricing/offerPricingSchemas.ts +92 -0
  465. package/src/pricing/ports.ts +17 -0
  466. package/src/pricing/pricingPolicyToken.ts +119 -0
  467. package/src/pricing/quoteAcquisitionTerms.ts +10 -0
  468. package/src/pricing/quoteCodeRejectionDetails.ts +23 -0
  469. package/src/pricing/quotePorts.ts +58 -0
  470. package/src/pricing/quotePrimitives.ts +21 -0
  471. package/src/pricing/quotePromotionEvidence.ts +82 -0
  472. package/src/pricing/quoteSchemas.ts +230 -0
  473. package/src/pricing/quoteShippingPromotions.ts +21 -0
  474. package/src/pricing/subscriptionPricePolicy.ts +113 -0
  475. package/src/pricing/taxProfileGuard.ts +33 -0
  476. package/src/pricing/validation.ts +3 -0
  477. package/src/promotions/adminBindings.ts +53 -0
  478. package/src/promotions/adminPromotionCodesContracts.ts +244 -0
  479. package/src/promotions/adminPromotionCodesDataPort.ts +56 -0
  480. package/src/promotions/adminPromotionCodesHandler.ts +254 -0
  481. package/src/promotions/adminPromotionsContracts.ts +121 -0
  482. package/src/promotions/adminPromotionsDataPort.ts +48 -0
  483. package/src/promotions/adminPromotionsHandler.ts +72 -0
  484. package/src/promotions/commerceQuoteReadPreparation.ts +29 -0
  485. package/src/promotions/contracts/index.ts +387 -0
  486. package/src/promotions/index.ts +25 -0
  487. package/src/promotions/promoDataPort.ts +65 -0
  488. package/src/promotions/promotionAcceptanceTelemetry.ts +73 -0
  489. package/src/promotions/promotionBenefitDefinition.ts +48 -0
  490. package/src/promotions/promotionClaimSweepPort.ts +23 -0
  491. package/src/promotions/promotionClaims.ts +70 -0
  492. package/src/promotions/promotionCodePreview.ts +183 -0
  493. package/src/promotions/promotionCodeQuotePort.ts +35 -0
  494. package/src/promotions/promotionQuoteAcceptance.ts +291 -0
  495. package/src/promotions/promotionQuoteBinding.ts +163 -0
  496. package/src/promotions/quoteAcquisitionBinding.ts +7 -0
  497. package/src/promotions/quoteAutomaticPromotions.ts +84 -0
  498. package/src/promotions/quotePromoDiscounts.ts +194 -0
  499. package/src/promotions/quotePromotionCodeAdjustments.ts +277 -0
  500. package/src/promotions/quotePromotionV2.ts +239 -0
  501. package/src/promotions/quoteShippingPolicy.ts +61 -0
  502. package/src/promotions/sql.ts +5 -0
  503. package/src/promotions/validation.ts +3 -0
  504. package/src/shipping/addressCanonLookupHandler.ts +114 -0
  505. package/src/shipping/addressCanonical.ts +31 -0
  506. package/src/shipping/addressPorts.ts +48 -0
  507. package/src/shipping/addressSchemas.ts +28 -0
  508. package/src/shipping/adminShippingRateHandler.ts +72 -0
  509. package/src/shipping/bindings.ts +74 -0
  510. package/src/shipping/contracts/address.ts +71 -0
  511. package/src/shipping/contracts/delivery.ts +114 -0
  512. package/src/shipping/contracts/index.ts +19 -0
  513. package/src/shipping/contracts/shippingRate.ts +7 -0
  514. package/src/shipping/contracts/threshold.ts +22 -0
  515. package/src/shipping/customerAddressesHandler.ts +100 -0
  516. package/src/shipping/customerSelfServiceAddressModels.ts +124 -0
  517. package/src/shipping/deliverySelectionHandler.ts +94 -0
  518. package/src/shipping/deliverySelectionPort.ts +10 -0
  519. package/src/shipping/deliverySelectionSummary.ts +56 -0
  520. package/src/shipping/handlerSchemas.ts +19 -0
  521. package/src/shipping/index.ts +26 -0
  522. package/src/shipping/pickupPointSearchPort.ts +8 -0
  523. package/src/shipping/readDeliverySelectionEvidence.ts +51 -0
  524. package/src/shipping/shippingRatePorts.ts +36 -0
  525. package/src/shipping/thresholdPolicy.ts +65 -0
@@ -0,0 +1,567 @@
1
+ -- Public platform bundle write boundary: the routines that are the only way a bundle
2
+ -- changes, and the ledger that makes a repeated attempt safe.
3
+ --
4
+ -- WHAT THIS FORWARD SHIPS. One relation and seven routines. The earlier forward
5
+ -- (20260812140000) created three relations and left them inert: a bundle could be
6
+ -- described but not written. This one supplies the writer. Every operation an operator
7
+ -- can perform on a bundle -- create it, edit it, clone it, replace its bill of materials,
8
+ -- price it, remove it from sale, put it back, publish it, unpublish it -- goes through
9
+ -- exactly one of these routines and through no other path.
10
+ --
11
+ -- WHY THE LEDGER EXISTS AND WHAT IT IS NOT. `catalog_bundle_write_events` records what
12
+ -- was done, by which principal, and under which idempotency key. It is not an audit
13
+ -- system: it makes no claim about authorisation and enforces no retention. It exists
14
+ -- because a write that can be retried must be able to recognise its own replay, and
15
+ -- because an operator asking "who last repriced this" has no other place to look on a
16
+ -- self-hosted node. The unique index is partial, so an unkeyed write is always allowed
17
+ -- and a keyed one can happen exactly once.
18
+ --
19
+ -- WHY REPLAY SHORT-CIRCUITS INSTEAD OF FAILING. A caller that lost its answer to a
20
+ -- network fault must be able to ask again and learn the outcome, not be told it broke a
21
+ -- rule. Each routine therefore returns the same shape it would have returned, with
22
+ -- `idempotent` set, and writes nothing. A conflicting write under a reused key is not a
23
+ -- case this rewards: the key names the attempt, not the payload.
24
+ --
25
+ -- WHY A DRY RUN VALIDATES AND THEN RETURNS. `p_mode = 'dry_run'` runs every precondition
26
+ -- and stops before the first statement that changes a row. An operator, or an agent
27
+ -- acting for one, can therefore learn whether a composition or a price would be accepted
28
+ -- without leaving a trace of having asked. Validation and commitment share one code path,
29
+ -- so the answer cannot drift from the act.
30
+ --
31
+ -- WHY THE PRICE MAY NOT EXCEED THE SUM OF THE PARTS. A bundle sold above its components
32
+ -- is a surcharge wearing a discount's name. The comparison is one line here and one line
33
+ -- in the pricing engine that allocates the accepted target back onto the components; both
34
+ -- refuse the same input for the same reason, and relaxing the rule stays a change in two
35
+ -- known places rather than a search.
36
+ --
37
+ -- WHY COMPOSITION IS REPLACED WHOLE. Every rule the bill of materials must satisfy is a
38
+ -- property of the set -- it must not be empty, it must not name a unit twice, and it must
39
+ -- contain something that is not an optional extra. An entry-level patch would let the set
40
+ -- pass through states no request described, so the routine deletes and re-inserts inside
41
+ -- its own transaction and checks the set it was handed, once.
42
+ --
43
+ -- WHAT THIS CHAIN CANNOT SAY, STATED PLAINLY. The managed deployment distinguishes a
44
+ -- human operator from a machine actor through a registry of administrators, and refuses
45
+ -- publication to the latter inside the database. This chain has no such registry: it has
46
+ -- one authenticated principal shape and no notion of an operator at all. The two
47
+ -- publish-state routines therefore take the principal, record it and enforce the state
48
+ -- preconditions, but they cannot re-derive what kind of principal it is, so the human-only
49
+ -- rule is enforced by the caller alone here. That is a real difference in defence depth
50
+ -- and it is named rather than papered over; closing it needs an operator-identity slice,
51
+ -- which is a decision about identity and not about bundles.
52
+ --
53
+ -- NO GRANTS AND NO ROW LEVEL SECURITY, DELIBERATELY. These are operator-owned objects. A
54
+ -- routine created here carries EXECUTE for its owner and for PUBLIC by default, so the
55
+ -- forward revokes PUBLIC explicitly and grants nothing back: only the owning role, which
56
+ -- is the adopter's own server identity, can call them. The actor bootstrap gives `anon`
57
+ -- and `authenticated` schema usage and one routine, never these, so both browser
58
+ -- principals are already denied without a policy to say so.
59
+
60
+ CREATE TABLE public.catalog_bundle_write_events (
61
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
62
+ bundle_code text NOT NULL,
63
+ action text NOT NULL,
64
+ principal_id uuid,
65
+ idempotency_key text,
66
+ before_state jsonb,
67
+ after_state jsonb NOT NULL DEFAULT '{}'::jsonb,
68
+ created_at timestamptz NOT NULL DEFAULT now(),
69
+ CONSTRAINT catalog_bundle_write_events_action_check CHECK (action IN (
70
+ 'bundle_draft_upsert', 'bundle_composition_set', 'bundle_target_price_set',
71
+ 'bundle_archive', 'bundle_restore', 'bundle_activate', 'bundle_deactivate'
72
+ )),
73
+ CONSTRAINT catalog_bundle_write_events_key_check
74
+ CHECK (idempotency_key IS NULL OR btrim(idempotency_key) <> '')
75
+ );
76
+
77
+ -- Partial and unique: an unkeyed write is always allowed, a keyed one lands once.
78
+ CREATE UNIQUE INDEX uq_catalog_bundle_write_events_key
79
+ ON public.catalog_bundle_write_events (idempotency_key)
80
+ WHERE idempotency_key IS NOT NULL;
81
+
82
+ CREATE INDEX idx_catalog_bundle_write_events_bundle_code
83
+ ON public.catalog_bundle_write_events (bundle_code, created_at DESC);
84
+
85
+ CREATE FUNCTION public.admin_upsert_bundle_draft(
86
+ p_actor_id uuid,
87
+ p_code text,
88
+ p_title text DEFAULT NULL,
89
+ p_fulfillment_mode text DEFAULT NULL,
90
+ p_composition_constraint jsonb DEFAULT NULL,
91
+ p_metadata jsonb DEFAULT NULL,
92
+ p_clone_from_code text DEFAULT NULL,
93
+ p_mode text DEFAULT 'commit',
94
+ p_idempotency_key text DEFAULT NULL,
95
+ p_request_id text DEFAULT NULL,
96
+ p_source text DEFAULT 'operator'
97
+ )
98
+ RETURNS jsonb
99
+ LANGUAGE plpgsql
100
+ SET search_path = pg_catalog, public
101
+ AS $$
102
+ DECLARE
103
+ v_bundle_id uuid;
104
+ v_source public.catalog_bundles%ROWTYPE;
105
+ BEGIN
106
+ IF p_actor_id IS NULL THEN RAISE EXCEPTION 'actor_required' USING ERRCODE = '42501'; END IF;
107
+
108
+ IF p_idempotency_key IS NOT NULL AND EXISTS (
109
+ SELECT 1 FROM public.catalog_bundle_write_events WHERE idempotency_key = p_idempotency_key
110
+ ) THEN
111
+ RETURN jsonb_build_object('code', p_code, 'idempotent', true, 'dryRun', false);
112
+ END IF;
113
+
114
+ SELECT id INTO v_bundle_id FROM public.catalog_bundles WHERE code = p_code;
115
+
116
+ IF p_clone_from_code IS NOT NULL THEN
117
+ IF v_bundle_id IS NOT NULL THEN RAISE EXCEPTION 'code_taken' USING ERRCODE = 'P0001'; END IF;
118
+ SELECT * INTO v_source FROM public.catalog_bundles WHERE code = p_clone_from_code;
119
+ IF NOT FOUND THEN RAISE EXCEPTION 'clone_source_not_found' USING ERRCODE = 'P0002'; END IF;
120
+ ELSIF v_bundle_id IS NULL AND (p_title IS NULL OR btrim(p_title) = '') THEN
121
+ RAISE EXCEPTION 'bundle_title_required' USING ERRCODE = 'P0001';
122
+ END IF;
123
+
124
+ IF p_mode = 'dry_run' THEN
125
+ RETURN jsonb_build_object('code', p_code, 'idempotent', false, 'dryRun', true);
126
+ END IF;
127
+
128
+ IF p_clone_from_code IS NOT NULL THEN
129
+ INSERT INTO public.catalog_bundles
130
+ (code, title, status, fulfillment_mode, composition_constraint, metadata)
131
+ VALUES (p_code, COALESCE(p_title, v_source.title), 'draft', v_source.fulfillment_mode,
132
+ v_source.composition_constraint, v_source.metadata)
133
+ RETURNING id INTO v_bundle_id;
134
+
135
+ INSERT INTO public.catalog_bundle_components
136
+ (bundle_id, catalog_sku_id, quantity, is_addon, sort_order)
137
+ SELECT v_bundle_id, component.catalog_sku_id, component.quantity,
138
+ component.is_addon, component.sort_order
139
+ FROM public.catalog_bundle_components AS component
140
+ WHERE component.bundle_id = v_source.id;
141
+ ELSE
142
+ INSERT INTO public.catalog_bundles
143
+ (code, title, status, fulfillment_mode, composition_constraint, metadata)
144
+ VALUES (p_code, COALESCE(p_title, p_code), 'draft', COALESCE(p_fulfillment_mode, 'virtual'),
145
+ COALESCE(p_composition_constraint, '{}'::jsonb), COALESCE(p_metadata, '{}'::jsonb))
146
+ ON CONFLICT (code) DO UPDATE
147
+ SET title = COALESCE(p_title, public.catalog_bundles.title),
148
+ fulfillment_mode = COALESCE(p_fulfillment_mode, public.catalog_bundles.fulfillment_mode),
149
+ composition_constraint =
150
+ COALESCE(p_composition_constraint, public.catalog_bundles.composition_constraint),
151
+ metadata = COALESCE(p_metadata, public.catalog_bundles.metadata),
152
+ updated_at = now()
153
+ RETURNING id INTO v_bundle_id;
154
+ END IF;
155
+
156
+ INSERT INTO public.catalog_bundle_write_events
157
+ (bundle_code, action, principal_id, idempotency_key, after_state)
158
+ VALUES (p_code, 'bundle_draft_upsert', p_actor_id, p_idempotency_key,
159
+ jsonb_build_object('title', p_title, 'clonedFrom', p_clone_from_code,
160
+ 'requestId', p_request_id, 'source', p_source));
161
+
162
+ RETURN jsonb_build_object('code', p_code, 'bundleId', v_bundle_id::text,
163
+ 'idempotent', false, 'dryRun', false);
164
+ END;
165
+ $$;
166
+
167
+ CREATE FUNCTION public.admin_set_bundle_composition(
168
+ p_actor_id uuid,
169
+ p_code text,
170
+ p_components jsonb,
171
+ p_mode text DEFAULT 'commit',
172
+ p_idempotency_key text DEFAULT NULL,
173
+ p_request_id text DEFAULT NULL,
174
+ p_source text DEFAULT 'operator'
175
+ )
176
+ RETURNS jsonb
177
+ LANGUAGE plpgsql
178
+ SET search_path = pg_catalog, public
179
+ AS $$
180
+ DECLARE
181
+ v_bundle_id uuid;
182
+ v_count integer;
183
+ v_distinct integer;
184
+ v_core integer;
185
+ v_resolved integer;
186
+ v_unsellable integer;
187
+ v_currency text;
188
+ v_mismatched integer;
189
+ BEGIN
190
+ IF p_actor_id IS NULL THEN RAISE EXCEPTION 'actor_required' USING ERRCODE = '42501'; END IF;
191
+
192
+ IF p_idempotency_key IS NOT NULL AND EXISTS (
193
+ SELECT 1 FROM public.catalog_bundle_write_events WHERE idempotency_key = p_idempotency_key
194
+ ) THEN
195
+ RETURN jsonb_build_object('code', p_code, 'idempotent', true, 'dryRun', false);
196
+ END IF;
197
+
198
+ SELECT id INTO v_bundle_id FROM public.catalog_bundles WHERE code = p_code;
199
+ IF NOT FOUND THEN RAISE EXCEPTION 'bundle_not_found' USING ERRCODE = 'P0002'; END IF;
200
+
201
+ -- The supplied set is read out of the argument each time it is needed. A temp
202
+ -- table is the obvious shortcut and the wrong one: ON COMMIT DROP lives until the
203
+ -- transaction ends, so a second composition write in the same transaction, or a
204
+ -- retry after a caught exception, collides with what the first call left behind.
205
+ SELECT count(*),
206
+ count(DISTINCT entry.sku),
207
+ count(*) FILTER (WHERE NOT COALESCE(entry.is_addon, false)),
208
+ count(unit.id),
209
+ count(*) FILTER (
210
+ WHERE unit.id IS NOT NULL
211
+ AND (unit.status <> 'active' OR unit.sellable_standalone IS NOT TRUE))
212
+ INTO v_count, v_distinct, v_core, v_resolved, v_unsellable
213
+ FROM jsonb_to_recordset(COALESCE(p_components, '[]'::jsonb))
214
+ AS entry(sku text, quantity integer, is_addon boolean, sort_order integer)
215
+ LEFT JOIN public.catalog_skus AS unit ON unit.sku = entry.sku;
216
+
217
+ IF v_count = 0 THEN RAISE EXCEPTION 'min_components' USING ERRCODE = 'P0001'; END IF;
218
+ IF v_distinct <> v_count THEN RAISE EXCEPTION 'duplicate_component' USING ERRCODE = 'P0001'; END IF;
219
+ IF v_core = 0 THEN RAISE EXCEPTION 'addon_only_composition' USING ERRCODE = 'P0001'; END IF;
220
+ IF v_resolved <> v_count THEN RAISE EXCEPTION 'component_not_found' USING ERRCODE = 'P0002'; END IF;
221
+ IF v_unsellable > 0 THEN RAISE EXCEPTION 'component_not_sellable' USING ERRCODE = 'P0001'; END IF;
222
+
223
+ SELECT list.currency INTO v_currency
224
+ FROM public.catalog_bundle_prices AS price
225
+ JOIN public.price_lists AS list ON list.id = price.price_list_id
226
+ WHERE price.bundle_id = v_bundle_id AND price.active
227
+ ORDER BY price.valid_from DESC
228
+ LIMIT 1;
229
+
230
+ IF v_currency IS NOT NULL THEN
231
+ SELECT count(*) INTO v_mismatched
232
+ FROM jsonb_to_recordset(COALESCE(p_components, '[]'::jsonb))
233
+ AS entry(sku text, quantity integer, is_addon boolean, sort_order integer)
234
+ JOIN public.catalog_skus AS unit ON unit.sku = entry.sku
235
+ WHERE NOT EXISTS (
236
+ SELECT 1
237
+ FROM public.price_entries AS unit_price
238
+ JOIN public.price_lists AS unit_list ON unit_list.id = unit_price.price_list_id
239
+ WHERE unit_price.variant_id = unit.id
240
+ AND unit_price.active
241
+ AND unit_list.currency = v_currency);
242
+ IF v_mismatched > 0 THEN
243
+ RAISE EXCEPTION 'component_currency_mismatch' USING ERRCODE = 'P0001';
244
+ END IF;
245
+ END IF;
246
+
247
+ IF p_mode = 'dry_run' THEN
248
+ RETURN jsonb_build_object('code', p_code, 'componentCount', v_count,
249
+ 'idempotent', false, 'dryRun', true);
250
+ END IF;
251
+
252
+ DELETE FROM public.catalog_bundle_components WHERE bundle_id = v_bundle_id;
253
+ INSERT INTO public.catalog_bundle_components
254
+ (bundle_id, catalog_sku_id, quantity, is_addon, sort_order)
255
+ SELECT v_bundle_id, unit.id, entry.quantity,
256
+ COALESCE(entry.is_addon, false), COALESCE(entry.sort_order, 0)
257
+ FROM jsonb_to_recordset(COALESCE(p_components, '[]'::jsonb))
258
+ AS entry(sku text, quantity integer, is_addon boolean, sort_order integer)
259
+ JOIN public.catalog_skus AS unit ON unit.sku = entry.sku;
260
+
261
+ UPDATE public.catalog_bundles SET updated_at = now() WHERE id = v_bundle_id;
262
+
263
+ INSERT INTO public.catalog_bundle_write_events
264
+ (bundle_code, action, principal_id, idempotency_key, after_state)
265
+ VALUES (p_code, 'bundle_composition_set', p_actor_id, p_idempotency_key,
266
+ jsonb_build_object('componentCount', v_count, 'requestId', p_request_id,
267
+ 'source', p_source));
268
+
269
+ RETURN jsonb_build_object('code', p_code, 'componentCount', v_count,
270
+ 'idempotent', false, 'dryRun', false);
271
+ END;
272
+ $$;
273
+
274
+ CREATE FUNCTION public.admin_set_bundle_target_price(
275
+ p_actor_id uuid,
276
+ p_code text,
277
+ p_price_mode text,
278
+ p_target_price_minor integer,
279
+ p_currency text,
280
+ p_amount_kind text DEFAULT 'gross',
281
+ p_mode text DEFAULT 'commit',
282
+ p_idempotency_key text DEFAULT NULL,
283
+ p_request_id text DEFAULT NULL,
284
+ p_source text DEFAULT 'operator'
285
+ )
286
+ RETURNS jsonb
287
+ LANGUAGE plpgsql
288
+ SET search_path = pg_catalog, public
289
+ AS $$
290
+ DECLARE
291
+ v_bundle_id uuid;
292
+ v_list_id uuid;
293
+ v_reference_total bigint;
294
+ v_unit_total bigint;
295
+ v_now timestamptz := clock_timestamp();
296
+ BEGIN
297
+ IF p_actor_id IS NULL THEN RAISE EXCEPTION 'actor_required' USING ERRCODE = '42501'; END IF;
298
+
299
+ IF p_idempotency_key IS NOT NULL AND EXISTS (
300
+ SELECT 1 FROM public.catalog_bundle_write_events WHERE idempotency_key = p_idempotency_key
301
+ ) THEN
302
+ RETURN jsonb_build_object('code', p_code, 'idempotent', true, 'dryRun', false);
303
+ END IF;
304
+
305
+ SELECT id INTO v_bundle_id FROM public.catalog_bundles WHERE code = p_code;
306
+ IF NOT FOUND THEN RAISE EXCEPTION 'bundle_not_found' USING ERRCODE = 'P0002'; END IF;
307
+
308
+ SELECT id INTO v_list_id
309
+ FROM public.price_lists
310
+ WHERE currency = p_currency AND status = 'active'
311
+ ORDER BY created_at DESC
312
+ LIMIT 1;
313
+ IF v_list_id IS NULL THEN RAISE EXCEPTION 'no_active_price_list' USING ERRCODE = 'P0002'; END IF;
314
+
315
+ SELECT COALESCE(sum(component.quantity::bigint * unit_price.unit_price_minor), 0),
316
+ COALESCE(sum(component.quantity::bigint), 0)
317
+ INTO v_reference_total, v_unit_total
318
+ FROM public.catalog_bundle_components AS component
319
+ JOIN public.price_entries AS unit_price
320
+ ON unit_price.variant_id = component.catalog_sku_id
321
+ AND unit_price.price_list_id = v_list_id
322
+ AND unit_price.active
323
+ WHERE component.bundle_id = v_bundle_id;
324
+
325
+ IF v_reference_total = 0 THEN RAISE EXCEPTION 'min_components' USING ERRCODE = 'P0001'; END IF;
326
+ IF p_target_price_minor > v_reference_total THEN
327
+ RAISE EXCEPTION 'target_above_component_sum' USING ERRCODE = 'P0001';
328
+ END IF;
329
+ IF p_target_price_minor < v_unit_total THEN
330
+ RAISE EXCEPTION 'target_below_floor' USING ERRCODE = 'P0001';
331
+ END IF;
332
+
333
+ IF p_mode = 'dry_run' THEN
334
+ RETURN jsonb_build_object('code', p_code, 'idempotent', false, 'dryRun', true);
335
+ END IF;
336
+
337
+ UPDATE public.catalog_bundle_prices
338
+ SET active = false, valid_to = v_now
339
+ WHERE bundle_id = v_bundle_id AND price_list_id = v_list_id AND mode = p_price_mode
340
+ AND active AND valid_from < v_now;
341
+
342
+ INSERT INTO public.catalog_bundle_prices
343
+ (bundle_id, price_list_id, mode, target_price_minor, amount_kind, active, valid_from)
344
+ VALUES (v_bundle_id, v_list_id, p_price_mode, p_target_price_minor,
345
+ COALESCE(p_amount_kind, 'gross'), true, v_now);
346
+
347
+ INSERT INTO public.catalog_bundle_write_events
348
+ (bundle_code, action, principal_id, idempotency_key, after_state)
349
+ VALUES (p_code, 'bundle_target_price_set', p_actor_id, p_idempotency_key,
350
+ jsonb_build_object('mode', p_price_mode, 'targetPriceMinor', p_target_price_minor,
351
+ 'currency', p_currency, 'referenceTotalMinor', v_reference_total,
352
+ 'requestId', p_request_id, 'source', p_source));
353
+
354
+ RETURN jsonb_build_object('code', p_code, 'idempotent', false, 'dryRun', false);
355
+ END;
356
+ $$;
357
+
358
+ CREATE FUNCTION public.admin_archive_bundle(
359
+ p_actor_id uuid,
360
+ p_code text,
361
+ p_mode text DEFAULT 'commit',
362
+ p_idempotency_key text DEFAULT NULL,
363
+ p_request_id text DEFAULT NULL,
364
+ p_source text DEFAULT 'operator'
365
+ )
366
+ RETURNS jsonb
367
+ LANGUAGE plpgsql
368
+ SET search_path = pg_catalog, public
369
+ AS $$
370
+ DECLARE
371
+ v_status text;
372
+ BEGIN
373
+ IF p_actor_id IS NULL THEN RAISE EXCEPTION 'actor_required' USING ERRCODE = '42501'; END IF;
374
+
375
+ IF p_idempotency_key IS NOT NULL AND EXISTS (
376
+ SELECT 1 FROM public.catalog_bundle_write_events WHERE idempotency_key = p_idempotency_key
377
+ ) THEN
378
+ RETURN jsonb_build_object('code', p_code, 'idempotent', true, 'dryRun', false);
379
+ END IF;
380
+
381
+ SELECT status INTO v_status FROM public.catalog_bundles WHERE code = p_code;
382
+ IF NOT FOUND THEN RAISE EXCEPTION 'bundle_not_found' USING ERRCODE = 'P0002'; END IF;
383
+
384
+ IF p_mode = 'dry_run' THEN
385
+ RETURN jsonb_build_object('code', p_code, 'idempotent', false, 'dryRun', true);
386
+ END IF;
387
+
388
+ UPDATE public.catalog_bundles SET status = 'archived', updated_at = now() WHERE code = p_code;
389
+
390
+ INSERT INTO public.catalog_bundle_write_events
391
+ (bundle_code, action, principal_id, idempotency_key, before_state, after_state)
392
+ VALUES (p_code, 'bundle_archive', p_actor_id, p_idempotency_key,
393
+ jsonb_build_object('status', v_status),
394
+ jsonb_build_object('status', 'archived', 'requestId', p_request_id, 'source', p_source));
395
+
396
+ RETURN jsonb_build_object('code', p_code, 'idempotent', false, 'dryRun', false);
397
+ END;
398
+ $$;
399
+
400
+ CREATE FUNCTION public.admin_restore_bundle(
401
+ p_actor_id uuid,
402
+ p_code text,
403
+ p_mode text DEFAULT 'commit',
404
+ p_idempotency_key text DEFAULT NULL,
405
+ p_request_id text DEFAULT NULL,
406
+ p_source text DEFAULT 'operator'
407
+ )
408
+ RETURNS jsonb
409
+ LANGUAGE plpgsql
410
+ SET search_path = pg_catalog, public
411
+ AS $$
412
+ DECLARE
413
+ v_status text;
414
+ BEGIN
415
+ IF p_actor_id IS NULL THEN RAISE EXCEPTION 'actor_required' USING ERRCODE = '42501'; END IF;
416
+
417
+ IF p_idempotency_key IS NOT NULL AND EXISTS (
418
+ SELECT 1 FROM public.catalog_bundle_write_events WHERE idempotency_key = p_idempotency_key
419
+ ) THEN
420
+ RETURN jsonb_build_object('code', p_code, 'idempotent', true, 'dryRun', false);
421
+ END IF;
422
+
423
+ SELECT status INTO v_status FROM public.catalog_bundles WHERE code = p_code;
424
+ IF NOT FOUND THEN RAISE EXCEPTION 'bundle_not_found' USING ERRCODE = 'P0002'; END IF;
425
+ IF v_status <> 'archived' THEN
426
+ RAISE EXCEPTION 'restore_requires_archived' USING ERRCODE = 'P0001';
427
+ END IF;
428
+
429
+ IF p_mode = 'dry_run' THEN
430
+ RETURN jsonb_build_object('code', p_code, 'idempotent', false, 'dryRun', true);
431
+ END IF;
432
+
433
+ UPDATE public.catalog_bundles SET status = 'draft', updated_at = now() WHERE code = p_code;
434
+
435
+ INSERT INTO public.catalog_bundle_write_events
436
+ (bundle_code, action, principal_id, idempotency_key, before_state, after_state)
437
+ VALUES (p_code, 'bundle_restore', p_actor_id, p_idempotency_key,
438
+ jsonb_build_object('status', v_status),
439
+ jsonb_build_object('status', 'draft', 'requestId', p_request_id, 'source', p_source));
440
+
441
+ RETURN jsonb_build_object('code', p_code, 'idempotent', false, 'dryRun', false);
442
+ END;
443
+ $$;
444
+
445
+ CREATE FUNCTION public.admin_activate_bundle(
446
+ p_actor_id uuid,
447
+ p_code text,
448
+ p_mode text DEFAULT 'commit',
449
+ p_idempotency_key text DEFAULT NULL,
450
+ p_request_id text DEFAULT NULL,
451
+ p_source text DEFAULT 'operator'
452
+ )
453
+ RETURNS jsonb
454
+ LANGUAGE plpgsql
455
+ SET search_path = pg_catalog, public
456
+ AS $$
457
+ DECLARE
458
+ v_bundle public.catalog_bundles%ROWTYPE;
459
+ v_components integer;
460
+ v_prices integer;
461
+ BEGIN
462
+ IF p_actor_id IS NULL THEN RAISE EXCEPTION 'actor_required' USING ERRCODE = '42501'; END IF;
463
+
464
+ IF p_idempotency_key IS NOT NULL AND EXISTS (
465
+ SELECT 1 FROM public.catalog_bundle_write_events WHERE idempotency_key = p_idempotency_key
466
+ ) THEN
467
+ RETURN jsonb_build_object('code', p_code, 'idempotent', true, 'dryRun', false);
468
+ END IF;
469
+
470
+ SELECT * INTO v_bundle FROM public.catalog_bundles WHERE code = p_code;
471
+ IF NOT FOUND THEN RAISE EXCEPTION 'bundle_not_found' USING ERRCODE = 'P0002'; END IF;
472
+
473
+ IF v_bundle.fulfillment_mode <> 'virtual' THEN
474
+ RAISE EXCEPTION 'fulfillment_mode_unsupported' USING ERRCODE = 'P0001';
475
+ END IF;
476
+
477
+ SELECT count(*) INTO v_components
478
+ FROM public.catalog_bundle_components WHERE bundle_id = v_bundle.id;
479
+ IF v_components = 0 THEN
480
+ RAISE EXCEPTION 'composition_required_to_sell' USING ERRCODE = 'P0001';
481
+ END IF;
482
+
483
+ SELECT count(*) INTO v_prices
484
+ FROM public.catalog_bundle_prices WHERE bundle_id = v_bundle.id AND active;
485
+ IF v_prices = 0 THEN
486
+ RAISE EXCEPTION 'price_required_to_sell' USING ERRCODE = 'P0001';
487
+ END IF;
488
+
489
+ IF p_mode = 'dry_run' THEN
490
+ RETURN jsonb_build_object('code', p_code, 'idempotent', false, 'dryRun', true);
491
+ END IF;
492
+
493
+ UPDATE public.catalog_bundles SET status = 'active', updated_at = now() WHERE id = v_bundle.id;
494
+
495
+ INSERT INTO public.catalog_bundle_write_events
496
+ (bundle_code, action, principal_id, idempotency_key, before_state, after_state)
497
+ VALUES (p_code, 'bundle_activate', p_actor_id, p_idempotency_key,
498
+ jsonb_build_object('status', v_bundle.status),
499
+ jsonb_build_object('status', 'active', 'requestId', p_request_id, 'source', p_source));
500
+
501
+ RETURN jsonb_build_object('code', p_code, 'idempotent', false, 'dryRun', false);
502
+ END;
503
+ $$;
504
+
505
+ CREATE FUNCTION public.admin_deactivate_bundle(
506
+ p_actor_id uuid,
507
+ p_code text,
508
+ p_mode text DEFAULT 'commit',
509
+ p_idempotency_key text DEFAULT NULL,
510
+ p_request_id text DEFAULT NULL,
511
+ p_source text DEFAULT 'operator'
512
+ )
513
+ RETURNS jsonb
514
+ LANGUAGE plpgsql
515
+ SET search_path = pg_catalog, public
516
+ AS $$
517
+ DECLARE
518
+ v_status text;
519
+ BEGIN
520
+ IF p_actor_id IS NULL THEN RAISE EXCEPTION 'actor_required' USING ERRCODE = '42501'; END IF;
521
+
522
+ IF p_idempotency_key IS NOT NULL AND EXISTS (
523
+ SELECT 1 FROM public.catalog_bundle_write_events WHERE idempotency_key = p_idempotency_key
524
+ ) THEN
525
+ RETURN jsonb_build_object('code', p_code, 'idempotent', true, 'dryRun', false);
526
+ END IF;
527
+
528
+ SELECT status INTO v_status FROM public.catalog_bundles WHERE code = p_code;
529
+ IF NOT FOUND THEN RAISE EXCEPTION 'bundle_not_found' USING ERRCODE = 'P0002'; END IF;
530
+ IF v_status <> 'active' THEN
531
+ RAISE EXCEPTION 'deactivate_requires_active' USING ERRCODE = 'P0001';
532
+ END IF;
533
+
534
+ IF p_mode = 'dry_run' THEN
535
+ RETURN jsonb_build_object('code', p_code, 'idempotent', false, 'dryRun', true);
536
+ END IF;
537
+
538
+ UPDATE public.catalog_bundles SET status = 'draft', updated_at = now() WHERE code = p_code;
539
+
540
+ INSERT INTO public.catalog_bundle_write_events
541
+ (bundle_code, action, principal_id, idempotency_key, before_state, after_state)
542
+ VALUES (p_code, 'bundle_deactivate', p_actor_id, p_idempotency_key,
543
+ jsonb_build_object('status', v_status),
544
+ jsonb_build_object('status', 'draft', 'requestId', p_request_id, 'source', p_source));
545
+
546
+ RETURN jsonb_build_object('code', p_code, 'idempotent', false, 'dryRun', false);
547
+ END;
548
+ $$;
549
+
550
+ REVOKE ALL ON FUNCTION public.admin_upsert_bundle_draft(
551
+ uuid, text, text, text, jsonb, jsonb, text, text, text, text, text
552
+ ) FROM PUBLIC;
553
+ REVOKE ALL ON FUNCTION public.admin_set_bundle_composition(
554
+ uuid, text, jsonb, text, text, text, text
555
+ ) FROM PUBLIC;
556
+ REVOKE ALL ON FUNCTION public.admin_set_bundle_target_price(
557
+ uuid, text, text, integer, text, text, text, text, text, text
558
+ ) FROM PUBLIC;
559
+ REVOKE ALL ON FUNCTION public.admin_archive_bundle(uuid, text, text, text, text, text) FROM PUBLIC;
560
+ REVOKE ALL ON FUNCTION public.admin_restore_bundle(uuid, text, text, text, text, text) FROM PUBLIC;
561
+ REVOKE ALL ON FUNCTION public.admin_activate_bundle(uuid, text, text, text, text, text) FROM PUBLIC;
562
+ REVOKE ALL ON FUNCTION public.admin_deactivate_bundle(uuid, text, text, text, text, text) FROM PUBLIC;
563
+
564
+ COMMENT ON TABLE public.catalog_bundle_write_events IS
565
+ 'Append-only record of every accepted bundle write: what changed, which principal asked, and under which idempotency key. Replay recognition, not an authorisation record.';
566
+ COMMENT ON COLUMN public.catalog_bundle_write_events.idempotency_key IS
567
+ 'Names one attempt, not one payload. Unique when present, so a retried call returns the original outcome instead of writing twice.';