@openlup/platform 0.17.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (486) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/README.md +284 -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 +20 -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 +166 -0
  22. package/dist/catalog/bundleCatalogReadPort.js +36 -0
  23. package/dist/catalog/bundleCatalogStore.d.ts +3 -0
  24. package/dist/catalog/bundleCatalogStore.js +152 -0
  25. package/dist/catalog/bundleRuleCodes.d.ts +41 -0
  26. package/dist/catalog/bundleRuleCodes.js +110 -0
  27. package/dist/catalog/bundleRules.d.ts +69 -0
  28. package/dist/catalog/bundleRules.js +102 -0
  29. package/dist/catalog/catalogDraftStore.d.ts +4 -0
  30. package/dist/catalog/catalogDraftStore.js +63 -0
  31. package/dist/catalog/catalogSkuEnvelope.d.ts +7 -0
  32. package/dist/catalog/catalogSkuEnvelope.js +142 -0
  33. package/dist/catalog/catalogSkuEnvelopeMapper.d.ts +38 -0
  34. package/dist/catalog/catalogSkuEnvelopeMapper.js +88 -0
  35. package/dist/catalog/contracts/bundleAdmin.d.ts +143 -0
  36. package/dist/catalog/contracts/bundleAdmin.js +1 -0
  37. package/dist/catalog/contracts/bundleAvailability.d.ts +61 -0
  38. package/dist/catalog/contracts/bundleAvailability.js +1 -0
  39. package/dist/catalog/contracts/bundleRead.d.ts +223 -0
  40. package/dist/catalog/contracts/bundleRead.js +1 -0
  41. package/dist/catalog/contracts/draft.d.ts +79 -0
  42. package/dist/catalog/contracts/draft.js +47 -0
  43. package/dist/catalog/contracts/foundation.d.ts +78 -0
  44. package/dist/catalog/contracts/foundation.js +53 -0
  45. package/dist/catalog/contracts/index.d.ts +14 -0
  46. package/dist/catalog/contracts/index.js +6 -0
  47. package/dist/catalog/contracts/lifecycle.d.ts +77 -0
  48. package/dist/catalog/contracts/lifecycle.js +1 -0
  49. package/dist/catalog/contracts/productMediaContracts.d.ts +77 -0
  50. package/dist/catalog/contracts/productMediaContracts.js +149 -0
  51. package/dist/catalog/contracts/productTypes.d.ts +54 -0
  52. package/dist/catalog/contracts/productTypes.js +16 -0
  53. package/dist/catalog/contracts/resolveProductMedia.d.ts +3 -0
  54. package/dist/catalog/contracts/resolveProductMedia.js +78 -0
  55. package/dist/catalog/contracts/sellable.d.ts +23 -0
  56. package/dist/catalog/contracts/sellable.js +1 -0
  57. package/dist/catalog/draftHandler.d.ts +25 -0
  58. package/dist/catalog/draftHandler.js +118 -0
  59. package/dist/catalog/draftPorts.d.ts +18 -0
  60. package/dist/catalog/draftPorts.js +8 -0
  61. package/dist/catalog/draftSchemas.d.ts +423 -0
  62. package/dist/catalog/draftSchemas.js +89 -0
  63. package/dist/catalog/draftValidation.d.ts +21 -0
  64. package/dist/catalog/draftValidation.js +125 -0
  65. package/dist/catalog/foundationSchemas.d.ts +98 -0
  66. package/dist/catalog/foundationSchemas.js +110 -0
  67. package/dist/catalog/index.d.ts +43 -0
  68. package/dist/catalog/index.js +25 -0
  69. package/dist/catalog/mediaSourcePack.d.ts +51 -0
  70. package/dist/catalog/mediaSourcePack.js +247 -0
  71. package/dist/catalog/productTypeSchemas.d.ts +46 -0
  72. package/dist/catalog/productTypeSchemas.js +72 -0
  73. package/dist/catalog/publicationPorts.d.ts +42 -0
  74. package/dist/catalog/publicationPorts.js +1 -0
  75. package/dist/catalog/readPorts.d.ts +51 -0
  76. package/dist/catalog/readPorts.js +1 -0
  77. package/dist/catalog/referenceCatalogHandler.d.ts +7 -0
  78. package/dist/catalog/referenceCatalogHandler.js +27 -0
  79. package/dist/catalog/resolvedSkuProjection.d.ts +88 -0
  80. package/dist/catalog/resolvedSkuProjection.js +60 -0
  81. package/dist/catalog/sellableBundleHandler.d.ts +46 -0
  82. package/dist/catalog/sellableBundleHandler.js +84 -0
  83. package/dist/catalog/sellableSchemas.d.ts +46 -0
  84. package/dist/catalog/sellableSchemas.js +27 -0
  85. package/dist/catalog/sql.d.ts +46 -0
  86. package/dist/catalog/sql.js +13 -0
  87. package/dist/catalog/validation.d.ts +2 -0
  88. package/dist/catalog/validation.js +3 -0
  89. package/dist/communications/contracts/index.d.ts +1 -0
  90. package/dist/communications/contracts/marketingSync.d.ts +194 -0
  91. package/dist/communications/contracts/marketingSync.js +1 -0
  92. package/dist/communications/index.d.ts +1 -0
  93. package/dist/communications/klaviyo/consentPush.d.ts +51 -0
  94. package/dist/communications/klaviyo/consentPush.js +19 -0
  95. package/dist/communications/klaviyo/index.d.ts +110 -0
  96. package/dist/communications/klaviyo/index.js +860 -0
  97. package/dist/communications/newsletterIntegrationContracts.d.ts +3 -3
  98. package/dist/communications/resend/index.d.ts +40 -0
  99. package/dist/communications/resend/index.js +310 -0
  100. package/dist/composition/catalogContribution.d.ts +18 -0
  101. package/dist/composition/catalogContribution.js +21 -0
  102. package/dist/composition/index.d.ts +1 -0
  103. package/dist/composition/index.js +1 -0
  104. package/dist/identity/customerContracts.d.ts +2 -2
  105. package/dist/inventory/adminInventory.d.ts +13 -0
  106. package/dist/inventory/adminInventory.js +214 -0
  107. package/dist/inventory/contracts/forecast.d.ts +86 -0
  108. package/dist/inventory/contracts/forecast.js +1 -0
  109. package/dist/inventory/contracts/index.d.ts +4 -0
  110. package/dist/inventory/contracts/index.js +1 -0
  111. package/dist/inventory/contracts/inventory.d.ts +157 -0
  112. package/dist/inventory/contracts/inventory.js +1 -0
  113. package/dist/inventory/contracts/newPurchaseStock.d.ts +20 -0
  114. package/dist/inventory/contracts/newPurchaseStock.js +1 -0
  115. package/dist/inventory/contracts/reservation.d.ts +18 -0
  116. package/dist/inventory/contracts/reservation.js +1 -0
  117. package/dist/inventory/forecast.d.ts +24 -0
  118. package/dist/inventory/forecast.js +200 -0
  119. package/dist/inventory/index.d.ts +13 -0
  120. package/dist/inventory/index.js +10 -0
  121. package/dist/inventory/inventoryHandlers.d.ts +53 -0
  122. package/dist/inventory/inventoryHandlers.js +151 -0
  123. package/dist/inventory/newPurchaseStockAvailability.d.ts +47 -0
  124. package/dist/inventory/newPurchaseStockAvailability.js +75 -0
  125. package/dist/inventory/ports.d.ts +21 -0
  126. package/dist/inventory/ports.js +16 -0
  127. package/dist/inventory/renewalReservation.d.ts +27 -0
  128. package/dist/inventory/renewalReservation.js +61 -0
  129. package/dist/inventory/reservationPolicy.d.ts +6 -0
  130. package/dist/inventory/reservationPolicy.js +19 -0
  131. package/dist/inventory/schemas.d.ts +137 -0
  132. package/dist/inventory/schemas.js +112 -0
  133. package/dist/inventory/sql.d.ts +14 -0
  134. package/dist/inventory/sql.js +5 -0
  135. package/dist/inventory/subscriptionForecastCalculation.d.ts +4 -0
  136. package/dist/inventory/subscriptionForecastCalculation.js +133 -0
  137. package/dist/pricing/acquisitionFinancialPolicy.d.ts +26 -0
  138. package/dist/pricing/acquisitionFinancialPolicy.js +1 -0
  139. package/dist/pricing/acquisitionOfferPricing.d.ts +27 -0
  140. package/dist/pricing/acquisitionOfferPricing.js +25 -0
  141. package/dist/pricing/acquisitionOfferPricingHandler.d.ts +20 -0
  142. package/dist/pricing/acquisitionOfferPricingHandler.js +105 -0
  143. package/dist/pricing/acquisitionOfferPricingSchemas.d.ts +31 -0
  144. package/dist/pricing/acquisitionOfferPricingSchemas.js +33 -0
  145. package/dist/pricing/acquisitionQuoteBinding.d.ts +12 -0
  146. package/dist/pricing/acquisitionQuoteBinding.js +12 -0
  147. package/dist/pricing/acquisitionReadiness.d.ts +31 -0
  148. package/dist/pricing/acquisitionReadiness.js +117 -0
  149. package/dist/pricing/acquisitionTerms.d.ts +19 -0
  150. package/dist/pricing/acquisitionTerms.js +126 -0
  151. package/dist/pricing/acquisitionTermsSchemas.d.ts +59 -0
  152. package/dist/pricing/acquisitionTermsSchemas.js +42 -0
  153. package/dist/pricing/catalogFactsProvenance.d.ts +33 -0
  154. package/dist/pricing/catalogFactsProvenance.js +64 -0
  155. package/dist/pricing/catalogFactsSchemas.d.ts +45 -0
  156. package/dist/pricing/catalogFactsSchemas.js +58 -0
  157. package/dist/pricing/commerceOfferPricing.d.ts +25 -0
  158. package/dist/pricing/commerceOfferPricing.js +139 -0
  159. package/dist/pricing/commerceOfferPricingCache.d.ts +10 -0
  160. package/dist/pricing/commerceOfferPricingCache.js +44 -0
  161. package/dist/pricing/commerceOfferPricingHandler.d.ts +21 -0
  162. package/dist/pricing/commerceOfferPricingHandler.js +78 -0
  163. package/dist/pricing/commercePriceAuthority.d.ts +26 -0
  164. package/dist/pricing/commercePriceAuthority.js +79 -0
  165. package/dist/pricing/commercePricingPolicyErrors.d.ts +40 -0
  166. package/dist/pricing/commercePricingPolicyErrors.js +67 -0
  167. package/dist/pricing/commerceQuoteBatchHandler.d.ts +37 -0
  168. package/dist/pricing/commerceQuoteBatchHandler.js +83 -0
  169. package/dist/pricing/commerceQuoteCatalogReadPort.d.ts +27 -0
  170. package/dist/pricing/commerceQuoteCatalogReadPort.js +8 -0
  171. package/dist/pricing/commerceQuoteHandler.d.ts +31 -0
  172. package/dist/pricing/commerceQuoteHandler.js +110 -0
  173. package/dist/pricing/commerceQuoteSnapshotExpect.d.ts +9 -0
  174. package/dist/pricing/commerceQuoteSnapshotExpect.js +22 -0
  175. package/dist/pricing/contracts/acquisitionTerms.d.ts +32 -0
  176. package/dist/pricing/contracts/acquisitionTerms.js +2 -0
  177. package/dist/pricing/contracts/authority.d.ts +27 -0
  178. package/dist/pricing/contracts/authority.js +1 -0
  179. package/dist/pricing/contracts/evidence.d.ts +33 -0
  180. package/dist/pricing/contracts/evidence.js +1 -0
  181. package/dist/pricing/contracts/index.d.ts +17 -0
  182. package/dist/pricing/contracts/index.js +7 -0
  183. package/dist/pricing/contracts/money.d.ts +27 -0
  184. package/dist/pricing/contracts/money.js +1 -0
  185. package/dist/pricing/contracts/offer.d.ts +52 -0
  186. package/dist/pricing/contracts/offer.js +4 -0
  187. package/dist/pricing/contracts/offerPolicy.d.ts +16 -0
  188. package/dist/pricing/contracts/offerPolicy.js +5 -0
  189. package/dist/pricing/contracts/policy.d.ts +22 -0
  190. package/dist/pricing/contracts/policy.js +3 -0
  191. package/dist/pricing/contracts/provenance.d.ts +24 -0
  192. package/dist/pricing/contracts/provenance.js +2 -0
  193. package/dist/pricing/contracts/quote.d.ts +94 -0
  194. package/dist/pricing/contracts/quote.js +1 -0
  195. package/dist/pricing/dbBackedCommerceQuoteHelpers.d.ts +78 -0
  196. package/dist/pricing/dbBackedCommerceQuoteHelpers.js +146 -0
  197. package/dist/pricing/dbBackedCommerceQuotePort.d.ts +66 -0
  198. package/dist/pricing/dbBackedCommerceQuotePort.js +206 -0
  199. package/dist/pricing/dbBackedCommerceQuoteSnapshotVerifier.d.ts +4 -0
  200. package/dist/pricing/dbBackedCommerceQuoteSnapshotVerifier.js +25 -0
  201. package/dist/pricing/index.d.ts +51 -0
  202. package/dist/pricing/index.js +36 -0
  203. package/dist/pricing/offerPolicyReadiness.d.ts +21 -0
  204. package/dist/pricing/offerPolicyReadiness.js +79 -0
  205. package/dist/pricing/offerPolicySchemas.d.ts +25 -0
  206. package/dist/pricing/offerPolicySchemas.js +15 -0
  207. package/dist/pricing/offerPricingSchemas.d.ts +315 -0
  208. package/dist/pricing/offerPricingSchemas.js +65 -0
  209. package/dist/pricing/ports.d.ts +13 -0
  210. package/dist/pricing/ports.js +9 -0
  211. package/dist/pricing/pricingPolicyToken.d.ts +39 -0
  212. package/dist/pricing/pricingPolicyToken.js +84 -0
  213. package/dist/pricing/quoteAcquisitionTerms.d.ts +2 -0
  214. package/dist/pricing/quoteAcquisitionTerms.js +4 -0
  215. package/dist/pricing/quoteCodeRejectionDetails.d.ts +20 -0
  216. package/dist/pricing/quoteCodeRejectionDetails.js +22 -0
  217. package/dist/pricing/quotePorts.d.ts +30 -0
  218. package/dist/pricing/quotePorts.js +20 -0
  219. package/dist/pricing/quotePrimitives.d.ts +13 -0
  220. package/dist/pricing/quotePrimitives.js +18 -0
  221. package/dist/pricing/quotePromotionEvidence.d.ts +39 -0
  222. package/dist/pricing/quotePromotionEvidence.js +73 -0
  223. package/dist/pricing/quoteSchemas.d.ts +1176 -0
  224. package/dist/pricing/quoteSchemas.js +196 -0
  225. package/dist/pricing/subscriptionPricePolicy.d.ts +52 -0
  226. package/dist/pricing/subscriptionPricePolicy.js +96 -0
  227. package/dist/pricing/taxProfileGuard.d.ts +2 -0
  228. package/dist/pricing/taxProfileGuard.js +24 -0
  229. package/dist/pricing/validation.d.ts +2 -0
  230. package/dist/pricing/validation.js +3 -0
  231. package/dist/promotions/adminBindings.d.ts +72 -0
  232. package/dist/promotions/adminBindings.js +1 -0
  233. package/dist/promotions/adminPromotionCodesContracts.d.ts +498 -0
  234. package/dist/promotions/adminPromotionCodesContracts.js +211 -0
  235. package/dist/promotions/adminPromotionCodesDataPort.d.ts +47 -0
  236. package/dist/promotions/adminPromotionCodesDataPort.js +1 -0
  237. package/dist/promotions/adminPromotionCodesHandler.d.ts +14 -0
  238. package/dist/promotions/adminPromotionCodesHandler.js +194 -0
  239. package/dist/promotions/adminPromotionsContracts.d.ts +235 -0
  240. package/dist/promotions/adminPromotionsContracts.js +99 -0
  241. package/dist/promotions/adminPromotionsDataPort.d.ts +42 -0
  242. package/dist/promotions/adminPromotionsDataPort.js +1 -0
  243. package/dist/promotions/adminPromotionsHandler.d.ts +16 -0
  244. package/dist/promotions/adminPromotionsHandler.js +47 -0
  245. package/dist/promotions/contracts/index.d.ts +387 -0
  246. package/dist/promotions/contracts/index.js +1 -0
  247. package/dist/promotions/index.d.ts +22 -0
  248. package/dist/promotions/index.js +21 -0
  249. package/dist/promotions/promoDataPort.d.ts +47 -0
  250. package/dist/promotions/promoDataPort.js +14 -0
  251. package/dist/promotions/promotionAcceptanceTelemetry.d.ts +29 -0
  252. package/dist/promotions/promotionAcceptanceTelemetry.js +45 -0
  253. package/dist/promotions/promotionBenefitDefinition.d.ts +37 -0
  254. package/dist/promotions/promotionBenefitDefinition.js +33 -0
  255. package/dist/promotions/promotionClaimSweepPort.d.ts +14 -0
  256. package/dist/promotions/promotionClaimSweepPort.js +8 -0
  257. package/dist/promotions/promotionClaims.d.ts +5 -0
  258. package/dist/promotions/promotionClaims.js +57 -0
  259. package/dist/promotions/promotionCodePreview.d.ts +11 -0
  260. package/dist/promotions/promotionCodePreview.js +154 -0
  261. package/dist/promotions/promotionCodeQuotePort.d.ts +25 -0
  262. package/dist/promotions/promotionCodeQuotePort.js +1 -0
  263. package/dist/promotions/promotionQuoteAcceptance.d.ts +65 -0
  264. package/dist/promotions/promotionQuoteAcceptance.js +202 -0
  265. package/dist/promotions/promotionQuoteBinding.d.ts +71 -0
  266. package/dist/promotions/promotionQuoteBinding.js +85 -0
  267. package/dist/promotions/quoteAutomaticPromotions.d.ts +24 -0
  268. package/dist/promotions/quoteAutomaticPromotions.js +36 -0
  269. package/dist/promotions/quotePromoDiscounts.d.ts +32 -0
  270. package/dist/promotions/quotePromoDiscounts.js +140 -0
  271. package/dist/promotions/quotePromotionCodeAdjustments.d.ts +27 -0
  272. package/dist/promotions/quotePromotionCodeAdjustments.js +223 -0
  273. package/dist/promotions/quotePromotionV2.d.ts +29 -0
  274. package/dist/promotions/quotePromotionV2.js +174 -0
  275. package/dist/promotions/sql.d.ts +14 -0
  276. package/dist/promotions/sql.js +5 -0
  277. package/dist/promotions/validation.d.ts +2 -0
  278. package/dist/promotions/validation.js +3 -0
  279. package/dist/shipping/addressCanonLookupHandler.d.ts +7 -0
  280. package/dist/shipping/addressCanonLookupHandler.js +105 -0
  281. package/dist/shipping/addressCanonical.d.ts +2 -0
  282. package/dist/shipping/addressCanonical.js +26 -0
  283. package/dist/shipping/addressPorts.d.ts +39 -0
  284. package/dist/shipping/addressPorts.js +1 -0
  285. package/dist/shipping/addressSchemas.d.ts +30 -0
  286. package/dist/shipping/addressSchemas.js +1 -0
  287. package/dist/shipping/adminShippingRateHandler.d.ts +4 -0
  288. package/dist/shipping/adminShippingRateHandler.js +64 -0
  289. package/dist/shipping/contracts/address.d.ts +144 -0
  290. package/dist/shipping/contracts/address.js +1 -0
  291. package/dist/shipping/contracts/delivery.d.ts +110 -0
  292. package/dist/shipping/contracts/delivery.js +1 -0
  293. package/dist/shipping/contracts/index.d.ts +3 -0
  294. package/dist/shipping/contracts/index.js +1 -0
  295. package/dist/shipping/contracts/shippingRate.d.ts +11 -0
  296. package/dist/shipping/contracts/shippingRate.js +1 -0
  297. package/dist/shipping/customerAddressesHandler.d.ts +5 -0
  298. package/dist/shipping/customerAddressesHandler.js +80 -0
  299. package/dist/shipping/customerSelfServiceAddressModels.d.ts +27 -0
  300. package/dist/shipping/customerSelfServiceAddressModels.js +101 -0
  301. package/dist/shipping/deliverySelectionHandler.d.ts +10 -0
  302. package/dist/shipping/deliverySelectionHandler.js +77 -0
  303. package/dist/shipping/deliverySelectionPort.d.ts +8 -0
  304. package/dist/shipping/deliverySelectionPort.js +1 -0
  305. package/dist/shipping/deliverySelectionSummary.d.ts +2 -0
  306. package/dist/shipping/deliverySelectionSummary.js +53 -0
  307. package/dist/shipping/handlerSchemas.d.ts +21 -0
  308. package/dist/shipping/handlerSchemas.js +1 -0
  309. package/dist/shipping/index.d.ts +17 -0
  310. package/dist/shipping/index.js +8 -0
  311. package/dist/shipping/pickupPointSearchPort.d.ts +4 -0
  312. package/dist/shipping/pickupPointSearchPort.js +1 -0
  313. package/dist/shipping/readDeliverySelectionEvidence.d.ts +2 -0
  314. package/dist/shipping/readDeliverySelectionEvidence.js +44 -0
  315. package/dist/shipping/shippingRatePorts.d.ts +52 -0
  316. package/dist/shipping/shippingRatePorts.js +1 -0
  317. package/package.json +62 -2
  318. package/release-gates.json +218 -10
  319. package/sql/20260807140000_read_parity_catalog.sql +94 -0
  320. package/sql/20260810120000_storefront_catalog_attributes.sql +51 -0
  321. package/sql/20260812140000_sellable_bundle_catalog.sql +124 -0
  322. package/sql/20260812210000_bundle_operator_write_boundary.sql +567 -0
  323. package/sql/20260814000000_fulfillment_stock_evidence.sql +239 -0
  324. package/sql/20260815120000_automatic_subscription_renewal.sql +1083 -0
  325. package/sql/20260817180000_promotion_claim_lifecycle.sql +591 -0
  326. package/sql/20260817180100_promotion_claim_lifecycle_validation.sql +23 -0
  327. package/sql/20260827220000_catalog_document_revision_foundation.sql +226 -0
  328. package/sql/20260827220001_catalog_document_revision_foundation_validate.sql +10 -0
  329. package/sql/20260908100000_catalog_draft_foundation.sql +217 -0
  330. package/src/catalog/activeSkuSelection.ts +20 -0
  331. package/src/catalog/adminBindings.ts +59 -0
  332. package/src/catalog/adminBundleHandler.ts +165 -0
  333. package/src/catalog/adminBundleReadHandler.ts +261 -0
  334. package/src/catalog/adminBundleWritePort.ts +102 -0
  335. package/src/catalog/bundleAvailability.ts +129 -0
  336. package/src/catalog/bundleAvailabilityService.ts +171 -0
  337. package/src/catalog/bundleBindings.ts +29 -0
  338. package/src/catalog/bundleCatalogQueries.ts +98 -0
  339. package/src/catalog/bundleCatalogReadPort.ts +177 -0
  340. package/src/catalog/bundleCatalogStore.ts +269 -0
  341. package/src/catalog/bundleRuleCodes.ts +134 -0
  342. package/src/catalog/bundleRules.ts +153 -0
  343. package/src/catalog/catalogDraftStore.ts +71 -0
  344. package/src/catalog/catalogSkuEnvelope.ts +214 -0
  345. package/src/catalog/catalogSkuEnvelopeMapper.ts +160 -0
  346. package/src/catalog/contracts/bundleAdmin.ts +20 -0
  347. package/src/catalog/contracts/bundleAvailability.ts +5 -0
  348. package/src/catalog/contracts/bundleRead.ts +13 -0
  349. package/src/catalog/contracts/draft.ts +69 -0
  350. package/src/catalog/contracts/foundation.ts +104 -0
  351. package/src/catalog/contracts/index.ts +14 -0
  352. package/src/catalog/contracts/lifecycle.ts +74 -0
  353. package/src/catalog/contracts/productMediaContracts.ts +170 -0
  354. package/src/catalog/contracts/productTypes.ts +35 -0
  355. package/src/catalog/contracts/resolveProductMedia.ts +85 -0
  356. package/src/catalog/contracts/sellable.ts +12 -0
  357. package/src/catalog/draftHandler.ts +105 -0
  358. package/src/catalog/draftPorts.ts +18 -0
  359. package/src/catalog/draftSchemas.ts +88 -0
  360. package/src/catalog/draftValidation.ts +100 -0
  361. package/src/catalog/foundationSchemas.ts +123 -0
  362. package/src/catalog/index.ts +43 -0
  363. package/src/catalog/mediaSourcePack.ts +219 -0
  364. package/src/catalog/productTypeSchemas.ts +70 -0
  365. package/src/catalog/publicationPorts.ts +44 -0
  366. package/src/catalog/readPorts.ts +61 -0
  367. package/src/catalog/referenceCatalogHandler.ts +39 -0
  368. package/src/catalog/resolvedSkuProjection.ts +76 -0
  369. package/src/catalog/sellableBundleHandler.ts +133 -0
  370. package/src/catalog/sellableSchemas.ts +33 -0
  371. package/src/catalog/sql.ts +14 -0
  372. package/src/catalog/validation.ts +3 -0
  373. package/src/communications/contracts/index.ts +1 -0
  374. package/src/communications/contracts/marketingSync.ts +109 -0
  375. package/src/communications/index.ts +1 -0
  376. package/src/communications/klaviyo/consentPush.ts +19 -0
  377. package/src/communications/klaviyo/index.ts +649 -0
  378. package/src/communications/resend/index.ts +350 -0
  379. package/src/composition/catalogContribution.ts +32 -0
  380. package/src/composition/index.ts +1 -0
  381. package/src/inventory/adminInventory.ts +251 -0
  382. package/src/inventory/contracts/forecast.ts +96 -0
  383. package/src/inventory/contracts/index.ts +18 -0
  384. package/src/inventory/contracts/inventory.ts +14 -0
  385. package/src/inventory/contracts/newPurchaseStock.ts +23 -0
  386. package/src/inventory/contracts/reservation.ts +20 -0
  387. package/src/inventory/forecast.ts +239 -0
  388. package/src/inventory/index.ts +21 -0
  389. package/src/inventory/inventoryHandlers.ts +215 -0
  390. package/src/inventory/newPurchaseStockAvailability.ts +120 -0
  391. package/src/inventory/ports.ts +46 -0
  392. package/src/inventory/renewalReservation.ts +88 -0
  393. package/src/inventory/reservationPolicy.ts +27 -0
  394. package/src/inventory/schemas.ts +128 -0
  395. package/src/inventory/sql.ts +5 -0
  396. package/src/inventory/subscriptionForecastCalculation.ts +157 -0
  397. package/src/pricing/acquisitionFinancialPolicy.ts +26 -0
  398. package/src/pricing/acquisitionOfferPricing.ts +38 -0
  399. package/src/pricing/acquisitionOfferPricingHandler.ts +157 -0
  400. package/src/pricing/acquisitionOfferPricingSchemas.ts +35 -0
  401. package/src/pricing/acquisitionQuoteBinding.ts +12 -0
  402. package/src/pricing/acquisitionReadiness.ts +148 -0
  403. package/src/pricing/acquisitionTerms.ts +125 -0
  404. package/src/pricing/acquisitionTermsSchemas.ts +52 -0
  405. package/src/pricing/catalogFactsProvenance.ts +102 -0
  406. package/src/pricing/catalogFactsSchemas.ts +69 -0
  407. package/src/pricing/commerceOfferPricing.ts +224 -0
  408. package/src/pricing/commerceOfferPricingCache.ts +53 -0
  409. package/src/pricing/commerceOfferPricingHandler.ts +137 -0
  410. package/src/pricing/commercePriceAuthority.ts +104 -0
  411. package/src/pricing/commercePricingPolicyErrors.ts +88 -0
  412. package/src/pricing/commerceQuoteBatchHandler.ts +129 -0
  413. package/src/pricing/commerceQuoteCatalogReadPort.ts +42 -0
  414. package/src/pricing/commerceQuoteHandler.ts +184 -0
  415. package/src/pricing/commerceQuoteSnapshotExpect.ts +48 -0
  416. package/src/pricing/contracts/acquisitionTerms.ts +26 -0
  417. package/src/pricing/contracts/authority.ts +44 -0
  418. package/src/pricing/contracts/evidence.ts +25 -0
  419. package/src/pricing/contracts/index.ts +17 -0
  420. package/src/pricing/contracts/money.ts +32 -0
  421. package/src/pricing/contracts/offer.ts +42 -0
  422. package/src/pricing/contracts/offerPolicy.ts +17 -0
  423. package/src/pricing/contracts/policy.ts +26 -0
  424. package/src/pricing/contracts/provenance.ts +19 -0
  425. package/src/pricing/contracts/quote.ts +106 -0
  426. package/src/pricing/dbBackedCommerceQuoteHelpers.ts +256 -0
  427. package/src/pricing/dbBackedCommerceQuotePort.ts +260 -0
  428. package/src/pricing/dbBackedCommerceQuoteSnapshotVerifier.ts +49 -0
  429. package/src/pricing/index.ts +52 -0
  430. package/src/pricing/offerPolicyReadiness.ts +110 -0
  431. package/src/pricing/offerPolicySchemas.ts +23 -0
  432. package/src/pricing/offerPricingSchemas.ts +92 -0
  433. package/src/pricing/ports.ts +17 -0
  434. package/src/pricing/pricingPolicyToken.ts +119 -0
  435. package/src/pricing/quoteAcquisitionTerms.ts +10 -0
  436. package/src/pricing/quoteCodeRejectionDetails.ts +23 -0
  437. package/src/pricing/quotePorts.ts +58 -0
  438. package/src/pricing/quotePrimitives.ts +21 -0
  439. package/src/pricing/quotePromotionEvidence.ts +82 -0
  440. package/src/pricing/quoteSchemas.ts +230 -0
  441. package/src/pricing/subscriptionPricePolicy.ts +113 -0
  442. package/src/pricing/taxProfileGuard.ts +33 -0
  443. package/src/pricing/validation.ts +3 -0
  444. package/src/promotions/adminBindings.ts +53 -0
  445. package/src/promotions/adminPromotionCodesContracts.ts +244 -0
  446. package/src/promotions/adminPromotionCodesDataPort.ts +56 -0
  447. package/src/promotions/adminPromotionCodesHandler.ts +254 -0
  448. package/src/promotions/adminPromotionsContracts.ts +121 -0
  449. package/src/promotions/adminPromotionsDataPort.ts +48 -0
  450. package/src/promotions/adminPromotionsHandler.ts +72 -0
  451. package/src/promotions/contracts/index.ts +387 -0
  452. package/src/promotions/index.ts +22 -0
  453. package/src/promotions/promoDataPort.ts +65 -0
  454. package/src/promotions/promotionAcceptanceTelemetry.ts +73 -0
  455. package/src/promotions/promotionBenefitDefinition.ts +48 -0
  456. package/src/promotions/promotionClaimSweepPort.ts +23 -0
  457. package/src/promotions/promotionClaims.ts +70 -0
  458. package/src/promotions/promotionCodePreview.ts +183 -0
  459. package/src/promotions/promotionCodeQuotePort.ts +35 -0
  460. package/src/promotions/promotionQuoteAcceptance.ts +284 -0
  461. package/src/promotions/promotionQuoteBinding.ts +158 -0
  462. package/src/promotions/quoteAutomaticPromotions.ts +58 -0
  463. package/src/promotions/quotePromoDiscounts.ts +185 -0
  464. package/src/promotions/quotePromotionCodeAdjustments.ts +277 -0
  465. package/src/promotions/quotePromotionV2.ts +223 -0
  466. package/src/promotions/sql.ts +5 -0
  467. package/src/promotions/validation.ts +3 -0
  468. package/src/shipping/addressCanonLookupHandler.ts +114 -0
  469. package/src/shipping/addressCanonical.ts +31 -0
  470. package/src/shipping/addressPorts.ts +48 -0
  471. package/src/shipping/addressSchemas.ts +28 -0
  472. package/src/shipping/adminShippingRateHandler.ts +72 -0
  473. package/src/shipping/contracts/address.ts +71 -0
  474. package/src/shipping/contracts/delivery.ts +114 -0
  475. package/src/shipping/contracts/index.ts +18 -0
  476. package/src/shipping/contracts/shippingRate.ts +7 -0
  477. package/src/shipping/customerAddressesHandler.ts +100 -0
  478. package/src/shipping/customerSelfServiceAddressModels.ts +124 -0
  479. package/src/shipping/deliverySelectionHandler.ts +94 -0
  480. package/src/shipping/deliverySelectionPort.ts +10 -0
  481. package/src/shipping/deliverySelectionSummary.ts +56 -0
  482. package/src/shipping/handlerSchemas.ts +19 -0
  483. package/src/shipping/index.ts +23 -0
  484. package/src/shipping/pickupPointSearchPort.ts +8 -0
  485. package/src/shipping/readDeliverySelectionEvidence.ts +51 -0
  486. package/src/shipping/shippingRatePorts.ts +36 -0
