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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (597) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/catalog_price_history_entry/index.js +53 -0
  3. package/dist/generated/entities/catalog_price_history_entry/index.js.map +7 -0
  4. package/dist/generated/entities/customer_group_terms/index.js +2 -0
  5. package/dist/generated/entities/customer_group_terms/index.js.map +2 -2
  6. package/dist/generated/entities/ecommerce_store/index.js +33 -0
  7. package/dist/generated/entities/ecommerce_store/index.js.map +7 -0
  8. package/dist/generated/entities/ecommerce_store_channel_binding/index.js +29 -0
  9. package/dist/generated/entities/ecommerce_store_channel_binding/index.js.map +7 -0
  10. package/dist/generated/entities/ecommerce_store_domain_binding/index.js +23 -0
  11. package/dist/generated/entities/ecommerce_store_domain_binding/index.js.map +7 -0
  12. package/dist/generated/entities.ids.generated.js +8 -1
  13. package/dist/generated/entities.ids.generated.js.map +2 -2
  14. package/dist/generated/entity-fields-registry.js +72 -0
  15. package/dist/generated/entity-fields-registry.js.map +2 -2
  16. package/dist/helpers/integration/customerGroupsFixtures.js.map +2 -2
  17. package/dist/modules/catalog/acl.js +8 -1
  18. package/dist/modules/catalog/acl.js.map +2 -2
  19. package/dist/modules/catalog/api/config/omnibus/route.js +184 -0
  20. package/dist/modules/catalog/api/config/omnibus/route.js.map +7 -0
  21. package/dist/modules/catalog/api/prices/history/route.js +131 -0
  22. package/dist/modules/catalog/api/prices/history/route.js.map +7 -0
  23. package/dist/modules/catalog/api/prices/omnibus-preview/route.js +184 -0
  24. package/dist/modules/catalog/api/prices/omnibus-preview/route.js.map +7 -0
  25. package/dist/modules/catalog/api/products/route.js +54 -245
  26. package/dist/modules/catalog/api/products/route.js.map +3 -3
  27. package/dist/modules/catalog/api/tags/route.js +6 -0
  28. package/dist/modules/catalog/api/tags/route.js.map +2 -2
  29. package/dist/modules/catalog/backend/config/catalog/page.js +3 -1
  30. package/dist/modules/catalog/backend/config/catalog/page.js.map +2 -2
  31. package/dist/modules/catalog/cli.js +80 -2
  32. package/dist/modules/catalog/cli.js.map +2 -2
  33. package/dist/modules/catalog/commands/categories.js +51 -2
  34. package/dist/modules/catalog/commands/categories.js.map +2 -2
  35. package/dist/modules/catalog/commands/prices.js +43 -1
  36. package/dist/modules/catalog/commands/prices.js.map +2 -2
  37. package/dist/modules/catalog/commands/products.js +71 -0
  38. package/dist/modules/catalog/commands/products.js.map +2 -2
  39. package/dist/modules/catalog/commands/variants.js +7 -0
  40. package/dist/modules/catalog/commands/variants.js.map +2 -2
  41. package/dist/modules/catalog/components/OmnibusSettings.js +703 -0
  42. package/dist/modules/catalog/components/OmnibusSettings.js.map +7 -0
  43. package/dist/modules/catalog/components/PriceEditorOmnibusRow.js +152 -0
  44. package/dist/modules/catalog/components/PriceEditorOmnibusRow.js.map +7 -0
  45. package/dist/modules/catalog/components/lookupSources.js +31 -0
  46. package/dist/modules/catalog/components/lookupSources.js.map +7 -0
  47. package/dist/modules/catalog/components/prices/PriceScopeSelectors.js +8 -2
  48. package/dist/modules/catalog/components/prices/PriceScopeSelectors.js.map +2 -2
  49. package/dist/modules/catalog/components/products/VariantBuilder.js +11 -1
  50. package/dist/modules/catalog/components/products/VariantBuilder.js.map +2 -2
  51. package/dist/modules/catalog/data/entities.js +122 -1
  52. package/dist/modules/catalog/data/entities.js.map +2 -2
  53. package/dist/modules/catalog/data/validators.js +78 -0
  54. package/dist/modules/catalog/data/validators.js.map +2 -2
  55. package/dist/modules/catalog/di.js +18 -0
  56. package/dist/modules/catalog/di.js.map +2 -2
  57. package/dist/modules/catalog/lib/omnibus.js +285 -0
  58. package/dist/modules/catalog/lib/omnibus.js.map +7 -0
  59. package/dist/modules/catalog/lib/omnibusBackfill.js +146 -0
  60. package/dist/modules/catalog/lib/omnibusBackfill.js.map +7 -0
  61. package/dist/modules/catalog/lib/omnibusCache.js +89 -0
  62. package/dist/modules/catalog/lib/omnibusCache.js.map +7 -0
  63. package/dist/modules/catalog/lib/omnibusConfig.js +64 -0
  64. package/dist/modules/catalog/lib/omnibusConfig.js.map +7 -0
  65. package/dist/modules/catalog/lib/omnibusHistoryQueries.js +191 -0
  66. package/dist/modules/catalog/lib/omnibusHistoryQueries.js.map +7 -0
  67. package/dist/modules/catalog/lib/omnibusPresentedEntry.js +73 -0
  68. package/dist/modules/catalog/lib/omnibusPresentedEntry.js.map +7 -0
  69. package/dist/modules/catalog/lib/omnibusProductListEnrichment.js +94 -0
  70. package/dist/modules/catalog/lib/omnibusProductListEnrichment.js.map +7 -0
  71. package/dist/modules/catalog/lib/omnibusTypes.js +115 -0
  72. package/dist/modules/catalog/lib/omnibusTypes.js.map +7 -0
  73. package/dist/modules/catalog/lib/optionSchemaTranslations.js +55 -0
  74. package/dist/modules/catalog/lib/optionSchemaTranslations.js.map +7 -0
  75. package/dist/modules/catalog/lib/priceHistoryQuery.js +103 -0
  76. package/dist/modules/catalog/lib/priceHistoryQuery.js.map +7 -0
  77. package/dist/modules/catalog/lib/pricing.js +26 -2
  78. package/dist/modules/catalog/lib/pricing.js.map +2 -2
  79. package/dist/modules/catalog/lib/productFilters.js +273 -0
  80. package/dist/modules/catalog/lib/productFilters.js.map +7 -0
  81. package/dist/modules/catalog/lib/productScopeKeys.js +105 -0
  82. package/dist/modules/catalog/lib/productScopeKeys.js.map +7 -0
  83. package/dist/modules/catalog/lib/productScopeReindex.js +73 -0
  84. package/dist/modules/catalog/lib/productScopeReindex.js.map +7 -0
  85. package/dist/modules/catalog/migrations/Migration20261005105352_catalog.js +39 -0
  86. package/dist/modules/catalog/migrations/Migration20261005105352_catalog.js.map +7 -0
  87. package/dist/modules/catalog/migrations/Migration20261005150824_catalog.js +39 -0
  88. package/dist/modules/catalog/migrations/Migration20261005150824_catalog.js.map +7 -0
  89. package/dist/modules/catalog/services/catalogOmnibusService.js +480 -0
  90. package/dist/modules/catalog/services/catalogOmnibusService.js.map +7 -0
  91. package/dist/modules/catalog/setup.js +3 -1
  92. package/dist/modules/catalog/setup.js.map +2 -2
  93. package/dist/modules/catalog/subscribers/product-scope-keys-category-deleted.js +16 -0
  94. package/dist/modules/catalog/subscribers/product-scope-keys-category-deleted.js.map +7 -0
  95. package/dist/modules/catalog/subscribers/product-scope-keys-category-updated.js +17 -0
  96. package/dist/modules/catalog/subscribers/product-scope-keys-category-updated.js.map +7 -0
  97. package/dist/modules/catalog/translations.js +10 -0
  98. package/dist/modules/catalog/translations.js.map +2 -2
  99. package/dist/modules/configs/lib/upgrade-actions.js +28 -0
  100. package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
  101. package/dist/modules/customer_groups/api/customer-groups/[id]/terms/route.js +5 -0
  102. package/dist/modules/customer_groups/api/customer-groups/[id]/terms/route.js.map +2 -2
  103. package/dist/modules/customer_groups/components/CustomerGroupTermsSection.js +111 -3
  104. package/dist/modules/customer_groups/components/CustomerGroupTermsSection.js.map +2 -2
  105. package/dist/modules/customer_groups/components/customerGroupAssortmentScope.js +43 -0
  106. package/dist/modules/customer_groups/components/customerGroupAssortmentScope.js.map +7 -0
  107. package/dist/modules/customer_groups/data/entities.js +3 -0
  108. package/dist/modules/customer_groups/data/entities.js.map +2 -2
  109. package/dist/modules/customer_groups/data/validators.js +9 -0
  110. package/dist/modules/customer_groups/data/validators.js.map +2 -2
  111. package/dist/modules/customer_groups/migrations/Migration20261005110250_customer_groups.js +17 -0
  112. package/dist/modules/customer_groups/migrations/Migration20261005110250_customer_groups.js.map +7 -0
  113. package/dist/modules/customer_groups/services/customerGroupsService.js +62 -15
  114. package/dist/modules/customer_groups/services/customerGroupsService.js.map +2 -2
  115. package/dist/modules/ecommerce/acl.js +13 -0
  116. package/dist/modules/ecommerce/acl.js.map +7 -0
  117. package/dist/modules/ecommerce/api/domain-mappings/route.js +72 -0
  118. package/dist/modules/ecommerce/api/domain-mappings/route.js.map +7 -0
  119. package/dist/modules/ecommerce/api/openapi.js +27 -0
  120. package/dist/modules/ecommerce/api/openapi.js.map +7 -0
  121. package/dist/modules/ecommerce/api/store-channel-bindings/[id]/assortment-count/route.js +109 -0
  122. package/dist/modules/ecommerce/api/store-channel-bindings/[id]/assortment-count/route.js.map +7 -0
  123. package/dist/modules/ecommerce/api/store-channel-bindings/crud.js +265 -0
  124. package/dist/modules/ecommerce/api/store-channel-bindings/crud.js.map +7 -0
  125. package/dist/modules/ecommerce/api/store-channel-bindings/route.js +67 -0
  126. package/dist/modules/ecommerce/api/store-channel-bindings/route.js.map +7 -0
  127. package/dist/modules/ecommerce/api/store-domain-bindings/crud.js +296 -0
  128. package/dist/modules/ecommerce/api/store-domain-bindings/crud.js.map +7 -0
  129. package/dist/modules/ecommerce/api/store-domain-bindings/route.js +73 -0
  130. package/dist/modules/ecommerce/api/store-domain-bindings/route.js.map +7 -0
  131. package/dist/modules/ecommerce/api/storefront/categories/[slug]/route.js +87 -0
  132. package/dist/modules/ecommerce/api/storefront/categories/[slug]/route.js.map +7 -0
  133. package/dist/modules/ecommerce/api/storefront/categories/openapiSchemas.js +50 -0
  134. package/dist/modules/ecommerce/api/storefront/categories/openapiSchemas.js.map +7 -0
  135. package/dist/modules/ecommerce/api/storefront/categories/route.js +75 -0
  136. package/dist/modules/ecommerce/api/storefront/categories/route.js.map +7 -0
  137. package/dist/modules/ecommerce/api/storefront/context/route.js +163 -0
  138. package/dist/modules/ecommerce/api/storefront/context/route.js.map +7 -0
  139. package/dist/modules/ecommerce/api/storefront/products/[idOrHandle]/route.js +87 -0
  140. package/dist/modules/ecommerce/api/storefront/products/[idOrHandle]/route.js.map +7 -0
  141. package/dist/modules/ecommerce/api/storefront/products/openapiSchemas.js +177 -0
  142. package/dist/modules/ecommerce/api/storefront/products/openapiSchemas.js.map +7 -0
  143. package/dist/modules/ecommerce/api/storefront/products/route.js +78 -0
  144. package/dist/modules/ecommerce/api/storefront/products/route.js.map +7 -0
  145. package/dist/modules/ecommerce/api/storefront/search/suggest/openapiSchemas.js +25 -0
  146. package/dist/modules/ecommerce/api/storefront/search/suggest/openapiSchemas.js.map +7 -0
  147. package/dist/modules/ecommerce/api/storefront/search/suggest/route.js +75 -0
  148. package/dist/modules/ecommerce/api/storefront/search/suggest/route.js.map +7 -0
  149. package/dist/modules/ecommerce/api/storefront/storefrontRouteSupport.js +58 -0
  150. package/dist/modules/ecommerce/api/storefront/storefrontRouteSupport.js.map +7 -0
  151. package/dist/modules/ecommerce/api/stores/[id]/branding/route.js +183 -0
  152. package/dist/modules/ecommerce/api/stores/[id]/branding/route.js.map +7 -0
  153. package/dist/modules/ecommerce/api/stores/[id]/preview-branding/route.js +92 -0
  154. package/dist/modules/ecommerce/api/stores/[id]/preview-branding/route.js.map +7 -0
  155. package/dist/modules/ecommerce/api/stores/crud.js +401 -0
  156. package/dist/modules/ecommerce/api/stores/crud.js.map +7 -0
  157. package/dist/modules/ecommerce/api/stores/route.js +64 -0
  158. package/dist/modules/ecommerce/api/stores/route.js.map +7 -0
  159. package/dist/modules/ecommerce/backend/config/ecommerce/[id]/page.js +9 -0
  160. package/dist/modules/ecommerce/backend/config/ecommerce/[id]/page.js.map +7 -0
  161. package/dist/modules/ecommerce/backend/config/ecommerce/[id]/page.meta.js +16 -0
  162. package/dist/modules/ecommerce/backend/config/ecommerce/[id]/page.meta.js.map +7 -0
  163. package/dist/modules/ecommerce/backend/config/ecommerce/page.js +9 -0
  164. package/dist/modules/ecommerce/backend/config/ecommerce/page.js.map +7 -0
  165. package/dist/modules/ecommerce/backend/config/ecommerce/page.meta.js +16 -0
  166. package/dist/modules/ecommerce/backend/config/ecommerce/page.meta.js.map +7 -0
  167. package/dist/modules/ecommerce/commands/stores.js +110 -0
  168. package/dist/modules/ecommerce/commands/stores.js.map +7 -0
  169. package/dist/modules/ecommerce/components/ChannelAssortmentCount.js +90 -0
  170. package/dist/modules/ecommerce/components/ChannelAssortmentCount.js.map +7 -0
  171. package/dist/modules/ecommerce/components/ChannelBindingFormSections.js +33 -0
  172. package/dist/modules/ecommerce/components/ChannelBindingFormSections.js.map +7 -0
  173. package/dist/modules/ecommerce/components/ChannelLookupPicker.js +11 -0
  174. package/dist/modules/ecommerce/components/ChannelLookupPicker.js.map +7 -0
  175. package/dist/modules/ecommerce/components/DomainStatusWarning.js +24 -0
  176. package/dist/modules/ecommerce/components/DomainStatusWarning.js.map +7 -0
  177. package/dist/modules/ecommerce/components/StoreAvailabilityDefaultsSection.js +190 -0
  178. package/dist/modules/ecommerce/components/StoreAvailabilityDefaultsSection.js.map +7 -0
  179. package/dist/modules/ecommerce/components/StoreBrandingControls.js +67 -0
  180. package/dist/modules/ecommerce/components/StoreBrandingControls.js.map +7 -0
  181. package/dist/modules/ecommerce/components/StoreBrandingPreview.js +69 -0
  182. package/dist/modules/ecommerce/components/StoreBrandingPreview.js.map +7 -0
  183. package/dist/modules/ecommerce/components/StoreBrandingTab.js +218 -0
  184. package/dist/modules/ecommerce/components/StoreBrandingTab.js.map +7 -0
  185. package/dist/modules/ecommerce/components/StoreChannelBindingDialog.js +270 -0
  186. package/dist/modules/ecommerce/components/StoreChannelBindingDialog.js.map +7 -0
  187. package/dist/modules/ecommerce/components/StoreChannelsTab.js +313 -0
  188. package/dist/modules/ecommerce/components/StoreChannelsTab.js.map +7 -0
  189. package/dist/modules/ecommerce/components/StoreCreateDialog.js +164 -0
  190. package/dist/modules/ecommerce/components/StoreCreateDialog.js.map +7 -0
  191. package/dist/modules/ecommerce/components/StoreDomainBindingDialog.js +162 -0
  192. package/dist/modules/ecommerce/components/StoreDomainBindingDialog.js.map +7 -0
  193. package/dist/modules/ecommerce/components/StoreDomainsTab.js +357 -0
  194. package/dist/modules/ecommerce/components/StoreDomainsTab.js.map +7 -0
  195. package/dist/modules/ecommerce/components/StoreEditPage.js +155 -0
  196. package/dist/modules/ecommerce/components/StoreEditPage.js.map +7 -0
  197. package/dist/modules/ecommerce/components/StoreGeneralTab.js +167 -0
  198. package/dist/modules/ecommerce/components/StoreGeneralTab.js.map +7 -0
  199. package/dist/modules/ecommerce/components/StoreListPage.js +290 -0
  200. package/dist/modules/ecommerce/components/StoreListPage.js.map +7 -0
  201. package/dist/modules/ecommerce/components/StoreSeoTab.js +132 -0
  202. package/dist/modules/ecommerce/components/StoreSeoTab.js.map +7 -0
  203. package/dist/modules/ecommerce/components/StoreStatusBadge.js +20 -0
  204. package/dist/modules/ecommerce/components/StoreStatusBadge.js.map +7 -0
  205. package/dist/modules/ecommerce/components/channelLookups.js +28 -0
  206. package/dist/modules/ecommerce/components/channelLookups.js.map +7 -0
  207. package/dist/modules/ecommerce/components/storeAdmin.js +41 -0
  208. package/dist/modules/ecommerce/components/storeAdmin.js.map +7 -0
  209. package/dist/modules/ecommerce/components/storeBrandingForm.js +197 -0
  210. package/dist/modules/ecommerce/components/storeBrandingForm.js.map +7 -0
  211. package/dist/modules/ecommerce/components/storeChannels.js +140 -0
  212. package/dist/modules/ecommerce/components/storeChannels.js.map +7 -0
  213. package/dist/modules/ecommerce/components/storeDomains.js +167 -0
  214. package/dist/modules/ecommerce/components/storeDomains.js.map +7 -0
  215. package/dist/modules/ecommerce/components/storeEditTabs.js +62 -0
  216. package/dist/modules/ecommerce/components/storeEditTabs.js.map +7 -0
  217. package/dist/modules/ecommerce/components/storeGeneral.js +95 -0
  218. package/dist/modules/ecommerce/components/storeGeneral.js.map +7 -0
  219. package/dist/modules/ecommerce/components/storeSeo.js +69 -0
  220. package/dist/modules/ecommerce/components/storeSeo.js.map +7 -0
  221. package/dist/modules/ecommerce/components/useChannelAssortmentCount.js +45 -0
  222. package/dist/modules/ecommerce/components/useChannelAssortmentCount.js.map +7 -0
  223. package/dist/modules/ecommerce/components/useStoreAccess.js +28 -0
  224. package/dist/modules/ecommerce/components/useStoreAccess.js.map +7 -0
  225. package/dist/modules/ecommerce/data/enrichers.js +157 -0
  226. package/dist/modules/ecommerce/data/enrichers.js.map +7 -0
  227. package/dist/modules/ecommerce/data/entities.js +197 -0
  228. package/dist/modules/ecommerce/data/entities.js.map +7 -0
  229. package/dist/modules/ecommerce/data/validators.js +406 -0
  230. package/dist/modules/ecommerce/data/validators.js.map +7 -0
  231. package/dist/modules/ecommerce/di.js +11 -0
  232. package/dist/modules/ecommerce/di.js.map +7 -0
  233. package/dist/modules/ecommerce/events.js +27 -0
  234. package/dist/modules/ecommerce/events.js.map +7 -0
  235. package/dist/modules/ecommerce/index.js +16 -0
  236. package/dist/modules/ecommerce/index.js.map +7 -0
  237. package/dist/modules/ecommerce/lib/assortmentCount.js +112 -0
  238. package/dist/modules/ecommerce/lib/assortmentCount.js.map +7 -0
  239. package/dist/modules/ecommerce/lib/brandingStyles.js +184 -0
  240. package/dist/modules/ecommerce/lib/brandingStyles.js.map +7 -0
  241. package/dist/modules/ecommerce/lib/buyerContext.js +284 -0
  242. package/dist/modules/ecommerce/lib/buyerContext.js.map +7 -0
  243. package/dist/modules/ecommerce/lib/cacheInvalidation.js +186 -0
  244. package/dist/modules/ecommerce/lib/cacheInvalidation.js.map +7 -0
  245. package/dist/modules/ecommerce/lib/cacheKeys.js +236 -0
  246. package/dist/modules/ecommerce/lib/cacheKeys.js.map +7 -0
  247. package/dist/modules/ecommerce/lib/createConflictRecheck.js +29 -0
  248. package/dist/modules/ecommerce/lib/createConflictRecheck.js.map +7 -0
  249. package/dist/modules/ecommerce/lib/crudEvents.js +94 -0
  250. package/dist/modules/ecommerce/lib/crudEvents.js.map +7 -0
  251. package/dist/modules/ecommerce/lib/crudSupport.js +64 -0
  252. package/dist/modules/ecommerce/lib/crudSupport.js.map +7 -0
  253. package/dist/modules/ecommerce/lib/domainBindingRebind.js +99 -0
  254. package/dist/modules/ecommerce/lib/domainBindingRebind.js.map +7 -0
  255. package/dist/modules/ecommerce/lib/domainMappingSummaries.js +39 -0
  256. package/dist/modules/ecommerce/lib/domainMappingSummaries.js.map +7 -0
  257. package/dist/modules/ecommerce/lib/exclusiveFlag.js +57 -0
  258. package/dist/modules/ecommerce/lib/exclusiveFlag.js.map +7 -0
  259. package/dist/modules/ecommerce/lib/references.js +70 -0
  260. package/dist/modules/ecommerce/lib/references.js.map +7 -0
  261. package/dist/modules/ecommerce/lib/seedDraftStore.js +99 -0
  262. package/dist/modules/ecommerce/lib/seedDraftStore.js.map +7 -0
  263. package/dist/modules/ecommerce/lib/storeBindingCascade.js +109 -0
  264. package/dist/modules/ecommerce/lib/storeBindingCascade.js.map +7 -0
  265. package/dist/modules/ecommerce/lib/storeBranding.js +40 -0
  266. package/dist/modules/ecommerce/lib/storeBranding.js.map +7 -0
  267. package/dist/modules/ecommerce/lib/storeBrandingRoute.js +76 -0
  268. package/dist/modules/ecommerce/lib/storeBrandingRoute.js.map +7 -0
  269. package/dist/modules/ecommerce/lib/storeContext.js +393 -0
  270. package/dist/modules/ecommerce/lib/storeContext.js.map +7 -0
  271. package/dist/modules/ecommerce/lib/storeContextService.js +43 -0
  272. package/dist/modules/ecommerce/lib/storeContextService.js.map +7 -0
  273. package/dist/modules/ecommerce/lib/storeLocale.js +60 -0
  274. package/dist/modules/ecommerce/lib/storeLocale.js.map +7 -0
  275. package/dist/modules/ecommerce/lib/storeNotifications.js +41 -0
  276. package/dist/modules/ecommerce/lib/storeNotifications.js.map +7 -0
  277. package/dist/modules/ecommerce/lib/storefrontCatalogSupport.js +248 -0
  278. package/dist/modules/ecommerce/lib/storefrontCatalogSupport.js.map +7 -0
  279. package/dist/modules/ecommerce/lib/storefrontCategories.js +241 -0
  280. package/dist/modules/ecommerce/lib/storefrontCategories.js.map +7 -0
  281. package/dist/modules/ecommerce/lib/storefrontCategoryCache.js +71 -0
  282. package/dist/modules/ecommerce/lib/storefrontCategoryCache.js.map +7 -0
  283. package/dist/modules/ecommerce/lib/storefrontDetail.js +447 -0
  284. package/dist/modules/ecommerce/lib/storefrontDetail.js.map +7 -0
  285. package/dist/modules/ecommerce/lib/storefrontFacets.js +441 -0
  286. package/dist/modules/ecommerce/lib/storefrontFacets.js.map +7 -0
  287. package/dist/modules/ecommerce/lib/storefrontOptionFilter.js +15 -0
  288. package/dist/modules/ecommerce/lib/storefrontOptionFilter.js.map +7 -0
  289. package/dist/modules/ecommerce/lib/storefrontPricing.js +393 -0
  290. package/dist/modules/ecommerce/lib/storefrontPricing.js.map +7 -0
  291. package/dist/modules/ecommerce/lib/storefrontProductCache.js +90 -0
  292. package/dist/modules/ecommerce/lib/storefrontProductCache.js.map +7 -0
  293. package/dist/modules/ecommerce/lib/storefrontProductScope.js +107 -0
  294. package/dist/modules/ecommerce/lib/storefrontProductScope.js.map +7 -0
  295. package/dist/modules/ecommerce/lib/storefrontProducts.js +670 -0
  296. package/dist/modules/ecommerce/lib/storefrontProducts.js.map +7 -0
  297. package/dist/modules/ecommerce/lib/storefrontQuery.js +156 -0
  298. package/dist/modules/ecommerce/lib/storefrontQuery.js.map +7 -0
  299. package/dist/modules/ecommerce/lib/storefrontRateLimit.js +99 -0
  300. package/dist/modules/ecommerce/lib/storefrontRateLimit.js.map +7 -0
  301. package/dist/modules/ecommerce/lib/storefrontSearch.js +303 -0
  302. package/dist/modules/ecommerce/lib/storefrontSearch.js.map +7 -0
  303. package/dist/modules/ecommerce/lib/storefrontSearchCache.js +35 -0
  304. package/dist/modules/ecommerce/lib/storefrontSearchCache.js.map +7 -0
  305. package/dist/modules/ecommerce/lib/subscriberSupport.js +33 -0
  306. package/dist/modules/ecommerce/lib/subscriberSupport.js.map +7 -0
  307. package/dist/modules/ecommerce/lib/types.js +1 -0
  308. package/dist/modules/ecommerce/lib/types.js.map +7 -0
  309. package/dist/modules/ecommerce/migrations/Migration20261005113233_ecommerce.js +32 -0
  310. package/dist/modules/ecommerce/migrations/Migration20261005113233_ecommerce.js.map +7 -0
  311. package/dist/modules/ecommerce/migrations/Migration20261006112733_ecommerce.js +17 -0
  312. package/dist/modules/ecommerce/migrations/Migration20261006112733_ecommerce.js.map +7 -0
  313. package/dist/modules/ecommerce/notifications.client.js +47 -0
  314. package/dist/modules/ecommerce/notifications.client.js.map +7 -0
  315. package/dist/modules/ecommerce/notifications.js +48 -0
  316. package/dist/modules/ecommerce/notifications.js.map +7 -0
  317. package/dist/modules/ecommerce/search.js +83 -0
  318. package/dist/modules/ecommerce/search.js.map +7 -0
  319. package/dist/modules/ecommerce/setup.js +17 -0
  320. package/dist/modules/ecommerce/setup.js.map +7 -0
  321. package/dist/modules/ecommerce/subscribers/assortment-empty-notifications.js +14 -0
  322. package/dist/modules/ecommerce/subscribers/assortment-empty-notifications.js.map +7 -0
  323. package/dist/modules/ecommerce/subscribers/availability-policy-cache-invalidation.js +14 -0
  324. package/dist/modules/ecommerce/subscribers/availability-policy-cache-invalidation.js.map +7 -0
  325. package/dist/modules/ecommerce/subscribers/catalog-category-cache-invalidation.js +14 -0
  326. package/dist/modules/ecommerce/subscribers/catalog-category-cache-invalidation.js.map +7 -0
  327. package/dist/modules/ecommerce/subscribers/catalog-price-cache-invalidation.js +14 -0
  328. package/dist/modules/ecommerce/subscribers/catalog-price-cache-invalidation.js.map +7 -0
  329. package/dist/modules/ecommerce/subscribers/catalog-product-cache-invalidation.js +14 -0
  330. package/dist/modules/ecommerce/subscribers/catalog-product-cache-invalidation.js.map +7 -0
  331. package/dist/modules/ecommerce/subscribers/catalog-variant-cache-invalidation.js +14 -0
  332. package/dist/modules/ecommerce/subscribers/catalog-variant-cache-invalidation.js.map +7 -0
  333. package/dist/modules/ecommerce/subscribers/customer-group-cache-invalidation.js +14 -0
  334. package/dist/modules/ecommerce/subscribers/customer-group-cache-invalidation.js.map +7 -0
  335. package/dist/modules/ecommerce/subscribers/customer-group-membership-cache-invalidation.js +14 -0
  336. package/dist/modules/ecommerce/subscribers/customer-group-membership-cache-invalidation.js.map +7 -0
  337. package/dist/modules/ecommerce/subscribers/customer-group-terms-cache-invalidation.js +14 -0
  338. package/dist/modules/ecommerce/subscribers/customer-group-terms-cache-invalidation.js.map +7 -0
  339. package/dist/modules/ecommerce/subscribers/customer-user-deleted-cache-invalidation.js +14 -0
  340. package/dist/modules/ecommerce/subscribers/customer-user-deleted-cache-invalidation.js.map +7 -0
  341. package/dist/modules/ecommerce/subscribers/customer-user-updated-cache-invalidation.js +14 -0
  342. package/dist/modules/ecommerce/subscribers/customer-user-updated-cache-invalidation.js.map +7 -0
  343. package/dist/modules/ecommerce/subscribers/domain-binding-rebind.js +16 -0
  344. package/dist/modules/ecommerce/subscribers/domain-binding-rebind.js.map +7 -0
  345. package/dist/modules/ecommerce/subscribers/domain-mapping-cache-invalidation.js +14 -0
  346. package/dist/modules/ecommerce/subscribers/domain-mapping-cache-invalidation.js.map +7 -0
  347. package/dist/modules/ecommerce/subscribers/store-cache-invalidation.js +14 -0
  348. package/dist/modules/ecommerce/subscribers/store-cache-invalidation.js.map +7 -0
  349. package/dist/modules/ecommerce/subscribers/store-channel-binding-cache-invalidation.js +14 -0
  350. package/dist/modules/ecommerce/subscribers/store-channel-binding-cache-invalidation.js.map +7 -0
  351. package/dist/modules/ecommerce/subscribers/store-domain-binding-cache-invalidation.js +14 -0
  352. package/dist/modules/ecommerce/subscribers/store-domain-binding-cache-invalidation.js.map +7 -0
  353. package/dist/modules/ecommerce/subscribers/store-notifications.js +14 -0
  354. package/dist/modules/ecommerce/subscribers/store-notifications.js.map +7 -0
  355. package/dist/modules/query_index/lib/batch.js +23 -9
  356. package/dist/modules/query_index/lib/batch.js.map +2 -2
  357. package/dist/modules/query_index/lib/doc-enrichers.js +212 -0
  358. package/dist/modules/query_index/lib/doc-enrichers.js.map +7 -0
  359. package/dist/modules/query_index/lib/document.js +2 -0
  360. package/dist/modules/query_index/lib/document.js.map +2 -2
  361. package/dist/modules/query_index/lib/engine.js +68 -1
  362. package/dist/modules/query_index/lib/engine.js.map +2 -2
  363. package/dist/modules/query_index/lib/indexer.js +7 -0
  364. package/dist/modules/query_index/lib/indexer.js.map +2 -2
  365. package/dist/modules/query_index/lib/search-tokens.js +2 -0
  366. package/dist/modules/query_index/lib/search-tokens.js.map +2 -2
  367. package/dist/modules/query_index/migrations/Migration20261005201500_query_index.js +17 -0
  368. package/dist/modules/query_index/migrations/Migration20261005201500_query_index.js.map +7 -0
  369. package/dist/modules/translations/components/TranslationManager.js +3 -3
  370. package/dist/modules/translations/components/TranslationManager.js.map +2 -2
  371. package/dist/modules/translations/data/validators.js +1 -1
  372. package/dist/modules/translations/data/validators.js.map +1 -1
  373. package/dist/modules/translations/lib/batch.js +21 -1
  374. package/dist/modules/translations/lib/batch.js.map +2 -2
  375. package/dist/modules/translations/lib/helpers.js +3 -0
  376. package/dist/modules/translations/lib/helpers.js.map +2 -2
  377. package/dist/modules/translations/lib/resolve-field-list.js +14 -2
  378. package/dist/modules/translations/lib/resolve-field-list.js.map +2 -2
  379. package/generated/entities/catalog_price_history_entry/index.ts +25 -0
  380. package/generated/entities/customer_group_terms/index.ts +1 -0
  381. package/generated/entities/ecommerce_store/index.ts +15 -0
  382. package/generated/entities/ecommerce_store_channel_binding/index.ts +13 -0
  383. package/generated/entities/ecommerce_store_domain_binding/index.ts +10 -0
  384. package/generated/entities.ids.generated.ts +8 -1
  385. package/generated/entity-fields-registry.ts +72 -0
  386. package/package.json +7 -7
  387. package/src/helpers/integration/customerGroupsFixtures.ts +2 -0
  388. package/src/modules/auth/i18n/de.json +8 -0
  389. package/src/modules/auth/i18n/en.json +8 -0
  390. package/src/modules/auth/i18n/es.json +8 -0
  391. package/src/modules/auth/i18n/ko.json +8 -0
  392. package/src/modules/auth/i18n/pl.json +8 -0
  393. package/src/modules/catalog/AGENTS.md +5 -2
  394. package/src/modules/catalog/acl.ts +7 -0
  395. package/src/modules/catalog/api/config/omnibus/route.ts +204 -0
  396. package/src/modules/catalog/api/prices/history/route.ts +139 -0
  397. package/src/modules/catalog/api/prices/omnibus-preview/route.ts +202 -0
  398. package/src/modules/catalog/api/products/route.ts +53 -308
  399. package/src/modules/catalog/api/tags/route.ts +6 -0
  400. package/src/modules/catalog/backend/config/catalog/page.tsx +2 -0
  401. package/src/modules/catalog/cli.ts +84 -1
  402. package/src/modules/catalog/commands/categories.ts +54 -2
  403. package/src/modules/catalog/commands/prices.ts +43 -1
  404. package/src/modules/catalog/commands/products.ts +84 -1
  405. package/src/modules/catalog/commands/variants.ts +7 -0
  406. package/src/modules/catalog/components/OmnibusSettings.tsx +832 -0
  407. package/src/modules/catalog/components/PriceEditorOmnibusRow.tsx +219 -0
  408. package/src/modules/catalog/components/lookupSources.ts +29 -0
  409. package/src/modules/catalog/components/prices/PriceScopeSelectors.tsx +9 -0
  410. package/src/modules/catalog/components/products/VariantBuilder.tsx +9 -0
  411. package/src/modules/catalog/data/entities.ts +123 -1
  412. package/src/modules/catalog/data/validators.ts +118 -0
  413. package/src/modules/catalog/di.ts +23 -0
  414. package/src/modules/catalog/i18n/de.json +77 -0
  415. package/src/modules/catalog/i18n/en.json +77 -0
  416. package/src/modules/catalog/i18n/es.json +77 -0
  417. package/src/modules/catalog/i18n/ko.json +77 -0
  418. package/src/modules/catalog/i18n/pl.json +77 -0
  419. package/src/modules/catalog/lib/omnibus.ts +406 -0
  420. package/src/modules/catalog/lib/omnibusBackfill.ts +198 -0
  421. package/src/modules/catalog/lib/omnibusCache.ts +111 -0
  422. package/src/modules/catalog/lib/omnibusConfig.ts +73 -0
  423. package/src/modules/catalog/lib/omnibusHistoryQueries.ts +248 -0
  424. package/src/modules/catalog/lib/omnibusPresentedEntry.ts +76 -0
  425. package/src/modules/catalog/lib/omnibusProductListEnrichment.ts +125 -0
  426. package/src/modules/catalog/lib/omnibusTypes.ts +202 -0
  427. package/src/modules/catalog/lib/optionSchemaTranslations.ts +55 -0
  428. package/src/modules/catalog/lib/priceHistoryQuery.ts +155 -0
  429. package/src/modules/catalog/lib/pricing.ts +42 -4
  430. package/src/modules/catalog/lib/productFilters.ts +349 -0
  431. package/src/modules/catalog/lib/productScopeKeys.ts +162 -0
  432. package/src/modules/catalog/lib/productScopeReindex.ts +139 -0
  433. package/src/modules/catalog/migrations/.snapshot-open-mercato.json +572 -0
  434. package/src/modules/catalog/migrations/Migration20261005105352_catalog.ts +39 -0
  435. package/src/modules/catalog/migrations/Migration20261005150824_catalog.ts +39 -0
  436. package/src/modules/catalog/services/catalogOmnibusService.ts +642 -0
  437. package/src/modules/catalog/setup.ts +2 -0
  438. package/src/modules/catalog/subscribers/product-scope-keys-category-deleted.ts +18 -0
  439. package/src/modules/catalog/subscribers/product-scope-keys-category-updated.ts +19 -0
  440. package/src/modules/catalog/translations.ts +12 -0
  441. package/src/modules/configs/i18n/de.json +5 -1
  442. package/src/modules/configs/i18n/en.json +5 -1
  443. package/src/modules/configs/i18n/es.json +5 -1
  444. package/src/modules/configs/i18n/ko.json +5 -1
  445. package/src/modules/configs/i18n/pl.json +5 -1
  446. package/src/modules/configs/lib/upgrade-actions.ts +35 -0
  447. package/src/modules/customer_groups/api/customer-groups/[id]/terms/route.ts +7 -0
  448. package/src/modules/customer_groups/components/CustomerGroupTermsSection.tsx +124 -2
  449. package/src/modules/customer_groups/components/customerGroupAssortmentScope.ts +46 -0
  450. package/src/modules/customer_groups/data/entities.ts +4 -0
  451. package/src/modules/customer_groups/data/validators.ts +14 -0
  452. package/src/modules/customer_groups/i18n/de.json +12 -0
  453. package/src/modules/customer_groups/i18n/en.json +12 -0
  454. package/src/modules/customer_groups/i18n/es.json +12 -0
  455. package/src/modules/customer_groups/i18n/ko.json +12 -0
  456. package/src/modules/customer_groups/i18n/pl.json +12 -0
  457. package/src/modules/customer_groups/migrations/.snapshot-open-mercato.json +17 -0
  458. package/src/modules/customer_groups/migrations/Migration20261005110250_customer_groups.ts +15 -0
  459. package/src/modules/customer_groups/services/customerGroupsService.ts +74 -23
  460. package/src/modules/ecommerce/acl.ts +9 -0
  461. package/src/modules/ecommerce/api/domain-mappings/route.ts +76 -0
  462. package/src/modules/ecommerce/api/openapi.ts +28 -0
  463. package/src/modules/ecommerce/api/store-channel-bindings/[id]/assortment-count/route.ts +141 -0
  464. package/src/modules/ecommerce/api/store-channel-bindings/crud.ts +283 -0
  465. package/src/modules/ecommerce/api/store-channel-bindings/route.ts +67 -0
  466. package/src/modules/ecommerce/api/store-domain-bindings/crud.ts +324 -0
  467. package/src/modules/ecommerce/api/store-domain-bindings/route.ts +73 -0
  468. package/src/modules/ecommerce/api/storefront/categories/[slug]/route.ts +94 -0
  469. package/src/modules/ecommerce/api/storefront/categories/openapiSchemas.ts +63 -0
  470. package/src/modules/ecommerce/api/storefront/categories/route.ts +78 -0
  471. package/src/modules/ecommerce/api/storefront/context/route.ts +203 -0
  472. package/src/modules/ecommerce/api/storefront/products/[idOrHandle]/route.ts +94 -0
  473. package/src/modules/ecommerce/api/storefront/products/openapiSchemas.ts +193 -0
  474. package/src/modules/ecommerce/api/storefront/products/route.ts +81 -0
  475. package/src/modules/ecommerce/api/storefront/search/suggest/openapiSchemas.ts +25 -0
  476. package/src/modules/ecommerce/api/storefront/search/suggest/route.ts +78 -0
  477. package/src/modules/ecommerce/api/storefront/storefrontRouteSupport.ts +68 -0
  478. package/src/modules/ecommerce/api/stores/[id]/branding/route.ts +205 -0
  479. package/src/modules/ecommerce/api/stores/[id]/preview-branding/route.ts +99 -0
  480. package/src/modules/ecommerce/api/stores/crud.ts +457 -0
  481. package/src/modules/ecommerce/api/stores/route.ts +66 -0
  482. package/src/modules/ecommerce/backend/config/ecommerce/[id]/page.meta.ts +12 -0
  483. package/src/modules/ecommerce/backend/config/ecommerce/[id]/page.tsx +5 -0
  484. package/src/modules/ecommerce/backend/config/ecommerce/page.meta.ts +12 -0
  485. package/src/modules/ecommerce/backend/config/ecommerce/page.tsx +5 -0
  486. package/src/modules/ecommerce/commands/stores.ts +154 -0
  487. package/src/modules/ecommerce/components/ChannelAssortmentCount.tsx +138 -0
  488. package/src/modules/ecommerce/components/ChannelBindingFormSections.tsx +40 -0
  489. package/src/modules/ecommerce/components/ChannelLookupPicker.tsx +5 -0
  490. package/src/modules/ecommerce/components/DomainStatusWarning.tsx +32 -0
  491. package/src/modules/ecommerce/components/StoreAvailabilityDefaultsSection.tsx +219 -0
  492. package/src/modules/ecommerce/components/StoreBrandingControls.tsx +73 -0
  493. package/src/modules/ecommerce/components/StoreBrandingPreview.tsx +77 -0
  494. package/src/modules/ecommerce/components/StoreBrandingTab.tsx +242 -0
  495. package/src/modules/ecommerce/components/StoreChannelBindingDialog.tsx +306 -0
  496. package/src/modules/ecommerce/components/StoreChannelsTab.tsx +375 -0
  497. package/src/modules/ecommerce/components/StoreCreateDialog.tsx +204 -0
  498. package/src/modules/ecommerce/components/StoreDomainBindingDialog.tsx +198 -0
  499. package/src/modules/ecommerce/components/StoreDomainsTab.tsx +426 -0
  500. package/src/modules/ecommerce/components/StoreEditPage.tsx +193 -0
  501. package/src/modules/ecommerce/components/StoreGeneralTab.tsx +189 -0
  502. package/src/modules/ecommerce/components/StoreListPage.tsx +326 -0
  503. package/src/modules/ecommerce/components/StoreSeoTab.tsx +152 -0
  504. package/src/modules/ecommerce/components/StoreStatusBadge.tsx +22 -0
  505. package/src/modules/ecommerce/components/channelLookups.ts +24 -0
  506. package/src/modules/ecommerce/components/storeAdmin.ts +56 -0
  507. package/src/modules/ecommerce/components/storeBrandingForm.ts +230 -0
  508. package/src/modules/ecommerce/components/storeChannels.ts +210 -0
  509. package/src/modules/ecommerce/components/storeDomains.ts +221 -0
  510. package/src/modules/ecommerce/components/storeEditTabs.tsx +76 -0
  511. package/src/modules/ecommerce/components/storeGeneral.ts +155 -0
  512. package/src/modules/ecommerce/components/storeSeo.ts +78 -0
  513. package/src/modules/ecommerce/components/useChannelAssortmentCount.ts +67 -0
  514. package/src/modules/ecommerce/components/useStoreAccess.ts +36 -0
  515. package/src/modules/ecommerce/data/enrichers.ts +213 -0
  516. package/src/modules/ecommerce/data/entities.ts +168 -0
  517. package/src/modules/ecommerce/data/validators.ts +587 -0
  518. package/src/modules/ecommerce/di.ts +9 -0
  519. package/src/modules/ecommerce/events.ts +27 -0
  520. package/src/modules/ecommerce/i18n/de.json +341 -0
  521. package/src/modules/ecommerce/i18n/en.json +341 -0
  522. package/src/modules/ecommerce/i18n/es.json +341 -0
  523. package/src/modules/ecommerce/i18n/ko.json +341 -0
  524. package/src/modules/ecommerce/i18n/pl.json +341 -0
  525. package/src/modules/ecommerce/index.ts +14 -0
  526. package/src/modules/ecommerce/lib/assortmentCount.ts +175 -0
  527. package/src/modules/ecommerce/lib/brandingStyles.ts +215 -0
  528. package/src/modules/ecommerce/lib/buyerContext.ts +401 -0
  529. package/src/modules/ecommerce/lib/cacheInvalidation.ts +302 -0
  530. package/src/modules/ecommerce/lib/cacheKeys.ts +340 -0
  531. package/src/modules/ecommerce/lib/createConflictRecheck.ts +31 -0
  532. package/src/modules/ecommerce/lib/crudEvents.ts +128 -0
  533. package/src/modules/ecommerce/lib/crudSupport.ts +85 -0
  534. package/src/modules/ecommerce/lib/domainBindingRebind.ts +138 -0
  535. package/src/modules/ecommerce/lib/domainMappingSummaries.ts +71 -0
  536. package/src/modules/ecommerce/lib/exclusiveFlag.ts +90 -0
  537. package/src/modules/ecommerce/lib/references.ts +93 -0
  538. package/src/modules/ecommerce/lib/seedDraftStore.ts +125 -0
  539. package/src/modules/ecommerce/lib/storeBindingCascade.ts +138 -0
  540. package/src/modules/ecommerce/lib/storeBranding.ts +48 -0
  541. package/src/modules/ecommerce/lib/storeBrandingRoute.ts +91 -0
  542. package/src/modules/ecommerce/lib/storeContext.ts +669 -0
  543. package/src/modules/ecommerce/lib/storeContextService.ts +71 -0
  544. package/src/modules/ecommerce/lib/storeLocale.ts +88 -0
  545. package/src/modules/ecommerce/lib/storeNotifications.ts +55 -0
  546. package/src/modules/ecommerce/lib/storefrontCatalogSupport.ts +427 -0
  547. package/src/modules/ecommerce/lib/storefrontCategories.ts +419 -0
  548. package/src/modules/ecommerce/lib/storefrontCategoryCache.ts +113 -0
  549. package/src/modules/ecommerce/lib/storefrontDetail.ts +654 -0
  550. package/src/modules/ecommerce/lib/storefrontFacets.ts +697 -0
  551. package/src/modules/ecommerce/lib/storefrontOptionFilter.ts +44 -0
  552. package/src/modules/ecommerce/lib/storefrontPricing.ts +583 -0
  553. package/src/modules/ecommerce/lib/storefrontProductCache.ts +126 -0
  554. package/src/modules/ecommerce/lib/storefrontProductScope.ts +181 -0
  555. package/src/modules/ecommerce/lib/storefrontProducts.ts +963 -0
  556. package/src/modules/ecommerce/lib/storefrontQuery.ts +215 -0
  557. package/src/modules/ecommerce/lib/storefrontRateLimit.ts +126 -0
  558. package/src/modules/ecommerce/lib/storefrontSearch.ts +454 -0
  559. package/src/modules/ecommerce/lib/storefrontSearchCache.ts +49 -0
  560. package/src/modules/ecommerce/lib/subscriberSupport.ts +41 -0
  561. package/src/modules/ecommerce/lib/types.ts +69 -0
  562. package/src/modules/ecommerce/migrations/.snapshot-open-mercato.json +829 -0
  563. package/src/modules/ecommerce/migrations/Migration20261005113233_ecommerce.ts +32 -0
  564. package/src/modules/ecommerce/migrations/Migration20261006112733_ecommerce.ts +15 -0
  565. package/src/modules/ecommerce/notifications.client.ts +46 -0
  566. package/src/modules/ecommerce/notifications.ts +46 -0
  567. package/src/modules/ecommerce/search.ts +93 -0
  568. package/src/modules/ecommerce/setup.ts +15 -0
  569. package/src/modules/ecommerce/subscribers/assortment-empty-notifications.ts +12 -0
  570. package/src/modules/ecommerce/subscribers/availability-policy-cache-invalidation.ts +12 -0
  571. package/src/modules/ecommerce/subscribers/catalog-category-cache-invalidation.ts +12 -0
  572. package/src/modules/ecommerce/subscribers/catalog-price-cache-invalidation.ts +12 -0
  573. package/src/modules/ecommerce/subscribers/catalog-product-cache-invalidation.ts +12 -0
  574. package/src/modules/ecommerce/subscribers/catalog-variant-cache-invalidation.ts +12 -0
  575. package/src/modules/ecommerce/subscribers/customer-group-cache-invalidation.ts +12 -0
  576. package/src/modules/ecommerce/subscribers/customer-group-membership-cache-invalidation.ts +12 -0
  577. package/src/modules/ecommerce/subscribers/customer-group-terms-cache-invalidation.ts +12 -0
  578. package/src/modules/ecommerce/subscribers/customer-user-deleted-cache-invalidation.ts +12 -0
  579. package/src/modules/ecommerce/subscribers/customer-user-updated-cache-invalidation.ts +12 -0
  580. package/src/modules/ecommerce/subscribers/domain-binding-rebind.ts +14 -0
  581. package/src/modules/ecommerce/subscribers/domain-mapping-cache-invalidation.ts +12 -0
  582. package/src/modules/ecommerce/subscribers/store-cache-invalidation.ts +12 -0
  583. package/src/modules/ecommerce/subscribers/store-channel-binding-cache-invalidation.ts +12 -0
  584. package/src/modules/ecommerce/subscribers/store-domain-binding-cache-invalidation.ts +12 -0
  585. package/src/modules/ecommerce/subscribers/store-notifications.ts +12 -0
  586. package/src/modules/query_index/lib/batch.ts +25 -9
  587. package/src/modules/query_index/lib/doc-enrichers.ts +306 -0
  588. package/src/modules/query_index/lib/document.ts +2 -0
  589. package/src/modules/query_index/lib/engine.ts +58 -1
  590. package/src/modules/query_index/lib/indexer.ts +8 -0
  591. package/src/modules/query_index/lib/search-tokens.ts +2 -0
  592. package/src/modules/query_index/migrations/Migration20261005201500_query_index.ts +42 -0
  593. package/src/modules/translations/components/TranslationManager.tsx +3 -3
  594. package/src/modules/translations/data/validators.ts +1 -1
  595. package/src/modules/translations/lib/batch.ts +47 -0
  596. package/src/modules/translations/lib/helpers.ts +4 -0
  597. package/src/modules/translations/lib/resolve-field-list.ts +16 -2
