@open-mercato/core 0.9.1-develop.7474.1.51db10479d → 0.9.1-develop.7475.1.28ca93ed97

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (597) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/catalog_price_history_entry/index.js +53 -0
  3. package/dist/generated/entities/catalog_price_history_entry/index.js.map +7 -0
  4. package/dist/generated/entities/customer_group_terms/index.js +2 -0
  5. package/dist/generated/entities/customer_group_terms/index.js.map +2 -2
  6. package/dist/generated/entities/ecommerce_store/index.js +33 -0
  7. package/dist/generated/entities/ecommerce_store/index.js.map +7 -0
  8. package/dist/generated/entities/ecommerce_store_channel_binding/index.js +29 -0
  9. package/dist/generated/entities/ecommerce_store_channel_binding/index.js.map +7 -0
  10. package/dist/generated/entities/ecommerce_store_domain_binding/index.js +23 -0
  11. package/dist/generated/entities/ecommerce_store_domain_binding/index.js.map +7 -0
  12. package/dist/generated/entities.ids.generated.js +8 -1
  13. package/dist/generated/entities.ids.generated.js.map +2 -2
  14. package/dist/generated/entity-fields-registry.js +72 -0
  15. package/dist/generated/entity-fields-registry.js.map +2 -2
  16. package/dist/helpers/integration/customerGroupsFixtures.js.map +2 -2
  17. package/dist/modules/catalog/acl.js +8 -1
  18. package/dist/modules/catalog/acl.js.map +2 -2
  19. package/dist/modules/catalog/api/config/omnibus/route.js +184 -0
  20. package/dist/modules/catalog/api/config/omnibus/route.js.map +7 -0
  21. package/dist/modules/catalog/api/prices/history/route.js +131 -0
  22. package/dist/modules/catalog/api/prices/history/route.js.map +7 -0
  23. package/dist/modules/catalog/api/prices/omnibus-preview/route.js +184 -0
  24. package/dist/modules/catalog/api/prices/omnibus-preview/route.js.map +7 -0
  25. package/dist/modules/catalog/api/products/route.js +54 -245
  26. package/dist/modules/catalog/api/products/route.js.map +3 -3
  27. package/dist/modules/catalog/api/tags/route.js +6 -0
  28. package/dist/modules/catalog/api/tags/route.js.map +2 -2
  29. package/dist/modules/catalog/backend/config/catalog/page.js +3 -1
  30. package/dist/modules/catalog/backend/config/catalog/page.js.map +2 -2
  31. package/dist/modules/catalog/cli.js +80 -2
  32. package/dist/modules/catalog/cli.js.map +2 -2
  33. package/dist/modules/catalog/commands/categories.js +51 -2
  34. package/dist/modules/catalog/commands/categories.js.map +2 -2
  35. package/dist/modules/catalog/commands/prices.js +43 -1
  36. package/dist/modules/catalog/commands/prices.js.map +2 -2
  37. package/dist/modules/catalog/commands/products.js +71 -0
  38. package/dist/modules/catalog/commands/products.js.map +2 -2
  39. package/dist/modules/catalog/commands/variants.js +7 -0
  40. package/dist/modules/catalog/commands/variants.js.map +2 -2
  41. package/dist/modules/catalog/components/OmnibusSettings.js +703 -0
  42. package/dist/modules/catalog/components/OmnibusSettings.js.map +7 -0
  43. package/dist/modules/catalog/components/PriceEditorOmnibusRow.js +152 -0
  44. package/dist/modules/catalog/components/PriceEditorOmnibusRow.js.map +7 -0
  45. package/dist/modules/catalog/components/lookupSources.js +31 -0
  46. package/dist/modules/catalog/components/lookupSources.js.map +7 -0
  47. package/dist/modules/catalog/components/prices/PriceScopeSelectors.js +8 -2
  48. package/dist/modules/catalog/components/prices/PriceScopeSelectors.js.map +2 -2
  49. package/dist/modules/catalog/components/products/VariantBuilder.js +11 -1
  50. package/dist/modules/catalog/components/products/VariantBuilder.js.map +2 -2
  51. package/dist/modules/catalog/data/entities.js +122 -1
  52. package/dist/modules/catalog/data/entities.js.map +2 -2
  53. package/dist/modules/catalog/data/validators.js +78 -0
  54. package/dist/modules/catalog/data/validators.js.map +2 -2
  55. package/dist/modules/catalog/di.js +18 -0
  56. package/dist/modules/catalog/di.js.map +2 -2
  57. package/dist/modules/catalog/lib/omnibus.js +285 -0
  58. package/dist/modules/catalog/lib/omnibus.js.map +7 -0
  59. package/dist/modules/catalog/lib/omnibusBackfill.js +146 -0
  60. package/dist/modules/catalog/lib/omnibusBackfill.js.map +7 -0
  61. package/dist/modules/catalog/lib/omnibusCache.js +89 -0
  62. package/dist/modules/catalog/lib/omnibusCache.js.map +7 -0
  63. package/dist/modules/catalog/lib/omnibusConfig.js +64 -0
  64. package/dist/modules/catalog/lib/omnibusConfig.js.map +7 -0
  65. package/dist/modules/catalog/lib/omnibusHistoryQueries.js +191 -0
  66. package/dist/modules/catalog/lib/omnibusHistoryQueries.js.map +7 -0
  67. package/dist/modules/catalog/lib/omnibusPresentedEntry.js +73 -0
  68. package/dist/modules/catalog/lib/omnibusPresentedEntry.js.map +7 -0
  69. package/dist/modules/catalog/lib/omnibusProductListEnrichment.js +94 -0
  70. package/dist/modules/catalog/lib/omnibusProductListEnrichment.js.map +7 -0
  71. package/dist/modules/catalog/lib/omnibusTypes.js +115 -0
  72. package/dist/modules/catalog/lib/omnibusTypes.js.map +7 -0
  73. package/dist/modules/catalog/lib/optionSchemaTranslations.js +55 -0
  74. package/dist/modules/catalog/lib/optionSchemaTranslations.js.map +7 -0
  75. package/dist/modules/catalog/lib/priceHistoryQuery.js +103 -0
  76. package/dist/modules/catalog/lib/priceHistoryQuery.js.map +7 -0
  77. package/dist/modules/catalog/lib/pricing.js +26 -2
  78. package/dist/modules/catalog/lib/pricing.js.map +2 -2
  79. package/dist/modules/catalog/lib/productFilters.js +273 -0
  80. package/dist/modules/catalog/lib/productFilters.js.map +7 -0
  81. package/dist/modules/catalog/lib/productScopeKeys.js +105 -0
  82. package/dist/modules/catalog/lib/productScopeKeys.js.map +7 -0
  83. package/dist/modules/catalog/lib/productScopeReindex.js +73 -0
  84. package/dist/modules/catalog/lib/productScopeReindex.js.map +7 -0
  85. package/dist/modules/catalog/migrations/Migration20261005105352_catalog.js +39 -0
  86. package/dist/modules/catalog/migrations/Migration20261005105352_catalog.js.map +7 -0
  87. package/dist/modules/catalog/migrations/Migration20261005150824_catalog.js +39 -0
  88. package/dist/modules/catalog/migrations/Migration20261005150824_catalog.js.map +7 -0
  89. package/dist/modules/catalog/services/catalogOmnibusService.js +480 -0
  90. package/dist/modules/catalog/services/catalogOmnibusService.js.map +7 -0
  91. package/dist/modules/catalog/setup.js +3 -1
  92. package/dist/modules/catalog/setup.js.map +2 -2
  93. package/dist/modules/catalog/subscribers/product-scope-keys-category-deleted.js +16 -0
  94. package/dist/modules/catalog/subscribers/product-scope-keys-category-deleted.js.map +7 -0
  95. package/dist/modules/catalog/subscribers/product-scope-keys-category-updated.js +17 -0
  96. package/dist/modules/catalog/subscribers/product-scope-keys-category-updated.js.map +7 -0
  97. package/dist/modules/catalog/translations.js +10 -0
  98. package/dist/modules/catalog/translations.js.map +2 -2
  99. package/dist/modules/configs/lib/upgrade-actions.js +28 -0
  100. package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
  101. package/dist/modules/customer_groups/api/customer-groups/[id]/terms/route.js +5 -0
  102. package/dist/modules/customer_groups/api/customer-groups/[id]/terms/route.js.map +2 -2
  103. package/dist/modules/customer_groups/components/CustomerGroupTermsSection.js +111 -3
  104. package/dist/modules/customer_groups/components/CustomerGroupTermsSection.js.map +2 -2
  105. package/dist/modules/customer_groups/components/customerGroupAssortmentScope.js +43 -0
  106. package/dist/modules/customer_groups/components/customerGroupAssortmentScope.js.map +7 -0
  107. package/dist/modules/customer_groups/data/entities.js +3 -0
  108. package/dist/modules/customer_groups/data/entities.js.map +2 -2
  109. package/dist/modules/customer_groups/data/validators.js +9 -0
  110. package/dist/modules/customer_groups/data/validators.js.map +2 -2
  111. package/dist/modules/customer_groups/migrations/Migration20261005110250_customer_groups.js +17 -0
  112. package/dist/modules/customer_groups/migrations/Migration20261005110250_customer_groups.js.map +7 -0
  113. package/dist/modules/customer_groups/services/customerGroupsService.js +62 -15
  114. package/dist/modules/customer_groups/services/customerGroupsService.js.map +2 -2
  115. package/dist/modules/ecommerce/acl.js +13 -0
  116. package/dist/modules/ecommerce/acl.js.map +7 -0
  117. package/dist/modules/ecommerce/api/domain-mappings/route.js +72 -0
  118. package/dist/modules/ecommerce/api/domain-mappings/route.js.map +7 -0
  119. package/dist/modules/ecommerce/api/openapi.js +27 -0
  120. package/dist/modules/ecommerce/api/openapi.js.map +7 -0
  121. package/dist/modules/ecommerce/api/store-channel-bindings/[id]/assortment-count/route.js +109 -0
  122. package/dist/modules/ecommerce/api/store-channel-bindings/[id]/assortment-count/route.js.map +7 -0
  123. package/dist/modules/ecommerce/api/store-channel-bindings/crud.js +265 -0
  124. package/dist/modules/ecommerce/api/store-channel-bindings/crud.js.map +7 -0
  125. package/dist/modules/ecommerce/api/store-channel-bindings/route.js +67 -0
  126. package/dist/modules/ecommerce/api/store-channel-bindings/route.js.map +7 -0
  127. package/dist/modules/ecommerce/api/store-domain-bindings/crud.js +296 -0
  128. package/dist/modules/ecommerce/api/store-domain-bindings/crud.js.map +7 -0
  129. package/dist/modules/ecommerce/api/store-domain-bindings/route.js +73 -0
  130. package/dist/modules/ecommerce/api/store-domain-bindings/route.js.map +7 -0
  131. package/dist/modules/ecommerce/api/storefront/categories/[slug]/route.js +87 -0
  132. package/dist/modules/ecommerce/api/storefront/categories/[slug]/route.js.map +7 -0
  133. package/dist/modules/ecommerce/api/storefront/categories/openapiSchemas.js +50 -0
  134. package/dist/modules/ecommerce/api/storefront/categories/openapiSchemas.js.map +7 -0
  135. package/dist/modules/ecommerce/api/storefront/categories/route.js +75 -0
  136. package/dist/modules/ecommerce/api/storefront/categories/route.js.map +7 -0
  137. package/dist/modules/ecommerce/api/storefront/context/route.js +163 -0
  138. package/dist/modules/ecommerce/api/storefront/context/route.js.map +7 -0
  139. package/dist/modules/ecommerce/api/storefront/products/[idOrHandle]/route.js +87 -0
  140. package/dist/modules/ecommerce/api/storefront/products/[idOrHandle]/route.js.map +7 -0
  141. package/dist/modules/ecommerce/api/storefront/products/openapiSchemas.js +177 -0
  142. package/dist/modules/ecommerce/api/storefront/products/openapiSchemas.js.map +7 -0
  143. package/dist/modules/ecommerce/api/storefront/products/route.js +78 -0
  144. package/dist/modules/ecommerce/api/storefront/products/route.js.map +7 -0
  145. package/dist/modules/ecommerce/api/storefront/search/suggest/openapiSchemas.js +25 -0
  146. package/dist/modules/ecommerce/api/storefront/search/suggest/openapiSchemas.js.map +7 -0
  147. package/dist/modules/ecommerce/api/storefront/search/suggest/route.js +75 -0
  148. package/dist/modules/ecommerce/api/storefront/search/suggest/route.js.map +7 -0
  149. package/dist/modules/ecommerce/api/storefront/storefrontRouteSupport.js +58 -0
  150. package/dist/modules/ecommerce/api/storefront/storefrontRouteSupport.js.map +7 -0
  151. package/dist/modules/ecommerce/api/stores/[id]/branding/route.js +183 -0
  152. package/dist/modules/ecommerce/api/stores/[id]/branding/route.js.map +7 -0
  153. package/dist/modules/ecommerce/api/stores/[id]/preview-branding/route.js +92 -0
  154. package/dist/modules/ecommerce/api/stores/[id]/preview-branding/route.js.map +7 -0
  155. package/dist/modules/ecommerce/api/stores/crud.js +401 -0
  156. package/dist/modules/ecommerce/api/stores/crud.js.map +7 -0
  157. package/dist/modules/ecommerce/api/stores/route.js +64 -0
  158. package/dist/modules/ecommerce/api/stores/route.js.map +7 -0
  159. package/dist/modules/ecommerce/backend/config/ecommerce/[id]/page.js +9 -0
  160. package/dist/modules/ecommerce/backend/config/ecommerce/[id]/page.js.map +7 -0
  161. package/dist/modules/ecommerce/backend/config/ecommerce/[id]/page.meta.js +16 -0
  162. package/dist/modules/ecommerce/backend/config/ecommerce/[id]/page.meta.js.map +7 -0
  163. package/dist/modules/ecommerce/backend/config/ecommerce/page.js +9 -0
  164. package/dist/modules/ecommerce/backend/config/ecommerce/page.js.map +7 -0
  165. package/dist/modules/ecommerce/backend/config/ecommerce/page.meta.js +16 -0
  166. package/dist/modules/ecommerce/backend/config/ecommerce/page.meta.js.map +7 -0
  167. package/dist/modules/ecommerce/commands/stores.js +110 -0
  168. package/dist/modules/ecommerce/commands/stores.js.map +7 -0
  169. package/dist/modules/ecommerce/components/ChannelAssortmentCount.js +90 -0
  170. package/dist/modules/ecommerce/components/ChannelAssortmentCount.js.map +7 -0
  171. package/dist/modules/ecommerce/components/ChannelBindingFormSections.js +33 -0
  172. package/dist/modules/ecommerce/components/ChannelBindingFormSections.js.map +7 -0
  173. package/dist/modules/ecommerce/components/ChannelLookupPicker.js +11 -0
  174. package/dist/modules/ecommerce/components/ChannelLookupPicker.js.map +7 -0
  175. package/dist/modules/ecommerce/components/DomainStatusWarning.js +24 -0
  176. package/dist/modules/ecommerce/components/DomainStatusWarning.js.map +7 -0
  177. package/dist/modules/ecommerce/components/StoreAvailabilityDefaultsSection.js +190 -0
  178. package/dist/modules/ecommerce/components/StoreAvailabilityDefaultsSection.js.map +7 -0
  179. package/dist/modules/ecommerce/components/StoreBrandingControls.js +67 -0
  180. package/dist/modules/ecommerce/components/StoreBrandingControls.js.map +7 -0
  181. package/dist/modules/ecommerce/components/StoreBrandingPreview.js +69 -0
  182. package/dist/modules/ecommerce/components/StoreBrandingPreview.js.map +7 -0
  183. package/dist/modules/ecommerce/components/StoreBrandingTab.js +218 -0
  184. package/dist/modules/ecommerce/components/StoreBrandingTab.js.map +7 -0
  185. package/dist/modules/ecommerce/components/StoreChannelBindingDialog.js +270 -0
  186. package/dist/modules/ecommerce/components/StoreChannelBindingDialog.js.map +7 -0
  187. package/dist/modules/ecommerce/components/StoreChannelsTab.js +313 -0
  188. package/dist/modules/ecommerce/components/StoreChannelsTab.js.map +7 -0
  189. package/dist/modules/ecommerce/components/StoreCreateDialog.js +164 -0
  190. package/dist/modules/ecommerce/components/StoreCreateDialog.js.map +7 -0
  191. package/dist/modules/ecommerce/components/StoreDomainBindingDialog.js +162 -0
  192. package/dist/modules/ecommerce/components/StoreDomainBindingDialog.js.map +7 -0
  193. package/dist/modules/ecommerce/components/StoreDomainsTab.js +357 -0
  194. package/dist/modules/ecommerce/components/StoreDomainsTab.js.map +7 -0
  195. package/dist/modules/ecommerce/components/StoreEditPage.js +155 -0
  196. package/dist/modules/ecommerce/components/StoreEditPage.js.map +7 -0
  197. package/dist/modules/ecommerce/components/StoreGeneralTab.js +167 -0
  198. package/dist/modules/ecommerce/components/StoreGeneralTab.js.map +7 -0
  199. package/dist/modules/ecommerce/components/StoreListPage.js +290 -0
  200. package/dist/modules/ecommerce/components/StoreListPage.js.map +7 -0
  201. package/dist/modules/ecommerce/components/StoreSeoTab.js +132 -0
  202. package/dist/modules/ecommerce/components/StoreSeoTab.js.map +7 -0
  203. package/dist/modules/ecommerce/components/StoreStatusBadge.js +20 -0
  204. package/dist/modules/ecommerce/components/StoreStatusBadge.js.map +7 -0
  205. package/dist/modules/ecommerce/components/channelLookups.js +28 -0
  206. package/dist/modules/ecommerce/components/channelLookups.js.map +7 -0
  207. package/dist/modules/ecommerce/components/storeAdmin.js +41 -0
  208. package/dist/modules/ecommerce/components/storeAdmin.js.map +7 -0
  209. package/dist/modules/ecommerce/components/storeBrandingForm.js +197 -0
  210. package/dist/modules/ecommerce/components/storeBrandingForm.js.map +7 -0
  211. package/dist/modules/ecommerce/components/storeChannels.js +140 -0
  212. package/dist/modules/ecommerce/components/storeChannels.js.map +7 -0
  213. package/dist/modules/ecommerce/components/storeDomains.js +167 -0
  214. package/dist/modules/ecommerce/components/storeDomains.js.map +7 -0
  215. package/dist/modules/ecommerce/components/storeEditTabs.js +62 -0
  216. package/dist/modules/ecommerce/components/storeEditTabs.js.map +7 -0
  217. package/dist/modules/ecommerce/components/storeGeneral.js +95 -0
  218. package/dist/modules/ecommerce/components/storeGeneral.js.map +7 -0
  219. package/dist/modules/ecommerce/components/storeSeo.js +69 -0
  220. package/dist/modules/ecommerce/components/storeSeo.js.map +7 -0
  221. package/dist/modules/ecommerce/components/useChannelAssortmentCount.js +45 -0
  222. package/dist/modules/ecommerce/components/useChannelAssortmentCount.js.map +7 -0
  223. package/dist/modules/ecommerce/components/useStoreAccess.js +28 -0
  224. package/dist/modules/ecommerce/components/useStoreAccess.js.map +7 -0
  225. package/dist/modules/ecommerce/data/enrichers.js +157 -0
  226. package/dist/modules/ecommerce/data/enrichers.js.map +7 -0
  227. package/dist/modules/ecommerce/data/entities.js +197 -0
  228. package/dist/modules/ecommerce/data/entities.js.map +7 -0
  229. package/dist/modules/ecommerce/data/validators.js +406 -0
  230. package/dist/modules/ecommerce/data/validators.js.map +7 -0
  231. package/dist/modules/ecommerce/di.js +11 -0
  232. package/dist/modules/ecommerce/di.js.map +7 -0
  233. package/dist/modules/ecommerce/events.js +27 -0
  234. package/dist/modules/ecommerce/events.js.map +7 -0
  235. package/dist/modules/ecommerce/index.js +16 -0
  236. package/dist/modules/ecommerce/index.js.map +7 -0
  237. package/dist/modules/ecommerce/lib/assortmentCount.js +112 -0
  238. package/dist/modules/ecommerce/lib/assortmentCount.js.map +7 -0
  239. package/dist/modules/ecommerce/lib/brandingStyles.js +184 -0
  240. package/dist/modules/ecommerce/lib/brandingStyles.js.map +7 -0
  241. package/dist/modules/ecommerce/lib/buyerContext.js +284 -0
  242. package/dist/modules/ecommerce/lib/buyerContext.js.map +7 -0
  243. package/dist/modules/ecommerce/lib/cacheInvalidation.js +186 -0
  244. package/dist/modules/ecommerce/lib/cacheInvalidation.js.map +7 -0
  245. package/dist/modules/ecommerce/lib/cacheKeys.js +236 -0
  246. package/dist/modules/ecommerce/lib/cacheKeys.js.map +7 -0
  247. package/dist/modules/ecommerce/lib/createConflictRecheck.js +29 -0
  248. package/dist/modules/ecommerce/lib/createConflictRecheck.js.map +7 -0
  249. package/dist/modules/ecommerce/lib/crudEvents.js +94 -0
  250. package/dist/modules/ecommerce/lib/crudEvents.js.map +7 -0
  251. package/dist/modules/ecommerce/lib/crudSupport.js +64 -0
  252. package/dist/modules/ecommerce/lib/crudSupport.js.map +7 -0
  253. package/dist/modules/ecommerce/lib/domainBindingRebind.js +99 -0
  254. package/dist/modules/ecommerce/lib/domainBindingRebind.js.map +7 -0
  255. package/dist/modules/ecommerce/lib/domainMappingSummaries.js +39 -0
  256. package/dist/modules/ecommerce/lib/domainMappingSummaries.js.map +7 -0
  257. package/dist/modules/ecommerce/lib/exclusiveFlag.js +57 -0
  258. package/dist/modules/ecommerce/lib/exclusiveFlag.js.map +7 -0
  259. package/dist/modules/ecommerce/lib/references.js +70 -0
  260. package/dist/modules/ecommerce/lib/references.js.map +7 -0
  261. package/dist/modules/ecommerce/lib/seedDraftStore.js +99 -0
  262. package/dist/modules/ecommerce/lib/seedDraftStore.js.map +7 -0
  263. package/dist/modules/ecommerce/lib/storeBindingCascade.js +109 -0
  264. package/dist/modules/ecommerce/lib/storeBindingCascade.js.map +7 -0
  265. package/dist/modules/ecommerce/lib/storeBranding.js +40 -0
  266. package/dist/modules/ecommerce/lib/storeBranding.js.map +7 -0
  267. package/dist/modules/ecommerce/lib/storeBrandingRoute.js +76 -0
  268. package/dist/modules/ecommerce/lib/storeBrandingRoute.js.map +7 -0
  269. package/dist/modules/ecommerce/lib/storeContext.js +393 -0
  270. package/dist/modules/ecommerce/lib/storeContext.js.map +7 -0
  271. package/dist/modules/ecommerce/lib/storeContextService.js +43 -0
  272. package/dist/modules/ecommerce/lib/storeContextService.js.map +7 -0
  273. package/dist/modules/ecommerce/lib/storeLocale.js +60 -0
  274. package/dist/modules/ecommerce/lib/storeLocale.js.map +7 -0
  275. package/dist/modules/ecommerce/lib/storeNotifications.js +41 -0
  276. package/dist/modules/ecommerce/lib/storeNotifications.js.map +7 -0
  277. package/dist/modules/ecommerce/lib/storefrontCatalogSupport.js +248 -0
  278. package/dist/modules/ecommerce/lib/storefrontCatalogSupport.js.map +7 -0
  279. package/dist/modules/ecommerce/lib/storefrontCategories.js +241 -0
  280. package/dist/modules/ecommerce/lib/storefrontCategories.js.map +7 -0
  281. package/dist/modules/ecommerce/lib/storefrontCategoryCache.js +71 -0
  282. package/dist/modules/ecommerce/lib/storefrontCategoryCache.js.map +7 -0
  283. package/dist/modules/ecommerce/lib/storefrontDetail.js +447 -0
  284. package/dist/modules/ecommerce/lib/storefrontDetail.js.map +7 -0
  285. package/dist/modules/ecommerce/lib/storefrontFacets.js +441 -0
  286. package/dist/modules/ecommerce/lib/storefrontFacets.js.map +7 -0
  287. package/dist/modules/ecommerce/lib/storefrontOptionFilter.js +15 -0
  288. package/dist/modules/ecommerce/lib/storefrontOptionFilter.js.map +7 -0
  289. package/dist/modules/ecommerce/lib/storefrontPricing.js +393 -0
  290. package/dist/modules/ecommerce/lib/storefrontPricing.js.map +7 -0
  291. package/dist/modules/ecommerce/lib/storefrontProductCache.js +90 -0
  292. package/dist/modules/ecommerce/lib/storefrontProductCache.js.map +7 -0
  293. package/dist/modules/ecommerce/lib/storefrontProductScope.js +107 -0
  294. package/dist/modules/ecommerce/lib/storefrontProductScope.js.map +7 -0
  295. package/dist/modules/ecommerce/lib/storefrontProducts.js +670 -0
  296. package/dist/modules/ecommerce/lib/storefrontProducts.js.map +7 -0
  297. package/dist/modules/ecommerce/lib/storefrontQuery.js +156 -0
  298. package/dist/modules/ecommerce/lib/storefrontQuery.js.map +7 -0
  299. package/dist/modules/ecommerce/lib/storefrontRateLimit.js +99 -0
  300. package/dist/modules/ecommerce/lib/storefrontRateLimit.js.map +7 -0
  301. package/dist/modules/ecommerce/lib/storefrontSearch.js +303 -0
  302. package/dist/modules/ecommerce/lib/storefrontSearch.js.map +7 -0
  303. package/dist/modules/ecommerce/lib/storefrontSearchCache.js +35 -0
  304. package/dist/modules/ecommerce/lib/storefrontSearchCache.js.map +7 -0
  305. package/dist/modules/ecommerce/lib/subscriberSupport.js +33 -0
  306. package/dist/modules/ecommerce/lib/subscriberSupport.js.map +7 -0
  307. package/dist/modules/ecommerce/lib/types.js +1 -0
  308. package/dist/modules/ecommerce/lib/types.js.map +7 -0
  309. package/dist/modules/ecommerce/migrations/Migration20261005113233_ecommerce.js +32 -0
  310. package/dist/modules/ecommerce/migrations/Migration20261005113233_ecommerce.js.map +7 -0
  311. package/dist/modules/ecommerce/migrations/Migration20261006112733_ecommerce.js +17 -0
  312. package/dist/modules/ecommerce/migrations/Migration20261006112733_ecommerce.js.map +7 -0
  313. package/dist/modules/ecommerce/notifications.client.js +47 -0
  314. package/dist/modules/ecommerce/notifications.client.js.map +7 -0
  315. package/dist/modules/ecommerce/notifications.js +48 -0
  316. package/dist/modules/ecommerce/notifications.js.map +7 -0
  317. package/dist/modules/ecommerce/search.js +83 -0
  318. package/dist/modules/ecommerce/search.js.map +7 -0
  319. package/dist/modules/ecommerce/setup.js +17 -0
  320. package/dist/modules/ecommerce/setup.js.map +7 -0
  321. package/dist/modules/ecommerce/subscribers/assortment-empty-notifications.js +14 -0
  322. package/dist/modules/ecommerce/subscribers/assortment-empty-notifications.js.map +7 -0
  323. package/dist/modules/ecommerce/subscribers/availability-policy-cache-invalidation.js +14 -0
  324. package/dist/modules/ecommerce/subscribers/availability-policy-cache-invalidation.js.map +7 -0
  325. package/dist/modules/ecommerce/subscribers/catalog-category-cache-invalidation.js +14 -0
  326. package/dist/modules/ecommerce/subscribers/catalog-category-cache-invalidation.js.map +7 -0
  327. package/dist/modules/ecommerce/subscribers/catalog-price-cache-invalidation.js +14 -0
  328. package/dist/modules/ecommerce/subscribers/catalog-price-cache-invalidation.js.map +7 -0
  329. package/dist/modules/ecommerce/subscribers/catalog-product-cache-invalidation.js +14 -0
  330. package/dist/modules/ecommerce/subscribers/catalog-product-cache-invalidation.js.map +7 -0
  331. package/dist/modules/ecommerce/subscribers/catalog-variant-cache-invalidation.js +14 -0
  332. package/dist/modules/ecommerce/subscribers/catalog-variant-cache-invalidation.js.map +7 -0
  333. package/dist/modules/ecommerce/subscribers/customer-group-cache-invalidation.js +14 -0
  334. package/dist/modules/ecommerce/subscribers/customer-group-cache-invalidation.js.map +7 -0
  335. package/dist/modules/ecommerce/subscribers/customer-group-membership-cache-invalidation.js +14 -0
  336. package/dist/modules/ecommerce/subscribers/customer-group-membership-cache-invalidation.js.map +7 -0
  337. package/dist/modules/ecommerce/subscribers/customer-group-terms-cache-invalidation.js +14 -0
  338. package/dist/modules/ecommerce/subscribers/customer-group-terms-cache-invalidation.js.map +7 -0
  339. package/dist/modules/ecommerce/subscribers/customer-user-deleted-cache-invalidation.js +14 -0
  340. package/dist/modules/ecommerce/subscribers/customer-user-deleted-cache-invalidation.js.map +7 -0
  341. package/dist/modules/ecommerce/subscribers/customer-user-updated-cache-invalidation.js +14 -0
  342. package/dist/modules/ecommerce/subscribers/customer-user-updated-cache-invalidation.js.map +7 -0
  343. package/dist/modules/ecommerce/subscribers/domain-binding-rebind.js +16 -0
  344. package/dist/modules/ecommerce/subscribers/domain-binding-rebind.js.map +7 -0
  345. package/dist/modules/ecommerce/subscribers/domain-mapping-cache-invalidation.js +14 -0
  346. package/dist/modules/ecommerce/subscribers/domain-mapping-cache-invalidation.js.map +7 -0
  347. package/dist/modules/ecommerce/subscribers/store-cache-invalidation.js +14 -0
  348. package/dist/modules/ecommerce/subscribers/store-cache-invalidation.js.map +7 -0
  349. package/dist/modules/ecommerce/subscribers/store-channel-binding-cache-invalidation.js +14 -0
  350. package/dist/modules/ecommerce/subscribers/store-channel-binding-cache-invalidation.js.map +7 -0
  351. package/dist/modules/ecommerce/subscribers/store-domain-binding-cache-invalidation.js +14 -0
  352. package/dist/modules/ecommerce/subscribers/store-domain-binding-cache-invalidation.js.map +7 -0
  353. package/dist/modules/ecommerce/subscribers/store-notifications.js +14 -0
  354. package/dist/modules/ecommerce/subscribers/store-notifications.js.map +7 -0
  355. package/dist/modules/query_index/lib/batch.js +23 -9
  356. package/dist/modules/query_index/lib/batch.js.map +2 -2
  357. package/dist/modules/query_index/lib/doc-enrichers.js +212 -0
  358. package/dist/modules/query_index/lib/doc-enrichers.js.map +7 -0
  359. package/dist/modules/query_index/lib/document.js +2 -0
  360. package/dist/modules/query_index/lib/document.js.map +2 -2
  361. package/dist/modules/query_index/lib/engine.js +68 -1
  362. package/dist/modules/query_index/lib/engine.js.map +2 -2
  363. package/dist/modules/query_index/lib/indexer.js +7 -0
  364. package/dist/modules/query_index/lib/indexer.js.map +2 -2
  365. package/dist/modules/query_index/lib/search-tokens.js +2 -0
  366. package/dist/modules/query_index/lib/search-tokens.js.map +2 -2
  367. package/dist/modules/query_index/migrations/Migration20261005201500_query_index.js +17 -0
  368. package/dist/modules/query_index/migrations/Migration20261005201500_query_index.js.map +7 -0
  369. package/dist/modules/translations/components/TranslationManager.js +3 -3
  370. package/dist/modules/translations/components/TranslationManager.js.map +2 -2
  371. package/dist/modules/translations/data/validators.js +1 -1
  372. package/dist/modules/translations/data/validators.js.map +1 -1
  373. package/dist/modules/translations/lib/batch.js +21 -1
  374. package/dist/modules/translations/lib/batch.js.map +2 -2
  375. package/dist/modules/translations/lib/helpers.js +3 -0
  376. package/dist/modules/translations/lib/helpers.js.map +2 -2
  377. package/dist/modules/translations/lib/resolve-field-list.js +14 -2
  378. package/dist/modules/translations/lib/resolve-field-list.js.map +2 -2
  379. package/generated/entities/catalog_price_history_entry/index.ts +25 -0
  380. package/generated/entities/customer_group_terms/index.ts +1 -0
  381. package/generated/entities/ecommerce_store/index.ts +15 -0
  382. package/generated/entities/ecommerce_store_channel_binding/index.ts +13 -0
  383. package/generated/entities/ecommerce_store_domain_binding/index.ts +10 -0
  384. package/generated/entities.ids.generated.ts +8 -1
  385. package/generated/entity-fields-registry.ts +72 -0
  386. package/package.json +7 -7
  387. package/src/helpers/integration/customerGroupsFixtures.ts +2 -0
  388. package/src/modules/auth/i18n/de.json +8 -0
  389. package/src/modules/auth/i18n/en.json +8 -0
  390. package/src/modules/auth/i18n/es.json +8 -0
  391. package/src/modules/auth/i18n/ko.json +8 -0
  392. package/src/modules/auth/i18n/pl.json +8 -0
  393. package/src/modules/catalog/AGENTS.md +5 -2
  394. package/src/modules/catalog/acl.ts +7 -0
  395. package/src/modules/catalog/api/config/omnibus/route.ts +204 -0
  396. package/src/modules/catalog/api/prices/history/route.ts +139 -0
  397. package/src/modules/catalog/api/prices/omnibus-preview/route.ts +202 -0
  398. package/src/modules/catalog/api/products/route.ts +53 -308
  399. package/src/modules/catalog/api/tags/route.ts +6 -0
  400. package/src/modules/catalog/backend/config/catalog/page.tsx +2 -0
  401. package/src/modules/catalog/cli.ts +84 -1
  402. package/src/modules/catalog/commands/categories.ts +54 -2
  403. package/src/modules/catalog/commands/prices.ts +43 -1
  404. package/src/modules/catalog/commands/products.ts +84 -1
  405. package/src/modules/catalog/commands/variants.ts +7 -0
  406. package/src/modules/catalog/components/OmnibusSettings.tsx +832 -0
  407. package/src/modules/catalog/components/PriceEditorOmnibusRow.tsx +219 -0
  408. package/src/modules/catalog/components/lookupSources.ts +29 -0
  409. package/src/modules/catalog/components/prices/PriceScopeSelectors.tsx +9 -0
  410. package/src/modules/catalog/components/products/VariantBuilder.tsx +9 -0
  411. package/src/modules/catalog/data/entities.ts +123 -1
  412. package/src/modules/catalog/data/validators.ts +118 -0
  413. package/src/modules/catalog/di.ts +23 -0
  414. package/src/modules/catalog/i18n/de.json +77 -0
  415. package/src/modules/catalog/i18n/en.json +77 -0
  416. package/src/modules/catalog/i18n/es.json +77 -0
  417. package/src/modules/catalog/i18n/ko.json +77 -0
  418. package/src/modules/catalog/i18n/pl.json +77 -0
  419. package/src/modules/catalog/lib/omnibus.ts +406 -0
  420. package/src/modules/catalog/lib/omnibusBackfill.ts +198 -0
  421. package/src/modules/catalog/lib/omnibusCache.ts +111 -0
  422. package/src/modules/catalog/lib/omnibusConfig.ts +73 -0
  423. package/src/modules/catalog/lib/omnibusHistoryQueries.ts +248 -0
  424. package/src/modules/catalog/lib/omnibusPresentedEntry.ts +76 -0
  425. package/src/modules/catalog/lib/omnibusProductListEnrichment.ts +125 -0
  426. package/src/modules/catalog/lib/omnibusTypes.ts +202 -0
  427. package/src/modules/catalog/lib/optionSchemaTranslations.ts +55 -0
  428. package/src/modules/catalog/lib/priceHistoryQuery.ts +155 -0
  429. package/src/modules/catalog/lib/pricing.ts +42 -4
  430. package/src/modules/catalog/lib/productFilters.ts +349 -0
  431. package/src/modules/catalog/lib/productScopeKeys.ts +162 -0
  432. package/src/modules/catalog/lib/productScopeReindex.ts +139 -0
  433. package/src/modules/catalog/migrations/.snapshot-open-mercato.json +572 -0
  434. package/src/modules/catalog/migrations/Migration20261005105352_catalog.ts +39 -0
  435. package/src/modules/catalog/migrations/Migration20261005150824_catalog.ts +39 -0
  436. package/src/modules/catalog/services/catalogOmnibusService.ts +642 -0
  437. package/src/modules/catalog/setup.ts +2 -0
  438. package/src/modules/catalog/subscribers/product-scope-keys-category-deleted.ts +18 -0
  439. package/src/modules/catalog/subscribers/product-scope-keys-category-updated.ts +19 -0
  440. package/src/modules/catalog/translations.ts +12 -0
  441. package/src/modules/configs/i18n/de.json +5 -1
  442. package/src/modules/configs/i18n/en.json +5 -1
  443. package/src/modules/configs/i18n/es.json +5 -1
  444. package/src/modules/configs/i18n/ko.json +5 -1
  445. package/src/modules/configs/i18n/pl.json +5 -1
  446. package/src/modules/configs/lib/upgrade-actions.ts +35 -0
  447. package/src/modules/customer_groups/api/customer-groups/[id]/terms/route.ts +7 -0
  448. package/src/modules/customer_groups/components/CustomerGroupTermsSection.tsx +124 -2
  449. package/src/modules/customer_groups/components/customerGroupAssortmentScope.ts +46 -0
  450. package/src/modules/customer_groups/data/entities.ts +4 -0
  451. package/src/modules/customer_groups/data/validators.ts +14 -0
  452. package/src/modules/customer_groups/i18n/de.json +12 -0
  453. package/src/modules/customer_groups/i18n/en.json +12 -0
  454. package/src/modules/customer_groups/i18n/es.json +12 -0
  455. package/src/modules/customer_groups/i18n/ko.json +12 -0
  456. package/src/modules/customer_groups/i18n/pl.json +12 -0
  457. package/src/modules/customer_groups/migrations/.snapshot-open-mercato.json +17 -0
  458. package/src/modules/customer_groups/migrations/Migration20261005110250_customer_groups.ts +15 -0
  459. package/src/modules/customer_groups/services/customerGroupsService.ts +74 -23
  460. package/src/modules/ecommerce/acl.ts +9 -0
  461. package/src/modules/ecommerce/api/domain-mappings/route.ts +76 -0
  462. package/src/modules/ecommerce/api/openapi.ts +28 -0
  463. package/src/modules/ecommerce/api/store-channel-bindings/[id]/assortment-count/route.ts +141 -0
  464. package/src/modules/ecommerce/api/store-channel-bindings/crud.ts +283 -0
  465. package/src/modules/ecommerce/api/store-channel-bindings/route.ts +67 -0
  466. package/src/modules/ecommerce/api/store-domain-bindings/crud.ts +324 -0
  467. package/src/modules/ecommerce/api/store-domain-bindings/route.ts +73 -0
  468. package/src/modules/ecommerce/api/storefront/categories/[slug]/route.ts +94 -0
  469. package/src/modules/ecommerce/api/storefront/categories/openapiSchemas.ts +63 -0
  470. package/src/modules/ecommerce/api/storefront/categories/route.ts +78 -0
  471. package/src/modules/ecommerce/api/storefront/context/route.ts +203 -0
  472. package/src/modules/ecommerce/api/storefront/products/[idOrHandle]/route.ts +94 -0
  473. package/src/modules/ecommerce/api/storefront/products/openapiSchemas.ts +193 -0
  474. package/src/modules/ecommerce/api/storefront/products/route.ts +81 -0
  475. package/src/modules/ecommerce/api/storefront/search/suggest/openapiSchemas.ts +25 -0
  476. package/src/modules/ecommerce/api/storefront/search/suggest/route.ts +78 -0
  477. package/src/modules/ecommerce/api/storefront/storefrontRouteSupport.ts +68 -0
  478. package/src/modules/ecommerce/api/stores/[id]/branding/route.ts +205 -0
  479. package/src/modules/ecommerce/api/stores/[id]/preview-branding/route.ts +99 -0
  480. package/src/modules/ecommerce/api/stores/crud.ts +457 -0
  481. package/src/modules/ecommerce/api/stores/route.ts +66 -0
  482. package/src/modules/ecommerce/backend/config/ecommerce/[id]/page.meta.ts +12 -0
  483. package/src/modules/ecommerce/backend/config/ecommerce/[id]/page.tsx +5 -0
  484. package/src/modules/ecommerce/backend/config/ecommerce/page.meta.ts +12 -0
  485. package/src/modules/ecommerce/backend/config/ecommerce/page.tsx +5 -0
  486. package/src/modules/ecommerce/commands/stores.ts +154 -0
  487. package/src/modules/ecommerce/components/ChannelAssortmentCount.tsx +138 -0
  488. package/src/modules/ecommerce/components/ChannelBindingFormSections.tsx +40 -0
  489. package/src/modules/ecommerce/components/ChannelLookupPicker.tsx +5 -0
  490. package/src/modules/ecommerce/components/DomainStatusWarning.tsx +32 -0
  491. package/src/modules/ecommerce/components/StoreAvailabilityDefaultsSection.tsx +219 -0
  492. package/src/modules/ecommerce/components/StoreBrandingControls.tsx +73 -0
  493. package/src/modules/ecommerce/components/StoreBrandingPreview.tsx +77 -0
  494. package/src/modules/ecommerce/components/StoreBrandingTab.tsx +242 -0
  495. package/src/modules/ecommerce/components/StoreChannelBindingDialog.tsx +306 -0
  496. package/src/modules/ecommerce/components/StoreChannelsTab.tsx +375 -0
  497. package/src/modules/ecommerce/components/StoreCreateDialog.tsx +204 -0
  498. package/src/modules/ecommerce/components/StoreDomainBindingDialog.tsx +198 -0
  499. package/src/modules/ecommerce/components/StoreDomainsTab.tsx +426 -0
  500. package/src/modules/ecommerce/components/StoreEditPage.tsx +193 -0
  501. package/src/modules/ecommerce/components/StoreGeneralTab.tsx +189 -0
  502. package/src/modules/ecommerce/components/StoreListPage.tsx +326 -0
  503. package/src/modules/ecommerce/components/StoreSeoTab.tsx +152 -0
  504. package/src/modules/ecommerce/components/StoreStatusBadge.tsx +22 -0
  505. package/src/modules/ecommerce/components/channelLookups.ts +24 -0
  506. package/src/modules/ecommerce/components/storeAdmin.ts +56 -0
  507. package/src/modules/ecommerce/components/storeBrandingForm.ts +230 -0
  508. package/src/modules/ecommerce/components/storeChannels.ts +210 -0
  509. package/src/modules/ecommerce/components/storeDomains.ts +221 -0
  510. package/src/modules/ecommerce/components/storeEditTabs.tsx +76 -0
  511. package/src/modules/ecommerce/components/storeGeneral.ts +155 -0
  512. package/src/modules/ecommerce/components/storeSeo.ts +78 -0
  513. package/src/modules/ecommerce/components/useChannelAssortmentCount.ts +67 -0
  514. package/src/modules/ecommerce/components/useStoreAccess.ts +36 -0
  515. package/src/modules/ecommerce/data/enrichers.ts +213 -0
  516. package/src/modules/ecommerce/data/entities.ts +168 -0
  517. package/src/modules/ecommerce/data/validators.ts +587 -0
  518. package/src/modules/ecommerce/di.ts +9 -0
  519. package/src/modules/ecommerce/events.ts +27 -0
  520. package/src/modules/ecommerce/i18n/de.json +341 -0
  521. package/src/modules/ecommerce/i18n/en.json +341 -0
  522. package/src/modules/ecommerce/i18n/es.json +341 -0
  523. package/src/modules/ecommerce/i18n/ko.json +341 -0
  524. package/src/modules/ecommerce/i18n/pl.json +341 -0
  525. package/src/modules/ecommerce/index.ts +14 -0
  526. package/src/modules/ecommerce/lib/assortmentCount.ts +175 -0
  527. package/src/modules/ecommerce/lib/brandingStyles.ts +215 -0
  528. package/src/modules/ecommerce/lib/buyerContext.ts +401 -0
  529. package/src/modules/ecommerce/lib/cacheInvalidation.ts +302 -0
  530. package/src/modules/ecommerce/lib/cacheKeys.ts +340 -0
  531. package/src/modules/ecommerce/lib/createConflictRecheck.ts +31 -0
  532. package/src/modules/ecommerce/lib/crudEvents.ts +128 -0
  533. package/src/modules/ecommerce/lib/crudSupport.ts +85 -0
  534. package/src/modules/ecommerce/lib/domainBindingRebind.ts +138 -0
  535. package/src/modules/ecommerce/lib/domainMappingSummaries.ts +71 -0
  536. package/src/modules/ecommerce/lib/exclusiveFlag.ts +90 -0
  537. package/src/modules/ecommerce/lib/references.ts +93 -0
  538. package/src/modules/ecommerce/lib/seedDraftStore.ts +125 -0
  539. package/src/modules/ecommerce/lib/storeBindingCascade.ts +138 -0
  540. package/src/modules/ecommerce/lib/storeBranding.ts +48 -0
  541. package/src/modules/ecommerce/lib/storeBrandingRoute.ts +91 -0
  542. package/src/modules/ecommerce/lib/storeContext.ts +669 -0
  543. package/src/modules/ecommerce/lib/storeContextService.ts +71 -0
  544. package/src/modules/ecommerce/lib/storeLocale.ts +88 -0
  545. package/src/modules/ecommerce/lib/storeNotifications.ts +55 -0
  546. package/src/modules/ecommerce/lib/storefrontCatalogSupport.ts +427 -0
  547. package/src/modules/ecommerce/lib/storefrontCategories.ts +419 -0
  548. package/src/modules/ecommerce/lib/storefrontCategoryCache.ts +113 -0
  549. package/src/modules/ecommerce/lib/storefrontDetail.ts +654 -0
  550. package/src/modules/ecommerce/lib/storefrontFacets.ts +697 -0
  551. package/src/modules/ecommerce/lib/storefrontOptionFilter.ts +44 -0
  552. package/src/modules/ecommerce/lib/storefrontPricing.ts +583 -0
  553. package/src/modules/ecommerce/lib/storefrontProductCache.ts +126 -0
  554. package/src/modules/ecommerce/lib/storefrontProductScope.ts +181 -0
  555. package/src/modules/ecommerce/lib/storefrontProducts.ts +963 -0
  556. package/src/modules/ecommerce/lib/storefrontQuery.ts +215 -0
  557. package/src/modules/ecommerce/lib/storefrontRateLimit.ts +126 -0
  558. package/src/modules/ecommerce/lib/storefrontSearch.ts +454 -0
  559. package/src/modules/ecommerce/lib/storefrontSearchCache.ts +49 -0
  560. package/src/modules/ecommerce/lib/subscriberSupport.ts +41 -0
  561. package/src/modules/ecommerce/lib/types.ts +69 -0
  562. package/src/modules/ecommerce/migrations/.snapshot-open-mercato.json +829 -0
  563. package/src/modules/ecommerce/migrations/Migration20261005113233_ecommerce.ts +32 -0
  564. package/src/modules/ecommerce/migrations/Migration20261006112733_ecommerce.ts +15 -0
  565. package/src/modules/ecommerce/notifications.client.ts +46 -0
  566. package/src/modules/ecommerce/notifications.ts +46 -0
  567. package/src/modules/ecommerce/search.ts +93 -0
  568. package/src/modules/ecommerce/setup.ts +15 -0
  569. package/src/modules/ecommerce/subscribers/assortment-empty-notifications.ts +12 -0
  570. package/src/modules/ecommerce/subscribers/availability-policy-cache-invalidation.ts +12 -0
  571. package/src/modules/ecommerce/subscribers/catalog-category-cache-invalidation.ts +12 -0
  572. package/src/modules/ecommerce/subscribers/catalog-price-cache-invalidation.ts +12 -0
  573. package/src/modules/ecommerce/subscribers/catalog-product-cache-invalidation.ts +12 -0
  574. package/src/modules/ecommerce/subscribers/catalog-variant-cache-invalidation.ts +12 -0
  575. package/src/modules/ecommerce/subscribers/customer-group-cache-invalidation.ts +12 -0
  576. package/src/modules/ecommerce/subscribers/customer-group-membership-cache-invalidation.ts +12 -0
  577. package/src/modules/ecommerce/subscribers/customer-group-terms-cache-invalidation.ts +12 -0
  578. package/src/modules/ecommerce/subscribers/customer-user-deleted-cache-invalidation.ts +12 -0
  579. package/src/modules/ecommerce/subscribers/customer-user-updated-cache-invalidation.ts +12 -0
  580. package/src/modules/ecommerce/subscribers/domain-binding-rebind.ts +14 -0
  581. package/src/modules/ecommerce/subscribers/domain-mapping-cache-invalidation.ts +12 -0
  582. package/src/modules/ecommerce/subscribers/store-cache-invalidation.ts +12 -0
  583. package/src/modules/ecommerce/subscribers/store-channel-binding-cache-invalidation.ts +12 -0
  584. package/src/modules/ecommerce/subscribers/store-domain-binding-cache-invalidation.ts +12 -0
  585. package/src/modules/ecommerce/subscribers/store-notifications.ts +12 -0
  586. package/src/modules/query_index/lib/batch.ts +25 -9
  587. package/src/modules/query_index/lib/doc-enrichers.ts +306 -0
  588. package/src/modules/query_index/lib/document.ts +2 -0
  589. package/src/modules/query_index/lib/engine.ts +58 -1
  590. package/src/modules/query_index/lib/indexer.ts +8 -0
  591. package/src/modules/query_index/lib/search-tokens.ts +2 -0
  592. package/src/modules/query_index/migrations/Migration20261005201500_query_index.ts +42 -0
  593. package/src/modules/translations/components/TranslationManager.tsx +3 -3
  594. package/src/modules/translations/data/validators.ts +1 -1
  595. package/src/modules/translations/lib/batch.ts +47 -0
  596. package/src/modules/translations/lib/helpers.ts +4 -0
  597. package/src/modules/translations/lib/resolve-field-list.ts +16 -2
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/ecommerce/api/store-channel-bindings/route.ts"],
4
+ "sourcesContent": ["import { z } from 'zod'\nimport { channelBindingCrud, channelBindingListQuerySchema, channelBindingRouteMetadata } from './crud'\nimport {\n ecommerceStoreChannelBindingCreateSchema,\n ecommerceStoreChannelBindingUpdateSchema,\n} from '../../data/validators'\nimport { createEcommerceCrudOpenApi, createPagedListResponseSchema, defaultOkResponseSchema } from '../openapi'\n\nexport const metadata = channelBindingRouteMetadata\n\nexport const GET = channelBindingCrud.GET\nexport const POST = channelBindingCrud.POST\nexport const PUT = channelBindingCrud.PUT\nexport const DELETE = channelBindingCrud.DELETE\n\nconst assortmentScopeSchema = z\n .object({\n categoryIds: z.array(z.string().uuid()).optional(),\n tagIds: z.array(z.string().uuid()).optional(),\n excludeProductIds: z.array(z.string().uuid()).optional(),\n excludeCategoryIds: z.array(z.string().uuid()).optional(),\n excludeTagIds: z.array(z.string().uuid()).optional(),\n })\n .nullable()\n\nexport const channelBindingListItemSchema = z.object({\n id: z.string().uuid(),\n organizationId: z.string().uuid().nullable(),\n tenantId: z.string().uuid().nullable(),\n storeId: z.string().uuid(),\n salesChannelId: z.string().uuid(),\n priceKindId: z.string().uuid().nullable(),\n assortmentScope: assortmentScopeSchema,\n priceSortFallback: z.enum(['approximate', 'unavailable']),\n isDefault: z.boolean(),\n requireAuthentication: z.boolean().describe('When true, anonymous buyers resolve to an empty assortment on this channel binding.'),\n createdAt: z.string().nullable(),\n updatedAt: z.string().nullable(),\n})\n\nexport const openApi = createEcommerceCrudOpenApi({\n resourceName: 'StoreChannelBinding',\n pluralName: 'StoreChannelBindings',\n querySchema: channelBindingListQuerySchema,\n listResponseSchema: createPagedListResponseSchema(channelBindingListItemSchema),\n create: {\n schema: ecommerceStoreChannelBindingCreateSchema,\n responseSchema: z.object({\n id: z.string().uuid().nullable(),\n isDefault: z\n .boolean()\n .describe('Whether the created binding is the default binding of its store. False when a concurrent request won the promotion.'),\n }),\n description:\n 'Binds a sales channel of the same organization to a store, with an optional price kind, assortment scope and require-authentication gate. Requesting isDefault clears the previous default binding of the store.',\n },\n update: {\n schema: ecommerceStoreChannelBindingUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Updates a store channel binding by id (id in the body). Setting isDefault clears the previous default binding of the store.',\n },\n del: {\n schema: z.object({ id: z.string().uuid() }),\n responseSchema: defaultOkResponseSchema,\n description: 'Soft-deletes a store channel binding by id (?id= query parameter).',\n },\n})\n"],
5
+ "mappings": "AAAA,SAAS,SAAS;AAClB,SAAS,oBAAoB,+BAA+B,mCAAmC;AAC/F;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,4BAA4B,+BAA+B,+BAA+B;AAE5F,MAAM,WAAW;AAEjB,MAAM,MAAM,mBAAmB;AAC/B,MAAM,OAAO,mBAAmB;AAChC,MAAM,MAAM,mBAAmB;AAC/B,MAAM,SAAS,mBAAmB;AAEzC,MAAM,wBAAwB,EAC3B,OAAO;AAAA,EACN,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,SAAS;AAAA,EACjD,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,SAAS;AAAA,EAC5C,mBAAmB,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,SAAS;AAAA,EACvD,oBAAoB,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,SAAS;AAAA,EACxD,eAAe,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,SAAS;AACrD,CAAC,EACA,SAAS;AAEL,MAAM,+BAA+B,EAAE,OAAO;AAAA,EACnD,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,EACpB,gBAAgB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EAC3C,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EACrC,SAAS,EAAE,OAAO,EAAE,KAAK;AAAA,EACzB,gBAAgB,EAAE,OAAO,EAAE,KAAK;AAAA,EAChC,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EACxC,iBAAiB;AAAA,EACjB,mBAAmB,EAAE,KAAK,CAAC,eAAe,aAAa,CAAC;AAAA,EACxD,WAAW,EAAE,QAAQ;AAAA,EACrB,uBAAuB,EAAE,QAAQ,EAAE,SAAS,qFAAqF;AAAA,EACjI,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,WAAW,EAAE,OAAO,EAAE,SAAS;AACjC,CAAC;AAEM,MAAM,UAAU,2BAA2B;AAAA,EAChD,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,oBAAoB,8BAA8B,4BAA4B;AAAA,EAC9E,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,gBAAgB,EAAE,OAAO;AAAA,MACvB,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,MAC/B,WAAW,EACR,QAAQ,EACR,SAAS,qHAAqH;AAAA,IACnI,CAAC;AAAA,IACD,aACE;AAAA,EACJ;AAAA,EACA,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,aAAa;AAAA,EACf;AAAA,EACA,KAAK;AAAA,IACH,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAAA,IAC1C,gBAAgB;AAAA,IAChB,aAAa;AAAA,EACf;AACF,CAAC;",
6
+ "names": []
7
+ }
@@ -0,0 +1,296 @@
1
+ import { z } from "zod";
2
+ import { makeCrudRoute } from "@open-mercato/shared/lib/crud/factory";
3
+ import { isUniqueViolation } from "@open-mercato/shared/lib/crud/errors";
4
+ import { parseBooleanToken } from "@open-mercato/shared/lib/boolean";
5
+ import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
6
+ import { E } from "../../../../generated/entities.ids.generated.js";
7
+ import * as F from "../../../../generated/entities/ecommerce_store_domain_binding/index.js";
8
+ import { EcommerceStoreDomainBinding } from "../../data/entities.js";
9
+ import {
10
+ ecommerceStoreDomainBindingCreateSchema,
11
+ ecommerceStoreDomainBindingUpdateSchema
12
+ } from "../../data/validators.js";
13
+ import {
14
+ assertRecordInWriteScope,
15
+ fieldError,
16
+ hasOwn,
17
+ resolveScopeOrganizationId,
18
+ resolveWriteScope
19
+ } from "../../lib/crudSupport.js";
20
+ import { clearCreateConflictRecheck, registerCreateConflictRecheck } from "../../lib/createConflictRecheck.js";
21
+ import { assignExclusiveFlag, promoteExclusiveFlag } from "../../lib/exclusiveFlag.js";
22
+ import { assertDomainMappingInScope, assertStoreInScope } from "../../lib/references.js";
23
+ import {
24
+ announceStoreDomainBindingsUpdated,
25
+ buildStoreDomainBindingEventPayload,
26
+ ECOMMERCE_EVENTS_MODULE,
27
+ rememberDomainBindingPlacement,
28
+ STORE_DOMAIN_BINDING_EVENT_ENTITY
29
+ } from "../../lib/crudEvents.js";
30
+ const rawBodySchema = z.object({}).passthrough();
31
+ const domainBindingListQuerySchema = z.object({
32
+ page: z.coerce.number().min(1).default(1),
33
+ pageSize: z.coerce.number().min(1).max(100).default(50),
34
+ id: z.string().uuid().optional(),
35
+ storeId: z.string().uuid().optional(),
36
+ domainMappingId: z.string().uuid().optional(),
37
+ isPrimary: z.string().optional(),
38
+ sortField: z.string().optional(),
39
+ sortDir: z.enum(["asc", "desc"]).optional()
40
+ }).passthrough();
41
+ const domainBindingRouteMetadata = {
42
+ GET: { requireAuth: true, requireFeatures: ["ecommerce.stores.view"] },
43
+ POST: { requireAuth: true, requireFeatures: ["ecommerce.domains.manage"] },
44
+ PUT: { requireAuth: true, requireFeatures: ["ecommerce.domains.manage"] },
45
+ DELETE: { requireAuth: true, requireFeatures: ["ecommerce.domains.manage"] }
46
+ };
47
+ const DOMAIN_BINDING_PREFIX_UNIQUE_CONSTRAINT = "ecommerce_store_domain_bindings_mapping_prefix_unique";
48
+ const DOMAIN_BINDING_PRIMARY_UNIQUE_CONSTRAINT = "ecommerce_store_domain_bindings_store_primary_unique";
49
+ const domainBindingPrimaryFlag = {
50
+ entity: EcommerceStoreDomainBinding,
51
+ flag: "isPrimary",
52
+ constraint: DOMAIN_BINDING_PRIMARY_UNIQUE_CONSTRAINT
53
+ };
54
+ function duplicateBindingConflict(translate) {
55
+ return fieldError(409, {
56
+ pathPrefix: translate(
57
+ "ecommerce.errors.domainBindingDuplicate",
58
+ "This domain and path prefix already serve a store."
59
+ )
60
+ });
61
+ }
62
+ function primaryBindingConflict(translate) {
63
+ return fieldError(409, {
64
+ isPrimary: translate(
65
+ "ecommerce.errors.primaryDomainConflict",
66
+ "Another domain binding of this store was made primary at the same time. Reload and try again."
67
+ )
68
+ });
69
+ }
70
+ function toDomainBindingUniqueConflict(err, translate) {
71
+ if (isUniqueViolation(err, DOMAIN_BINDING_PREFIX_UNIQUE_CONSTRAINT)) return duplicateBindingConflict(translate);
72
+ if (isUniqueViolation(err, DOMAIN_BINDING_PRIMARY_UNIQUE_CONSTRAINT)) return primaryBindingConflict(translate);
73
+ return err;
74
+ }
75
+ async function assertDomainBindingAvailable(em, placement, translate) {
76
+ const where = {
77
+ tenantId: placement.tenantId,
78
+ domainMappingId: placement.domainMappingId,
79
+ pathPrefix: placement.pathPrefix,
80
+ deletedAt: null
81
+ };
82
+ if (placement.bindingId) where.id = { $ne: placement.bindingId };
83
+ if (await em.count(EcommerceStoreDomainBinding, where) > 0) {
84
+ throw duplicateBindingConflict(translate);
85
+ }
86
+ }
87
+ function parseCreateInput(input, ctx) {
88
+ return ecommerceStoreDomainBindingCreateSchema.parse({ ...input, ...resolveWriteScope(ctx) });
89
+ }
90
+ function parseUpdateInput(input, ctx) {
91
+ return ecommerceStoreDomainBindingUpdateSchema.parse({ ...input, ...resolveWriteScope(ctx) });
92
+ }
93
+ function bindingScope(binding) {
94
+ return { tenantId: binding.tenantId, organizationId: binding.organizationId };
95
+ }
96
+ const clearedPrimaryByUpdate = /* @__PURE__ */ new WeakMap();
97
+ function toDomainBindingListItem(row) {
98
+ return {
99
+ id: row[F.id],
100
+ organizationId: row[F.organization_id] ?? null,
101
+ tenantId: row[F.tenant_id] ?? null,
102
+ storeId: row[F.store_id],
103
+ domainMappingId: row[F.domain_mapping_id],
104
+ pathPrefix: row[F.path_prefix] ?? null,
105
+ isPrimary: row[F.is_primary] === true,
106
+ createdAt: row[F.created_at] ?? null,
107
+ updatedAt: row[F.updated_at] ?? null
108
+ };
109
+ }
110
+ const NOT_FOUND = { key: "ecommerce.errors.domainBindingNotFound", fallback: "The selected domain binding does not exist in this organization." };
111
+ const domainBindingCrud = makeCrudRoute({
112
+ metadata: domainBindingRouteMetadata,
113
+ orm: {
114
+ entity: EcommerceStoreDomainBinding,
115
+ idField: "id",
116
+ orgField: "organizationId",
117
+ tenantField: "tenantId",
118
+ softDeleteField: "deletedAt"
119
+ },
120
+ indexer: { entityType: E.ecommerce.ecommerce_store_domain_binding },
121
+ enrichers: { entityId: E.ecommerce.ecommerce_store_domain_binding },
122
+ list: {
123
+ schema: domainBindingListQuerySchema,
124
+ entityId: E.ecommerce.ecommerce_store_domain_binding,
125
+ fields: [
126
+ F.id,
127
+ F.organization_id,
128
+ F.tenant_id,
129
+ F.store_id,
130
+ F.domain_mapping_id,
131
+ F.path_prefix,
132
+ F.is_primary,
133
+ F.created_at,
134
+ F.updated_at
135
+ ],
136
+ sortFieldMap: {
137
+ pathPrefix: F.path_prefix,
138
+ isPrimary: F.is_primary,
139
+ createdAt: F.created_at,
140
+ updatedAt: F.updated_at
141
+ },
142
+ defaultSort: { field: "createdAt", dir: "asc" },
143
+ tiebreakSortField: F.id,
144
+ buildFilters: async (query, ctx) => {
145
+ const filters = {};
146
+ const scopeOrganizationId = resolveScopeOrganizationId(ctx);
147
+ if (scopeOrganizationId) filters[F.organization_id] = { $eq: scopeOrganizationId };
148
+ if (query.id) filters[F.id] = { $eq: query.id };
149
+ if (query.storeId) filters[F.store_id] = { $eq: query.storeId };
150
+ if (query.domainMappingId) filters[F.domain_mapping_id] = { $eq: query.domainMappingId };
151
+ const isPrimary = parseBooleanToken(query.isPrimary);
152
+ if (isPrimary !== null) filters[F.is_primary] = { $eq: isPrimary };
153
+ return filters;
154
+ },
155
+ transformItem: (item) => toDomainBindingListItem(item)
156
+ },
157
+ events: {
158
+ module: ECOMMERCE_EVENTS_MODULE,
159
+ entity: STORE_DOMAIN_BINDING_EVENT_ENTITY,
160
+ persistent: true,
161
+ buildPayload: (emitCtx) => buildStoreDomainBindingEventPayload(emitCtx.entity)
162
+ },
163
+ create: {
164
+ schema: rawBodySchema,
165
+ mapToEntity: (input, ctx) => {
166
+ const parsed = parseCreateInput(input, ctx);
167
+ return {
168
+ organizationId: parsed.organizationId,
169
+ tenantId: parsed.tenantId,
170
+ storeId: parsed.storeId,
171
+ domainMappingId: parsed.domainMappingId,
172
+ pathPrefix: parsed.pathPrefix ?? null,
173
+ isPrimary: false
174
+ };
175
+ },
176
+ response: (entity) => {
177
+ const binding = entity;
178
+ return { id: binding.id, isPrimary: binding.isPrimary };
179
+ }
180
+ },
181
+ update: {
182
+ schema: rawBodySchema,
183
+ getId: (input) => typeof input.id === "string" ? input.id : "",
184
+ applyToEntity: async (entity, input, ctx) => {
185
+ const binding = entity;
186
+ rememberDomainBindingPlacement(binding);
187
+ const parsed = parseUpdateInput(input, ctx);
188
+ const em = ctx.container.resolve("em");
189
+ const { translate } = await resolveTranslations();
190
+ const scope = bindingScope(binding);
191
+ const nextStoreId = parsed.storeId ?? binding.storeId;
192
+ const nextDomainMappingId = parsed.domainMappingId ?? binding.domainMappingId;
193
+ const nextPathPrefix = hasOwn(parsed, "pathPrefix") ? parsed.pathPrefix ?? null : binding.pathPrefix ?? null;
194
+ const storeChanged = nextStoreId !== binding.storeId;
195
+ if (storeChanged) await assertStoreInScope(em, nextStoreId, scope, translate);
196
+ if (nextDomainMappingId !== binding.domainMappingId) {
197
+ await assertDomainMappingInScope(ctx.container, nextDomainMappingId, scope, translate);
198
+ }
199
+ if (nextDomainMappingId !== binding.domainMappingId || nextPathPrefix !== (binding.pathPrefix ?? null)) {
200
+ await assertDomainBindingAvailable(
201
+ em,
202
+ { tenantId: binding.tenantId, bindingId: binding.id, domainMappingId: nextDomainMappingId, pathPrefix: nextPathPrefix },
203
+ translate
204
+ );
205
+ }
206
+ const nextPrimary = parsed.isPrimary ?? binding.isPrimary;
207
+ if (nextPrimary && (!binding.isPrimary || storeChanged)) {
208
+ clearedPrimaryByUpdate.set(
209
+ binding,
210
+ await assignExclusiveFlag(
211
+ em,
212
+ domainBindingPrimaryFlag,
213
+ { ...scope, storeId: nextStoreId },
214
+ binding.id,
215
+ () => primaryBindingConflict(translate)
216
+ )
217
+ );
218
+ }
219
+ binding.storeId = nextStoreId;
220
+ binding.domainMappingId = nextDomainMappingId;
221
+ binding.pathPrefix = nextPathPrefix;
222
+ binding.isPrimary = nextPrimary;
223
+ try {
224
+ await em.flush();
225
+ } catch (err) {
226
+ throw toDomainBindingUniqueConflict(err, translate);
227
+ }
228
+ },
229
+ response: () => ({ ok: true })
230
+ },
231
+ del: { idFrom: "query", softDelete: true, response: () => ({ ok: true }) },
232
+ hooks: {
233
+ beforeUpdate: async (input, ctx) => {
234
+ await assertRecordInWriteScope(ctx, EcommerceStoreDomainBinding, input.id, NOT_FOUND);
235
+ },
236
+ beforeDelete: async (id, ctx) => {
237
+ await assertRecordInWriteScope(ctx, EcommerceStoreDomainBinding, id, NOT_FOUND);
238
+ },
239
+ beforeCreate: async (input, ctx) => {
240
+ const result = ecommerceStoreDomainBindingCreateSchema.safeParse({ ...input, ...resolveWriteScope(ctx) });
241
+ if (!result.success) return;
242
+ const { translate } = await resolveTranslations();
243
+ const em = ctx.container.resolve("em").fork();
244
+ const scope = { tenantId: result.data.tenantId, organizationId: result.data.organizationId };
245
+ await assertStoreInScope(em, result.data.storeId, scope, translate);
246
+ await assertDomainMappingInScope(ctx.container, result.data.domainMappingId, scope, translate);
247
+ const placement = {
248
+ tenantId: scope.tenantId,
249
+ bindingId: null,
250
+ domainMappingId: result.data.domainMappingId,
251
+ pathPrefix: result.data.pathPrefix ?? null
252
+ };
253
+ await assertDomainBindingAvailable(em, placement, translate);
254
+ registerCreateConflictRecheck(
255
+ ctx.request,
256
+ () => assertDomainBindingAvailable(ctx.container.resolve("em").fork(), placement, translate)
257
+ );
258
+ },
259
+ afterCreate: async (entity, ctx) => {
260
+ clearCreateConflictRecheck(ctx.request);
261
+ const binding = entity;
262
+ if (parseCreateInput(ctx.input, ctx).isPrimary !== true) return;
263
+ const now = /* @__PURE__ */ new Date();
264
+ const em = ctx.container.resolve("em");
265
+ const { promoted, cleared } = await promoteExclusiveFlag(
266
+ em,
267
+ domainBindingPrimaryFlag,
268
+ { ...bindingScope(binding), storeId: binding.storeId },
269
+ binding.id,
270
+ now
271
+ );
272
+ if (!promoted) return;
273
+ binding.isPrimary = true;
274
+ binding.updatedAt = now;
275
+ await announceStoreDomainBindingsUpdated(ctx.container, cleared);
276
+ },
277
+ afterUpdate: async (entity, ctx) => {
278
+ const binding = entity;
279
+ const cleared = clearedPrimaryByUpdate.get(binding);
280
+ if (!cleared) return;
281
+ clearedPrimaryByUpdate.delete(binding);
282
+ await announceStoreDomainBindingsUpdated(ctx.container, cleared);
283
+ }
284
+ }
285
+ });
286
+ export {
287
+ DOMAIN_BINDING_PREFIX_UNIQUE_CONSTRAINT,
288
+ DOMAIN_BINDING_PRIMARY_UNIQUE_CONSTRAINT,
289
+ assertDomainBindingAvailable,
290
+ domainBindingCrud,
291
+ domainBindingListQuerySchema,
292
+ domainBindingRouteMetadata,
293
+ toDomainBindingListItem,
294
+ toDomainBindingUniqueConflict
295
+ };
296
+ //# sourceMappingURL=crud.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/ecommerce/api/store-domain-bindings/crud.ts"],
4
+ "sourcesContent": ["import { z } from 'zod'\nimport type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport type { CrudCtx } from '@open-mercato/shared/lib/crud/factory'\nimport { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { isUniqueViolation } from '@open-mercato/shared/lib/crud/errors'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { E } from '#generated/entities.ids.generated'\nimport * as F from '#generated/entities/ecommerce_store_domain_binding'\nimport { EcommerceStoreDomainBinding } from '../../data/entities'\nimport {\n ecommerceStoreDomainBindingCreateSchema,\n ecommerceStoreDomainBindingUpdateSchema,\n type EcommerceStoreDomainBindingCreateInput,\n type EcommerceStoreDomainBindingUpdateInput,\n} from '../../data/validators'\nimport {\n assertRecordInWriteScope,\n fieldError,\n hasOwn,\n resolveScopeOrganizationId,\n resolveWriteScope,\n type EcommerceWriteScope,\n type Translate,\n} from '../../lib/crudSupport'\nimport { clearCreateConflictRecheck, registerCreateConflictRecheck } from '../../lib/createConflictRecheck'\nimport { assignExclusiveFlag, promoteExclusiveFlag, type ExclusiveFlagConfig } from '../../lib/exclusiveFlag'\nimport { assertDomainMappingInScope, assertStoreInScope } from '../../lib/references'\nimport {\n announceStoreDomainBindingsUpdated,\n buildStoreDomainBindingEventPayload,\n ECOMMERCE_EVENTS_MODULE,\n rememberDomainBindingPlacement,\n STORE_DOMAIN_BINDING_EVENT_ENTITY,\n} from '../../lib/crudEvents'\n\nconst rawBodySchema = z.object({}).passthrough()\ntype RawDomainBindingInput = z.infer<typeof rawBodySchema>\n\nexport const domainBindingListQuerySchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n id: z.string().uuid().optional(),\n storeId: z.string().uuid().optional(),\n domainMappingId: z.string().uuid().optional(),\n isPrimary: z.string().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n })\n .passthrough()\n\nexport type DomainBindingListQuery = z.infer<typeof domainBindingListQuerySchema>\n\nexport const domainBindingRouteMetadata = {\n GET: { requireAuth: true, requireFeatures: ['ecommerce.stores.view'] },\n POST: { requireAuth: true, requireFeatures: ['ecommerce.domains.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['ecommerce.domains.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['ecommerce.domains.manage'] },\n}\n\nexport const DOMAIN_BINDING_PREFIX_UNIQUE_CONSTRAINT = 'ecommerce_store_domain_bindings_mapping_prefix_unique'\nexport const DOMAIN_BINDING_PRIMARY_UNIQUE_CONSTRAINT = 'ecommerce_store_domain_bindings_store_primary_unique'\n\nconst domainBindingPrimaryFlag: ExclusiveFlagConfig<EcommerceStoreDomainBinding> = {\n entity: EcommerceStoreDomainBinding,\n flag: 'isPrimary',\n constraint: DOMAIN_BINDING_PRIMARY_UNIQUE_CONSTRAINT,\n}\n\nfunction duplicateBindingConflict(translate: Translate) {\n return fieldError(409, {\n pathPrefix: translate(\n 'ecommerce.errors.domainBindingDuplicate',\n 'This domain and path prefix already serve a store.',\n ),\n })\n}\n\nfunction primaryBindingConflict(translate: Translate) {\n return fieldError(409, {\n isPrimary: translate(\n 'ecommerce.errors.primaryDomainConflict',\n 'Another domain binding of this store was made primary at the same time. Reload and try again.',\n ),\n })\n}\n\nexport function toDomainBindingUniqueConflict(err: unknown, translate: Translate): unknown {\n if (isUniqueViolation(err, DOMAIN_BINDING_PREFIX_UNIQUE_CONSTRAINT)) return duplicateBindingConflict(translate)\n if (isUniqueViolation(err, DOMAIN_BINDING_PRIMARY_UNIQUE_CONSTRAINT)) return primaryBindingConflict(translate)\n return err\n}\n\nexport type DomainBindingPlacement = {\n tenantId: string\n bindingId: string | null\n domainMappingId: string\n pathPrefix: string | null\n}\n\nexport async function assertDomainBindingAvailable(\n em: EntityManager,\n placement: DomainBindingPlacement,\n translate: Translate,\n): Promise<void> {\n const where: Record<string, unknown> = {\n tenantId: placement.tenantId,\n domainMappingId: placement.domainMappingId,\n pathPrefix: placement.pathPrefix,\n deletedAt: null,\n }\n if (placement.bindingId) where.id = { $ne: placement.bindingId }\n if ((await em.count(EcommerceStoreDomainBinding, where as FilterQuery<EcommerceStoreDomainBinding>)) > 0) {\n throw duplicateBindingConflict(translate)\n }\n}\n\nfunction parseCreateInput(input: RawDomainBindingInput, ctx: CrudCtx): EcommerceStoreDomainBindingCreateInput {\n return ecommerceStoreDomainBindingCreateSchema.parse({ ...input, ...resolveWriteScope(ctx) })\n}\n\nfunction parseUpdateInput(input: RawDomainBindingInput, ctx: CrudCtx): EcommerceStoreDomainBindingUpdateInput {\n return ecommerceStoreDomainBindingUpdateSchema.parse({ ...input, ...resolveWriteScope(ctx) })\n}\n\nfunction bindingScope(binding: Pick<EcommerceStoreDomainBinding, 'tenantId' | 'organizationId'>): EcommerceWriteScope {\n return { tenantId: binding.tenantId, organizationId: binding.organizationId }\n}\n\nconst clearedPrimaryByUpdate = new WeakMap<EcommerceStoreDomainBinding, EcommerceStoreDomainBinding[]>()\n\ntype DomainBindingListRow = Record<string, unknown>\n\nexport function toDomainBindingListItem(row: DomainBindingListRow): Record<string, unknown> {\n return {\n id: row[F.id],\n organizationId: row[F.organization_id] ?? null,\n tenantId: row[F.tenant_id] ?? null,\n storeId: row[F.store_id],\n domainMappingId: row[F.domain_mapping_id],\n pathPrefix: row[F.path_prefix] ?? null,\n isPrimary: row[F.is_primary] === true,\n createdAt: row[F.created_at] ?? null,\n updatedAt: row[F.updated_at] ?? null,\n }\n}\n\nconst NOT_FOUND = { key: 'ecommerce.errors.domainBindingNotFound', fallback: 'The selected domain binding does not exist in this organization.' }\n\nexport const domainBindingCrud = makeCrudRoute<RawDomainBindingInput, RawDomainBindingInput, DomainBindingListQuery>({\n metadata: domainBindingRouteMetadata,\n orm: {\n entity: EcommerceStoreDomainBinding,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n indexer: { entityType: E.ecommerce.ecommerce_store_domain_binding },\n enrichers: { entityId: E.ecommerce.ecommerce_store_domain_binding },\n list: {\n schema: domainBindingListQuerySchema,\n entityId: E.ecommerce.ecommerce_store_domain_binding,\n fields: [\n F.id,\n F.organization_id,\n F.tenant_id,\n F.store_id,\n F.domain_mapping_id,\n F.path_prefix,\n F.is_primary,\n F.created_at,\n F.updated_at,\n ],\n sortFieldMap: {\n pathPrefix: F.path_prefix,\n isPrimary: F.is_primary,\n createdAt: F.created_at,\n updatedAt: F.updated_at,\n },\n defaultSort: { field: 'createdAt', dir: 'asc' },\n tiebreakSortField: F.id,\n buildFilters: async (query, ctx) => {\n const filters: Record<string, unknown> = {}\n const scopeOrganizationId = resolveScopeOrganizationId(ctx)\n if (scopeOrganizationId) filters[F.organization_id] = { $eq: scopeOrganizationId }\n if (query.id) filters[F.id] = { $eq: query.id }\n if (query.storeId) filters[F.store_id] = { $eq: query.storeId }\n if (query.domainMappingId) filters[F.domain_mapping_id] = { $eq: query.domainMappingId }\n const isPrimary = parseBooleanToken(query.isPrimary)\n if (isPrimary !== null) filters[F.is_primary] = { $eq: isPrimary }\n return filters\n },\n transformItem: (item: DomainBindingListRow) => toDomainBindingListItem(item),\n },\n events: {\n module: ECOMMERCE_EVENTS_MODULE,\n entity: STORE_DOMAIN_BINDING_EVENT_ENTITY,\n persistent: true,\n buildPayload: (emitCtx) => buildStoreDomainBindingEventPayload(emitCtx.entity as EcommerceStoreDomainBinding),\n },\n create: {\n schema: rawBodySchema,\n mapToEntity: (input, ctx) => {\n const parsed = parseCreateInput(input, ctx)\n return {\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n storeId: parsed.storeId,\n domainMappingId: parsed.domainMappingId,\n pathPrefix: parsed.pathPrefix ?? null,\n isPrimary: false,\n }\n },\n response: (entity) => {\n const binding = entity as EcommerceStoreDomainBinding\n return { id: binding.id, isPrimary: binding.isPrimary }\n },\n },\n update: {\n schema: rawBodySchema,\n getId: (input) => (typeof input.id === 'string' ? input.id : ''),\n applyToEntity: async (entity, input, ctx) => {\n const binding = entity as EcommerceStoreDomainBinding\n rememberDomainBindingPlacement(binding)\n const parsed = parseUpdateInput(input, ctx)\n const em = ctx.container.resolve('em') as EntityManager\n const { translate } = await resolveTranslations()\n const scope = bindingScope(binding)\n const nextStoreId = parsed.storeId ?? binding.storeId\n const nextDomainMappingId = parsed.domainMappingId ?? binding.domainMappingId\n const nextPathPrefix = hasOwn(parsed, 'pathPrefix') ? parsed.pathPrefix ?? null : binding.pathPrefix ?? null\n const storeChanged = nextStoreId !== binding.storeId\n if (storeChanged) await assertStoreInScope(em, nextStoreId, scope, translate)\n if (nextDomainMappingId !== binding.domainMappingId) {\n await assertDomainMappingInScope(ctx.container, nextDomainMappingId, scope, translate)\n }\n if (nextDomainMappingId !== binding.domainMappingId || nextPathPrefix !== (binding.pathPrefix ?? null)) {\n await assertDomainBindingAvailable(\n em,\n { tenantId: binding.tenantId, bindingId: binding.id, domainMappingId: nextDomainMappingId, pathPrefix: nextPathPrefix },\n translate,\n )\n }\n const nextPrimary = parsed.isPrimary ?? binding.isPrimary\n if (nextPrimary && (!binding.isPrimary || storeChanged)) {\n clearedPrimaryByUpdate.set(\n binding,\n await assignExclusiveFlag(\n em,\n domainBindingPrimaryFlag,\n { ...scope, storeId: nextStoreId },\n binding.id,\n () => primaryBindingConflict(translate),\n ),\n )\n }\n binding.storeId = nextStoreId\n binding.domainMappingId = nextDomainMappingId\n binding.pathPrefix = nextPathPrefix\n binding.isPrimary = nextPrimary\n try {\n await em.flush()\n } catch (err) {\n throw toDomainBindingUniqueConflict(err, translate)\n }\n },\n response: () => ({ ok: true }),\n },\n del: { idFrom: 'query', softDelete: true, response: () => ({ ok: true }) },\n hooks: {\n beforeUpdate: async (input, ctx) => {\n await assertRecordInWriteScope(ctx, EcommerceStoreDomainBinding, (input as { id?: unknown }).id, NOT_FOUND)\n },\n beforeDelete: async (id, ctx) => {\n await assertRecordInWriteScope(ctx, EcommerceStoreDomainBinding, id, NOT_FOUND)\n },\n beforeCreate: async (input, ctx) => {\n const result = ecommerceStoreDomainBindingCreateSchema.safeParse({ ...input, ...resolveWriteScope(ctx) })\n if (!result.success) return\n const { translate } = await resolveTranslations()\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const scope: EcommerceWriteScope = { tenantId: result.data.tenantId, organizationId: result.data.organizationId }\n await assertStoreInScope(em, result.data.storeId, scope, translate)\n await assertDomainMappingInScope(ctx.container, result.data.domainMappingId, scope, translate)\n const placement: DomainBindingPlacement = {\n tenantId: scope.tenantId,\n bindingId: null,\n domainMappingId: result.data.domainMappingId,\n pathPrefix: result.data.pathPrefix ?? null,\n }\n await assertDomainBindingAvailable(em, placement, translate)\n registerCreateConflictRecheck(ctx.request, () =>\n assertDomainBindingAvailable((ctx.container.resolve('em') as EntityManager).fork(), placement, translate),\n )\n },\n afterCreate: async (entity, ctx) => {\n clearCreateConflictRecheck(ctx.request)\n const binding = entity as EcommerceStoreDomainBinding\n if (parseCreateInput(ctx.input, ctx).isPrimary !== true) return\n const now = new Date()\n const em = ctx.container.resolve('em') as EntityManager\n const { promoted, cleared } = await promoteExclusiveFlag(\n em,\n domainBindingPrimaryFlag,\n { ...bindingScope(binding), storeId: binding.storeId },\n binding.id,\n now,\n )\n if (!promoted) return\n binding.isPrimary = true\n binding.updatedAt = now\n await announceStoreDomainBindingsUpdated(ctx.container, cleared)\n },\n afterUpdate: async (entity, ctx) => {\n const binding = entity as EcommerceStoreDomainBinding\n const cleared = clearedPrimaryByUpdate.get(binding)\n if (!cleared) return\n clearedPrimaryByUpdate.delete(binding)\n await announceStoreDomainBindingsUpdated(ctx.container, cleared)\n },\n },\n})\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,73 @@
1
+ import { z } from "zod";
2
+ import { withCreateConflictRecheck } from "../../lib/createConflictRecheck.js";
3
+ import { domainBindingCrud, domainBindingListQuerySchema, domainBindingRouteMetadata } from "./crud.js";
4
+ import {
5
+ ecommerceStoreDomainBindingCreateSchema,
6
+ ecommerceStoreDomainBindingUpdateSchema
7
+ } from "../../data/validators.js";
8
+ import { createEcommerceCrudOpenApi, createPagedListResponseSchema, defaultOkResponseSchema } from "../openapi.js";
9
+ const metadata = domainBindingRouteMetadata;
10
+ const GET = domainBindingCrud.GET;
11
+ const POST = withCreateConflictRecheck(domainBindingCrud.POST);
12
+ const PUT = domainBindingCrud.PUT;
13
+ const DELETE = domainBindingCrud.DELETE;
14
+ const domainBindingMappingSchema = z.discriminatedUnion("state", [
15
+ z.object({
16
+ state: z.literal("found"),
17
+ hostname: z.string(),
18
+ status: z.string().describe("pending, verified, active, dns_failed or tls_failed. Only active serves."),
19
+ lastDnsCheckAt: z.string().nullable(),
20
+ dnsFailureReason: z.string().nullable(),
21
+ tlsFailureReason: z.string().nullable()
22
+ }),
23
+ z.object({ state: z.literal("removed") }),
24
+ z.object({ state: z.literal("unavailable") })
25
+ ]);
26
+ const domainBindingListItemSchema = z.object({
27
+ id: z.string().uuid(),
28
+ organizationId: z.string().uuid().nullable(),
29
+ tenantId: z.string().uuid().nullable(),
30
+ storeId: z.string().uuid(),
31
+ domainMappingId: z.string().uuid(),
32
+ pathPrefix: z.string().nullable(),
33
+ isPrimary: z.boolean(),
34
+ createdAt: z.string().nullable(),
35
+ updatedAt: z.string().nullable(),
36
+ _domainMapping: domainBindingMappingSchema.optional().describe(
37
+ "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."
38
+ )
39
+ });
40
+ const openApi = createEcommerceCrudOpenApi({
41
+ resourceName: "StoreDomainBinding",
42
+ pluralName: "StoreDomainBindings",
43
+ querySchema: domainBindingListQuerySchema,
44
+ listResponseSchema: createPagedListResponseSchema(domainBindingListItemSchema),
45
+ create: {
46
+ schema: ecommerceStoreDomainBindingCreateSchema,
47
+ responseSchema: z.object({
48
+ id: z.string().uuid().nullable(),
49
+ isPrimary: z.boolean().describe("Whether the created binding is the primary binding of its store. False when a concurrent request won the promotion.")
50
+ }),
51
+ description: "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."
52
+ },
53
+ update: {
54
+ schema: ecommerceStoreDomainBindingUpdateSchema,
55
+ responseSchema: defaultOkResponseSchema,
56
+ description: "Updates a store domain binding by id (id in the body). Setting isPrimary clears the previous primary binding of the store."
57
+ },
58
+ del: {
59
+ schema: z.object({ id: z.string().uuid() }),
60
+ responseSchema: defaultOkResponseSchema,
61
+ description: "Soft-deletes a store domain binding by id (?id= query parameter)."
62
+ }
63
+ });
64
+ export {
65
+ DELETE,
66
+ GET,
67
+ POST,
68
+ PUT,
69
+ domainBindingListItemSchema,
70
+ metadata,
71
+ openApi
72
+ };
73
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/ecommerce/api/store-domain-bindings/route.ts"],
4
+ "sourcesContent": ["import { z } from 'zod'\nimport { withCreateConflictRecheck } from '../../lib/createConflictRecheck'\nimport { domainBindingCrud, domainBindingListQuerySchema, domainBindingRouteMetadata } from './crud'\nimport {\n ecommerceStoreDomainBindingCreateSchema,\n ecommerceStoreDomainBindingUpdateSchema,\n} from '../../data/validators'\nimport { createEcommerceCrudOpenApi, createPagedListResponseSchema, defaultOkResponseSchema } from '../openapi'\n\nexport const metadata = domainBindingRouteMetadata\n\nexport const GET = domainBindingCrud.GET\nexport const POST = withCreateConflictRecheck(domainBindingCrud.POST)\nexport const PUT = domainBindingCrud.PUT\nexport const DELETE = domainBindingCrud.DELETE\n\nconst domainBindingMappingSchema = z.discriminatedUnion('state', [\n z.object({\n state: z.literal('found'),\n hostname: z.string(),\n status: z.string().describe('pending, verified, active, dns_failed or tls_failed. Only active serves.'),\n lastDnsCheckAt: z.string().nullable(),\n dnsFailureReason: z.string().nullable(),\n tlsFailureReason: z.string().nullable(),\n }),\n z.object({ state: z.literal('removed') }),\n z.object({ state: z.literal('unavailable') }),\n])\n\nexport const domainBindingListItemSchema = z.object({\n id: z.string().uuid(),\n organizationId: z.string().uuid().nullable(),\n tenantId: z.string().uuid().nullable(),\n storeId: z.string().uuid(),\n domainMappingId: z.string().uuid(),\n pathPrefix: z.string().nullable(),\n isPrimary: z.boolean(),\n createdAt: z.string().nullable(),\n updatedAt: z.string().nullable(),\n _domainMapping: domainBindingMappingSchema\n .optional()\n .describe(\n '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.',\n ),\n})\n\nexport const openApi = createEcommerceCrudOpenApi({\n resourceName: 'StoreDomainBinding',\n pluralName: 'StoreDomainBindings',\n querySchema: domainBindingListQuerySchema,\n listResponseSchema: createPagedListResponseSchema(domainBindingListItemSchema),\n create: {\n schema: ecommerceStoreDomainBindingCreateSchema,\n responseSchema: z.object({\n id: z.string().uuid().nullable(),\n isPrimary: z\n .boolean()\n .describe('Whether the created binding is the primary binding of its store. False when a concurrent request won the promotion.'),\n }),\n description:\n '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.',\n },\n update: {\n schema: ecommerceStoreDomainBindingUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Updates a store domain binding by id (id in the body). Setting isPrimary clears the previous primary binding of the store.',\n },\n del: {\n schema: z.object({ id: z.string().uuid() }),\n responseSchema: defaultOkResponseSchema,\n description: 'Soft-deletes a store domain binding by id (?id= query parameter).',\n },\n})\n"],
5
+ "mappings": "AAAA,SAAS,SAAS;AAClB,SAAS,iCAAiC;AAC1C,SAAS,mBAAmB,8BAA8B,kCAAkC;AAC5F;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,4BAA4B,+BAA+B,+BAA+B;AAE5F,MAAM,WAAW;AAEjB,MAAM,MAAM,kBAAkB;AAC9B,MAAM,OAAO,0BAA0B,kBAAkB,IAAI;AAC7D,MAAM,MAAM,kBAAkB;AAC9B,MAAM,SAAS,kBAAkB;AAExC,MAAM,6BAA6B,EAAE,mBAAmB,SAAS;AAAA,EAC/D,EAAE,OAAO;AAAA,IACP,OAAO,EAAE,QAAQ,OAAO;AAAA,IACxB,UAAU,EAAE,OAAO;AAAA,IACnB,QAAQ,EAAE,OAAO,EAAE,SAAS,0EAA0E;AAAA,IACtG,gBAAgB,EAAE,OAAO,EAAE,SAAS;AAAA,IACpC,kBAAkB,EAAE,OAAO,EAAE,SAAS;AAAA,IACtC,kBAAkB,EAAE,OAAO,EAAE,SAAS;AAAA,EACxC,CAAC;AAAA,EACD,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,SAAS,EAAE,CAAC;AAAA,EACxC,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,aAAa,EAAE,CAAC;AAC9C,CAAC;AAEM,MAAM,8BAA8B,EAAE,OAAO;AAAA,EAClD,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,EACpB,gBAAgB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EAC3C,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EACrC,SAAS,EAAE,OAAO,EAAE,KAAK;AAAA,EACzB,iBAAiB,EAAE,OAAO,EAAE,KAAK;AAAA,EACjC,YAAY,EAAE,OAAO,EAAE,SAAS;AAAA,EAChC,WAAW,EAAE,QAAQ;AAAA,EACrB,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,gBAAgB,2BACb,SAAS,EACT;AAAA,IACC;AAAA,EACF;AACJ,CAAC;AAEM,MAAM,UAAU,2BAA2B;AAAA,EAChD,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,oBAAoB,8BAA8B,2BAA2B;AAAA,EAC7E,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,gBAAgB,EAAE,OAAO;AAAA,MACvB,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,MAC/B,WAAW,EACR,QAAQ,EACR,SAAS,qHAAqH;AAAA,IACnI,CAAC;AAAA,IACD,aACE;AAAA,EACJ;AAAA,EACA,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,aAAa;AAAA,EACf;AAAA,EACA,KAAK;AAAA,IACH,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAAA,IAC1C,gBAAgB;AAAA,IAChB,aAAa;AAAA,EACf;AACF,CAAC;",
6
+ "names": []
7
+ }
@@ -0,0 +1,87 @@
1
+ import { NextResponse } from "next/server";
2
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
3
+ import { rateLimitErrorSchema } from "@open-mercato/shared/lib/ratelimit/helpers";
4
+ import {
5
+ ecommerceStorefrontCategoryLandingQuerySchema,
6
+ ecommerceStorefrontCategoryParamsSchema
7
+ } from "../../../../data/validators.js";
8
+ import { enforceStorefrontRateLimit } from "../../../../lib/storefrontRateLimit.js";
9
+ import { cachedGetStorefrontCategoryLanding } from "../../../../lib/storefrontCategoryCache.js";
10
+ import { parseStorefrontCategoryLandingQuery } from "../../../../lib/storefrontQuery.js";
11
+ import {
12
+ storefrontErrorResponse,
13
+ storefrontErrorSchema,
14
+ storefrontInvalidQueryErrorSchema,
15
+ storefrontRouteErrorResponse,
16
+ storefrontSuccessHeaders,
17
+ runInStoreCacheTenant
18
+ } from "../../storefrontRouteSupport.js";
19
+ import { storefrontCategoryLandingResponseSchema } from "../openapiSchemas.js";
20
+ const metadata = {
21
+ path: "/ecommerce/storefront/categories/[slug]",
22
+ GET: {
23
+ requireAuth: false
24
+ }
25
+ };
26
+ const ANONYMOUS_CACHE_CONTROL = "public, max-age=30, stale-while-revalidate=30";
27
+ const CATEGORY_NOT_FOUND = { error: "category_not_found" };
28
+ async function GET(req, routeContext = {}) {
29
+ try {
30
+ const query = parseStorefrontCategoryLandingQuery(new URL(req.url).searchParams);
31
+ const container = await createRequestContainer();
32
+ const service = container.resolve("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, "categoryLanding");
36
+ if (rateLimited) return rateLimited;
37
+ const params = ecommerceStorefrontCategoryParamsSchema.safeParse(await routeContext.params ?? {});
38
+ if (!params.success) return storefrontErrorResponse(404, { ...CATEGORY_NOT_FOUND });
39
+ const landing = await cachedGetStorefrontCategoryLanding(container, context, params.data.slug, query);
40
+ if (!landing) return storefrontErrorResponse(404, { ...CATEGORY_NOT_FOUND });
41
+ const headers = storefrontSuccessHeaders(context, ANONYMOUS_CACHE_CONTROL);
42
+ if (landing.products.sortApproximate) headers["X-Sort-Approximate"] = "true";
43
+ if (landing.products.sortUnavailable) headers["X-Sort-Unavailable"] = "true";
44
+ return NextResponse.json(landing, { headers });
45
+ });
46
+ } catch (error) {
47
+ return storefrontRouteErrorResponse(error, {
48
+ message: "Storefront category landing failed",
49
+ code: "ecommerce.storefront_category_landing_failed"
50
+ });
51
+ }
52
+ }
53
+ var route_default = GET;
54
+ const storefrontCategoryTag = "Ecommerce";
55
+ const storefrontCategoryGetDoc = {
56
+ summary: "Get one storefront category landing for the current buyer",
57
+ description: '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.',
58
+ tags: [storefrontCategoryTag],
59
+ pathParams: ecommerceStorefrontCategoryParamsSchema,
60
+ query: ecommerceStorefrontCategoryLandingQuerySchema,
61
+ responses: [
62
+ { status: 200, description: "The category landing as this buyer sees it.", schema: storefrontCategoryLandingResponseSchema }
63
+ ],
64
+ errors: [
65
+ { status: 400, description: "Unknown, repeated or malformed query parameters", schema: storefrontInvalidQueryErrorSchema },
66
+ { status: 401, description: "Portal session invalid or issued for another tenant or organization", schema: storefrontErrorSchema },
67
+ { status: 403, description: "Draft store (only when OM_ECOMMERCE_DEV_STORE_SLUG=true)", schema: storefrontErrorSchema },
68
+ { status: 404, description: "No store serves this host or path, or the category is not visible to this buyer", schema: storefrontErrorSchema },
69
+ { status: 410, description: "Store archived", schema: storefrontErrorSchema },
70
+ { status: 429, description: "Too many requests", schema: rateLimitErrorSchema },
71
+ { status: 503, description: "Store misconfigured (no default channel binding)", schema: storefrontErrorSchema }
72
+ ]
73
+ };
74
+ const openApi = {
75
+ tag: storefrontCategoryTag,
76
+ summary: "Public storefront category landing",
77
+ methods: {
78
+ GET: storefrontCategoryGetDoc
79
+ }
80
+ };
81
+ export {
82
+ GET,
83
+ route_default as default,
84
+ metadata,
85
+ openApi
86
+ };
87
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../src/modules/ecommerce/api/storefront/categories/%5Bslug%5D/route.ts"],
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { rateLimitErrorSchema } from '@open-mercato/shared/lib/ratelimit/helpers'\nimport {\n ecommerceStorefrontCategoryLandingQuerySchema,\n ecommerceStorefrontCategoryParamsSchema,\n} from '../../../../data/validators'\nimport { enforceStorefrontRateLimit } from '../../../../lib/storefrontRateLimit'\nimport type { StoreContextService } from '../../../../lib/storeContextService'\nimport { cachedGetStorefrontCategoryLanding } from '../../../../lib/storefrontCategoryCache'\nimport { parseStorefrontCategoryLandingQuery } from '../../../../lib/storefrontQuery'\nimport {\n storefrontErrorResponse,\n storefrontErrorSchema,\n storefrontInvalidQueryErrorSchema,\n storefrontRouteErrorResponse,\n storefrontSuccessHeaders,\n runInStoreCacheTenant,\n} from '../../storefrontRouteSupport'\nimport { storefrontCategoryLandingResponseSchema } from '../openapiSchemas'\n\nexport const metadata = {\n path: '/ecommerce/storefront/categories/[slug]',\n GET: {\n requireAuth: false,\n },\n}\n\nconst ANONYMOUS_CACHE_CONTROL = 'public, max-age=30, stale-while-revalidate=30'\nconst CATEGORY_NOT_FOUND = { error: 'category_not_found' } as const\n\ntype CategoryRouteParams = { slug?: string }\n\ntype CategoryRouteContext = { params?: CategoryRouteParams | Promise<CategoryRouteParams> }\n\nexport async function GET(req: Request, routeContext: CategoryRouteContext = {}) {\n try {\n const query = parseStorefrontCategoryLandingQuery(new URL(req.url).searchParams)\n const container = await createRequestContainer()\n const service = container.resolve('storeContextService') as StoreContextService\n const context = await service.resolve(req, { pathname: query.path ?? '/' })\n return await runInStoreCacheTenant(context, async () => {\n const rateLimited = await enforceStorefrontRateLimit(container, req, context, 'categoryLanding')\n if (rateLimited) return rateLimited\n const params = ecommerceStorefrontCategoryParamsSchema.safeParse((await routeContext.params) ?? {})\n if (!params.success) return storefrontErrorResponse(404, { ...CATEGORY_NOT_FOUND })\n const landing = await cachedGetStorefrontCategoryLanding(container, context, params.data.slug, query)\n if (!landing) return storefrontErrorResponse(404, { ...CATEGORY_NOT_FOUND })\n const headers = storefrontSuccessHeaders(context, ANONYMOUS_CACHE_CONTROL)\n if (landing.products.sortApproximate) headers['X-Sort-Approximate'] = 'true'\n if (landing.products.sortUnavailable) headers['X-Sort-Unavailable'] = 'true'\n return NextResponse.json(landing, { headers })\n })\n } catch (error) {\n return storefrontRouteErrorResponse(error, {\n message: 'Storefront category landing failed',\n code: 'ecommerce.storefront_category_landing_failed',\n })\n }\n}\n\nexport default GET\n\nconst storefrontCategoryTag = 'Ecommerce'\n\nconst storefrontCategoryGetDoc: OpenApiMethodDoc = {\n summary: 'Get one storefront category landing for the current buyer',\n description:\n '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.',\n tags: [storefrontCategoryTag],\n pathParams: ecommerceStorefrontCategoryParamsSchema,\n query: ecommerceStorefrontCategoryLandingQuerySchema,\n responses: [\n { status: 200, description: 'The category landing as this buyer sees it.', schema: storefrontCategoryLandingResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Unknown, repeated or malformed query parameters', schema: storefrontInvalidQueryErrorSchema },\n { status: 401, description: 'Portal session invalid or issued for another tenant or organization', schema: storefrontErrorSchema },\n { status: 403, description: 'Draft store (only when OM_ECOMMERCE_DEV_STORE_SLUG=true)', schema: storefrontErrorSchema },\n { status: 404, description: 'No store serves this host or path, or the category is not visible to this buyer', schema: storefrontErrorSchema },\n { status: 410, description: 'Store archived', schema: storefrontErrorSchema },\n { status: 429, description: 'Too many requests', schema: rateLimitErrorSchema },\n { status: 503, description: 'Store misconfigured (no default channel binding)', schema: storefrontErrorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: storefrontCategoryTag,\n summary: 'Public storefront category landing',\n methods: {\n GET: storefrontCategoryGetDoc,\n },\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,8BAA8B;AAEvC,SAAS,4BAA4B;AACrC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,kCAAkC;AAE3C,SAAS,0CAA0C;AACnD,SAAS,2CAA2C;AACpD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,+CAA+C;AAEjD,MAAM,WAAW;AAAA,EACtB,MAAM;AAAA,EACN,KAAK;AAAA,IACH,aAAa;AAAA,EACf;AACF;AAEA,MAAM,0BAA0B;AAChC,MAAM,qBAAqB,EAAE,OAAO,qBAAqB;AAMzD,eAAsB,IAAI,KAAc,eAAqC,CAAC,GAAG;AAC/E,MAAI;AACF,UAAM,QAAQ,oCAAoC,IAAI,IAAI,IAAI,GAAG,EAAE,YAAY;AAC/E,UAAM,YAAY,MAAM,uBAAuB;AAC/C,UAAM,UAAU,UAAU,QAAQ,qBAAqB;AACvD,UAAM,UAAU,MAAM,QAAQ,QAAQ,KAAK,EAAE,UAAU,MAAM,QAAQ,IAAI,CAAC;AAC1E,WAAO,MAAM,sBAAsB,SAAS,YAAY;AACtD,YAAM,cAAc,MAAM,2BAA2B,WAAW,KAAK,SAAS,iBAAiB;AAC/F,UAAI,YAAa,QAAO;AACxB,YAAM,SAAS,wCAAwC,UAAW,MAAM,aAAa,UAAW,CAAC,CAAC;AAClG,UAAI,CAAC,OAAO,QAAS,QAAO,wBAAwB,KAAK,EAAE,GAAG,mBAAmB,CAAC;AAClF,YAAM,UAAU,MAAM,mCAAmC,WAAW,SAAS,OAAO,KAAK,MAAM,KAAK;AACpG,UAAI,CAAC,QAAS,QAAO,wBAAwB,KAAK,EAAE,GAAG,mBAAmB,CAAC;AAC3E,YAAM,UAAU,yBAAyB,SAAS,uBAAuB;AACzE,UAAI,QAAQ,SAAS,gBAAiB,SAAQ,oBAAoB,IAAI;AACtE,UAAI,QAAQ,SAAS,gBAAiB,SAAQ,oBAAoB,IAAI;AACtE,aAAO,aAAa,KAAK,SAAS,EAAE,QAAQ,CAAC;AAAA,IAC/C,CAAC;AAAA,EACH,SAAS,OAAO;AACd,WAAO,6BAA6B,OAAO;AAAA,MACzC,SAAS;AAAA,MACT,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AACF;AAEA,IAAO,gBAAQ;AAEf,MAAM,wBAAwB;AAE9B,MAAM,2BAA6C;AAAA,EACjD,SAAS;AAAA,EACT,aACE;AAAA,EACF,MAAM,CAAC,qBAAqB;AAAA,EAC5B,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,WAAW;AAAA,IACT,EAAE,QAAQ,KAAK,aAAa,+CAA+C,QAAQ,wCAAwC;AAAA,EAC7H;AAAA,EACA,QAAQ;AAAA,IACN,EAAE,QAAQ,KAAK,aAAa,mDAAmD,QAAQ,kCAAkC;AAAA,IACzH,EAAE,QAAQ,KAAK,aAAa,uEAAuE,QAAQ,sBAAsB;AAAA,IACjI,EAAE,QAAQ,KAAK,aAAa,4DAA4D,QAAQ,sBAAsB;AAAA,IACtH,EAAE,QAAQ,KAAK,aAAa,mFAAmF,QAAQ,sBAAsB;AAAA,IAC7I,EAAE,QAAQ,KAAK,aAAa,kBAAkB,QAAQ,sBAAsB;AAAA,IAC5E,EAAE,QAAQ,KAAK,aAAa,qBAAqB,QAAQ,qBAAqB;AAAA,IAC9E,EAAE,QAAQ,KAAK,aAAa,oDAAoD,QAAQ,sBAAsB;AAAA,EAChH;AACF;AAEO,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,KAAK;AAAA,EACP;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,50 @@
1
+ import { z } from "zod";
2
+ import { storefrontProductListResponseSchema } from "../products/openapiSchemas.js";
3
+ const storefrontCategoryNodeSchema = z.lazy(
4
+ () => z.object({
5
+ id: z.string().uuid(),
6
+ name: z.string(),
7
+ slug: z.string().nullable(),
8
+ description: z.string().nullable(),
9
+ depth: z.number().int(),
10
+ parentId: z.string().uuid().nullable(),
11
+ productCount: z.number().int().nullable().describe(
12
+ "Products of the buyer's effective assortment in this category or a descendant; null when the assortment is too large to count"
13
+ ),
14
+ hasChildren: z.boolean(),
15
+ children: z.array(storefrontCategoryNodeSchema)
16
+ })
17
+ );
18
+ const storefrontCategoryTreeResponseSchema = z.object({
19
+ tree: z.array(storefrontCategoryNodeSchema),
20
+ effectiveLocale: z.string()
21
+ });
22
+ const storefrontCategoryLandingResponseSchema = z.object({
23
+ category: z.object({
24
+ id: z.string().uuid(),
25
+ name: z.string(),
26
+ slug: z.string().nullable(),
27
+ description: z.string().nullable(),
28
+ depth: z.number().int(),
29
+ parentId: z.string().uuid().nullable(),
30
+ ancestorIds: z.array(z.string().uuid()),
31
+ breadcrumb: z.array(z.object({ id: z.string().uuid(), name: z.string(), slug: z.string().nullable() })),
32
+ children: z.array(
33
+ z.object({ id: z.string().uuid(), name: z.string(), slug: z.string().nullable(), productCount: z.number().int().nullable() })
34
+ ),
35
+ productCount: z.number().int().nullable(),
36
+ seo: z.object({
37
+ title: z.string().nullable(),
38
+ description: z.string().nullable(),
39
+ canonicalUrl: z.string().nullable()
40
+ })
41
+ }),
42
+ products: storefrontProductListResponseSchema.describe("Identical to GET /products with the category filter applied"),
43
+ effectiveLocale: z.string()
44
+ });
45
+ export {
46
+ storefrontCategoryLandingResponseSchema,
47
+ storefrontCategoryNodeSchema,
48
+ storefrontCategoryTreeResponseSchema
49
+ };
50
+ //# sourceMappingURL=openapiSchemas.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/ecommerce/api/storefront/categories/openapiSchemas.ts"],
4
+ "sourcesContent": ["import { z } from 'zod'\nimport { storefrontProductListResponseSchema } from '../products/openapiSchemas'\n\nexport type StorefrontCategoryNodeSchema = {\n id: string\n name: string\n slug: string | null\n description: string | null\n depth: number\n parentId: string | null\n productCount: number | null\n hasChildren: boolean\n children: StorefrontCategoryNodeSchema[]\n}\n\nexport const storefrontCategoryNodeSchema: z.ZodType<StorefrontCategoryNodeSchema> = z.lazy(() =>\n z.object({\n id: z.string().uuid(),\n name: z.string(),\n slug: z.string().nullable(),\n description: z.string().nullable(),\n depth: z.number().int(),\n parentId: z.string().uuid().nullable(),\n productCount: z\n .number()\n .int()\n .nullable()\n .describe(\n 'Products of the buyer\\'s effective assortment in this category or a descendant; null when the assortment is too large to count',\n ),\n hasChildren: z.boolean(),\n children: z.array(storefrontCategoryNodeSchema),\n }),\n)\n\nexport const storefrontCategoryTreeResponseSchema = z.object({\n tree: z.array(storefrontCategoryNodeSchema),\n effectiveLocale: z.string(),\n})\n\nexport const storefrontCategoryLandingResponseSchema = z.object({\n category: z.object({\n id: z.string().uuid(),\n name: z.string(),\n slug: z.string().nullable(),\n description: z.string().nullable(),\n depth: z.number().int(),\n parentId: z.string().uuid().nullable(),\n ancestorIds: z.array(z.string().uuid()),\n breadcrumb: z.array(z.object({ id: z.string().uuid(), name: z.string(), slug: z.string().nullable() })),\n children: z.array(\n z.object({ id: z.string().uuid(), name: z.string(), slug: z.string().nullable(), productCount: z.number().int().nullable() }),\n ),\n productCount: z.number().int().nullable(),\n seo: z.object({\n title: z.string().nullable(),\n description: z.string().nullable(),\n canonicalUrl: z.string().nullable(),\n }),\n }),\n products: storefrontProductListResponseSchema.describe('Identical to GET /products with the category filter applied'),\n effectiveLocale: z.string(),\n})\n"],
5
+ "mappings": "AAAA,SAAS,SAAS;AAClB,SAAS,2CAA2C;AAc7C,MAAM,+BAAwE,EAAE;AAAA,EAAK,MAC1F,EAAE,OAAO;AAAA,IACP,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,IACpB,MAAM,EAAE,OAAO;AAAA,IACf,MAAM,EAAE,OAAO,EAAE,SAAS;AAAA,IAC1B,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,IACjC,OAAO,EAAE,OAAO,EAAE,IAAI;AAAA,IACtB,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,IACrC,cAAc,EACX,OAAO,EACP,IAAI,EACJ,SAAS,EACT;AAAA,MACC;AAAA,IACF;AAAA,IACF,aAAa,EAAE,QAAQ;AAAA,IACvB,UAAU,EAAE,MAAM,4BAA4B;AAAA,EAChD,CAAC;AACH;AAEO,MAAM,uCAAuC,EAAE,OAAO;AAAA,EAC3D,MAAM,EAAE,MAAM,4BAA4B;AAAA,EAC1C,iBAAiB,EAAE,OAAO;AAC5B,CAAC;AAEM,MAAM,0CAA0C,EAAE,OAAO;AAAA,EAC9D,UAAU,EAAE,OAAO;AAAA,IACjB,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,IACpB,MAAM,EAAE,OAAO;AAAA,IACf,MAAM,EAAE,OAAO,EAAE,SAAS;AAAA,IAC1B,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,IACjC,OAAO,EAAE,OAAO,EAAE,IAAI;AAAA,IACtB,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,IACrC,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC;AAAA,IACtC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,GAAG,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;AAAA,IACtG,UAAU,EAAE;AAAA,MACV,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,GAAG,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,OAAO,EAAE,SAAS,GAAG,cAAc,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AAAA,IAC9H;AAAA,IACA,cAAc,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AAAA,IACxC,KAAK,EAAE,OAAO;AAAA,MACZ,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,MAC3B,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,MACjC,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA,IACpC,CAAC;AAAA,EACH,CAAC;AAAA,EACD,UAAU,oCAAoC,SAAS,6DAA6D;AAAA,EACpH,iBAAiB,EAAE,OAAO;AAC5B,CAAC;",
6
+ "names": []
7
+ }