@@ -0,0 +1,61 @@
1
+ import type { SellableCatalogItem, SellableCatalogProfile, CatalogSkuEnvelopeSelector, NeutralSkuEnvelopeV1 } from "./contracts/index.js";
2
+
3
+ /**
4
+ * Minimal neutral catalog seam used by the local reference storefront. It is
5
+ * independent of richer application catalog read ports.
6
+ */
7
+ export interface SellableCatalogPort {
8
+ getStorefrontProfile(): Promise<SellableCatalogProfile>;
9
+ listSellableItems(): Promise<SellableCatalogItem[]>;
10
+ }
11
+
12
+ /**
13
+ * Dark W2a read seam. Its row-safe return value deliberately contains document
14
+ * references, never a document payload or a SKU-level document override.
15
+ */
16
+ export interface CatalogSkuEnvelopeReadPort {
17
+ listSkuEnvelopes(query: CatalogSkuEnvelopeListQuery): Promise<CatalogSkuEnvelopePage>;
18
+ /**
19
+ * Page only active trade items that are sellable in at least one commerce
20
+ * channel. This is intentionally distinct from the neutral inventory-style
21
+ * listing above: consumers such as compatibility must not discover dormant
22
+ * SKUs and then try to filter them with a second authority.
23
+ */
24
+ listActiveSkuEnvelopes(query: CatalogActiveSkuEnvelopeListQuery): Promise<CatalogSkuEnvelopePage>;
25
+ getSkuEnvelope(selector: CatalogSkuEnvelopeSelector): Promise<NeutralSkuEnvelopeV1 | null>;
26
+ /** Compatibility lookup for callers that already persist the neutral SKU UUID. */
27
+ getSkuEnvelopeById(skuId: string): Promise<NeutralSkuEnvelopeV1 | null>;
28
+ }
29
+
30
+ /**
31
+ * Additive discovery capability. Existing commerce consumers remain typed to
32
+ * {@link CatalogSkuEnvelopeReadPort} and therefore cannot accidentally use it.
33
+ */
34
+ export interface PublicCatalogSkuEnvelopeReadPort extends CatalogSkuEnvelopeReadPort {
35
+ /**
36
+ * Public discovery's active-only page. Unlike the commerce method above it
37
+ * intentionally includes temporarily unsellable active variants.
38
+ */
39
+ listPublicActiveSkuEnvelopes(query: CatalogSkuEnvelopeListQuery): Promise<CatalogSkuEnvelopePage>;
40
+ /** Active-only cursor page restricted to one already-authoritative product. */
41
+ listPublicActiveProductSkuEnvelopes(
42
+ productId: string,
43
+ query: CatalogSkuEnvelopeListQuery,
44
+ ): Promise<CatalogSkuEnvelopePage>;
45
+ }
46
+
47
+ export interface CatalogSkuEnvelopeListQuery {
48
+ cursor: string | null;
49
+ limit: number;
50
+ }
51
+
52
+ export interface CatalogActiveSkuEnvelopeListQuery extends CatalogSkuEnvelopeListQuery {
53
+ /** Optional server-selected scope; supplied selections contain 1–100 unique SKU codes. */
54
+ skuCodes?: readonly string[];
55
+ }
56
+
57
+ export interface CatalogSkuEnvelopePage {
58
+ items: NeutralSkuEnvelopeV1[];
59
+ nextCursor: string | null;
60
+ }
61
+
@@ -0,0 +1,39 @@
1
+ import type { HttpRequest, HttpResponse } from "../http/index.js";
2
+ import type { BffResponder } from "../bff/index.js";
3
+ import {
4
+ SELLABLE_CATALOG_CONTRACT_VERSION,
5
+ sellableCatalogListResponseSchema,
6
+ } from "./sellableSchemas.js";
7
+ import type { SellableCatalogPort } from "./readPorts.js";
8
+
9
+ export function createReferenceCatalogItemsHandler({
10
+ catalogPort, responder,
11
+ }: {
12
+ catalogPort: SellableCatalogPort;
13
+ responder: BffResponder;
14
+ }) {
15
+ const { sendBffError, sendBffSuccess, sendMethodNotAllowed } = responder;
16
+ return async function handler(req: HttpRequest, res: HttpResponse): Promise<void> {
17
+ if (req.method !== "GET") {
18
+ sendMethodNotAllowed(res, ["GET"]);
19
+ return;
20
+ }
21
+
22
+ try {
23
+ const response = sellableCatalogListResponseSchema.safeParse({
24
+ contractVersion: SELLABLE_CATALOG_CONTRACT_VERSION,
25
+ profile: await catalogPort.getStorefrontProfile(),
26
+ items: await catalogPort.listSellableItems(),
27
+ });
28
+ if (!response.success) {
29
+ sendBffError(res, "INVALID_RESPONSE", "Reference catalog returned invalid items");
30
+ return;
31
+ }
32
+ sendBffSuccess(res, response.data, { contractVersion: SELLABLE_CATALOG_CONTRACT_VERSION }, {
33
+ cacheControl: "no-store",
34
+ });
35
+ } catch {
36
+ sendBffError(res, "UPSTREAM_UNAVAILABLE", "Reference catalog is unavailable");
37
+ }
38
+ };
39
+ }
@@ -0,0 +1,76 @@
1
+ import { z } from "./validation.js";
2
+ import {
3
+ CatalogSkuAuthorityError,
4
+ assertPrimaryUnitGtinAuthority,
5
+ neutralSkuEnvelopeV1Schema,
6
+ type NeutralSkuEnvelopeV1,
7
+ } from "./foundationSchemas.js";
8
+
9
+ const uuidSchema = z.guid();
10
+ const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
11
+ const currencySchema = z.string().regex(/^[A-Z]{3}$/u);
12
+ const moneyMinorSchema = z.number().int().nonnegative();
13
+
14
+ /**
15
+ * Server-only, read-safe projection. It intentionally composes opaque document
16
+ * references with resolved prices and never selects or exposes document payload.
17
+ */
18
+ export const RESOLVED_SKU_PROJECTION_VERSION = "catalog.resolved-sku.v1";
19
+
20
+ export const resolvedSkuProjectionSchema = z.object({
21
+ contractVersion: z.literal(RESOLVED_SKU_PROJECTION_VERSION),
22
+ envelope: neutralSkuEnvelopeV1Schema,
23
+ priceContext: z.object({
24
+ priceListId: uuidSchema,
25
+ regionCode: z.string().regex(/^[A-Z]{2,3}$/u),
26
+ currency: currencySchema,
27
+ channel: z.string().trim().min(1).max(64),
28
+ atTime: z.string().datetime({ offset: true }),
29
+ }).strict(),
30
+ pricing: z.object({
31
+ base: z.object({
32
+ priceEntryId: uuidSchema,
33
+ amountMinor: moneyMinorSchema,
34
+ }).strict(),
35
+ subscription: z.object({
36
+ policyRevisionId: uuidSchema,
37
+ policyDigest: digestSchema,
38
+ amountMinor: moneyMinorSchema,
39
+ }).strict().nullable(),
40
+ }).strict(),
41
+ }).strict().superRefine((projection, context) => {
42
+ try {
43
+ assertPrimaryUnitGtinAuthority(projection.envelope.identifiers);
44
+ } catch (error) {
45
+ if (!(error instanceof CatalogSkuAuthorityError)) throw error;
46
+ context.addIssue({
47
+ code: z.ZodIssueCode.custom,
48
+ path: ["envelope", "identifiers"],
49
+ message: error.code,
50
+ });
51
+ }
52
+ const subscriptionSellable = projection.envelope.sku.sellability.subscription;
53
+ if (subscriptionSellable && projection.pricing.subscription === null) {
54
+ context.addIssue({ code: z.ZodIssueCode.custom, path: ["pricing", "subscription"], message: "subscription-sellable SKUs require a subscription price" });
55
+ }
56
+ if (!subscriptionSellable && projection.pricing.subscription !== null) {
57
+ context.addIssue({ code: z.ZodIssueCode.custom, path: ["pricing", "subscription"], message: "one-time-only SKUs must not carry a subscription price" });
58
+ }
59
+ });
60
+
61
+ export type ResolvedSkuProjection = z.infer<typeof resolvedSkuProjectionSchema>;
62
+
63
+ /** Row-safe constructor boundary for adapters; it accepts no raw document row/payload. */
64
+ export interface ResolvedSkuProjectionInput {
65
+ envelope: NeutralSkuEnvelopeV1;
66
+ priceContext: ResolvedSkuProjection["priceContext"];
67
+ pricing: ResolvedSkuProjection["pricing"];
68
+ }
69
+
70
+ export function buildResolvedSkuProjection(input: ResolvedSkuProjectionInput): ResolvedSkuProjection {
71
+ assertPrimaryUnitGtinAuthority(input.envelope.identifiers);
72
+ return resolvedSkuProjectionSchema.parse({
73
+ contractVersion: RESOLVED_SKU_PROJECTION_VERSION,
74
+ ...input,
75
+ });
76
+ }
@@ -0,0 +1,133 @@
1
+ import { allocateBundleTargetPrice } from "@openlup/core/pricing";
2
+ import type { HttpRequest, HttpResponse } from "../http/index.js";
3
+ import type { BffResponder } from "../bff/index.js";
4
+ import type { BundleResponseSchema } from "./adminBindings.js";
5
+ import type { SellableBundle } from "./contracts/bundleAvailability.js";
6
+ import type { BundleAvailability } from "./bundleAvailability.js";
7
+ import type { ActiveBundleComposition } from "./bundleCatalogReadPort.js";
8
+ import type { BundleAvailabilityService } from "./bundleAvailabilityService.js";
9
+
10
+
11
+ /**
12
+ * The public sellable-bundle feed handler, shaped after the reference catalog
13
+ * handler: GET-only, one
14
+ * port call, validate the body before sending, and never serve an unvalidated
15
+ * shape.
16
+ *
17
+ * It composes rather than computes twice: the availability service already folds
18
+ * one batched stock answer into the live compositions, and the pricing kernel
19
+ * turns the stored target price into per-component money. This module only decides
20
+ * what a storefront is allowed to see.
21
+ *
22
+ * A bundle whose money cannot be derived is DROPPED from the feed, not published
23
+ * with a guessed figure. Three cases reach that door — an unpriced component, an
24
+ * empty composition, and a target price the kernel refuses — and all three mean
25
+ * the same thing to a storefront: this is not sellable today. Publishing it with a
26
+ * partial price would put a number on a shelf that no order line could reproduce.
27
+ */
28
+
29
+ export interface SellableBundleHandlerDeps {
30
+ availabilityService: BundleAvailabilityService;
31
+ /** Narrow the feed to one currency; omitted means every currency an operator priced. */
32
+ currency?: string;
33
+ enabled: boolean;
34
+ }
35
+
36
+ export function createSellableBundleHandlers({ responder, sellableBundleListResponseSchema, SELLABLE_BUNDLE_CONTRACT_VERSION }: { responder: BffResponder; sellableBundleListResponseSchema: BundleResponseSchema; SELLABLE_BUNDLE_CONTRACT_VERSION: "catalog.sellable-bundle.v1" }) {
37
+ const { sendBffError, sendBffSuccess, sendMethodNotAllowed } = responder;
38
+ function createSellableBundleHandler(deps: SellableBundleHandlerDeps) {
39
+ return async function handler(req: HttpRequest, res: HttpResponse): Promise<void> {
40
+ if (req.method !== "GET") {
41
+ sendMethodNotAllowed(res, ["GET"]);
42
+ return;
43
+ }
44
+ if (!deps.enabled) {
45
+ sendBffError(res, "UPSTREAM_UNAVAILABLE", "Bundle reads are disabled", {
46
+ details: { reason: "feature_flag_disabled", featureFlag: "COMMERCE_BUNDLE_READ_ENABLED" },
47
+ });
48
+ return;
49
+ }
50
+
51
+ try {
52
+ const rows = await deps.availabilityService.describeActiveBundles(
53
+ deps.currency ? { currency: deps.currency } : {},
54
+ );
55
+ const response = sellableBundleListResponseSchema.safeParse({
56
+ contractVersion: SELLABLE_BUNDLE_CONTRACT_VERSION,
57
+ bundles: rows
58
+ .map((row) => toSellableBundle(row.composition, row.availability))
59
+ .filter((bundle): bundle is SellableBundle => bundle !== null),
60
+ });
61
+ if (!response.success) {
62
+ sendBffError(res, "INVALID_RESPONSE", "Sellable bundle feed returned an invalid shape");
63
+ return;
64
+ }
65
+ sendBffSuccess(
66
+ res,
67
+ response.data,
68
+ { contractVersion: SELLABLE_BUNDLE_CONTRACT_VERSION },
69
+ { cacheControl: "no-store" },
70
+ );
71
+ } catch {
72
+ sendBffError(res, "UPSTREAM_UNAVAILABLE", "Sellable bundles are unavailable");
73
+ }
74
+ };
75
+ }
76
+
77
+ return { createSellableBundleHandler };
78
+ }
79
+
80
+ /**
81
+ * Derive the row a storefront sees, or `null` when the money cannot be stated.
82
+ *
83
+ * The allocator emits TWO lines for a quantity whose subtotal does not divide
84
+ * evenly, which is exactly why the feed carries lines rather than a unit price per
85
+ * component: `unitPrice x quantity === lineSubtotal` holds on every line, and the
86
+ * lines sum to the bundle's price with no remainder.
87
+ */
88
+ export function toSellableBundle(
89
+ composition: ActiveBundleComposition,
90
+ availability: BundleAvailability,
91
+ ): SellableBundle | null {
92
+ if (composition.components.length === 0) return null;
93
+ if (composition.components.some((component) => component.referenceUnitPriceMinor === null)) {
94
+ return null;
95
+ }
96
+
97
+ const allocation = allocateBundleTargetPrice({
98
+ components: composition.components.map((component) => ({
99
+ sku: component.sku,
100
+ quantity: component.quantity,
101
+ unitPriceMinor: component.referenceUnitPriceMinor as number,
102
+ })),
103
+ targetPriceMinor: composition.targetPriceMinor,
104
+ });
105
+ if (!allocation.ok) return null;
106
+
107
+ // Basis points come from the per-component allocation, so a component split
108
+ // across two lines reports the same discount on both halves rather than two
109
+ // rounded figures that disagree.
110
+ const discountBpsBySku = new Map(
111
+ allocation.components.map((component) => [component.sku, component.discountBps]),
112
+ );
113
+
114
+ return {
115
+ code: composition.code,
116
+ title: composition.title,
117
+ fulfillmentMode: composition.fulfillmentMode,
118
+ price: { amountMinor: composition.targetPriceMinor, currency: composition.currency },
119
+ components: allocation.lines.map((line) => ({
120
+ sku: line.sku,
121
+ quantity: line.quantity,
122
+ effectiveUnitPriceMinor: line.effectiveUnitPriceMinor,
123
+ lineSubtotalMinor: line.lineSubtotalMinor,
124
+ discountBps: discountBpsBySku.get(line.sku) ?? 0,
125
+ })),
126
+ availability: {
127
+ status: availability.status,
128
+ sellableNow: availability.sellableNow,
129
+ limitingSku: availability.limitingSku,
130
+ reasonCode: availability.reasonCode,
131
+ },
132
+ };
133
+ }
@@ -0,0 +1,33 @@
1
+ import { z } from "./validation.js";
2
+ import { SELLABLE_CATALOG_CONTRACT_VERSION } from "./contracts/sellable.js";
3
+ export { SELLABLE_CATALOG_CONTRACT_VERSION } from "./contracts/sellable.js";
4
+ export type { SellableCatalogItem, SellableCatalogProfile, SellableCatalogListResponse } from "./contracts/sellable.js";
5
+
6
+ /** Minimal neutral storefront wire schema. */
7
+ const sellableCatalogMoneySchema = z.object({
8
+ amountMinor: z.number().int().nonnegative(),
9
+ currency: z.string().regex(/^[A-Z]{3}$/),
10
+ }).strict();
11
+
12
+ export const sellableCatalogItemSchema = z.object({
13
+ sku: z.string().trim().min(1).max(128).regex(/^[a-zA-Z0-9._:-]+$/),
14
+ title: z.string().trim().min(1).max(160),
15
+ unitPrice: sellableCatalogMoneySchema,
16
+ permittedPurchaseModes: z.array(z.enum(["one_time", "subscription"])).min(1).max(2),
17
+ }).strict();
18
+
19
+ export const sellableCatalogProfileSchema = z.object({
20
+ id: z.string().trim().min(1).max(120),
21
+ brand: z.string().trim().min(1).max(160),
22
+ country: z.string().regex(/^[A-Z]{2}$/),
23
+ currency: z.string().regex(/^[A-Z]{3}$/),
24
+ locale: z.string().trim().min(2).max(35),
25
+ timezone: z.string().trim().min(1).max(80),
26
+ }).strict();
27
+
28
+ export const sellableCatalogListResponseSchema = z.object({
29
+ contractVersion: z.literal(SELLABLE_CATALOG_CONTRACT_VERSION),
30
+ profile: sellableCatalogProfileSchema,
31
+ items: z.array(sellableCatalogItemSchema).min(1),
32
+ }).strict();
33
+
@@ -0,0 +1,14 @@
1
+ /** Sparse projections of canonical units, never an install chain. */
2
+ export const BUNDLE_MIGRATIONS = [
3
+ { id: "20260812140000_sellable_bundle_catalog", catalogPath: "db/platform/migrations/20260812140000_sellable_bundle_catalog.sql", position: 16, sha256: "62fbf0910a3a7a2ee47e2d798933fe0a5a273fd1242cae7cc78ff237ba39e394", url: new URL("../../sql/20260812140000_sellable_bundle_catalog.sql", import.meta.url) },
4
+ { id: "20260812210000_bundle_operator_write_boundary", catalogPath: "db/platform/migrations/20260812210000_bundle_operator_write_boundary.sql", position: 19, sha256: "d0ec6674d7eedf4cd3ce0d8ccd7345ebe2f93da635316a68a6bd178ec55b2679", url: new URL("../../sql/20260812210000_bundle_operator_write_boundary.sql", import.meta.url) },
5
+ ] as const;
6
+
7
+ /** Catalog foundation projections; install the complete canonical catalogue. */
8
+ export const CATALOG_MIGRATIONS = [
9
+ { id: "20260807140000_read_parity_catalog", catalogPath: "db/platform/migrations/20260807140000_read_parity_catalog.sql", position: 1, sha256: "fcf3827028c1651ae42a7c7d7472153825a3121c936d58a3d105befc7dece9ea", url: new URL("../../sql/20260807140000_read_parity_catalog.sql", import.meta.url) },
10
+ { id: "20260810120000_storefront_catalog_attributes", catalogPath: "db/platform/migrations/20260810120000_storefront_catalog_attributes.sql", position: 8, sha256: "2bf938f5d0076193557bea834f86dbd9bcb7f3e2de00f99fb91cc1229b93a14b", url: new URL("../../sql/20260810120000_storefront_catalog_attributes.sql", import.meta.url) },
11
+ { id: "20260827220000_catalog_document_revision_foundation", catalogPath: "db/platform/migrations/20260827220000_catalog_document_revision_foundation.sql", position: 58, sha256: "715e74d655b76d35b8a49e0d2a5625594a6c4856863f369cd1eadcd1fb1ae90e", url: new URL("../../sql/20260827220000_catalog_document_revision_foundation.sql", import.meta.url) },
12
+ { id: "20260827220001_catalog_document_revision_foundation_validate", catalogPath: "db/platform/migrations/20260827220001_catalog_document_revision_foundation_validate.sql", position: 59, sha256: "f3cd7fb61dcebac1cf0468cf2d7c1f9dd036817ab0094c72864266e307b269de", url: new URL("../../sql/20260827220001_catalog_document_revision_foundation_validate.sql", import.meta.url) },
13
+ { id: "20260908100000_catalog_draft_foundation", catalogPath: "db/platform/migrations/20260908100000_catalog_draft_foundation.sql", position: 61, sha256: "ac0d614cdb2874df8fbdbbbda4f9455986ef95241fe1334b0b54a1fa6478ab2e", url: new URL("../../sql/20260908100000_catalog_draft_foundation.sql", import.meta.url) },
14
+ ] as const;
@@ -0,0 +1,3 @@
1
+ import { z } from "zod";
2
+ z.config({ jitless: true });
3
+ export { z };
@@ -49,3 +49,4 @@ export * from "./invoiceDocumentContent.js";
49
49
  export * from "./roleNotificationContent.js";