@@ -0,0 +1,128 @@
1
+ import type { CrudCtx } from '@open-mercato/shared/lib/crud/factory'
2
+ import { canonicalizeResourceTag, invalidateCrudCache } from '@open-mercato/shared/lib/crud/cache'
3
+ import type { EcommerceStore, EcommerceStoreChannelBinding, EcommerceStoreDomainBinding } from '../data/entities'
4
+ import { emitEcommerceEvent, type EcommerceEventId } from '../events'
5
+
6
+ export const ECOMMERCE_EVENTS_MODULE = 'ecommerce'
7
+ export const STORE_EVENT_ENTITY = 'store'
8
+ export const STORE_DOMAIN_BINDING_EVENT_ENTITY = 'store_domain_binding'
9
+ export const STORE_CHANNEL_BINDING_EVENT_ENTITY = 'store_channel_binding'
10
+
11
+ export type StoreEventPayload = { id: string; tenantId: string; organizationId: string }
12
+ export type StoreDomainBindingEventPayload = StoreEventPayload & {
13
+ storeId: string
14
+ domainMappingId: string
15
+ previousStoreId?: string
16
+ previousDomainMappingId?: string
17
+ }
18
+ export type StoreChannelBindingEventPayload = StoreEventPayload & {
19
+ storeId: string
20
+ salesChannelId: string
21
+ previousStoreId?: string
22
+ }
23
+
24
+ type DomainBindingPlacementSnapshot = Pick<EcommerceStoreDomainBinding, 'storeId' | 'domainMappingId'>
25
+
26
+ const previousDomainBindingPlacements = new WeakMap<EcommerceStoreDomainBinding, DomainBindingPlacementSnapshot>()
27
+ const previousChannelBindingStores = new WeakMap<EcommerceStoreChannelBinding, string>()
28
+
29
+ /**
30
+ * Records where a domain binding pointed before an update, so its `updated` event also names the
31
+ * store and domain mapping it moved away from and their cached storefront entries are evicted.
32
+ */
33
+ export function rememberDomainBindingPlacement(binding: EcommerceStoreDomainBinding): void {
34
+ previousDomainBindingPlacements.set(binding, { storeId: binding.storeId, domainMappingId: binding.domainMappingId })
35
+ }
36
+
37
+ /** Channel-binding counterpart of `rememberDomainBindingPlacement`: the store it moved away from. */
38
+ export function rememberChannelBindingStore(binding: EcommerceStoreChannelBinding): void {
39
+ previousChannelBindingStores.set(binding, binding.storeId)
40
+ }
41
+
42
+ export function buildStoreEventPayload(store: EcommerceStore): StoreEventPayload {
43
+ return { id: store.id, tenantId: store.tenantId, organizationId: store.organizationId }
44
+ }
45
+
46
+ export function buildStoreDomainBindingEventPayload(binding: EcommerceStoreDomainBinding): StoreDomainBindingEventPayload {
47
+ const payload: StoreDomainBindingEventPayload = {
48
+ id: binding.id,
49
+ storeId: binding.storeId,
50
+ domainMappingId: binding.domainMappingId,
51
+ tenantId: binding.tenantId,
52
+ organizationId: binding.organizationId,
53
+ }
54
+ const previous = previousDomainBindingPlacements.get(binding)
55
+ if (previous && previous.storeId !== binding.storeId) payload.previousStoreId = previous.storeId
56
+ if (previous && previous.domainMappingId !== binding.domainMappingId) {
57
+ payload.previousDomainMappingId = previous.domainMappingId
58
+ }
59
+ return payload
60
+ }
61
+
62
+ export function buildStoreChannelBindingEventPayload(
63
+ binding: EcommerceStoreChannelBinding,
64
+ ): StoreChannelBindingEventPayload {
65
+ const payload: StoreChannelBindingEventPayload = {
66
+ id: binding.id,
67
+ storeId: binding.storeId,
68
+ salesChannelId: binding.salesChannelId,
69
+ tenantId: binding.tenantId,
70
+ organizationId: binding.organizationId,
71
+ }
72
+ const previousStoreId = previousChannelBindingStores.get(binding)
73
+ if (previousStoreId && previousStoreId !== binding.storeId) payload.previousStoreId = previousStoreId
74
+ return payload
75
+ }
76
+
77
+ export function resourceKindFor(entity: string): string {
78
+ const raw = `${ECOMMERCE_EVENTS_MODULE}.${entity}`
79
+ return canonicalizeResourceTag(raw) ?? raw
80
+ }
81
+
82
+ type ScopedRow = { id: string; tenantId: string; organizationId: string }
83
+
84
+ async function announceUpdatedRows<TRow extends ScopedRow>(
85
+ container: CrudCtx['container'],
86
+ entity: string,
87
+ eventId: EcommerceEventId,
88
+ rows: TRow[],
89
+ buildPayload: (row: TRow) => StoreEventPayload,
90
+ ): Promise<void> {
91
+ const resourceKind = resourceKindFor(entity)
92
+ for (const row of rows) {
93
+ await invalidateCrudCache(
94
+ container,
95
+ resourceKind,
96
+ { id: row.id, tenantId: row.tenantId, organizationId: row.organizationId },
97
+ row.tenantId,
98
+ 'updated',
99
+ )
100
+ await emitEcommerceEvent(eventId, buildPayload(row), {
101
+ persistent: true,
102
+ tenantId: row.tenantId,
103
+ organizationId: row.organizationId,
104
+ })
105
+ }
106
+ }
107
+
108
+ export function announceStoresUpdated(container: CrudCtx['container'], stores: EcommerceStore[]): Promise<void> {
109
+ return announceUpdatedRows(container, STORE_EVENT_ENTITY, 'ecommerce.store.updated', stores, buildStoreEventPayload)
110
+ }
111
+
112
+ export function announceStoreBrandingUpdated(container: CrudCtx['container'], store: EcommerceStore): Promise<void> {
113
+ return announceUpdatedRows(container, STORE_EVENT_ENTITY, 'ecommerce.store.branding_updated', [store], buildStoreEventPayload)
114
+ }
115
+
116
+ export function announceStoreDomainBindingsUpdated(
117
+ container: CrudCtx['container'],
118
+ bindings: EcommerceStoreDomainBinding[],
119
+ ): Promise<void> {
120
+ return announceUpdatedRows(container, STORE_DOMAIN_BINDING_EVENT_ENTITY, 'ecommerce.store_domain_binding.updated', bindings, buildStoreDomainBindingEventPayload)
121
+ }
122
+
123
+ export function announceStoreChannelBindingsUpdated(
124
+ container: CrudCtx['container'],
125
+ bindings: EcommerceStoreChannelBinding[],
126
+ ): Promise<void> {
127
+ return announceUpdatedRows(container, STORE_CHANNEL_BINDING_EVENT_ENTITY, 'ecommerce.store_channel_binding.updated', bindings, buildStoreChannelBindingEventPayload)
128
+ }
@@ -0,0 +1,85 @@
1
+ import { z } from 'zod'
2
+ import type { EntityManager } from '@mikro-orm/postgresql'
3
+ import type { EntityName, FilterQuery } from '@mikro-orm/core'
4
+ import type { CrudCtx } from '@open-mercato/shared/lib/crud/factory'
5
+ import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
6
+ import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
7
+ import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
8
+
9
+ export type Translate = (key: string, fallback?: string) => string
10
+
11
+ export type EcommerceWriteScope = { tenantId: string; organizationId: string }
12
+
13
+ export function resolveWriteScope(ctx: CrudCtx): EcommerceWriteScope {
14
+ const tenantId = ctx.auth?.tenantId ?? null
15
+ if (!tenantId) throw new CrudHttpError(400, { error: '[internal] ecommerce write scope is missing a tenant id' })
16
+ const organizationId = resolveScopeOrganizationId(ctx)
17
+ if (!organizationId) {
18
+ throw new CrudHttpError(400, { error: '[internal] ecommerce write scope is missing an organization id' })
19
+ }
20
+ return { tenantId, organizationId }
21
+ }
22
+
23
+ export function resolveScopeOrganizationId(ctx: CrudCtx): string | null {
24
+ return ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null
25
+ }
26
+
27
+ const recordIdSchema = z.string().uuid()
28
+
29
+ type ScopedRecord = { id: string; tenantId: string; organizationId: string; deletedAt?: Date | null }
30
+
31
+ /**
32
+ * Writes, reference checks, enrichers and the branding routes all work in the single organization of
33
+ * `resolveWriteScope`, while the CRUD factory also admits descendant organizations. Updates and
34
+ * deletes are held to that organization too, so a parent selection cannot half-edit a child
35
+ * organization's record; a record outside it answers the same 404 as a missing one.
36
+ */
37
+ export async function assertRecordInWriteScope<T extends ScopedRecord>(
38
+ ctx: CrudCtx,
39
+ entityName: EntityName<T>,
40
+ id: unknown,
41
+ notFound: { key: string; fallback: string },
42
+ ): Promise<void> {
43
+ const tenantId = ctx.auth?.tenantId ?? null
44
+ const organizationId = resolveScopeOrganizationId(ctx)
45
+ if (typeof id !== 'string' || !recordIdSchema.safeParse(id).success || !tenantId || !organizationId) return
46
+ const em = (ctx.container.resolve('em') as EntityManager).fork()
47
+ const record = await findOneWithDecryption(
48
+ em,
49
+ entityName,
50
+ { id, tenantId, deletedAt: null } as FilterQuery<T>,
51
+ undefined,
52
+ { tenantId, organizationId },
53
+ )
54
+ if (!record || record.organizationId === organizationId) return
55
+ throw new CrudHttpError(404, { error: await translateEcommerceError(notFound.key, notFound.fallback) })
56
+ }
57
+
58
+ export function hasOwn(input: object, key: string): boolean {
59
+ return Object.prototype.hasOwnProperty.call(input, key)
60
+ }
61
+
62
+ /**
63
+ * A client-facing error message from the `ecommerce.errors.*` catalogue, falling back to `fallback`
64
+ * when translations cannot be loaded (route catch blocks must still answer).
65
+ */
66
+ export async function translateEcommerceError(key: string, fallback: string): Promise<string> {
67
+ try {
68
+ const { translate } = await resolveTranslations()
69
+ return translate(key, fallback)
70
+ } catch {
71
+ return fallback
72
+ }
73
+ }
74
+
75
+ export const ECOMMERCE_INTERNAL_ERROR_KEY = 'ecommerce.errors.internal'
76
+ export const ECOMMERCE_INTERNAL_ERROR_FALLBACK = 'Something went wrong. Try again.'
77
+
78
+ export async function ecommerceInternalErrorBody(): Promise<{ error: string }> {
79
+ return { error: await translateEcommerceError(ECOMMERCE_INTERNAL_ERROR_KEY, ECOMMERCE_INTERNAL_ERROR_FALLBACK) }
80
+ }
81
+
82
+ export function fieldError(status: number, fieldErrors: Record<string, string>): CrudHttpError {
83
+ const [message] = Object.values(fieldErrors)
84
+ return new CrudHttpError(status, { error: message, fieldErrors })
85
+ }
@@ -0,0 +1,138 @@
1
+ import type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'
2
+ import type { AwilixContainer } from 'awilix'
3
+ import type { DataEngine } from '@open-mercato/shared/lib/data/engine'
4
+ import { invalidateCrudCache } from '@open-mercato/shared/lib/crud/cache'
5
+ import { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
6
+ import { createLogger } from '@open-mercato/shared/lib/logger'
7
+ import { EcommerceStoreDomainBinding } from '../data/entities'
8
+ import { resourceKindFor, STORE_DOMAIN_BINDING_EVENT_ENTITY } from './crudEvents'
9
+ import { domainBindingEvents, domainBindingIndexer } from './storeBindingCascade'
10
+ import {
11
+ asPayloadRecord,
12
+ readEventScope,
13
+ readPayloadString,
14
+ tryResolveService,
15
+ type EcommerceSubscriberContext,
16
+ } from './subscriberSupport'
17
+
18
+ /**
19
+ * Follows `customer_accounts` domain supersession (SPEC-029 §5.2, D5): when a replacement mapping
20
+ * becomes active, `customer_accounts.domain_mapping.replaced` carries the new mapping as `id` and the
21
+ * superseded one as `replacedDomainId`. Every live binding on the superseded id is re-pointed to the
22
+ * new id within the same tenant and organization. Re-running is a no-op because no binding is left
23
+ * on the superseded id; a binding whose `path_prefix` is already taken on the new mapping stays on
24
+ * the superseded id (dangling, R3) and is logged.
25
+ */
26
+
27
+ export type DomainMappingReplacement = {
28
+ domainMappingId: string
29
+ replacedDomainMappingId: string
30
+ tenantId: string
31
+ organizationId: string
32
+ }
33
+
34
+ export type DomainBindingRebindResult = {
35
+ rebound: EcommerceStoreDomainBinding[]
36
+ skipped: EcommerceStoreDomainBinding[]
37
+ }
38
+
39
+ const logger = createLogger('ecommerce').child({ component: 'domain-binding-rebind' })
40
+
41
+ export function readDomainMappingReplacement(
42
+ payload: unknown,
43
+ ctx: EcommerceSubscriberContext,
44
+ ): DomainMappingReplacement | null {
45
+ const record = asPayloadRecord(payload)
46
+ const domainMappingId = readPayloadString(record, 'id')
47
+ const replacedDomainMappingId = readPayloadString(record, 'replacedDomainId')
48
+ const { tenantId, organizationId } = readEventScope(record, ctx)
49
+ if (!domainMappingId || !replacedDomainMappingId || !tenantId || !organizationId) return null
50
+ if (domainMappingId === replacedDomainMappingId) return null
51
+ return { domainMappingId, replacedDomainMappingId, tenantId, organizationId }
52
+ }
53
+
54
+ function prefixKey(binding: EcommerceStoreDomainBinding): string {
55
+ return binding.pathPrefix ?? ''
56
+ }
57
+
58
+ function findLiveBindings(
59
+ em: EntityManager,
60
+ domainMappingId: string,
61
+ replacement: DomainMappingReplacement,
62
+ ): Promise<EcommerceStoreDomainBinding[]> {
63
+ const scope = { tenantId: replacement.tenantId, organizationId: replacement.organizationId }
64
+ return findWithDecryption(
65
+ em,
66
+ EcommerceStoreDomainBinding,
67
+ { domainMappingId, ...scope, deletedAt: null } as FilterQuery<EcommerceStoreDomainBinding>,
68
+ undefined,
69
+ scope,
70
+ )
71
+ }
72
+
73
+ async function announceRebound(
74
+ ctx: EcommerceSubscriberContext,
75
+ bindings: EcommerceStoreDomainBinding[],
76
+ ): Promise<void> {
77
+ const dataEngine = tryResolveService<DataEngine>(ctx, 'dataEngine')
78
+ if (dataEngine) {
79
+ for (const binding of bindings) {
80
+ dataEngine.markOrmEntityChange({
81
+ action: 'updated',
82
+ entity: binding,
83
+ identifiers: { id: binding.id, tenantId: binding.tenantId, organizationId: binding.organizationId },
84
+ events: domainBindingEvents,
85
+ indexer: domainBindingIndexer,
86
+ })
87
+ }
88
+ await dataEngine.flushOrmEntityChanges()
89
+ }
90
+ const container = { resolve: ctx.resolve } as unknown as AwilixContainer
91
+ const resourceKind = resourceKindFor(STORE_DOMAIN_BINDING_EVENT_ENTITY)
92
+ for (const binding of bindings) {
93
+ await invalidateCrudCache(
94
+ container,
95
+ resourceKind,
96
+ { id: binding.id, tenantId: binding.tenantId, organizationId: binding.organizationId },
97
+ binding.tenantId,
98
+ 'updated',
99
+ )
100
+ }
101
+ }
102
+
103
+ export async function rebindReplacedDomainMapping(
104
+ ctx: EcommerceSubscriberContext,
105
+ replacement: DomainMappingReplacement,
106
+ ): Promise<DomainBindingRebindResult> {
107
+ const rootEm = tryResolveService<EntityManager>(ctx, 'em')
108
+ if (!rootEm) throw new Error('[internal] ecommerce domain re-binding requires the DI entity manager')
109
+ const em = rootEm.fork()
110
+ const superseded = await findLiveBindings(em, replacement.replacedDomainMappingId, replacement)
111
+ if (superseded.length === 0) return { rebound: [], skipped: [] }
112
+ const existing = await findLiveBindings(em, replacement.domainMappingId, replacement)
113
+ const takenPrefixes = new Set(existing.map(prefixKey))
114
+ const rebound: EcommerceStoreDomainBinding[] = []
115
+ const skipped: EcommerceStoreDomainBinding[] = []
116
+ for (const binding of superseded) {
117
+ const key = prefixKey(binding)
118
+ if (takenPrefixes.has(key)) {
119
+ skipped.push(binding)
120
+ continue
121
+ }
122
+ takenPrefixes.add(key)
123
+ binding.domainMappingId = replacement.domainMappingId
124
+ rebound.push(binding)
125
+ }
126
+ if (rebound.length > 0) await em.flush()
127
+ for (const binding of skipped) {
128
+ logger.warn('Domain binding not re-pointed: path prefix already bound on the replacement mapping', {
129
+ bindingId: binding.id,
130
+ storeId: binding.storeId,
131
+ pathPrefix: binding.pathPrefix ?? null,
132
+ domainMappingId: replacement.domainMappingId,
133
+ replacedDomainMappingId: replacement.replacedDomainMappingId,
134
+ })
135
+ }
136
+ if (rebound.length > 0) await announceRebound(ctx, rebound)
137
+ return { rebound, skipped }
138
+ }
@@ -0,0 +1,71 @@
1
+ export const DOMAIN_MAPPING_STATUSES = ['pending', 'verified', 'active', 'dns_failed', 'tls_failed'] as const
2
+
3
+ export type DomainMappingStatus = (typeof DOMAIN_MAPPING_STATUSES)[number]
4
+
5
+ export type DomainMappingSummary = {
6
+ id: string
7
+ hostname: string
8
+ status: string
9
+ lastDnsCheckAt: string | null
10
+ dnsFailureReason: string | null
11
+ tlsFailureReason: string | null
12
+ }
13
+
14
+ type DomainMappingRecord = {
15
+ id: string
16
+ hostname: string
17
+ organizationId: string
18
+ tenantId: string
19
+ status?: string | null
20
+ lastDnsCheckAt?: Date | string | null
21
+ dnsFailureReason?: string | null
22
+ tlsFailureReason?: string | null
23
+ }
24
+
25
+ type DomainMappingReader = {
26
+ findByOrganization(organizationId: string, scope?: { tenantId?: string }): Promise<DomainMappingRecord[]>
27
+ }
28
+
29
+ type ContainerLike = { resolve(name: string): unknown }
30
+
31
+ export function isDomainMappingStatus(value: unknown): value is DomainMappingStatus {
32
+ return typeof value === 'string' && (DOMAIN_MAPPING_STATUSES as readonly string[]).includes(value)
33
+ }
34
+
35
+ function toIsoString(value: Date | string | null | undefined): string | null {
36
+ if (!value) return null
37
+ const date = value instanceof Date ? value : new Date(value)
38
+ return Number.isNaN(date.getTime()) ? null : date.toISOString()
39
+ }
40
+
41
+ function toSummary(record: DomainMappingRecord): DomainMappingSummary {
42
+ return {
43
+ id: record.id,
44
+ hostname: record.hostname,
45
+ status: record.status ?? 'pending',
46
+ lastDnsCheckAt: toIsoString(record.lastDnsCheckAt),
47
+ dnsFailureReason: record.dnsFailureReason ?? null,
48
+ tlsFailureReason: record.tlsFailureReason ?? null,
49
+ }
50
+ }
51
+
52
+ function resolveDomainMappingReader(container: unknown): DomainMappingReader | null {
53
+ try {
54
+ const service = (container as ContainerLike).resolve('domainMappingService') as DomainMappingReader | null | undefined
55
+ return service && typeof service.findByOrganization === 'function' ? service : null
56
+ } catch {
57
+ return null
58
+ }
59
+ }
60
+
61
+ export async function loadOrganizationDomainMappings(
62
+ container: unknown,
63
+ scope: { organizationId: string; tenantId: string },
64
+ ): Promise<DomainMappingSummary[] | null> {
65
+ const reader = resolveDomainMappingReader(container)
66
+ if (!reader) return null
67
+ const records = await reader.findByOrganization(scope.organizationId, { tenantId: scope.tenantId })
68
+ return records
69
+ .filter((record) => record.tenantId === scope.tenantId && record.organizationId === scope.organizationId)
70
+ .map(toSummary)
71
+ }
@@ -0,0 +1,90 @@
1
+ import type { EntityData, EntityManager, EntityName, FilterQuery } from '@mikro-orm/postgresql'
2
+ import { isUniqueViolation } from '@open-mercato/shared/lib/crud/errors'
3
+ import { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
4
+
5
+ export type ExclusiveFlagRow = { id: string }
6
+
7
+ export type ExclusiveFlagConfig<T extends ExclusiveFlagRow> = {
8
+ entity: EntityName<T>
9
+ flag: 'isPrimary' | 'isDefault'
10
+ constraint: string
11
+ }
12
+
13
+ export type ExclusiveFlagScope = Record<string, string>
14
+
15
+ const PROMOTION_ATTEMPTS = 3
16
+
17
+ function flaggedRowsWhere<T extends ExclusiveFlagRow>(
18
+ config: ExclusiveFlagConfig<T>,
19
+ scope: ExclusiveFlagScope,
20
+ keepId: string | null,
21
+ ): FilterQuery<T> {
22
+ const where: Record<string, unknown> = { ...scope, [config.flag]: true, deletedAt: null }
23
+ if (keepId) where.id = { $ne: keepId }
24
+ return where as FilterQuery<T>
25
+ }
26
+
27
+ function ownRowWhere<T extends ExclusiveFlagRow>(scope: ExclusiveFlagScope, id: string): FilterQuery<T> {
28
+ return { ...scope, id, deletedAt: null } as FilterQuery<T>
29
+ }
30
+
31
+ function flagData<T extends ExclusiveFlagRow>(config: ExclusiveFlagConfig<T>, value: boolean, now?: Date): EntityData<T> {
32
+ const data: Record<string, unknown> = { [config.flag]: value }
33
+ if (now) data.updatedAt = now
34
+ return data as EntityData<T>
35
+ }
36
+
37
+ export async function clearExclusiveFlag<T extends ExclusiveFlagRow>(
38
+ em: EntityManager,
39
+ config: ExclusiveFlagConfig<T>,
40
+ scope: ExclusiveFlagScope,
41
+ keepId: string | null,
42
+ ): Promise<T[]> {
43
+ const where = flaggedRowsWhere(config, scope, keepId)
44
+ const cleared = await findWithDecryption(em, config.entity, where, undefined, {
45
+ tenantId: scope.tenantId ?? null,
46
+ organizationId: scope.organizationId ?? null,
47
+ })
48
+ if (!cleared.length) return []
49
+ await em.nativeUpdate(config.entity, where, flagData(config, false, new Date()))
50
+ return cleared
51
+ }
52
+
53
+ export async function promoteExclusiveFlag<T extends ExclusiveFlagRow>(
54
+ em: EntityManager,
55
+ config: ExclusiveFlagConfig<T>,
56
+ scope: ExclusiveFlagScope,
57
+ id: string,
58
+ now: Date,
59
+ ): Promise<{ promoted: boolean; cleared: T[] }> {
60
+ for (let attempt = 1; ; attempt += 1) {
61
+ try {
62
+ const cleared = await em.transactional(async (tem) => {
63
+ const rows = await clearExclusiveFlag(tem, config, scope, id)
64
+ await tem.nativeUpdate(config.entity, ownRowWhere<T>(scope, id), flagData(config, true, now))
65
+ return rows
66
+ })
67
+ return { promoted: true, cleared }
68
+ } catch (err) {
69
+ if (!isUniqueViolation(err, config.constraint)) throw err
70
+ if (attempt >= PROMOTION_ATTEMPTS) return { promoted: false, cleared: [] }
71
+ }
72
+ }
73
+ }
74
+
75
+ export async function assignExclusiveFlag<T extends ExclusiveFlagRow>(
76
+ em: EntityManager,
77
+ config: ExclusiveFlagConfig<T>,
78
+ scope: ExclusiveFlagScope,
79
+ id: string,
80
+ toConflict: () => unknown,
81
+ ): Promise<T[]> {
82
+ try {
83
+ const cleared = await clearExclusiveFlag(em, config, scope, id)
84
+ await em.nativeUpdate(config.entity, ownRowWhere<T>(scope, id), flagData(config, true))
85
+ return cleared
86
+ } catch (err) {
87
+ if (isUniqueViolation(err, config.constraint)) throw toConflict()
88
+ throw err
89
+ }
90
+ }
@@ -0,0 +1,93 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql'
2
+ import type { CrudCtx } from '@open-mercato/shared/lib/crud/factory'
3
+ import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
4
+ import { CatalogPriceKind } from '@open-mercato/core/modules/catalog/data/entities'
5
+ import { SalesChannel } from '@open-mercato/core/modules/sales/data/entities'
6
+ import { EcommerceStore } from '../data/entities'
7
+ import { fieldError, type EcommerceWriteScope, type Translate } from './crudSupport'
8
+
9
+ type DomainMappingReader = {
10
+ findById(id: string, scope?: { tenantId?: string }): Promise<{ tenantId: string; organizationId: string } | null>
11
+ }
12
+
13
+ export async function assertStoreInScope(
14
+ em: EntityManager,
15
+ storeId: string,
16
+ scope: EcommerceWriteScope,
17
+ translate: Translate,
18
+ ): Promise<void> {
19
+ const store = await findOneWithDecryption(
20
+ em,
21
+ EcommerceStore,
22
+ { id: storeId, tenantId: scope.tenantId, organizationId: scope.organizationId, deletedAt: null },
23
+ undefined,
24
+ scope,
25
+ )
26
+ if (store) return
27
+ throw fieldError(400, {
28
+ storeId: translate('ecommerce.errors.storeNotFound', 'The selected store does not exist in this organization.'),
29
+ })
30
+ }
31
+
32
+ export async function assertDomainMappingInScope(
33
+ container: CrudCtx['container'],
34
+ domainMappingId: string,
35
+ scope: EcommerceWriteScope,
36
+ translate: Translate,
37
+ ): Promise<void> {
38
+ const service = container.resolve<DomainMappingReader>('domainMappingService')
39
+ const mapping = await service.findById(domainMappingId, { tenantId: scope.tenantId })
40
+ if (mapping && mapping.tenantId === scope.tenantId && mapping.organizationId === scope.organizationId) return
41
+ throw fieldError(400, {
42
+ domainMappingId: translate(
43
+ 'ecommerce.errors.domainMappingNotFound',
44
+ 'The selected domain does not belong to this organization.',
45
+ ),
46
+ })
47
+ }
48
+
49
+ export async function assertSalesChannelInScope(
50
+ em: EntityManager,
51
+ salesChannelId: string,
52
+ scope: EcommerceWriteScope,
53
+ translate: Translate,
54
+ ): Promise<void> {
55
+ const channel = await findOneWithDecryption(
56
+ em,
57
+ SalesChannel,
58
+ { id: salesChannelId, tenantId: scope.tenantId, organizationId: scope.organizationId, deletedAt: null },
59
+ undefined,
60
+ scope,
61
+ )
62
+ if (channel) return
63
+ throw fieldError(400, {
64
+ salesChannelId: translate(
65
+ 'ecommerce.errors.salesChannelNotFound',
66
+ 'The selected sales channel does not exist in this organization.',
67
+ ),
68
+ })
69
+ }
70
+
71
+ export async function assertPriceKindInScope(
72
+ em: EntityManager,
73
+ priceKindId: string,
74
+ scope: EcommerceWriteScope,
75
+ translate: Translate,
76
+ ): Promise<void> {
77
+ const priceKind = await findOneWithDecryption(
78
+ em,
79
+ CatalogPriceKind,
80
+ {
81
+ id: priceKindId,
82
+ tenantId: scope.tenantId,
83
+ deletedAt: null,
84
+ $or: [{ organizationId: null }, { organizationId: scope.organizationId }],
85
+ },
86
+ undefined,
87
+ scope,
88
+ )
89
+ if (priceKind) return
90
+ throw fieldError(400, {
91
+ priceKindId: translate('ecommerce.errors.priceKindNotFound', 'The selected price kind does not exist in this organization.'),
92
+ })
93
+ }