@open-mercato/core 0.9.1-develop.7473.1.de5b22888f → 0.9.1-develop.7475.1.28ca93ed97

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 (597) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/catalog_price_history_entry/index.js +53 -0
  3. package/dist/generated/entities/catalog_price_history_entry/index.js.map +7 -0
  4. package/dist/generated/entities/customer_group_terms/index.js +2 -0
  5. package/dist/generated/entities/customer_group_terms/index.js.map +2 -2
  6. package/dist/generated/entities/ecommerce_store/index.js +33 -0
  7. package/dist/generated/entities/ecommerce_store/index.js.map +7 -0
  8. package/dist/generated/entities/ecommerce_store_channel_binding/index.js +29 -0
  9. package/dist/generated/entities/ecommerce_store_channel_binding/index.js.map +7 -0
  10. package/dist/generated/entities/ecommerce_store_domain_binding/index.js +23 -0
  11. package/dist/generated/entities/ecommerce_store_domain_binding/index.js.map +7 -0
  12. package/dist/generated/entities.ids.generated.js +8 -1
  13. package/dist/generated/entities.ids.generated.js.map +2 -2
  14. package/dist/generated/entity-fields-registry.js +72 -0
  15. package/dist/generated/entity-fields-registry.js.map +2 -2
  16. package/dist/helpers/integration/customerGroupsFixtures.js.map +2 -2
  17. package/dist/modules/catalog/acl.js +8 -1
  18. package/dist/modules/catalog/acl.js.map +2 -2
  19. package/dist/modules/catalog/api/config/omnibus/route.js +184 -0
  20. package/dist/modules/catalog/api/config/omnibus/route.js.map +7 -0
  21. package/dist/modules/catalog/api/prices/history/route.js +131 -0
  22. package/dist/modules/catalog/api/prices/history/route.js.map +7 -0
  23. package/dist/modules/catalog/api/prices/omnibus-preview/route.js +184 -0
  24. package/dist/modules/catalog/api/prices/omnibus-preview/route.js.map +7 -0
  25. package/dist/modules/catalog/api/products/route.js +54 -245
  26. package/dist/modules/catalog/api/products/route.js.map +3 -3
  27. package/dist/modules/catalog/api/tags/route.js +6 -0
  28. package/dist/modules/catalog/api/tags/route.js.map +2 -2
  29. package/dist/modules/catalog/backend/config/catalog/page.js +3 -1
  30. package/dist/modules/catalog/backend/config/catalog/page.js.map +2 -2
  31. package/dist/modules/catalog/cli.js +80 -2
  32. package/dist/modules/catalog/cli.js.map +2 -2
  33. package/dist/modules/catalog/commands/categories.js +51 -2
  34. package/dist/modules/catalog/commands/categories.js.map +2 -2
  35. package/dist/modules/catalog/commands/prices.js +43 -1
  36. package/dist/modules/catalog/commands/prices.js.map +2 -2
  37. package/dist/modules/catalog/commands/products.js +71 -0
  38. package/dist/modules/catalog/commands/products.js.map +2 -2
  39. package/dist/modules/catalog/commands/variants.js +7 -0
  40. package/dist/modules/catalog/commands/variants.js.map +2 -2
  41. package/dist/modules/catalog/components/OmnibusSettings.js +703 -0
  42. package/dist/modules/catalog/components/OmnibusSettings.js.map +7 -0
  43. package/dist/modules/catalog/components/PriceEditorOmnibusRow.js +152 -0
  44. package/dist/modules/catalog/components/PriceEditorOmnibusRow.js.map +7 -0
  45. package/dist/modules/catalog/components/lookupSources.js +31 -0
  46. package/dist/modules/catalog/components/lookupSources.js.map +7 -0
  47. package/dist/modules/catalog/components/prices/PriceScopeSelectors.js +8 -2
  48. package/dist/modules/catalog/components/prices/PriceScopeSelectors.js.map +2 -2
  49. package/dist/modules/catalog/components/products/VariantBuilder.js +11 -1
  50. package/dist/modules/catalog/components/products/VariantBuilder.js.map +2 -2
  51. package/dist/modules/catalog/data/entities.js +122 -1
  52. package/dist/modules/catalog/data/entities.js.map +2 -2
  53. package/dist/modules/catalog/data/validators.js +78 -0
  54. package/dist/modules/catalog/data/validators.js.map +2 -2
  55. package/dist/modules/catalog/di.js +18 -0
  56. package/dist/modules/catalog/di.js.map +2 -2
  57. package/dist/modules/catalog/lib/omnibus.js +285 -0
  58. package/dist/modules/catalog/lib/omnibus.js.map +7 -0
  59. package/dist/modules/catalog/lib/omnibusBackfill.js +146 -0
  60. package/dist/modules/catalog/lib/omnibusBackfill.js.map +7 -0
  61. package/dist/modules/catalog/lib/omnibusCache.js +89 -0
  62. package/dist/modules/catalog/lib/omnibusCache.js.map +7 -0
  63. package/dist/modules/catalog/lib/omnibusConfig.js +64 -0
  64. package/dist/modules/catalog/lib/omnibusConfig.js.map +7 -0
  65. package/dist/modules/catalog/lib/omnibusHistoryQueries.js +191 -0
  66. package/dist/modules/catalog/lib/omnibusHistoryQueries.js.map +7 -0
  67. package/dist/modules/catalog/lib/omnibusPresentedEntry.js +73 -0
  68. package/dist/modules/catalog/lib/omnibusPresentedEntry.js.map +7 -0
  69. package/dist/modules/catalog/lib/omnibusProductListEnrichment.js +94 -0
  70. package/dist/modules/catalog/lib/omnibusProductListEnrichment.js.map +7 -0
  71. package/dist/modules/catalog/lib/omnibusTypes.js +115 -0
  72. package/dist/modules/catalog/lib/omnibusTypes.js.map +7 -0
  73. package/dist/modules/catalog/lib/optionSchemaTranslations.js +55 -0
  74. package/dist/modules/catalog/lib/optionSchemaTranslations.js.map +7 -0
  75. package/dist/modules/catalog/lib/priceHistoryQuery.js +103 -0
  76. package/dist/modules/catalog/lib/priceHistoryQuery.js.map +7 -0
  77. package/dist/modules/catalog/lib/pricing.js +26 -2
  78. package/dist/modules/catalog/lib/pricing.js.map +2 -2
  79. package/dist/modules/catalog/lib/productFilters.js +273 -0
  80. package/dist/modules/catalog/lib/productFilters.js.map +7 -0
  81. package/dist/modules/catalog/lib/productScopeKeys.js +105 -0
  82. package/dist/modules/catalog/lib/productScopeKeys.js.map +7 -0
  83. package/dist/modules/catalog/lib/productScopeReindex.js +73 -0
  84. package/dist/modules/catalog/lib/productScopeReindex.js.map +7 -0
  85. package/dist/modules/catalog/migrations/Migration20261005105352_catalog.js +39 -0
  86. package/dist/modules/catalog/migrations/Migration20261005105352_catalog.js.map +7 -0
  87. package/dist/modules/catalog/migrations/Migration20261005150824_catalog.js +39 -0
  88. package/dist/modules/catalog/migrations/Migration20261005150824_catalog.js.map +7 -0
  89. package/dist/modules/catalog/services/catalogOmnibusService.js +480 -0
  90. package/dist/modules/catalog/services/catalogOmnibusService.js.map +7 -0
  91. package/dist/modules/catalog/setup.js +3 -1
  92. package/dist/modules/catalog/setup.js.map +2 -2
  93. package/dist/modules/catalog/subscribers/product-scope-keys-category-deleted.js +16 -0
  94. package/dist/modules/catalog/subscribers/product-scope-keys-category-deleted.js.map +7 -0
  95. package/dist/modules/catalog/subscribers/product-scope-keys-category-updated.js +17 -0
  96. package/dist/modules/catalog/subscribers/product-scope-keys-category-updated.js.map +7 -0
  97. package/dist/modules/catalog/translations.js +10 -0
  98. package/dist/modules/catalog/translations.js.map +2 -2
  99. package/dist/modules/configs/lib/upgrade-actions.js +28 -0
  100. package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
  101. package/dist/modules/customer_groups/api/customer-groups/[id]/terms/route.js +5 -0
  102. package/dist/modules/customer_groups/api/customer-groups/[id]/terms/route.js.map +2 -2
  103. package/dist/modules/customer_groups/components/CustomerGroupTermsSection.js +111 -3
  104. package/dist/modules/customer_groups/components/CustomerGroupTermsSection.js.map +2 -2
  105. package/dist/modules/customer_groups/components/customerGroupAssortmentScope.js +43 -0
  106. package/dist/modules/customer_groups/components/customerGroupAssortmentScope.js.map +7 -0
  107. package/dist/modules/customer_groups/data/entities.js +3 -0
  108. package/dist/modules/customer_groups/data/entities.js.map +2 -2
  109. package/dist/modules/customer_groups/data/validators.js +9 -0
  110. package/dist/modules/customer_groups/data/validators.js.map +2 -2
  111. package/dist/modules/customer_groups/migrations/Migration20261005110250_customer_groups.js +17 -0
  112. package/dist/modules/customer_groups/migrations/Migration20261005110250_customer_groups.js.map +7 -0
  113. package/dist/modules/customer_groups/services/customerGroupsService.js +62 -15
  114. package/dist/modules/customer_groups/services/customerGroupsService.js.map +2 -2
  115. package/dist/modules/ecommerce/acl.js +13 -0
  116. package/dist/modules/ecommerce/acl.js.map +7 -0
  117. package/dist/modules/ecommerce/api/domain-mappings/route.js +72 -0
  118. package/dist/modules/ecommerce/api/domain-mappings/route.js.map +7 -0
  119. package/dist/modules/ecommerce/api/openapi.js +27 -0
  120. package/dist/modules/ecommerce/api/openapi.js.map +7 -0
  121. package/dist/modules/ecommerce/api/store-channel-bindings/[id]/assortment-count/route.js +109 -0
  122. package/dist/modules/ecommerce/api/store-channel-bindings/[id]/assortment-count/route.js.map +7 -0
  123. package/dist/modules/ecommerce/api/store-channel-bindings/crud.js +265 -0
  124. package/dist/modules/ecommerce/api/store-channel-bindings/crud.js.map +7 -0
  125. package/dist/modules/ecommerce/api/store-channel-bindings/route.js +67 -0
  126. package/dist/modules/ecommerce/api/store-channel-bindings/route.js.map +7 -0
  127. package/dist/modules/ecommerce/api/store-domain-bindings/crud.js +296 -0
  128. package/dist/modules/ecommerce/api/store-domain-bindings/crud.js.map +7 -0
  129. package/dist/modules/ecommerce/api/store-domain-bindings/route.js +73 -0
  130. package/dist/modules/ecommerce/api/store-domain-bindings/route.js.map +7 -0
  131. package/dist/modules/ecommerce/api/storefront/categories/[slug]/route.js +87 -0
  132. package/dist/modules/ecommerce/api/storefront/categories/[slug]/route.js.map +7 -0
  133. package/dist/modules/ecommerce/api/storefront/categories/openapiSchemas.js +50 -0
  134. package/dist/modules/ecommerce/api/storefront/categories/openapiSchemas.js.map +7 -0
  135. package/dist/modules/ecommerce/api/storefront/categories/route.js +75 -0
  136. package/dist/modules/ecommerce/api/storefront/categories/route.js.map +7 -0
  137. package/dist/modules/ecommerce/api/storefront/context/route.js +163 -0
  138. package/dist/modules/ecommerce/api/storefront/context/route.js.map +7 -0
  139. package/dist/modules/ecommerce/api/storefront/products/[idOrHandle]/route.js +87 -0
  140. package/dist/modules/ecommerce/api/storefront/products/[idOrHandle]/route.js.map +7 -0
  141. package/dist/modules/ecommerce/api/storefront/products/openapiSchemas.js +177 -0
  142. package/dist/modules/ecommerce/api/storefront/products/openapiSchemas.js.map +7 -0
  143. package/dist/modules/ecommerce/api/storefront/products/route.js +78 -0
  144. package/dist/modules/ecommerce/api/storefront/products/route.js.map +7 -0
  145. package/dist/modules/ecommerce/api/storefront/search/suggest/openapiSchemas.js +25 -0
  146. package/dist/modules/ecommerce/api/storefront/search/suggest/openapiSchemas.js.map +7 -0
  147. package/dist/modules/ecommerce/api/storefront/search/suggest/route.js +75 -0
  148. package/dist/modules/ecommerce/api/storefront/search/suggest/route.js.map +7 -0
  149. package/dist/modules/ecommerce/api/storefront/storefrontRouteSupport.js +58 -0
  150. package/dist/modules/ecommerce/api/storefront/storefrontRouteSupport.js.map +7 -0
  151. package/dist/modules/ecommerce/api/stores/[id]/branding/route.js +183 -0
  152. package/dist/modules/ecommerce/api/stores/[id]/branding/route.js.map +7 -0
  153. package/dist/modules/ecommerce/api/stores/[id]/preview-branding/route.js +92 -0
  154. package/dist/modules/ecommerce/api/stores/[id]/preview-branding/route.js.map +7 -0
  155. package/dist/modules/ecommerce/api/stores/crud.js +401 -0
  156. package/dist/modules/ecommerce/api/stores/crud.js.map +7 -0
  157. package/dist/modules/ecommerce/api/stores/route.js +64 -0
  158. package/dist/modules/ecommerce/api/stores/route.js.map +7 -0
  159. package/dist/modules/ecommerce/backend/config/ecommerce/[id]/page.js +9 -0
  160. package/dist/modules/ecommerce/backend/config/ecommerce/[id]/page.js.map +7 -0
  161. package/dist/modules/ecommerce/backend/config/ecommerce/[id]/page.meta.js +16 -0
  162. package/dist/modules/ecommerce/backend/config/ecommerce/[id]/page.meta.js.map +7 -0
  163. package/dist/modules/ecommerce/backend/config/ecommerce/page.js +9 -0
  164. package/dist/modules/ecommerce/backend/config/ecommerce/page.js.map +7 -0
  165. package/dist/modules/ecommerce/backend/config/ecommerce/page.meta.js +16 -0
  166. package/dist/modules/ecommerce/backend/config/ecommerce/page.meta.js.map +7 -0
  167. package/dist/modules/ecommerce/commands/stores.js +110 -0
  168. package/dist/modules/ecommerce/commands/stores.js.map +7 -0
  169. package/dist/modules/ecommerce/components/ChannelAssortmentCount.js +90 -0
  170. package/dist/modules/ecommerce/components/ChannelAssortmentCount.js.map +7 -0
  171. package/dist/modules/ecommerce/components/ChannelBindingFormSections.js +33 -0
  172. package/dist/modules/ecommerce/components/ChannelBindingFormSections.js.map +7 -0
  173. package/dist/modules/ecommerce/components/ChannelLookupPicker.js +11 -0
  174. package/dist/modules/ecommerce/components/ChannelLookupPicker.js.map +7 -0
  175. package/dist/modules/ecommerce/components/DomainStatusWarning.js +24 -0
  176. package/dist/modules/ecommerce/components/DomainStatusWarning.js.map +7 -0
  177. package/dist/modules/ecommerce/components/StoreAvailabilityDefaultsSection.js +190 -0
  178. package/dist/modules/ecommerce/components/StoreAvailabilityDefaultsSection.js.map +7 -0
  179. package/dist/modules/ecommerce/components/StoreBrandingControls.js +67 -0
  180. package/dist/modules/ecommerce/components/StoreBrandingControls.js.map +7 -0
  181. package/dist/modules/ecommerce/components/StoreBrandingPreview.js +69 -0
  182. package/dist/modules/ecommerce/components/StoreBrandingPreview.js.map +7 -0
  183. package/dist/modules/ecommerce/components/StoreBrandingTab.js +218 -0
  184. package/dist/modules/ecommerce/components/StoreBrandingTab.js.map +7 -0
  185. package/dist/modules/ecommerce/components/StoreChannelBindingDialog.js +270 -0
  186. package/dist/modules/ecommerce/components/StoreChannelBindingDialog.js.map +7 -0
  187. package/dist/modules/ecommerce/components/StoreChannelsTab.js +313 -0
  188. package/dist/modules/ecommerce/components/StoreChannelsTab.js.map +7 -0
  189. package/dist/modules/ecommerce/components/StoreCreateDialog.js +164 -0
  190. package/dist/modules/ecommerce/components/StoreCreateDialog.js.map +7 -0
  191. package/dist/modules/ecommerce/components/StoreDomainBindingDialog.js +162 -0
  192. package/dist/modules/ecommerce/components/StoreDomainBindingDialog.js.map +7 -0
  193. package/dist/modules/ecommerce/components/StoreDomainsTab.js +357 -0
  194. package/dist/modules/ecommerce/components/StoreDomainsTab.js.map +7 -0
  195. package/dist/modules/ecommerce/components/StoreEditPage.js +155 -0
  196. package/dist/modules/ecommerce/components/StoreEditPage.js.map +7 -0
  197. package/dist/modules/ecommerce/components/StoreGeneralTab.js +167 -0
  198. package/dist/modules/ecommerce/components/StoreGeneralTab.js.map +7 -0
  199. package/dist/modules/ecommerce/components/StoreListPage.js +290 -0
  200. package/dist/modules/ecommerce/components/StoreListPage.js.map +7 -0
  201. package/dist/modules/ecommerce/components/StoreSeoTab.js +132 -0
  202. package/dist/modules/ecommerce/components/StoreSeoTab.js.map +7 -0
  203. package/dist/modules/ecommerce/components/StoreStatusBadge.js +20 -0
  204. package/dist/modules/ecommerce/components/StoreStatusBadge.js.map +7 -0
  205. package/dist/modules/ecommerce/components/channelLookups.js +28 -0
  206. package/dist/modules/ecommerce/components/channelLookups.js.map +7 -0
  207. package/dist/modules/ecommerce/components/storeAdmin.js +41 -0
  208. package/dist/modules/ecommerce/components/storeAdmin.js.map +7 -0
  209. package/dist/modules/ecommerce/components/storeBrandingForm.js +197 -0
  210. package/dist/modules/ecommerce/components/storeBrandingForm.js.map +7 -0
  211. package/dist/modules/ecommerce/components/storeChannels.js +140 -0
  212. package/dist/modules/ecommerce/components/storeChannels.js.map +7 -0
  213. package/dist/modules/ecommerce/components/storeDomains.js +167 -0
  214. package/dist/modules/ecommerce/components/storeDomains.js.map +7 -0
  215. package/dist/modules/ecommerce/components/storeEditTabs.js +62 -0
  216. package/dist/modules/ecommerce/components/storeEditTabs.js.map +7 -0
  217. package/dist/modules/ecommerce/components/storeGeneral.js +95 -0
  218. package/dist/modules/ecommerce/components/storeGeneral.js.map +7 -0
  219. package/dist/modules/ecommerce/components/storeSeo.js +69 -0
  220. package/dist/modules/ecommerce/components/storeSeo.js.map +7 -0
  221. package/dist/modules/ecommerce/components/useChannelAssortmentCount.js +45 -0
  222. package/dist/modules/ecommerce/components/useChannelAssortmentCount.js.map +7 -0
  223. package/dist/modules/ecommerce/components/useStoreAccess.js +28 -0
  224. package/dist/modules/ecommerce/components/useStoreAccess.js.map +7 -0
  225. package/dist/modules/ecommerce/data/enrichers.js +157 -0
  226. package/dist/modules/ecommerce/data/enrichers.js.map +7 -0
  227. package/dist/modules/ecommerce/data/entities.js +197 -0
  228. package/dist/modules/ecommerce/data/entities.js.map +7 -0
  229. package/dist/modules/ecommerce/data/validators.js +406 -0
  230. package/dist/modules/ecommerce/data/validators.js.map +7 -0
  231. package/dist/modules/ecommerce/di.js +11 -0
  232. package/dist/modules/ecommerce/di.js.map +7 -0
  233. package/dist/modules/ecommerce/events.js +27 -0
  234. package/dist/modules/ecommerce/events.js.map +7 -0
  235. package/dist/modules/ecommerce/index.js +16 -0
  236. package/dist/modules/ecommerce/index.js.map +7 -0
  237. package/dist/modules/ecommerce/lib/assortmentCount.js +112 -0
  238. package/dist/modules/ecommerce/lib/assortmentCount.js.map +7 -0
  239. package/dist/modules/ecommerce/lib/brandingStyles.js +184 -0
  240. package/dist/modules/ecommerce/lib/brandingStyles.js.map +7 -0
  241. package/dist/modules/ecommerce/lib/buyerContext.js +284 -0
  242. package/dist/modules/ecommerce/lib/buyerContext.js.map +7 -0
  243. package/dist/modules/ecommerce/lib/cacheInvalidation.js +186 -0
  244. package/dist/modules/ecommerce/lib/cacheInvalidation.js.map +7 -0
  245. package/dist/modules/ecommerce/lib/cacheKeys.js +236 -0
  246. package/dist/modules/ecommerce/lib/cacheKeys.js.map +7 -0
  247. package/dist/modules/ecommerce/lib/createConflictRecheck.js +29 -0
  248. package/dist/modules/ecommerce/lib/createConflictRecheck.js.map +7 -0
  249. package/dist/modules/ecommerce/lib/crudEvents.js +94 -0
  250. package/dist/modules/ecommerce/lib/crudEvents.js.map +7 -0
  251. package/dist/modules/ecommerce/lib/crudSupport.js +64 -0
  252. package/dist/modules/ecommerce/lib/crudSupport.js.map +7 -0
  253. package/dist/modules/ecommerce/lib/domainBindingRebind.js +99 -0
  254. package/dist/modules/ecommerce/lib/domainBindingRebind.js.map +7 -0
  255. package/dist/modules/ecommerce/lib/domainMappingSummaries.js +39 -0
  256. package/dist/modules/ecommerce/lib/domainMappingSummaries.js.map +7 -0
  257. package/dist/modules/ecommerce/lib/exclusiveFlag.js +57 -0
  258. package/dist/modules/ecommerce/lib/exclusiveFlag.js.map +7 -0
  259. package/dist/modules/ecommerce/lib/references.js +70 -0
  260. package/dist/modules/ecommerce/lib/references.js.map +7 -0
  261. package/dist/modules/ecommerce/lib/seedDraftStore.js +99 -0
  262. package/dist/modules/ecommerce/lib/seedDraftStore.js.map +7 -0
  263. package/dist/modules/ecommerce/lib/storeBindingCascade.js +109 -0
  264. package/dist/modules/ecommerce/lib/storeBindingCascade.js.map +7 -0
  265. package/dist/modules/ecommerce/lib/storeBranding.js +40 -0
  266. package/dist/modules/ecommerce/lib/storeBranding.js.map +7 -0
  267. package/dist/modules/ecommerce/lib/storeBrandingRoute.js +76 -0
  268. package/dist/modules/ecommerce/lib/storeBrandingRoute.js.map +7 -0
  269. package/dist/modules/ecommerce/lib/storeContext.js +393 -0
  270. package/dist/modules/ecommerce/lib/storeContext.js.map +7 -0
  271. package/dist/modules/ecommerce/lib/storeContextService.js +43 -0
  272. package/dist/modules/ecommerce/lib/storeContextService.js.map +7 -0
  273. package/dist/modules/ecommerce/lib/storeLocale.js +60 -0
  274. package/dist/modules/ecommerce/lib/storeLocale.js.map +7 -0
  275. package/dist/modules/ecommerce/lib/storeNotifications.js +41 -0
  276. package/dist/modules/ecommerce/lib/storeNotifications.js.map +7 -0
  277. package/dist/modules/ecommerce/lib/storefrontCatalogSupport.js +248 -0
  278. package/dist/modules/ecommerce/lib/storefrontCatalogSupport.js.map +7 -0
  279. package/dist/modules/ecommerce/lib/storefrontCategories.js +241 -0
  280. package/dist/modules/ecommerce/lib/storefrontCategories.js.map +7 -0
  281. package/dist/modules/ecommerce/lib/storefrontCategoryCache.js +71 -0
  282. package/dist/modules/ecommerce/lib/storefrontCategoryCache.js.map +7 -0
  283. package/dist/modules/ecommerce/lib/storefrontDetail.js +447 -0
  284. package/dist/modules/ecommerce/lib/storefrontDetail.js.map +7 -0
  285. package/dist/modules/ecommerce/lib/storefrontFacets.js +441 -0
  286. package/dist/modules/ecommerce/lib/storefrontFacets.js.map +7 -0
  287. package/dist/modules/ecommerce/lib/storefrontOptionFilter.js +15 -0
  288. package/dist/modules/ecommerce/lib/storefrontOptionFilter.js.map +7 -0
  289. package/dist/modules/ecommerce/lib/storefrontPricing.js +393 -0
  290. package/dist/modules/ecommerce/lib/storefrontPricing.js.map +7 -0
  291. package/dist/modules/ecommerce/lib/storefrontProductCache.js +90 -0
  292. package/dist/modules/ecommerce/lib/storefrontProductCache.js.map +7 -0
  293. package/dist/modules/ecommerce/lib/storefrontProductScope.js +107 -0
  294. package/dist/modules/ecommerce/lib/storefrontProductScope.js.map +7 -0
  295. package/dist/modules/ecommerce/lib/storefrontProducts.js +670 -0
  296. package/dist/modules/ecommerce/lib/storefrontProducts.js.map +7 -0
  297. package/dist/modules/ecommerce/lib/storefrontQuery.js +156 -0
  298. package/dist/modules/ecommerce/lib/storefrontQuery.js.map +7 -0
  299. package/dist/modules/ecommerce/lib/storefrontRateLimit.js +99 -0
  300. package/dist/modules/ecommerce/lib/storefrontRateLimit.js.map +7 -0
  301. package/dist/modules/ecommerce/lib/storefrontSearch.js +303 -0
  302. package/dist/modules/ecommerce/lib/storefrontSearch.js.map +7 -0
  303. package/dist/modules/ecommerce/lib/storefrontSearchCache.js +35 -0
  304. package/dist/modules/ecommerce/lib/storefrontSearchCache.js.map +7 -0
  305. package/dist/modules/ecommerce/lib/subscriberSupport.js +33 -0
  306. package/dist/modules/ecommerce/lib/subscriberSupport.js.map +7 -0
  307. package/dist/modules/ecommerce/lib/types.js +1 -0
  308. package/dist/modules/ecommerce/lib/types.js.map +7 -0
  309. package/dist/modules/ecommerce/migrations/Migration20261005113233_ecommerce.js +32 -0
  310. package/dist/modules/ecommerce/migrations/Migration20261005113233_ecommerce.js.map +7 -0
  311. package/dist/modules/ecommerce/migrations/Migration20261006112733_ecommerce.js +17 -0
  312. package/dist/modules/ecommerce/migrations/Migration20261006112733_ecommerce.js.map +7 -0
  313. package/dist/modules/ecommerce/notifications.client.js +47 -0
  314. package/dist/modules/ecommerce/notifications.client.js.map +7 -0
  315. package/dist/modules/ecommerce/notifications.js +48 -0
  316. package/dist/modules/ecommerce/notifications.js.map +7 -0
  317. package/dist/modules/ecommerce/search.js +83 -0
  318. package/dist/modules/ecommerce/search.js.map +7 -0
  319. package/dist/modules/ecommerce/setup.js +17 -0
  320. package/dist/modules/ecommerce/setup.js.map +7 -0
  321. package/dist/modules/ecommerce/subscribers/assortment-empty-notifications.js +14 -0
  322. package/dist/modules/ecommerce/subscribers/assortment-empty-notifications.js.map +7 -0
  323. package/dist/modules/ecommerce/subscribers/availability-policy-cache-invalidation.js +14 -0
  324. package/dist/modules/ecommerce/subscribers/availability-policy-cache-invalidation.js.map +7 -0
  325. package/dist/modules/ecommerce/subscribers/catalog-category-cache-invalidation.js +14 -0
  326. package/dist/modules/ecommerce/subscribers/catalog-category-cache-invalidation.js.map +7 -0
  327. package/dist/modules/ecommerce/subscribers/catalog-price-cache-invalidation.js +14 -0
  328. package/dist/modules/ecommerce/subscribers/catalog-price-cache-invalidation.js.map +7 -0
  329. package/dist/modules/ecommerce/subscribers/catalog-product-cache-invalidation.js +14 -0
  330. package/dist/modules/ecommerce/subscribers/catalog-product-cache-invalidation.js.map +7 -0
  331. package/dist/modules/ecommerce/subscribers/catalog-variant-cache-invalidation.js +14 -0
  332. package/dist/modules/ecommerce/subscribers/catalog-variant-cache-invalidation.js.map +7 -0
  333. package/dist/modules/ecommerce/subscribers/customer-group-cache-invalidation.js +14 -0
  334. package/dist/modules/ecommerce/subscribers/customer-group-cache-invalidation.js.map +7 -0
  335. package/dist/modules/ecommerce/subscribers/customer-group-membership-cache-invalidation.js +14 -0
  336. package/dist/modules/ecommerce/subscribers/customer-group-membership-cache-invalidation.js.map +7 -0
  337. package/dist/modules/ecommerce/subscribers/customer-group-terms-cache-invalidation.js +14 -0
  338. package/dist/modules/ecommerce/subscribers/customer-group-terms-cache-invalidation.js.map +7 -0
  339. package/dist/modules/ecommerce/subscribers/customer-user-deleted-cache-invalidation.js +14 -0
  340. package/dist/modules/ecommerce/subscribers/customer-user-deleted-cache-invalidation.js.map +7 -0
  341. package/dist/modules/ecommerce/subscribers/customer-user-updated-cache-invalidation.js +14 -0
  342. package/dist/modules/ecommerce/subscribers/customer-user-updated-cache-invalidation.js.map +7 -0
  343. package/dist/modules/ecommerce/subscribers/domain-binding-rebind.js +16 -0
  344. package/dist/modules/ecommerce/subscribers/domain-binding-rebind.js.map +7 -0
  345. package/dist/modules/ecommerce/subscribers/domain-mapping-cache-invalidation.js +14 -0
  346. package/dist/modules/ecommerce/subscribers/domain-mapping-cache-invalidation.js.map +7 -0
  347. package/dist/modules/ecommerce/subscribers/store-cache-invalidation.js +14 -0
  348. package/dist/modules/ecommerce/subscribers/store-cache-invalidation.js.map +7 -0
  349. package/dist/modules/ecommerce/subscribers/store-channel-binding-cache-invalidation.js +14 -0
  350. package/dist/modules/ecommerce/subscribers/store-channel-binding-cache-invalidation.js.map +7 -0
  351. package/dist/modules/ecommerce/subscribers/store-domain-binding-cache-invalidation.js +14 -0
  352. package/dist/modules/ecommerce/subscribers/store-domain-binding-cache-invalidation.js.map +7 -0
  353. package/dist/modules/ecommerce/subscribers/store-notifications.js +14 -0
  354. package/dist/modules/ecommerce/subscribers/store-notifications.js.map +7 -0
  355. package/dist/modules/query_index/lib/batch.js +23 -9
  356. package/dist/modules/query_index/lib/batch.js.map +2 -2
  357. package/dist/modules/query_index/lib/doc-enrichers.js +212 -0
  358. package/dist/modules/query_index/lib/doc-enrichers.js.map +7 -0
  359. package/dist/modules/query_index/lib/document.js +2 -0
  360. package/dist/modules/query_index/lib/document.js.map +2 -2
  361. package/dist/modules/query_index/lib/engine.js +68 -1
  362. package/dist/modules/query_index/lib/engine.js.map +2 -2
  363. package/dist/modules/query_index/lib/indexer.js +7 -0
  364. package/dist/modules/query_index/lib/indexer.js.map +2 -2
  365. package/dist/modules/query_index/lib/search-tokens.js +2 -0
  366. package/dist/modules/query_index/lib/search-tokens.js.map +2 -2
  367. package/dist/modules/query_index/migrations/Migration20261005201500_query_index.js +17 -0
  368. package/dist/modules/query_index/migrations/Migration20261005201500_query_index.js.map +7 -0
  369. package/dist/modules/translations/components/TranslationManager.js +3 -3
  370. package/dist/modules/translations/components/TranslationManager.js.map +2 -2
  371. package/dist/modules/translations/data/validators.js +1 -1
  372. package/dist/modules/translations/data/validators.js.map +1 -1
  373. package/dist/modules/translations/lib/batch.js +21 -1
  374. package/dist/modules/translations/lib/batch.js.map +2 -2
  375. package/dist/modules/translations/lib/helpers.js +3 -0
  376. package/dist/modules/translations/lib/helpers.js.map +2 -2
  377. package/dist/modules/translations/lib/resolve-field-list.js +14 -2
  378. package/dist/modules/translations/lib/resolve-field-list.js.map +2 -2
  379. package/generated/entities/catalog_price_history_entry/index.ts +25 -0
  380. package/generated/entities/customer_group_terms/index.ts +1 -0
  381. package/generated/entities/ecommerce_store/index.ts +15 -0
  382. package/generated/entities/ecommerce_store_channel_binding/index.ts +13 -0
  383. package/generated/entities/ecommerce_store_domain_binding/index.ts +10 -0
  384. package/generated/entities.ids.generated.ts +8 -1
  385. package/generated/entity-fields-registry.ts +72 -0
  386. package/package.json +7 -7
  387. package/src/helpers/integration/customerGroupsFixtures.ts +2 -0
  388. package/src/modules/auth/i18n/de.json +8 -0
  389. package/src/modules/auth/i18n/en.json +8 -0
  390. package/src/modules/auth/i18n/es.json +8 -0
  391. package/src/modules/auth/i18n/ko.json +8 -0
  392. package/src/modules/auth/i18n/pl.json +8 -0
  393. package/src/modules/catalog/AGENTS.md +5 -2
  394. package/src/modules/catalog/acl.ts +7 -0
  395. package/src/modules/catalog/api/config/omnibus/route.ts +204 -0
  396. package/src/modules/catalog/api/prices/history/route.ts +139 -0
  397. package/src/modules/catalog/api/prices/omnibus-preview/route.ts +202 -0
  398. package/src/modules/catalog/api/products/route.ts +53 -308
  399. package/src/modules/catalog/api/tags/route.ts +6 -0
  400. package/src/modules/catalog/backend/config/catalog/page.tsx +2 -0
  401. package/src/modules/catalog/cli.ts +84 -1
  402. package/src/modules/catalog/commands/categories.ts +54 -2
  403. package/src/modules/catalog/commands/prices.ts +43 -1
  404. package/src/modules/catalog/commands/products.ts +84 -1
  405. package/src/modules/catalog/commands/variants.ts +7 -0
  406. package/src/modules/catalog/components/OmnibusSettings.tsx +832 -0
  407. package/src/modules/catalog/components/PriceEditorOmnibusRow.tsx +219 -0
  408. package/src/modules/catalog/components/lookupSources.ts +29 -0
  409. package/src/modules/catalog/components/prices/PriceScopeSelectors.tsx +9 -0
  410. package/src/modules/catalog/components/products/VariantBuilder.tsx +9 -0
  411. package/src/modules/catalog/data/entities.ts +123 -1
  412. package/src/modules/catalog/data/validators.ts +118 -0
  413. package/src/modules/catalog/di.ts +23 -0
  414. package/src/modules/catalog/i18n/de.json +77 -0
  415. package/src/modules/catalog/i18n/en.json +77 -0
  416. package/src/modules/catalog/i18n/es.json +77 -0
  417. package/src/modules/catalog/i18n/ko.json +77 -0
  418. package/src/modules/catalog/i18n/pl.json +77 -0
  419. package/src/modules/catalog/lib/omnibus.ts +406 -0
  420. package/src/modules/catalog/lib/omnibusBackfill.ts +198 -0
  421. package/src/modules/catalog/lib/omnibusCache.ts +111 -0
  422. package/src/modules/catalog/lib/omnibusConfig.ts +73 -0
  423. package/src/modules/catalog/lib/omnibusHistoryQueries.ts +248 -0
  424. package/src/modules/catalog/lib/omnibusPresentedEntry.ts +76 -0
  425. package/src/modules/catalog/lib/omnibusProductListEnrichment.ts +125 -0
  426. package/src/modules/catalog/lib/omnibusTypes.ts +202 -0
  427. package/src/modules/catalog/lib/optionSchemaTranslations.ts +55 -0
  428. package/src/modules/catalog/lib/priceHistoryQuery.ts +155 -0
  429. package/src/modules/catalog/lib/pricing.ts +42 -4
  430. package/src/modules/catalog/lib/productFilters.ts +349 -0
  431. package/src/modules/catalog/lib/productScopeKeys.ts +162 -0
  432. package/src/modules/catalog/lib/productScopeReindex.ts +139 -0
  433. package/src/modules/catalog/migrations/.snapshot-open-mercato.json +572 -0
  434. package/src/modules/catalog/migrations/Migration20261005105352_catalog.ts +39 -0
  435. package/src/modules/catalog/migrations/Migration20261005150824_catalog.ts +39 -0
  436. package/src/modules/catalog/services/catalogOmnibusService.ts +642 -0
  437. package/src/modules/catalog/setup.ts +2 -0
  438. package/src/modules/catalog/subscribers/product-scope-keys-category-deleted.ts +18 -0
  439. package/src/modules/catalog/subscribers/product-scope-keys-category-updated.ts +19 -0
  440. package/src/modules/catalog/translations.ts +12 -0
  441. package/src/modules/configs/i18n/de.json +5 -1
  442. package/src/modules/configs/i18n/en.json +5 -1
  443. package/src/modules/configs/i18n/es.json +5 -1
  444. package/src/modules/configs/i18n/ko.json +5 -1
  445. package/src/modules/configs/i18n/pl.json +5 -1
  446. package/src/modules/configs/lib/upgrade-actions.ts +35 -0
  447. package/src/modules/customer_groups/api/customer-groups/[id]/terms/route.ts +7 -0
  448. package/src/modules/customer_groups/components/CustomerGroupTermsSection.tsx +124 -2
  449. package/src/modules/customer_groups/components/customerGroupAssortmentScope.ts +46 -0
  450. package/src/modules/customer_groups/data/entities.ts +4 -0
  451. package/src/modules/customer_groups/data/validators.ts +14 -0
  452. package/src/modules/customer_groups/i18n/de.json +12 -0
  453. package/src/modules/customer_groups/i18n/en.json +12 -0
  454. package/src/modules/customer_groups/i18n/es.json +12 -0
  455. package/src/modules/customer_groups/i18n/ko.json +12 -0
  456. package/src/modules/customer_groups/i18n/pl.json +12 -0
  457. package/src/modules/customer_groups/migrations/.snapshot-open-mercato.json +17 -0
  458. package/src/modules/customer_groups/migrations/Migration20261005110250_customer_groups.ts +15 -0
  459. package/src/modules/customer_groups/services/customerGroupsService.ts +74 -23
  460. package/src/modules/ecommerce/acl.ts +9 -0
  461. package/src/modules/ecommerce/api/domain-mappings/route.ts +76 -0
  462. package/src/modules/ecommerce/api/openapi.ts +28 -0
  463. package/src/modules/ecommerce/api/store-channel-bindings/[id]/assortment-count/route.ts +141 -0
  464. package/src/modules/ecommerce/api/store-channel-bindings/crud.ts +283 -0
  465. package/src/modules/ecommerce/api/store-channel-bindings/route.ts +67 -0
  466. package/src/modules/ecommerce/api/store-domain-bindings/crud.ts +324 -0
  467. package/src/modules/ecommerce/api/store-domain-bindings/route.ts +73 -0
  468. package/src/modules/ecommerce/api/storefront/categories/[slug]/route.ts +94 -0
  469. package/src/modules/ecommerce/api/storefront/categories/openapiSchemas.ts +63 -0
  470. package/src/modules/ecommerce/api/storefront/categories/route.ts +78 -0
  471. package/src/modules/ecommerce/api/storefront/context/route.ts +203 -0
  472. package/src/modules/ecommerce/api/storefront/products/[idOrHandle]/route.ts +94 -0
  473. package/src/modules/ecommerce/api/storefront/products/openapiSchemas.ts +193 -0
  474. package/src/modules/ecommerce/api/storefront/products/route.ts +81 -0
  475. package/src/modules/ecommerce/api/storefront/search/suggest/openapiSchemas.ts +25 -0
  476. package/src/modules/ecommerce/api/storefront/search/suggest/route.ts +78 -0
  477. package/src/modules/ecommerce/api/storefront/storefrontRouteSupport.ts +68 -0
  478. package/src/modules/ecommerce/api/stores/[id]/branding/route.ts +205 -0
  479. package/src/modules/ecommerce/api/stores/[id]/preview-branding/route.ts +99 -0
  480. package/src/modules/ecommerce/api/stores/crud.ts +457 -0
  481. package/src/modules/ecommerce/api/stores/route.ts +66 -0
  482. package/src/modules/ecommerce/backend/config/ecommerce/[id]/page.meta.ts +12 -0
  483. package/src/modules/ecommerce/backend/config/ecommerce/[id]/page.tsx +5 -0
  484. package/src/modules/ecommerce/backend/config/ecommerce/page.meta.ts +12 -0
  485. package/src/modules/ecommerce/backend/config/ecommerce/page.tsx +5 -0
  486. package/src/modules/ecommerce/commands/stores.ts +154 -0
  487. package/src/modules/ecommerce/components/ChannelAssortmentCount.tsx +138 -0
  488. package/src/modules/ecommerce/components/ChannelBindingFormSections.tsx +40 -0
  489. package/src/modules/ecommerce/components/ChannelLookupPicker.tsx +5 -0
  490. package/src/modules/ecommerce/components/DomainStatusWarning.tsx +32 -0
  491. package/src/modules/ecommerce/components/StoreAvailabilityDefaultsSection.tsx +219 -0
  492. package/src/modules/ecommerce/components/StoreBrandingControls.tsx +73 -0
  493. package/src/modules/ecommerce/components/StoreBrandingPreview.tsx +77 -0
  494. package/src/modules/ecommerce/components/StoreBrandingTab.tsx +242 -0
  495. package/src/modules/ecommerce/components/StoreChannelBindingDialog.tsx +306 -0
  496. package/src/modules/ecommerce/components/StoreChannelsTab.tsx +375 -0
  497. package/src/modules/ecommerce/components/StoreCreateDialog.tsx +204 -0
  498. package/src/modules/ecommerce/components/StoreDomainBindingDialog.tsx +198 -0
  499. package/src/modules/ecommerce/components/StoreDomainsTab.tsx +426 -0
  500. package/src/modules/ecommerce/components/StoreEditPage.tsx +193 -0
  501. package/src/modules/ecommerce/components/StoreGeneralTab.tsx +189 -0
  502. package/src/modules/ecommerce/components/StoreListPage.tsx +326 -0
  503. package/src/modules/ecommerce/components/StoreSeoTab.tsx +152 -0
  504. package/src/modules/ecommerce/components/StoreStatusBadge.tsx +22 -0
  505. package/src/modules/ecommerce/components/channelLookups.ts +24 -0
  506. package/src/modules/ecommerce/components/storeAdmin.ts +56 -0
  507. package/src/modules/ecommerce/components/storeBrandingForm.ts +230 -0
  508. package/src/modules/ecommerce/components/storeChannels.ts +210 -0
  509. package/src/modules/ecommerce/components/storeDomains.ts +221 -0
  510. package/src/modules/ecommerce/components/storeEditTabs.tsx +76 -0
  511. package/src/modules/ecommerce/components/storeGeneral.ts +155 -0
  512. package/src/modules/ecommerce/components/storeSeo.ts +78 -0
  513. package/src/modules/ecommerce/components/useChannelAssortmentCount.ts +67 -0
  514. package/src/modules/ecommerce/components/useStoreAccess.ts +36 -0
  515. package/src/modules/ecommerce/data/enrichers.ts +213 -0
  516. package/src/modules/ecommerce/data/entities.ts +168 -0
  517. package/src/modules/ecommerce/data/validators.ts +587 -0
  518. package/src/modules/ecommerce/di.ts +9 -0
  519. package/src/modules/ecommerce/events.ts +27 -0
  520. package/src/modules/ecommerce/i18n/de.json +341 -0
  521. package/src/modules/ecommerce/i18n/en.json +341 -0
  522. package/src/modules/ecommerce/i18n/es.json +341 -0
  523. package/src/modules/ecommerce/i18n/ko.json +341 -0
  524. package/src/modules/ecommerce/i18n/pl.json +341 -0
  525. package/src/modules/ecommerce/index.ts +14 -0
  526. package/src/modules/ecommerce/lib/assortmentCount.ts +175 -0
  527. package/src/modules/ecommerce/lib/brandingStyles.ts +215 -0
  528. package/src/modules/ecommerce/lib/buyerContext.ts +401 -0
  529. package/src/modules/ecommerce/lib/cacheInvalidation.ts +302 -0
  530. package/src/modules/ecommerce/lib/cacheKeys.ts +340 -0
  531. package/src/modules/ecommerce/lib/createConflictRecheck.ts +31 -0
  532. package/src/modules/ecommerce/lib/crudEvents.ts +128 -0
  533. package/src/modules/ecommerce/lib/crudSupport.ts +85 -0
  534. package/src/modules/ecommerce/lib/domainBindingRebind.ts +138 -0
  535. package/src/modules/ecommerce/lib/domainMappingSummaries.ts +71 -0
  536. package/src/modules/ecommerce/lib/exclusiveFlag.ts +90 -0
  537. package/src/modules/ecommerce/lib/references.ts +93 -0
  538. package/src/modules/ecommerce/lib/seedDraftStore.ts +125 -0
  539. package/src/modules/ecommerce/lib/storeBindingCascade.ts +138 -0
  540. package/src/modules/ecommerce/lib/storeBranding.ts +48 -0
  541. package/src/modules/ecommerce/lib/storeBrandingRoute.ts +91 -0
  542. package/src/modules/ecommerce/lib/storeContext.ts +669 -0
  543. package/src/modules/ecommerce/lib/storeContextService.ts +71 -0
  544. package/src/modules/ecommerce/lib/storeLocale.ts +88 -0
  545. package/src/modules/ecommerce/lib/storeNotifications.ts +55 -0
  546. package/src/modules/ecommerce/lib/storefrontCatalogSupport.ts +427 -0
  547. package/src/modules/ecommerce/lib/storefrontCategories.ts +419 -0
  548. package/src/modules/ecommerce/lib/storefrontCategoryCache.ts +113 -0
  549. package/src/modules/ecommerce/lib/storefrontDetail.ts +654 -0
  550. package/src/modules/ecommerce/lib/storefrontFacets.ts +697 -0
  551. package/src/modules/ecommerce/lib/storefrontOptionFilter.ts +44 -0
  552. package/src/modules/ecommerce/lib/storefrontPricing.ts +583 -0
  553. package/src/modules/ecommerce/lib/storefrontProductCache.ts +126 -0
  554. package/src/modules/ecommerce/lib/storefrontProductScope.ts +181 -0
  555. package/src/modules/ecommerce/lib/storefrontProducts.ts +963 -0
  556. package/src/modules/ecommerce/lib/storefrontQuery.ts +215 -0
  557. package/src/modules/ecommerce/lib/storefrontRateLimit.ts +126 -0
  558. package/src/modules/ecommerce/lib/storefrontSearch.ts +454 -0
  559. package/src/modules/ecommerce/lib/storefrontSearchCache.ts +49 -0
  560. package/src/modules/ecommerce/lib/subscriberSupport.ts +41 -0
  561. package/src/modules/ecommerce/lib/types.ts +69 -0
  562. package/src/modules/ecommerce/migrations/.snapshot-open-mercato.json +829 -0
  563. package/src/modules/ecommerce/migrations/Migration20261005113233_ecommerce.ts +32 -0
  564. package/src/modules/ecommerce/migrations/Migration20261006112733_ecommerce.ts +15 -0
  565. package/src/modules/ecommerce/notifications.client.ts +46 -0
  566. package/src/modules/ecommerce/notifications.ts +46 -0
  567. package/src/modules/ecommerce/search.ts +93 -0
  568. package/src/modules/ecommerce/setup.ts +15 -0
  569. package/src/modules/ecommerce/subscribers/assortment-empty-notifications.ts +12 -0
  570. package/src/modules/ecommerce/subscribers/availability-policy-cache-invalidation.ts +12 -0
  571. package/src/modules/ecommerce/subscribers/catalog-category-cache-invalidation.ts +12 -0
  572. package/src/modules/ecommerce/subscribers/catalog-price-cache-invalidation.ts +12 -0
  573. package/src/modules/ecommerce/subscribers/catalog-product-cache-invalidation.ts +12 -0
  574. package/src/modules/ecommerce/subscribers/catalog-variant-cache-invalidation.ts +12 -0
  575. package/src/modules/ecommerce/subscribers/customer-group-cache-invalidation.ts +12 -0
  576. package/src/modules/ecommerce/subscribers/customer-group-membership-cache-invalidation.ts +12 -0
  577. package/src/modules/ecommerce/subscribers/customer-group-terms-cache-invalidation.ts +12 -0
  578. package/src/modules/ecommerce/subscribers/customer-user-deleted-cache-invalidation.ts +12 -0
  579. package/src/modules/ecommerce/subscribers/customer-user-updated-cache-invalidation.ts +12 -0
  580. package/src/modules/ecommerce/subscribers/domain-binding-rebind.ts +14 -0
  581. package/src/modules/ecommerce/subscribers/domain-mapping-cache-invalidation.ts +12 -0
  582. package/src/modules/ecommerce/subscribers/store-cache-invalidation.ts +12 -0
  583. package/src/modules/ecommerce/subscribers/store-channel-binding-cache-invalidation.ts +12 -0
  584. package/src/modules/ecommerce/subscribers/store-domain-binding-cache-invalidation.ts +12 -0
  585. package/src/modules/ecommerce/subscribers/store-notifications.ts +12 -0
  586. package/src/modules/query_index/lib/batch.ts +25 -9
  587. package/src/modules/query_index/lib/doc-enrichers.ts +306 -0
  588. package/src/modules/query_index/lib/document.ts +2 -0
  589. package/src/modules/query_index/lib/engine.ts +58 -1
  590. package/src/modules/query_index/lib/indexer.ts +8 -0
  591. package/src/modules/query_index/lib/search-tokens.ts +2 -0
  592. package/src/modules/query_index/migrations/Migration20261005201500_query_index.ts +42 -0
  593. package/src/modules/translations/components/TranslationManager.tsx +3 -3
  594. package/src/modules/translations/data/validators.ts +1 -1
  595. package/src/modules/translations/lib/batch.ts +47 -0
  596. package/src/modules/translations/lib/helpers.ts +4 -0
  597. package/src/modules/translations/lib/resolve-field-list.ts +16 -2