50
50
  export * from "./inquiryContent.js";
51
51
  export * from "./optOutContent.js";
52
+ export type * from "./marketingSync.js";
@@ -0,0 +1,109 @@
1
+ // Private helpers/symbols, never exported. Brands are not serialization authority.
2
+ declare const subjectBrand: unique symbol, feedbackBrand: unique symbol,
3
+ receiptBrand: unique symbol, readBrand: unique symbol;
4
+ type Operation = "profile.sync" | "consent.read" | "erasure.request" | "erasure.read";
5
+ type Mutation = "profile.sync" | "erasure.request";
6
+ type SubjectReference = string & { readonly [subjectBrand]: "opaque-subject" };
7
+ export type SyncBinding = Readonly<{ account: string; environment: string; namespace: string; mappingVersion: string }>;
8
+ export type SyncAudience = Readonly<{ kind: "account-wide" } | { kind: "audience"; reference: string }>;
9
+ export type ProfileProjection = Readonly<{
10
+ email: string; externalReference: string;
11
+ fields: Readonly<Partial<Record<"givenName" | "familyName" | "locale", string>>>;
12
+ properties: Readonly<Record<string, string | number | boolean>>;
13
+ }>;
14
+ type Payload = {
15
+ "profile.sync": { projection: ProfileProjection; consentEvidenceReference: string };
16
+ "consent.read": Readonly<Record<string, never>>;
17
+ "erasure.request": { caseReference: string; authorityReference: string };
18
+ "erasure.read": { receipt: SyncReceipt<"erasure.request"> };
19
+ };
20
+ export type SyncRequest<O extends Operation = Operation> = { [K in O]: Readonly<{
21
+ binding: SyncBinding; audience: SyncAudience; subjectReference: SubjectReference;
22
+ requestId: string; generation: number; deadlineMs: number; authorizationReference: string;
23
+ operation: K; payload: Readonly<Payload[K]>;
24
+ }> }[O];
25
+ export type NegativeFeedback = Readonly<{
26
+ [feedbackBrand]: "provider-negative"; sourceReference: string; occurredAtMs: number | null; audience: SyncAudience;
27
+ }> & Readonly<
28
+ | { kind: "unsubscribe" | "complaint" | "manual_block"; outcome: "deny_permission" }
29
+ | { kind: "hard_bounce" | "invalid_address"; outcome: "deny_delivery" }
30
+ >;
31
+ export type ConsentReadback = Readonly<{
32
+ audience: SyncAudience; observedAtMs: number;
33
+ remoteConsent: "subscribed" | "unsubscribed" | "pending" | "never_subscribed" | "unknown";
34
+ feedback: readonly NegativeFeedback[];
35
+ // Coverage describes this current-state observation only; never historical completeness.
36
+ coverage: "complete" | "partial" | "unsupported";
37
+ }>;
38
+ export type SyncFailure<O extends Operation = Operation> =
39
+ | { kind: "refused"; reason: "invalid_request" | "binding" | "provenance" | "stale" | "deadline" |
40
+ "unsupported" | "verification_pending" | "restricted" | "identity_conflict" | "provider_refusal" }
41
+ | { kind: "retryable"; reason: "cooling" | "host" | "provider"; afterSeconds: number }
42
+ | ({ kind: "unknown"; reason: "dispatch_unresolved" | "acceptance_ambiguous" | "record_failed" } &
43
+ (O extends Mutation ? { receipt?: SyncReceipt<O> } : { receipt?: never }));
44
+ // Receipt identifies the original admitted mutation; status lives only in the result union.
45
+ export type SyncReceipt<O extends Mutation> = Readonly<{
46
+ [receiptBrand]: O; operation: O; request: SyncRequest<O>;
47
+ }>;
48
+ type MutationSuccess<O extends Mutation> =
49
+ { kind: "accepted" | "acknowledged"; receipt: SyncReceipt<O>; journal: "confirmed" | "unknown" };
50
+ type Success = {
51
+ "profile.sync": MutationSuccess<"profile.sync">;
52
+ "consent.read": { kind: "completed"; value: ConsentReadback; journal: "confirmed" | "unknown" };
53
+ "erasure.request": MutationSuccess<"erasure.request">;
54
+ "erasure.read": MutationSuccess<"erasure.request"> | {
55
+ kind: "completed"; receipt: SyncReceipt<"erasure.request">; journal: "confirmed" | "unknown";
56
+ evidence: Readonly<{ [readBrand]: "provider-read"; readRequestId: string; observedAtMs: number;
57
+ evidenceReference: string; assurance: "provider_record_deleted" | "provider_erasure_confirmed" }>;
58
+ };
59
+ };
60
+ export type SyncResult<O extends Operation = Operation> = { [K in O]:
61
+ Readonly<Pick<SyncRequest<K>, "binding" | "operation" | "subjectReference" | "requestId" | "generation">> &
62
+ Readonly<Success[K] | SyncFailure<K>>
63
+ }[O];
64
+ export type SyncCapabilities = Readonly<{ binding: SyncBinding; audience: SyncAudience; operations: Readonly<Record<Operation,
65
+ { state: "supported"; evidenceReference: string; maxRequestBytes: number; maxFeedbackItems: number } |
66
+ { state: "unsupported" | "verification_pending" }>> }>;
67
+ export interface ProfileSyncPort {
68
+ sync(request: SyncRequest<"profile.sync">): Promise<SyncResult<"profile.sync">>;
69
+ }
70
+ export interface ConsentReadPort {
71
+ read(request: SyncRequest<"consent.read">): Promise<SyncResult<"consent.read">>;
72
+ }
73
+ export interface ErasurePort {
74
+ request(request: SyncRequest<"erasure.request">): Promise<SyncResult<"erasure.request">>;
75
+ read(request: SyncRequest<"erasure.read">): Promise<SyncResult<"erasure.read">>;
76
+ }
77
+ export interface MarketingSyncProvider {
78
+ readonly capabilities: SyncCapabilities;
79
+ readonly profiles: ProfileSyncPort; readonly consent: ConsentReadPort; readonly erasure: ErasurePort;
80
+ }
81
+
82
+ export type ConsentPushRequest = Readonly<
83
+ Omit<SyncRequest<"profile.sync">, "operation" | "audience" | "payload"> & {
84
+ operation: "consent.push";
85
+ } & (
86
+ { audience: Extract<SyncAudience, { kind: "audience" }>;
87
+ payload: Readonly<{ action: "grant"; consentEvidenceReference: string }> } |
88
+ { audience: SyncAudience;
89
+ payload: Readonly<{ action: "withdraw"; consentEvidenceReference: string }> }
90
+ )>;
91
+ declare const pushReceiptBrand: unique symbol; // private, same receipt style
92
+ type PushReceipt = Readonly<{ [pushReceiptBrand]: "consent.push";
93
+ operation: "consent.push"; request: ConsentPushRequest }>;
94
+ type PushFailure = Exclude<SyncFailure, { kind: "unknown" }> |
95
+ (Omit<Extract<SyncFailure, { kind: "unknown" }>, "receipt"> & { receipt?: PushReceipt });
96
+ export type ConsentPushResult = Readonly<Pick<ConsentPushRequest,
97
+ "binding" | "operation" | "subjectReference" | "requestId" | "generation">> & Readonly<
98
+ { kind: "accepted" | "acknowledged"; receipt: PushReceipt;
99
+ journal: "confirmed" | "unknown"; reconcileRequired: boolean } | PushFailure>;
100
+ export interface ConsentPushPort {
101
+ readonly capabilities: SyncCapabilities & Readonly<{
102
+ operations: SyncCapabilities["operations"] & Readonly<{
103
+ "consent.push": SyncCapabilities["operations"]["profile.sync"] & Readonly<{
104
+ grantEffect: "audience" | "account-wide";
105
+ }>;
106
+ }>;
107
+ }>;
108
+ push(request: ConsentPushRequest): Promise<ConsentPushResult>;
109
+ }
@@ -44,3 +44,4 @@ export * from "./postgresDeliveryReconciliation.js";
44
44
  export * from "./postgresCommunicationSync.js";
45
45
  export * from "./postgresNotificationRecipients.js";
46
46
  export * from "./postgresConsentEvaluator.js";
47
+ export type * from "./contracts/marketingSync.js";
@@ -0,0 +1,19 @@
1
+ import type { ConsentPushRequest } from "../contracts/marketingSync.js";
2
+
3
+ /** Private singleton translation. Local evidence is deliberately absent from wire attributes. */
4
+ export function consentCommand(request: ConsentPushRequest, target: { id: string; email: string }, listId: string | undefined) {
5
+ if (request.payload.action === "withdraw" && request.audience.kind === "audience") {
6
+ if (!listId) throw new Error("scope");
7
+ return { method: "DELETE", path: `/api/lists/${listId}/relationships/profiles`, expected: [204],
8
+ body: { data: [{ type: "profile", id: target.id }] } };
9
+ }
10
+ const grant = request.payload.action === "grant";
11
+ const type = grant ? "profile-subscription-bulk-create-job" : "profile-subscription-bulk-delete-job";
12
+ if (grant && !listId) throw new Error("scope");
13
+ return { method: "POST", path: `/api/${type}s`, expected: [202], body: { data: { type,
14
+ attributes: { profiles: { data: [{ type: "profile", ...(grant ? { id: target.id } : {}), attributes: {
15
+ email: target.email, subscriptions: { email: { marketing: { consent: grant ? "SUBSCRIBED" : "UNSUBSCRIBED" } } },
16
+ } }] } },
17
+ ...(grant ? { relationships: { list: { data: { type: "list", id: listId } } } } : {}),
18
+ } } };
19
+ }