@open-mercato/core 0.9.1-develop.7474.1.51db10479d → 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,642 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql'
2
+ import type { CacheStrategy } from '@open-mercato/cache'
3
+ import { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
4
+ import { createLogger } from '@open-mercato/shared/lib/logger'
5
+ import { getTelemetryRuntime } from '@open-mercato/shared/lib/telemetry/runtime'
6
+ import type { ModuleConfigService } from '@open-mercato/core/modules/configs/lib/module-config-service'
7
+ import { CatalogPriceHistoryEntry } from '../data/entities'
8
+ import {
9
+ OMNIBUS_CONFIG_MODULE_ID,
10
+ OMNIBUS_CONFIG_NAME,
11
+ OMNIBUS_DEFAULT_LOOKBACK_DAYS,
12
+ OMNIBUS_PRICE_REMOVING_UNDO_COMMAND,
13
+ omnibusConfigSchema,
14
+ type OmnibusApplicabilityReason,
15
+ type OmnibusBlock,
16
+ type OmnibusConfig,
17
+ type OmnibusHistoryRow,
18
+ type OmnibusLowestPriceResult,
19
+ type OmnibusMinimizationAxis,
20
+ type OmnibusPresentedEntry,
21
+ type OmnibusResolutionContext,
22
+ type OmnibusResolutionRequest,
23
+ } from '../lib/omnibusTypes'
24
+ import {
25
+ buildOmnibusCacheKey,
26
+ buildOmnibusCacheTags,
27
+ readOmnibusCache,
28
+ writeOmnibusCache,
29
+ } from '../lib/omnibusCache'
30
+ import {
31
+ fetchOmnibusFirstOfferIds,
32
+ fetchOmnibusWindowIds,
33
+ type OmnibusFirstOfferLookup,
34
+ type OmnibusScopeColumn,
35
+ type OmnibusWindowLookup,
36
+ } from '../lib/omnibusHistoryQueries'
37
+ import { formatFixedDecimal4 } from '../lib/priceHistoryQuery'
38
+
39
+ const logger = createLogger('catalog')
40
+
41
+ const DAY_MS = 24 * 60 * 60 * 1000
42
+
43
+ const DECIMAL_SCALE = 4
44
+
45
+ export type OmnibusConfigScope = {
46
+ tenantId: string
47
+ organizationId?: string | null
48
+ }
49
+
50
+ export interface CatalogOmnibusService {
51
+ getConfig(scope: OmnibusConfigScope): Promise<OmnibusConfig | null>
52
+ resolvePresentedPriceKindId(
53
+ config: OmnibusConfig,
54
+ channelId: string | null | undefined,
55
+ fallbackPriceKindId?: string | null,
56
+ ): string | null
57
+ computeLowestPrice(
58
+ em: EntityManager,
59
+ context: OmnibusResolutionContext,
60
+ config: OmnibusConfig,
61
+ presentedEntry?: OmnibusPresentedEntry | null,
62
+ ): Promise<OmnibusLowestPriceResult>
63
+ computeLowestPrices(
64
+ em: EntityManager,
65
+ entries: Array<{ context: OmnibusResolutionContext; config: OmnibusConfig; presentedEntry?: OmnibusPresentedEntry | null }>,
66
+ ): Promise<OmnibusLowestPriceResult[]>
67
+ resolveOmnibusBlock(
68
+ em: EntityManager,
69
+ context: OmnibusResolutionContext,
70
+ presentedEntry?: OmnibusPresentedEntry | null,
71
+ priceKindIsPromotion?: boolean,
72
+ ): Promise<OmnibusBlock | null>
73
+ resolveOmnibusBlocks(em: EntityManager, requests: OmnibusResolutionRequest[]): Promise<Array<OmnibusBlock | null>>
74
+ }
75
+
76
+ type HistoryScope = { column: OmnibusScopeColumn; id: string }
77
+
78
+ type PreparedLookup = {
79
+ index: number
80
+ context: OmnibusResolutionContext
81
+ presentedEntry: OmnibusPresentedEntry | null
82
+ presentedPriceKindId: string
83
+ scope: HistoryScope
84
+ channelId: string | null
85
+ lookbackDays: number
86
+ axis: OmnibusMinimizationAxis
87
+ offerId: string | null
88
+ anchor: Date | null
89
+ now: Date
90
+ }
91
+
92
+ type WindowedLookup = PreparedLookup & {
93
+ windowStart: Date
94
+ windowEnd: Date
95
+ cacheKey: string
96
+ }
97
+
98
+ type CachedFirstOffer = { recordedAt: string | null }
99
+
100
+ function toDate(value: Date | string | null | undefined): Date | null {
101
+ if (value === null || value === undefined) return null
102
+ const date = value instanceof Date ? value : new Date(value)
103
+ return Number.isNaN(date.getTime()) ? null : date
104
+ }
105
+
106
+ function truncToUtcDay(date: Date): string {
107
+ return date.toISOString().slice(0, 10)
108
+ }
109
+
110
+ function parseScaledDecimal(value: string | null): bigint | null {
111
+ if (value === null) return null
112
+ const match = /^(-?)(\d+)(?:\.(\d*))?$/.exec(value.trim())
113
+ if (!match) return null
114
+ const [, sign, whole, fraction = ''] = match
115
+ const scaled = BigInt(`${whole}${fraction.slice(0, DECIMAL_SCALE).padEnd(DECIMAL_SCALE, '0')}`)
116
+ return sign === '-' ? -scaled : scaled
117
+ }
118
+
119
+ function priceValue(row: OmnibusHistoryRow, axis: OmnibusMinimizationAxis): bigint | null {
120
+ return parseScaledDecimal(axis === 'gross' ? row.unitPriceGross : row.unitPriceNet)
121
+ }
122
+
123
+ function compareChronological(left: OmnibusHistoryRow, right: OmnibusHistoryRow): number {
124
+ const delta = Date.parse(left.recordedAt) - Date.parse(right.recordedAt)
125
+ if (delta !== 0) return delta
126
+ return left.id < right.id ? -1 : left.id > right.id ? 1 : 0
127
+ }
128
+
129
+ function compareByAxis(left: OmnibusHistoryRow, right: OmnibusHistoryRow, axis: OmnibusMinimizationAxis): number {
130
+ const leftValue = priceValue(left, axis)
131
+ const rightValue = priceValue(right, axis)
132
+ if (leftValue === null && rightValue !== null) return 1
133
+ if (leftValue !== null && rightValue === null) return -1
134
+ if (leftValue !== null && rightValue !== null && leftValue !== rightValue) return leftValue < rightValue ? -1 : 1
135
+ return compareChronological(left, right)
136
+ }
137
+
138
+ function isPresentedReduction(row: OmnibusHistoryRow, presented: OmnibusPresentedEntry | null): boolean {
139
+ if (!presented) return false
140
+ const presentedAt = toDate(presented.recordedAt)
141
+ return (
142
+ row.priceId === presented.priceId &&
143
+ row.changeType === presented.changeType &&
144
+ presentedAt !== null &&
145
+ Date.parse(row.recordedAt) === presentedAt.getTime()
146
+ )
147
+ }
148
+
149
+ function isPriceObservation(row: OmnibusHistoryRow): boolean {
150
+ if (row.changeType === 'delete') return false
151
+ return !(row.changeType === 'undo' && row.undoneCommand === OMNIBUS_PRICE_REMOVING_UNDO_COMMAND)
152
+ }
153
+
154
+ export type OmnibusCandidateSelection = {
155
+ lowestRow: OmnibusHistoryRow | null
156
+ previousRow: OmnibusHistoryRow | null
157
+ insufficientHistory: boolean
158
+ coverageStartAt: string | null
159
+ }
160
+
161
+ export function selectOmnibusCandidates(input: {
162
+ baseline: OmnibusHistoryRow | null
163
+ inWindow: OmnibusHistoryRow[]
164
+ presentedEntry: OmnibusPresentedEntry | null
165
+ anchor: Date | null
166
+ axis: OmnibusMinimizationAxis
167
+ }): OmnibusCandidateSelection {
168
+ const anchorMs = input.anchor ? input.anchor.getTime() : null
169
+ const pool = input.baseline ? [input.baseline, ...input.inWindow] : [...input.inWindow]
170
+ const candidates = pool.filter(
171
+ (row) =>
172
+ isPriceObservation(row) &&
173
+ !isPresentedReduction(row, input.presentedEntry) &&
174
+ (anchorMs === null || Date.parse(row.recordedAt) < anchorMs),
175
+ )
176
+ if (!candidates.length) {
177
+ return { lowestRow: null, previousRow: null, insufficientHistory: false, coverageStartAt: null }
178
+ }
179
+ const lowest = [...candidates].sort((left, right) => compareByAxis(left, right, input.axis))[0]
180
+ const lowestRow = priceValue(lowest, input.axis) === null ? null : lowest
181
+ const baselineKept = input.baseline !== null && candidates.includes(input.baseline)
182
+ if (baselineKept) {
183
+ return { lowestRow, previousRow: input.baseline, insufficientHistory: false, coverageStartAt: null }
184
+ }
185
+ const oldest = [...candidates].sort(compareChronological)[0]
186
+ return { lowestRow, previousRow: oldest, insufficientHistory: true, coverageStartAt: oldest.recordedAt }
187
+ }
188
+
189
+ function toHistoryRow(entry: CatalogPriceHistoryEntry): OmnibusHistoryRow {
190
+ const recordedAt = entry.recordedAt instanceof Date ? entry.recordedAt : new Date(entry.recordedAt)
191
+ const undoneCommand = undoneCommandOf(entry.metadata)
192
+ return {
193
+ id: entry.id,
194
+ priceId: entry.priceId,
195
+ changeType: entry.changeType,
196
+ recordedAt: recordedAt.toISOString(),
197
+ unitPriceNet: entry.unitPriceNet ?? null,
198
+ unitPriceGross: entry.unitPriceGross ?? null,
199
+ ...(undoneCommand ? { undoneCommand } : {}),
200
+ }
201
+ }
202
+
203
+ function undoneCommandOf(metadata: Record<string, unknown> | null | undefined): string | null {
204
+ const value = metadata?.undoneCommand
205
+ return typeof value === 'string' ? value : null
206
+ }
207
+
208
+ function resolveHistoryScope(context: OmnibusResolutionContext): HistoryScope | null {
209
+ if (context.offerId) return { column: 'offer_id', id: context.offerId }
210
+ if (context.variantId) return { column: 'variant_id', id: context.variantId }
211
+ if (context.productId) return { column: 'product_id', id: context.productId }
212
+ return null
213
+ }
214
+
215
+ function earlyResult(
216
+ reason: OmnibusApplicabilityReason,
217
+ presentedPriceKindId: string | null,
218
+ lookbackDays: number,
219
+ axis: OmnibusMinimizationAxis,
220
+ ): OmnibusLowestPriceResult {
221
+ return {
222
+ reason,
223
+ presentedPriceKindId,
224
+ lowestRow: null,
225
+ previousRow: null,
226
+ insufficientHistory: false,
227
+ promotionAnchorAt: null,
228
+ coverageStartAt: null,
229
+ windowStart: null,
230
+ windowEnd: null,
231
+ lookbackDays,
232
+ minimizationAxis: axis,
233
+ }
234
+ }
235
+
236
+ function emptyBlock(result: OmnibusLowestPriceResult, currencyCode: string, reason: OmnibusApplicabilityReason): OmnibusBlock {
237
+ return {
238
+ presentedPriceKindId: result.presentedPriceKindId,
239
+ lookbackDays: result.lookbackDays,
240
+ minimizationAxis: result.minimizationAxis,
241
+ promotionAnchorAt: result.promotionAnchorAt,
242
+ windowStart: result.windowStart,
243
+ windowEnd: result.windowEnd,
244
+ coverageStartAt: null,
245
+ lowestPriceNet: null,
246
+ lowestPriceGross: null,
247
+ previousPriceNet: null,
248
+ previousPriceGross: null,
249
+ currencyCode,
250
+ applicable: false,
251
+ applicabilityReason: reason,
252
+ }
253
+ }
254
+
255
+ export function buildOmnibusBlock(
256
+ result: OmnibusLowestPriceResult,
257
+ context: OmnibusResolutionContext,
258
+ presentedEntry: OmnibusPresentedEntry | null,
259
+ priceKindIsPromotion: boolean,
260
+ ): OmnibusBlock {
261
+ if (result.reason === 'not_in_eu_market' || result.reason === 'missing_channel_context') {
262
+ return emptyBlock(result, context.currencyCode, result.reason)
263
+ }
264
+ if (!result.lowestRow) return emptyBlock(result, context.currencyCode, result.reason ?? 'no_history')
265
+ const applicable =
266
+ Boolean(presentedEntry?.startsAt) ||
267
+ Boolean(presentedEntry?.offerId) ||
268
+ presentedEntry?.isAnnounced === true ||
269
+ priceKindIsPromotion
270
+ const reason: OmnibusApplicabilityReason =
271
+ result.reason ?? (result.insufficientHistory ? 'insufficient_history' : applicable ? 'announced_promotion' : 'not_announced')
272
+ return {
273
+ presentedPriceKindId: result.presentedPriceKindId,
274
+ lookbackDays: result.lookbackDays,
275
+ minimizationAxis: result.minimizationAxis,
276
+ promotionAnchorAt: result.promotionAnchorAt,
277
+ windowStart: result.windowStart,
278
+ windowEnd: result.windowEnd,
279
+ coverageStartAt: result.coverageStartAt,
280
+ lowestPriceNet: formatFixedDecimal4(result.lowestRow.unitPriceNet),
281
+ lowestPriceGross: formatFixedDecimal4(result.lowestRow.unitPriceGross),
282
+ previousPriceNet: formatFixedDecimal4(result.previousRow?.unitPriceNet ?? null),
283
+ previousPriceGross: formatFixedDecimal4(result.previousRow?.unitPriceGross ?? null),
284
+ currencyCode: context.currencyCode,
285
+ applicable,
286
+ applicabilityReason: reason,
287
+ }
288
+ }
289
+
290
+ function presentedIdentity(presented: OmnibusPresentedEntry | null): string {
291
+ if (!presented) return 'none'
292
+ const recordedAt = toDate(presented.recordedAt)
293
+ return `${presented.priceId}|${presented.changeType}|${recordedAt ? recordedAt.toISOString() : 'invalid'}`
294
+ }
295
+
296
+ function scopeTargets(context: OmnibusResolutionContext) {
297
+ return { productId: context.productId ?? null, variantId: context.variantId ?? null, offerId: context.offerId ?? null }
298
+ }
299
+
300
+ export class DefaultCatalogOmnibusService implements CatalogOmnibusService {
301
+ constructor(
302
+ private readonly moduleConfigService: ModuleConfigService | null,
303
+ private readonly cache: CacheStrategy | null,
304
+ ) {}
305
+
306
+ async getConfig(scope: OmnibusConfigScope): Promise<OmnibusConfig | null> {
307
+ if (!this.moduleConfigService) return null
308
+ const raw = await this.moduleConfigService.getValue<unknown>(OMNIBUS_CONFIG_MODULE_ID, OMNIBUS_CONFIG_NAME, {
309
+ scope: { tenantId: scope.tenantId, organizationId: scope.organizationId ?? null },
310
+ })
311
+ if (raw === null || raw === undefined) return null
312
+ const parsed = omnibusConfigSchema.safeParse(raw)
313
+ if (!parsed.success) {
314
+ logger.warn('[internal] catalog omnibus config is invalid; treating omnibus as disabled', {
315
+ tenantId: scope.tenantId,
316
+ organizationId: scope.organizationId ?? null,
317
+ issues: parsed.error.issues.map((issue) => issue.path.join('.')),
318
+ })
319
+ return null
320
+ }
321
+ return parsed.data
322
+ }
323
+
324
+ resolvePresentedPriceKindId(
325
+ config: OmnibusConfig,
326
+ channelId: string | null | undefined,
327
+ fallbackPriceKindId?: string | null,
328
+ ): string | null {
329
+ const channelConfig = channelId ? config.channels[channelId] : undefined
330
+ return channelConfig?.presentedPriceKindId ?? config.defaultPresentedPriceKindId ?? fallbackPriceKindId ?? null
331
+ }
332
+
333
+ async computeLowestPrice(
334
+ em: EntityManager,
335
+ context: OmnibusResolutionContext,
336
+ config: OmnibusConfig,
337
+ presentedEntry: OmnibusPresentedEntry | null = null,
338
+ ): Promise<OmnibusLowestPriceResult> {
339
+ const [result] = await this.computeLowestPrices(em, [{ context, config, presentedEntry }])
340
+ return result
341
+ }
342
+
343
+ async computeLowestPrices(
344
+ em: EntityManager,
345
+ entries: Array<{ context: OmnibusResolutionContext; config: OmnibusConfig; presentedEntry?: OmnibusPresentedEntry | null }>,
346
+ ): Promise<OmnibusLowestPriceResult[]> {
347
+ const results: Array<OmnibusLowestPriceResult | null> = entries.map(() => null)
348
+ const prepared: PreparedLookup[] = []
349
+ entries.forEach((entry, index) => {
350
+ const gated = this.prepareLookup(index, entry.context, entry.config, entry.presentedEntry ?? null)
351
+ if ('reason' in gated) results[index] = gated
352
+ else prepared.push(gated)
353
+ })
354
+ await this.resolveOfferAnchors(em, prepared)
355
+ const windowed = prepared.map((lookup) => this.applyWindow(lookup))
356
+ const pending: WindowedLookup[] = []
357
+ for (const lookup of windowed) {
358
+ const cached = await readOmnibusCache<OmnibusLowestPriceResult>(this.cache, lookup.context.tenantId, lookup.cacheKey)
359
+ if (cached) results[lookup.index] = cached
360
+ else pending.push(lookup)
361
+ }
362
+ if (pending.length) {
363
+ const computed = await this.computeFromHistory(em, pending)
364
+ for (let position = 0; position < pending.length; position += 1) {
365
+ const lookup = pending[position]
366
+ const result = computed[position]
367
+ results[lookup.index] = result
368
+ await writeOmnibusCache(
369
+ this.cache,
370
+ lookup.context.tenantId,
371
+ lookup.cacheKey,
372
+ result,
373
+ buildOmnibusCacheTags(lookup.context, scopeTargets(lookup.context)),
374
+ )
375
+ }
376
+ }
377
+ return results.map((result, index) => {
378
+ if (result) return result
379
+ const entry = entries[index]
380
+ return earlyResult('no_history', null, entry.config.lookbackDays, entry.config.minimizationAxis)
381
+ })
382
+ }
383
+
384
+ async resolveOmnibusBlock(
385
+ em: EntityManager,
386
+ context: OmnibusResolutionContext,
387
+ presentedEntry: OmnibusPresentedEntry | null = null,
388
+ priceKindIsPromotion = false,
389
+ ): Promise<OmnibusBlock | null> {
390
+ const [block] = await this.resolveOmnibusBlocks(em, [{ context, presentedEntry, priceKindIsPromotion }])
391
+ return block ?? null
392
+ }
393
+
394
+ async resolveOmnibusBlocks(em: EntityManager, requests: OmnibusResolutionRequest[]): Promise<Array<OmnibusBlock | null>> {
395
+ const blocks: Array<OmnibusBlock | null> = requests.map(() => null)
396
+ if (!requests.length) return blocks
397
+ const configs = await this.loadConfigs(requests)
398
+ const enabled: Array<{ index: number; config: OmnibusConfig }> = []
399
+ requests.forEach((request, index) => {
400
+ const config = configs.get(configKey(request.context)) ?? null
401
+ if (config && config.enabled === true) enabled.push({ index, config })
402
+ })
403
+ if (!enabled.length) return blocks
404
+ try {
405
+ const results = await this.computeLowestPrices(
406
+ em,
407
+ enabled.map(({ index, config }) => ({
408
+ context: requests[index].context,
409
+ config,
410
+ presentedEntry: requests[index].presentedEntry ?? null,
411
+ })),
412
+ )
413
+ enabled.forEach(({ index }, position) => {
414
+ const request = requests[index]
415
+ blocks[index] = buildOmnibusBlock(
416
+ results[position],
417
+ request.context,
418
+ request.presentedEntry ?? null,
419
+ request.priceKindIsPromotion === true,
420
+ )
421
+ })
422
+ } catch (err) {
423
+ const first = requests[enabled[0].index].context
424
+ logger.error('[internal] catalog omnibus resolution failed', {
425
+ tenantId: first.tenantId,
426
+ organizationId: first.organizationId,
427
+ channelId: first.channelId ?? null,
428
+ priceKindId: first.priceKindId ?? null,
429
+ currencyCode: first.currencyCode,
430
+ requestCount: enabled.length,
431
+ err,
432
+ })
433
+ getTelemetryRuntime()?.reportError(err, {
434
+ module: 'catalog',
435
+ code: 'catalog.omnibus_resolution_failed',
436
+ attributes: { requestCount: enabled.length },
437
+ })
438
+ return requests.map(() => null)
439
+ }
440
+ return blocks
441
+ }
442
+
443
+ private async loadConfigs(requests: OmnibusResolutionRequest[]): Promise<Map<string, OmnibusConfig | null>> {
444
+ const configs = new Map<string, OmnibusConfig | null>()
445
+ for (const request of requests) {
446
+ const key = configKey(request.context)
447
+ if (configs.has(key)) continue
448
+ configs.set(
449
+ key,
450
+ await this.getConfig({ tenantId: request.context.tenantId, organizationId: request.context.organizationId }),
451
+ )
452
+ }
453
+ return configs
454
+ }
455
+
456
+ private prepareLookup(
457
+ index: number,
458
+ context: OmnibusResolutionContext,
459
+ config: OmnibusConfig,
460
+ presentedEntry: OmnibusPresentedEntry | null,
461
+ ): PreparedLookup | OmnibusLowestPriceResult {
462
+ const channelId = context.channelId ?? null
463
+ const channelConfig = channelId ? config.channels[channelId] : undefined
464
+ const lookbackDays = channelConfig?.lookbackDays ?? config.lookbackDays ?? OMNIBUS_DEFAULT_LOOKBACK_DAYS
465
+ const axis = channelConfig?.minimizationAxis ?? config.minimizationAxis ?? 'gross'
466
+ const presentedPriceKindId = this.resolvePresentedPriceKindId(config, channelId, context.priceKindId)
467
+ if (config.enabled !== true) return earlyResult('no_history', presentedPriceKindId, lookbackDays, axis)
468
+ const codes = config.enabledCountryCodes ?? []
469
+ if (!codes.length) return earlyResult('not_in_eu_market', presentedPriceKindId, lookbackDays, axis)
470
+ if (!channelId) {
471
+ const mode = context.isStorefront === true ? 'require_channel' : config.noChannelMode ?? 'best_effort'
472
+ if (mode === 'require_channel') {
473
+ return earlyResult('missing_channel_context', presentedPriceKindId, lookbackDays, axis)
474
+ }
475
+ } else {
476
+ const countryCode = channelConfig?.countryCode
477
+ if (!countryCode || !codes.includes(countryCode)) {
478
+ return earlyResult('not_in_eu_market', presentedPriceKindId, lookbackDays, axis)
479
+ }
480
+ }
481
+ const scope = resolveHistoryScope(context)
482
+ if (!scope || !presentedPriceKindId) return earlyResult('no_history', presentedPriceKindId, lookbackDays, axis)
483
+ return {
484
+ index,
485
+ context,
486
+ presentedEntry,
487
+ presentedPriceKindId,
488
+ scope,
489
+ channelId,
490
+ lookbackDays,
491
+ axis,
492
+ offerId: context.offerId ?? presentedEntry?.offerId ?? null,
493
+ anchor: toDate(presentedEntry?.startsAt ?? null),
494
+ now: context.now ?? new Date(),
495
+ }
496
+ }
497
+
498
+ private async resolveOfferAnchors(em: EntityManager, prepared: PreparedLookup[]): Promise<void> {
499
+ const needing = prepared.filter((lookup) => lookup.anchor === null && lookup.offerId !== null)
500
+ if (!needing.length) return
501
+ const misses: Array<{ lookup: PreparedLookup; key: string }> = []
502
+ for (const lookup of needing) {
503
+ const key = firstOfferCacheKey(lookup)
504
+ const cached = await readOmnibusCache<CachedFirstOffer>(this.cache, lookup.context.tenantId, key)
505
+ if (cached) lookup.anchor = toDate(cached.recordedAt)
506
+ else misses.push({ lookup, key })
507
+ }
508
+ if (!misses.length) return
509
+ const offerLookups: OmnibusFirstOfferLookup[] = misses.map(({ lookup }) => ({
510
+ tenantId: lookup.context.tenantId,
511
+ organizationId: lookup.context.organizationId,
512
+ offerId: lookup.offerId as string,
513
+ priceKindId: lookup.presentedPriceKindId,
514
+ currencyCode: lookup.context.currencyCode,
515
+ channelId: lookup.channelId,
516
+ }))
517
+ const ids = await fetchOmnibusFirstOfferIds(em, offerLookups)
518
+ const rows = await this.hydrate(em, misses.map(({ lookup }) => lookup.context), ids.filter(isPresent))
519
+ for (let position = 0; position < misses.length; position += 1) {
520
+ const { lookup, key } = misses[position]
521
+ const id = ids[position]
522
+ const row = id ? rows.get(id) ?? null : null
523
+ lookup.anchor = row ? new Date(row.recordedAt) : null
524
+ await writeOmnibusCache(
525
+ this.cache,
526
+ lookup.context.tenantId,
527
+ key,
528
+ { recordedAt: row ? row.recordedAt : null } satisfies CachedFirstOffer,
529
+ buildOmnibusCacheTags(lookup.context, { ...scopeTargets(lookup.context), offerId: lookup.offerId }),
530
+ )
531
+ }
532
+ }
533
+
534
+ private applyWindow(lookup: PreparedLookup): WindowedLookup {
535
+ const windowEnd = lookup.anchor ?? lookup.now
536
+ const windowStart = new Date(windowEnd.getTime() - lookup.lookbackDays * DAY_MS)
537
+ const cacheKey = buildOmnibusCacheKey('lowest', [
538
+ lookup.context.tenantId,
539
+ lookup.context.organizationId,
540
+ `${lookup.scope.column}=${lookup.scope.id}`,
541
+ lookup.channelId ?? 'none',
542
+ lookup.presentedPriceKindId,
543
+ lookup.context.currencyCode,
544
+ lookup.axis,
545
+ lookup.lookbackDays,
546
+ truncToUtcDay(windowStart),
547
+ lookup.anchor ? truncToUtcDay(lookup.anchor) : 'none',
548
+ lookup.anchor ? lookup.anchor.toISOString() : 'sliding',
549
+ presentedIdentity(lookup.presentedEntry),
550
+ ])
551
+ return { ...lookup, windowStart, windowEnd, cacheKey }
552
+ }
553
+
554
+ private async computeFromHistory(em: EntityManager, lookups: WindowedLookup[]): Promise<OmnibusLowestPriceResult[]> {
555
+ const windowLookups: OmnibusWindowLookup[] = lookups.map((lookup) => ({
556
+ tenantId: lookup.context.tenantId,
557
+ organizationId: lookup.context.organizationId,
558
+ scopeColumn: lookup.scope.column,
559
+ scopeId: lookup.scope.id,
560
+ priceKindId: lookup.presentedPriceKindId,
561
+ currencyCode: lookup.context.currencyCode,
562
+ channelId: lookup.channelId,
563
+ windowStart: lookup.windowStart,
564
+ windowEnd: lookup.windowEnd,
565
+ }))
566
+ const idSets = await fetchOmnibusWindowIds(em, windowLookups)
567
+ const allIds = new Set<string>()
568
+ for (const set of idSets) {
569
+ if (set.baselineId) allIds.add(set.baselineId)
570
+ for (const id of set.inWindowIds) allIds.add(id)
571
+ }
572
+ const rows = await this.hydrate(em, lookups.map((lookup) => lookup.context), Array.from(allIds))
573
+ return lookups.map((lookup, position) => {
574
+ const set = idSets[position]
575
+ const baseline = set.baselineId ? rows.get(set.baselineId) ?? null : null
576
+ const inWindow = set.inWindowIds.map((id) => rows.get(id)).filter(isPresent)
577
+ const selection = selectOmnibusCandidates({
578
+ baseline,
579
+ inWindow,
580
+ presentedEntry: lookup.presentedEntry,
581
+ anchor: lookup.anchor,
582
+ axis: lookup.axis,
583
+ })
584
+ return {
585
+ reason: null,
586
+ presentedPriceKindId: lookup.presentedPriceKindId,
587
+ lowestRow: selection.lowestRow,
588
+ previousRow: selection.previousRow,
589
+ insufficientHistory: selection.insufficientHistory,
590
+ promotionAnchorAt: lookup.anchor ? lookup.anchor.toISOString() : null,
591
+ coverageStartAt: selection.coverageStartAt,
592
+ windowStart: lookup.windowStart.toISOString(),
593
+ windowEnd: lookup.windowEnd.toISOString(),
594
+ lookbackDays: lookup.lookbackDays,
595
+ minimizationAxis: lookup.axis,
596
+ }
597
+ })
598
+ }
599
+
600
+ private async hydrate(
601
+ em: EntityManager,
602
+ contexts: OmnibusResolutionContext[],
603
+ ids: string[],
604
+ ): Promise<Map<string, OmnibusHistoryRow>> {
605
+ const rows = new Map<string, OmnibusHistoryRow>()
606
+ if (!ids.length) return rows
607
+ const scopes = new Map<string, { tenantId: string; organizationId: string }>()
608
+ for (const context of contexts) {
609
+ scopes.set(configKey(context), { tenantId: context.tenantId, organizationId: context.organizationId })
610
+ }
611
+ for (const scope of scopes.values()) {
612
+ const entries = await findWithDecryption(
613
+ em,
614
+ CatalogPriceHistoryEntry,
615
+ { id: { $in: ids }, tenantId: scope.tenantId, organizationId: scope.organizationId },
616
+ undefined,
617
+ scope,
618
+ )
619
+ for (const entry of entries) rows.set(entry.id, toHistoryRow(entry))
620
+ }
621
+ return rows
622
+ }
623
+ }
624
+
625
+ function configKey(context: Pick<OmnibusResolutionContext, 'tenantId' | 'organizationId'>): string {
626
+ return `${context.tenantId}|${context.organizationId}`
627
+ }
628
+
629
+ function firstOfferCacheKey(lookup: PreparedLookup): string {
630
+ return buildOmnibusCacheKey('first-offer', [
631
+ lookup.context.tenantId,
632
+ lookup.context.organizationId,
633
+ lookup.offerId,
634
+ lookup.presentedPriceKindId,
635
+ lookup.context.currencyCode,
636
+ lookup.channelId ?? 'none',
637
+ ])
638
+ }
639
+
640
+ function isPresent<T>(value: T | null | undefined): value is T {
641
+ return value !== null && value !== undefined
642
+ }
@@ -22,6 +22,8 @@ export const setup: ModuleSetupConfig = {
22
22
  'catalog.categories.manage',
23
23
  'catalog.variants.manage',
24
24
  'catalog.pricing.manage',
25
+ 'catalog.price_history.view',
26
+ 'catalog.settings.view',
25
27
  ],
26
28
  },
27
29
  }
@@ -0,0 +1,18 @@
1
+ import {
2
+ reindexProductsForCategorySubtree,
3
+ type CategoryScopeChangePayload,
4
+ type ProductScopeReindexContext,
5
+ } from '../lib/productScopeReindex'
6
+
7
+ export const metadata = {
8
+ event: 'catalog.category.deleted',
9
+ persistent: true,
10
+ id: 'catalog:product-scope-keys-category-deleted',
11
+ }
12
+
13
+ export default async function handle(
14
+ payload: CategoryScopeChangePayload,
15
+ ctx: ProductScopeReindexContext,
16
+ ): Promise<void> {
17
+ await reindexProductsForCategorySubtree(payload, ctx)
18
+ }
@@ -0,0 +1,19 @@
1
+ import {
2
+ reindexProductsForCategorySubtree,
3
+ type CategoryScopeChangePayload,
4
+ type ProductScopeReindexContext,
5
+ } from '../lib/productScopeReindex'
6
+
7
+ export const metadata = {
8
+ event: 'catalog.category.updated',
9
+ persistent: true,
10
+ id: 'catalog:product-scope-keys-category-updated',
11
+ }
12
+
13
+ export default async function handle(
14
+ payload: CategoryScopeChangePayload,
15
+ ctx: ProductScopeReindexContext,
16
+ ): Promise<void> {
17
+ if (payload?.hierarchyChanged !== true) return
18
+ await reindexProductsForCategorySubtree(payload, ctx)
19
+ }