@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,112 @@
1
+ import { parseBooleanToken } from "@open-mercato/shared/lib/boolean";
2
+ import { createLogger } from "@open-mercato/shared/lib/logger";
3
+ import { PRODUCT_SCOPE_KEYS_DOC_KEY } from "@open-mercato/core/modules/catalog/lib/productScopeKeys";
4
+ import { E } from "../../../generated/entities.ids.generated.js";
5
+ import { ecommerceAssortmentScopeSchema } from "../data/validators.js";
6
+ import { resolveAnonymousAssortmentScope } from "./buyerContext.js";
7
+ import { buildStorefrontProductScope, composeStorefrontProductFilters } from "./storefrontProductScope.js";
8
+ const logger = createLogger("ecommerce").child({ component: "assortment-count" });
9
+ const DRAFT_SCOPE_PARAM = "draftScope";
10
+ const DRAFT_REQUIRE_AUTHENTICATION_PARAM = "draftRequireAuthentication";
11
+ function readSingleParam(searchParams, name) {
12
+ const values = searchParams.getAll(name);
13
+ if (values.length === 0) return null;
14
+ if (values.length > 1) return "duplicate";
15
+ return values[0];
16
+ }
17
+ function parseAssortmentCountDraft(searchParams, translate) {
18
+ const fieldErrors = {};
19
+ const draft = {};
20
+ const rawScope = readSingleParam(searchParams, DRAFT_SCOPE_PARAM);
21
+ if (rawScope !== null) {
22
+ const invalidJson = translate(
23
+ "ecommerce.validation.draftScopeJsonInvalid",
24
+ "The draft scope must be a JSON-encoded assortment scope."
25
+ );
26
+ if (rawScope === "duplicate") {
27
+ fieldErrors[DRAFT_SCOPE_PARAM] = invalidJson;
28
+ } else {
29
+ let decoded;
30
+ let decodable = true;
31
+ try {
32
+ decoded = JSON.parse(rawScope);
33
+ } catch {
34
+ decodable = false;
35
+ fieldErrors[DRAFT_SCOPE_PARAM] = invalidJson;
36
+ }
37
+ if (decodable) {
38
+ const parsed = ecommerceAssortmentScopeSchema.safeParse(decoded);
39
+ if (parsed.success) {
40
+ draft.scope = parsed.data;
41
+ } else {
42
+ fieldErrors[DRAFT_SCOPE_PARAM] = translate(
43
+ "ecommerce.validation.draftScopeInvalid",
44
+ "The draft scope may only contain category, tag and excluded product, category and tag ids."
45
+ );
46
+ }
47
+ }
48
+ }
49
+ }
50
+ const rawRequireAuthentication = readSingleParam(searchParams, DRAFT_REQUIRE_AUTHENTICATION_PARAM);
51
+ if (rawRequireAuthentication !== null) {
52
+ const parsed = rawRequireAuthentication === "duplicate" ? null : parseBooleanToken(rawRequireAuthentication);
53
+ if (parsed === null) {
54
+ fieldErrors[DRAFT_REQUIRE_AUTHENTICATION_PARAM] = translate(
55
+ "ecommerce.validation.draftRequireAuthenticationInvalid",
56
+ "The draft require-authentication value must be true or false."
57
+ );
58
+ } else {
59
+ draft.requireAuthentication = parsed;
60
+ }
61
+ }
62
+ if (Object.keys(fieldErrors).length > 0) return { ok: false, fieldErrors };
63
+ return { ok: true, draft };
64
+ }
65
+ async function countStorefrontProducts(queryEngine, scope, assortmentScope, extra) {
66
+ if (Array.isArray(assortmentScope) && assortmentScope.length === 0) return 0;
67
+ const productScope = buildStorefrontProductScope({ ...scope, buyer: { assortmentScope } });
68
+ const result = await queryEngine.query(E.catalog.catalog_product, {
69
+ tenantId: productScope.tenantId,
70
+ organizationId: productScope.organizationId,
71
+ withDeleted: productScope.withDeleted,
72
+ filters: composeStorefrontProductFilters(productScope, extra),
73
+ fields: ["id"],
74
+ page: { page: 1, pageSize: 1 }
75
+ });
76
+ return result.total;
77
+ }
78
+ async function countChannelAssortment(container, input) {
79
+ const queryEngine = container.resolve("queryEngine");
80
+ const scope = { tenantId: input.tenantId, organizationId: input.organizationId };
81
+ const openScope = await resolveAnonymousAssortmentScope(container, {
82
+ tenantId: input.tenantId,
83
+ channelAssortmentScope: input.channelAssortmentScope,
84
+ requireAuthentication: false
85
+ });
86
+ const countWithoutAuthentication = await countStorefrontProducts(queryEngine, scope, openScope);
87
+ const count = input.requireAuthentication ? 0 : countWithoutAuthentication;
88
+ const restricted = openScope !== null && openScope.length > 0;
89
+ const unindexedCount = restricted ? await countStorefrontProducts(queryEngine, scope, null, { [PRODUCT_SCOPE_KEYS_DOC_KEY]: { $exists: false } }) : 0;
90
+ if (unindexedCount > 0) {
91
+ logger.info("Active products without a scope index fail closed under a restricted assortment scope", {
92
+ organizationId: input.organizationId,
93
+ scopeSource: input.scopeSource,
94
+ unindexedCount
95
+ });
96
+ }
97
+ return {
98
+ count,
99
+ scopeSource: input.scopeSource,
100
+ requireAuthentication: input.requireAuthentication,
101
+ reducedByAuthentication: input.requireAuthentication && countWithoutAuthentication > 0,
102
+ countWithoutAuthentication,
103
+ unindexedCount
104
+ };
105
+ }
106
+ export {
107
+ DRAFT_REQUIRE_AUTHENTICATION_PARAM,
108
+ DRAFT_SCOPE_PARAM,
109
+ countChannelAssortment,
110
+ parseAssortmentCountDraft
111
+ };
112
+ //# sourceMappingURL=assortmentCount.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/ecommerce/lib/assortmentCount.ts"],
4
+ "sourcesContent": ["import type { AwilixContainer } from 'awilix'\nimport type { AssortmentScope, EffectiveAssortmentScope } from '@open-mercato/shared/lib/catalog-visibility'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport type { QueryEngine, Where } from '@open-mercato/shared/lib/query/types'\nimport { PRODUCT_SCOPE_KEYS_DOC_KEY } from '@open-mercato/core/modules/catalog/lib/productScopeKeys'\nimport { E } from '#generated/entities.ids.generated'\nimport { ecommerceAssortmentScopeSchema } from '../data/validators'\nimport { resolveAnonymousAssortmentScope } from './buyerContext'\nimport type { Translate } from './crudSupport'\nimport { buildStorefrontProductScope, composeStorefrontProductFilters } from './storefrontProductScope'\n\nconst logger = createLogger('ecommerce').child({ component: 'assortment-count' })\n\nexport type AssortmentCountScopeSource = 'saved' | 'draft'\n\nexport type AssortmentCountResponse = {\n count: number\n scopeSource: AssortmentCountScopeSource\n requireAuthentication: boolean\n reducedByAuthentication: boolean\n countWithoutAuthentication: number\n unindexedCount: number\n}\n\nexport type AssortmentCountDraft = {\n scope?: AssortmentScope | null\n requireAuthentication?: boolean\n}\n\nexport type AssortmentCountDraftResult =\n | { ok: true; draft: AssortmentCountDraft }\n | { ok: false; fieldErrors: Record<string, string> }\n\nexport const DRAFT_SCOPE_PARAM = 'draftScope'\nexport const DRAFT_REQUIRE_AUTHENTICATION_PARAM = 'draftRequireAuthentication'\n\nfunction readSingleParam(searchParams: URLSearchParams, name: string): string | null | 'duplicate' {\n const values = searchParams.getAll(name)\n if (values.length === 0) return null\n if (values.length > 1) return 'duplicate'\n return values[0]\n}\n\n/**\n * Parses the unsaved Channels-tab state a count request may carry. `draftScope` is a JSON-encoded\n * `assortment_scope` validated with the schema the channel binding write path uses, so `allOf` and\n * any unknown key are rejected exactly as they are on save; `draftRequireAuthentication` is a\n * boolean token. Absent parameters fall back to the saved binding.\n */\nexport function parseAssortmentCountDraft(searchParams: URLSearchParams, translate: Translate): AssortmentCountDraftResult {\n const fieldErrors: Record<string, string> = {}\n const draft: AssortmentCountDraft = {}\n\n const rawScope = readSingleParam(searchParams, DRAFT_SCOPE_PARAM)\n if (rawScope !== null) {\n const invalidJson = translate(\n 'ecommerce.validation.draftScopeJsonInvalid',\n 'The draft scope must be a JSON-encoded assortment scope.',\n )\n if (rawScope === 'duplicate') {\n fieldErrors[DRAFT_SCOPE_PARAM] = invalidJson\n } else {\n let decoded: unknown\n let decodable = true\n try {\n decoded = JSON.parse(rawScope)\n } catch {\n decodable = false\n fieldErrors[DRAFT_SCOPE_PARAM] = invalidJson\n }\n if (decodable) {\n const parsed = ecommerceAssortmentScopeSchema.safeParse(decoded)\n if (parsed.success) {\n draft.scope = parsed.data\n } else {\n fieldErrors[DRAFT_SCOPE_PARAM] = translate(\n 'ecommerce.validation.draftScopeInvalid',\n 'The draft scope may only contain category, tag and excluded product, category and tag ids.',\n )\n }\n }\n }\n }\n\n const rawRequireAuthentication = readSingleParam(searchParams, DRAFT_REQUIRE_AUTHENTICATION_PARAM)\n if (rawRequireAuthentication !== null) {\n const parsed = rawRequireAuthentication === 'duplicate' ? null : parseBooleanToken(rawRequireAuthentication)\n if (parsed === null) {\n fieldErrors[DRAFT_REQUIRE_AUTHENTICATION_PARAM] = translate(\n 'ecommerce.validation.draftRequireAuthenticationInvalid',\n 'The draft require-authentication value must be true or false.',\n )\n } else {\n draft.requireAuthentication = parsed\n }\n }\n\n if (Object.keys(fieldErrors).length > 0) return { ok: false, fieldErrors }\n return { ok: true, draft }\n}\n\ntype CountScope = { tenantId: string; organizationId: string }\n\nasync function countStorefrontProducts(\n queryEngine: QueryEngine,\n scope: CountScope,\n assortmentScope: EffectiveAssortmentScope,\n extra?: Where,\n): Promise<number> {\n if (Array.isArray(assortmentScope) && assortmentScope.length === 0) return 0\n const productScope = buildStorefrontProductScope({ ...scope, buyer: { assortmentScope } })\n const result = await queryEngine.query(E.catalog.catalog_product, {\n tenantId: productScope.tenantId,\n organizationId: productScope.organizationId,\n withDeleted: productScope.withDeleted,\n filters: composeStorefrontProductFilters(productScope, extra),\n fields: ['id'],\n page: { page: 1, pageSize: 1 },\n })\n return result.total\n}\n\n/**\n * Live product count behind the Channels tab (SPEC-029 \u00A79.2, \u00A710a US-E1): how many products the\n * channel would show an anonymous buyer. The effective assortment comes from the storefront's own\n * buyer resolution (`resolveAnonymousAssortmentScope`) and is counted through the same\n * `buildStorefrontProductScope` + `composeStorefrontProductFilters` query the listing composes, so\n * the admin number cannot drift from the storefront's total.\n *\n * `count` is the anonymous figure \u2014 `0` while `requireAuthentication` is on \u2014 and\n * `countWithoutAuthentication` is what the same scope would show with the gate off, so the form can\n * say how many products the toggle hides. `unindexedCount` reports active products with no\n * `scope_keys` document yet: they fail closed under any restricted scope, so a non-zero value\n * explains a count that looks too low.\n */\nexport async function countChannelAssortment(\n container: AwilixContainer,\n input: {\n tenantId: string\n organizationId: string\n channelAssortmentScope: AssortmentScope | null\n requireAuthentication: boolean\n scopeSource: AssortmentCountScopeSource\n },\n): Promise<AssortmentCountResponse> {\n const queryEngine = container.resolve('queryEngine') as QueryEngine\n const scope: CountScope = { tenantId: input.tenantId, organizationId: input.organizationId }\n const openScope = await resolveAnonymousAssortmentScope(container, {\n tenantId: input.tenantId,\n channelAssortmentScope: input.channelAssortmentScope,\n requireAuthentication: false,\n })\n const countWithoutAuthentication = await countStorefrontProducts(queryEngine, scope, openScope)\n const count = input.requireAuthentication ? 0 : countWithoutAuthentication\n const restricted = openScope !== null && openScope.length > 0\n const unindexedCount = restricted\n ? await countStorefrontProducts(queryEngine, scope, null, { [PRODUCT_SCOPE_KEYS_DOC_KEY]: { $exists: false } })\n : 0\n if (unindexedCount > 0) {\n logger.info('Active products without a scope index fail closed under a restricted assortment scope', {\n organizationId: input.organizationId,\n scopeSource: input.scopeSource,\n unindexedCount,\n })\n }\n return {\n count,\n scopeSource: input.scopeSource,\n requireAuthentication: input.requireAuthentication,\n reducedByAuthentication: input.requireAuthentication && countWithoutAuthentication > 0,\n countWithoutAuthentication,\n unindexedCount,\n }\n}\n"],
5
+ "mappings": "AAEA,SAAS,yBAAyB;AAClC,SAAS,oBAAoB;AAE7B,SAAS,kCAAkC;AAC3C,SAAS,SAAS;AAClB,SAAS,sCAAsC;AAC/C,SAAS,uCAAuC;AAEhD,SAAS,6BAA6B,uCAAuC;AAE7E,MAAM,SAAS,aAAa,WAAW,EAAE,MAAM,EAAE,WAAW,mBAAmB,CAAC;AAsBzE,MAAM,oBAAoB;AAC1B,MAAM,qCAAqC;AAElD,SAAS,gBAAgB,cAA+B,MAA2C;AACjG,QAAM,SAAS,aAAa,OAAO,IAAI;AACvC,MAAI,OAAO,WAAW,EAAG,QAAO;AAChC,MAAI,OAAO,SAAS,EAAG,QAAO;AAC9B,SAAO,OAAO,CAAC;AACjB;AAQO,SAAS,0BAA0B,cAA+B,WAAkD;AACzH,QAAM,cAAsC,CAAC;AAC7C,QAAM,QAA8B,CAAC;AAErC,QAAM,WAAW,gBAAgB,cAAc,iBAAiB;AAChE,MAAI,aAAa,MAAM;AACrB,UAAM,cAAc;AAAA,MAClB;AAAA,MACA;AAAA,IACF;AACA,QAAI,aAAa,aAAa;AAC5B,kBAAY,iBAAiB,IAAI;AAAA,IACnC,OAAO;AACL,UAAI;AACJ,UAAI,YAAY;AAChB,UAAI;AACF,kBAAU,KAAK,MAAM,QAAQ;AAAA,MAC/B,QAAQ;AACN,oBAAY;AACZ,oBAAY,iBAAiB,IAAI;AAAA,MACnC;AACA,UAAI,WAAW;AACb,cAAM,SAAS,+BAA+B,UAAU,OAAO;AAC/D,YAAI,OAAO,SAAS;AAClB,gBAAM,QAAQ,OAAO;AAAA,QACvB,OAAO;AACL,sBAAY,iBAAiB,IAAI;AAAA,YAC/B;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,2BAA2B,gBAAgB,cAAc,kCAAkC;AACjG,MAAI,6BAA6B,MAAM;AACrC,UAAM,SAAS,6BAA6B,cAAc,OAAO,kBAAkB,wBAAwB;AAC3G,QAAI,WAAW,MAAM;AACnB,kBAAY,kCAAkC,IAAI;AAAA,QAChD;AAAA,QACA;AAAA,MACF;AAAA,IACF,OAAO;AACL,YAAM,wBAAwB;AAAA,IAChC;AAAA,EACF;AAEA,MAAI,OAAO,KAAK,WAAW,EAAE,SAAS,EAAG,QAAO,EAAE,IAAI,OAAO,YAAY;AACzE,SAAO,EAAE,IAAI,MAAM,MAAM;AAC3B;AAIA,eAAe,wBACb,aACA,OACA,iBACA,OACiB;AACjB,MAAI,MAAM,QAAQ,eAAe,KAAK,gBAAgB,WAAW,EAAG,QAAO;AAC3E,QAAM,eAAe,4BAA4B,EAAE,GAAG,OAAO,OAAO,EAAE,gBAAgB,EAAE,CAAC;AACzF,QAAM,SAAS,MAAM,YAAY,MAAM,EAAE,QAAQ,iBAAiB;AAAA,IAChE,UAAU,aAAa;AAAA,IACvB,gBAAgB,aAAa;AAAA,IAC7B,aAAa,aAAa;AAAA,IAC1B,SAAS,gCAAgC,cAAc,KAAK;AAAA,IAC5D,QAAQ,CAAC,IAAI;AAAA,IACb,MAAM,EAAE,MAAM,GAAG,UAAU,EAAE;AAAA,EAC/B,CAAC;AACD,SAAO,OAAO;AAChB;AAeA,eAAsB,uBACpB,WACA,OAOkC;AAClC,QAAM,cAAc,UAAU,QAAQ,aAAa;AACnD,QAAM,QAAoB,EAAE,UAAU,MAAM,UAAU,gBAAgB,MAAM,eAAe;AAC3F,QAAM,YAAY,MAAM,gCAAgC,WAAW;AAAA,IACjE,UAAU,MAAM;AAAA,IAChB,wBAAwB,MAAM;AAAA,IAC9B,uBAAuB;AAAA,EACzB,CAAC;AACD,QAAM,6BAA6B,MAAM,wBAAwB,aAAa,OAAO,SAAS;AAC9F,QAAM,QAAQ,MAAM,wBAAwB,IAAI;AAChD,QAAM,aAAa,cAAc,QAAQ,UAAU,SAAS;AAC5D,QAAM,iBAAiB,aACnB,MAAM,wBAAwB,aAAa,OAAO,MAAM,EAAE,CAAC,0BAA0B,GAAG,EAAE,SAAS,MAAM,EAAE,CAAC,IAC5G;AACJ,MAAI,iBAAiB,GAAG;AACtB,WAAO,KAAK,yFAAyF;AAAA,MACnG,gBAAgB,MAAM;AAAA,MACtB,aAAa,MAAM;AAAA,MACnB;AAAA,IACF,CAAC;AAAA,EACH;AACA,SAAO;AAAA,IACL;AAAA,IACA,aAAa,MAAM;AAAA,IACnB,uBAAuB,MAAM;AAAA,IAC7B,yBAAyB,MAAM,yBAAyB,6BAA6B;AAAA,IACrF;AAAA,IACA;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,184 @@
1
+ const ECOMMERCE_BRANDING_FONTS = [
2
+ {
3
+ id: "system-sans",
4
+ label: "System sans-serif",
5
+ stack: "system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif",
6
+ googleFamily: null
7
+ },
8
+ { id: "system-serif", label: "System serif", stack: "Georgia, 'Times New Roman', Times, serif", googleFamily: null },
9
+ {
10
+ id: "system-mono",
11
+ label: "System monospace",
12
+ stack: "ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace",
13
+ googleFamily: null
14
+ },
15
+ { id: "inter", label: "Inter", stack: "'Inter', sans-serif", googleFamily: "Inter" },
16
+ { id: "roboto", label: "Roboto", stack: "'Roboto', sans-serif", googleFamily: "Roboto" },
17
+ { id: "open-sans", label: "Open Sans", stack: "'Open Sans', sans-serif", googleFamily: "Open Sans" },
18
+ { id: "lato", label: "Lato", stack: "'Lato', sans-serif", googleFamily: "Lato" },
19
+ { id: "montserrat", label: "Montserrat", stack: "'Montserrat', sans-serif", googleFamily: "Montserrat" },
20
+ { id: "poppins", label: "Poppins", stack: "'Poppins', sans-serif", googleFamily: "Poppins" },
21
+ { id: "dm-sans", label: "DM Sans", stack: "'DM Sans', sans-serif", googleFamily: "DM Sans" },
22
+ { id: "merriweather", label: "Merriweather", stack: "'Merriweather', serif", googleFamily: "Merriweather" },
23
+ {
24
+ id: "playfair-display",
25
+ label: "Playfair Display",
26
+ stack: "'Playfair Display', serif",
27
+ googleFamily: "Playfair Display"
28
+ }
29
+ ];
30
+ const ecommerceBrandingFontIds = ECOMMERCE_BRANDING_FONTS.map((font) => font.id);
31
+ function findEcommerceBrandingFont(id) {
32
+ return ECOMMERCE_BRANDING_FONTS.find((font) => font.id === id) ?? null;
33
+ }
34
+ const NUMBER_PATTERN = "(\\d+(?:\\.\\d+)?|\\.\\d+)";
35
+ const OKLCH_PATTERN = new RegExp(
36
+ `^oklch\\(\\s*${NUMBER_PATTERN}(%?)\\s+${NUMBER_PATTERN}(%?)\\s+${NUMBER_PATTERN}(deg)?\\s*(?:\\/\\s*${NUMBER_PATTERN}(%?)\\s*)?\\)$`
37
+ );
38
+ const HEX_COLOR_PATTERN = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
39
+ const BORDER_RADIUS_PATTERN = /^(\d+(?:\.\d+)?|\.\d+)(rem|px)$/;
40
+ const OKLCH_CHROMA_MAX = 0.5;
41
+ const OKLCH_HUE_MAX = 360;
42
+ const BORDER_RADIUS_REM_MAX = 5;
43
+ const BORDER_RADIUS_PX_MAX = 80;
44
+ function withinBounds(raw, percent, numericMax) {
45
+ const value = Number(raw);
46
+ if (!Number.isFinite(value) || value < 0) return false;
47
+ return percent ? value <= 100 : value <= numericMax;
48
+ }
49
+ function isValidOklchColor(value) {
50
+ return normalizeOklchColor(value) !== null;
51
+ }
52
+ function normalizeOklchColor(value) {
53
+ const match = OKLCH_PATTERN.exec(value);
54
+ if (!match) return null;
55
+ const [, lightness, lightnessPercent, chroma, chromaPercent, hue, hueUnit, alpha, alphaPercent] = match;
56
+ if (!withinBounds(lightness, lightnessPercent === "%", 1)) return null;
57
+ if (!withinBounds(chroma, chromaPercent === "%", OKLCH_CHROMA_MAX)) return null;
58
+ if (!withinBounds(hue, false, OKLCH_HUE_MAX)) return null;
59
+ if (alpha !== void 0 && !withinBounds(alpha, alphaPercent === "%", 1)) return null;
60
+ const alphaPart = alpha === void 0 ? "" : ` / ${alpha}${alphaPercent}`;
61
+ return `oklch(${lightness}${lightnessPercent} ${chroma}${chromaPercent} ${hue}${hueUnit ?? ""}${alphaPart})`;
62
+ }
63
+ function isValidBrandingColor(value) {
64
+ return HEX_COLOR_PATTERN.test(value) || isValidOklchColor(value);
65
+ }
66
+ function normalizeBrandingColor(value) {
67
+ if (HEX_COLOR_PATTERN.test(value)) return value.toLowerCase();
68
+ return normalizeOklchColor(value);
69
+ }
70
+ function isValidBorderRadius(value) {
71
+ if (value === "0") return true;
72
+ const match = BORDER_RADIUS_PATTERN.exec(value);
73
+ if (!match) return false;
74
+ const amount = Number(match[1]);
75
+ if (!Number.isFinite(amount)) return false;
76
+ return match[2] === "rem" ? amount <= BORDER_RADIUS_REM_MAX : amount <= BORDER_RADIUS_PX_MAX;
77
+ }
78
+ const ECOMMERCE_BRANDING_COLOR_KEYS = [
79
+ "primaryColor",
80
+ "primaryForeground",
81
+ "accentColor",
82
+ "accentForeground",
83
+ "backgroundColor",
84
+ "foregroundColor"
85
+ ];
86
+ const ECOMMERCE_BRANDING_FONT_KEYS = ["fontFamilyBase", "fontFamilyHeading"];
87
+ const BRANDING_PROPERTY_BY_KEY = {
88
+ primaryColor: "--primary",
89
+ primaryForeground: "--primary-foreground",
90
+ accentColor: "--accent",
91
+ accentForeground: "--accent-foreground",
92
+ backgroundColor: "--background",
93
+ foregroundColor: "--foreground",
94
+ borderRadius: "--radius",
95
+ fontFamilyBase: "--font-base",
96
+ fontFamilyHeading: "--font-heading"
97
+ };
98
+ const BRANDING_TOKEN_ORDER = [
99
+ "primaryColor",
100
+ "primaryForeground",
101
+ "accentColor",
102
+ "accentForeground",
103
+ "backgroundColor",
104
+ "foregroundColor",
105
+ "borderRadius",
106
+ "fontFamilyBase",
107
+ "fontFamilyHeading"
108
+ ];
109
+ const ECOMMERCE_BRANDING_CSS_PROPERTIES = BRANDING_TOKEN_ORDER.map(
110
+ (key) => BRANDING_PROPERTY_BY_KEY[key]
111
+ );
112
+ const DEFAULT_FONT_BASE_ID = "inter";
113
+ const HEADING_INHERITS_BASE = "var(--font-base)";
114
+ const ECOMMERCE_BRANDING_DEFAULTS = {
115
+ primaryColor: "oklch(0.205 0 0)",
116
+ primaryForeground: "oklch(0.985 0 0)",
117
+ accentColor: "oklch(0.97 0 0)",
118
+ accentForeground: "oklch(0.205 0 0)",
119
+ backgroundColor: "oklch(1 0 0)",
120
+ foregroundColor: "oklch(0.145 0 0)",
121
+ borderRadius: "0.625rem"
122
+ };
123
+ const NONCE_PATTERN = /^[A-Za-z0-9+/_-]{8,128}={0,2}$/;
124
+ function readOwnString(source, key) {
125
+ if (typeof source !== "object" || source === null) return null;
126
+ if (!Object.prototype.hasOwnProperty.call(source, key)) return null;
127
+ const value = source[key];
128
+ return typeof value === "string" ? value : null;
129
+ }
130
+ function resolveColor(source, key) {
131
+ const raw = readOwnString(source, key);
132
+ return (raw === null ? null : normalizeBrandingColor(raw)) ?? ECOMMERCE_BRANDING_DEFAULTS[key];
133
+ }
134
+ function resolveBorderRadius(source) {
135
+ const raw = readOwnString(source, "borderRadius");
136
+ return raw !== null && isValidBorderRadius(raw) ? raw : ECOMMERCE_BRANDING_DEFAULTS.borderRadius;
137
+ }
138
+ function resolveFontStack(source, key) {
139
+ const raw = readOwnString(source, key);
140
+ const font = raw === null ? null : findEcommerceBrandingFont(raw);
141
+ return font ? font.stack : null;
142
+ }
143
+ function buildBrandingDeclarations(branding) {
144
+ const baseStack = resolveFontStack(branding, "fontFamilyBase") ?? findEcommerceBrandingFont(DEFAULT_FONT_BASE_ID)?.stack ?? "sans-serif";
145
+ const headingStack = resolveFontStack(branding, "fontFamilyHeading") ?? HEADING_INHERITS_BASE;
146
+ const values = {
147
+ primaryColor: resolveColor(branding, "primaryColor"),
148
+ primaryForeground: resolveColor(branding, "primaryForeground"),
149
+ accentColor: resolveColor(branding, "accentColor"),
150
+ accentForeground: resolveColor(branding, "accentForeground"),
151
+ backgroundColor: resolveColor(branding, "backgroundColor"),
152
+ foregroundColor: resolveColor(branding, "foregroundColor"),
153
+ borderRadius: resolveBorderRadius(branding),
154
+ fontFamilyBase: baseStack,
155
+ fontFamilyHeading: headingStack
156
+ };
157
+ return BRANDING_TOKEN_ORDER.map((key) => ({ property: BRANDING_PROPERTY_BY_KEY[key], value: values[key] }));
158
+ }
159
+ function renderBrandingRootRule(branding) {
160
+ const body = buildBrandingDeclarations(branding).map((declaration) => `${declaration.property}:${declaration.value}`).join(";");
161
+ return `:root{${body}}`;
162
+ }
163
+ function renderBrandingStyleBlock(branding, options = {}) {
164
+ const nonce = typeof options.nonce === "string" && NONCE_PATTERN.test(options.nonce) ? options.nonce : null;
165
+ const attributes = nonce ? ` nonce="${nonce}"` : "";
166
+ return `<style data-ecommerce-branding${attributes}>${renderBrandingRootRule(branding)}</style>`;
167
+ }
168
+ export {
169
+ ECOMMERCE_BRANDING_COLOR_KEYS,
170
+ ECOMMERCE_BRANDING_CSS_PROPERTIES,
171
+ ECOMMERCE_BRANDING_DEFAULTS,
172
+ ECOMMERCE_BRANDING_FONTS,
173
+ ECOMMERCE_BRANDING_FONT_KEYS,
174
+ buildBrandingDeclarations,
175
+ ecommerceBrandingFontIds,
176
+ findEcommerceBrandingFont,
177
+ isValidBorderRadius,
178
+ isValidBrandingColor,
179
+ isValidOklchColor,
180
+ normalizeBrandingColor,
181
+ renderBrandingRootRule,
182
+ renderBrandingStyleBlock
183
+ };
184
+ //# sourceMappingURL=brandingStyles.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/ecommerce/lib/brandingStyles.ts"],
4
+ "sourcesContent": ["export type EcommerceBrandingFont = {\n id: string\n label: string\n stack: string\n googleFamily: string | null\n}\n\nexport const ECOMMERCE_BRANDING_FONTS: readonly EcommerceBrandingFont[] = [\n {\n id: 'system-sans',\n label: 'System sans-serif',\n stack: \"system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif\",\n googleFamily: null,\n },\n { id: 'system-serif', label: 'System serif', stack: \"Georgia, 'Times New Roman', Times, serif\", googleFamily: null },\n {\n id: 'system-mono',\n label: 'System monospace',\n stack: \"ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace\",\n googleFamily: null,\n },\n { id: 'inter', label: 'Inter', stack: \"'Inter', sans-serif\", googleFamily: 'Inter' },\n { id: 'roboto', label: 'Roboto', stack: \"'Roboto', sans-serif\", googleFamily: 'Roboto' },\n { id: 'open-sans', label: 'Open Sans', stack: \"'Open Sans', sans-serif\", googleFamily: 'Open Sans' },\n { id: 'lato', label: 'Lato', stack: \"'Lato', sans-serif\", googleFamily: 'Lato' },\n { id: 'montserrat', label: 'Montserrat', stack: \"'Montserrat', sans-serif\", googleFamily: 'Montserrat' },\n { id: 'poppins', label: 'Poppins', stack: \"'Poppins', sans-serif\", googleFamily: 'Poppins' },\n { id: 'dm-sans', label: 'DM Sans', stack: \"'DM Sans', sans-serif\", googleFamily: 'DM Sans' },\n { id: 'merriweather', label: 'Merriweather', stack: \"'Merriweather', serif\", googleFamily: 'Merriweather' },\n {\n id: 'playfair-display',\n label: 'Playfair Display',\n stack: \"'Playfair Display', serif\",\n googleFamily: 'Playfair Display',\n },\n]\n\nexport const ecommerceBrandingFontIds: readonly string[] = ECOMMERCE_BRANDING_FONTS.map((font) => font.id)\n\nexport function findEcommerceBrandingFont(id: string): EcommerceBrandingFont | null {\n return ECOMMERCE_BRANDING_FONTS.find((font) => font.id === id) ?? null\n}\n\nconst NUMBER_PATTERN = '(\\\\d+(?:\\\\.\\\\d+)?|\\\\.\\\\d+)'\nconst OKLCH_PATTERN = new RegExp(\n `^oklch\\\\(\\\\s*${NUMBER_PATTERN}(%?)\\\\s+${NUMBER_PATTERN}(%?)\\\\s+${NUMBER_PATTERN}(deg)?\\\\s*(?:\\\\/\\\\s*${NUMBER_PATTERN}(%?)\\\\s*)?\\\\)$`,\n)\nconst HEX_COLOR_PATTERN = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/\nconst BORDER_RADIUS_PATTERN = /^(\\d+(?:\\.\\d+)?|\\.\\d+)(rem|px)$/\n\nconst OKLCH_CHROMA_MAX = 0.5\nconst OKLCH_HUE_MAX = 360\nconst BORDER_RADIUS_REM_MAX = 5\nconst BORDER_RADIUS_PX_MAX = 80\n\nfunction withinBounds(raw: string, percent: boolean, numericMax: number): boolean {\n const value = Number(raw)\n if (!Number.isFinite(value) || value < 0) return false\n return percent ? value <= 100 : value <= numericMax\n}\n\nexport function isValidOklchColor(value: string): boolean {\n return normalizeOklchColor(value) !== null\n}\n\nfunction normalizeOklchColor(value: string): string | null {\n const match = OKLCH_PATTERN.exec(value)\n if (!match) return null\n const [, lightness, lightnessPercent, chroma, chromaPercent, hue, hueUnit, alpha, alphaPercent] = match\n if (!withinBounds(lightness, lightnessPercent === '%', 1)) return null\n if (!withinBounds(chroma, chromaPercent === '%', OKLCH_CHROMA_MAX)) return null\n if (!withinBounds(hue, false, OKLCH_HUE_MAX)) return null\n if (alpha !== undefined && !withinBounds(alpha, alphaPercent === '%', 1)) return null\n const alphaPart = alpha === undefined ? '' : ` / ${alpha}${alphaPercent}`\n return `oklch(${lightness}${lightnessPercent} ${chroma}${chromaPercent} ${hue}${hueUnit ?? ''}${alphaPart})`\n}\n\nexport function isValidBrandingColor(value: string): boolean {\n return HEX_COLOR_PATTERN.test(value) || isValidOklchColor(value)\n}\n\nexport function normalizeBrandingColor(value: string): string | null {\n if (HEX_COLOR_PATTERN.test(value)) return value.toLowerCase()\n return normalizeOklchColor(value)\n}\n\nexport function isValidBorderRadius(value: string): boolean {\n if (value === '0') return true\n const match = BORDER_RADIUS_PATTERN.exec(value)\n if (!match) return false\n const amount = Number(match[1])\n if (!Number.isFinite(amount)) return false\n return match[2] === 'rem' ? amount <= BORDER_RADIUS_REM_MAX : amount <= BORDER_RADIUS_PX_MAX\n}\n\nexport const ECOMMERCE_BRANDING_COLOR_KEYS = [\n 'primaryColor',\n 'primaryForeground',\n 'accentColor',\n 'accentForeground',\n 'backgroundColor',\n 'foregroundColor',\n] as const\n\nexport const ECOMMERCE_BRANDING_FONT_KEYS = ['fontFamilyBase', 'fontFamilyHeading'] as const\n\nexport type EcommerceBrandingColorKey = (typeof ECOMMERCE_BRANDING_COLOR_KEYS)[number]\nexport type EcommerceBrandingFontKey = (typeof ECOMMERCE_BRANDING_FONT_KEYS)[number]\nexport type EcommerceBrandingTokenKey = EcommerceBrandingColorKey | 'borderRadius' | EcommerceBrandingFontKey\n\nexport type EcommerceBrandingDeclaration = {\n property: string\n value: string\n}\n\nconst BRANDING_PROPERTY_BY_KEY: Readonly<Record<EcommerceBrandingTokenKey, string>> = {\n primaryColor: '--primary',\n primaryForeground: '--primary-foreground',\n accentColor: '--accent',\n accentForeground: '--accent-foreground',\n backgroundColor: '--background',\n foregroundColor: '--foreground',\n borderRadius: '--radius',\n fontFamilyBase: '--font-base',\n fontFamilyHeading: '--font-heading',\n}\n\nconst BRANDING_TOKEN_ORDER: readonly EcommerceBrandingTokenKey[] = [\n 'primaryColor',\n 'primaryForeground',\n 'accentColor',\n 'accentForeground',\n 'backgroundColor',\n 'foregroundColor',\n 'borderRadius',\n 'fontFamilyBase',\n 'fontFamilyHeading',\n]\n\nexport const ECOMMERCE_BRANDING_CSS_PROPERTIES: readonly string[] = BRANDING_TOKEN_ORDER.map(\n (key) => BRANDING_PROPERTY_BY_KEY[key],\n)\n\nconst DEFAULT_FONT_BASE_ID = 'inter'\nconst HEADING_INHERITS_BASE = 'var(--font-base)'\n\nexport const ECOMMERCE_BRANDING_DEFAULTS: Readonly<Record<EcommerceBrandingColorKey | 'borderRadius', string>> = {\n primaryColor: 'oklch(0.205 0 0)',\n primaryForeground: 'oklch(0.985 0 0)',\n accentColor: 'oklch(0.97 0 0)',\n accentForeground: 'oklch(0.205 0 0)',\n backgroundColor: 'oklch(1 0 0)',\n foregroundColor: 'oklch(0.145 0 0)',\n borderRadius: '0.625rem',\n}\n\nconst NONCE_PATTERN = /^[A-Za-z0-9+/_-]{8,128}={0,2}$/\n\nfunction readOwnString(source: unknown, key: string): string | null {\n if (typeof source !== 'object' || source === null) return null\n if (!Object.prototype.hasOwnProperty.call(source, key)) return null\n const value = (source as Record<string, unknown>)[key]\n return typeof value === 'string' ? value : null\n}\n\nfunction resolveColor(source: unknown, key: EcommerceBrandingColorKey): string {\n const raw = readOwnString(source, key)\n return (raw === null ? null : normalizeBrandingColor(raw)) ?? ECOMMERCE_BRANDING_DEFAULTS[key]\n}\n\nfunction resolveBorderRadius(source: unknown): string {\n const raw = readOwnString(source, 'borderRadius')\n return raw !== null && isValidBorderRadius(raw) ? raw : ECOMMERCE_BRANDING_DEFAULTS.borderRadius\n}\n\nfunction resolveFontStack(source: unknown, key: EcommerceBrandingFontKey): string | null {\n const raw = readOwnString(source, key)\n const font = raw === null ? null : findEcommerceBrandingFont(raw)\n return font ? font.stack : null\n}\n\nexport function buildBrandingDeclarations(branding: unknown): EcommerceBrandingDeclaration[] {\n const baseStack =\n resolveFontStack(branding, 'fontFamilyBase') ?? findEcommerceBrandingFont(DEFAULT_FONT_BASE_ID)?.stack ?? 'sans-serif'\n const headingStack = resolveFontStack(branding, 'fontFamilyHeading') ?? HEADING_INHERITS_BASE\n const values: Record<EcommerceBrandingTokenKey, string> = {\n primaryColor: resolveColor(branding, 'primaryColor'),\n primaryForeground: resolveColor(branding, 'primaryForeground'),\n accentColor: resolveColor(branding, 'accentColor'),\n accentForeground: resolveColor(branding, 'accentForeground'),\n backgroundColor: resolveColor(branding, 'backgroundColor'),\n foregroundColor: resolveColor(branding, 'foregroundColor'),\n borderRadius: resolveBorderRadius(branding),\n fontFamilyBase: baseStack,\n fontFamilyHeading: headingStack,\n }\n return BRANDING_TOKEN_ORDER.map((key) => ({ property: BRANDING_PROPERTY_BY_KEY[key], value: values[key] }))\n}\n\nexport function renderBrandingRootRule(branding: unknown): string {\n const body = buildBrandingDeclarations(branding)\n .map((declaration) => `${declaration.property}:${declaration.value}`)\n .join(';')\n return `:root{${body}}`\n}\n\nexport type BrandingStyleBlockOptions = {\n nonce?: string | null\n}\n\nexport function renderBrandingStyleBlock(branding: unknown, options: BrandingStyleBlockOptions = {}): string {\n const nonce = typeof options.nonce === 'string' && NONCE_PATTERN.test(options.nonce) ? options.nonce : null\n const attributes = nonce ? ` nonce=\"${nonce}\"` : ''\n return `<style data-ecommerce-branding${attributes}>${renderBrandingRootRule(branding)}</style>`\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,284 @@
1
+ import { createHash } from "node:crypto";
2
+ import {
3
+ hashEffectiveScope,
4
+ intersectScopes
5
+ } from "@open-mercato/shared/lib/catalog-visibility";
6
+ import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
7
+ import { createLogger } from "@open-mercato/shared/lib/logger";
8
+ import { getTelemetryRuntime } from "@open-mercato/shared/lib/telemetry/runtime";
9
+ import { CustomerUser } from "@open-mercato/core/modules/customer_accounts/data/entities";
10
+ import { CatalogPriceKind } from "@open-mercato/core/modules/catalog/data/entities";
11
+ import { emitEcommerceEvent } from "../events.js";
12
+ import { buyerContextCache, ecommerceResolutionCache } from "./cacheKeys.js";
13
+ import { StorefrontResolutionError } from "./storeContext.js";
14
+ const ASSORTMENT_EMPTY_THROTTLE_MS = 36e5;
15
+ const DIGEST_LENGTH = 16;
16
+ const NULL_SEGMENT = "-";
17
+ const logger = createLogger("ecommerce").child({ component: "buyer-context" });
18
+ const ANONYMOUS_IDENTITY = {
19
+ customerUserId: null,
20
+ customerId: null,
21
+ companyId: null,
22
+ customerIds: [],
23
+ isAuthenticated: false
24
+ };
25
+ function tryResolve(container, name) {
26
+ try {
27
+ const resolved = container.resolve(name);
28
+ return resolved ?? null;
29
+ } catch {
30
+ return null;
31
+ }
32
+ }
33
+ function requireService(container, name) {
34
+ const service = tryResolve(container, name);
35
+ if (!service) throw new Error(`[internal] ecommerce buyer context requires the DI service ${name}`);
36
+ return service;
37
+ }
38
+ function sha256Truncated(segments) {
39
+ return createHash("sha256").update(segments.join("|")).digest("hex").slice(0, DIGEST_LENGTH);
40
+ }
41
+ function computePriceScopeKey(input) {
42
+ const sortedGroupIds = [...input.customerGroupIds].sort((left, right) => left < right ? -1 : left > right ? 1 : 0);
43
+ return sha256Truncated([
44
+ input.channelId,
45
+ input.currencyCode,
46
+ input.priceKindId ?? NULL_SEGMENT,
47
+ sortedGroupIds.join(",")
48
+ ]);
49
+ }
50
+ function computeStoreContextDigest(input) {
51
+ return sha256Truncated([
52
+ input.storeId,
53
+ input.effectiveLocale,
54
+ input.taxMode,
55
+ input.priceScopeKey,
56
+ input.assortmentScopeHash,
57
+ input.customerOverlayId ?? NULL_SEGMENT
58
+ ]);
59
+ }
60
+ function buyerIdentityFromCustomerUser(user) {
61
+ const personId = user.personEntityId ?? null;
62
+ const companyId = user.customerEntityId ?? null;
63
+ const customerIds = Array.from(
64
+ new Set([personId, companyId].filter((id) => typeof id === "string" && id.length > 0))
65
+ );
66
+ return {
67
+ customerUserId: user.id,
68
+ customerId: personId ?? companyId,
69
+ companyId,
70
+ customerIds,
71
+ isAuthenticated: true
72
+ };
73
+ }
74
+ async function loadBuyerIdentity(em, customerUserId, store) {
75
+ const scope = { tenantId: store.tenantId, organizationId: store.organizationId };
76
+ const user = await findOneWithDecryption(
77
+ em,
78
+ CustomerUser,
79
+ { id: customerUserId, ...scope, isActive: true, deletedAt: null },
80
+ void 0,
81
+ scope
82
+ );
83
+ if (!user) {
84
+ logger.debug("Portal session points at a missing or inactive customer user", { storeId: store.store.id });
85
+ throw new StorefrontResolutionError(401, "portal_session_invalid");
86
+ }
87
+ return buyerIdentityFromCustomerUser(user);
88
+ }
89
+ function taxModeFromPriceKindDisplayMode(displayMode) {
90
+ if (displayMode === "excluding-tax") return "net";
91
+ if (displayMode === "including-tax") return "gross";
92
+ return null;
93
+ }
94
+ async function resolveTaxMode(em, priceKindId, store) {
95
+ const fallback = store.store.settings.display.priceDisplayModeDefault;
96
+ if (!priceKindId) return fallback;
97
+ const scope = { tenantId: store.tenantId, organizationId: store.organizationId };
98
+ const priceKind = await findOneWithDecryption(
99
+ em,
100
+ CatalogPriceKind,
101
+ {
102
+ id: priceKindId,
103
+ tenantId: store.tenantId,
104
+ deletedAt: null,
105
+ $or: [{ organizationId: null }, { organizationId: store.organizationId }]
106
+ },
107
+ void 0,
108
+ scope
109
+ );
110
+ return taxModeFromPriceKindDisplayMode(priceKind?.displayMode) ?? fallback;
111
+ }
112
+ async function resolveCustomerOverlayId(em, customerIds, store) {
113
+ if (customerIds.length === 0) return null;
114
+ const rows = await em.getKysely().selectFrom("catalog_product_variant_prices").select("customer_id").distinct().where("customer_id", "in", customerIds).where("tenant_id", "=", store.tenantId).where("organization_id", "=", store.organizationId).execute();
115
+ const withOwnRows = new Set(rows.map((row) => row.customer_id).filter((id) => typeof id === "string"));
116
+ const overlayIds = customerIds.filter((id) => withOwnRows.has(id));
117
+ return overlayIds.length > 0 ? overlayIds.join(",") : null;
118
+ }
119
+ async function resolveEffectiveAssortment(service, tenantId, channelAssortmentScope, identity) {
120
+ const buyerScope = await service.resolveAssortmentScope({
121
+ customerId: identity.customerId,
122
+ customerIds: identity.customerIds,
123
+ tenantId
124
+ });
125
+ return intersectScopes(channelAssortmentScope, buyerScope.scope);
126
+ }
127
+ async function resolveAnonymousAssortmentScope(container, input) {
128
+ if (input.requireAuthentication) return [];
129
+ const service = requireService(container, "customerGroupsService");
130
+ return resolveEffectiveAssortment(service, input.tenantId, input.channelAssortmentScope, ANONYMOUS_IDENTITY);
131
+ }
132
+ async function reportEmptyAssortment(container, store, assortmentScopeHash) {
133
+ const cache = ecommerceResolutionCache(container);
134
+ const key = ["assortment-empty", store.store.id, store.channel.channelBindingId, assortmentScopeHash];
135
+ const alreadyReported = await cache.get(key);
136
+ if (alreadyReported) return;
137
+ await cache.set(key, { reportedAt: (/* @__PURE__ */ new Date()).toISOString() }, { ttlMs: ASSORTMENT_EMPTY_THROTTLE_MS });
138
+ logger.warn("Authenticated buyer resolved to an empty assortment", {
139
+ storeId: store.store.id,
140
+ channelBindingId: store.channel.channelBindingId,
141
+ assortmentScopeHash
142
+ });
143
+ try {
144
+ await emitEcommerceEvent(
145
+ "ecommerce.assortment.empty_detected",
146
+ {
147
+ id: store.store.id,
148
+ storeId: store.store.id,
149
+ channelBindingId: store.channel.channelBindingId,
150
+ assortmentScopeHash,
151
+ tenantId: store.tenantId,
152
+ organizationId: store.organizationId
153
+ },
154
+ { persistent: true, tenantId: store.tenantId, organizationId: store.organizationId }
155
+ );
156
+ } catch (error) {
157
+ logger.warn("Failed to emit empty assortment event", { storeId: store.store.id, err: error });
158
+ getTelemetryRuntime()?.reportError(error, {
159
+ module: "ecommerce",
160
+ code: "ecommerce.assortment_empty_emit_failed",
161
+ attributes: { storeId: store.store.id }
162
+ });
163
+ }
164
+ }
165
+ function isClosedToBuyer(store, identity) {
166
+ return store.channel.requireAuthentication && !identity.isAuthenticated;
167
+ }
168
+ async function resolveBuyerGroupLayer(container, store, identity) {
169
+ if (isClosedToBuyer(store, identity)) {
170
+ return {
171
+ customerGroupIds: [],
172
+ priceKindId: store.channel.priceKindId ?? null,
173
+ allowPurchaseOnAccount: false,
174
+ approvalRequiredAbove: null,
175
+ assortmentScope: []
176
+ };
177
+ }
178
+ const service = requireService(container, "customerGroupsService");
179
+ const groups = await service.resolveGroups({
180
+ customerId: identity.customerId,
181
+ customerIds: identity.customerIds,
182
+ tenantId: store.tenantId
183
+ });
184
+ const terms = await service.resolveTerms({
185
+ customerId: identity.customerId,
186
+ customerIds: identity.customerIds,
187
+ tenantId: store.tenantId,
188
+ groupIds: groups.groupIds
189
+ });
190
+ return {
191
+ customerGroupIds: groups.groupIds,
192
+ priceKindId: terms.priceKindId ?? store.channel.priceKindId ?? null,
193
+ allowPurchaseOnAccount: terms.allowPurchaseOnAccount,
194
+ approvalRequiredAbove: terms.approvalRequiredAbove,
195
+ assortmentScope: await resolveEffectiveAssortment(
196
+ service,
197
+ store.tenantId,
198
+ store.channel.assortmentScope,
199
+ identity
200
+ )
201
+ };
202
+ }
203
+ async function buildBuyerContext(container, store, identity) {
204
+ const em = requireService(container, "em");
205
+ const { customerGroupIds, priceKindId, allowPurchaseOnAccount, approvalRequiredAbove, assortmentScope } = await resolveBuyerGroupLayer(container, store, identity);
206
+ const taxMode = await resolveTaxMode(em, priceKindId, store);
207
+ const assortmentScopeHash = hashEffectiveScope(assortmentScope);
208
+ if (identity.isAuthenticated && Array.isArray(assortmentScope) && assortmentScope.length === 0) {
209
+ await reportEmptyAssortment(container, store, assortmentScopeHash);
210
+ }
211
+ const customerOverlayId = await resolveCustomerOverlayId(em, identity.customerIds, store);
212
+ return {
213
+ ...identity,
214
+ customerGroupIds,
215
+ taxMode,
216
+ priceKindId,
217
+ allowPurchaseOnAccount,
218
+ approvalRequiredAbove,
219
+ assortmentScope,
220
+ assortmentScopeHash,
221
+ priceScopeKey: computePriceScopeKey({
222
+ channelId: store.channel.salesChannelId,
223
+ currencyCode: store.currencyCode,
224
+ priceKindId,
225
+ customerGroupIds
226
+ }),
227
+ customerOverlayId
228
+ };
229
+ }
230
+ async function readPortalSession(request) {
231
+ const { getCustomerAuthFromRequest } = await import("@open-mercato/core/modules/customer_accounts/lib/customerAuth");
232
+ return getCustomerAuthFromRequest(request);
233
+ }
234
+ async function resolveBuyerContext(container, store, request) {
235
+ const auth = request ? await readPortalSession(request) : null;
236
+ if (auth && (auth.tenantId !== store.tenantId || auth.orgId !== store.organizationId)) {
237
+ logger.debug("Portal session scope does not match the resolved store", { storeId: store.store.id });
238
+ throw new StorefrontResolutionError(401, "portal_session_scope_mismatch");
239
+ }
240
+ const customerUserId = auth?.sub ?? null;
241
+ const cache = buyerContextCache(container, { tenantId: store.tenantId, storeId: store.store.id });
242
+ const cached = await cache.get(customerUserId);
243
+ if (cached && cached.customerUserId === customerUserId) return cached;
244
+ const identity = customerUserId ? await loadBuyerIdentity(requireService(container, "em"), customerUserId, store) : ANONYMOUS_IDENTITY;
245
+ const buyer = await buildBuyerContext(container, store, identity);
246
+ await cache.set(buyer);
247
+ return buyer;
248
+ }
249
+ function composeStoreContext(store, buyer) {
250
+ return {
251
+ store: store.store,
252
+ tenantId: store.tenantId,
253
+ organizationId: store.organizationId,
254
+ channel: {
255
+ channelBindingId: store.channel.channelBindingId,
256
+ salesChannelId: store.channel.salesChannelId,
257
+ priceKindId: store.channel.priceKindId,
258
+ priceSortFallback: store.channel.priceSortFallback
259
+ },
260
+ buyer,
261
+ effectiveLocale: store.effectiveLocale,
262
+ requestedLocale: store.requestedLocale,
263
+ currencyCode: store.currencyCode,
264
+ digest: computeStoreContextDigest({
265
+ storeId: store.store.id,
266
+ effectiveLocale: store.effectiveLocale,
267
+ taxMode: buyer.taxMode,
268
+ priceScopeKey: buyer.priceScopeKey,
269
+ assortmentScopeHash: buyer.assortmentScopeHash,
270
+ customerOverlayId: buyer.customerOverlayId
271
+ })
272
+ };
273
+ }
274
+ export {
275
+ ASSORTMENT_EMPTY_THROTTLE_MS,
276
+ buyerIdentityFromCustomerUser,
277
+ composeStoreContext,
278
+ computePriceScopeKey,
279
+ computeStoreContextDigest,
280
+ resolveAnonymousAssortmentScope,
281
+ resolveBuyerContext,
282
+ taxModeFromPriceKindDisplayMode
283
+ };
284
+ //# sourceMappingURL=buyerContext.js.map