@@ -0,0 +1,393 @@
1
+ import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
2
+ import { createLogger } from "@open-mercato/shared/lib/logger";
3
+ import { getTelemetryRuntime } from "@open-mercato/shared/lib/telemetry/runtime";
4
+ import { formatCurrency } from "@open-mercato/ui/utils/format";
5
+ import { CatalogProductPrice } from "@open-mercato/core/modules/catalog/data/entities";
6
+ import {
7
+ buildPriceRowFilter,
8
+ resolvePriceKindId,
9
+ resolvePriceOfferId,
10
+ resolvePriceVariantId,
11
+ selectBestPrice
12
+ } from "@open-mercato/core/modules/catalog/lib/pricing";
13
+ import {
14
+ isPromotionPriceKind,
15
+ resolveOmnibusPresentedEntries
16
+ } from "@open-mercato/core/modules/catalog/lib/omnibusPresentedEntry";
17
+ import { SalesTaxRate } from "@open-mercato/core/modules/sales/data/entities";
18
+ const logger = createLogger("ecommerce");
19
+ const AMOUNT_PRECISION = 4;
20
+ function storefrontPricingCustomerIds(buyer) {
21
+ if (!buyer.customerOverlayId) return [];
22
+ return buyer.customerOverlayId.split(",").filter((id) => id.length > 0);
23
+ }
24
+ function buildStorefrontPricingContext(ctx, overrides = {}) {
25
+ const customerIds = storefrontPricingCustomerIds(ctx.buyer);
26
+ const customerId = ctx.buyer.customerId && customerIds.includes(ctx.buyer.customerId) ? ctx.buyer.customerId : null;
27
+ return {
28
+ channelId: ctx.channel?.salesChannelId ?? null,
29
+ priceKindId: ctx.buyer.priceKindId ?? ctx.channel?.priceKindId ?? null,
30
+ customerId,
31
+ customerIds,
32
+ customerGroupIds: [...ctx.buyer.customerGroupIds],
33
+ currencyCode: ctx.currencyCode,
34
+ quantity: overrides.quantity ?? 1,
35
+ date: overrides.date ?? /* @__PURE__ */ new Date()
36
+ };
37
+ }
38
+ function tryResolve(container, name) {
39
+ try {
40
+ const resolved = container.resolve(name);
41
+ return resolved ?? null;
42
+ } catch {
43
+ return null;
44
+ }
45
+ }
46
+ function roundAmount(value) {
47
+ const factor = 10 ** AMOUNT_PRECISION;
48
+ return Math.round(value * factor) / factor;
49
+ }
50
+ function parseAmount(value) {
51
+ if (value === null || value === void 0 || value === "") return null;
52
+ const numeric = typeof value === "number" ? value : Number(value);
53
+ return Number.isFinite(numeric) ? numeric : null;
54
+ }
55
+ function formatAmount(amount, currencyCode, locale) {
56
+ return formatCurrency(amount, currencyCode, locale) ?? String(amount);
57
+ }
58
+ function resolveRowProductId(row, variantToProduct) {
59
+ const variantId = resolvePriceVariantId(row);
60
+ if (variantId) return variantToProduct.get(variantId) ?? null;
61
+ if (!row.product) return null;
62
+ return typeof row.product === "string" ? row.product : row.product.id ?? null;
63
+ }
64
+ function compareTaxRates(left, right) {
65
+ const channelDiff = (right.channelId ? 1 : 0) - (left.channelId ? 1 : 0);
66
+ if (channelDiff !== 0) return channelDiff;
67
+ const defaultDiff = (right.isDefault ? 1 : 0) - (left.isDefault ? 1 : 0);
68
+ if (defaultDiff !== 0) return defaultDiff;
69
+ const priorityDiff = (right.priority ?? 0) - (left.priority ?? 0);
70
+ if (priorityDiff !== 0) return priorityDiff;
71
+ return left.code < right.code ? -1 : left.code > right.code ? 1 : 0;
72
+ }
73
+ function selectStorefrontTaxRate(rates, customerGroupIds, date) {
74
+ const active = rates.filter(
75
+ (rate) => (!rate.startsAt || rate.startsAt <= date) && (!rate.endsAt || rate.endsAt >= date)
76
+ );
77
+ const pick = (candidates) => {
78
+ if (!candidates.length) return null;
79
+ const [best] = [...candidates].sort(compareTaxRates);
80
+ return parseAmount(best.rate);
81
+ };
82
+ for (const groupId of customerGroupIds) {
83
+ const groupRate = pick(active.filter((rate) => rate.customerGroupId === groupId));
84
+ if (groupRate !== null) return groupRate;
85
+ }
86
+ return pick(active.filter((rate) => !rate.customerGroupId));
87
+ }
88
+ function deriveSide(other, ratePercent, target) {
89
+ const factor = 1 + ratePercent / 100;
90
+ return roundAmount(target === "gross" ? other * factor : other / factor);
91
+ }
92
+ async function selectAmount(runtime, source) {
93
+ const wanted = parseAmount(runtime.taxMode === "gross" ? source.gross : source.net);
94
+ if (wanted !== null) return wanted;
95
+ const other = parseAmount(runtime.taxMode === "gross" ? source.net : source.gross);
96
+ if (other === null) return null;
97
+ const rowRate = parseAmount(source.rowTaxRate);
98
+ const rate = rowRate ?? await runtime.fallbackRate();
99
+ if (rate === null) return null;
100
+ return deriveSide(other, rate, runtime.taxMode);
101
+ }
102
+ function rowAmountSource(row) {
103
+ return { net: row.unitPriceNet, gross: row.unitPriceGross, rowTaxRate: row.taxRate };
104
+ }
105
+ function createFallbackRateLoader(em, ctx, pricingContext) {
106
+ let pending = null;
107
+ return () => {
108
+ if (!pending) {
109
+ pending = (async () => {
110
+ const scope = { tenantId: ctx.tenantId, organizationId: ctx.organizationId };
111
+ const groupIds = pricingContext.customerGroupIds;
112
+ const where = {
113
+ tenantId: ctx.tenantId,
114
+ organizationId: ctx.organizationId,
115
+ deletedAt: null,
116
+ productCategoryId: null,
117
+ $and: [
118
+ pricingContext.channelId ? { $or: [{ channelId: null }, { channelId: pricingContext.channelId }] } : { channelId: null },
119
+ groupIds.length ? { $or: [{ customerGroupId: null }, { customerGroupId: { $in: groupIds } }] } : { customerGroupId: null }
120
+ ]
121
+ };
122
+ const rates = await findWithDecryption(em, SalesTaxRate, where, void 0, scope);
123
+ return selectStorefrontTaxRate(rates, groupIds, pricingContext.date);
124
+ })();
125
+ }
126
+ return pending;
127
+ };
128
+ }
129
+ function variantTargetKey(productId, variantId) {
130
+ return `${productId}:${variantId}`;
131
+ }
132
+ async function resolveMany(service, entries) {
133
+ if (!entries.length) return [];
134
+ if (service) return service.resolvePriceMany(entries);
135
+ return entries.map(({ rows, context }) => selectBestPrice(rows, context));
136
+ }
137
+ function omnibusLowestSource(block) {
138
+ if (!block || !block.applicable) return null;
139
+ if (block.lowestPriceNet === null && block.lowestPriceGross === null) return null;
140
+ return { net: block.lowestPriceNet, gross: block.lowestPriceGross, rowTaxRate: null };
141
+ }
142
+ async function resolveOmnibusBlocksForPage(container, runtime, targets) {
143
+ const blocks = /* @__PURE__ */ new Map();
144
+ if (!targets.length) return blocks;
145
+ const service = tryResolve(container, "catalogOmnibusService");
146
+ if (!service) return blocks;
147
+ const { ctx, em } = runtime;
148
+ try {
149
+ const presentedRows = targets.map((target) => target.selected).filter((row) => row !== null);
150
+ const presentedEntries = await resolveOmnibusPresentedEntries(em, presentedRows);
151
+ const requests = targets.map((target) => {
152
+ const row = target.selected;
153
+ return {
154
+ context: {
155
+ tenantId: ctx.tenantId,
156
+ organizationId: ctx.organizationId,
157
+ productId: target.productId,
158
+ variantId: resolvePriceVariantId(row),
159
+ offerId: resolvePriceOfferId(row),
160
+ channelId: runtime.pricingContext.channelId,
161
+ priceKindId: resolvePriceKindId(row),
162
+ currencyCode: row.currencyCode,
163
+ isStorefront: true,
164
+ now: runtime.pricingContext.date
165
+ },
166
+ presentedEntry: presentedEntries.get(row.id) ?? null,
167
+ priceKindIsPromotion: true
168
+ };
169
+ });
170
+ const resolved = await service.resolveOmnibusBlocks(em, requests);
171
+ targets.forEach((target, index) => blocks.set(target.key, resolved[index] ?? null));
172
+ } catch (err) {
173
+ logger.error("[internal] ecommerce storefront omnibus resolution failed", {
174
+ tenantId: ctx.tenantId,
175
+ organizationId: ctx.organizationId,
176
+ storeId: ctx.store.id,
177
+ promotionCount: targets.length,
178
+ err
179
+ });
180
+ getTelemetryRuntime()?.reportError(err, {
181
+ module: "ecommerce",
182
+ code: "ecommerce.storefront_omnibus_failed",
183
+ attributes: { promotionCount: targets.length }
184
+ });
185
+ blocks.clear();
186
+ }
187
+ return blocks;
188
+ }
189
+ async function buildPrice(runtime, selected, original, omnibus) {
190
+ const amount = await selectAmount(runtime, rowAmountSource(selected));
191
+ if (amount === null) return null;
192
+ const locale = runtime.ctx.effectiveLocale;
193
+ const currencyCode = selected.currencyCode;
194
+ let isPromotion = false;
195
+ let originalAmount = null;
196
+ let lowestPriorAmount = null;
197
+ if (isPromotionPriceKind(selected)) {
198
+ const lowestSource = omnibusLowestSource(omnibus);
199
+ lowestPriorAmount = lowestSource ? await selectAmount(runtime, lowestSource) : null;
200
+ if (lowestPriorAmount !== null) {
201
+ isPromotion = true;
202
+ const originalValue = original ? await selectAmount(runtime, rowAmountSource(original)) : null;
203
+ originalAmount = originalValue !== null && originalValue > amount ? originalValue : null;
204
+ }
205
+ }
206
+ return {
207
+ currencyCode,
208
+ displayMode: runtime.taxMode,
209
+ amount,
210
+ formatted: formatAmount(amount, currencyCode, locale),
211
+ isPromotion,
212
+ originalAmount,
213
+ formattedOriginal: originalAmount === null ? null : formatAmount(originalAmount, currencyCode, locale),
214
+ lowestPriorAmount,
215
+ formattedLowestPrior: lowestPriorAmount === null ? null : formatAmount(lowestPriorAmount, currencyCode, locale)
216
+ };
217
+ }
218
+ function collectTierQuantities(rows) {
219
+ const quantities = /* @__PURE__ */ new Set([1]);
220
+ for (const row of rows) {
221
+ const minQuantity = row.minQuantity ?? 1;
222
+ if (Number.isInteger(minQuantity) && minQuantity > 1) quantities.add(minQuantity);
223
+ }
224
+ return Array.from(quantities).sort((left, right) => left - right);
225
+ }
226
+ async function buildPriceTiers(runtime, quantities, rows) {
227
+ const collapsed = [];
228
+ for (let index = 0; index < quantities.length; index += 1) {
229
+ const row = rows[index];
230
+ if (!row) continue;
231
+ const amount = await selectAmount(runtime, rowAmountSource(row));
232
+ if (amount === null) continue;
233
+ const previous = collapsed[collapsed.length - 1];
234
+ if (previous && previous.amount === amount && previous.currencyCode === row.currencyCode) continue;
235
+ collapsed.push({ minQuantity: quantities[index], amount, currencyCode: row.currencyCode });
236
+ }
237
+ if (collapsed.length < 2) return [];
238
+ return collapsed.map((tier, index) => {
239
+ const next = collapsed[index + 1];
240
+ return {
241
+ minQuantity: tier.minQuantity,
242
+ maxQuantity: next ? next.minQuantity - 1 : null,
243
+ amount: tier.amount,
244
+ formatted: formatAmount(tier.amount, tier.currencyCode, runtime.ctx.effectiveLocale)
245
+ };
246
+ });
247
+ }
248
+ function buildPriceRange(runtime, prices) {
249
+ const priced = prices.filter((price) => price !== null);
250
+ if (!priced.length) return null;
251
+ const currencyCode = priced[0].currencyCode;
252
+ const amounts = priced.filter((price) => price.currencyCode === currencyCode).map((price) => price.amount);
253
+ const min = Math.min(...amounts);
254
+ const max = Math.max(...amounts);
255
+ const locale = runtime.ctx.effectiveLocale;
256
+ return {
257
+ min,
258
+ max,
259
+ formattedMin: formatAmount(min, currencyCode, locale),
260
+ formattedMax: formatAmount(max, currencyCode, locale)
261
+ };
262
+ }
263
+ async function fetchPageRows(em, ctx, pricingContext, productIds, variantIds) {
264
+ const targetClause = variantIds.length ? { $or: [{ product: { $in: productIds } }, { variant: { $in: variantIds } }] } : { product: { $in: productIds } };
265
+ const where = {
266
+ $and: [buildPriceRowFilter(pricingContext), targetClause],
267
+ tenantId: ctx.tenantId,
268
+ organizationId: ctx.organizationId
269
+ };
270
+ const scope = { tenantId: ctx.tenantId, organizationId: ctx.organizationId };
271
+ const rows = await findWithDecryption(em, CatalogProductPrice, where, { populate: ["priceKind", "offer"] }, scope);
272
+ return rows;
273
+ }
274
+ async function resolveStorefrontPrices(container, ctx, items, options = {}) {
275
+ const results = /* @__PURE__ */ new Map();
276
+ if (!items.length) return results;
277
+ const em = tryResolve(container, "em");
278
+ if (!em) throw new Error("[internal] ecommerce storefront pricing requires the DI service em");
279
+ const pricingService = tryResolve(container, "catalogPricingService");
280
+ const pricingContext = buildStorefrontPricingContext(ctx, { date: options.date });
281
+ const runtime = {
282
+ ctx,
283
+ em,
284
+ pricingContext,
285
+ taxMode: ctx.buyer.taxMode,
286
+ fallbackRate: createFallbackRateLoader(em, ctx, pricingContext)
287
+ };
288
+ const detail = options.detail === true;
289
+ const variantToProduct = /* @__PURE__ */ new Map();
290
+ for (const item of items) {
291
+ for (const variantId of item.variantIds) variantToProduct.set(variantId, item.productId);
292
+ }
293
+ const productIds = Array.from(new Set(items.map((item) => item.productId)));
294
+ const rows = await fetchPageRows(em, ctx, pricingContext, productIds, Array.from(variantToProduct.keys()));
295
+ const rowsByProduct = /* @__PURE__ */ new Map();
296
+ for (const row of rows) {
297
+ const productId = resolveRowProductId(row, variantToProduct);
298
+ if (!productId) continue;
299
+ const bucket = rowsByProduct.get(productId) ?? [];
300
+ bucket.push(row);
301
+ rowsByProduct.set(productId, bucket);
302
+ }
303
+ const targets = [];
304
+ const tierPlans = /* @__PURE__ */ new Map();
305
+ const entries = [];
306
+ for (const item of items) {
307
+ const productRows = rowsByProduct.get(item.productId) ?? [];
308
+ targets.push({ key: item.productId, productId: item.productId, variantId: null, rows: productRows, selected: null });
309
+ entries.push({ rows: productRows, context: pricingContext });
310
+ for (const variantId of item.variantIds) {
311
+ const variantRows = productRows.filter((row) => {
312
+ const rowVariantId = resolvePriceVariantId(row);
313
+ return rowVariantId === null || rowVariantId === variantId;
314
+ });
315
+ targets.push({
316
+ key: variantTargetKey(item.productId, variantId),
317
+ productId: item.productId,
318
+ variantId,
319
+ rows: variantRows,
320
+ selected: null
321
+ });
322
+ entries.push({ rows: variantRows, context: pricingContext });
323
+ }
324
+ }
325
+ if (detail) {
326
+ const tierProductIds = options.tierProductIds ? new Set(options.tierProductIds) : null;
327
+ for (const target of targets) {
328
+ if (tierProductIds && !tierProductIds.has(target.productId)) continue;
329
+ const quantities = collectTierQuantities(target.rows);
330
+ tierPlans.set(target.key, { quantities, start: entries.length });
331
+ for (const quantity of quantities) entries.push({ rows: target.rows, context: { ...pricingContext, quantity } });
332
+ }
333
+ }
334
+ const resolved = await resolveMany(pricingService, entries);
335
+ const tiersFor = async (key) => {
336
+ const plan = tierPlans.get(key);
337
+ if (!plan) return [];
338
+ return buildPriceTiers(runtime, plan.quantities, resolved.slice(plan.start, plan.start + plan.quantities.length));
339
+ };
340
+ targets.forEach((target, index) => {
341
+ target.selected = resolved[index] ?? null;
342
+ });
343
+ const promotional = targets.filter(
344
+ (target) => target.selected !== null && isPromotionPriceKind(target.selected) && (detail || target.variantId === null)
345
+ );
346
+ const originalsResolved = await resolveMany(
347
+ pricingService,
348
+ promotional.map((target) => ({
349
+ rows: target.rows.filter((row) => !isPromotionPriceKind(row)),
350
+ context: pricingContext
351
+ }))
352
+ );
353
+ const originals = /* @__PURE__ */ new Map();
354
+ promotional.forEach((target, index) => originals.set(target.key, originalsResolved[index] ?? null));
355
+ const omnibusBlocks = await resolveOmnibusBlocksForPage(container, runtime, promotional);
356
+ const prices = /* @__PURE__ */ new Map();
357
+ for (const target of targets) {
358
+ prices.set(
359
+ target.key,
360
+ target.selected ? await buildPrice(
361
+ runtime,
362
+ target.selected,
363
+ originals.get(target.key) ?? null,
364
+ omnibusBlocks.get(target.key) ?? null
365
+ ) : null
366
+ );
367
+ }
368
+ for (const item of items) {
369
+ const variantPrices = /* @__PURE__ */ new Map();
370
+ const variantPriceTiers = /* @__PURE__ */ new Map();
371
+ for (const variantId of item.variantIds) {
372
+ const key = variantTargetKey(item.productId, variantId);
373
+ variantPrices.set(variantId, prices.get(key) ?? null);
374
+ variantPriceTiers.set(variantId, await tiersFor(key));
375
+ }
376
+ results.set(item.productId, {
377
+ productId: item.productId,
378
+ price: prices.get(item.productId) ?? null,
379
+ priceRange: item.variantIds.length ? buildPriceRange(runtime, Array.from(variantPrices.values())) : null,
380
+ variantPrices,
381
+ priceTiers: await tiersFor(item.productId),
382
+ variantPriceTiers
383
+ });
384
+ }
385
+ return results;
386
+ }
387
+ export {
388
+ buildStorefrontPricingContext,
389
+ resolveStorefrontPrices,
390
+ selectStorefrontTaxRate,
391
+ storefrontPricingCustomerIds
392
+ };
393
+ //# sourceMappingURL=storefrontPricing.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/ecommerce/lib/storefrontPricing.ts"],
4
+ "sourcesContent": ["import type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { getTelemetryRuntime } from '@open-mercato/shared/lib/telemetry/runtime'\nimport { formatCurrency } from '@open-mercato/ui/utils/format'\nimport { CatalogProductPrice } from '@open-mercato/core/modules/catalog/data/entities'\nimport {\n buildPriceRowFilter,\n resolvePriceKindId,\n resolvePriceOfferId,\n resolvePriceVariantId,\n selectBestPrice,\n type PriceRow,\n type PricingContext,\n} from '@open-mercato/core/modules/catalog/lib/pricing'\nimport {\n isPromotionPriceKind,\n resolveOmnibusPresentedEntries,\n} from '@open-mercato/core/modules/catalog/lib/omnibusPresentedEntry'\nimport type { OmnibusBlock, OmnibusResolutionRequest } from '@open-mercato/core/modules/catalog/lib/omnibusTypes'\nimport type { CatalogOmnibusService } from '@open-mercato/core/modules/catalog/services/catalogOmnibusService'\nimport type { CatalogPricingService } from '@open-mercato/core/modules/catalog/services/catalogPricingService'\nimport { SalesTaxRate } from '@open-mercato/core/modules/sales/data/entities'\nimport type { EcommercePriceDisplayMode } from '../data/validators'\nimport type { StoreContext } from './types'\n\nconst logger = createLogger('ecommerce')\n\nconst AMOUNT_PRECISION = 4\n\nexport type StorefrontPricingContext = PricingContext & {\n channelId: string | null\n priceKindId: string | null\n customerId: string | null\n customerIds: string[]\n customerGroupIds: string[]\n currencyCode: string\n quantity: number\n date: Date\n}\n\nexport type StorefrontPrice = {\n currencyCode: string\n displayMode: EcommercePriceDisplayMode\n amount: number\n formatted: string\n isPromotion: boolean\n originalAmount: number | null\n formattedOriginal: string | null\n lowestPriorAmount: number | null\n formattedLowestPrior: string | null\n}\n\nexport type StorefrontPriceRange = {\n min: number\n max: number\n formattedMin: string\n formattedMax: string\n}\n\nexport type StorefrontPriceTier = {\n minQuantity: number\n maxQuantity: number | null\n amount: number\n formatted: string\n}\n\nexport type StorefrontPriceItem = {\n productId: string\n variantIds: string[]\n}\n\nexport type StorefrontProductPricing = {\n productId: string\n price: StorefrontPrice | null\n priceRange: StorefrontPriceRange | null\n variantPrices: Map<string, StorefrontPrice | null>\n priceTiers: StorefrontPriceTier[]\n variantPriceTiers: Map<string, StorefrontPriceTier[]>\n}\n\nexport type ResolveStorefrontPricesOptions = {\n detail?: boolean\n /** Detail mode only: products whose quantity tiers are resolved; every item when omitted. */\n tierProductIds?: string[]\n date?: Date\n}\n\nexport type StorefrontPricingContainer = {\n resolve: (name: string) => unknown\n}\n\n/**\n * The customer ids pricing may match on: only `buyer.customerOverlayId`, the ids that are part of the\n * cache `digest`. A contract price added after the buyer context was cached is ignored until that\n * context is re-resolved, so a response is never cached under a digest it does not belong to.\n */\nexport function storefrontPricingCustomerIds(buyer: Pick<StoreContext['buyer'], 'customerOverlayId'>): string[] {\n if (!buyer.customerOverlayId) return []\n return buyer.customerOverlayId.split(',').filter((id) => id.length > 0)\n}\n\nexport function buildStorefrontPricingContext(\n ctx: StoreContext,\n overrides: { quantity?: number; date?: Date } = {},\n): StorefrontPricingContext {\n const customerIds = storefrontPricingCustomerIds(ctx.buyer)\n const customerId = ctx.buyer.customerId && customerIds.includes(ctx.buyer.customerId) ? ctx.buyer.customerId : null\n return {\n channelId: ctx.channel?.salesChannelId ?? null,\n priceKindId: ctx.buyer.priceKindId ?? ctx.channel?.priceKindId ?? null,\n customerId,\n customerIds,\n customerGroupIds: [...ctx.buyer.customerGroupIds],\n currencyCode: ctx.currencyCode,\n quantity: overrides.quantity ?? 1,\n date: overrides.date ?? new Date(),\n }\n}\n\nfunction tryResolve<T>(container: StorefrontPricingContainer, name: string): T | null {\n try {\n const resolved = container.resolve(name) as T | null | undefined\n return resolved ?? null\n } catch {\n return null\n }\n}\n\nfunction roundAmount(value: number): number {\n const factor = 10 ** AMOUNT_PRECISION\n return Math.round(value * factor) / factor\n}\n\nfunction parseAmount(value: string | number | null | undefined): number | null {\n if (value === null || value === undefined || value === '') return null\n const numeric = typeof value === 'number' ? value : Number(value)\n return Number.isFinite(numeric) ? numeric : null\n}\n\nfunction formatAmount(amount: number, currencyCode: string, locale: string): string {\n return formatCurrency(amount, currencyCode, locale) ?? String(amount)\n}\n\nfunction resolveRowProductId(row: PriceRow, variantToProduct: Map<string, string>): string | null {\n const variantId = resolvePriceVariantId(row)\n if (variantId) return variantToProduct.get(variantId) ?? null\n if (!row.product) return null\n return typeof row.product === 'string' ? row.product : row.product.id ?? null\n}\n\ntype TaxRateCandidate = Pick<\n SalesTaxRate,\n 'rate' | 'customerGroupId' | 'channelId' | 'priority' | 'isDefault' | 'code' | 'startsAt' | 'endsAt'\n>\n\nfunction compareTaxRates(left: TaxRateCandidate, right: TaxRateCandidate): number {\n const channelDiff = (right.channelId ? 1 : 0) - (left.channelId ? 1 : 0)\n if (channelDiff !== 0) return channelDiff\n const defaultDiff = (right.isDefault ? 1 : 0) - (left.isDefault ? 1 : 0)\n if (defaultDiff !== 0) return defaultDiff\n const priorityDiff = (right.priority ?? 0) - (left.priority ?? 0)\n if (priorityDiff !== 0) return priorityDiff\n return left.code < right.code ? -1 : left.code > right.code ? 1 : 0\n}\n\n/**\n * Storefront Public API \u00A76.2 multi-group rule: `sales_tax_rates.customer_group_id` is single-valued,\n * so the rate of the highest-priority group (in `customerGroupIds` order) that has one wins; otherwise\n * the ungrouped rate applies.\n */\nexport function selectStorefrontTaxRate(\n rates: TaxRateCandidate[],\n customerGroupIds: string[],\n date: Date,\n): number | null {\n const active = rates.filter(\n (rate) => (!rate.startsAt || rate.startsAt <= date) && (!rate.endsAt || rate.endsAt >= date),\n )\n const pick = (candidates: TaxRateCandidate[]): number | null => {\n if (!candidates.length) return null\n const [best] = [...candidates].sort(compareTaxRates)\n return parseAmount(best.rate)\n }\n for (const groupId of customerGroupIds) {\n const groupRate = pick(active.filter((rate) => rate.customerGroupId === groupId))\n if (groupRate !== null) return groupRate\n }\n return pick(active.filter((rate) => !rate.customerGroupId))\n}\n\nfunction deriveSide(other: number, ratePercent: number, target: EcommercePriceDisplayMode): number {\n const factor = 1 + ratePercent / 100\n return roundAmount(target === 'gross' ? other * factor : other / factor)\n}\n\ntype AmountSource = {\n net: string | number | null | undefined\n gross: string | number | null | undefined\n rowTaxRate: string | number | null | undefined\n}\n\ntype PageRuntime = {\n ctx: StoreContext\n em: EntityManager\n pricingContext: StorefrontPricingContext\n taxMode: EcommercePriceDisplayMode\n fallbackRate: () => Promise<number | null>\n}\n\nasync function selectAmount(runtime: PageRuntime, source: AmountSource): Promise<number | null> {\n const wanted = parseAmount(runtime.taxMode === 'gross' ? source.gross : source.net)\n if (wanted !== null) return wanted\n const other = parseAmount(runtime.taxMode === 'gross' ? source.net : source.gross)\n if (other === null) return null\n const rowRate = parseAmount(source.rowTaxRate)\n const rate = rowRate ?? (await runtime.fallbackRate())\n if (rate === null) return null\n return deriveSide(other, rate, runtime.taxMode)\n}\n\nfunction rowAmountSource(row: PriceRow): AmountSource {\n return { net: row.unitPriceNet, gross: row.unitPriceGross, rowTaxRate: row.taxRate }\n}\n\nfunction createFallbackRateLoader(\n em: EntityManager,\n ctx: StoreContext,\n pricingContext: StorefrontPricingContext,\n): () => Promise<number | null> {\n let pending: Promise<number | null> | null = null\n return () => {\n if (!pending) {\n pending = (async () => {\n const scope = { tenantId: ctx.tenantId, organizationId: ctx.organizationId }\n const groupIds = pricingContext.customerGroupIds\n const where: FilterQuery<SalesTaxRate> = {\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n deletedAt: null,\n productCategoryId: null,\n $and: [\n pricingContext.channelId\n ? { $or: [{ channelId: null }, { channelId: pricingContext.channelId }] }\n : { channelId: null },\n groupIds.length\n ? { $or: [{ customerGroupId: null }, { customerGroupId: { $in: groupIds } }] }\n : { customerGroupId: null },\n ],\n }\n const rates = await findWithDecryption(em, SalesTaxRate, where, undefined, scope)\n return selectStorefrontTaxRate(rates, groupIds, pricingContext.date)\n })()\n }\n return pending\n }\n}\n\ntype ResolvedTarget = {\n key: string\n productId: string\n variantId: string | null\n rows: PriceRow[]\n selected: PriceRow | null\n}\n\nfunction variantTargetKey(productId: string, variantId: string): string {\n return `${productId}:${variantId}`\n}\n\nasync function resolveMany(\n service: CatalogPricingService | null,\n entries: Array<{ rows: PriceRow[]; context: PricingContext }>,\n): Promise<Array<PriceRow | null>> {\n if (!entries.length) return []\n if (service) return service.resolvePriceMany(entries)\n return entries.map(({ rows, context }) => selectBestPrice(rows, context))\n}\n\nfunction omnibusLowestSource(block: OmnibusBlock | null): AmountSource | null {\n if (!block || !block.applicable) return null\n if (block.lowestPriceNet === null && block.lowestPriceGross === null) return null\n return { net: block.lowestPriceNet, gross: block.lowestPriceGross, rowTaxRate: null }\n}\n\nasync function resolveOmnibusBlocksForPage(\n container: StorefrontPricingContainer,\n runtime: PageRuntime,\n targets: ResolvedTarget[],\n): Promise<Map<string, OmnibusBlock | null>> {\n const blocks = new Map<string, OmnibusBlock | null>()\n if (!targets.length) return blocks\n const service = tryResolve<CatalogOmnibusService>(container, 'catalogOmnibusService')\n if (!service) return blocks\n const { ctx, em } = runtime\n try {\n const presentedRows = targets.map((target) => target.selected).filter((row): row is PriceRow => row !== null)\n const presentedEntries = await resolveOmnibusPresentedEntries(em, presentedRows)\n const requests: OmnibusResolutionRequest[] = targets.map((target) => {\n const row = target.selected as PriceRow\n return {\n context: {\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n productId: target.productId,\n variantId: resolvePriceVariantId(row),\n offerId: resolvePriceOfferId(row),\n channelId: runtime.pricingContext.channelId,\n priceKindId: resolvePriceKindId(row),\n currencyCode: row.currencyCode,\n isStorefront: true,\n now: runtime.pricingContext.date,\n },\n presentedEntry: presentedEntries.get(row.id) ?? null,\n priceKindIsPromotion: true,\n }\n })\n const resolved = await service.resolveOmnibusBlocks(em, requests)\n targets.forEach((target, index) => blocks.set(target.key, resolved[index] ?? null))\n } catch (err) {\n logger.error('[internal] ecommerce storefront omnibus resolution failed', {\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n storeId: ctx.store.id,\n promotionCount: targets.length,\n err,\n })\n getTelemetryRuntime()?.reportError(err, {\n module: 'ecommerce',\n code: 'ecommerce.storefront_omnibus_failed',\n attributes: { promotionCount: targets.length },\n })\n blocks.clear()\n }\n return blocks\n}\n\nasync function buildPrice(\n runtime: PageRuntime,\n selected: PriceRow,\n original: PriceRow | null,\n omnibus: OmnibusBlock | null,\n): Promise<StorefrontPrice | null> {\n const amount = await selectAmount(runtime, rowAmountSource(selected))\n if (amount === null) return null\n const locale = runtime.ctx.effectiveLocale\n const currencyCode = selected.currencyCode\n let isPromotion = false\n let originalAmount: number | null = null\n let lowestPriorAmount: number | null = null\n if (isPromotionPriceKind(selected)) {\n const lowestSource = omnibusLowestSource(omnibus)\n lowestPriorAmount = lowestSource ? await selectAmount(runtime, lowestSource) : null\n if (lowestPriorAmount !== null) {\n isPromotion = true\n const originalValue = original ? await selectAmount(runtime, rowAmountSource(original)) : null\n originalAmount = originalValue !== null && originalValue > amount ? originalValue : null\n }\n }\n return {\n currencyCode,\n displayMode: runtime.taxMode,\n amount,\n formatted: formatAmount(amount, currencyCode, locale),\n isPromotion,\n originalAmount,\n formattedOriginal: originalAmount === null ? null : formatAmount(originalAmount, currencyCode, locale),\n lowestPriorAmount,\n formattedLowestPrior: lowestPriorAmount === null ? null : formatAmount(lowestPriorAmount, currencyCode, locale),\n }\n}\n\nfunction collectTierQuantities(rows: PriceRow[]): number[] {\n const quantities = new Set<number>([1])\n for (const row of rows) {\n const minQuantity = row.minQuantity ?? 1\n if (Number.isInteger(minQuantity) && minQuantity > 1) quantities.add(minQuantity)\n }\n return Array.from(quantities).sort((left, right) => left - right)\n}\n\nasync function buildPriceTiers(\n runtime: PageRuntime,\n quantities: number[],\n rows: Array<PriceRow | null>,\n): Promise<StorefrontPriceTier[]> {\n const collapsed: Array<{ minQuantity: number; amount: number; currencyCode: string }> = []\n for (let index = 0; index < quantities.length; index += 1) {\n const row = rows[index]\n if (!row) continue\n const amount = await selectAmount(runtime, rowAmountSource(row))\n if (amount === null) continue\n const previous = collapsed[collapsed.length - 1]\n if (previous && previous.amount === amount && previous.currencyCode === row.currencyCode) continue\n collapsed.push({ minQuantity: quantities[index], amount, currencyCode: row.currencyCode })\n }\n if (collapsed.length < 2) return []\n return collapsed.map((tier, index) => {\n const next = collapsed[index + 1]\n return {\n minQuantity: tier.minQuantity,\n maxQuantity: next ? next.minQuantity - 1 : null,\n amount: tier.amount,\n formatted: formatAmount(tier.amount, tier.currencyCode, runtime.ctx.effectiveLocale),\n }\n })\n}\n\nfunction buildPriceRange(runtime: PageRuntime, prices: Array<StorefrontPrice | null>): StorefrontPriceRange | null {\n const priced = prices.filter((price): price is StorefrontPrice => price !== null)\n if (!priced.length) return null\n const currencyCode = priced[0].currencyCode\n const amounts = priced.filter((price) => price.currencyCode === currencyCode).map((price) => price.amount)\n const min = Math.min(...amounts)\n const max = Math.max(...amounts)\n const locale = runtime.ctx.effectiveLocale\n return {\n min,\n max,\n formattedMin: formatAmount(min, currencyCode, locale),\n formattedMax: formatAmount(max, currencyCode, locale),\n }\n}\n\nasync function fetchPageRows(\n em: EntityManager,\n ctx: StoreContext,\n pricingContext: StorefrontPricingContext,\n productIds: string[],\n variantIds: string[],\n): Promise<PriceRow[]> {\n const targetClause: FilterQuery<CatalogProductPrice> = variantIds.length\n ? { $or: [{ product: { $in: productIds } }, { variant: { $in: variantIds } }] }\n : { product: { $in: productIds } }\n const where: FilterQuery<CatalogProductPrice> = {\n $and: [buildPriceRowFilter(pricingContext), targetClause],\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n }\n const scope = { tenantId: ctx.tenantId, organizationId: ctx.organizationId }\n const rows = await findWithDecryption(em, CatalogProductPrice, where, { populate: ['priceKind', 'offer'] }, scope)\n return rows as PriceRow[]\n}\n\n/**\n * Batched storefront price resolution (Storefront Public API rev 4 \u00A76.1, \u00A76.2, \u00A75.1, \u00A75.2; R13).\n *\n * One `CatalogProductPrice` query per page, narrowed by `buildPriceRowFilter`, resolved in memory\n * through `catalogPricingService` so registered resolvers apply. Rows of promotional price kinds overlay the\n * buyer's resolved `priceKindId` (pricing-engine amendment D2a), so a promotion wins by `scorePrice` as in admin;\n * `originalAmount` is the best non-promotional row of the resolved kind from the same batch. Promotions are\n * presented only when Omnibus supplies `lowestPriorAmount` for the presented price (R7): an unavailable, disabled or\n * not-applicable Omnibus result yields `isPromotion: false`, `originalAmount: null` and\n * `lowestPriorAmount: null` while `amount` stays the price the buyer actually pays. In detail mode quantity tiers\n * are resolved from the same batch per product and per variant (a variant's own rows plus product-level rows).\n */\nexport async function resolveStorefrontPrices(\n container: StorefrontPricingContainer,\n ctx: StoreContext,\n items: StorefrontPriceItem[],\n options: ResolveStorefrontPricesOptions = {},\n): Promise<Map<string, StorefrontProductPricing>> {\n const results = new Map<string, StorefrontProductPricing>()\n if (!items.length) return results\n const em = tryResolve<EntityManager>(container, 'em')\n if (!em) throw new Error('[internal] ecommerce storefront pricing requires the DI service em')\n const pricingService = tryResolve<CatalogPricingService>(container, 'catalogPricingService')\n const pricingContext = buildStorefrontPricingContext(ctx, { date: options.date })\n const runtime: PageRuntime = {\n ctx,\n em,\n pricingContext,\n taxMode: ctx.buyer.taxMode,\n fallbackRate: createFallbackRateLoader(em, ctx, pricingContext),\n }\n const detail = options.detail === true\n\n const variantToProduct = new Map<string, string>()\n for (const item of items) {\n for (const variantId of item.variantIds) variantToProduct.set(variantId, item.productId)\n }\n const productIds = Array.from(new Set(items.map((item) => item.productId)))\n const rows = await fetchPageRows(em, ctx, pricingContext, productIds, Array.from(variantToProduct.keys()))\n\n const rowsByProduct = new Map<string, PriceRow[]>()\n for (const row of rows) {\n const productId = resolveRowProductId(row, variantToProduct)\n if (!productId) continue\n const bucket = rowsByProduct.get(productId) ?? []\n bucket.push(row)\n rowsByProduct.set(productId, bucket)\n }\n\n const targets: ResolvedTarget[] = []\n const tierPlans = new Map<string, { quantities: number[]; start: number }>()\n const entries: Array<{ rows: PriceRow[]; context: PricingContext }> = []\n for (const item of items) {\n const productRows = rowsByProduct.get(item.productId) ?? []\n targets.push({ key: item.productId, productId: item.productId, variantId: null, rows: productRows, selected: null })\n entries.push({ rows: productRows, context: pricingContext })\n for (const variantId of item.variantIds) {\n const variantRows = productRows.filter((row) => {\n const rowVariantId = resolvePriceVariantId(row)\n return rowVariantId === null || rowVariantId === variantId\n })\n targets.push({\n key: variantTargetKey(item.productId, variantId),\n productId: item.productId,\n variantId,\n rows: variantRows,\n selected: null,\n })\n entries.push({ rows: variantRows, context: pricingContext })\n }\n }\n if (detail) {\n const tierProductIds = options.tierProductIds ? new Set(options.tierProductIds) : null\n for (const target of targets) {\n if (tierProductIds && !tierProductIds.has(target.productId)) continue\n const quantities = collectTierQuantities(target.rows)\n tierPlans.set(target.key, { quantities, start: entries.length })\n for (const quantity of quantities) entries.push({ rows: target.rows, context: { ...pricingContext, quantity } })\n }\n }\n\n const resolved = await resolveMany(pricingService, entries)\n const tiersFor = async (key: string): Promise<StorefrontPriceTier[]> => {\n const plan = tierPlans.get(key)\n if (!plan) return []\n return buildPriceTiers(runtime, plan.quantities, resolved.slice(plan.start, plan.start + plan.quantities.length))\n }\n targets.forEach((target, index) => {\n target.selected = resolved[index] ?? null\n })\n\n const promotional = targets.filter(\n (target) => target.selected !== null && isPromotionPriceKind(target.selected) && (detail || target.variantId === null),\n )\n const originalsResolved = await resolveMany(\n pricingService,\n promotional.map((target) => ({\n rows: target.rows.filter((row) => !isPromotionPriceKind(row)),\n context: pricingContext,\n })),\n )\n const originals = new Map<string, PriceRow | null>()\n promotional.forEach((target, index) => originals.set(target.key, originalsResolved[index] ?? null))\n const omnibusBlocks = await resolveOmnibusBlocksForPage(container, runtime, promotional)\n\n const prices = new Map<string, StorefrontPrice | null>()\n for (const target of targets) {\n prices.set(\n target.key,\n target.selected\n ? await buildPrice(\n runtime,\n target.selected,\n originals.get(target.key) ?? null,\n omnibusBlocks.get(target.key) ?? null,\n )\n : null,\n )\n }\n\n for (const item of items) {\n const variantPrices = new Map<string, StorefrontPrice | null>()\n const variantPriceTiers = new Map<string, StorefrontPriceTier[]>()\n for (const variantId of item.variantIds) {\n const key = variantTargetKey(item.productId, variantId)\n variantPrices.set(variantId, prices.get(key) ?? null)\n variantPriceTiers.set(variantId, await tiersFor(key))\n }\n results.set(item.productId, {\n productId: item.productId,\n price: prices.get(item.productId) ?? null,\n priceRange: item.variantIds.length ? buildPriceRange(runtime, Array.from(variantPrices.values())) : null,\n variantPrices,\n priceTiers: await tiersFor(item.productId),\n variantPriceTiers,\n })\n }\n return results\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,90 @@
1
+ import {
2
+ catalogCategoryTag,
3
+ catalogPriceTag,
4
+ catalogProductTag,
5
+ catalogProductsTag,
6
+ storefrontAvailabilityTag,
7
+ storefrontCache,
8
+ storefrontCacheValueHash
9
+ } from "./cacheKeys.js";
10
+ import {
11
+ getStorefrontProductDetail
12
+ } from "./storefrontDetail.js";
13
+ import { listStorefrontProducts } from "./storefrontProducts.js";
14
+ const STOREFRONT_PRODUCT_LIST_TTL_MS = 3e4;
15
+ const STOREFRONT_PRODUCT_DETAIL_TTL_MS = 6e4;
16
+ const LIST_CACHE_SEGMENT = "products-list";
17
+ const DETAIL_CACHE_SEGMENT = "products-detail";
18
+ const NULL_SEGMENT = "-";
19
+ const RESOLUTION_ONLY_PARAMETERS = /* @__PURE__ */ new Set(["path", "storeSlug"]);
20
+ function storefrontProductListCacheParts(query) {
21
+ const relevant = Object.fromEntries(
22
+ Object.entries(query).filter(([key]) => !RESOLUTION_ONLY_PARAMETERS.has(key))
23
+ );
24
+ return [LIST_CACHE_SEGMENT, storefrontCacheValueHash(relevant)];
25
+ }
26
+ function storefrontProductDetailCacheParts(idOrHandle, options) {
27
+ return [DETAIL_CACHE_SEGMENT, idOrHandle.trim(), options.variantId ?? NULL_SEGMENT, options.locale ?? NULL_SEGMENT];
28
+ }
29
+ function storefrontProductListCacheTags(ctx, response) {
30
+ const categoryId = response.appliedFilters.category?.id;
31
+ return [
32
+ catalogProductsTag(ctx.tenantId),
33
+ storefrontAvailabilityTag(ctx.tenantId),
34
+ ...categoryId ? [catalogCategoryTag(categoryId)] : []
35
+ ];
36
+ }
37
+ function storefrontProductDetailCacheTags(ctx, detail) {
38
+ const productIds = [detail.id, ...detail.relatedProducts.map((item) => item.id)];
39
+ const categoryIds = [
40
+ ...detail.categories.map((category) => category.id),
41
+ ...detail.breadcrumb.map((entry) => entry.id),
42
+ ...detail.relatedProducts.flatMap((item) => item.categories.map((category) => category.id))
43
+ ];
44
+ return Array.from(
45
+ /* @__PURE__ */ new Set([
46
+ ...productIds.map(catalogProductTag),
47
+ ...productIds.map(catalogPriceTag),
48
+ ...categoryIds.map(catalogCategoryTag),
49
+ storefrontAvailabilityTag(ctx.tenantId)
50
+ ])
51
+ );
52
+ }
53
+ async function cachedListStorefrontProducts(container, ctx, query) {
54
+ const cache = storefrontCache(container, ctx);
55
+ const parts = storefrontProductListCacheParts(query);
56
+ const cached = await cache.get(parts, { scope: "digest" });
57
+ if (cached) return { ...cached, requestedLocale: ctx.requestedLocale };
58
+ const response = await listStorefrontProducts(container, ctx, query);
59
+ await cache.set(parts, response, {
60
+ scope: "digest",
61
+ ttlMs: STOREFRONT_PRODUCT_LIST_TTL_MS,
62
+ tags: storefrontProductListCacheTags(ctx, response)
63
+ });
64
+ return response;
65
+ }
66
+ async function cachedGetStorefrontProductDetail(container, ctx, idOrHandle, options = {}) {
67
+ const cache = storefrontCache(container, ctx);
68
+ const parts = storefrontProductDetailCacheParts(idOrHandle, options);
69
+ const cached = await cache.get(parts, { scope: "digest" });
70
+ if (cached) return cached;
71
+ const detail = await getStorefrontProductDetail(container, ctx, idOrHandle, options);
72
+ if (!detail) return null;
73
+ await cache.set(parts, detail, {
74
+ scope: "digest",
75
+ ttlMs: STOREFRONT_PRODUCT_DETAIL_TTL_MS,
76
+ tags: storefrontProductDetailCacheTags(ctx, detail)
77
+ });
78
+ return detail;
79
+ }
80
+ export {
81
+ STOREFRONT_PRODUCT_DETAIL_TTL_MS,
82
+ STOREFRONT_PRODUCT_LIST_TTL_MS,
83
+ cachedGetStorefrontProductDetail,
84
+ cachedListStorefrontProducts,
85
+ storefrontProductDetailCacheParts,
86
+ storefrontProductDetailCacheTags,
87
+ storefrontProductListCacheParts,
88
+ storefrontProductListCacheTags
89
+ };
90
+ //# sourceMappingURL=storefrontProductCache.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/ecommerce/lib/storefrontProductCache.ts"],
4
+ "sourcesContent": ["import type { AwilixContainer } from 'awilix'\nimport type { EcommerceStorefrontProductListQuery } from '../data/validators'\nimport {\n catalogCategoryTag,\n catalogPriceTag,\n catalogProductTag,\n catalogProductsTag,\n storefrontAvailabilityTag,\n storefrontCache,\n storefrontCacheValueHash,\n} from './cacheKeys'\nimport {\n getStorefrontProductDetail,\n type GetStorefrontProductDetailOptions,\n type StorefrontProductDetail,\n} from './storefrontDetail'\nimport { listStorefrontProducts, type StorefrontProductListResponse } from './storefrontProducts'\nimport type { StoreContext } from './types'\n\n/**\n * Server-side cache for `GET /products` and `GET /products/:idOrHandle` (Storefront Public API\n * rev 4 \u00A79). Both are keyed on the full `digest` through `storefrontCache(container, ctx)`, so two\n * buyers share an entry only when every field that changes what they see or pay is equal; the\n * HTTP layer decides separately whether a browser or CDN may keep the response.\n *\n * Invalidation (subscribers, `cacheInvalidation.ts`): listings carry the tenant's\n * `catalog-products` collection tag, evicted by every product, variant, category and price event;\n * details carry per-product, per-price and per-category tags. Anything `catalog` emits no event for\n * (tags, offers, price kinds, option schemas, a deleted variant or price whose product can no longer\n * be read back, new related products) is picked up when the entry expires \u2014 30 s for listings,\n * 60 s for details \u2014 never on write.\n */\n\nexport const STOREFRONT_PRODUCT_LIST_TTL_MS = 30_000\nexport const STOREFRONT_PRODUCT_DETAIL_TTL_MS = 60_000\n\nconst LIST_CACHE_SEGMENT = 'products-list'\nconst DETAIL_CACHE_SEGMENT = 'products-detail'\nconst NULL_SEGMENT = '-'\nconst RESOLUTION_ONLY_PARAMETERS: ReadonlySet<string> = new Set(['path', 'storeSlug'])\n\n/**\n * The listing's normalized query: every parsed parameter except the store-resolution ones (the\n * resolved store is already part of the key), with object keys sorted, hashed to a fixed length.\n */\nexport function storefrontProductListCacheParts(query: EcommerceStorefrontProductListQuery): string[] {\n const relevant = Object.fromEntries(\n Object.entries(query).filter(([key]) => !RESOLUTION_ONLY_PARAMETERS.has(key)),\n )\n return [LIST_CACHE_SEGMENT, storefrontCacheValueHash(relevant)]\n}\n\nexport function storefrontProductDetailCacheParts(\n idOrHandle: string,\n options: Pick<GetStorefrontProductDetailOptions, 'variantId' | 'locale'>,\n): string[] {\n return [DETAIL_CACHE_SEGMENT, idOrHandle.trim(), options.variantId ?? NULL_SEGMENT, options.locale ?? NULL_SEGMENT]\n}\n\nexport function storefrontProductListCacheTags(\n ctx: StoreContext,\n response: Pick<StorefrontProductListResponse, 'appliedFilters'>,\n): string[] {\n const categoryId = response.appliedFilters.category?.id\n return [\n catalogProductsTag(ctx.tenantId),\n storefrontAvailabilityTag(ctx.tenantId),\n ...(categoryId ? [catalogCategoryTag(categoryId)] : []),\n ]\n}\n\nexport function storefrontProductDetailCacheTags(ctx: StoreContext, detail: StorefrontProductDetail): string[] {\n const productIds = [detail.id, ...detail.relatedProducts.map((item) => item.id)]\n const categoryIds = [\n ...detail.categories.map((category) => category.id),\n ...detail.breadcrumb.map((entry) => entry.id),\n ...detail.relatedProducts.flatMap((item) => item.categories.map((category) => category.id)),\n ]\n return Array.from(\n new Set([\n ...productIds.map(catalogProductTag),\n ...productIds.map(catalogPriceTag),\n ...categoryIds.map(catalogCategoryTag),\n storefrontAvailabilityTag(ctx.tenantId),\n ]),\n )\n}\n\nexport async function cachedListStorefrontProducts(\n container: AwilixContainer,\n ctx: StoreContext,\n query: EcommerceStorefrontProductListQuery,\n): Promise<StorefrontProductListResponse> {\n const cache = storefrontCache(container, ctx)\n const parts = storefrontProductListCacheParts(query)\n const cached = await cache.get<StorefrontProductListResponse>(parts, { scope: 'digest' })\n if (cached) return { ...cached, requestedLocale: ctx.requestedLocale }\n const response = await listStorefrontProducts(container, ctx, query)\n await cache.set(parts, response, {\n scope: 'digest',\n ttlMs: STOREFRONT_PRODUCT_LIST_TTL_MS,\n tags: storefrontProductListCacheTags(ctx, response),\n })\n return response\n}\n\n/** A `null` (not found or not visible) is never cached, so a 404 costs the same on every request. */\nexport async function cachedGetStorefrontProductDetail(\n container: AwilixContainer,\n ctx: StoreContext,\n idOrHandle: string,\n options: Pick<GetStorefrontProductDetailOptions, 'variantId' | 'locale'> = {},\n): Promise<StorefrontProductDetail | null> {\n const cache = storefrontCache(container, ctx)\n const parts = storefrontProductDetailCacheParts(idOrHandle, options)\n const cached = await cache.get<StorefrontProductDetail>(parts, { scope: 'digest' })\n if (cached) return cached\n const detail = await getStorefrontProductDetail(container, ctx, idOrHandle, options)\n if (!detail) return null\n await cache.set(parts, detail, {\n scope: 'digest',\n ttlMs: STOREFRONT_PRODUCT_DETAIL_TTL_MS,\n tags: storefrontProductDetailCacheTags(ctx, detail),\n })\n return detail\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }