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