@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,71 @@
1
+ import { catalogCategoryTag, catalogProductsTag, storefrontCache, storefrontCacheValueHash } from "./cacheKeys.js";
2
+ import {
3
+ getStorefrontCategoryLanding,
4
+ getStorefrontCategoryTree
5
+ } from "./storefrontCategories.js";
6
+ import { cachedListStorefrontProducts } from "./storefrontProductCache.js";
7
+ const STOREFRONT_CATEGORY_TREE_TTL_MS = 3e5;
8
+ const STOREFRONT_CATEGORY_LANDING_TTL_MS = 6e4;
9
+ const TREE_CACHE_SEGMENT = "categories-tree";
10
+ const LANDING_CACHE_SEGMENT = "categories-landing";
11
+ const RESOLUTION_ONLY_PARAMETERS = /* @__PURE__ */ new Set(["path", "storeSlug", "locale"]);
12
+ function storefrontCategoryTreeCacheParts(query) {
13
+ const relevant = Object.fromEntries(
14
+ Object.entries(query).filter(([key]) => !RESOLUTION_ONLY_PARAMETERS.has(key))
15
+ );
16
+ return [TREE_CACHE_SEGMENT, storefrontCacheValueHash(relevant)];
17
+ }
18
+ function storefrontCategoryLandingCacheParts(slug) {
19
+ return [LANDING_CACHE_SEGMENT, slug.trim()];
20
+ }
21
+ function storefrontCategoryTreeCacheTags(ctx) {
22
+ return [catalogProductsTag(ctx.tenantId)];
23
+ }
24
+ function storefrontCategoryLandingCacheTags(ctx, block) {
25
+ return [catalogProductsTag(ctx.tenantId), catalogCategoryTag(block.category.id)];
26
+ }
27
+ async function cachedGetStorefrontCategoryTree(container, ctx, query) {
28
+ const cache = storefrontCache(container, ctx);
29
+ const parts = storefrontCategoryTreeCacheParts(query);
30
+ const cached = await cache.get(parts, { scope: "assortment" });
31
+ if (cached) return cached;
32
+ const response = await getStorefrontCategoryTree(container, ctx, query);
33
+ await cache.set(parts, response, {
34
+ scope: "assortment",
35
+ ttlMs: STOREFRONT_CATEGORY_TREE_TTL_MS,
36
+ tags: storefrontCategoryTreeCacheTags(ctx)
37
+ });
38
+ return response;
39
+ }
40
+ async function cachedGetStorefrontCategoryBlock(container, ctx, slug) {
41
+ const cache = storefrontCache(container, ctx);
42
+ const parts = storefrontCategoryLandingCacheParts(slug);
43
+ const cached = await cache.get(parts, { scope: "assortment" });
44
+ if (cached) return cached;
45
+ const block = await getStorefrontCategoryLanding(container, ctx, slug);
46
+ if (!block) return null;
47
+ await cache.set(parts, block, {
48
+ scope: "assortment",
49
+ ttlMs: STOREFRONT_CATEGORY_LANDING_TTL_MS,
50
+ tags: storefrontCategoryLandingCacheTags(ctx, block)
51
+ });
52
+ return block;
53
+ }
54
+ async function cachedGetStorefrontCategoryLanding(container, ctx, slug, query) {
55
+ const block = await cachedGetStorefrontCategoryBlock(container, ctx, slug);
56
+ if (!block) return null;
57
+ const products = await cachedListStorefrontProducts(container, ctx, { ...query, categoryId: block.category.id });
58
+ if (products.appliedFilters.category?.id !== block.category.id) return null;
59
+ return { ...block, products };
60
+ }
61
+ export {
62
+ STOREFRONT_CATEGORY_LANDING_TTL_MS,
63
+ STOREFRONT_CATEGORY_TREE_TTL_MS,
64
+ cachedGetStorefrontCategoryLanding,
65
+ cachedGetStorefrontCategoryTree,
66
+ storefrontCategoryLandingCacheParts,
67
+ storefrontCategoryLandingCacheTags,
68
+ storefrontCategoryTreeCacheParts,
69
+ storefrontCategoryTreeCacheTags
70
+ };
71
+ //# sourceMappingURL=storefrontCategoryCache.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/ecommerce/lib/storefrontCategoryCache.ts"],
4
+ "sourcesContent": ["import type { AwilixContainer } from 'awilix'\nimport type { EcommerceStorefrontCategoryLandingQuery, EcommerceStorefrontCategoryTreeQuery } from '../data/validators'\nimport { catalogCategoryTag, catalogProductsTag, storefrontCache, storefrontCacheValueHash } from './cacheKeys'\nimport {\n getStorefrontCategoryLanding,\n getStorefrontCategoryTree,\n type StorefrontCategoryLandingBlock,\n type StorefrontCategoryTreeResponse,\n} from './storefrontCategories'\nimport { cachedListStorefrontProducts } from './storefrontProductCache'\nimport type { StorefrontProductListResponse } from './storefrontProducts'\nimport type { StoreContext } from './types'\n\n/**\n * Server-side cache for `GET /categories` and `GET /categories/:slug` (Storefront Public API rev 4\n * \u00A79). Neither response carries a price, so the tree and the landing's category block are keyed on\n * `assortmentScopeHash` (`scope: 'assortment'`: store, effective locale, hash) and shared by every\n * buyer with the same effective assortment. The landing's embedded `/products` response is\n * buyer-priced: it goes through `cachedListStorefrontProducts` on the full digest, and shares its\n * entry with an identical `GET /products?categoryId=\u2026` request.\n *\n * A cached category block is only ever stored for a category visible under that assortment, so a\n * hit never needs the visibility check again; a miss that resolves to nothing is never cached, so\n * a 404 costs the same on every request. Entries carry the tenant's `catalog-products` tag, which\n * every category, product and price event evicts, plus the landing category's own tag.\n */\n\nexport const STOREFRONT_CATEGORY_TREE_TTL_MS = 300_000\nexport const STOREFRONT_CATEGORY_LANDING_TTL_MS = 60_000\n\nconst TREE_CACHE_SEGMENT = 'categories-tree'\nconst LANDING_CACHE_SEGMENT = 'categories-landing'\nconst RESOLUTION_ONLY_PARAMETERS: ReadonlySet<string> = new Set(['path', 'storeSlug', 'locale'])\n\nexport function storefrontCategoryTreeCacheParts(\n query: Pick<EcommerceStorefrontCategoryTreeQuery, 'parentId' | 'depth' | 'includeEmpty'>,\n): string[] {\n const relevant = Object.fromEntries(\n Object.entries(query).filter(([key]) => !RESOLUTION_ONLY_PARAMETERS.has(key)),\n )\n return [TREE_CACHE_SEGMENT, storefrontCacheValueHash(relevant)]\n}\n\nexport function storefrontCategoryLandingCacheParts(slug: string): string[] {\n return [LANDING_CACHE_SEGMENT, slug.trim()]\n}\n\nexport function storefrontCategoryTreeCacheTags(ctx: StoreContext): string[] {\n return [catalogProductsTag(ctx.tenantId)]\n}\n\nexport function storefrontCategoryLandingCacheTags(ctx: StoreContext, block: StorefrontCategoryLandingBlock): string[] {\n return [catalogProductsTag(ctx.tenantId), catalogCategoryTag(block.category.id)]\n}\n\nexport async function cachedGetStorefrontCategoryTree(\n container: AwilixContainer,\n ctx: StoreContext,\n query: Pick<EcommerceStorefrontCategoryTreeQuery, 'parentId' | 'depth' | 'includeEmpty'>,\n): Promise<StorefrontCategoryTreeResponse> {\n const cache = storefrontCache(container, ctx)\n const parts = storefrontCategoryTreeCacheParts(query)\n const cached = await cache.get<StorefrontCategoryTreeResponse>(parts, { scope: 'assortment' })\n if (cached) return cached\n const response = await getStorefrontCategoryTree(container, ctx, query)\n await cache.set(parts, response, {\n scope: 'assortment',\n ttlMs: STOREFRONT_CATEGORY_TREE_TTL_MS,\n tags: storefrontCategoryTreeCacheTags(ctx),\n })\n return response\n}\n\nexport type StorefrontCategoryLandingResponse = StorefrontCategoryLandingBlock & {\n products: StorefrontProductListResponse\n}\n\nasync function cachedGetStorefrontCategoryBlock(\n container: AwilixContainer,\n ctx: StoreContext,\n slug: string,\n): Promise<StorefrontCategoryLandingBlock | null> {\n const cache = storefrontCache(container, ctx)\n const parts = storefrontCategoryLandingCacheParts(slug)\n const cached = await cache.get<StorefrontCategoryLandingBlock>(parts, { scope: 'assortment' })\n if (cached) return cached\n const block = await getStorefrontCategoryLanding(container, ctx, slug)\n if (!block) return null\n await cache.set(parts, block, {\n scope: 'assortment',\n ttlMs: STOREFRONT_CATEGORY_LANDING_TTL_MS,\n tags: storefrontCategoryLandingCacheTags(ctx, block),\n })\n return block\n}\n\n/**\n * `null` when no visible category has this slug \u2014 and nothing about the embedded listing is touched\n * then \u2014 or when the listing no longer resolves the category (it went inactive between the cached\n * block and the listing), so a landing can never serve an unfiltered catalogue.\n */\nexport async function cachedGetStorefrontCategoryLanding(\n container: AwilixContainer,\n ctx: StoreContext,\n slug: string,\n query: EcommerceStorefrontCategoryLandingQuery,\n): Promise<StorefrontCategoryLandingResponse | null> {\n const block = await cachedGetStorefrontCategoryBlock(container, ctx, slug)\n if (!block) return null\n const products = await cachedListStorefrontProducts(container, ctx, { ...query, categoryId: block.category.id })\n if (products.appliedFilters.category?.id !== block.category.id) return null\n return { ...block, products }\n}\n"],
5
+ "mappings": "AAEA,SAAS,oBAAoB,oBAAoB,iBAAiB,gCAAgC;AAClG;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AACP,SAAS,oCAAoC;AAkBtC,MAAM,kCAAkC;AACxC,MAAM,qCAAqC;AAElD,MAAM,qBAAqB;AAC3B,MAAM,wBAAwB;AAC9B,MAAM,6BAAkD,oBAAI,IAAI,CAAC,QAAQ,aAAa,QAAQ,CAAC;AAExF,SAAS,iCACd,OACU;AACV,QAAM,WAAW,OAAO;AAAA,IACtB,OAAO,QAAQ,KAAK,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,CAAC,2BAA2B,IAAI,GAAG,CAAC;AAAA,EAC9E;AACA,SAAO,CAAC,oBAAoB,yBAAyB,QAAQ,CAAC;AAChE;AAEO,SAAS,oCAAoC,MAAwB;AAC1E,SAAO,CAAC,uBAAuB,KAAK,KAAK,CAAC;AAC5C;AAEO,SAAS,gCAAgC,KAA6B;AAC3E,SAAO,CAAC,mBAAmB,IAAI,QAAQ,CAAC;AAC1C;AAEO,SAAS,mCAAmC,KAAmB,OAAiD;AACrH,SAAO,CAAC,mBAAmB,IAAI,QAAQ,GAAG,mBAAmB,MAAM,SAAS,EAAE,CAAC;AACjF;AAEA,eAAsB,gCACpB,WACA,KACA,OACyC;AACzC,QAAM,QAAQ,gBAAgB,WAAW,GAAG;AAC5C,QAAM,QAAQ,iCAAiC,KAAK;AACpD,QAAM,SAAS,MAAM,MAAM,IAAoC,OAAO,EAAE,OAAO,aAAa,CAAC;AAC7F,MAAI,OAAQ,QAAO;AACnB,QAAM,WAAW,MAAM,0BAA0B,WAAW,KAAK,KAAK;AACtE,QAAM,MAAM,IAAI,OAAO,UAAU;AAAA,IAC/B,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM,gCAAgC,GAAG;AAAA,EAC3C,CAAC;AACD,SAAO;AACT;AAMA,eAAe,iCACb,WACA,KACA,MACgD;AAChD,QAAM,QAAQ,gBAAgB,WAAW,GAAG;AAC5C,QAAM,QAAQ,oCAAoC,IAAI;AACtD,QAAM,SAAS,MAAM,MAAM,IAAoC,OAAO,EAAE,OAAO,aAAa,CAAC;AAC7F,MAAI,OAAQ,QAAO;AACnB,QAAM,QAAQ,MAAM,6BAA6B,WAAW,KAAK,IAAI;AACrE,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,MAAM,IAAI,OAAO,OAAO;AAAA,IAC5B,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM,mCAAmC,KAAK,KAAK;AAAA,EACrD,CAAC;AACD,SAAO;AACT;AAOA,eAAsB,mCACpB,WACA,KACA,MACA,OACmD;AACnD,QAAM,QAAQ,MAAM,iCAAiC,WAAW,KAAK,IAAI;AACzE,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,WAAW,MAAM,6BAA6B,WAAW,KAAK,EAAE,GAAG,OAAO,YAAY,MAAM,SAAS,GAAG,CAAC;AAC/G,MAAI,SAAS,eAAe,UAAU,OAAO,MAAM,SAAS,GAAI,QAAO;AACvE,SAAO,EAAE,GAAG,OAAO,SAAS;AAC9B;",
6
+ "names": []
7
+ }
@@ -0,0 +1,447 @@
1
+ import { LoadStrategy } from "@mikro-orm/postgresql";
2
+ import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
3
+ import { sanitizeRichTextHtml } from "@open-mercato/shared/lib/html/sanitizeRichText";
4
+ import { SortDir } from "@open-mercato/shared/lib/query/types";
5
+ import { E } from "../../../generated/entities.ids.generated.js";
6
+ import { Attachment } from "@open-mercato/core/modules/attachments/data/entities";
7
+ import {
8
+ CatalogProduct
9
+ } from "@open-mercato/core/modules/catalog/data/entities";
10
+ import { PRODUCT_SCOPE_KEYS_DOC_KEY, categoryScopeKey } from "@open-mercato/core/modules/catalog/lib/productScopeKeys";
11
+ import {
12
+ optionChoiceLabelTranslationField,
13
+ optionLabelTranslationField
14
+ } from "@open-mercato/core/modules/catalog/lib/optionSchemaTranslations";
15
+ import { buildStorefrontProductScope, composeStorefrontProductFilters } from "./storefrontProductScope.js";
16
+ import {
17
+ resolveStorefrontPrices
18
+ } from "./storefrontPricing.js";
19
+ import {
20
+ STOREFRONT_CATEGORY_ENTITY_TYPE,
21
+ STOREFRONT_OPTION_SCHEMA_ENTITY_TYPE,
22
+ STOREFRONT_PRODUCT_ENTITY_TYPE,
23
+ STOREFRONT_TAG_ENTITY_TYPE,
24
+ STOREFRONT_VARIANT_ENTITY_TYPE,
25
+ buildStorefrontListItem,
26
+ isCategoryInAssortment,
27
+ isImageMimeType,
28
+ loadPublicPartitionCodes,
29
+ resolvePublicDefaultMediaUrls,
30
+ selectCategoriesWithActiveAncestors,
31
+ loadStorefrontTranslations,
32
+ localeChain,
33
+ localize,
34
+ nonEmptyString,
35
+ referenceId,
36
+ resolveStorefrontAvailability,
37
+ storefrontAvailabilityKey,
38
+ stringList,
39
+ toStorefrontAvailability,
40
+ tryResolve
41
+ } from "./storefrontCatalogSupport.js";
42
+ const STOREFRONT_RELATED_PRODUCTS_LIMIT = 8;
43
+ const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
44
+ function collectionItems(value) {
45
+ if (Array.isArray(value)) return value;
46
+ if (value && typeof value === "object" && "getItems" in value) {
47
+ const getItems = value.getItems;
48
+ if (typeof getItems === "function") {
49
+ try {
50
+ return getItems.call(value);
51
+ } catch {
52
+ return [];
53
+ }
54
+ }
55
+ }
56
+ return [];
57
+ }
58
+ function toNumber(value) {
59
+ if (value === null || value === void 0 || value === "") return null;
60
+ const numeric = typeof value === "number" ? value : Number(value);
61
+ return Number.isFinite(numeric) ? numeric : null;
62
+ }
63
+ function toDimensions(value) {
64
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
65
+ const source = value;
66
+ const dimensions = {
67
+ length: toNumber(source.length ?? source.depth),
68
+ width: toNumber(source.width),
69
+ height: toNumber(source.height),
70
+ unit: nonEmptyString(source.unit)
71
+ };
72
+ const empty = dimensions.length === null && dimensions.width === null && dimensions.height === null;
73
+ return empty && dimensions.unit === null ? null : dimensions;
74
+ }
75
+ function stringRecord(value) {
76
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
77
+ const entries = Object.entries(value).filter(
78
+ (entry) => typeof entry[1] === "string"
79
+ );
80
+ return Object.fromEntries(entries);
81
+ }
82
+ function lookupClause(idOrHandle) {
83
+ const value = idOrHandle.trim();
84
+ return UUID_PATTERN.test(value) ? { id: { $eq: value } } : { handle: { $eq: value } };
85
+ }
86
+ async function findScopedProductId(runtime, idOrHandle) {
87
+ const scope = buildStorefrontProductScope(runtime.ctx);
88
+ const result = await runtime.queryEngine.query(E.catalog.catalog_product, {
89
+ tenantId: scope.tenantId,
90
+ organizationId: scope.organizationId,
91
+ withDeleted: scope.withDeleted,
92
+ filters: composeStorefrontProductFilters(scope, lookupClause(idOrHandle)),
93
+ fields: ["id"],
94
+ page: { page: 1, pageSize: 1 }
95
+ });
96
+ const [first] = result.items;
97
+ return first && typeof first.id === "string" ? first.id : null;
98
+ }
99
+ async function loadProducts(runtime, ids, withOptionSchema, knownAncestors = /* @__PURE__ */ new Map()) {
100
+ if (!ids.length) return { products: [], ancestors: knownAncestors };
101
+ const { ctx } = runtime;
102
+ const populate = [
103
+ "variants",
104
+ "categoryAssignments.category",
105
+ "tagAssignments.tag"
106
+ ];
107
+ if (withOptionSchema) populate.push("optionSchemaTemplate");
108
+ const products = await findWithDecryption(
109
+ runtime.em,
110
+ CatalogProduct,
111
+ { id: { $in: ids }, tenantId: ctx.tenantId, organizationId: ctx.organizationId, deletedAt: null, isActive: true },
112
+ { populate, strategy: LoadStrategy.JOINED },
113
+ runtime.decryptionScope
114
+ );
115
+ const byId = new Map(products.map((product) => [product.id, product]));
116
+ const loaded = ids.flatMap((id) => {
117
+ const product = byId.get(id);
118
+ return product ? [toLoadedProduct(runtime, product)] : [];
119
+ });
120
+ const categories = loaded.flatMap((entry) => entry.categories);
121
+ const { visibleIds, ancestors } = await selectCategoriesWithActiveAncestors(runtime.em, ctx, categories, {
122
+ activeIds: categories.map((category) => category.id),
123
+ ancestors: knownAncestors
124
+ });
125
+ return {
126
+ products: loaded.map((entry) => ({
127
+ ...entry,
128
+ categories: entry.categories.filter((category) => visibleIds.has(category.id))
129
+ })),
130
+ ancestors
131
+ };
132
+ }
133
+ function toLoadedProduct(runtime, product) {
134
+ const { ctx } = runtime;
135
+ const variants = collectionItems(product.variants).filter((variant) => !variant.deletedAt && variant.isActive !== false).map((variant, index) => ({ variant, index })).sort(
136
+ (left, right) => Number(right.variant.isDefault === true) - Number(left.variant.isDefault === true) || left.index - right.index
137
+ ).map((entry) => entry.variant);
138
+ const categories = [];
139
+ const assignments = collectionItems(product.categoryAssignments).map((assignment, index) => ({ assignment, index })).sort((left, right) => (left.assignment.position ?? 0) - (right.assignment.position ?? 0) || left.index - right.index);
140
+ for (const { assignment } of assignments) {
141
+ const category = assignment.category;
142
+ if (!category || typeof category !== "object" || category.deletedAt || category.isActive === false) continue;
143
+ if (category.tenantId !== ctx.tenantId || category.organizationId !== ctx.organizationId) continue;
144
+ const ancestorIds = stringList(category.ancestorIds);
145
+ const lineage = { id: category.id, ancestorIds, descendantIds: stringList(category.descendantIds) };
146
+ if (!isCategoryInAssortment(lineage, ctx.buyer.assortmentScope)) continue;
147
+ if (categories.some((entry) => entry.id === category.id)) continue;
148
+ categories.push({ id: category.id, name: category.name, slug: category.slug ?? null, ancestorIds });
149
+ }
150
+ const tags = [];
151
+ for (const assignment of collectionItems(product.tagAssignments)) {
152
+ const tag = assignment.tag;
153
+ if (!tag || typeof tag !== "object" || typeof tag.id !== "string") continue;
154
+ if (tag.tenantId !== ctx.tenantId || tag.organizationId !== ctx.organizationId) continue;
155
+ if (tags.some((entry) => entry.id === tag.id)) continue;
156
+ tags.push({ id: tag.id, label: tag.label ?? "" });
157
+ }
158
+ return { product, variants, categories, tags };
159
+ }
160
+ async function findRelatedProductIds(runtime, product) {
161
+ if (!product.categories.length) return [];
162
+ const scope = buildStorefrontProductScope(runtime.ctx);
163
+ const result = await runtime.queryEngine.query(E.catalog.catalog_product, {
164
+ tenantId: scope.tenantId,
165
+ organizationId: scope.organizationId,
166
+ withDeleted: scope.withDeleted,
167
+ filters: composeStorefrontProductFilters(scope, {
168
+ [PRODUCT_SCOPE_KEYS_DOC_KEY]: { $overlap: product.categories.map((category) => categoryScopeKey(category.id)) },
169
+ id: { $nin: [product.product.id] }
170
+ }),
171
+ fields: ["id"],
172
+ sort: [{ field: "created_at", dir: SortDir.Desc }],
173
+ page: { page: 1, pageSize: STOREFRONT_RELATED_PRODUCTS_LIMIT }
174
+ });
175
+ return result.items.map((item) => item.id).filter((id) => typeof id === "string" && id !== product.product.id);
176
+ }
177
+ function breadcrumbAncestors(primary, ancestors) {
178
+ if (!primary) return [];
179
+ return primary.ancestorIds.flatMap((id) => {
180
+ const ancestor = ancestors.get(id);
181
+ return ancestor ? [ancestor] : [];
182
+ });
183
+ }
184
+ async function loadMedia(runtime, product) {
185
+ const { ctx } = runtime;
186
+ const attachments = await findWithDecryption(
187
+ runtime.em,
188
+ Attachment,
189
+ {
190
+ entityId: E.catalog.catalog_product,
191
+ recordId: product.id,
192
+ tenantId: ctx.tenantId,
193
+ organizationId: ctx.organizationId
194
+ },
195
+ { fields: ["id", "url", "createdAt", "partitionCode", "mimeType"], orderBy: { createdAt: "asc" } },
196
+ runtime.decryptionScope
197
+ );
198
+ const images = attachments.filter(
199
+ (attachment) => nonEmptyString(attachment.url) !== null && isImageMimeType(attachment.mimeType)
200
+ );
201
+ const publicPartitions = await loadPublicPartitionCodes(
202
+ runtime.em,
203
+ ctx,
204
+ Array.from(new Set(images.map((attachment) => attachment.partitionCode)))
205
+ );
206
+ const defaultMediaId = product.defaultMediaId ?? null;
207
+ return images.filter((attachment) => publicPartitions.has(attachment.partitionCode)).map((attachment, index) => ({ attachment, index })).sort(
208
+ (left, right) => Number(right.attachment.id === defaultMediaId) - Number(left.attachment.id === defaultMediaId) || left.index - right.index
209
+ ).map(({ attachment }, sortOrder) => ({ id: attachment.id, url: attachment.url, alt: null, sortOrder }));
210
+ }
211
+ function detailDefaultMediaUrl(product, media) {
212
+ const defaultMediaId = product.defaultMediaId ?? null;
213
+ const stored = nonEmptyString(product.defaultMediaUrl);
214
+ if (defaultMediaId && stored && media.some((entry) => entry.id === defaultMediaId)) return stored;
215
+ return media[0]?.url ?? null;
216
+ }
217
+ function localizeOptionSchema(product, ctx, overlay, locales) {
218
+ const template = product.optionSchemaTemplate;
219
+ if (!template || typeof template !== "object" || template.deletedAt || template.isActive === false) return null;
220
+ if (template.tenantId !== ctx.tenantId || template.organizationId !== ctx.organizationId) return null;
221
+ const schema = template.schema;
222
+ if (!schema || typeof schema !== "object" || !Array.isArray(schema.options)) return null;
223
+ return {
224
+ ...schema,
225
+ name: localize(schema.name ?? template.name, overlay, "name", locales),
226
+ description: localize(schema.description ?? template.description, overlay, "description", locales),
227
+ options: schema.options.map((option) => ({
228
+ ...option,
229
+ label: localize(option.label, overlay, optionLabelTranslationField(option.code), locales) ?? option.label,
230
+ ...Array.isArray(option.choices) ? {
231
+ choices: option.choices.map((choice) => ({
232
+ ...choice,
233
+ label: localize(choice.label, overlay, optionChoiceLabelTranslationField(option.code, choice.code), locales) ?? choice.label ?? null
234
+ }))
235
+ } : {}
236
+ }))
237
+ };
238
+ }
239
+ function sanitizeDescription(value) {
240
+ if (value === null) return null;
241
+ const sanitized = sanitizeRichTextHtml(value);
242
+ return sanitized.trim().length ? sanitized : null;
243
+ }
244
+ function relatedListItems(related, context) {
245
+ const items = [];
246
+ for (const entry of related) {
247
+ const { product } = entry;
248
+ const resolved = context.availability.get(storefrontAvailabilityKey({ productId: product.id, variantId: null }));
249
+ const availability = toStorefrontAvailability(resolved?.result ?? null);
250
+ if (resolved?.policy?.hideWhenOutOfStock?.value === true && availability.state === "out_of_stock") continue;
251
+ items.push(
252
+ buildStorefrontListItem(
253
+ {
254
+ id: product.id,
255
+ handle: product.handle,
256
+ title: product.title,
257
+ subtitle: product.subtitle,
258
+ defaultMediaUrl: context.mediaUrls.get(product.id) ?? null,
259
+ productType: product.productType,
260
+ isConfigurable: product.isConfigurable
261
+ },
262
+ {
263
+ locales: context.locales,
264
+ overlay: context.productTranslations?.get(product.id),
265
+ variantCount: entry.variants.length,
266
+ categories: entry.categories,
267
+ categoryTranslations: context.categoryTranslations,
268
+ tags: entry.tags,
269
+ tagTranslations: context.tagTranslations,
270
+ pricing: context.pricing.get(product.id),
271
+ availability
272
+ }
273
+ )
274
+ );
275
+ }
276
+ return items;
277
+ }
278
+ async function getStorefrontProductDetail(container, ctx, idOrHandle, options = {}) {
279
+ const em = tryResolve(container, "em");
280
+ const queryEngine = tryResolve(container, "queryEngine");
281
+ if (!em || !queryEngine) throw new Error("[internal] ecommerce storefront detail requires em and queryEngine");
282
+ const runtime = {
283
+ container,
284
+ ctx,
285
+ em,
286
+ queryEngine,
287
+ decryptionScope: { tenantId: ctx.tenantId, organizationId: ctx.organizationId }
288
+ };
289
+ const productId = await findScopedProductId(runtime, idOrHandle);
290
+ if (!productId) return null;
291
+ const mainLoad = await loadProducts(runtime, [productId], true);
292
+ const [main] = mainLoad.products;
293
+ if (!main) return null;
294
+ const { product } = main;
295
+ const primaryCategory = main.categories[0];
296
+ const knownAncestors = new Map(mainLoad.ancestors);
297
+ for (const category of main.categories) knownAncestors.set(category.id, category);
298
+ const ancestors = breadcrumbAncestors(primaryCategory, knownAncestors);
299
+ const [relatedIds, media] = await Promise.all([findRelatedProductIds(runtime, main), loadMedia(runtime, product)]);
300
+ const { products: related } = await loadProducts(runtime, relatedIds, false, knownAncestors);
301
+ const variantIds = main.variants.map((variant) => variant.id);
302
+ const templateId = referenceId(product.optionSchemaTemplate);
303
+ const categoryIds = /* @__PURE__ */ new Set([...ancestors.map((category) => category.id)]);
304
+ const tagIds = /* @__PURE__ */ new Set();
305
+ for (const entry of [main, ...related]) {
306
+ for (const category of entry.categories) categoryIds.add(category.id);
307
+ for (const tag of entry.tags) tagIds.add(tag.id);
308
+ }
309
+ const availabilityTargets = [
310
+ { productId: product.id, variantId: null },
311
+ ...variantIds.map((variantId) => ({ productId: product.id, variantId })),
312
+ ...related.map((entry) => ({ productId: entry.product.id, variantId: null }))
313
+ ];
314
+ const [pricing, translations, availability, relatedMediaUrls] = await Promise.all([
315
+ resolveStorefrontPrices(
316
+ container,
317
+ ctx,
318
+ [
319
+ { productId: product.id, variantIds },
320
+ ...related.map((entry) => ({ productId: entry.product.id, variantIds: entry.variants.map((variant) => variant.id) }))
321
+ ],
322
+ { detail: true, tierProductIds: [product.id], date: options.date }
323
+ ),
324
+ loadStorefrontTranslations(em, ctx, [
325
+ { entityType: STOREFRONT_PRODUCT_ENTITY_TYPE, ids: [product.id, ...related.map((entry) => entry.product.id)] },
326
+ { entityType: STOREFRONT_VARIANT_ENTITY_TYPE, ids: variantIds },
327
+ { entityType: STOREFRONT_CATEGORY_ENTITY_TYPE, ids: Array.from(categoryIds) },
328
+ { entityType: STOREFRONT_TAG_ENTITY_TYPE, ids: Array.from(tagIds) },
329
+ { entityType: STOREFRONT_OPTION_SCHEMA_ENTITY_TYPE, ids: templateId ? [templateId] : [] }
330
+ ]),
331
+ resolveStorefrontAvailability(container, ctx, em, availabilityTargets),
332
+ resolvePublicDefaultMediaUrls(
333
+ em,
334
+ ctx,
335
+ related.map((entry) => ({
336
+ id: entry.product.id,
337
+ defaultMediaId: entry.product.defaultMediaId,
338
+ defaultMediaUrl: entry.product.defaultMediaUrl
339
+ }))
340
+ )
341
+ ]);
342
+ const locales = localeChain(ctx, options.locale);
343
+ const productTranslations = translations.get(STOREFRONT_PRODUCT_ENTITY_TYPE);
344
+ const variantTranslations = translations.get(STOREFRONT_VARIANT_ENTITY_TYPE);
345
+ const categoryTranslations = translations.get(STOREFRONT_CATEGORY_ENTITY_TYPE);
346
+ const tagTranslations = translations.get(STOREFRONT_TAG_ENTITY_TYPE);
347
+ const overlay = productTranslations?.get(product.id);
348
+ const productPricing = pricing.get(product.id);
349
+ const productAvailability = availability.get(storefrontAvailabilityKey({ productId: product.id, variantId: null }));
350
+ const policy = productAvailability?.policy ?? null;
351
+ const listItem = buildStorefrontListItem(
352
+ {
353
+ id: product.id,
354
+ handle: product.handle,
355
+ title: product.title,
356
+ subtitle: product.subtitle,
357
+ defaultMediaUrl: detailDefaultMediaUrl(product, media),
358
+ productType: product.productType,
359
+ isConfigurable: product.isConfigurable
360
+ },
361
+ {
362
+ locales,
363
+ overlay,
364
+ variantCount: main.variants.length,
365
+ categories: [],
366
+ tags: main.tags,
367
+ tagTranslations,
368
+ pricing: productPricing,
369
+ availability: toStorefrontAvailability(productAvailability?.result ?? null)
370
+ }
371
+ );
372
+ const categoryName = (category) => localize(category.name, categoryTranslations?.get(category.id), "name", locales) ?? category.name;
373
+ const variants = main.variants.map((variant) => {
374
+ const variantAvailability = availability.get(storefrontAvailabilityKey({ productId: product.id, variantId: variant.id }));
375
+ return {
376
+ id: variant.id,
377
+ name: localize(variant.name, variantTranslations?.get(variant.id), "name", locales) ?? nonEmptyString(variant.sku) ?? "",
378
+ sku: nonEmptyString(variant.sku),
379
+ optionValues: stringRecord(variant.optionValues),
380
+ isDefault: variant.isDefault === true,
381
+ price: productPricing?.variantPrices.get(variant.id) ?? null,
382
+ priceTiers: productPricing?.variantPriceTiers.get(variant.id) ?? [],
383
+ availability: toStorefrontAvailability(variantAvailability?.result ?? null),
384
+ dimensions: toDimensions(variant.dimensions),
385
+ weightValue: toNumber(variant.weightValue),
386
+ weightUnit: nonEmptyString(variant.weightUnit)
387
+ };
388
+ });
389
+ const requestedVariant = options.variantId ? variants.find((variant) => variant.id === options.variantId) : void 0;
390
+ const defaultVariant = variants.find((variant) => variant.isDefault);
391
+ const selectedVariant = requestedVariant ?? defaultVariant;
392
+ return {
393
+ ...listItem,
394
+ description: sanitizeDescription(localize(product.description, overlay, "description", locales)),
395
+ sku: nonEmptyString(product.sku),
396
+ media,
397
+ dimensions: toDimensions(product.dimensions),
398
+ weightValue: toNumber(product.weightValue),
399
+ weightUnit: nonEmptyString(product.weightUnit),
400
+ categories: main.categories.map((category) => ({
401
+ id: category.id,
402
+ name: categoryName(category),
403
+ slug: category.slug,
404
+ ancestorIds: category.ancestorIds
405
+ })),
406
+ breadcrumb: primaryCategory ? [...ancestors, primaryCategory].map((category) => ({
407
+ id: category.id,
408
+ name: categoryName(category),
409
+ slug: category.slug
410
+ })) : [],
411
+ optionSchema: localizeOptionSchema(
412
+ product,
413
+ ctx,
414
+ templateId ? translations.get(STOREFRONT_OPTION_SCHEMA_ENTITY_TYPE)?.get(templateId) : void 0,
415
+ locales
416
+ ),
417
+ variants,
418
+ selectedVariantId: selectedVariant?.id ?? null,
419
+ quantityRules: {
420
+ minOrderQuantity: policy?.minOrderQuantity?.value ?? toNumber(product.minOrderQty),
421
+ maxOrderQuantity: policy?.maxOrderQuantity?.value ?? toNumber(product.maxOrderQty),
422
+ quantityIncrement: policy?.quantityIncrement?.value ?? toNumber(product.orderQtyIncrement)
423
+ },
424
+ priceTiers: selectedVariant ? selectedVariant.priceTiers : productPricing?.priceTiers ?? [],
425
+ relatedProducts: relatedListItems(related, {
426
+ locales,
427
+ productTranslations,
428
+ categoryTranslations,
429
+ tagTranslations,
430
+ pricing,
431
+ availability,
432
+ mediaUrls: relatedMediaUrls
433
+ }),
434
+ seo: {
435
+ title: localize(product.seoTitle, overlay, "seoTitle", locales),
436
+ description: localize(product.seoDescription, overlay, "seoDescription", locales),
437
+ canonicalUrl: nonEmptyString(product.canonicalUrl)
438
+ }
439
+ };
440
+ }
441
+ export {
442
+ STOREFRONT_RELATED_PRODUCTS_LIMIT,
443
+ getStorefrontProductDetail,
444
+ optionChoiceLabelTranslationField,
445
+ optionLabelTranslationField
446
+ };
447
+ //# sourceMappingURL=storefrontDetail.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/ecommerce/lib/storefrontDetail.ts"],
4
+ "sourcesContent": ["import type { AwilixContainer } from 'awilix'\nimport { LoadStrategy, type EntityManager } from '@mikro-orm/postgresql'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { sanitizeRichTextHtml } from '@open-mercato/shared/lib/html/sanitizeRichText'\nimport { SortDir, type QueryEngine, type Where } from '@open-mercato/shared/lib/query/types'\nimport { E } from '#generated/entities.ids.generated'\nimport { Attachment } from '@open-mercato/core/modules/attachments/data/entities'\nimport {\n CatalogProduct,\n CatalogProductCategory,\n CatalogProductVariant,\n} from '@open-mercato/core/modules/catalog/data/entities'\nimport type { CatalogProductOptionSchema } from '@open-mercato/core/modules/catalog/data/types'\nimport { PRODUCT_SCOPE_KEYS_DOC_KEY, categoryScopeKey } from '@open-mercato/core/modules/catalog/lib/productScopeKeys'\nimport {\n optionChoiceLabelTranslationField,\n optionLabelTranslationField,\n} from '@open-mercato/core/modules/catalog/lib/optionSchemaTranslations'\nimport { buildStorefrontProductScope, composeStorefrontProductFilters } from './storefrontProductScope'\nimport {\n resolveStorefrontPrices,\n type StorefrontPrice,\n type StorefrontPriceTier,\n} from './storefrontPricing'\nimport {\n STOREFRONT_CATEGORY_ENTITY_TYPE,\n STOREFRONT_OPTION_SCHEMA_ENTITY_TYPE,\n STOREFRONT_PRODUCT_ENTITY_TYPE,\n STOREFRONT_TAG_ENTITY_TYPE,\n STOREFRONT_VARIANT_ENTITY_TYPE,\n buildStorefrontListItem,\n isCategoryInAssortment,\n isImageMimeType,\n loadPublicPartitionCodes,\n resolvePublicDefaultMediaUrls,\n selectCategoriesWithActiveAncestors,\n type ActiveCategoryAncestor,\n loadStorefrontTranslations,\n localeChain,\n localize,\n nonEmptyString,\n referenceId,\n resolveStorefrontAvailability,\n storefrontAvailabilityKey,\n stringList,\n toStorefrontAvailability,\n tryResolve,\n type StorefrontAvailability,\n type StorefrontAvailabilityTarget,\n type StorefrontProductListItem,\n type TagRef,\n type TranslationMap,\n type TranslationOverlay,\n} from './storefrontCatalogSupport'\nimport type { StoreContext } from './types'\n\nexport { optionChoiceLabelTranslationField, optionLabelTranslationField }\n\n/** Storefront Public API \u00A75.2: `relatedProducts` is capped at 8. */\nexport const STOREFRONT_RELATED_PRODUCTS_LIMIT = 8\n\nconst UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i\n\nexport type StorefrontDimensions = {\n length: number | null\n width: number | null\n height: number | null\n unit: string | null\n}\n\nexport type StorefrontProductMedia = { id: string; url: string; alt: string | null; sortOrder: number }\n\nexport type StorefrontDetailCategory = { id: string; name: string; slug: string | null; ancestorIds: string[] }\n\nexport type StorefrontBreadcrumbEntry = { id: string; name: string; slug: string | null }\n\nexport type StorefrontProductVariant = {\n id: string\n name: string\n sku: string | null\n optionValues: Record<string, string>\n isDefault: boolean\n price: StorefrontPrice | null\n priceTiers: StorefrontPriceTier[]\n availability: StorefrontAvailability\n dimensions: StorefrontDimensions | null\n weightValue: number | null\n weightUnit: string | null\n}\n\nexport type StorefrontQuantityRules = {\n minOrderQuantity: number | null\n maxOrderQuantity: number | null\n quantityIncrement: number | null\n}\n\nexport type StorefrontProductSeo = { title: string | null; description: string | null; canonicalUrl: string | null }\n\nexport type StorefrontProductDetail = Omit<StorefrontProductListItem, 'categories'> & {\n description: string | null\n sku: string | null\n media: StorefrontProductMedia[]\n dimensions: StorefrontDimensions | null\n weightValue: number | null\n weightUnit: string | null\n categories: StorefrontDetailCategory[]\n breadcrumb: StorefrontBreadcrumbEntry[]\n optionSchema: CatalogProductOptionSchema | null\n variants: StorefrontProductVariant[]\n /** The requested `variantId` when it is one of `variants`, else the default variant, else null. */\n selectedVariantId: string | null\n quantityRules: StorefrontQuantityRules\n /** Quantity tiers of `selectedVariantId` when one is selected, else of the product. */\n priceTiers: StorefrontPriceTier[]\n relatedProducts: StorefrontProductListItem[]\n seo: StorefrontProductSeo\n}\n\nexport type GetStorefrontProductDetailOptions = {\n variantId?: string | null\n /** Overrides `ctx.effectiveLocale` for the overlay chain when it is one of the store's supported locales. */\n locale?: string | null\n date?: Date\n}\n\ntype DetailRuntime = {\n container: AwilixContainer\n ctx: StoreContext\n em: EntityManager\n queryEngine: QueryEngine\n decryptionScope: { tenantId: string; organizationId: string }\n}\n\ntype VisibleCategory = {\n id: string\n name: string\n slug: string | null\n ancestorIds: string[]\n}\n\ntype LoadedProduct = {\n product: CatalogProduct\n variants: CatalogProductVariant[]\n categories: VisibleCategory[]\n tags: TagRef[]\n}\n\nfunction collectionItems<T>(value: unknown): T[] {\n if (Array.isArray(value)) return value as T[]\n if (value && typeof value === 'object' && 'getItems' in value) {\n const getItems = (value as { getItems: unknown }).getItems\n if (typeof getItems === 'function') {\n try {\n return getItems.call(value) as T[]\n } catch {\n return []\n }\n }\n }\n return []\n}\n\nfunction toNumber(value: unknown): number | null {\n if (value === null || value === undefined || value === '') return null\n const numeric = typeof value === 'number' ? value : Number(value)\n return Number.isFinite(numeric) ? numeric : null\n}\n\nfunction toDimensions(value: unknown): StorefrontDimensions | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return null\n const source = value as Record<string, unknown>\n const dimensions: StorefrontDimensions = {\n length: toNumber(source.length ?? source.depth),\n width: toNumber(source.width),\n height: toNumber(source.height),\n unit: nonEmptyString(source.unit),\n }\n const empty = dimensions.length === null && dimensions.width === null && dimensions.height === null\n return empty && dimensions.unit === null ? null : dimensions\n}\n\nfunction stringRecord(value: unknown): Record<string, string> {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return {}\n const entries = Object.entries(value as Record<string, unknown>).filter(\n (entry): entry is [string, string] => typeof entry[1] === 'string',\n )\n return Object.fromEntries(entries)\n}\n\nfunction lookupClause(idOrHandle: string): Where {\n const value = idOrHandle.trim()\n return UUID_PATTERN.test(value) ? { id: { $eq: value } } : { handle: { $eq: value } }\n}\n\n/**\n * The detail lookup: the id/handle match AND the full storefront scope in one query (\u00A73.3, R4), so\n * a restricted, inactive, deleted, other-tenant or nonexistent product all end here with the same\n * single empty query and nothing else runs.\n */\nasync function findScopedProductId(runtime: DetailRuntime, idOrHandle: string): Promise<string | null> {\n const scope = buildStorefrontProductScope(runtime.ctx)\n const result = await runtime.queryEngine.query<{ id: string }>(E.catalog.catalog_product, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n withDeleted: scope.withDeleted,\n filters: composeStorefrontProductFilters(scope, lookupClause(idOrHandle)),\n fields: ['id'],\n page: { page: 1, pageSize: 1 },\n })\n const [first] = result.items\n return first && typeof first.id === 'string' ? first.id : null\n}\n\n/**\n * Products with their variants, category and tag assignments (and, for the detail product, the\n * option schema template) in one joined query \u2014 the row count grows with variants \u00D7 assignments of\n * the loaded products, the query count does not.\n */\nasync function loadProducts(\n runtime: DetailRuntime,\n ids: string[],\n withOptionSchema: boolean,\n knownAncestors: Map<string, ActiveCategoryAncestor> = new Map(),\n): Promise<{ products: LoadedProduct[]; ancestors: Map<string, ActiveCategoryAncestor> }> {\n if (!ids.length) return { products: [], ancestors: knownAncestors }\n const { ctx } = runtime\n const populate: Array<'variants' | 'categoryAssignments.category' | 'tagAssignments.tag' | 'optionSchemaTemplate'> = [\n 'variants',\n 'categoryAssignments.category',\n 'tagAssignments.tag',\n ]\n if (withOptionSchema) populate.push('optionSchemaTemplate')\n const products = await findWithDecryption(\n runtime.em,\n CatalogProduct,\n { id: { $in: ids }, tenantId: ctx.tenantId, organizationId: ctx.organizationId, deletedAt: null, isActive: true },\n { populate, strategy: LoadStrategy.JOINED },\n runtime.decryptionScope,\n )\n const byId = new Map(products.map((product) => [product.id, product]))\n const loaded = ids.flatMap((id) => {\n const product = byId.get(id)\n return product ? [toLoadedProduct(runtime, product)] : []\n })\n const categories = loaded.flatMap((entry) => entry.categories)\n const { visibleIds, ancestors } = await selectCategoriesWithActiveAncestors(runtime.em, ctx, categories, {\n activeIds: categories.map((category) => category.id),\n ancestors: knownAncestors,\n })\n return {\n products: loaded.map((entry) => ({\n ...entry,\n categories: entry.categories.filter((category) => visibleIds.has(category.id)),\n })),\n ancestors,\n }\n}\n\nfunction toLoadedProduct(runtime: DetailRuntime, product: CatalogProduct): LoadedProduct {\n const { ctx } = runtime\n const variants = collectionItems<CatalogProductVariant>(product.variants)\n .filter((variant) => !variant.deletedAt && variant.isActive !== false)\n .map((variant, index) => ({ variant, index }))\n .sort(\n (left, right) =>\n Number(right.variant.isDefault === true) - Number(left.variant.isDefault === true) || left.index - right.index,\n )\n .map((entry) => entry.variant)\n const categories: VisibleCategory[] = []\n const assignments = collectionItems<{ position?: number | null; category?: unknown }>(product.categoryAssignments)\n .map((assignment, index) => ({ assignment, index }))\n .sort((left, right) => (left.assignment.position ?? 0) - (right.assignment.position ?? 0) || left.index - right.index)\n for (const { assignment } of assignments) {\n const category = assignment.category as CatalogProductCategory | null | undefined\n if (!category || typeof category !== 'object' || category.deletedAt || category.isActive === false) continue\n if (category.tenantId !== ctx.tenantId || category.organizationId !== ctx.organizationId) continue\n const ancestorIds = stringList(category.ancestorIds)\n const lineage = { id: category.id, ancestorIds, descendantIds: stringList(category.descendantIds) }\n if (!isCategoryInAssortment(lineage, ctx.buyer.assortmentScope)) continue\n if (categories.some((entry) => entry.id === category.id)) continue\n categories.push({ id: category.id, name: category.name, slug: category.slug ?? null, ancestorIds })\n }\n const tags: TagRef[] = []\n for (const assignment of collectionItems<{ tag?: unknown }>(product.tagAssignments)) {\n const tag = assignment.tag as { id?: string; label?: string; tenantId?: string; organizationId?: string } | null\n if (!tag || typeof tag !== 'object' || typeof tag.id !== 'string') continue\n if (tag.tenantId !== ctx.tenantId || tag.organizationId !== ctx.organizationId) continue\n if (tags.some((entry) => entry.id === tag.id)) continue\n tags.push({ id: tag.id, label: tag.label ?? '' })\n }\n return { product, variants, categories, tags }\n}\n\nasync function findRelatedProductIds(runtime: DetailRuntime, product: LoadedProduct): Promise<string[]> {\n if (!product.categories.length) return []\n const scope = buildStorefrontProductScope(runtime.ctx)\n const result = await runtime.queryEngine.query<{ id: string }>(E.catalog.catalog_product, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n withDeleted: scope.withDeleted,\n filters: composeStorefrontProductFilters(scope, {\n [PRODUCT_SCOPE_KEYS_DOC_KEY]: { $overlap: product.categories.map((category) => categoryScopeKey(category.id)) },\n id: { $nin: [product.product.id] },\n }),\n fields: ['id'],\n sort: [{ field: 'created_at', dir: SortDir.Desc }],\n page: { page: 1, pageSize: STOREFRONT_RELATED_PRODUCTS_LIMIT },\n })\n return result.items\n .map((item) => item.id)\n .filter((id): id is string => typeof id === 'string' && id !== product.product.id)\n}\n\nfunction breadcrumbAncestors(\n primary: VisibleCategory | undefined,\n ancestors: Map<string, ActiveCategoryAncestor>,\n): VisibleCategory[] {\n if (!primary) return []\n return primary.ancestorIds.flatMap((id) => {\n const ancestor = ancestors.get(id)\n return ancestor ? [ancestor] : []\n })\n}\n\n/**\n * The product's gallery: only `image/*` attachments stored in a public partition, the only files an\n * anonymous storefront visitor can fetch; private-partition files and documents are never listed.\n */\nasync function loadMedia(runtime: DetailRuntime, product: CatalogProduct): Promise<StorefrontProductMedia[]> {\n const { ctx } = runtime\n const attachments = await findWithDecryption(\n runtime.em,\n Attachment,\n {\n entityId: E.catalog.catalog_product,\n recordId: product.id,\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n },\n { fields: ['id', 'url', 'createdAt', 'partitionCode', 'mimeType'], orderBy: { createdAt: 'asc' } },\n runtime.decryptionScope,\n )\n const images = attachments.filter(\n (attachment) => nonEmptyString(attachment.url) !== null && isImageMimeType(attachment.mimeType),\n )\n const publicPartitions = await loadPublicPartitionCodes(\n runtime.em,\n ctx,\n Array.from(new Set(images.map((attachment) => attachment.partitionCode))),\n )\n const defaultMediaId = product.defaultMediaId ?? null\n return images\n .filter((attachment) => publicPartitions.has(attachment.partitionCode))\n .map((attachment, index) => ({ attachment, index }))\n .sort(\n (left, right) =>\n Number(right.attachment.id === defaultMediaId) - Number(left.attachment.id === defaultMediaId) ||\n left.index - right.index,\n )\n .map(({ attachment }, sortOrder) => ({ id: attachment.id, url: attachment.url, alt: null, sortOrder }))\n}\n\n/**\n * The detail's `defaultMediaUrl`, consistent with its public gallery: the stored URL while the\n * default media is in `media`, otherwise the first public image, otherwise `null`.\n */\nfunction detailDefaultMediaUrl(product: CatalogProduct, media: StorefrontProductMedia[]): string | null {\n const defaultMediaId = product.defaultMediaId ?? null\n const stored = nonEmptyString(product.defaultMediaUrl)\n if (defaultMediaId && stored && media.some((entry) => entry.id === defaultMediaId)) return stored\n return media[0]?.url ?? null\n}\n\nfunction localizeOptionSchema(\n product: CatalogProduct,\n ctx: StoreContext,\n overlay: TranslationOverlay | undefined,\n locales: string[],\n): CatalogProductOptionSchema | null {\n const template = product.optionSchemaTemplate\n if (!template || typeof template !== 'object' || template.deletedAt || template.isActive === false) return null\n if (template.tenantId !== ctx.tenantId || template.organizationId !== ctx.organizationId) return null\n const schema = template.schema\n if (!schema || typeof schema !== 'object' || !Array.isArray(schema.options)) return null\n return {\n ...schema,\n name: localize(schema.name ?? template.name, overlay, 'name', locales),\n description: localize(schema.description ?? template.description, overlay, 'description', locales),\n options: schema.options.map((option) => ({\n ...option,\n label: localize(option.label, overlay, optionLabelTranslationField(option.code), locales) ?? option.label,\n ...(Array.isArray(option.choices)\n ? {\n choices: option.choices.map((choice) => ({\n ...choice,\n label:\n localize(choice.label, overlay, optionChoiceLabelTranslationField(option.code, choice.code), locales) ??\n choice.label ??\n null,\n })),\n }\n : {}),\n })),\n }\n}\n\nfunction sanitizeDescription(value: string | null): string | null {\n if (value === null) return null\n const sanitized = sanitizeRichTextHtml(value)\n return sanitized.trim().length ? sanitized : null\n}\n\nfunction relatedListItems(\n related: LoadedProduct[],\n context: {\n locales: string[]\n productTranslations?: TranslationMap\n categoryTranslations?: TranslationMap\n tagTranslations?: TranslationMap\n pricing: Awaited<ReturnType<typeof resolveStorefrontPrices>>\n availability: Awaited<ReturnType<typeof resolveStorefrontAvailability>>\n mediaUrls: Map<string, string | null>\n },\n): StorefrontProductListItem[] {\n const items: StorefrontProductListItem[] = []\n for (const entry of related) {\n const { product } = entry\n const resolved = context.availability.get(storefrontAvailabilityKey({ productId: product.id, variantId: null }))\n const availability = toStorefrontAvailability(resolved?.result ?? null)\n if (resolved?.policy?.hideWhenOutOfStock?.value === true && availability.state === 'out_of_stock') continue\n items.push(\n buildStorefrontListItem(\n {\n id: product.id,\n handle: product.handle,\n title: product.title,\n subtitle: product.subtitle,\n defaultMediaUrl: context.mediaUrls.get(product.id) ?? null,\n productType: product.productType,\n isConfigurable: product.isConfigurable,\n },\n {\n locales: context.locales,\n overlay: context.productTranslations?.get(product.id),\n variantCount: entry.variants.length,\n categories: entry.categories,\n categoryTranslations: context.categoryTranslations,\n tags: entry.tags,\n tagTranslations: context.tagTranslations,\n pricing: context.pricing.get(product.id),\n availability,\n },\n ),\n )\n }\n return items\n}\n\n/**\n * `GET /products/:idOrHandle` (Storefront Public API rev 4 \u00A74.2, \u00A75.2, \u00A77, \u00A710).\n *\n * Returns `null` \u2014 mapped by the route to one 404 body \u2014 for a product that is nonexistent,\n * inactive, deleted, of another tenant or outside the buyer's effective assortment: the id/handle\n * match and the assortment invariant are one query, and nothing else runs when it is empty (R4).\n * A found product is hydrated with a constant number of batched queries regardless of its variant\n * count: one joined product load, its active-ancestor lookup (which also yields the breadcrumb),\n * related-product lookup and load (plus an ancestor lookup only for ancestors not already known), media,\n * the related cards' public default-media check (one query, skipped when none has default media),\n * one price query (\u00A76.1, per variant and with tiers), one translation query for every overlaid\n * entity type, one availability state and one policy resolution. `relatedProducts` are up to 8\n * products sharing a category (or a category's subtree) within the same scope.\n */\nexport async function getStorefrontProductDetail(\n container: AwilixContainer,\n ctx: StoreContext,\n idOrHandle: string,\n options: GetStorefrontProductDetailOptions = {},\n): Promise<StorefrontProductDetail | null> {\n const em = tryResolve<EntityManager>(container, 'em')\n const queryEngine = tryResolve<QueryEngine>(container, 'queryEngine')\n if (!em || !queryEngine) throw new Error('[internal] ecommerce storefront detail requires em and queryEngine')\n const runtime: DetailRuntime = {\n container,\n ctx,\n em,\n queryEngine,\n decryptionScope: { tenantId: ctx.tenantId, organizationId: ctx.organizationId },\n }\n\n const productId = await findScopedProductId(runtime, idOrHandle)\n if (!productId) return null\n const mainLoad = await loadProducts(runtime, [productId], true)\n const [main] = mainLoad.products\n if (!main) return null\n const { product } = main\n const primaryCategory = main.categories[0]\n const knownAncestors = new Map(mainLoad.ancestors)\n for (const category of main.categories) knownAncestors.set(category.id, category)\n const ancestors = breadcrumbAncestors(primaryCategory, knownAncestors)\n\n const [relatedIds, media] = await Promise.all([findRelatedProductIds(runtime, main), loadMedia(runtime, product)])\n const { products: related } = await loadProducts(runtime, relatedIds, false, knownAncestors)\n const variantIds = main.variants.map((variant) => variant.id)\n const templateId = referenceId(product.optionSchemaTemplate)\n\n const categoryIds = new Set<string>([...ancestors.map((category) => category.id)])\n const tagIds = new Set<string>()\n for (const entry of [main, ...related]) {\n for (const category of entry.categories) categoryIds.add(category.id)\n for (const tag of entry.tags) tagIds.add(tag.id)\n }\n const availabilityTargets: StorefrontAvailabilityTarget[] = [\n { productId: product.id, variantId: null },\n ...variantIds.map((variantId) => ({ productId: product.id, variantId })),\n ...related.map((entry) => ({ productId: entry.product.id, variantId: null })),\n ]\n\n const [pricing, translations, availability, relatedMediaUrls] = await Promise.all([\n resolveStorefrontPrices(\n container,\n ctx,\n [\n { productId: product.id, variantIds },\n ...related.map((entry) => ({ productId: entry.product.id, variantIds: entry.variants.map((variant) => variant.id) })),\n ],\n { detail: true, tierProductIds: [product.id], date: options.date },\n ),\n loadStorefrontTranslations(em, ctx, [\n { entityType: STOREFRONT_PRODUCT_ENTITY_TYPE, ids: [product.id, ...related.map((entry) => entry.product.id)] },\n { entityType: STOREFRONT_VARIANT_ENTITY_TYPE, ids: variantIds },\n { entityType: STOREFRONT_CATEGORY_ENTITY_TYPE, ids: Array.from(categoryIds) },\n { entityType: STOREFRONT_TAG_ENTITY_TYPE, ids: Array.from(tagIds) },\n { entityType: STOREFRONT_OPTION_SCHEMA_ENTITY_TYPE, ids: templateId ? [templateId] : [] },\n ]),\n resolveStorefrontAvailability(container, ctx, em, availabilityTargets),\n resolvePublicDefaultMediaUrls(\n em,\n ctx,\n related.map((entry) => ({\n id: entry.product.id,\n defaultMediaId: entry.product.defaultMediaId,\n defaultMediaUrl: entry.product.defaultMediaUrl,\n })),\n ),\n ])\n\n const locales = localeChain(ctx, options.locale)\n const productTranslations = translations.get(STOREFRONT_PRODUCT_ENTITY_TYPE)\n const variantTranslations = translations.get(STOREFRONT_VARIANT_ENTITY_TYPE)\n const categoryTranslations = translations.get(STOREFRONT_CATEGORY_ENTITY_TYPE)\n const tagTranslations = translations.get(STOREFRONT_TAG_ENTITY_TYPE)\n const overlay = productTranslations?.get(product.id)\n const productPricing = pricing.get(product.id)\n const productAvailability = availability.get(storefrontAvailabilityKey({ productId: product.id, variantId: null }))\n const policy = productAvailability?.policy ?? null\n\n const listItem = buildStorefrontListItem(\n {\n id: product.id,\n handle: product.handle,\n title: product.title,\n subtitle: product.subtitle,\n defaultMediaUrl: detailDefaultMediaUrl(product, media),\n productType: product.productType,\n isConfigurable: product.isConfigurable,\n },\n {\n locales,\n overlay,\n variantCount: main.variants.length,\n categories: [],\n tags: main.tags,\n tagTranslations,\n pricing: productPricing,\n availability: toStorefrontAvailability(productAvailability?.result ?? null),\n },\n )\n const categoryName = (category: VisibleCategory) =>\n localize(category.name, categoryTranslations?.get(category.id), 'name', locales) ?? category.name\n\n const variants: StorefrontProductVariant[] = main.variants.map((variant) => {\n const variantAvailability = availability.get(storefrontAvailabilityKey({ productId: product.id, variantId: variant.id }))\n return {\n id: variant.id,\n name:\n localize(variant.name, variantTranslations?.get(variant.id), 'name', locales) ??\n nonEmptyString(variant.sku) ??\n '',\n sku: nonEmptyString(variant.sku),\n optionValues: stringRecord(variant.optionValues),\n isDefault: variant.isDefault === true,\n price: productPricing?.variantPrices.get(variant.id) ?? null,\n priceTiers: productPricing?.variantPriceTiers.get(variant.id) ?? [],\n availability: toStorefrontAvailability(variantAvailability?.result ?? null),\n dimensions: toDimensions(variant.dimensions),\n weightValue: toNumber(variant.weightValue),\n weightUnit: nonEmptyString(variant.weightUnit),\n }\n })\n const requestedVariant = options.variantId ? variants.find((variant) => variant.id === options.variantId) : undefined\n const defaultVariant = variants.find((variant) => variant.isDefault)\n const selectedVariant = requestedVariant ?? defaultVariant\n\n return {\n ...listItem,\n description: sanitizeDescription(localize(product.description, overlay, 'description', locales)),\n sku: nonEmptyString(product.sku),\n media,\n dimensions: toDimensions(product.dimensions),\n weightValue: toNumber(product.weightValue),\n weightUnit: nonEmptyString(product.weightUnit),\n categories: main.categories.map((category) => ({\n id: category.id,\n name: categoryName(category),\n slug: category.slug,\n ancestorIds: category.ancestorIds,\n })),\n breadcrumb: primaryCategory\n ? [...ancestors, primaryCategory].map((category) => ({\n id: category.id,\n name: categoryName(category),\n slug: category.slug,\n }))\n : [],\n optionSchema: localizeOptionSchema(\n product,\n ctx,\n templateId ? translations.get(STOREFRONT_OPTION_SCHEMA_ENTITY_TYPE)?.get(templateId) : undefined,\n locales,\n ),\n variants,\n selectedVariantId: selectedVariant?.id ?? null,\n quantityRules: {\n minOrderQuantity: policy?.minOrderQuantity?.value ?? toNumber(product.minOrderQty),\n maxOrderQuantity: policy?.maxOrderQuantity?.value ?? toNumber(product.maxOrderQty),\n quantityIncrement: policy?.quantityIncrement?.value ?? toNumber(product.orderQtyIncrement),\n },\n priceTiers: selectedVariant ? selectedVariant.priceTiers : productPricing?.priceTiers ?? [],\n relatedProducts: relatedListItems(related, {\n locales,\n productTranslations,\n categoryTranslations,\n tagTranslations,\n pricing,\n availability,\n mediaUrls: relatedMediaUrls,\n }),\n seo: {\n title: localize(product.seoTitle, overlay, 'seoTitle', locales),\n description: localize(product.seoDescription, overlay, 'seoDescription', locales),\n canonicalUrl: nonEmptyString(product.canonicalUrl),\n },\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }