@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,14 @@
1
+ export { CATALOG_DRAFT_MAX_BYTES, CATALOG_DRAFT_MAX_PAGE, canonicalCatalogDraftJson } from "./draft.js";
2
+ export type { CatalogDraftPayload, CatalogDraftCommand, CatalogDraftSummary, CatalogDraftRecord, CatalogDraftPage, CatalogDraftRefusal, CatalogDraftApplyResult, CatalogDraftGetQuery, CatalogDraftListQuery } from "./draft.js";
3
+ export { CATALOG_FOUNDATION_CONTRACT_VERSION, CatalogSkuAuthorityError, isValidGtin, assertPrimaryUnitGtinAuthority, requireCurrentCatalogDocumentAuthority } from "./foundation.js";
4
+ export type { CatalogDocumentRevisionRef, CatalogSkuIdentifier, CatalogSkuEnvelopeSelector, NeutralSkuEnvelopeV1, CatalogPriceContext, CatalogSkuAuthorityRefusal } from "./foundation.js";
5
+ export { validateCatalogQuantity, canonicalCatalogOption } from "./productTypes.js";
6
+ export type { CatalogQuantityDimension, CatalogQuantity, CatalogOptionValue, CatalogDraftIssue, CatalogProductTypeRef, CatalogProductTypeDefinition, CatalogProductTypeRegistry } from "./productTypes.js";
7
+ export { SELLABLE_CATALOG_CONTRACT_VERSION } from "./sellable.js";
8
+ export type { SellableCatalogItem, SellableCatalogProfile, SellableCatalogListResponse } from "./sellable.js";
9
+ export * from "./productMediaContracts.js";
10
+ export { resolveProductMedia } from "./resolveProductMedia.js";
11
+ export type { CatalogVariantState, CatalogHeadRef, CatalogLifecycleObservation, CatalogPublishCommand, CatalogPublicationReceipt, CatalogPublicationRefusal, CatalogPublicationReadResult, CatalogPublicationReceiptResult, CatalogHistoryResult } from "./lifecycle.js";
12
+ export type { CreateBundleDraftRequest, CreateBundleDraftResponse, UpdateBundleDraftRequest, UpdateBundleDraftResponse, BundleComponent, SetBundleCompositionRequest, SetBundleCompositionResponse, SetBundleTargetPriceRequest, SetBundleTargetPriceResponse, ArchiveBundleRequest, ArchiveBundleResponse, RestoreBundleRequest, RestoreBundleResponse, CloneBundleDraftRequest, CloneBundleDraftResponse, ActivateBundleRequest, ActivateBundleResponse, DeactivateBundleRequest, DeactivateBundleResponse } from "./bundleAdmin.js";
13
+ export type { ListBundlesRequest, ListBundlesResponse, BundleSummaryView, GetBundleRequest, GetBundleResponse, BundleDetailView, BundleAvailabilityView, BundleHistoryRequest, BundleHistoryResponse, PreviewBundlePriceRequest, PreviewBundlePriceResponse, BundlePricePreview } from "./bundleRead.js";
14
+ export type { SellableBundle, SellableBundleComponent, SellableBundleAvailability, SellableBundleListResponse } from "./bundleAvailability.js";
@@ -0,0 +1,6 @@
1
+ export { CATALOG_DRAFT_MAX_BYTES, CATALOG_DRAFT_MAX_PAGE, canonicalCatalogDraftJson } from "./draft.js";
2
+ export { CATALOG_FOUNDATION_CONTRACT_VERSION, CatalogSkuAuthorityError, isValidGtin, assertPrimaryUnitGtinAuthority, requireCurrentCatalogDocumentAuthority } from "./foundation.js";
3
+ export { validateCatalogQuantity, canonicalCatalogOption } from "./productTypes.js";
4
+ export { SELLABLE_CATALOG_CONTRACT_VERSION } from "./sellable.js";
5
+ export * from "./productMediaContracts.js";
6
+ export { resolveProductMedia } from "./resolveProductMedia.js";
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Lifecycle observations are additive DTOs, not changes to existing wire status.
3
+ * SKU draft/active/archived and draft receipt open/abandoned remain unchanged.
4
+ * Map archived to withdrawn only where an authority establishes that meaning;
5
+ * abandoning a draft never implies withdrawing the active variant.
6
+ */
7
+ export type CatalogVariantState = "draft" | "active" | "withdrawn";
8
+ export interface CatalogHeadRef {
9
+ publicationId: string;
10
+ revision: number;
11
+ documentDigest: string;
12
+ }
13
+ export interface CatalogLifecycleObservation {
14
+ productId: string;
15
+ variantId: string;
16
+ sku: string;
17
+ state: CatalogVariantState;
18
+ variantRevision: number;
19
+ /** Null means known unpublished, never unknown publication lineage. */
20
+ publicationHead: CatalogHeadRef | null;
21
+ draftRef: {
22
+ draftId: string;
23
+ revision: number;
24
+ } | null;
25
+ }
26
+ export interface CatalogPublishCommand {
27
+ commandKey: string;
28
+ productId: string;
29
+ candidateRef: string;
30
+ candidateDigest: string;
31
+ expectedHead: CatalogHeadRef | null;
32
+ changes: readonly {
33
+ variantId: string;
34
+ expectedRevision: number;
35
+ state: "active" | "withdrawn";
36
+ }[];
37
+ }
38
+ export interface CatalogPublicationReceipt {
39
+ commandKey: string;
40
+ fingerprint: string;
41
+ actorId: string;
42
+ previousHead: CatalogHeadRef | null;
43
+ head: CatalogHeadRef;
44
+ changes: readonly CatalogLifecycleObservation[];
45
+ }
46
+ /** Named refusals contain no arbitrary provider diagnostics. */
47
+ export interface CatalogPublicationRefusal {
48
+ outcome: "unauthorized" | "validation_issue" | "conflict" | "dependency_unavailable" | "unsupported";
49
+ reason: "publisher_not_authorized" | "candidate_invalid" | "head_conflict" | "publication_authority_unavailable" | "publication_not_supported";
50
+ }
51
+ /** Success requires coherent authoritative document and lifecycle facts. */
52
+ export type CatalogPublicationReadResult = {
53
+ outcome: "found";
54
+ head: CatalogHeadRef;
55
+ documentRef: string;
56
+ observations: readonly CatalogLifecycleObservation[];
57
+ } | {
58
+ outcome: "unpublished";
59
+ } | CatalogPublicationRefusal;
60
+ /** Absence is distinct from an unavailable or unsupported authority. */
61
+ export type CatalogPublicationReceiptResult = {
62
+ outcome: "found";
63
+ receipt: CatalogPublicationReceipt;
64
+ } | {
65
+ outcome: "absent";
66
+ } | CatalogPublicationRefusal;
67
+ /**
68
+ * Authority-owned history, newest publication revision first with stable ordering.
69
+ * Limit is an integer from 1 through 100; receipts never exceed limit.
70
+ * nextCursor is opaque, and null only when no further page exists.
71
+ */
72
+ export type CatalogHistoryResult = {
73
+ outcome: "page";
74
+ receipts: readonly CatalogPublicationReceipt[];
75
+ limit: number;
76
+ nextCursor: string | null;
77
+ } | CatalogPublicationRefusal;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,77 @@
1
+ /** Provider-free source metadata. Delivery URLs are supplied projections. */
2
+ export type ProductMediaRole = "primary" | "view" | "illustration";
3
+ export interface ProductMediaAsset {
4
+ assetRef: string;
5
+ source: string;
6
+ contentType: "image/jpeg" | "image/png" | "image/webp";
7
+ byteLength: number;
8
+ sha256: string;
9
+ artworkLocale: string | null;
10
+ }
11
+ export interface ProductMediaProduct {
12
+ productId: string;
13
+ variantIds: readonly string[];
14
+ primaryVariantId: string;
15
+ }
16
+ export interface ProductMediaAssociation {
17
+ itemId: string;
18
+ assetRef: string;
19
+ productId: string;
20
+ scope: "product" | "variant";
21
+ variantId?: string;
22
+ locale: string | null;
23
+ role: ProductMediaRole;
24
+ viewKey?: string;
25
+ order: number;
26
+ label: string;
27
+ }
28
+ export interface ProductMediaManifest {
29
+ schemaVersion: 1;
30
+ assets: readonly ProductMediaAsset[];
31
+ products: readonly ProductMediaProduct[];
32
+ associations: readonly ProductMediaAssociation[];
33
+ }
34
+ export interface ProductMediaRequest {
35
+ productId: string;
36
+ variantId: string;
37
+ locale: string;
38
+ }
39
+ export interface ResolvedProductMediaItem {
40
+ id: string;
41
+ assetRef: string;
42
+ src: string;
43
+ label: string;
44
+ role: ProductMediaRole;
45
+ }
46
+ export interface ResolvedProductMedia {
47
+ key: string;
48
+ revision: string;
49
+ items: readonly ResolvedProductMediaItem[];
50
+ primaryItemId: string | null;
51
+ }
52
+ export interface StorefrontProductMedia {
53
+ productId: string;
54
+ locale: string;
55
+ primaryVariantId: string;
56
+ collections: Readonly<Record<string, ResolvedProductMedia>>;
57
+ }
58
+ export declare const PRODUCT_MEDIA_LIMITS: Readonly<{
59
+ assets: 512;
60
+ products: 256;
61
+ variants: 1024;
62
+ associations: 8192;
63
+ imageBytes: 358400;
64
+ totalBytes: number;
65
+ manifestBytes: number;
66
+ resolvedItems: 32;
67
+ }>;
68
+ export type ProductMediaValidationCode = "invalid_schema" | "duplicate_identity" | "unsafe_path" | "undeclared_variant" | "missing_resource" | "conflicting_primary" | "collection_limit_exceeded" | "pack_limit_exceeded";
69
+ export declare class ProductMediaValidationError extends Error {
70
+ readonly code: ProductMediaValidationCode;
71
+ readonly reference: string;
72
+ constructor(code: ProductMediaValidationCode, reference: string);
73
+ }
74
+ /** Checks contained relative resource bindings; the resource owner also checks symlinks. */
75
+ export declare function validateProductMediaSourcePath(value: unknown): string;
76
+ /** Structural authoring/pack validation. Signatures and digests require the source bytes. */
77
+ export declare function validateProductMediaManifest(value: unknown): ProductMediaManifest;
@@ -0,0 +1,149 @@
1
+ export const PRODUCT_MEDIA_LIMITS = Object.freeze({
2
+ assets: 512, products: 256, variants: 1024, associations: 8192,
3
+ imageBytes: 358400, totalBytes: 128 * 1024 * 1024,
4
+ manifestBytes: 2 * 1024 * 1024, resolvedItems: 32,
5
+ });
6
+ export class ProductMediaValidationError extends Error {
7
+ code;
8
+ reference;
9
+ constructor(code, reference) {
10
+ super(`${code}: ${reference}`);
11
+ this.code = code;
12
+ this.reference = reference;
13
+ this.name = "ProductMediaValidationError";
14
+ }
15
+ }
16
+ function refuse(code, reference) {
17
+ throw new ProductMediaValidationError(code, reference);
18
+ }
19
+ function record(value, reference) {
20
+ if (!value || typeof value !== "object" || Array.isArray(value))
21
+ refuse("invalid_schema", reference);
22
+ return value;
23
+ }
24
+ function fields(value, allowed, reference) {
25
+ if (Object.keys(value).some((key) => !allowed.includes(key)))
26
+ refuse("invalid_schema", reference);
27
+ }
28
+ function text(value, reference, max = 256) {
29
+ if (typeof value !== "string" || !value.trim() || value.length > max || Array.from(value).some((char) => char.charCodeAt(0) < 32 || char.charCodeAt(0) === 127)) {
30
+ refuse("invalid_schema", reference);
31
+ }
32
+ return value;
33
+ }
34
+ function locale(value, reference) {
35
+ if (value !== null)
36
+ text(value, reference, 64);
37
+ }
38
+ function array(value, reference, limit) {
39
+ if (!Array.isArray(value))
40
+ refuse("invalid_schema", reference);
41
+ if (value.length > limit)
42
+ refuse("pack_limit_exceeded", reference);
43
+ return value;
44
+ }
45
+ function unique(set, id) {
46
+ if (set.has(id))
47
+ refuse("duplicate_identity", id);
48
+ set.add(id);
49
+ }
50
+ /** Checks contained relative resource bindings; the resource owner also checks symlinks. */
51
+ export function validateProductMediaSourcePath(value) {
52
+ const source = text(value, "source", 512);
53
+ if (source.startsWith("/") || /[:\\%?#]/u.test(source) || source.split("/").some((part) => !part || part === "." || part === "..")) {
54
+ refuse("unsafe_path", "source");
55
+ }
56
+ return source;
57
+ }
58
+ /** Structural authoring/pack validation. Signatures and digests require the source bytes. */
59
+ export function validateProductMediaManifest(value) {
60
+ const manifest = record(value, "manifest");
61
+ fields(manifest, ["schemaVersion", "assets", "products", "associations"], "manifest");
62
+ if (manifest.schemaVersion !== 1)
63
+ refuse("invalid_schema", "schemaVersion");
64
+ const assets = array(manifest.assets, "assets", PRODUCT_MEDIA_LIMITS.assets);
65
+ const products = array(manifest.products, "products", PRODUCT_MEDIA_LIMITS.products);
66
+ const associations = array(manifest.associations, "associations", PRODUCT_MEDIA_LIMITS.associations);
67
+ const assetIds = new Set();
68
+ let totalBytes = 0;
69
+ for (const entry of assets) {
70
+ const asset = record(entry, "asset");
71
+ fields(asset, ["assetRef", "source", "contentType", "byteLength", "sha256", "artworkLocale"], "asset");
72
+ const id = text(asset.assetRef, "assetRef");
73
+ unique(assetIds, id);
74
+ validateProductMediaSourcePath(asset.source);
75
+ if (!["image/jpeg", "image/png", "image/webp"].includes(asset.contentType))
76
+ refuse("invalid_schema", id);
77
+ if (!Number.isSafeInteger(asset.byteLength) || asset.byteLength <= 0)
78
+ refuse("invalid_schema", id);
79
+ if (asset.byteLength > PRODUCT_MEDIA_LIMITS.imageBytes)
80
+ refuse("pack_limit_exceeded", id);
81
+ totalBytes += asset.byteLength;
82
+ if (totalBytes > PRODUCT_MEDIA_LIMITS.totalBytes)
83
+ refuse("pack_limit_exceeded", "totalBytes");
84
+ if (typeof asset.sha256 !== "string" || !/^[a-f0-9]{64}$/u.test(asset.sha256))
85
+ refuse("invalid_schema", id);
86
+ locale(asset.artworkLocale, id);
87
+ }
88
+ const productVariants = new Map();
89
+ let variantCount = 0;
90
+ for (const entry of products) {
91
+ const product = record(entry, "product");
92
+ fields(product, ["productId", "variantIds", "primaryVariantId"], "product");
93
+ const id = text(product.productId, "productId");
94
+ if (productVariants.has(id))
95
+ refuse("duplicate_identity", id);
96
+ const variants = new Set();
97
+ for (const variant of array(product.variantIds, id, PRODUCT_MEDIA_LIMITS.variants))
98
+ unique(variants, text(variant, id));
99
+ variantCount += variants.size;
100
+ if (variantCount > PRODUCT_MEDIA_LIMITS.variants)
101
+ refuse("pack_limit_exceeded", "variants");
102
+ const primary = text(product.primaryVariantId, id);
103
+ if (!variants.has(primary))
104
+ refuse("undeclared_variant", primary);
105
+ productVariants.set(id, variants);
106
+ }
107
+ const referencedAssets = new Set();
108
+ const identities = new Map();
109
+ const localizedIds = new Set();
110
+ for (const entry of associations) {
111
+ const item = record(entry, "association");
112
+ fields(item, ["itemId", "assetRef", "productId", "scope", "variantId", "locale", "role", "viewKey", "order", "label"], "association");
113
+ const id = text(item.itemId, "itemId");
114
+ const assetRef = text(item.assetRef, id);
115
+ if (!assetIds.has(assetRef))
116
+ refuse("missing_resource", assetRef);
117
+ referencedAssets.add(assetRef);
118
+ const productId = text(item.productId, id);
119
+ const variants = productVariants.get(productId);
120
+ if (!variants)
121
+ refuse("invalid_schema", productId);
122
+ if (item.scope !== "product" && item.scope !== "variant")
123
+ refuse("invalid_schema", id);
124
+ if (item.scope === "product" && item.variantId !== undefined)
125
+ refuse("invalid_schema", id);
126
+ if (item.scope === "variant") {
127
+ const variantId = text(item.variantId, id);
128
+ if (!variants.has(variantId))
129
+ refuse("undeclared_variant", variantId);
130
+ }
131
+ locale(item.locale, id);
132
+ if (!["primary", "view", "illustration"].includes(item.role))
133
+ refuse("invalid_schema", id);
134
+ if (item.viewKey !== undefined)
135
+ text(item.viewKey, id);
136
+ if (!Number.isSafeInteger(item.order))
137
+ refuse("invalid_schema", id);
138
+ text(item.label, id, 1024);
139
+ const identity = JSON.stringify([productId, item.scope, item.variantId, item.role, item.viewKey]);
140
+ if (identities.has(id) && identities.get(id) !== identity)
141
+ refuse("duplicate_identity", id);
142
+ identities.set(id, identity);
143
+ unique(localizedIds, JSON.stringify([id, item.locale]));
144
+ }
145
+ for (const id of assetIds)
146
+ if (!referencedAssets.has(id))
147
+ refuse("missing_resource", id);
148
+ return value;
149
+ }
@@ -0,0 +1,54 @@
1
+ export type CatalogQuantityDimension = "mass" | "volume" | "count";
2
+ export interface CatalogQuantity {
3
+ dimension: CatalogQuantityDimension;
4
+ unscaled: string;
5
+ scale: number;
6
+ unit: string;
7
+ }
8
+ export type CatalogOptionValue = {
9
+ kind: "choice";
10
+ value: string;
11
+ } | {
12
+ kind: "quantity";
13
+ value: CatalogQuantity;
14
+ } | {
15
+ kind: "boolean";
16
+ value: boolean;
17
+ };
18
+ export type CatalogDraftIssue = {
19
+ path: string;
20
+ code: string;
21
+ };
22
+ export interface CatalogProductTypeRef {
23
+ key: string;
24
+ version: number;
25
+ }
26
+ export interface CatalogProductTypeDefinition extends CatalogProductTypeRef {
27
+ units: {
28
+ code: string;
29
+ dimension: CatalogQuantityDimension;
30
+ integral: boolean;
31
+ }[];
32
+ netContentUnits: string[];
33
+ dimensions: ({
34
+ key: string;
35
+ kind: "choice";
36
+ values: string[];
37
+ } | {
38
+ key: string;
39
+ kind: "quantity";
40
+ dimension: CatalogQuantityDimension;
41
+ units: string[];
42
+ role?: "net_content";
43
+ } | {
44
+ key: string;
45
+ kind: "boolean";
46
+ })[];
47
+ validateContent: (content: unknown) => readonly CatalogDraftIssue[];
48
+ }
49
+ export interface CatalogProductTypeRegistry {
50
+ resolve(ref: CatalogProductTypeRef): CatalogProductTypeDefinition | undefined;
51
+ }
52
+ export declare function validateCatalogQuantity(quantity: CatalogQuantity, definition: CatalogProductTypeDefinition): boolean;
53
+ /** Decimal spelling cannot make two equal option values distinct. No unit conversion is inferred. */
54
+ export declare function canonicalCatalogOption(value: CatalogOptionValue): string;
@@ -0,0 +1,16 @@
1
+ export function validateCatalogQuantity(quantity, definition) {
2
+ const unit = definition.units.find(({ code }) => code === quantity.unit);
3
+ return !!unit && unit.dimension === quantity.dimension
4
+ && (!unit.integral || BigInt(quantity.unscaled) % (10n ** BigInt(quantity.scale)) === 0n);
5
+ }
6
+ /** Decimal spelling cannot make two equal option values distinct. No unit conversion is inferred. */
7
+ export function canonicalCatalogOption(value) {
8
+ if (value.kind !== "quantity")
9
+ return JSON.stringify(value);
10
+ let { unscaled, scale } = value.value;
11
+ while (scale > 0 && unscaled.endsWith("0")) {
12
+ unscaled = unscaled.slice(0, -1);
13
+ scale -= 1;
14
+ }
15
+ return JSON.stringify(["quantity", value.value.dimension, value.value.unit, unscaled, scale]);
16
+ }
@@ -0,0 +1,3 @@
1
+ import { type ProductMediaManifest, type ProductMediaRequest, type ResolvedProductMedia } from "./productMediaContracts.js";
2
+ /** Resolve only declared variant/product material; no filename, SKU or provider inference. */
3
+ export declare function resolveProductMedia(value: ProductMediaManifest, request: ProductMediaRequest, assetUrls: Readonly<Record<string, string>>): ResolvedProductMedia;
@@ -0,0 +1,78 @@
1
+ import { PRODUCT_MEDIA_LIMITS, ProductMediaValidationError, validateProductMediaManifest, } from "./productMediaContracts.js";
2
+ const hasControls = (value) => Array.from(value).some((char) => char.charCodeAt(0) < 32 || char.charCodeAt(0) === 127);
3
+ const compareText = (a, b) => a < b ? -1 : a > b ? 1 : 0;
4
+ const ordered = (a, b) => a.order - b.order || compareText(a.itemId, b.itemId);
5
+ /** A deterministic browser-safe fingerprint, not a cryptographic integrity receipt. */
6
+ function fingerprint(value) {
7
+ return [0x811c9dc5, 0x9e3779b9, 0x85ebca6b, 0xc2b2ae35].map((seed) => {
8
+ let hash = seed;
9
+ for (let index = 0; index < value.length; index++)
10
+ hash = Math.imul(hash ^ value.charCodeAt(index), 0x01000193);
11
+ return (hash >>> 0).toString(16).padStart(8, "0");
12
+ }).join("");
13
+ }
14
+ function revision(source, urls) {
15
+ const assets = [...source.assets].sort((a, b) => compareText(a.assetRef, b.assetRef)).map((a) => [a.assetRef, a.source, a.contentType, a.byteLength, a.sha256, a.artworkLocale, Object.prototype.hasOwnProperty.call(urls, a.assetRef) ? urls[a.assetRef] : null]);
16
+ const products = [...source.products].sort((a, b) => compareText(a.productId, b.productId)).map((p) => [p.productId, [...p.variantIds].sort(compareText), p.primaryVariantId]);
17
+ const associations = source.associations.map((a) => [a.itemId, a.assetRef, a.productId, a.scope, a.variantId ?? null, a.locale, a.role, a.viewKey ?? null, a.order, a.label]).sort((a, b) => compareText(JSON.stringify(a), JSON.stringify(b)));
18
+ return fingerprint(JSON.stringify([source.schemaVersion, assets, products, associations]));
19
+ }
20
+ function survives(a, b, locale) {
21
+ const ranks = (item) => [item.role === "primary" ? 1 : 0,
22
+ item.scope === "variant" ? 1 : 0, item.locale === locale ? 1 : 0];
23
+ const ar = ranks(a), br = ranks(b);
24
+ for (let index = 0; index < ar.length; index++)
25
+ if (ar[index] !== br[index])
26
+ return ar[index] > br[index];
27
+ return ordered(a, b) < 0;
28
+ }
29
+ /** Resolve only declared variant/product material; no filename, SKU or provider inference. */
30
+ export function resolveProductMedia(value, request, assetUrls) {
31
+ const source = validateProductMediaManifest(value);
32
+ const product = source.products.find((p) => p.productId === request.productId);
33
+ if (!product || !product.variantIds.includes(request.variantId)) {
34
+ throw new ProductMediaValidationError("undeclared_variant", request.variantId);
35
+ }
36
+ if (typeof request.locale !== "string" || !request.locale.trim() || request.locale.length > 64 || hasControls(request.locale)) {
37
+ throw new ProductMediaValidationError("invalid_schema", "locale");
38
+ }
39
+ const logicalItems = new Map();
40
+ for (const item of source.associations) {
41
+ if (item.productId !== request.productId || (item.scope === "variant" && item.variantId !== request.variantId))
42
+ continue;
43
+ if (item.locale !== null && item.locale !== request.locale)
44
+ continue;
45
+ const current = logicalItems.get(item.itemId);
46
+ if (!current || item.locale === request.locale)
47
+ logicalItems.set(item.itemId, item);
48
+ }
49
+ const applicable = [...logicalItems.values()];
50
+ if (applicable.filter((item) => item.role === "primary").length > 1) {
51
+ throw new ProductMediaValidationError("conflicting_primary", request.productId);
52
+ }
53
+ const uniqueAssets = new Map();
54
+ for (const item of applicable) {
55
+ const current = uniqueAssets.get(item.assetRef);
56
+ if (!current || survives(item, current, request.locale))
57
+ uniqueAssets.set(item.assetRef, item);
58
+ }
59
+ if (uniqueAssets.size > PRODUCT_MEDIA_LIMITS.resolvedItems) {
60
+ throw new ProductMediaValidationError("collection_limit_exceeded", request.productId);
61
+ }
62
+ const items = [...uniqueAssets.values()].sort(ordered).map((item) => {
63
+ const src = Object.prototype.hasOwnProperty.call(assetUrls, item.assetRef) ? assetUrls[item.assetRef] : undefined;
64
+ if (typeof src !== "string" || !src.trim())
65
+ throw new ProductMediaValidationError("missing_resource", item.assetRef);
66
+ // The projection may contain opaque relative delivery paths or HTTP(S) URLs only.
67
+ if (/^[a-z][a-z\d+.-]*:/iu.test(src) && !/^https?:\/\//iu.test(src) || /^\s/u.test(src) || hasControls(src)) {
68
+ throw new ProductMediaValidationError("unsafe_path", item.assetRef);
69
+ }
70
+ return { id: item.itemId, assetRef: item.assetRef, src, label: item.label, role: item.role };
71
+ });
72
+ const mediaRevision = revision(source, assetUrls);
73
+ return {
74
+ key: JSON.stringify([request.productId, request.variantId, request.locale, mediaRevision]),
75
+ revision: mediaRevision, items,
76
+ primaryItemId: items.find((item) => item.role === "primary")?.id ?? null,
77
+ };
78
+ }
@@ -0,0 +1,23 @@
1
+ export declare const SELLABLE_CATALOG_CONTRACT_VERSION = "catalog.sellable.v1";
2
+ export interface SellableCatalogItem {
3
+ sku: string;
4
+ title: string;
5
+ unitPrice: {
6
+ amountMinor: number;
7
+ currency: string;
8
+ };
9
+ permittedPurchaseModes: ("one_time" | "subscription")[];
10
+ }
11
+ export interface SellableCatalogProfile {
12
+ id: string;
13
+ brand: string;
14
+ country: string;
15
+ currency: string;
16
+ locale: string;
17
+ timezone: string;
18
+ }
19
+ export interface SellableCatalogListResponse {
20
+ contractVersion: typeof SELLABLE_CATALOG_CONTRACT_VERSION;
21
+ profile: SellableCatalogProfile;
22
+ items: SellableCatalogItem[];
23
+ }
@@ -0,0 +1 @@
1
+ export const SELLABLE_CATALOG_CONTRACT_VERSION = "catalog.sellable.v1";
@@ -0,0 +1,25 @@
1
+ import { type CatalogDraftApplyResult, type CatalogDraftPage, type CatalogDraftRecord, type CatalogDraftRefusal } from "./draftSchemas.js";
2
+ import { type CatalogDraftOperator, type CatalogDraftStorePort } from "./draftPorts.js";
3
+ import type { CatalogDraftIssue, CatalogProductTypeRegistry } from "./productTypeSchemas.js";
4
+ import { type CatalogDraftReadiness } from "./draftValidation.js";
5
+ export interface CatalogDraftHandlerDeps<Context> {
6
+ store: CatalogDraftStorePort;
7
+ registry: CatalogProductTypeRegistry;
8
+ authorizeOperator: (context: Context) => Promise<CatalogDraftOperator | null>;
9
+ }
10
+ export type CatalogDraftCommandResult = CatalogDraftApplyResult & {
11
+ issues?: readonly CatalogDraftIssue[];
12
+ readiness?: CatalogDraftReadiness;
13
+ };
14
+ /** Dark use case: mounting requires a verified operator authorizer at composition. */
15
+ export declare function createCatalogDraftHandler<Context>(deps: CatalogDraftHandlerDeps<Context>): {
16
+ execute(input: unknown, context: Context): Promise<CatalogDraftCommandResult>;
17
+ get(input: unknown, context: Context): Promise<{
18
+ outcome: "found";
19
+ record: CatalogDraftRecord | null;
20
+ } | CatalogDraftRefusal>;
21
+ list(input: unknown, context: Context): Promise<{
22
+ outcome: "listed";
23
+ page: CatalogDraftPage;
24
+ } | CatalogDraftRefusal>;
25
+ };
@@ -0,0 +1,118 @@
1
+ import { createHash } from "node:crypto";
2
+ import { canonicalCatalogDraftJson, catalogDraftApplyResultSchema, catalogDraftCommandSchema, catalogDraftGetQuerySchema, catalogDraftListQuerySchema, catalogDraftPageSchema, catalogDraftRecordSchema, catalogDraftRefusalSchema, } from "./draftSchemas.js";
3
+ import { CatalogDraftStoreError, } from "./draftPorts.js";
4
+ import { validateCatalogDraft } from "./draftValidation.js";
5
+ const unavailable = () => ({ outcome: "dependency_unavailable", reason: "draft_dependency_unavailable" });
6
+ const invalid = () => ({ outcome: "validation_issue", reason: "draft_input_invalid" });
7
+ function failure(error) {
8
+ if (error instanceof CatalogDraftStoreError) {
9
+ const parsed = catalogDraftRefusalSchema.safeParse(error.refusal);
10
+ if (parsed.success)
11
+ return parsed.data;
12
+ }
13
+ return unavailable();
14
+ }
15
+ /** Dark use case: mounting requires a verified operator authorizer at composition. */
16
+ export function createCatalogDraftHandler(deps) {
17
+ async function actor(context) {
18
+ try {
19
+ const operator = await deps.authorizeOperator(context);
20
+ if (!operator || !/^[^\s].{0,239}$/u.test(operator.actorId) || operator.actorId.trim() !== operator.actorId) {
21
+ return { outcome: "unauthorized", reason: "draft_operator_required" };
22
+ }
23
+ return operator;
24
+ }
25
+ catch {
26
+ return unavailable();
27
+ }
28
+ }
29
+ return {
30
+ async execute(input, context) {
31
+ const operator = await actor(context);
32
+ if ("outcome" in operator)
33
+ return operator;
34
+ let canonicalCommand;
35
+ try {
36
+ canonicalCommand = canonicalCatalogDraftJson(input);
37
+ }
38
+ catch {
39
+ return invalid();
40
+ }
41
+ const parsed = catalogDraftCommandSchema.safeParse(JSON.parse(canonicalCommand));
42
+ if (!parsed.success)
43
+ return invalid();
44
+ const command = parsed.data;
45
+ const fingerprint = createHash("sha256").update(canonicalCommand).digest("hex");
46
+ try {
47
+ // Read receipts first: changed registry, moved head or abandonment cannot erase a committed command.
48
+ const original = await deps.store.get({ draftId: command.draftId, commandKey: command.commandKey }, operator.actorId);
49
+ if (original !== null) {
50
+ const record = catalogDraftRecordSchema.parse(original);
51
+ if (record.draftId !== command.draftId || record.commandKey !== command.commandKey)
52
+ return unavailable();
53
+ if (record.actorId !== operator.actorId)
54
+ return { outcome: "unauthorized", reason: "draft_command_actor_mismatch" };
55
+ if (record.fingerprint !== fingerprint)
56
+ return { outcome: "conflict", reason: "draft_command_key_reused" };
57
+ return { outcome: "replayed", record };
58
+ }
59
+ let readiness;
60
+ if (command.payload) {
61
+ const validation = validateCatalogDraft(command.payload, deps.registry);
62
+ if (!validation.valid)
63
+ return { outcome: "validation_issue", reason: "draft_payload_invalid", issues: validation.issues };
64
+ readiness = validation.readiness;
65
+ }
66
+ const result = catalogDraftApplyResultSchema.parse(await deps.store.apply({ canonicalCommand, fingerprint, actorId: operator.actorId }));
67
+ if (result.outcome === "committed" || result.outcome === "replayed") {
68
+ if (result.record.draftId !== command.draftId || result.record.commandKey !== command.commandKey
69
+ || result.record.fingerprint !== fingerprint || result.record.actorId !== operator.actorId)
70
+ return unavailable();
71
+ return { ...result, ...(readiness ? { readiness } : {}) };
72
+ }
73
+ return result;
74
+ }
75
+ catch (error) {
76
+ return failure(error);
77
+ }
78
+ },
79
+ async get(input, context) {
80
+ const operator = await actor(context);
81
+ if ("outcome" in operator)
82
+ return operator;
83
+ const parsed = catalogDraftGetQuerySchema.safeParse(input);
84
+ if (!parsed.success)
85
+ return invalid();
86
+ try {
87
+ const value = await deps.store.get(parsed.data, operator.actorId);
88
+ const record = value === null ? null : catalogDraftRecordSchema.parse(value);
89
+ if (record && (record.draftId !== parsed.data.draftId
90
+ || (parsed.data.revision !== undefined && record.revision !== parsed.data.revision)
91
+ || (parsed.data.commandKey !== undefined && record.commandKey !== parsed.data.commandKey)))
92
+ return unavailable();
93
+ return { outcome: "found", record };
94
+ }
95
+ catch (error) {
96
+ return failure(error);
97
+ }
98
+ },
99
+ async list(input, context) {
100
+ const operator = await actor(context);
101
+ if ("outcome" in operator)
102
+ return operator;
103
+ const parsed = catalogDraftListQuerySchema.safeParse(input);
104
+ if (!parsed.success)
105
+ return invalid();
106
+ try {
107
+ const page = catalogDraftPageSchema.parse(await deps.store.list(parsed.data, operator.actorId));
108
+ if (page.items.length > parsed.data.limit || (parsed.data.afterDraftId
109
+ && page.items.some(({ draftId }) => draftId <= parsed.data.afterDraftId)))
110
+ return unavailable();
111
+ return { outcome: "listed", page };
112
+ }
113
+ catch (error) {
114
+ return failure(error);
115
+ }
116
+ },
117
+ };
118
+ }