@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,73 @@
1
+ import { z } from 'zod'
2
+ import { withCreateConflictRecheck } from '../../lib/createConflictRecheck'
3
+ import { domainBindingCrud, domainBindingListQuerySchema, domainBindingRouteMetadata } from './crud'
4
+ import {
5
+ ecommerceStoreDomainBindingCreateSchema,
6
+ ecommerceStoreDomainBindingUpdateSchema,
7
+ } from '../../data/validators'
8
+ import { createEcommerceCrudOpenApi, createPagedListResponseSchema, defaultOkResponseSchema } from '../openapi'
9
+
10
+ export const metadata = domainBindingRouteMetadata
11
+
12
+ export const GET = domainBindingCrud.GET
13
+ export const POST = withCreateConflictRecheck(domainBindingCrud.POST)
14
+ export const PUT = domainBindingCrud.PUT
15
+ export const DELETE = domainBindingCrud.DELETE
16
+
17
+ const domainBindingMappingSchema = z.discriminatedUnion('state', [
18
+ z.object({
19
+ state: z.literal('found'),
20
+ hostname: z.string(),
21
+ status: z.string().describe('pending, verified, active, dns_failed or tls_failed. Only active serves.'),
22
+ lastDnsCheckAt: z.string().nullable(),
23
+ dnsFailureReason: z.string().nullable(),
24
+ tlsFailureReason: z.string().nullable(),
25
+ }),
26
+ z.object({ state: z.literal('removed') }),
27
+ z.object({ state: z.literal('unavailable') }),
28
+ ])
29
+
30
+ export const domainBindingListItemSchema = z.object({
31
+ id: z.string().uuid(),
32
+ organizationId: z.string().uuid().nullable(),
33
+ tenantId: z.string().uuid().nullable(),
34
+ storeId: z.string().uuid(),
35
+ domainMappingId: z.string().uuid(),
36
+ pathPrefix: z.string().nullable(),
37
+ isPrimary: z.boolean(),
38
+ createdAt: z.string().nullable(),
39
+ updatedAt: z.string().nullable(),
40
+ _domainMapping: domainBindingMappingSchema
41
+ .optional()
42
+ .describe(
43
+ 'Added by the ecommerce.store-domain-binding-mapping response enricher: the bound customer_accounts domain mapping as read through domainMappingService. state is removed when the mapping no longer exists and unavailable when it could not be read.',
44
+ ),
45
+ })
46
+
47
+ export const openApi = createEcommerceCrudOpenApi({
48
+ resourceName: 'StoreDomainBinding',
49
+ pluralName: 'StoreDomainBindings',
50
+ querySchema: domainBindingListQuerySchema,
51
+ listResponseSchema: createPagedListResponseSchema(domainBindingListItemSchema),
52
+ create: {
53
+ schema: ecommerceStoreDomainBindingCreateSchema,
54
+ responseSchema: z.object({
55
+ id: z.string().uuid().nullable(),
56
+ isPrimary: z
57
+ .boolean()
58
+ .describe('Whether the created binding is the primary binding of its store. False when a concurrent request won the promotion.'),
59
+ }),
60
+ description:
61
+ 'Binds a customer_accounts domain mapping (any status) and optional path prefix to a store of the same organization. A duplicate domain and path prefix is rejected with a field-level 409. Requesting isPrimary clears the previous primary binding of the store.',
62
+ },
63
+ update: {
64
+ schema: ecommerceStoreDomainBindingUpdateSchema,
65
+ responseSchema: defaultOkResponseSchema,
66
+ description: 'Updates a store domain binding by id (id in the body). Setting isPrimary clears the previous primary binding of the store.',
67
+ },
68
+ del: {
69
+ schema: z.object({ id: z.string().uuid() }),
70
+ responseSchema: defaultOkResponseSchema,
71
+ description: 'Soft-deletes a store domain binding by id (?id= query parameter).',
72
+ },
73
+ })
@@ -0,0 +1,94 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
3
+ import type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
4
+ import { rateLimitErrorSchema } from '@open-mercato/shared/lib/ratelimit/helpers'
5
+ import {
6
+ ecommerceStorefrontCategoryLandingQuerySchema,
7
+ ecommerceStorefrontCategoryParamsSchema,
8
+ } from '../../../../data/validators'
9
+ import { enforceStorefrontRateLimit } from '../../../../lib/storefrontRateLimit'
10
+ import type { StoreContextService } from '../../../../lib/storeContextService'
11
+ import { cachedGetStorefrontCategoryLanding } from '../../../../lib/storefrontCategoryCache'
12
+ import { parseStorefrontCategoryLandingQuery } from '../../../../lib/storefrontQuery'
13
+ import {
14
+ storefrontErrorResponse,
15
+ storefrontErrorSchema,
16
+ storefrontInvalidQueryErrorSchema,
17
+ storefrontRouteErrorResponse,
18
+ storefrontSuccessHeaders,
19
+ runInStoreCacheTenant,
20
+ } from '../../storefrontRouteSupport'
21
+ import { storefrontCategoryLandingResponseSchema } from '../openapiSchemas'
22
+
23
+ export const metadata = {
24
+ path: '/ecommerce/storefront/categories/[slug]',
25
+ GET: {
26
+ requireAuth: false,
27
+ },
28
+ }
29
+
30
+ const ANONYMOUS_CACHE_CONTROL = 'public, max-age=30, stale-while-revalidate=30'
31
+ const CATEGORY_NOT_FOUND = { error: 'category_not_found' } as const
32
+
33
+ type CategoryRouteParams = { slug?: string }
34
+
35
+ type CategoryRouteContext = { params?: CategoryRouteParams | Promise<CategoryRouteParams> }
36
+
37
+ export async function GET(req: Request, routeContext: CategoryRouteContext = {}) {
38
+ try {
39
+ const query = parseStorefrontCategoryLandingQuery(new URL(req.url).searchParams)
40
+ const container = await createRequestContainer()
41
+ const service = container.resolve('storeContextService') as StoreContextService
42
+ const context = await service.resolve(req, { pathname: query.path ?? '/' })
43
+ return await runInStoreCacheTenant(context, async () => {
44
+ const rateLimited = await enforceStorefrontRateLimit(container, req, context, 'categoryLanding')
45
+ if (rateLimited) return rateLimited
46
+ const params = ecommerceStorefrontCategoryParamsSchema.safeParse((await routeContext.params) ?? {})
47
+ if (!params.success) return storefrontErrorResponse(404, { ...CATEGORY_NOT_FOUND })
48
+ const landing = await cachedGetStorefrontCategoryLanding(container, context, params.data.slug, query)
49
+ if (!landing) return storefrontErrorResponse(404, { ...CATEGORY_NOT_FOUND })
50
+ const headers = storefrontSuccessHeaders(context, ANONYMOUS_CACHE_CONTROL)
51
+ if (landing.products.sortApproximate) headers['X-Sort-Approximate'] = 'true'
52
+ if (landing.products.sortUnavailable) headers['X-Sort-Unavailable'] = 'true'
53
+ return NextResponse.json(landing, { headers })
54
+ })
55
+ } catch (error) {
56
+ return storefrontRouteErrorResponse(error, {
57
+ message: 'Storefront category landing failed',
58
+ code: 'ecommerce.storefront_category_landing_failed',
59
+ })
60
+ }
61
+ }
62
+
63
+ export default GET
64
+
65
+ const storefrontCategoryTag = 'Ecommerce'
66
+
67
+ const storefrontCategoryGetDoc: OpenApiMethodDoc = {
68
+ summary: 'Get one storefront category landing for the current buyer',
69
+ description:
70
+ 'Public. Resolves the store from the Host header and the optional portal session, then returns the category by slug with its breadcrumb, visible non-empty children and descendant-inclusive `productCount` within the buyer\'s assortment (null past 10 000 products in the assortment), plus a full `GET /products` response filtered to the category (`products`, same query grammar except `categoryId` / `categorySlug`). A slug that does not exist, is inactive, deleted, of another tenant, under an inactive ancestor or outside the buyer\'s effective assortment answers the same 404 `{ "error": "category_not_found" }`. `seo` fields are null until the catalog stores category SEO data. Anonymous responses are `public, max-age=30, stale-while-revalidate=30`; authenticated responses are `private, no-store`. The category block is cached server-side for 60 s per assortment scope; the embedded listing shares the `GET /products` cache. Rate limited per IP and store.',
71
+ tags: [storefrontCategoryTag],
72
+ pathParams: ecommerceStorefrontCategoryParamsSchema,
73
+ query: ecommerceStorefrontCategoryLandingQuerySchema,
74
+ responses: [
75
+ { status: 200, description: 'The category landing as this buyer sees it.', schema: storefrontCategoryLandingResponseSchema },
76
+ ],
77
+ errors: [
78
+ { status: 400, description: 'Unknown, repeated or malformed query parameters', schema: storefrontInvalidQueryErrorSchema },
79
+ { status: 401, description: 'Portal session invalid or issued for another tenant or organization', schema: storefrontErrorSchema },
80
+ { status: 403, description: 'Draft store (only when OM_ECOMMERCE_DEV_STORE_SLUG=true)', schema: storefrontErrorSchema },
81
+ { status: 404, description: 'No store serves this host or path, or the category is not visible to this buyer', schema: storefrontErrorSchema },
82
+ { status: 410, description: 'Store archived', schema: storefrontErrorSchema },
83
+ { status: 429, description: 'Too many requests', schema: rateLimitErrorSchema },
84
+ { status: 503, description: 'Store misconfigured (no default channel binding)', schema: storefrontErrorSchema },
85
+ ],
86
+ }
87
+
88
+ export const openApi: OpenApiRouteDoc = {
89
+ tag: storefrontCategoryTag,
90
+ summary: 'Public storefront category landing',
91
+ methods: {
92
+ GET: storefrontCategoryGetDoc,
93
+ },
94
+ }
@@ -0,0 +1,63 @@
1
+ import { z } from 'zod'
2
+ import { storefrontProductListResponseSchema } from '../products/openapiSchemas'
3
+
4
+ export type StorefrontCategoryNodeSchema = {
5
+ id: string
6
+ name: string
7
+ slug: string | null
8
+ description: string | null
9
+ depth: number
10
+ parentId: string | null
11
+ productCount: number | null
12
+ hasChildren: boolean
13
+ children: StorefrontCategoryNodeSchema[]
14
+ }
15
+
16
+ export const storefrontCategoryNodeSchema: z.ZodType<StorefrontCategoryNodeSchema> = z.lazy(() =>
17
+ z.object({
18
+ id: z.string().uuid(),
19
+ name: z.string(),
20
+ slug: z.string().nullable(),
21
+ description: z.string().nullable(),
22
+ depth: z.number().int(),
23
+ parentId: z.string().uuid().nullable(),
24
+ productCount: z
25
+ .number()
26
+ .int()
27
+ .nullable()
28
+ .describe(
29
+ 'Products of the buyer\'s effective assortment in this category or a descendant; null when the assortment is too large to count',
30
+ ),
31
+ hasChildren: z.boolean(),
32
+ children: z.array(storefrontCategoryNodeSchema),
33
+ }),
34
+ )
35
+
36
+ export const storefrontCategoryTreeResponseSchema = z.object({
37
+ tree: z.array(storefrontCategoryNodeSchema),
38
+ effectiveLocale: z.string(),
39
+ })
40
+
41
+ export const storefrontCategoryLandingResponseSchema = z.object({
42
+ category: z.object({
43
+ id: z.string().uuid(),
44
+ name: z.string(),
45
+ slug: z.string().nullable(),
46
+ description: z.string().nullable(),
47
+ depth: z.number().int(),
48
+ parentId: z.string().uuid().nullable(),
49
+ ancestorIds: z.array(z.string().uuid()),
50
+ breadcrumb: z.array(z.object({ id: z.string().uuid(), name: z.string(), slug: z.string().nullable() })),
51
+ children: z.array(
52
+ z.object({ id: z.string().uuid(), name: z.string(), slug: z.string().nullable(), productCount: z.number().int().nullable() }),
53
+ ),
54
+ productCount: z.number().int().nullable(),
55
+ seo: z.object({
56
+ title: z.string().nullable(),
57
+ description: z.string().nullable(),
58
+ canonicalUrl: z.string().nullable(),
59
+ }),
60
+ }),
61
+ products: storefrontProductListResponseSchema.describe('Identical to GET /products with the category filter applied'),
62
+ effectiveLocale: z.string(),
63
+ })
@@ -0,0 +1,78 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
3
+ import type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
4
+ import { rateLimitErrorSchema } from '@open-mercato/shared/lib/ratelimit/helpers'
5
+ import { ecommerceStorefrontCategoryTreeQuerySchema } from '../../../data/validators'
6
+ import { enforceStorefrontRateLimit } from '../../../lib/storefrontRateLimit'
7
+ import type { StoreContextService } from '../../../lib/storeContextService'
8
+ import { cachedGetStorefrontCategoryTree } from '../../../lib/storefrontCategoryCache'
9
+ import { parseStorefrontCategoryTreeQuery } from '../../../lib/storefrontQuery'
10
+ import {
11
+ storefrontErrorSchema,
12
+ storefrontInvalidQueryErrorSchema,
13
+ storefrontRouteErrorResponse,
14
+ storefrontSuccessHeaders,
15
+ runInStoreCacheTenant,
16
+ } from '../storefrontRouteSupport'
17
+ import { storefrontCategoryTreeResponseSchema } from './openapiSchemas'
18
+
19
+ export const metadata = {
20
+ path: '/ecommerce/storefront/categories',
21
+ GET: {
22
+ requireAuth: false,
23
+ },
24
+ }
25
+
26
+ const ANONYMOUS_CACHE_CONTROL = 'public, max-age=300, stale-while-revalidate=60'
27
+
28
+ export async function GET(req: Request) {
29
+ try {
30
+ const query = parseStorefrontCategoryTreeQuery(new URL(req.url).searchParams)
31
+ const container = await createRequestContainer()
32
+ const service = container.resolve('storeContextService') as StoreContextService
33
+ const context = await service.resolve(req, { pathname: query.path ?? '/' })
34
+ return await runInStoreCacheTenant(context, async () => {
35
+ const rateLimited = await enforceStorefrontRateLimit(container, req, context, 'categories')
36
+ if (rateLimited) return rateLimited
37
+ const body = await cachedGetStorefrontCategoryTree(container, context, query)
38
+ return NextResponse.json(body, { headers: storefrontSuccessHeaders(context, ANONYMOUS_CACHE_CONTROL) })
39
+ })
40
+ } catch (error) {
41
+ return storefrontRouteErrorResponse(error, {
42
+ message: 'Storefront category tree failed',
43
+ code: 'ecommerce.storefront_categories_failed',
44
+ })
45
+ }
46
+ }
47
+
48
+ export default GET
49
+
50
+ const storefrontCategoriesTag = 'Ecommerce'
51
+
52
+ const storefrontCategoriesGetDoc: OpenApiMethodDoc = {
53
+ summary: 'List the storefront category tree for the current buyer',
54
+ description:
55
+ 'Public. Resolves the store from the Host header and the optional portal session, then returns the visible category tree below `parentId` (the roots when omitted), `depth` levels deep (all levels when omitted). `productCount` is descendant-inclusive and counts only products inside the buyer\'s effective assortment; categories the buyer cannot browse are absent, and empty categories are absent unless `includeEmpty=true`. Past 10 000 products in the assortment the counts are not computed: every `productCount` is null and no category is dropped as empty. A `parentId` that is unknown or not visible to the buyer yields an empty tree, identical to a nonexistent one. A nested node beyond `depth` has empty `children` and `hasChildren: true`. Anonymous responses are `public, max-age=300, stale-while-revalidate=60`; authenticated responses are `private, no-store`. Cached server-side for 300 s per assortment scope. Rate limited per IP and store.',
56
+ tags: [storefrontCategoriesTag],
57
+ query: ecommerceStorefrontCategoryTreeQuerySchema,
58
+ responses: [
59
+ { status: 200, description: 'The category tree within the buyer\'s assortment.', schema: storefrontCategoryTreeResponseSchema },
60
+ ],
61
+ errors: [
62
+ { status: 400, description: 'Unknown, repeated or malformed query parameters', schema: storefrontInvalidQueryErrorSchema },
63
+ { status: 401, description: 'Portal session invalid or issued for another tenant or organization', schema: storefrontErrorSchema },
64
+ { status: 403, description: 'Draft store (only when OM_ECOMMERCE_DEV_STORE_SLUG=true)', schema: storefrontErrorSchema },
65
+ { status: 404, description: 'No store serves this host or path', schema: storefrontErrorSchema },
66
+ { status: 410, description: 'Store archived', schema: storefrontErrorSchema },
67
+ { status: 429, description: 'Too many requests', schema: rateLimitErrorSchema },
68
+ { status: 503, description: 'Store misconfigured (no default channel binding)', schema: storefrontErrorSchema },
69
+ ],
70
+ }
71
+
72
+ export const openApi: OpenApiRouteDoc = {
73
+ tag: storefrontCategoriesTag,
74
+ summary: 'Public storefront category tree',
75
+ methods: {
76
+ GET: storefrontCategoriesGetDoc,
77
+ },
78
+ }
@@ -0,0 +1,203 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { z } from 'zod'
3
+ import type { EntityManager } from '@mikro-orm/postgresql'
4
+ import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
5
+ import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
6
+ import type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
7
+ import { rateLimitErrorSchema } from '@open-mercato/shared/lib/ratelimit/helpers'
8
+ import { CustomerUser } from '@open-mercato/core/modules/customer_accounts/data/entities'
9
+ import { CustomerEntity } from '@open-mercato/core/modules/customers/data/entities'
10
+ import { ecommercePriceDisplayModeSchema, ecommerceStorefrontContextQuerySchema } from '../../../data/validators'
11
+ import { parseStorefrontContextQuery } from '../../../lib/storefrontQuery'
12
+ import { enforceStorefrontRateLimit } from '../../../lib/storefrontRateLimit'
13
+ import type { StoreContextService } from '../../../lib/storeContextService'
14
+ import type { StoreContext } from '../../../lib/types'
15
+ import {
16
+ runInStoreCacheTenant,
17
+ storefrontErrorSchema,
18
+ storefrontInvalidQueryErrorSchema,
19
+ storefrontRouteErrorResponse,
20
+ storefrontSuccessHeaders,
21
+ } from '../storefrontRouteSupport'
22
+
23
+ export const metadata = {
24
+ path: '/ecommerce/storefront/context',
25
+ GET: {
26
+ requireAuth: false,
27
+ },
28
+ }
29
+
30
+ const ANONYMOUS_CACHE_CONTROL = 'public, max-age=60'
31
+
32
+ export type StorefrontBuyerNames = {
33
+ displayName: string | null
34
+ companyName: string | null
35
+ }
36
+
37
+ export type StorefrontContextResponse = {
38
+ store: {
39
+ id: string
40
+ code: string
41
+ name: string
42
+ slug: string
43
+ status: 'active'
44
+ defaultLocale: string
45
+ supportedLocales: string[]
46
+ defaultCurrencyCode: string
47
+ settings: StoreContext['store']['settings']
48
+ }
49
+ effectiveLocale: string
50
+ requestedLocale: string | null
51
+ supportedLocales: string[]
52
+ currencyCode: string
53
+ buyer: {
54
+ isAuthenticated: boolean
55
+ taxMode: StoreContext['buyer']['taxMode']
56
+ displayName: string | null
57
+ companyName: string | null
58
+ allowPurchaseOnAccount: boolean
59
+ }
60
+ }
61
+
62
+ export function projectStorefrontContext(context: StoreContext, names: StorefrontBuyerNames): StorefrontContextResponse {
63
+ const { store, buyer } = context
64
+ return {
65
+ store: {
66
+ id: store.id,
67
+ code: store.code,
68
+ name: store.name,
69
+ slug: store.slug,
70
+ status: store.status,
71
+ defaultLocale: store.defaultLocale,
72
+ supportedLocales: [...store.supportedLocales],
73
+ defaultCurrencyCode: store.defaultCurrencyCode,
74
+ settings: store.settings,
75
+ },
76
+ effectiveLocale: context.effectiveLocale,
77
+ requestedLocale: context.requestedLocale,
78
+ supportedLocales: [...store.supportedLocales],
79
+ currencyCode: context.currencyCode,
80
+ buyer: {
81
+ isAuthenticated: buyer.isAuthenticated,
82
+ taxMode: buyer.taxMode,
83
+ displayName: buyer.isAuthenticated ? names.displayName : null,
84
+ companyName: buyer.isAuthenticated ? names.companyName : null,
85
+ allowPurchaseOnAccount: buyer.allowPurchaseOnAccount,
86
+ },
87
+ }
88
+ }
89
+
90
+ function nonEmptyOrNull(value: string | null | undefined): string | null {
91
+ return typeof value === 'string' && value.trim().length > 0 ? value : null
92
+ }
93
+
94
+ async function loadBuyerNames(em: EntityManager, context: StoreContext): Promise<StorefrontBuyerNames> {
95
+ const { buyer } = context
96
+ if (!buyer.isAuthenticated || !buyer.customerUserId) return { displayName: null, companyName: null }
97
+ const scope = { tenantId: context.tenantId, organizationId: context.organizationId }
98
+ const user = await findOneWithDecryption(
99
+ em,
100
+ CustomerUser,
101
+ { id: buyer.customerUserId, ...scope, deletedAt: null },
102
+ undefined,
103
+ scope,
104
+ )
105
+ const company = buyer.companyId
106
+ ? await findOneWithDecryption(
107
+ em,
108
+ CustomerEntity,
109
+ { id: buyer.companyId, ...scope, deletedAt: null },
110
+ undefined,
111
+ scope,
112
+ )
113
+ : null
114
+ return {
115
+ displayName: nonEmptyOrNull(user?.displayName),
116
+ companyName: nonEmptyOrNull(company?.displayName),
117
+ }
118
+ }
119
+
120
+ export async function GET(req: Request) {
121
+ try {
122
+ const query = parseStorefrontContextQuery(new URL(req.url).searchParams)
123
+ const container = await createRequestContainer()
124
+ const service = container.resolve('storeContextService') as StoreContextService
125
+ const context = await service.resolve(req, { pathname: query.path ?? '/' })
126
+ return await runInStoreCacheTenant(context, async () => {
127
+ const rateLimited = await enforceStorefrontRateLimit(container, req, context, 'context')
128
+ if (rateLimited) return rateLimited
129
+ const em = container.resolve('em') as EntityManager
130
+ const names = await loadBuyerNames(em, context)
131
+ const body = projectStorefrontContext(context, names)
132
+ return NextResponse.json(body, { headers: storefrontSuccessHeaders(context, ANONYMOUS_CACHE_CONTROL) })
133
+ })
134
+ } catch (error) {
135
+ return storefrontRouteErrorResponse(error, {
136
+ message: 'Storefront context resolution failed',
137
+ code: 'ecommerce.storefront_context_failed',
138
+ })
139
+ }
140
+ }
141
+
142
+ export default GET
143
+
144
+ const storefrontContextTag = 'Ecommerce'
145
+
146
+ export const storefrontContextResponseSchema = z.object({
147
+ store: z.object({
148
+ id: z.string().uuid(),
149
+ code: z.string(),
150
+ name: z.string(),
151
+ slug: z.string(),
152
+ status: z.literal('active'),
153
+ defaultLocale: z.string(),
154
+ supportedLocales: z.array(z.string()),
155
+ defaultCurrencyCode: z.string(),
156
+ settings: z.record(z.string(), z.unknown()),
157
+ }),
158
+ effectiveLocale: z.string(),
159
+ requestedLocale: z.string().nullable(),
160
+ supportedLocales: z.array(z.string()),
161
+ currencyCode: z.string(),
162
+ buyer: z.object({
163
+ isAuthenticated: z.boolean(),
164
+ taxMode: ecommercePriceDisplayModeSchema,
165
+ displayName: z.string().nullable(),
166
+ companyName: z.string().nullable(),
167
+ allowPurchaseOnAccount: z.boolean(),
168
+ }),
169
+ })
170
+
171
+ const storefrontContextBadRequestSchema = z.union([storefrontInvalidQueryErrorSchema, storefrontErrorSchema])
172
+
173
+ const storefrontContextGetDoc: OpenApiMethodDoc = {
174
+ summary: 'Resolve the storefront context for the current host',
175
+ description:
176
+ 'Public, unauthenticated. Resolves the store from the Host header (or `storeSlug` when OM_ECOMMERCE_DEV_STORE_SLUG=true) and the optional portal session, and returns a safe projection of the store and buyer context. Anonymous responses are `public, max-age=60`; authenticated responses are `private, no-store`. Rate limited per IP and store.',
177
+ tags: [storefrontContextTag],
178
+ query: ecommerceStorefrontContextQuerySchema,
179
+ responses: [
180
+ { status: 200, description: 'Storefront context resolved.', schema: storefrontContextResponseSchema },
181
+ ],
182
+ errors: [
183
+ {
184
+ status: 400,
185
+ description: 'Unknown, repeated or malformed query parameters, or storeSlug used while the development flag is off',
186
+ schema: storefrontContextBadRequestSchema,
187
+ },
188
+ { status: 401, description: 'Portal session invalid or issued for another tenant or organization', schema: storefrontErrorSchema },
189
+ { status: 403, description: 'Draft store (only when OM_ECOMMERCE_DEV_STORE_SLUG=true)', schema: storefrontErrorSchema },
190
+ { status: 404, description: 'No store serves this host or path', schema: storefrontErrorSchema },
191
+ { status: 410, description: 'Store archived', schema: storefrontErrorSchema },
192
+ { status: 429, description: 'Too many requests', schema: rateLimitErrorSchema },
193
+ { status: 503, description: 'Store misconfigured (no default channel binding)', schema: storefrontErrorSchema },
194
+ ],
195
+ }
196
+
197
+ export const openApi: OpenApiRouteDoc = {
198
+ tag: storefrontContextTag,
199
+ summary: 'Public storefront context',
200
+ methods: {
201
+ GET: storefrontContextGetDoc,
202
+ },
203
+ }
@@ -0,0 +1,94 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
3
+ import type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
4
+ import { rateLimitErrorSchema } from '@open-mercato/shared/lib/ratelimit/helpers'
5
+ import {
6
+ ecommerceStorefrontProductDetailQuerySchema,
7
+ ecommerceStorefrontProductParamsSchema,
8
+ } from '../../../../data/validators'
9
+ import { enforceStorefrontRateLimit } from '../../../../lib/storefrontRateLimit'
10
+ import type { StoreContextService } from '../../../../lib/storeContextService'
11
+ import { cachedGetStorefrontProductDetail } from '../../../../lib/storefrontProductCache'
12
+ import { parseStorefrontProductDetailQuery } from '../../../../lib/storefrontQuery'
13
+ import {
14
+ storefrontErrorResponse,
15
+ storefrontErrorSchema,
16
+ storefrontInvalidQueryErrorSchema,
17
+ storefrontRouteErrorResponse,
18
+ storefrontSuccessHeaders,
19
+ runInStoreCacheTenant,
20
+ } from '../../storefrontRouteSupport'
21
+ import { storefrontProductDetailResponseSchema } from '../openapiSchemas'
22
+
23
+ export const metadata = {
24
+ path: '/ecommerce/storefront/products/[idOrHandle]',
25
+ GET: {
26
+ requireAuth: false,
27
+ },
28
+ }
29
+
30
+ const ANONYMOUS_CACHE_CONTROL = 'public, max-age=60'
31
+ const PRODUCT_NOT_FOUND = { error: 'product_not_found' } as const
32
+
33
+ type ProductRouteParams = { idOrHandle?: string }
34
+
35
+ type ProductRouteContext = { params?: ProductRouteParams | Promise<ProductRouteParams> }
36
+
37
+ export async function GET(req: Request, routeContext: ProductRouteContext = {}) {
38
+ try {
39
+ const query = parseStorefrontProductDetailQuery(new URL(req.url).searchParams)
40
+ const container = await createRequestContainer()
41
+ const service = container.resolve('storeContextService') as StoreContextService
42
+ const context = await service.resolve(req, { pathname: query.path ?? '/' })
43
+ return await runInStoreCacheTenant(context, async () => {
44
+ const rateLimited = await enforceStorefrontRateLimit(container, req, context, 'productDetail')
45
+ if (rateLimited) return rateLimited
46
+ const params = ecommerceStorefrontProductParamsSchema.safeParse((await routeContext.params) ?? {})
47
+ if (!params.success) return storefrontErrorResponse(404, { ...PRODUCT_NOT_FOUND })
48
+ const detail = await cachedGetStorefrontProductDetail(container, context, params.data.idOrHandle, {
49
+ variantId: query.variantId ?? null,
50
+ locale: query.locale ?? null,
51
+ })
52
+ if (!detail) return storefrontErrorResponse(404, { ...PRODUCT_NOT_FOUND })
53
+ return NextResponse.json(detail, { headers: storefrontSuccessHeaders(context, ANONYMOUS_CACHE_CONTROL) })
54
+ })
55
+ } catch (error) {
56
+ return storefrontRouteErrorResponse(error, {
57
+ message: 'Storefront product detail failed',
58
+ code: 'ecommerce.storefront_product_detail_failed',
59
+ })
60
+ }
61
+ }
62
+
63
+ export default GET
64
+
65
+ const storefrontProductTag = 'Ecommerce'
66
+
67
+ const storefrontProductGetDoc: OpenApiMethodDoc = {
68
+ summary: 'Get one storefront product for the current buyer',
69
+ description:
70
+ 'Public. Resolves the store from the Host header and the optional portal session, then returns the product by UUID or handle with buyer-resolved prices per variant. A product that does not exist, is inactive, deleted, of another tenant or outside the buyer\'s effective assortment answers the same 404 `{ "error": "product_not_found" }`. `variantId` preselects a variant; top-level `priceTiers` are the selected variant\'s tiers. Anonymous responses are `public, max-age=60`; authenticated responses are `private, no-store`. Cached server-side for 60 s per buyer context digest. Rate limited per IP and store.',
71
+ tags: [storefrontProductTag],
72
+ pathParams: ecommerceStorefrontProductParamsSchema,
73
+ query: ecommerceStorefrontProductDetailQuerySchema,
74
+ responses: [
75
+ { status: 200, description: 'The product as this buyer sees it.', schema: storefrontProductDetailResponseSchema },
76
+ ],
77
+ errors: [
78
+ { status: 400, description: 'Unknown, repeated or malformed query parameters', schema: storefrontInvalidQueryErrorSchema },
79
+ { status: 401, description: 'Portal session invalid or issued for another tenant or organization', schema: storefrontErrorSchema },
80
+ { status: 403, description: 'Draft store (only when OM_ECOMMERCE_DEV_STORE_SLUG=true)', schema: storefrontErrorSchema },
81
+ { status: 404, description: 'No store serves this host or path, or the product is not visible to this buyer', schema: storefrontErrorSchema },
82
+ { status: 410, description: 'Store archived', schema: storefrontErrorSchema },
83
+ { status: 429, description: 'Too many requests', schema: rateLimitErrorSchema },
84
+ { status: 503, description: 'Store misconfigured (no default channel binding)', schema: storefrontErrorSchema },
85
+ ],
86
+ }
87
+
88
+ export const openApi: OpenApiRouteDoc = {
89
+ tag: storefrontProductTag,
90
+ summary: 'Public storefront product detail',
91
+ methods: {
92
+ GET: storefrontProductGetDoc,
93
+ },
94
+ }