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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (597) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/catalog_price_history_entry/index.js +53 -0
  3. package/dist/generated/entities/catalog_price_history_entry/index.js.map +7 -0
  4. package/dist/generated/entities/customer_group_terms/index.js +2 -0
  5. package/dist/generated/entities/customer_group_terms/index.js.map +2 -2
  6. package/dist/generated/entities/ecommerce_store/index.js +33 -0
  7. package/dist/generated/entities/ecommerce_store/index.js.map +7 -0
  8. package/dist/generated/entities/ecommerce_store_channel_binding/index.js +29 -0
  9. package/dist/generated/entities/ecommerce_store_channel_binding/index.js.map +7 -0
  10. package/dist/generated/entities/ecommerce_store_domain_binding/index.js +23 -0
  11. package/dist/generated/entities/ecommerce_store_domain_binding/index.js.map +7 -0
  12. package/dist/generated/entities.ids.generated.js +8 -1
  13. package/dist/generated/entities.ids.generated.js.map +2 -2
  14. package/dist/generated/entity-fields-registry.js +72 -0
  15. package/dist/generated/entity-fields-registry.js.map +2 -2
  16. package/dist/helpers/integration/customerGroupsFixtures.js.map +2 -2
  17. package/dist/modules/catalog/acl.js +8 -1
  18. package/dist/modules/catalog/acl.js.map +2 -2
  19. package/dist/modules/catalog/api/config/omnibus/route.js +184 -0
  20. package/dist/modules/catalog/api/config/omnibus/route.js.map +7 -0
  21. package/dist/modules/catalog/api/prices/history/route.js +131 -0
  22. package/dist/modules/catalog/api/prices/history/route.js.map +7 -0
  23. package/dist/modules/catalog/api/prices/omnibus-preview/route.js +184 -0
  24. package/dist/modules/catalog/api/prices/omnibus-preview/route.js.map +7 -0
  25. package/dist/modules/catalog/api/products/route.js +54 -245
  26. package/dist/modules/catalog/api/products/route.js.map +3 -3
  27. package/dist/modules/catalog/api/tags/route.js +6 -0
  28. package/dist/modules/catalog/api/tags/route.js.map +2 -2
  29. package/dist/modules/catalog/backend/config/catalog/page.js +3 -1
  30. package/dist/modules/catalog/backend/config/catalog/page.js.map +2 -2
  31. package/dist/modules/catalog/cli.js +80 -2
  32. package/dist/modules/catalog/cli.js.map +2 -2
  33. package/dist/modules/catalog/commands/categories.js +51 -2
  34. package/dist/modules/catalog/commands/categories.js.map +2 -2
  35. package/dist/modules/catalog/commands/prices.js +43 -1
  36. package/dist/modules/catalog/commands/prices.js.map +2 -2
  37. package/dist/modules/catalog/commands/products.js +71 -0
  38. package/dist/modules/catalog/commands/products.js.map +2 -2
  39. package/dist/modules/catalog/commands/variants.js +7 -0
  40. package/dist/modules/catalog/commands/variants.js.map +2 -2
  41. package/dist/modules/catalog/components/OmnibusSettings.js +703 -0
  42. package/dist/modules/catalog/components/OmnibusSettings.js.map +7 -0
  43. package/dist/modules/catalog/components/PriceEditorOmnibusRow.js +152 -0
  44. package/dist/modules/catalog/components/PriceEditorOmnibusRow.js.map +7 -0
  45. package/dist/modules/catalog/components/lookupSources.js +31 -0
  46. package/dist/modules/catalog/components/lookupSources.js.map +7 -0
  47. package/dist/modules/catalog/components/prices/PriceScopeSelectors.js +8 -2
  48. package/dist/modules/catalog/components/prices/PriceScopeSelectors.js.map +2 -2
  49. package/dist/modules/catalog/components/products/VariantBuilder.js +11 -1
  50. package/dist/modules/catalog/components/products/VariantBuilder.js.map +2 -2
  51. package/dist/modules/catalog/data/entities.js +122 -1
  52. package/dist/modules/catalog/data/entities.js.map +2 -2
  53. package/dist/modules/catalog/data/validators.js +78 -0
  54. package/dist/modules/catalog/data/validators.js.map +2 -2
  55. package/dist/modules/catalog/di.js +18 -0
  56. package/dist/modules/catalog/di.js.map +2 -2
  57. package/dist/modules/catalog/lib/omnibus.js +285 -0
  58. package/dist/modules/catalog/lib/omnibus.js.map +7 -0
  59. package/dist/modules/catalog/lib/omnibusBackfill.js +146 -0
  60. package/dist/modules/catalog/lib/omnibusBackfill.js.map +7 -0
  61. package/dist/modules/catalog/lib/omnibusCache.js +89 -0
  62. package/dist/modules/catalog/lib/omnibusCache.js.map +7 -0
  63. package/dist/modules/catalog/lib/omnibusConfig.js +64 -0
  64. package/dist/modules/catalog/lib/omnibusConfig.js.map +7 -0
  65. package/dist/modules/catalog/lib/omnibusHistoryQueries.js +191 -0
  66. package/dist/modules/catalog/lib/omnibusHistoryQueries.js.map +7 -0
  67. package/dist/modules/catalog/lib/omnibusPresentedEntry.js +73 -0
  68. package/dist/modules/catalog/lib/omnibusPresentedEntry.js.map +7 -0
  69. package/dist/modules/catalog/lib/omnibusProductListEnrichment.js +94 -0
  70. package/dist/modules/catalog/lib/omnibusProductListEnrichment.js.map +7 -0
  71. package/dist/modules/catalog/lib/omnibusTypes.js +115 -0
  72. package/dist/modules/catalog/lib/omnibusTypes.js.map +7 -0
  73. package/dist/modules/catalog/lib/optionSchemaTranslations.js +55 -0
  74. package/dist/modules/catalog/lib/optionSchemaTranslations.js.map +7 -0
  75. package/dist/modules/catalog/lib/priceHistoryQuery.js +103 -0
  76. package/dist/modules/catalog/lib/priceHistoryQuery.js.map +7 -0
  77. package/dist/modules/catalog/lib/pricing.js +26 -2
  78. package/dist/modules/catalog/lib/pricing.js.map +2 -2
  79. package/dist/modules/catalog/lib/productFilters.js +273 -0
  80. package/dist/modules/catalog/lib/productFilters.js.map +7 -0
  81. package/dist/modules/catalog/lib/productScopeKeys.js +105 -0
  82. package/dist/modules/catalog/lib/productScopeKeys.js.map +7 -0
  83. package/dist/modules/catalog/lib/productScopeReindex.js +73 -0
  84. package/dist/modules/catalog/lib/productScopeReindex.js.map +7 -0
  85. package/dist/modules/catalog/migrations/Migration20261005105352_catalog.js +39 -0
  86. package/dist/modules/catalog/migrations/Migration20261005105352_catalog.js.map +7 -0
  87. package/dist/modules/catalog/migrations/Migration20261005150824_catalog.js +39 -0
  88. package/dist/modules/catalog/migrations/Migration20261005150824_catalog.js.map +7 -0
  89. package/dist/modules/catalog/services/catalogOmnibusService.js +480 -0
  90. package/dist/modules/catalog/services/catalogOmnibusService.js.map +7 -0
  91. package/dist/modules/catalog/setup.js +3 -1
  92. package/dist/modules/catalog/setup.js.map +2 -2
  93. package/dist/modules/catalog/subscribers/product-scope-keys-category-deleted.js +16 -0
  94. package/dist/modules/catalog/subscribers/product-scope-keys-category-deleted.js.map +7 -0
  95. package/dist/modules/catalog/subscribers/product-scope-keys-category-updated.js +17 -0
  96. package/dist/modules/catalog/subscribers/product-scope-keys-category-updated.js.map +7 -0
  97. package/dist/modules/catalog/translations.js +10 -0
  98. package/dist/modules/catalog/translations.js.map +2 -2
  99. package/dist/modules/configs/lib/upgrade-actions.js +28 -0
  100. package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
  101. package/dist/modules/customer_groups/api/customer-groups/[id]/terms/route.js +5 -0
  102. package/dist/modules/customer_groups/api/customer-groups/[id]/terms/route.js.map +2 -2
  103. package/dist/modules/customer_groups/components/CustomerGroupTermsSection.js +111 -3
  104. package/dist/modules/customer_groups/components/CustomerGroupTermsSection.js.map +2 -2
  105. package/dist/modules/customer_groups/components/customerGroupAssortmentScope.js +43 -0
  106. package/dist/modules/customer_groups/components/customerGroupAssortmentScope.js.map +7 -0
  107. package/dist/modules/customer_groups/data/entities.js +3 -0
  108. package/dist/modules/customer_groups/data/entities.js.map +2 -2
  109. package/dist/modules/customer_groups/data/validators.js +9 -0
  110. package/dist/modules/customer_groups/data/validators.js.map +2 -2
  111. package/dist/modules/customer_groups/migrations/Migration20261005110250_customer_groups.js +17 -0
  112. package/dist/modules/customer_groups/migrations/Migration20261005110250_customer_groups.js.map +7 -0
  113. package/dist/modules/customer_groups/services/customerGroupsService.js +62 -15
  114. package/dist/modules/customer_groups/services/customerGroupsService.js.map +2 -2
  115. package/dist/modules/ecommerce/acl.js +13 -0
  116. package/dist/modules/ecommerce/acl.js.map +7 -0
  117. package/dist/modules/ecommerce/api/domain-mappings/route.js +72 -0
  118. package/dist/modules/ecommerce/api/domain-mappings/route.js.map +7 -0
  119. package/dist/modules/ecommerce/api/openapi.js +27 -0
  120. package/dist/modules/ecommerce/api/openapi.js.map +7 -0
  121. package/dist/modules/ecommerce/api/store-channel-bindings/[id]/assortment-count/route.js +109 -0
  122. package/dist/modules/ecommerce/api/store-channel-bindings/[id]/assortment-count/route.js.map +7 -0
  123. package/dist/modules/ecommerce/api/store-channel-bindings/crud.js +265 -0
  124. package/dist/modules/ecommerce/api/store-channel-bindings/crud.js.map +7 -0
  125. package/dist/modules/ecommerce/api/store-channel-bindings/route.js +67 -0
  126. package/dist/modules/ecommerce/api/store-channel-bindings/route.js.map +7 -0
  127. package/dist/modules/ecommerce/api/store-domain-bindings/crud.js +296 -0
  128. package/dist/modules/ecommerce/api/store-domain-bindings/crud.js.map +7 -0
  129. package/dist/modules/ecommerce/api/store-domain-bindings/route.js +73 -0
  130. package/dist/modules/ecommerce/api/store-domain-bindings/route.js.map +7 -0
  131. package/dist/modules/ecommerce/api/storefront/categories/[slug]/route.js +87 -0
  132. package/dist/modules/ecommerce/api/storefront/categories/[slug]/route.js.map +7 -0
  133. package/dist/modules/ecommerce/api/storefront/categories/openapiSchemas.js +50 -0
  134. package/dist/modules/ecommerce/api/storefront/categories/openapiSchemas.js.map +7 -0
  135. package/dist/modules/ecommerce/api/storefront/categories/route.js +75 -0
  136. package/dist/modules/ecommerce/api/storefront/categories/route.js.map +7 -0
  137. package/dist/modules/ecommerce/api/storefront/context/route.js +163 -0
  138. package/dist/modules/ecommerce/api/storefront/context/route.js.map +7 -0
  139. package/dist/modules/ecommerce/api/storefront/products/[idOrHandle]/route.js +87 -0
  140. package/dist/modules/ecommerce/api/storefront/products/[idOrHandle]/route.js.map +7 -0
  141. package/dist/modules/ecommerce/api/storefront/products/openapiSchemas.js +177 -0
  142. package/dist/modules/ecommerce/api/storefront/products/openapiSchemas.js.map +7 -0
  143. package/dist/modules/ecommerce/api/storefront/products/route.js +78 -0
  144. package/dist/modules/ecommerce/api/storefront/products/route.js.map +7 -0
  145. package/dist/modules/ecommerce/api/storefront/search/suggest/openapiSchemas.js +25 -0
  146. package/dist/modules/ecommerce/api/storefront/search/suggest/openapiSchemas.js.map +7 -0
  147. package/dist/modules/ecommerce/api/storefront/search/suggest/route.js +75 -0
  148. package/dist/modules/ecommerce/api/storefront/search/suggest/route.js.map +7 -0
  149. package/dist/modules/ecommerce/api/storefront/storefrontRouteSupport.js +58 -0
  150. package/dist/modules/ecommerce/api/storefront/storefrontRouteSupport.js.map +7 -0
  151. package/dist/modules/ecommerce/api/stores/[id]/branding/route.js +183 -0
  152. package/dist/modules/ecommerce/api/stores/[id]/branding/route.js.map +7 -0
  153. package/dist/modules/ecommerce/api/stores/[id]/preview-branding/route.js +92 -0
  154. package/dist/modules/ecommerce/api/stores/[id]/preview-branding/route.js.map +7 -0
  155. package/dist/modules/ecommerce/api/stores/crud.js +401 -0
  156. package/dist/modules/ecommerce/api/stores/crud.js.map +7 -0
  157. package/dist/modules/ecommerce/api/stores/route.js +64 -0
  158. package/dist/modules/ecommerce/api/stores/route.js.map +7 -0
  159. package/dist/modules/ecommerce/backend/config/ecommerce/[id]/page.js +9 -0
  160. package/dist/modules/ecommerce/backend/config/ecommerce/[id]/page.js.map +7 -0
  161. package/dist/modules/ecommerce/backend/config/ecommerce/[id]/page.meta.js +16 -0
  162. package/dist/modules/ecommerce/backend/config/ecommerce/[id]/page.meta.js.map +7 -0
  163. package/dist/modules/ecommerce/backend/config/ecommerce/page.js +9 -0
  164. package/dist/modules/ecommerce/backend/config/ecommerce/page.js.map +7 -0
  165. package/dist/modules/ecommerce/backend/config/ecommerce/page.meta.js +16 -0
  166. package/dist/modules/ecommerce/backend/config/ecommerce/page.meta.js.map +7 -0
  167. package/dist/modules/ecommerce/commands/stores.js +110 -0
  168. package/dist/modules/ecommerce/commands/stores.js.map +7 -0
  169. package/dist/modules/ecommerce/components/ChannelAssortmentCount.js +90 -0
  170. package/dist/modules/ecommerce/components/ChannelAssortmentCount.js.map +7 -0
  171. package/dist/modules/ecommerce/components/ChannelBindingFormSections.js +33 -0
  172. package/dist/modules/ecommerce/components/ChannelBindingFormSections.js.map +7 -0
  173. package/dist/modules/ecommerce/components/ChannelLookupPicker.js +11 -0
  174. package/dist/modules/ecommerce/components/ChannelLookupPicker.js.map +7 -0
  175. package/dist/modules/ecommerce/components/DomainStatusWarning.js +24 -0
  176. package/dist/modules/ecommerce/components/DomainStatusWarning.js.map +7 -0
  177. package/dist/modules/ecommerce/components/StoreAvailabilityDefaultsSection.js +190 -0
  178. package/dist/modules/ecommerce/components/StoreAvailabilityDefaultsSection.js.map +7 -0
  179. package/dist/modules/ecommerce/components/StoreBrandingControls.js +67 -0
  180. package/dist/modules/ecommerce/components/StoreBrandingControls.js.map +7 -0
  181. package/dist/modules/ecommerce/components/StoreBrandingPreview.js +69 -0
  182. package/dist/modules/ecommerce/components/StoreBrandingPreview.js.map +7 -0
  183. package/dist/modules/ecommerce/components/StoreBrandingTab.js +218 -0
  184. package/dist/modules/ecommerce/components/StoreBrandingTab.js.map +7 -0
  185. package/dist/modules/ecommerce/components/StoreChannelBindingDialog.js +270 -0
  186. package/dist/modules/ecommerce/components/StoreChannelBindingDialog.js.map +7 -0
  187. package/dist/modules/ecommerce/components/StoreChannelsTab.js +313 -0
  188. package/dist/modules/ecommerce/components/StoreChannelsTab.js.map +7 -0
  189. package/dist/modules/ecommerce/components/StoreCreateDialog.js +164 -0
  190. package/dist/modules/ecommerce/components/StoreCreateDialog.js.map +7 -0
  191. package/dist/modules/ecommerce/components/StoreDomainBindingDialog.js +162 -0
  192. package/dist/modules/ecommerce/components/StoreDomainBindingDialog.js.map +7 -0
  193. package/dist/modules/ecommerce/components/StoreDomainsTab.js +357 -0
  194. package/dist/modules/ecommerce/components/StoreDomainsTab.js.map +7 -0
  195. package/dist/modules/ecommerce/components/StoreEditPage.js +155 -0
  196. package/dist/modules/ecommerce/components/StoreEditPage.js.map +7 -0
  197. package/dist/modules/ecommerce/components/StoreGeneralTab.js +167 -0
  198. package/dist/modules/ecommerce/components/StoreGeneralTab.js.map +7 -0
  199. package/dist/modules/ecommerce/components/StoreListPage.js +290 -0
  200. package/dist/modules/ecommerce/components/StoreListPage.js.map +7 -0
  201. package/dist/modules/ecommerce/components/StoreSeoTab.js +132 -0
  202. package/dist/modules/ecommerce/components/StoreSeoTab.js.map +7 -0
  203. package/dist/modules/ecommerce/components/StoreStatusBadge.js +20 -0
  204. package/dist/modules/ecommerce/components/StoreStatusBadge.js.map +7 -0
  205. package/dist/modules/ecommerce/components/channelLookups.js +28 -0
  206. package/dist/modules/ecommerce/components/channelLookups.js.map +7 -0
  207. package/dist/modules/ecommerce/components/storeAdmin.js +41 -0
  208. package/dist/modules/ecommerce/components/storeAdmin.js.map +7 -0
  209. package/dist/modules/ecommerce/components/storeBrandingForm.js +197 -0
  210. package/dist/modules/ecommerce/components/storeBrandingForm.js.map +7 -0
  211. package/dist/modules/ecommerce/components/storeChannels.js +140 -0
  212. package/dist/modules/ecommerce/components/storeChannels.js.map +7 -0
  213. package/dist/modules/ecommerce/components/storeDomains.js +167 -0
  214. package/dist/modules/ecommerce/components/storeDomains.js.map +7 -0
  215. package/dist/modules/ecommerce/components/storeEditTabs.js +62 -0
  216. package/dist/modules/ecommerce/components/storeEditTabs.js.map +7 -0
  217. package/dist/modules/ecommerce/components/storeGeneral.js +95 -0
  218. package/dist/modules/ecommerce/components/storeGeneral.js.map +7 -0
  219. package/dist/modules/ecommerce/components/storeSeo.js +69 -0
  220. package/dist/modules/ecommerce/components/storeSeo.js.map +7 -0
  221. package/dist/modules/ecommerce/components/useChannelAssortmentCount.js +45 -0
  222. package/dist/modules/ecommerce/components/useChannelAssortmentCount.js.map +7 -0
  223. package/dist/modules/ecommerce/components/useStoreAccess.js +28 -0
  224. package/dist/modules/ecommerce/components/useStoreAccess.js.map +7 -0
  225. package/dist/modules/ecommerce/data/enrichers.js +157 -0
  226. package/dist/modules/ecommerce/data/enrichers.js.map +7 -0
  227. package/dist/modules/ecommerce/data/entities.js +197 -0
  228. package/dist/modules/ecommerce/data/entities.js.map +7 -0
  229. package/dist/modules/ecommerce/data/validators.js +406 -0
  230. package/dist/modules/ecommerce/data/validators.js.map +7 -0
  231. package/dist/modules/ecommerce/di.js +11 -0
  232. package/dist/modules/ecommerce/di.js.map +7 -0
  233. package/dist/modules/ecommerce/events.js +27 -0
  234. package/dist/modules/ecommerce/events.js.map +7 -0
  235. package/dist/modules/ecommerce/index.js +16 -0
  236. package/dist/modules/ecommerce/index.js.map +7 -0
  237. package/dist/modules/ecommerce/lib/assortmentCount.js +112 -0
  238. package/dist/modules/ecommerce/lib/assortmentCount.js.map +7 -0
  239. package/dist/modules/ecommerce/lib/brandingStyles.js +184 -0
  240. package/dist/modules/ecommerce/lib/brandingStyles.js.map +7 -0
  241. package/dist/modules/ecommerce/lib/buyerContext.js +284 -0
  242. package/dist/modules/ecommerce/lib/buyerContext.js.map +7 -0
  243. package/dist/modules/ecommerce/lib/cacheInvalidation.js +186 -0
  244. package/dist/modules/ecommerce/lib/cacheInvalidation.js.map +7 -0
  245. package/dist/modules/ecommerce/lib/cacheKeys.js +236 -0
  246. package/dist/modules/ecommerce/lib/cacheKeys.js.map +7 -0
  247. package/dist/modules/ecommerce/lib/createConflictRecheck.js +29 -0
  248. package/dist/modules/ecommerce/lib/createConflictRecheck.js.map +7 -0
  249. package/dist/modules/ecommerce/lib/crudEvents.js +94 -0
  250. package/dist/modules/ecommerce/lib/crudEvents.js.map +7 -0
  251. package/dist/modules/ecommerce/lib/crudSupport.js +64 -0
  252. package/dist/modules/ecommerce/lib/crudSupport.js.map +7 -0
  253. package/dist/modules/ecommerce/lib/domainBindingRebind.js +99 -0
  254. package/dist/modules/ecommerce/lib/domainBindingRebind.js.map +7 -0
  255. package/dist/modules/ecommerce/lib/domainMappingSummaries.js +39 -0
  256. package/dist/modules/ecommerce/lib/domainMappingSummaries.js.map +7 -0
  257. package/dist/modules/ecommerce/lib/exclusiveFlag.js +57 -0
  258. package/dist/modules/ecommerce/lib/exclusiveFlag.js.map +7 -0
  259. package/dist/modules/ecommerce/lib/references.js +70 -0
  260. package/dist/modules/ecommerce/lib/references.js.map +7 -0
  261. package/dist/modules/ecommerce/lib/seedDraftStore.js +99 -0
  262. package/dist/modules/ecommerce/lib/seedDraftStore.js.map +7 -0
  263. package/dist/modules/ecommerce/lib/storeBindingCascade.js +109 -0
  264. package/dist/modules/ecommerce/lib/storeBindingCascade.js.map +7 -0
  265. package/dist/modules/ecommerce/lib/storeBranding.js +40 -0
  266. package/dist/modules/ecommerce/lib/storeBranding.js.map +7 -0
  267. package/dist/modules/ecommerce/lib/storeBrandingRoute.js +76 -0
  268. package/dist/modules/ecommerce/lib/storeBrandingRoute.js.map +7 -0
  269. package/dist/modules/ecommerce/lib/storeContext.js +393 -0
  270. package/dist/modules/ecommerce/lib/storeContext.js.map +7 -0
  271. package/dist/modules/ecommerce/lib/storeContextService.js +43 -0
  272. package/dist/modules/ecommerce/lib/storeContextService.js.map +7 -0
  273. package/dist/modules/ecommerce/lib/storeLocale.js +60 -0
  274. package/dist/modules/ecommerce/lib/storeLocale.js.map +7 -0
  275. package/dist/modules/ecommerce/lib/storeNotifications.js +41 -0
  276. package/dist/modules/ecommerce/lib/storeNotifications.js.map +7 -0
  277. package/dist/modules/ecommerce/lib/storefrontCatalogSupport.js +248 -0
  278. package/dist/modules/ecommerce/lib/storefrontCatalogSupport.js.map +7 -0
  279. package/dist/modules/ecommerce/lib/storefrontCategories.js +241 -0
  280. package/dist/modules/ecommerce/lib/storefrontCategories.js.map +7 -0
  281. package/dist/modules/ecommerce/lib/storefrontCategoryCache.js +71 -0
  282. package/dist/modules/ecommerce/lib/storefrontCategoryCache.js.map +7 -0
  283. package/dist/modules/ecommerce/lib/storefrontDetail.js +447 -0
  284. package/dist/modules/ecommerce/lib/storefrontDetail.js.map +7 -0
  285. package/dist/modules/ecommerce/lib/storefrontFacets.js +441 -0
  286. package/dist/modules/ecommerce/lib/storefrontFacets.js.map +7 -0
  287. package/dist/modules/ecommerce/lib/storefrontOptionFilter.js +15 -0
  288. package/dist/modules/ecommerce/lib/storefrontOptionFilter.js.map +7 -0
  289. package/dist/modules/ecommerce/lib/storefrontPricing.js +393 -0
  290. package/dist/modules/ecommerce/lib/storefrontPricing.js.map +7 -0
  291. package/dist/modules/ecommerce/lib/storefrontProductCache.js +90 -0
  292. package/dist/modules/ecommerce/lib/storefrontProductCache.js.map +7 -0
  293. package/dist/modules/ecommerce/lib/storefrontProductScope.js +107 -0
  294. package/dist/modules/ecommerce/lib/storefrontProductScope.js.map +7 -0
  295. package/dist/modules/ecommerce/lib/storefrontProducts.js +670 -0
  296. package/dist/modules/ecommerce/lib/storefrontProducts.js.map +7 -0
  297. package/dist/modules/ecommerce/lib/storefrontQuery.js +156 -0
  298. package/dist/modules/ecommerce/lib/storefrontQuery.js.map +7 -0
  299. package/dist/modules/ecommerce/lib/storefrontRateLimit.js +99 -0
  300. package/dist/modules/ecommerce/lib/storefrontRateLimit.js.map +7 -0
  301. package/dist/modules/ecommerce/lib/storefrontSearch.js +303 -0
  302. package/dist/modules/ecommerce/lib/storefrontSearch.js.map +7 -0
  303. package/dist/modules/ecommerce/lib/storefrontSearchCache.js +35 -0
  304. package/dist/modules/ecommerce/lib/storefrontSearchCache.js.map +7 -0
  305. package/dist/modules/ecommerce/lib/subscriberSupport.js +33 -0
  306. package/dist/modules/ecommerce/lib/subscriberSupport.js.map +7 -0
  307. package/dist/modules/ecommerce/lib/types.js +1 -0
  308. package/dist/modules/ecommerce/lib/types.js.map +7 -0
  309. package/dist/modules/ecommerce/migrations/Migration20261005113233_ecommerce.js +32 -0
  310. package/dist/modules/ecommerce/migrations/Migration20261005113233_ecommerce.js.map +7 -0
  311. package/dist/modules/ecommerce/migrations/Migration20261006112733_ecommerce.js +17 -0
  312. package/dist/modules/ecommerce/migrations/Migration20261006112733_ecommerce.js.map +7 -0
  313. package/dist/modules/ecommerce/notifications.client.js +47 -0
  314. package/dist/modules/ecommerce/notifications.client.js.map +7 -0
  315. package/dist/modules/ecommerce/notifications.js +48 -0
  316. package/dist/modules/ecommerce/notifications.js.map +7 -0
  317. package/dist/modules/ecommerce/search.js +83 -0
  318. package/dist/modules/ecommerce/search.js.map +7 -0
  319. package/dist/modules/ecommerce/setup.js +17 -0
  320. package/dist/modules/ecommerce/setup.js.map +7 -0
  321. package/dist/modules/ecommerce/subscribers/assortment-empty-notifications.js +14 -0
  322. package/dist/modules/ecommerce/subscribers/assortment-empty-notifications.js.map +7 -0
  323. package/dist/modules/ecommerce/subscribers/availability-policy-cache-invalidation.js +14 -0
  324. package/dist/modules/ecommerce/subscribers/availability-policy-cache-invalidation.js.map +7 -0
  325. package/dist/modules/ecommerce/subscribers/catalog-category-cache-invalidation.js +14 -0
  326. package/dist/modules/ecommerce/subscribers/catalog-category-cache-invalidation.js.map +7 -0
  327. package/dist/modules/ecommerce/subscribers/catalog-price-cache-invalidation.js +14 -0
  328. package/dist/modules/ecommerce/subscribers/catalog-price-cache-invalidation.js.map +7 -0
  329. package/dist/modules/ecommerce/subscribers/catalog-product-cache-invalidation.js +14 -0
  330. package/dist/modules/ecommerce/subscribers/catalog-product-cache-invalidation.js.map +7 -0
  331. package/dist/modules/ecommerce/subscribers/catalog-variant-cache-invalidation.js +14 -0
  332. package/dist/modules/ecommerce/subscribers/catalog-variant-cache-invalidation.js.map +7 -0
  333. package/dist/modules/ecommerce/subscribers/customer-group-cache-invalidation.js +14 -0
  334. package/dist/modules/ecommerce/subscribers/customer-group-cache-invalidation.js.map +7 -0
  335. package/dist/modules/ecommerce/subscribers/customer-group-membership-cache-invalidation.js +14 -0
  336. package/dist/modules/ecommerce/subscribers/customer-group-membership-cache-invalidation.js.map +7 -0
  337. package/dist/modules/ecommerce/subscribers/customer-group-terms-cache-invalidation.js +14 -0
  338. package/dist/modules/ecommerce/subscribers/customer-group-terms-cache-invalidation.js.map +7 -0
  339. package/dist/modules/ecommerce/subscribers/customer-user-deleted-cache-invalidation.js +14 -0
  340. package/dist/modules/ecommerce/subscribers/customer-user-deleted-cache-invalidation.js.map +7 -0
  341. package/dist/modules/ecommerce/subscribers/customer-user-updated-cache-invalidation.js +14 -0
  342. package/dist/modules/ecommerce/subscribers/customer-user-updated-cache-invalidation.js.map +7 -0
  343. package/dist/modules/ecommerce/subscribers/domain-binding-rebind.js +16 -0
  344. package/dist/modules/ecommerce/subscribers/domain-binding-rebind.js.map +7 -0
  345. package/dist/modules/ecommerce/subscribers/domain-mapping-cache-invalidation.js +14 -0
  346. package/dist/modules/ecommerce/subscribers/domain-mapping-cache-invalidation.js.map +7 -0
  347. package/dist/modules/ecommerce/subscribers/store-cache-invalidation.js +14 -0
  348. package/dist/modules/ecommerce/subscribers/store-cache-invalidation.js.map +7 -0
  349. package/dist/modules/ecommerce/subscribers/store-channel-binding-cache-invalidation.js +14 -0
  350. package/dist/modules/ecommerce/subscribers/store-channel-binding-cache-invalidation.js.map +7 -0
  351. package/dist/modules/ecommerce/subscribers/store-domain-binding-cache-invalidation.js +14 -0
  352. package/dist/modules/ecommerce/subscribers/store-domain-binding-cache-invalidation.js.map +7 -0
  353. package/dist/modules/ecommerce/subscribers/store-notifications.js +14 -0
  354. package/dist/modules/ecommerce/subscribers/store-notifications.js.map +7 -0
  355. package/dist/modules/query_index/lib/batch.js +23 -9
  356. package/dist/modules/query_index/lib/batch.js.map +2 -2
  357. package/dist/modules/query_index/lib/doc-enrichers.js +212 -0
  358. package/dist/modules/query_index/lib/doc-enrichers.js.map +7 -0
  359. package/dist/modules/query_index/lib/document.js +2 -0
  360. package/dist/modules/query_index/lib/document.js.map +2 -2
  361. package/dist/modules/query_index/lib/engine.js +68 -1
  362. package/dist/modules/query_index/lib/engine.js.map +2 -2
  363. package/dist/modules/query_index/lib/indexer.js +7 -0
  364. package/dist/modules/query_index/lib/indexer.js.map +2 -2
  365. package/dist/modules/query_index/lib/search-tokens.js +2 -0
  366. package/dist/modules/query_index/lib/search-tokens.js.map +2 -2
  367. package/dist/modules/query_index/migrations/Migration20261005201500_query_index.js +17 -0
  368. package/dist/modules/query_index/migrations/Migration20261005201500_query_index.js.map +7 -0
  369. package/dist/modules/translations/components/TranslationManager.js +3 -3
  370. package/dist/modules/translations/components/TranslationManager.js.map +2 -2
  371. package/dist/modules/translations/data/validators.js +1 -1
  372. package/dist/modules/translations/data/validators.js.map +1 -1
  373. package/dist/modules/translations/lib/batch.js +21 -1
  374. package/dist/modules/translations/lib/batch.js.map +2 -2
  375. package/dist/modules/translations/lib/helpers.js +3 -0
  376. package/dist/modules/translations/lib/helpers.js.map +2 -2
  377. package/dist/modules/translations/lib/resolve-field-list.js +14 -2
  378. package/dist/modules/translations/lib/resolve-field-list.js.map +2 -2
  379. package/generated/entities/catalog_price_history_entry/index.ts +25 -0
  380. package/generated/entities/customer_group_terms/index.ts +1 -0
  381. package/generated/entities/ecommerce_store/index.ts +15 -0
  382. package/generated/entities/ecommerce_store_channel_binding/index.ts +13 -0
  383. package/generated/entities/ecommerce_store_domain_binding/index.ts +10 -0
  384. package/generated/entities.ids.generated.ts +8 -1
  385. package/generated/entity-fields-registry.ts +72 -0
  386. package/package.json +7 -7
  387. package/src/helpers/integration/customerGroupsFixtures.ts +2 -0
  388. package/src/modules/auth/i18n/de.json +8 -0
  389. package/src/modules/auth/i18n/en.json +8 -0
  390. package/src/modules/auth/i18n/es.json +8 -0
  391. package/src/modules/auth/i18n/ko.json +8 -0
  392. package/src/modules/auth/i18n/pl.json +8 -0
  393. package/src/modules/catalog/AGENTS.md +5 -2
  394. package/src/modules/catalog/acl.ts +7 -0
  395. package/src/modules/catalog/api/config/omnibus/route.ts +204 -0
  396. package/src/modules/catalog/api/prices/history/route.ts +139 -0
  397. package/src/modules/catalog/api/prices/omnibus-preview/route.ts +202 -0
  398. package/src/modules/catalog/api/products/route.ts +53 -308
  399. package/src/modules/catalog/api/tags/route.ts +6 -0
  400. package/src/modules/catalog/backend/config/catalog/page.tsx +2 -0
  401. package/src/modules/catalog/cli.ts +84 -1
  402. package/src/modules/catalog/commands/categories.ts +54 -2
  403. package/src/modules/catalog/commands/prices.ts +43 -1
  404. package/src/modules/catalog/commands/products.ts +84 -1
  405. package/src/modules/catalog/commands/variants.ts +7 -0
  406. package/src/modules/catalog/components/OmnibusSettings.tsx +832 -0
  407. package/src/modules/catalog/components/PriceEditorOmnibusRow.tsx +219 -0
  408. package/src/modules/catalog/components/lookupSources.ts +29 -0
  409. package/src/modules/catalog/components/prices/PriceScopeSelectors.tsx +9 -0
  410. package/src/modules/catalog/components/products/VariantBuilder.tsx +9 -0
  411. package/src/modules/catalog/data/entities.ts +123 -1
  412. package/src/modules/catalog/data/validators.ts +118 -0
  413. package/src/modules/catalog/di.ts +23 -0
  414. package/src/modules/catalog/i18n/de.json +77 -0
  415. package/src/modules/catalog/i18n/en.json +77 -0
  416. package/src/modules/catalog/i18n/es.json +77 -0
  417. package/src/modules/catalog/i18n/ko.json +77 -0
  418. package/src/modules/catalog/i18n/pl.json +77 -0
  419. package/src/modules/catalog/lib/omnibus.ts +406 -0
  420. package/src/modules/catalog/lib/omnibusBackfill.ts +198 -0
  421. package/src/modules/catalog/lib/omnibusCache.ts +111 -0
  422. package/src/modules/catalog/lib/omnibusConfig.ts +73 -0
  423. package/src/modules/catalog/lib/omnibusHistoryQueries.ts +248 -0
  424. package/src/modules/catalog/lib/omnibusPresentedEntry.ts +76 -0
  425. package/src/modules/catalog/lib/omnibusProductListEnrichment.ts +125 -0
  426. package/src/modules/catalog/lib/omnibusTypes.ts +202 -0
  427. package/src/modules/catalog/lib/optionSchemaTranslations.ts +55 -0
  428. package/src/modules/catalog/lib/priceHistoryQuery.ts +155 -0
  429. package/src/modules/catalog/lib/pricing.ts +42 -4
  430. package/src/modules/catalog/lib/productFilters.ts +349 -0
  431. package/src/modules/catalog/lib/productScopeKeys.ts +162 -0
  432. package/src/modules/catalog/lib/productScopeReindex.ts +139 -0
  433. package/src/modules/catalog/migrations/.snapshot-open-mercato.json +572 -0
  434. package/src/modules/catalog/migrations/Migration20261005105352_catalog.ts +39 -0
  435. package/src/modules/catalog/migrations/Migration20261005150824_catalog.ts +39 -0
  436. package/src/modules/catalog/services/catalogOmnibusService.ts +642 -0
  437. package/src/modules/catalog/setup.ts +2 -0
  438. package/src/modules/catalog/subscribers/product-scope-keys-category-deleted.ts +18 -0
  439. package/src/modules/catalog/subscribers/product-scope-keys-category-updated.ts +19 -0
  440. package/src/modules/catalog/translations.ts +12 -0
  441. package/src/modules/configs/i18n/de.json +5 -1
  442. package/src/modules/configs/i18n/en.json +5 -1
  443. package/src/modules/configs/i18n/es.json +5 -1
  444. package/src/modules/configs/i18n/ko.json +5 -1
  445. package/src/modules/configs/i18n/pl.json +5 -1
  446. package/src/modules/configs/lib/upgrade-actions.ts +35 -0
  447. package/src/modules/customer_groups/api/customer-groups/[id]/terms/route.ts +7 -0
  448. package/src/modules/customer_groups/components/CustomerGroupTermsSection.tsx +124 -2
  449. package/src/modules/customer_groups/components/customerGroupAssortmentScope.ts +46 -0
  450. package/src/modules/customer_groups/data/entities.ts +4 -0
  451. package/src/modules/customer_groups/data/validators.ts +14 -0
  452. package/src/modules/customer_groups/i18n/de.json +12 -0
  453. package/src/modules/customer_groups/i18n/en.json +12 -0
  454. package/src/modules/customer_groups/i18n/es.json +12 -0
  455. package/src/modules/customer_groups/i18n/ko.json +12 -0
  456. package/src/modules/customer_groups/i18n/pl.json +12 -0
  457. package/src/modules/customer_groups/migrations/.snapshot-open-mercato.json +17 -0
  458. package/src/modules/customer_groups/migrations/Migration20261005110250_customer_groups.ts +15 -0
  459. package/src/modules/customer_groups/services/customerGroupsService.ts +74 -23
  460. package/src/modules/ecommerce/acl.ts +9 -0
  461. package/src/modules/ecommerce/api/domain-mappings/route.ts +76 -0
  462. package/src/modules/ecommerce/api/openapi.ts +28 -0
  463. package/src/modules/ecommerce/api/store-channel-bindings/[id]/assortment-count/route.ts +141 -0
  464. package/src/modules/ecommerce/api/store-channel-bindings/crud.ts +283 -0
  465. package/src/modules/ecommerce/api/store-channel-bindings/route.ts +67 -0
  466. package/src/modules/ecommerce/api/store-domain-bindings/crud.ts +324 -0
  467. package/src/modules/ecommerce/api/store-domain-bindings/route.ts +73 -0
  468. package/src/modules/ecommerce/api/storefront/categories/[slug]/route.ts +94 -0
  469. package/src/modules/ecommerce/api/storefront/categories/openapiSchemas.ts +63 -0
  470. package/src/modules/ecommerce/api/storefront/categories/route.ts +78 -0
  471. package/src/modules/ecommerce/api/storefront/context/route.ts +203 -0
  472. package/src/modules/ecommerce/api/storefront/products/[idOrHandle]/route.ts +94 -0
  473. package/src/modules/ecommerce/api/storefront/products/openapiSchemas.ts +193 -0
  474. package/src/modules/ecommerce/api/storefront/products/route.ts +81 -0
  475. package/src/modules/ecommerce/api/storefront/search/suggest/openapiSchemas.ts +25 -0
  476. package/src/modules/ecommerce/api/storefront/search/suggest/route.ts +78 -0
  477. package/src/modules/ecommerce/api/storefront/storefrontRouteSupport.ts +68 -0
  478. package/src/modules/ecommerce/api/stores/[id]/branding/route.ts +205 -0
  479. package/src/modules/ecommerce/api/stores/[id]/preview-branding/route.ts +99 -0
  480. package/src/modules/ecommerce/api/stores/crud.ts +457 -0
  481. package/src/modules/ecommerce/api/stores/route.ts +66 -0
  482. package/src/modules/ecommerce/backend/config/ecommerce/[id]/page.meta.ts +12 -0
  483. package/src/modules/ecommerce/backend/config/ecommerce/[id]/page.tsx +5 -0
  484. package/src/modules/ecommerce/backend/config/ecommerce/page.meta.ts +12 -0
  485. package/src/modules/ecommerce/backend/config/ecommerce/page.tsx +5 -0
  486. package/src/modules/ecommerce/commands/stores.ts +154 -0
  487. package/src/modules/ecommerce/components/ChannelAssortmentCount.tsx +138 -0
  488. package/src/modules/ecommerce/components/ChannelBindingFormSections.tsx +40 -0
  489. package/src/modules/ecommerce/components/ChannelLookupPicker.tsx +5 -0
  490. package/src/modules/ecommerce/components/DomainStatusWarning.tsx +32 -0
  491. package/src/modules/ecommerce/components/StoreAvailabilityDefaultsSection.tsx +219 -0
  492. package/src/modules/ecommerce/components/StoreBrandingControls.tsx +73 -0
  493. package/src/modules/ecommerce/components/StoreBrandingPreview.tsx +77 -0
  494. package/src/modules/ecommerce/components/StoreBrandingTab.tsx +242 -0
  495. package/src/modules/ecommerce/components/StoreChannelBindingDialog.tsx +306 -0
  496. package/src/modules/ecommerce/components/StoreChannelsTab.tsx +375 -0
  497. package/src/modules/ecommerce/components/StoreCreateDialog.tsx +204 -0
  498. package/src/modules/ecommerce/components/StoreDomainBindingDialog.tsx +198 -0
  499. package/src/modules/ecommerce/components/StoreDomainsTab.tsx +426 -0
  500. package/src/modules/ecommerce/components/StoreEditPage.tsx +193 -0
  501. package/src/modules/ecommerce/components/StoreGeneralTab.tsx +189 -0
  502. package/src/modules/ecommerce/components/StoreListPage.tsx +326 -0
  503. package/src/modules/ecommerce/components/StoreSeoTab.tsx +152 -0
  504. package/src/modules/ecommerce/components/StoreStatusBadge.tsx +22 -0
  505. package/src/modules/ecommerce/components/channelLookups.ts +24 -0
  506. package/src/modules/ecommerce/components/storeAdmin.ts +56 -0
  507. package/src/modules/ecommerce/components/storeBrandingForm.ts +230 -0
  508. package/src/modules/ecommerce/components/storeChannels.ts +210 -0
  509. package/src/modules/ecommerce/components/storeDomains.ts +221 -0
  510. package/src/modules/ecommerce/components/storeEditTabs.tsx +76 -0
  511. package/src/modules/ecommerce/components/storeGeneral.ts +155 -0
  512. package/src/modules/ecommerce/components/storeSeo.ts +78 -0
  513. package/src/modules/ecommerce/components/useChannelAssortmentCount.ts +67 -0
  514. package/src/modules/ecommerce/components/useStoreAccess.ts +36 -0
  515. package/src/modules/ecommerce/data/enrichers.ts +213 -0
  516. package/src/modules/ecommerce/data/entities.ts +168 -0
  517. package/src/modules/ecommerce/data/validators.ts +587 -0
  518. package/src/modules/ecommerce/di.ts +9 -0
  519. package/src/modules/ecommerce/events.ts +27 -0
  520. package/src/modules/ecommerce/i18n/de.json +341 -0
  521. package/src/modules/ecommerce/i18n/en.json +341 -0
  522. package/src/modules/ecommerce/i18n/es.json +341 -0
  523. package/src/modules/ecommerce/i18n/ko.json +341 -0
  524. package/src/modules/ecommerce/i18n/pl.json +341 -0
  525. package/src/modules/ecommerce/index.ts +14 -0
  526. package/src/modules/ecommerce/lib/assortmentCount.ts +175 -0
  527. package/src/modules/ecommerce/lib/brandingStyles.ts +215 -0
  528. package/src/modules/ecommerce/lib/buyerContext.ts +401 -0
  529. package/src/modules/ecommerce/lib/cacheInvalidation.ts +302 -0
  530. package/src/modules/ecommerce/lib/cacheKeys.ts +340 -0
  531. package/src/modules/ecommerce/lib/createConflictRecheck.ts +31 -0
  532. package/src/modules/ecommerce/lib/crudEvents.ts +128 -0
  533. package/src/modules/ecommerce/lib/crudSupport.ts +85 -0
  534. package/src/modules/ecommerce/lib/domainBindingRebind.ts +138 -0
  535. package/src/modules/ecommerce/lib/domainMappingSummaries.ts +71 -0
  536. package/src/modules/ecommerce/lib/exclusiveFlag.ts +90 -0
  537. package/src/modules/ecommerce/lib/references.ts +93 -0
  538. package/src/modules/ecommerce/lib/seedDraftStore.ts +125 -0
  539. package/src/modules/ecommerce/lib/storeBindingCascade.ts +138 -0
  540. package/src/modules/ecommerce/lib/storeBranding.ts +48 -0
  541. package/src/modules/ecommerce/lib/storeBrandingRoute.ts +91 -0
  542. package/src/modules/ecommerce/lib/storeContext.ts +669 -0
  543. package/src/modules/ecommerce/lib/storeContextService.ts +71 -0
  544. package/src/modules/ecommerce/lib/storeLocale.ts +88 -0
  545. package/src/modules/ecommerce/lib/storeNotifications.ts +55 -0
  546. package/src/modules/ecommerce/lib/storefrontCatalogSupport.ts +427 -0
  547. package/src/modules/ecommerce/lib/storefrontCategories.ts +419 -0
  548. package/src/modules/ecommerce/lib/storefrontCategoryCache.ts +113 -0
  549. package/src/modules/ecommerce/lib/storefrontDetail.ts +654 -0
  550. package/src/modules/ecommerce/lib/storefrontFacets.ts +697 -0
  551. package/src/modules/ecommerce/lib/storefrontOptionFilter.ts +44 -0
  552. package/src/modules/ecommerce/lib/storefrontPricing.ts +583 -0
  553. package/src/modules/ecommerce/lib/storefrontProductCache.ts +126 -0
  554. package/src/modules/ecommerce/lib/storefrontProductScope.ts +181 -0
  555. package/src/modules/ecommerce/lib/storefrontProducts.ts +963 -0
  556. package/src/modules/ecommerce/lib/storefrontQuery.ts +215 -0
  557. package/src/modules/ecommerce/lib/storefrontRateLimit.ts +126 -0
  558. package/src/modules/ecommerce/lib/storefrontSearch.ts +454 -0
  559. package/src/modules/ecommerce/lib/storefrontSearchCache.ts +49 -0
  560. package/src/modules/ecommerce/lib/subscriberSupport.ts +41 -0
  561. package/src/modules/ecommerce/lib/types.ts +69 -0
  562. package/src/modules/ecommerce/migrations/.snapshot-open-mercato.json +829 -0
  563. package/src/modules/ecommerce/migrations/Migration20261005113233_ecommerce.ts +32 -0
  564. package/src/modules/ecommerce/migrations/Migration20261006112733_ecommerce.ts +15 -0
  565. package/src/modules/ecommerce/notifications.client.ts +46 -0
  566. package/src/modules/ecommerce/notifications.ts +46 -0
  567. package/src/modules/ecommerce/search.ts +93 -0
  568. package/src/modules/ecommerce/setup.ts +15 -0
  569. package/src/modules/ecommerce/subscribers/assortment-empty-notifications.ts +12 -0
  570. package/src/modules/ecommerce/subscribers/availability-policy-cache-invalidation.ts +12 -0
  571. package/src/modules/ecommerce/subscribers/catalog-category-cache-invalidation.ts +12 -0
  572. package/src/modules/ecommerce/subscribers/catalog-price-cache-invalidation.ts +12 -0
  573. package/src/modules/ecommerce/subscribers/catalog-product-cache-invalidation.ts +12 -0
  574. package/src/modules/ecommerce/subscribers/catalog-variant-cache-invalidation.ts +12 -0
  575. package/src/modules/ecommerce/subscribers/customer-group-cache-invalidation.ts +12 -0
  576. package/src/modules/ecommerce/subscribers/customer-group-membership-cache-invalidation.ts +12 -0
  577. package/src/modules/ecommerce/subscribers/customer-group-terms-cache-invalidation.ts +12 -0
  578. package/src/modules/ecommerce/subscribers/customer-user-deleted-cache-invalidation.ts +12 -0
  579. package/src/modules/ecommerce/subscribers/customer-user-updated-cache-invalidation.ts +12 -0
  580. package/src/modules/ecommerce/subscribers/domain-binding-rebind.ts +14 -0
  581. package/src/modules/ecommerce/subscribers/domain-mapping-cache-invalidation.ts +12 -0
  582. package/src/modules/ecommerce/subscribers/store-cache-invalidation.ts +12 -0
  583. package/src/modules/ecommerce/subscribers/store-channel-binding-cache-invalidation.ts +12 -0
  584. package/src/modules/ecommerce/subscribers/store-domain-binding-cache-invalidation.ts +12 -0
  585. package/src/modules/ecommerce/subscribers/store-notifications.ts +12 -0
  586. package/src/modules/query_index/lib/batch.ts +25 -9
  587. package/src/modules/query_index/lib/doc-enrichers.ts +306 -0
  588. package/src/modules/query_index/lib/document.ts +2 -0
  589. package/src/modules/query_index/lib/engine.ts +58 -1
  590. package/src/modules/query_index/lib/indexer.ts +8 -0
  591. package/src/modules/query_index/lib/search-tokens.ts +2 -0
  592. package/src/modules/query_index/migrations/Migration20261005201500_query_index.ts +42 -0
  593. package/src/modules/translations/components/TranslationManager.tsx +3 -3
  594. package/src/modules/translations/data/validators.ts +1 -1
  595. package/src/modules/translations/lib/batch.ts +47 -0
  596. package/src/modules/translations/lib/helpers.ts +4 -0
  597. package/src/modules/translations/lib/resolve-field-list.ts +16 -2
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/ecommerce/lib/storefrontProducts.ts"],
4
+ "sourcesContent": ["import type { AwilixContainer } from 'awilix'\nimport type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport type { AvailabilityItemResult, AvailabilityState } from '@open-mercato/shared/lib/availability'\nimport type { CrudCtx } from '@open-mercato/shared/lib/crud/factory'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { sanitizeSearchTerm } from '@open-mercato/shared/lib/query/sanitizeSearchTerm'\nimport { SortDir, type QueryEngine, type Sort, type Where } from '@open-mercato/shared/lib/query/types'\nimport { E } from '#generated/entities.ids.generated'\nimport {\n CatalogProductCategory,\n CatalogProductCategoryAssignment,\n CatalogProductPrice,\n CatalogProductTag,\n CatalogProductTagAssignment,\n CatalogProductVariant,\n} from '@open-mercato/core/modules/catalog/data/entities'\nimport type { CatalogProductType } from '@open-mercato/core/modules/catalog/data/types'\nimport {\n buildProductFilters,\n scoreProductSearchRelevance,\n type CatalogProductFilterQuery,\n} from '@open-mercato/core/modules/catalog/lib/productFilters'\nimport type {\n EcommercePriceDisplayMode,\n EcommercePriceSortFallback,\n EcommerceStorefrontProductListQuery,\n EcommerceStorefrontProductSort,\n} from '../data/validators'\nimport {\n STOREFRONT_FACET_UNIVERSE_CAP,\n countStorefrontFacets,\n labelStorefrontCountFacets,\n loadStorefrontFacetSource,\n loadStorefrontProductTypeLabeler,\n readCachedStorefrontCountFacets,\n storefrontAvailabilityFacet,\n storefrontFacetTranslationRequests,\n storefrontFacetVariantIndex,\n storefrontPriceRangeFacet,\n writeCachedStorefrontCountFacets,\n type StorefrontCountFacets,\n type StorefrontFacetSelection,\n type StorefrontFacetSource,\n type StorefrontFacets,\n} from './storefrontFacets'\nimport { buildStorefrontProductScope, composeStorefrontProductFilters, type StorefrontProductScope } from './storefrontProductScope'\nimport { resolveStorefrontPrices, type StorefrontProductPricing } from './storefrontPricing'\nimport { queryStorefrontOptionProductIds } from './storefrontOptionFilter'\nimport {\n STOREFRONT_CATEGORY_ENTITY_TYPE,\n STOREFRONT_PRODUCT_ENTITY_TYPE,\n STOREFRONT_TAG_ENTITY_TYPE,\n buildStorefrontListItem,\n isCategoryInAssortment,\n selectCategoriesWithActiveAncestors,\n loadStorefrontTranslations,\n localeChain,\n nonEmptyString,\n referenceId,\n resolvePublicDefaultMediaUrls,\n resolveStorefrontAvailability,\n storefrontAvailabilityKey,\n stringList,\n toStorefrontAvailability,\n tryResolve,\n type CategoryLineage,\n type CategoryRef,\n type StorefrontAvailability,\n type StorefrontProductListItem,\n type TagRef,\n type TranslationMap,\n} from './storefrontCatalogSupport'\nimport type { StoreContext } from './types'\n\nexport { isCategoryInAssortment } from './storefrontCatalogSupport'\nexport type { StorefrontAvailability, StorefrontProductListItem } from './storefrontCatalogSupport'\nexport type { StorefrontFacets } from './storefrontFacets'\n\n/** Storefront Public API \u00A76.3: the pre-sort id set that is priced and sorted in memory. */\nexport const STOREFRONT_PRICE_SORT_CAP = 5000\n\n/**\n * The largest filtered set the `'approximate'` fallback still loads in full (candidates plus their\n * list-price rows); past it the listing answers as under `'unavailable'`.\n */\nexport const STOREFRONT_APPROXIMATE_SORT_CEILING = 50_000\n\nconst APPROXIMATE_PRICE_CHUNK = 1000\n\nconst NO_MATCH_ID = '00000000-0000-0000-0000-000000000000'\n\nconst LIST_FIELDS = [\n 'id',\n 'title',\n 'subtitle',\n 'handle',\n 'sku',\n 'product_type',\n 'is_configurable',\n 'default_media_id',\n 'default_media_url',\n 'created_at',\n]\n\nconst CANDIDATE_FIELDS = ['id', 'title', 'sku']\n\nconst SORT_ORDER: readonly EcommerceStorefrontProductSort[] = [\n 'relevance',\n 'price_asc',\n 'price_desc',\n 'title_asc',\n 'title_desc',\n 'newest',\n 'featured',\n]\n\nexport type StorefrontAppliedFilters = {\n search?: string\n category?: { id: string; slug: string | null; includesDescendants: true }\n tagSlugs?: string[]\n price?: { min: number | null; max: number | null; approximate: boolean }\n options?: Record<string, string[]>\n productType?: CatalogProductType\n availability?: { value: 'in_stock' | 'available'; scope: 'page' }\n}\n\nexport type StorefrontPriceSortPolicy = {\n cap: number\n fallback: EcommercePriceSortFallback\n capExceeded: boolean\n}\n\nexport type StorefrontProductListResponse = {\n items: StorefrontProductListItem[]\n total: number\n page: number\n pageSize: number\n totalPages: number\n facets: StorefrontFacets\n effectiveLocale: string\n requestedLocale: string | null\n currencyCode: string\n taxMode: EcommercePriceDisplayMode\n appliedFilters: StorefrontAppliedFilters\n availableSorts: EcommerceStorefrontProductSort[]\n appliedSort: EcommerceStorefrontProductSort\n priceSort: StorefrontPriceSortPolicy\n /** The applied order is not exact \u2014 the route emits `X-Sort-Approximate: true` (\u00A76.3). */\n sortApproximate: boolean\n /** A requested price sort was declined by the channel's `'unavailable'` policy \u2014 `X-Sort-Unavailable: true`. */\n sortUnavailable: boolean\n}\n\nexport type ListStorefrontProductsOptions = {\n /** Overrides `STOREFRONT_PRICE_SORT_CAP`; intended for tests. */\n priceSortCap?: number\n /** Overrides `STOREFRONT_APPROXIMATE_SORT_CEILING`; intended for tests. */\n approximateSortCeiling?: number\n /** Overrides `STOREFRONT_FACET_UNIVERSE_CAP`; intended for tests. */\n facetUniverseCap?: number\n date?: Date\n}\n\ntype ProductRecord = {\n id: string\n title?: string | null\n subtitle?: string | null\n handle?: string | null\n sku?: string | null\n product_type?: string | null\n is_configurable?: unknown\n default_media_id?: string | null\n default_media_url?: string | null\n}\n\ntype Candidate = { id: string; title: string; sku: string | null }\n\ntype Runtime = {\n container: AwilixContainer\n ctx: StoreContext\n em: EntityManager\n queryEngine: QueryEngine\n scope: StorefrontProductScope\n decryptionScope: { tenantId: string; organizationId: string }\n date: Date\n facetUniverseCap?: number\n}\n\ntype ResolvedFilters = {\n extra: Where | null\n universe: Where | null\n selection: StorefrontFacetSelection\n applied: StorefrontAppliedFilters\n searchTerm: string | null\n}\n\ntype HydratedPage = {\n items: StorefrontProductListItem[]\n availabilityStates: AvailabilityState[]\n translations: Map<string, TranslationMap>\n}\n\ntype CountFacetState = {\n cached: StorefrontCountFacets | null\n source: StorefrontFacetSource | null\n overCap: boolean\n}\n\nconst EMPTY_COUNT_FACETS: StorefrontCountFacets = { categories: [], tags: [], options: [], productTypes: [] }\n\ntype PagePrefetch = {\n variants?: Map<string, string[]>\n pricing?: Map<string, StorefrontProductPricing>\n}\n\nfunction isPriceSort(sort: EcommerceStorefrontProductSort): boolean {\n return sort === 'price_asc' || sort === 'price_desc'\n}\n\nfunction sqlSortFor(sort: EcommerceStorefrontProductSort): Sort[] {\n switch (sort) {\n case 'title_desc':\n return [{ field: 'title', dir: SortDir.Desc }]\n case 'newest':\n case 'featured':\n return [{ field: 'created_at', dir: SortDir.Desc }]\n default:\n return [{ field: 'title', dir: SortDir.Asc }]\n }\n}\n\nasync function resolveCategoryFilter(\n runtime: Runtime,\n query: EcommerceStorefrontProductListQuery,\n): Promise<{ id: string; slug: string | null } | null> {\n if (!query.categoryId && !query.categorySlug) return null\n const { ctx } = runtime\n const where: FilterQuery<CatalogProductCategory> = {\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n deletedAt: null,\n isActive: true,\n ...(query.categoryId ? { id: query.categoryId } : { slug: query.categorySlug }),\n }\n const [category] = await findWithDecryption(\n runtime.em,\n CatalogProductCategory,\n where,\n { fields: ['id', 'slug', 'ancestorIds', 'descendantIds'], limit: 1 },\n runtime.decryptionScope,\n )\n if (!category) return null\n const lineage: CategoryLineage = {\n id: category.id,\n ancestorIds: stringList(category.ancestorIds),\n descendantIds: stringList(category.descendantIds),\n }\n if (!isCategoryInAssortment(lineage, ctx.buyer.assortmentScope)) return null\n const { visibleIds } = await selectCategoriesWithActiveAncestors(runtime.em, ctx, [lineage])\n if (!visibleIds.has(category.id)) return null\n return { id: category.id, slug: category.slug ?? null }\n}\n\nasync function resolveTagFilter(runtime: Runtime, slugs: string[] | undefined): Promise<Array<{ id: string; slug: string }>> {\n if (!slugs?.length) return []\n const { ctx } = runtime\n const tags = await findWithDecryption(\n runtime.em,\n CatalogProductTag,\n { tenantId: ctx.tenantId, organizationId: ctx.organizationId, slug: { $in: slugs } },\n { fields: ['id', 'slug'] },\n runtime.decryptionScope,\n )\n const bySlug = new Map(tags.map((tag) => [tag.slug, tag.id]))\n return slugs.flatMap((slug) => {\n const id = bySlug.get(slug)\n return id ? [{ id, slug }] : []\n })\n}\n\nconst OPTION_FILTER_OVER_CAP = 'over_cap'\n\ntype OptionFilterResolution = string[] | typeof OPTION_FILTER_OVER_CAP | null\n\nasync function resolveOptionProductIds(\n runtime: Runtime,\n options: Record<string, string[]> | undefined,\n): Promise<OptionFilterResolution> {\n if (!options) return null\n const ids = await queryStorefrontOptionProductIds(\n runtime.em,\n runtime.decryptionScope,\n options,\n runtime.facetUniverseCap ?? STOREFRONT_FACET_UNIVERSE_CAP,\n )\n return ids ?? OPTION_FILTER_OVER_CAP\n}\n\nasync function productFilterClause(\n runtime: Runtime,\n filterQuery: Omit<CatalogProductFilterQuery, 'page' | 'pageSize'>,\n): Promise<Where | null> {\n return buildStorefrontProductFilterClause(runtime.container, runtime.ctx, filterQuery)\n}\n\n/**\n * `catalog`'s product filters (\u00A73.2 reuse) for the store's tenant and organization \u2014 for `search`\n * the Phase 1 escaped, accent-insensitive `ILIKE` over title, subtitle, description, SKU and handle\n * (\u00A78.1). `null` when no filter applies. Compose the result with the storefront scope.\n */\nexport async function buildStorefrontProductFilterClause(\n container: AwilixContainer,\n ctx: StoreContext,\n filterQuery: Omit<CatalogProductFilterQuery, 'page' | 'pageSize'>,\n): Promise<Where | null> {\n const crudCtx: CrudCtx = {\n container,\n auth: { sub: 'ecommerce:storefront', tenantId: ctx.tenantId, orgId: ctx.organizationId },\n organizationScope: null,\n selectedOrganizationId: ctx.organizationId,\n organizationIds: [ctx.organizationId],\n }\n const productFilters = await buildProductFilters({ page: 1, pageSize: 1, ...filterQuery }, crudCtx, {\n includeCategoryDescendants: true,\n })\n return Object.keys(productFilters).length ? productFilters : null\n}\n\nfunction andClauses(clauses: Array<Where | null>): Where | null {\n const present = clauses.filter((clause): clause is Where => clause !== null)\n if (present.length === 0) return null\n return present.length === 1 ? present[0] : { $and: present }\n}\n\n/**\n * Resolves the listing's filters. `extra` is every product filter AND'ed (composed with the scope\n * by the caller); `universe` is the search clause alone \u2014 the facet universe (\u00A75.4), since search\n * is the one filter no facet dimension excludes; `selection` is what the facets cross-exclude.\n */\nasync function resolveListFilters(\n runtime: Runtime,\n query: EcommerceStorefrontProductListQuery,\n): Promise<ResolvedFilters> {\n const applied: StorefrontAppliedFilters = {}\n const searchTerm = sanitizeSearchTerm(query.search)\n const [category, resolvedTags, optionProductIds] = await Promise.all([\n resolveCategoryFilter(runtime, query),\n resolveTagFilter(runtime, query.tagSlugs),\n resolveOptionProductIds(runtime, query.options),\n ])\n if (searchTerm) applied.search = searchTerm\n if (category) applied.category = { id: category.id, slug: category.slug, includesDescendants: true }\n if (resolvedTags.length) applied.tagSlugs = resolvedTags.map((tag) => tag.slug)\n if (query.productType) applied.productType = query.productType\n const optionsApplied = Array.isArray(optionProductIds)\n if (optionsApplied && query.options) applied.options = query.options\n\n const [searchClause, facetClause] = await Promise.all([\n searchTerm ? productFilterClause(runtime, { search: searchTerm }) : Promise.resolve(null),\n category || resolvedTags.length || query.productType\n ? productFilterClause(runtime, {\n categoryIds: category?.id,\n tagIds: resolvedTags.length ? resolvedTags.map((tag) => tag.id).join(',') : undefined,\n productType: query.productType,\n })\n : Promise.resolve(null),\n ])\n const optionClause: Where | null = Array.isArray(optionProductIds)\n ? optionProductIds.length\n ? { id: { $in: optionProductIds } }\n : { id: { $eq: NO_MATCH_ID } }\n : null\n return {\n extra: andClauses([searchClause, facetClause, optionClause]),\n universe: searchClause,\n selection: {\n categoryId: category?.id ?? null,\n tagIds: resolvedTags.map((tag) => tag.id),\n productType: query.productType ?? null,\n options: optionsApplied ? query.options ?? {} : {},\n },\n applied,\n searchTerm: searchTerm || null,\n }\n}\n\nasync function queryCandidates(\n runtime: Runtime,\n filters: Where,\n sort: Sort[],\n pageSize: number,\n): Promise<{ items: Candidate[]; total: number }> {\n const result = await runtime.queryEngine.query<ProductRecord>(E.catalog.catalog_product, {\n tenantId: runtime.scope.tenantId,\n organizationId: runtime.scope.organizationId,\n withDeleted: runtime.scope.withDeleted,\n filters,\n fields: CANDIDATE_FIELDS,\n sort,\n page: { page: 1, pageSize: Math.max(1, pageSize) },\n })\n const items = result.items\n .filter((item) => typeof item.id === 'string')\n .map((item) => ({ id: item.id, title: nonEmptyString(item.title) ?? '', sku: nonEmptyString(item.sku) }))\n return { items, total: result.total }\n}\n\nasync function queryProductsByIds(runtime: Runtime, ids: string[]): Promise<ProductRecord[]> {\n if (!ids.length) return []\n const result = await runtime.queryEngine.query<ProductRecord>(E.catalog.catalog_product, {\n tenantId: runtime.scope.tenantId,\n organizationId: runtime.scope.organizationId,\n withDeleted: runtime.scope.withDeleted,\n filters: composeStorefrontProductFilters(runtime.scope, { id: { $in: ids } }),\n fields: LIST_FIELDS,\n page: { page: 1, pageSize: ids.length },\n })\n const byId = new Map(result.items.map((item) => [item.id, item]))\n return ids.map((id) => byId.get(id)).filter((item): item is ProductRecord => !!item)\n}\n\nasync function loadVariantIds(runtime: Runtime, productIds: string[]): Promise<Map<string, string[]>> {\n const byProduct = new Map<string, string[]>()\n if (!productIds.length) return byProduct\n const { ctx } = runtime\n const variants = await findWithDecryption(\n runtime.em,\n CatalogProductVariant,\n {\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n deletedAt: null,\n isActive: true,\n product: { $in: productIds },\n },\n { fields: ['id', 'product'] },\n runtime.decryptionScope,\n )\n for (const variant of variants) {\n const productId = referenceId(variant.product)\n if (!productId) continue\n const bucket = byProduct.get(productId) ?? []\n bucket.push(variant.id)\n byProduct.set(productId, bucket)\n }\n return byProduct\n}\n\nasync function loadCategories(\n runtime: Runtime,\n productIds: string[],\n): Promise<{ byProduct: Map<string, CategoryRef[]>; categoryIds: string[] }> {\n const candidates = new Map<string, Array<CategoryRef & { ancestorIds: string[] }>>()\n const { ctx } = runtime\n const assignments = await findWithDecryption(\n runtime.em,\n CatalogProductCategoryAssignment,\n { tenantId: ctx.tenantId, organizationId: ctx.organizationId, product: { $in: productIds } },\n { populate: ['category'], orderBy: { position: 'asc' } },\n runtime.decryptionScope,\n )\n const categoryIds = new Set<string>()\n for (const assignment of assignments) {\n const productId = referenceId(assignment.product)\n const category = typeof assignment.category === 'object' ? assignment.category : null\n if (!productId || !category || category.deletedAt || category.isActive === false) continue\n const lineage: CategoryLineage = {\n id: category.id,\n ancestorIds: stringList(category.ancestorIds),\n descendantIds: stringList(category.descendantIds),\n }\n if (!isCategoryInAssortment(lineage, ctx.buyer.assortmentScope)) continue\n const bucket = candidates.get(productId) ?? []\n if (bucket.some((entry) => entry.id === category.id)) continue\n bucket.push({ id: category.id, name: category.name, slug: category.slug ?? null, ancestorIds: lineage.ancestorIds })\n candidates.set(productId, bucket)\n categoryIds.add(category.id)\n }\n const allCandidates = Array.from(candidates.values()).flat()\n const { visibleIds: visible } = await selectCategoriesWithActiveAncestors(runtime.em, ctx, allCandidates, {\n activeIds: categoryIds,\n })\n const byProduct = new Map<string, CategoryRef[]>()\n for (const [productId, bucket] of candidates) {\n const refs = bucket.filter((entry) => visible.has(entry.id)).map(({ id, name, slug }) => ({ id, name, slug }))\n if (refs.length > 0) byProduct.set(productId, refs)\n }\n return { byProduct, categoryIds: Array.from(categoryIds).filter((id) => visible.has(id)) }\n}\n\nasync function loadTags(\n runtime: Runtime,\n productIds: string[],\n): Promise<{ byProduct: Map<string, TagRef[]>; tagIds: string[] }> {\n const byProduct = new Map<string, TagRef[]>()\n const { ctx } = runtime\n const assignments = await findWithDecryption(\n runtime.em,\n CatalogProductTagAssignment,\n { tenantId: ctx.tenantId, organizationId: ctx.organizationId, product: { $in: productIds } },\n { populate: ['tag'] },\n runtime.decryptionScope,\n )\n const tagIds = new Set<string>()\n for (const assignment of assignments) {\n const productId = referenceId(assignment.product)\n const tag = typeof assignment.tag === 'object' ? assignment.tag : null\n if (!productId || !tag) continue\n const bucket = byProduct.get(productId) ?? []\n if (bucket.some((entry) => entry.id === tag.id)) continue\n bucket.push({ id: tag.id, label: tag.label })\n byProduct.set(productId, bucket)\n tagIds.add(tag.id)\n }\n return { byProduct, tagIds: Array.from(tagIds) }\n}\n\ntype PageAvailability = Map<string, { result: AvailabilityItemResult | null; hideWhenOutOfStock: boolean }>\n\nasync function resolvePageAvailability(runtime: Runtime, productIds: string[]): Promise<PageAvailability> {\n const results: PageAvailability = new Map()\n if (!productIds.length) return results\n const targets = productIds.map((productId) => ({ productId, variantId: null }))\n const resolved = await resolveStorefrontAvailability(runtime.container, runtime.ctx, runtime.em, targets)\n for (const target of targets) {\n const entry = resolved.get(storefrontAvailabilityKey(target))\n results.set(target.productId, {\n result: entry?.result ?? null,\n hideWhenOutOfStock: entry?.policy?.hideWhenOutOfStock?.value === true,\n })\n }\n return results\n}\n\nconst IN_STOCK_STATES: ReadonlySet<AvailabilityState> = new Set(['in_stock', 'low_stock', 'not_tracked'])\n\nfunction matchesAvailabilityFilter(\n availability: StorefrontAvailability,\n filter: EcommerceStorefrontProductListQuery['availability'],\n): boolean {\n if (filter === 'in_stock') return IN_STOCK_STATES.has(availability.state) && availability.canFulfil\n if (filter === 'available') return availability.state !== 'out_of_stock' && availability.canFulfil\n return true\n}\n\n/**\n * The page's items. `extraTranslations` (the count facets' overlays) ride on the page's single\n * translation query; `availabilityStates` are the page's states after `hideWhenOutOfStock` and\n * before the `availability=` filter \u2014 what the page-scoped availability facet counts (\u00A75.3).\n */\nasync function hydratePage(\n runtime: Runtime,\n records: ProductRecord[],\n query: EcommerceStorefrontProductListQuery,\n prefetch: PagePrefetch,\n extraTranslations: Array<{ entityType: string; ids: string[] }>,\n): Promise<HydratedPage> {\n const { ctx, container } = runtime\n if (!records.length) {\n const translations = extraTranslations.length\n ? await loadStorefrontTranslations(runtime.em, ctx, extraTranslations)\n : new Map<string, TranslationMap>()\n return { items: [], availabilityStates: [], translations }\n }\n const productIds = records.map((record) => record.id)\n const [variantsByProduct, categories, tags] = await Promise.all([\n prefetch.variants ?? loadVariantIds(runtime, productIds),\n loadCategories(runtime, productIds),\n loadTags(runtime, productIds),\n ])\n const [pricing, translations, availability, mediaUrls] = await Promise.all([\n prefetch.pricing ??\n resolveStorefrontPrices(\n container,\n ctx,\n productIds.map((productId) => ({ productId, variantIds: variantsByProduct.get(productId) ?? [] })),\n { date: runtime.date },\n ),\n loadStorefrontTranslations(runtime.em, ctx, [\n { entityType: STOREFRONT_PRODUCT_ENTITY_TYPE, ids: productIds },\n { entityType: STOREFRONT_CATEGORY_ENTITY_TYPE, ids: categories.categoryIds },\n { entityType: STOREFRONT_TAG_ENTITY_TYPE, ids: tags.tagIds },\n ...extraTranslations,\n ]),\n resolvePageAvailability(runtime, productIds),\n resolvePublicDefaultMediaUrls(\n runtime.em,\n ctx,\n records.map((record) => ({\n id: record.id,\n defaultMediaId: record.default_media_id,\n defaultMediaUrl: record.default_media_url,\n })),\n ),\n ])\n const locales = localeChain(ctx)\n const productTranslations = translations.get(STOREFRONT_PRODUCT_ENTITY_TYPE)\n const categoryTranslations = translations.get(STOREFRONT_CATEGORY_ENTITY_TYPE)\n const tagTranslations = translations.get(STOREFRONT_TAG_ENTITY_TYPE)\n\n const items: StorefrontProductListItem[] = []\n const availabilityStates: AvailabilityState[] = []\n for (const record of records) {\n const productAvailability = availability.get(record.id)\n const storefrontAvailability = toStorefrontAvailability(productAvailability?.result ?? null)\n if (productAvailability?.hideWhenOutOfStock && storefrontAvailability.state === 'out_of_stock') continue\n availabilityStates.push(storefrontAvailability.state)\n if (!matchesAvailabilityFilter(storefrontAvailability, query.availability)) continue\n items.push(\n buildStorefrontListItem(\n {\n id: record.id,\n handle: record.handle,\n title: record.title,\n subtitle: record.subtitle,\n defaultMediaUrl: mediaUrls.get(record.id) ?? null,\n productType: record.product_type,\n isConfigurable: record.is_configurable,\n },\n {\n locales,\n overlay: productTranslations?.get(record.id),\n variantCount: variantsByProduct.get(record.id)?.length ?? 0,\n categories: categories.byProduct.get(record.id) ?? [],\n categoryTranslations,\n tags: tags.byProduct.get(record.id) ?? [],\n tagTranslations,\n pricing: pricing.get(record.id),\n availability: storefrontAvailability,\n },\n ),\n )\n }\n return { items, availabilityStates, translations }\n}\n\nfunction pricingAmount(pricing: StorefrontProductPricing | undefined): number | null {\n if (!pricing) return null\n return pricing.price?.amount ?? pricing.priceRange?.min ?? null\n}\n\nfunction parseAmount(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 chunk<T>(values: T[], size: number): T[][] {\n const chunks: T[][] = []\n for (let index = 0; index < values.length; index += size) chunks.push(values.slice(index, index + size))\n return chunks\n}\n\n/**\n * Past the cap with `price_sort_fallback = 'approximate'` (\u00A76.3): each candidate's lowest list\n * price among the channel default price kind's unscoped rows (no customer, group or user), read on\n * the buyer's `taxMode` side. Only the candidates' rows are read \u2014 those on the product itself or on\n * one of its variants \u2014 in sequential chunks of `APPROXIMATE_PRICE_CHUNK` products, so one request\n * holds at most one connection; never buyer prices.\n */\nasync function loadApproximateAmounts(runtime: Runtime, productIds: string[]): Promise<Map<string, number>> {\n const { ctx } = runtime\n const amounts = new Map<string, number>()\n const priceKindId = ctx.channel?.priceKindId ?? null\n if (!priceKindId || !productIds.length) return amounts\n const rows: CatalogProductPrice[] = []\n for (const ids of chunk(productIds, APPROXIMATE_PRICE_CHUNK)) {\n rows.push(\n ...(await findWithDecryption(\n runtime.em,\n CatalogProductPrice,\n {\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n priceKind: priceKindId,\n currencyCode: ctx.currencyCode,\n customerId: null,\n customerGroupId: null,\n userId: null,\n userGroupId: null,\n $or: [{ product: { $in: ids } }, { variant: { product: { $in: ids } } }],\n },\n { populate: ['variant'] },\n runtime.decryptionScope,\n )),\n )\n }\n const wantedIds = new Set(productIds)\n const channelId = ctx.channel?.salesChannelId ?? null\n const now = runtime.date\n for (const row of rows) {\n if (row.channelId && row.channelId !== channelId) continue\n if ((row.minQuantity ?? 1) > 1) continue\n if (row.startsAt && row.startsAt > now) continue\n if (row.endsAt && row.endsAt < now) continue\n const productId =\n referenceId(row.product) ?? (row.variant && typeof row.variant === 'object' ? referenceId(row.variant.product) : null)\n if (!productId || !wantedIds.has(productId)) continue\n const wanted = parseAmount(ctx.buyer.taxMode === 'gross' ? row.unitPriceGross : row.unitPriceNet)\n const amount = wanted ?? parseAmount(ctx.buyer.taxMode === 'gross' ? row.unitPriceNet : row.unitPriceGross)\n if (amount === null) continue\n const current = amounts.get(productId)\n if (current === undefined || amount < current) amounts.set(productId, amount)\n }\n return amounts\n}\n\nfunction compareAmounts(left: number | null, right: number | null, direction: 1 | -1): number {\n if (left === null && right === null) return 0\n if (left === null) return 1\n if (right === null) return -1\n return (left - right) * direction\n}\n\nfunction rankByRelevance<T extends { title: string; sku: string | null }>(items: T[], searchTerm: string): T[] {\n const needle = searchTerm.toLowerCase()\n return items\n .map((item, index) => ({ item, index, score: scoreProductSearchRelevance(needle, item.title, item.sku) }))\n .sort((left, right) => left.score - right.score || left.item.title.localeCompare(right.item.title) || left.index - right.index)\n .map((entry) => entry.item)\n}\n\nfunction matchesPriceFilter(amount: number | null, query: EcommerceStorefrontProductListQuery): boolean {\n if (amount === null) return false\n if (query.priceMin !== undefined && amount < query.priceMin) return false\n if (query.priceMax !== undefined && amount > query.priceMax) return false\n return true\n}\n\nfunction availableSortsFor(searchTerm: string | null, priceSortsOffered: boolean): EcommerceStorefrontProductSort[] {\n return SORT_ORDER.filter((sort) => {\n if (sort === 'relevance') return !!searchTerm\n if (isPriceSort(sort)) return priceSortsOffered\n return true\n })\n}\n\nasync function resolveCountFacetState(\n runtime: Runtime,\n selection: StorefrontFacetSelection,\n searchTerm: string | null,\n universe: Where | null,\n): Promise<CountFacetState> {\n const cached = await readCachedStorefrontCountFacets(runtime.container, runtime.ctx, selection, searchTerm)\n if (cached) return { cached, source: null, overCap: false }\n const source = await loadStorefrontFacetSource(\n runtime,\n composeStorefrontProductFilters(runtime.scope, universe),\n runtime.facetUniverseCap,\n )\n return { cached: null, source, overCap: source === null }\n}\n\n/**\n * `GET /products` listing (Storefront Public API rev 4 \u00A74.1, \u00A75.1, \u00A75.3, \u00A76.3, \u00A77, \u00A78.1, \u00A710).\n *\n * Every product query composes `buildStorefrontProductScope` (\u00A73.3). The filtered set is read as\n * ordered candidates (title, newest and featured order in SQL \u2014 `featured` orders by `created_at\n * desc` until merchandising supplies a featured rank). While it is at most\n * `STOREFRONT_PRICE_SORT_CAP` products it is priced for the buyer in one batch: that pricing feeds\n * the price sorts, the price filter, relevance ranking and `facets.priceRange` (the filtered set\n * without the price filter, \u00A75.4) and is reused for the page. Past the cap the channel's\n * `priceSortFallback` decides: `'approximate'` orders, filters and ranges by the channel default\n * price kind's list rows and flags `sortApproximate` \u2014 loading every candidate and those candidates'\n * default-kind rows only when a price sort or price filter asks for it, otherwise `priceRange` is\n * `null` \u2014 `'unavailable'` withdraws the price sorts from `availableSorts`, applies the default sort\n * instead (`sortUnavailable`), leaves the price filter unapplied and returns `priceRange: null`. A\n * filtered set larger than `STOREFRONT_APPROXIMATE_SORT_CEILING` is never loaded in full: there\n * `'approximate'` answers as `'unavailable'` and `priceSort.fallback` reports `'unavailable'`. `availability=` and `hideWhenOutOfStock` are\n * page-scoped (D21): the page is chosen first, then filtered, so `total` counts the\n * pre-availability set and a page may be short; `facets.availability` counts the page.\n *\n * The count facets come from `storefrontFacets` \u2014 cached per assortment scope (\u00A79.1); on a miss\n * their universe load also supplies the variant index the pricing reads, and their labels ride on\n * the page's translation query. A facet universe past `STOREFRONT_FACET_UNIVERSE_CAP` products is\n * not loaded and the count facets are empty. An `options[code]` filter is resolved in one query of\n * at most `STOREFRONT_FACET_UNIVERSE_CAP` product ids; when more products match, it is left\n * unapplied like a price filter past the cap \u2014 absent from `appliedFilters` and from the facet\n * selection. The whole response, `priceRange` included, is cached by the caller on the full digest.\n */\nexport async function listStorefrontProducts(\n container: AwilixContainer,\n ctx: StoreContext,\n query: EcommerceStorefrontProductListQuery,\n options: ListStorefrontProductsOptions = {},\n): Promise<StorefrontProductListResponse> {\n const em = tryResolve<EntityManager>(container, 'em')\n const queryEngine = tryResolve<QueryEngine>(container, 'queryEngine')\n if (!em || !queryEngine) throw new Error('[internal] ecommerce storefront listing requires em and queryEngine')\n const scope = buildStorefrontProductScope(ctx)\n const runtime: Runtime = {\n container,\n ctx,\n em,\n queryEngine,\n scope,\n decryptionScope: { tenantId: ctx.tenantId, organizationId: ctx.organizationId },\n date: options.date ?? new Date(),\n facetUniverseCap: options.facetUniverseCap,\n }\n const cap = Math.max(1, options.priceSortCap ?? STOREFRONT_PRICE_SORT_CAP)\n const approximateCeiling = Math.max(cap, options.approximateSortCeiling ?? STOREFRONT_APPROXIMATE_SORT_CEILING)\n const channelFallback: EcommercePriceSortFallback = ctx.channel?.priceSortFallback ?? 'approximate'\n const { page, pageSize } = query\n\n const { extra, universe, selection, applied, searchTerm } = await resolveListFilters(runtime, query)\n const filters = composeStorefrontProductFilters(scope, extra)\n if (query.availability === 'in_stock' || query.availability === 'available') {\n applied.availability = { value: query.availability, scope: 'page' }\n }\n\n const defaultSort: EcommerceStorefrontProductSort = searchTerm ? 'relevance' : 'featured'\n const requestedSort = query.sort ?? defaultSort\n const priceSortRequested = isPriceSort(requestedSort)\n const priceFilterRequested = query.priceMin !== undefined || query.priceMax !== undefined\n const baseSort: EcommerceStorefrontProductSort =\n priceSortRequested || requestedSort === 'relevance' ? 'title_asc' : requestedSort\n\n const [countFacetState, first] = await Promise.all([\n resolveCountFacetState(runtime, selection, searchTerm, universe),\n queryCandidates(runtime, filters, sqlSortFor(baseSort), cap),\n ])\n const facetVariants = countFacetState.source ? storefrontFacetVariantIndex(countFacetState.source) : null\n const capExceeded = first.total > cap\n const fallback: EcommercePriceSortFallback =\n capExceeded && first.total > approximateCeiling ? 'unavailable' : channelFallback\n const priceOnOffer = !capExceeded || fallback === 'approximate'\n\n let sortApproximate = false\n let sortUnavailable = false\n let appliedSort: EcommerceStorefrontProductSort = requestedSort === 'relevance' && !searchTerm ? defaultSort : requestedSort\n let total = 0\n let records: ProductRecord[] = []\n let prefetch: PagePrefetch = {}\n let candidates: Candidate[] | null = null\n let amounts: Map<string, number | null> | null = null\n\n if (!capExceeded) {\n candidates = first.items\n const candidateIds = candidates.map((candidate) => candidate.id)\n const variants = facetVariants\n ? new Map(candidateIds.map((id) => [id, facetVariants.get(id) ?? []]))\n : await loadVariantIds(runtime, candidateIds)\n const pricing = await resolveStorefrontPrices(\n container,\n ctx,\n candidateIds.map((id) => ({ productId: id, variantIds: variants.get(id) ?? [] })),\n { date: runtime.date },\n )\n prefetch = { variants, pricing }\n amounts = new Map(candidateIds.map((id) => [id, pricingAmount(pricing.get(id))]))\n } else if (priceOnOffer && (priceSortRequested || priceFilterRequested)) {\n const all = await queryCandidates(runtime, filters, sqlSortFor(baseSort), first.total)\n const approximate = await loadApproximateAmounts(runtime, all.items.map((candidate) => candidate.id))\n amounts = new Map(all.items.map((candidate) => [candidate.id, approximate.get(candidate.id) ?? null]))\n candidates = all.items\n }\n const priceRange = amounts ? storefrontPriceRangeFacet(amounts.values(), ctx.currencyCode) : null\n\n if (priceFilterRequested && priceOnOffer) {\n applied.price = { min: query.priceMin ?? null, max: query.priceMax ?? null, approximate: capExceeded }\n }\n if (priceSortRequested && !priceOnOffer) {\n sortUnavailable = true\n appliedSort = defaultSort\n }\n\n if (candidates) {\n let ordered = priceFilterRequested && priceOnOffer\n ? candidates.filter((candidate) => matchesPriceFilter(amounts?.get(candidate.id) ?? null, query))\n : candidates\n if (appliedSort === 'relevance' && searchTerm) {\n ordered = rankByRelevance(ordered, searchTerm)\n } else if (isPriceSort(appliedSort)) {\n const direction = appliedSort === 'price_asc' ? 1 : -1\n ordered = ordered\n .map((candidate, index) => ({ candidate, index }))\n .sort(\n (left, right) =>\n compareAmounts(amounts?.get(left.candidate.id) ?? null, amounts?.get(right.candidate.id) ?? null, direction) ||\n left.index - right.index,\n )\n .map((entry) => entry.candidate)\n sortApproximate = capExceeded\n }\n total = ordered.length\n const pageIds = ordered.slice((page - 1) * pageSize, page * pageSize).map((candidate) => candidate.id)\n records = await queryProductsByIds(runtime, pageIds)\n } else {\n const sqlOrder: EcommerceStorefrontProductSort = appliedSort === 'relevance' ? 'title_asc' : appliedSort\n const result = await runtime.queryEngine.query<ProductRecord>(E.catalog.catalog_product, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n withDeleted: scope.withDeleted,\n filters,\n fields: LIST_FIELDS,\n sort: sqlSortFor(sqlOrder),\n page: { page, pageSize },\n })\n total = result.total\n records = result.items.filter((item) => typeof item.id === 'string')\n if (appliedSort === 'relevance' && searchTerm) {\n records = rankByRelevance(\n records.map((record) => ({ record, title: nonEmptyString(record.title) ?? '', sku: nonEmptyString(record.sku) })),\n searchTerm,\n ).map((entry) => entry.record)\n sortApproximate = true\n }\n if (facetVariants) prefetch = { variants: new Map(records.map((record) => [record.id, facetVariants.get(record.id) ?? []])) }\n }\n\n const availableSorts = availableSortsFor(searchTerm, !(capExceeded && fallback === 'unavailable'))\n const { source, cached, overCap } = countFacetState\n const rawCounts = source ? countStorefrontFacets(source, selection) : null\n const hydrated = await hydratePage(\n runtime,\n records,\n query,\n prefetch,\n source && rawCounts ? storefrontFacetTranslationRequests(source, rawCounts) : [],\n )\n let countFacets: StorefrontCountFacets = cached ?? EMPTY_COUNT_FACETS\n if (source && rawCounts) {\n countFacets = labelStorefrontCountFacets(source, rawCounts, {\n locales: localeChain(ctx),\n translations: hydrated.translations,\n assortmentScope: ctx.buyer.assortmentScope,\n productTypeLabel: await loadStorefrontProductTypeLabeler(ctx.effectiveLocale),\n })\n await writeCachedStorefrontCountFacets(container, ctx, selection, searchTerm, countFacets)\n } else if (overCap) {\n await writeCachedStorefrontCountFacets(container, ctx, selection, searchTerm, EMPTY_COUNT_FACETS)\n }\n const facets: StorefrontFacets = {\n categories: countFacets.categories,\n tags: countFacets.tags,\n priceRange,\n options: countFacets.options,\n productTypes: countFacets.productTypes,\n availability: storefrontAvailabilityFacet(hydrated.availabilityStates),\n availabilityScope: 'page',\n total,\n }\n\n return {\n items: hydrated.items,\n total,\n page,\n pageSize,\n totalPages: Math.ceil(total / pageSize),\n facets,\n effectiveLocale: ctx.effectiveLocale,\n requestedLocale: ctx.requestedLocale,\n currencyCode: ctx.currencyCode,\n taxMode: ctx.buyer.taxMode,\n appliedFilters: applied,\n availableSorts,\n appliedSort,\n priceSort: { cap, fallback, capExceeded },\n sortApproximate,\n sortUnavailable,\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["isCategoryInAssortment", "translations"]
7
+ }
@@ -0,0 +1,156 @@
1
+ import {
2
+ ecommerceStorefrontCategoryLandingQuerySchema,
3
+ ecommerceStorefrontCategoryTreeQuerySchema,
4
+ ecommerceStorefrontContextQuerySchema,
5
+ ecommerceStorefrontProductDetailQuerySchema,
6
+ ecommerceStorefrontProductListQuerySchema,
7
+ ecommerceStorefrontSearchSuggestQuerySchema
8
+ } from "../data/validators.js";
9
+ class StorefrontQueryError extends Error {
10
+ constructor(code, issues) {
11
+ super(`[internal] storefront query rejected: ${code} (${issues.map((issue) => issue.parameter).join(", ")})`);
12
+ this.status = 400;
13
+ this.name = "StorefrontQueryError";
14
+ this.code = code;
15
+ this.issues = issues;
16
+ }
17
+ }
18
+ function isStorefrontQueryError(error) {
19
+ return error instanceof StorefrontQueryError;
20
+ }
21
+ const OPTION_PARAMETER = /^options\[([^\][]*)\]$/;
22
+ function toEntries(input) {
23
+ if (input instanceof URLSearchParams) return Array.from(input.entries());
24
+ if (Symbol.iterator in Object(input)) return Array.from(input);
25
+ return Object.entries(input);
26
+ }
27
+ function splitValues(value) {
28
+ return Array.from(
29
+ new Set(
30
+ value.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0)
31
+ )
32
+ );
33
+ }
34
+ function zodIssues(error) {
35
+ const unknown = [];
36
+ const invalid = [];
37
+ for (const issue of error.issues) {
38
+ if (issue.code === "unrecognized_keys") {
39
+ for (const key of issue.keys) unknown.push({ parameter: key, message: "unknown parameter" });
40
+ continue;
41
+ }
42
+ const [head, ...rest] = issue.path.map(String);
43
+ const parameter = head === "options" && rest.length ? `options[${rest[0]}]` : head ?? "query";
44
+ invalid.push({ parameter, message: issue.message });
45
+ }
46
+ if (unknown.length) return { code: "unknown_parameter", issues: unknown };
47
+ return { code: "invalid_parameter", issues: invalid };
48
+ }
49
+ function parseStorefrontProductListQuery(input) {
50
+ return parseStorefrontListGrammar(input, ecommerceStorefrontProductListQuerySchema);
51
+ }
52
+ function parseStorefrontCategoryLandingQuery(input) {
53
+ return parseStorefrontListGrammar(input, ecommerceStorefrontCategoryLandingQuerySchema);
54
+ }
55
+ function parseStorefrontListGrammar(input, schema) {
56
+ const scalars = {};
57
+ const options = {};
58
+ const duplicates = /* @__PURE__ */ new Set();
59
+ const malformed = [];
60
+ for (const [key, value] of toEntries(input)) {
61
+ const optionMatch = OPTION_PARAMETER.exec(key);
62
+ if (optionMatch) {
63
+ const code = optionMatch[1].trim();
64
+ if (!code.length) {
65
+ malformed.push({ parameter: key, message: "option code is required" });
66
+ continue;
67
+ }
68
+ if (code === "__proto__") {
69
+ malformed.push({ parameter: key, message: "option code is invalid" });
70
+ continue;
71
+ }
72
+ if (Object.prototype.hasOwnProperty.call(options, code)) {
73
+ duplicates.add(key);
74
+ continue;
75
+ }
76
+ const values = splitValues(value);
77
+ if (!values.length) {
78
+ malformed.push({ parameter: key, message: "option values are required" });
79
+ continue;
80
+ }
81
+ options[code] = values;
82
+ continue;
83
+ }
84
+ if (key.startsWith("options")) {
85
+ malformed.push({ parameter: key, message: "options use bracket notation: options[code]=a,b" });
86
+ continue;
87
+ }
88
+ if (Object.prototype.hasOwnProperty.call(scalars, key)) {
89
+ duplicates.add(key);
90
+ continue;
91
+ }
92
+ scalars[key] = value;
93
+ }
94
+ if (duplicates.size) {
95
+ throw new StorefrontQueryError(
96
+ "duplicate_parameter",
97
+ Array.from(duplicates).map((parameter) => ({ parameter, message: "parameter given more than once" }))
98
+ );
99
+ }
100
+ if (malformed.length) throw new StorefrontQueryError("invalid_parameter", malformed);
101
+ const parsed = schema.safeParse({
102
+ ...scalars,
103
+ ...Object.keys(options).length ? { options } : {}
104
+ });
105
+ if (!parsed.success) {
106
+ const { code, issues } = zodIssues(parsed.error);
107
+ throw new StorefrontQueryError(code, issues);
108
+ }
109
+ return parsed.data;
110
+ }
111
+ function parseStorefrontProductDetailQuery(input) {
112
+ return parseStorefrontScalarQuery(input, ecommerceStorefrontProductDetailQuerySchema);
113
+ }
114
+ function parseStorefrontCategoryTreeQuery(input) {
115
+ return parseStorefrontScalarQuery(input, ecommerceStorefrontCategoryTreeQuerySchema);
116
+ }
117
+ function parseStorefrontSearchSuggestQuery(input) {
118
+ return parseStorefrontScalarQuery(input, ecommerceStorefrontSearchSuggestQuerySchema);
119
+ }
120
+ function parseStorefrontContextQuery(input) {
121
+ return parseStorefrontScalarQuery(input, ecommerceStorefrontContextQuerySchema);
122
+ }
123
+ function parseStorefrontScalarQuery(input, schema) {
124
+ const scalars = {};
125
+ const duplicates = /* @__PURE__ */ new Set();
126
+ for (const [key, value] of toEntries(input)) {
127
+ if (Object.prototype.hasOwnProperty.call(scalars, key)) {
128
+ duplicates.add(key);
129
+ continue;
130
+ }
131
+ scalars[key] = value;
132
+ }
133
+ if (duplicates.size) {
134
+ throw new StorefrontQueryError(
135
+ "duplicate_parameter",
136
+ Array.from(duplicates).map((parameter) => ({ parameter, message: "parameter given more than once" }))
137
+ );
138
+ }
139
+ const parsed = schema.safeParse(scalars);
140
+ if (!parsed.success) {
141
+ const { code, issues } = zodIssues(parsed.error);
142
+ throw new StorefrontQueryError(code, issues);
143
+ }
144
+ return parsed.data;
145
+ }
146
+ export {
147
+ StorefrontQueryError,
148
+ isStorefrontQueryError,
149
+ parseStorefrontCategoryLandingQuery,
150
+ parseStorefrontCategoryTreeQuery,
151
+ parseStorefrontContextQuery,
152
+ parseStorefrontProductDetailQuery,
153
+ parseStorefrontProductListQuery,
154
+ parseStorefrontSearchSuggestQuery
155
+ };
156
+ //# sourceMappingURL=storefrontQuery.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/ecommerce/lib/storefrontQuery.ts"],
4
+ "sourcesContent": ["import type { z } from 'zod'\nimport {\n ecommerceStorefrontCategoryLandingQuerySchema,\n ecommerceStorefrontCategoryTreeQuerySchema,\n ecommerceStorefrontContextQuerySchema,\n ecommerceStorefrontProductDetailQuerySchema,\n ecommerceStorefrontProductListQuerySchema,\n ecommerceStorefrontSearchSuggestQuerySchema,\n type EcommerceStorefrontCategoryLandingQuery,\n type EcommerceStorefrontCategoryTreeQuery,\n type EcommerceStorefrontContextQuery,\n type EcommerceStorefrontProductDetailQuery,\n type EcommerceStorefrontProductListQuery,\n type EcommerceStorefrontSearchSuggestQuery,\n} from '../data/validators'\n\nexport type StorefrontQueryErrorCode = 'unknown_parameter' | 'duplicate_parameter' | 'invalid_parameter'\n\nexport type StorefrontQueryIssue = {\n parameter: string\n message: string\n}\n\n/**\n * A rejected storefront query string (Storefront Public API \u00A74.1, R6). Unknown, repeated and\n * malformed parameters are a `400`, never silently ignored: an ignored misspelled filter shows the\n * buyer an unfiltered catalogue that looks filtered.\n */\nexport class StorefrontQueryError extends Error {\n readonly status = 400 as const\n readonly code: StorefrontQueryErrorCode\n readonly issues: StorefrontQueryIssue[]\n\n constructor(code: StorefrontQueryErrorCode, issues: StorefrontQueryIssue[]) {\n super(`[internal] storefront query rejected: ${code} (${issues.map((issue) => issue.parameter).join(', ')})`)\n this.name = 'StorefrontQueryError'\n this.code = code\n this.issues = issues\n }\n}\n\nexport function isStorefrontQueryError(error: unknown): error is StorefrontQueryError {\n return error instanceof StorefrontQueryError\n}\n\nexport type StorefrontQueryInput = URLSearchParams | Iterable<readonly [string, string]> | Record<string, string>\n\nconst OPTION_PARAMETER = /^options\\[([^\\][]*)\\]$/\n\nfunction toEntries(input: StorefrontQueryInput): Array<readonly [string, string]> {\n if (input instanceof URLSearchParams) return Array.from(input.entries())\n if (Symbol.iterator in Object(input)) return Array.from(input as Iterable<readonly [string, string]>)\n return Object.entries(input as Record<string, string>)\n}\n\nfunction splitValues(value: string): string[] {\n return Array.from(\n new Set(\n value\n .split(',')\n .map((entry) => entry.trim())\n .filter((entry) => entry.length > 0),\n ),\n )\n}\n\nfunction zodIssues(error: z.ZodError): { code: StorefrontQueryErrorCode; issues: StorefrontQueryIssue[] } {\n const unknown: StorefrontQueryIssue[] = []\n const invalid: StorefrontQueryIssue[] = []\n for (const issue of error.issues) {\n if (issue.code === 'unrecognized_keys') {\n for (const key of issue.keys) unknown.push({ parameter: key, message: 'unknown parameter' })\n continue\n }\n const [head, ...rest] = issue.path.map(String)\n const parameter = head === 'options' && rest.length ? `options[${rest[0]}]` : head ?? 'query'\n invalid.push({ parameter, message: issue.message })\n }\n if (unknown.length) return { code: 'unknown_parameter', issues: unknown }\n return { code: 'invalid_parameter', issues: invalid }\n}\n\n/**\n * Parses the `GET /products` query grammar (Storefront Public API \u00A74.1): bracket notation for\n * `options[code]=a,b`, comma lists for `tagSlugs`, `pageSize` capped at 100, strict keys. The\n * store-resolution parameters (`path`, `storeSlug`, `locale`) pass through so the route can parse\n * one query string once. Throws `StorefrontQueryError` (status 400) on any rejection.\n */\nexport function parseStorefrontProductListQuery(input: StorefrontQueryInput): EcommerceStorefrontProductListQuery {\n return parseStorefrontListGrammar(input, ecommerceStorefrontProductListQuerySchema)\n}\n\n/**\n * Parses the `GET /categories/:slug` query (Storefront Public API \u00A74.4): the `GET /products` grammar\n * for the embedded listing, minus `categoryId` / `categorySlug` \u2014 the path slug is the category, so\n * either parameter is an unknown one and a `400`.\n */\nexport function parseStorefrontCategoryLandingQuery(input: StorefrontQueryInput): EcommerceStorefrontCategoryLandingQuery {\n return parseStorefrontListGrammar(input, ecommerceStorefrontCategoryLandingQuerySchema)\n}\n\nfunction parseStorefrontListGrammar<T>(input: StorefrontQueryInput, schema: z.ZodType<T, unknown>): T {\n const scalars: Record<string, string> = {}\n const options: Record<string, string[]> = {}\n const duplicates = new Set<string>()\n const malformed: StorefrontQueryIssue[] = []\n for (const [key, value] of toEntries(input)) {\n const optionMatch = OPTION_PARAMETER.exec(key)\n if (optionMatch) {\n const code = optionMatch[1].trim()\n if (!code.length) {\n malformed.push({ parameter: key, message: 'option code is required' })\n continue\n }\n if (code === '__proto__') {\n malformed.push({ parameter: key, message: 'option code is invalid' })\n continue\n }\n if (Object.prototype.hasOwnProperty.call(options, code)) {\n duplicates.add(key)\n continue\n }\n const values = splitValues(value)\n if (!values.length) {\n malformed.push({ parameter: key, message: 'option values are required' })\n continue\n }\n options[code] = values\n continue\n }\n if (key.startsWith('options')) {\n malformed.push({ parameter: key, message: 'options use bracket notation: options[code]=a,b' })\n continue\n }\n if (Object.prototype.hasOwnProperty.call(scalars, key)) {\n duplicates.add(key)\n continue\n }\n scalars[key] = value\n }\n if (duplicates.size) {\n throw new StorefrontQueryError(\n 'duplicate_parameter',\n Array.from(duplicates).map((parameter) => ({ parameter, message: 'parameter given more than once' })),\n )\n }\n if (malformed.length) throw new StorefrontQueryError('invalid_parameter', malformed)\n const parsed = schema.safeParse({\n ...scalars,\n ...(Object.keys(options).length ? { options } : {}),\n })\n if (!parsed.success) {\n const { code, issues } = zodIssues(parsed.error)\n throw new StorefrontQueryError(code, issues)\n }\n return parsed.data\n}\n\n/**\n * Parses the `GET /products/:idOrHandle` query (Storefront Public API \u00A74.2): `variantId`, `locale`\n * and the store-resolution parameters, with the same strictness as the listing \u2014 unknown, repeated\n * and malformed parameters throw `StorefrontQueryError` (status 400).\n */\nexport function parseStorefrontProductDetailQuery(input: StorefrontQueryInput): EcommerceStorefrontProductDetailQuery {\n return parseStorefrontScalarQuery(input, ecommerceStorefrontProductDetailQuerySchema)\n}\n\n/**\n * Parses the `GET /categories` query (Storefront Public API \u00A74.3): `parentId`, `depth`,\n * `includeEmpty`, `locale` and the store-resolution parameters, strict like the other routes.\n */\nexport function parseStorefrontCategoryTreeQuery(input: StorefrontQueryInput): EcommerceStorefrontCategoryTreeQuery {\n return parseStorefrontScalarQuery(input, ecommerceStorefrontCategoryTreeQuerySchema)\n}\n\n/**\n * Parses the `GET /search/suggest` query (Storefront Public API \u00A74.5): `q` (required, at most 200\n * characters \u2014 a shorter-than-minimum `q` is answered with empty results, not rejected), `limit`\n * (default 8, max 20), `locale` and the store-resolution parameters, strict like the other routes.\n */\nexport function parseStorefrontSearchSuggestQuery(input: StorefrontQueryInput): EcommerceStorefrontSearchSuggestQuery {\n return parseStorefrontScalarQuery(input, ecommerceStorefrontSearchSuggestQuerySchema)\n}\n\n/**\n * Parses the `GET /context` query: the store-resolution parameters (`storeSlug`, `locale`, `path`)\n * only, strict like the other routes.\n */\nexport function parseStorefrontContextQuery(input: StorefrontQueryInput): EcommerceStorefrontContextQuery {\n return parseStorefrontScalarQuery(input, ecommerceStorefrontContextQuerySchema)\n}\n\nfunction parseStorefrontScalarQuery<T>(input: StorefrontQueryInput, schema: z.ZodType<T, unknown>): T {\n const scalars: Record<string, string> = {}\n const duplicates = new Set<string>()\n for (const [key, value] of toEntries(input)) {\n if (Object.prototype.hasOwnProperty.call(scalars, key)) {\n duplicates.add(key)\n continue\n }\n scalars[key] = value\n }\n if (duplicates.size) {\n throw new StorefrontQueryError(\n 'duplicate_parameter',\n Array.from(duplicates).map((parameter) => ({ parameter, message: 'parameter given more than once' })),\n )\n }\n const parsed = schema.safeParse(scalars)\n if (!parsed.success) {\n const { code, issues } = zodIssues(parsed.error)\n throw new StorefrontQueryError(code, issues)\n }\n return parsed.data\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,99 @@
1
+ import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
2
+ import { createLogger } from "@open-mercato/shared/lib/logger";
3
+ import { readEndpointRateLimitConfig } from "@open-mercato/shared/lib/ratelimit/config";
4
+ import {
5
+ checkRateLimit,
6
+ getClientIp,
7
+ RATE_LIMIT_ERROR_FALLBACK,
8
+ RATE_LIMIT_ERROR_KEY
9
+ } from "@open-mercato/shared/lib/ratelimit/helpers";
10
+ import { getTelemetryRuntime } from "@open-mercato/shared/lib/telemetry/runtime";
11
+ const STOREFRONT_RATE_LIMITS = {
12
+ context: { envPrefix: "ECOMMERCE_STOREFRONT_CONTEXT", points: 120, keyPrefix: "ecommerce_storefront_context" },
13
+ products: { envPrefix: "ECOMMERCE_STOREFRONT_PRODUCTS", points: 120, keyPrefix: "ecommerce_storefront_products" },
14
+ productDetail: {
15
+ envPrefix: "ECOMMERCE_STOREFRONT_PRODUCT_DETAIL",
16
+ points: 240,
17
+ keyPrefix: "ecommerce_storefront_product_detail"
18
+ },
19
+ categories: { envPrefix: "ECOMMERCE_STOREFRONT_CATEGORIES", points: 120, keyPrefix: "ecommerce_storefront_categories" },
20
+ categoryLanding: {
21
+ envPrefix: "ECOMMERCE_STOREFRONT_CATEGORY_LANDING",
22
+ points: 120,
23
+ keyPrefix: "ecommerce_storefront_category_landing"
24
+ },
25
+ searchSuggest: {
26
+ envPrefix: "ECOMMERCE_STOREFRONT_SEARCH_SUGGEST",
27
+ points: 300,
28
+ keyPrefix: "ecommerce_storefront_search_suggest"
29
+ }
30
+ };
31
+ const STOREFRONT_RATE_LIMIT_WINDOW_SECONDS = 60;
32
+ const STOREFRONT_RATE_LIMITED_CACHE_CONTROL = "no-store";
33
+ const logger = createLogger("ecommerce").child({ component: "storefront-rate-limit" });
34
+ let untrustedProxyWarned = false;
35
+ function warnOnceWithoutTrustedProxy(trustProxyDepth) {
36
+ if (untrustedProxyWarned || Number.isInteger(trustProxyDepth) && trustProxyDepth > 0) return;
37
+ untrustedProxyWarned = true;
38
+ logger.warn(
39
+ "Storefront rate limits are inactive until RATE_LIMIT_TRUST_PROXY_DEPTH is set; the client IP cannot be trusted without it",
40
+ { trustProxyDepth }
41
+ );
42
+ }
43
+ function readStorefrontRateLimitConfig(endpoint) {
44
+ const defaults = STOREFRONT_RATE_LIMITS[endpoint];
45
+ return readEndpointRateLimitConfig(defaults.envPrefix, {
46
+ points: defaults.points,
47
+ duration: STOREFRONT_RATE_LIMIT_WINDOW_SECONDS,
48
+ keyPrefix: defaults.keyPrefix
49
+ });
50
+ }
51
+ function buildStorefrontRateLimitKey(clientIp, storeId) {
52
+ return `${clientIp}:${storeId}`;
53
+ }
54
+ function resolveRateLimiter(container) {
55
+ const hasRegistration = container.hasRegistration;
56
+ if (typeof hasRegistration === "function" && !hasRegistration.call(container, "rateLimiterService")) return null;
57
+ try {
58
+ return container.resolve("rateLimiterService") ?? null;
59
+ } catch (error) {
60
+ logger.warn("Storefront rate limiter unavailable; serving without a limit", { err: error });
61
+ return null;
62
+ }
63
+ }
64
+ async function resolveRateLimitMessage() {
65
+ try {
66
+ const { translate } = await resolveTranslations();
67
+ return translate(RATE_LIMIT_ERROR_KEY, RATE_LIMIT_ERROR_FALLBACK);
68
+ } catch {
69
+ return RATE_LIMIT_ERROR_FALLBACK;
70
+ }
71
+ }
72
+ async function enforceStorefrontRateLimit(container, req, context, endpoint) {
73
+ const limiter = resolveRateLimiter(container);
74
+ if (!limiter) return null;
75
+ try {
76
+ warnOnceWithoutTrustedProxy(limiter.trustProxyDepth);
77
+ const clientIp = getClientIp(req, limiter.trustProxyDepth);
78
+ if (!clientIp) return null;
79
+ const limited = await checkRateLimit(
80
+ limiter,
81
+ readStorefrontRateLimitConfig(endpoint),
82
+ buildStorefrontRateLimitKey(clientIp, context.store.id),
83
+ await resolveRateLimitMessage()
84
+ );
85
+ limited?.headers.set("Cache-Control", STOREFRONT_RATE_LIMITED_CACHE_CONTROL);
86
+ return limited;
87
+ } catch (error) {
88
+ logger.warn("Storefront rate limit check failed; serving without a limit", { err: error, endpoint });
89
+ getTelemetryRuntime()?.reportError(error, { module: "ecommerce", code: "ecommerce.storefront_rate_limit_failed" });
90
+ return null;
91
+ }
92
+ }
93
+ export {
94
+ STOREFRONT_RATE_LIMITS,
95
+ buildStorefrontRateLimitKey,
96
+ enforceStorefrontRateLimit,
97
+ readStorefrontRateLimitConfig
98
+ };
99
+ //# sourceMappingURL=storefrontRateLimit.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/ecommerce/lib/storefrontRateLimit.ts"],
4
+ "sourcesContent": ["import type { NextResponse } from 'next/server'\nimport type { AppContainer } from '@open-mercato/shared/lib/di/container'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { readEndpointRateLimitConfig } from '@open-mercato/shared/lib/ratelimit/config'\nimport {\n checkRateLimit,\n getClientIp,\n RATE_LIMIT_ERROR_FALLBACK,\n RATE_LIMIT_ERROR_KEY,\n} from '@open-mercato/shared/lib/ratelimit/helpers'\nimport type { RateLimiterService } from '@open-mercato/shared/lib/ratelimit/service'\nimport type { RateLimitConfig } from '@open-mercato/shared/lib/ratelimit/types'\nimport { getTelemetryRuntime } from '@open-mercato/shared/lib/telemetry/runtime'\nimport type { StoreContext } from './types'\n\ntype StorefrontRateLimitDefaults = {\n envPrefix: string\n points: number\n keyPrefix: string\n}\n\nexport const STOREFRONT_RATE_LIMITS = {\n context: { envPrefix: 'ECOMMERCE_STOREFRONT_CONTEXT', points: 120, keyPrefix: 'ecommerce_storefront_context' },\n products: { envPrefix: 'ECOMMERCE_STOREFRONT_PRODUCTS', points: 120, keyPrefix: 'ecommerce_storefront_products' },\n productDetail: {\n envPrefix: 'ECOMMERCE_STOREFRONT_PRODUCT_DETAIL',\n points: 240,\n keyPrefix: 'ecommerce_storefront_product_detail',\n },\n categories: { envPrefix: 'ECOMMERCE_STOREFRONT_CATEGORIES', points: 120, keyPrefix: 'ecommerce_storefront_categories' },\n categoryLanding: {\n envPrefix: 'ECOMMERCE_STOREFRONT_CATEGORY_LANDING',\n points: 120,\n keyPrefix: 'ecommerce_storefront_category_landing',\n },\n searchSuggest: {\n envPrefix: 'ECOMMERCE_STOREFRONT_SEARCH_SUGGEST',\n points: 300,\n keyPrefix: 'ecommerce_storefront_search_suggest',\n },\n} as const satisfies Record<string, StorefrontRateLimitDefaults>\n\nexport type StorefrontRateLimitEndpoint = keyof typeof STOREFRONT_RATE_LIMITS\n\nconst STOREFRONT_RATE_LIMIT_WINDOW_SECONDS = 60\nconst STOREFRONT_RATE_LIMITED_CACHE_CONTROL = 'no-store'\n\nconst logger = createLogger('ecommerce').child({ component: 'storefront-rate-limit' })\n\nlet untrustedProxyWarned = false\n\nfunction warnOnceWithoutTrustedProxy(trustProxyDepth: number): void {\n if (untrustedProxyWarned || (Number.isInteger(trustProxyDepth) && trustProxyDepth > 0)) return\n untrustedProxyWarned = true\n logger.warn(\n 'Storefront rate limits are inactive until RATE_LIMIT_TRUST_PROXY_DEPTH is set; the client IP cannot be trusted without it',\n { trustProxyDepth },\n )\n}\n\nexport function readStorefrontRateLimitConfig(endpoint: StorefrontRateLimitEndpoint): RateLimitConfig {\n const defaults = STOREFRONT_RATE_LIMITS[endpoint]\n return readEndpointRateLimitConfig(defaults.envPrefix, {\n points: defaults.points,\n duration: STOREFRONT_RATE_LIMIT_WINDOW_SECONDS,\n keyPrefix: defaults.keyPrefix,\n })\n}\n\nexport function buildStorefrontRateLimitKey(clientIp: string, storeId: string): string {\n return `${clientIp}:${storeId}`\n}\n\nfunction resolveRateLimiter(container: AppContainer): RateLimiterService | null {\n const hasRegistration = (container as { hasRegistration?: (name: string) => boolean }).hasRegistration\n if (typeof hasRegistration === 'function' && !hasRegistration.call(container, 'rateLimiterService')) return null\n try {\n return (container.resolve('rateLimiterService') as RateLimiterService | null) ?? null\n } catch (error) {\n logger.warn('Storefront rate limiter unavailable; serving without a limit', { err: error })\n return null\n }\n}\n\nasync function resolveRateLimitMessage(): Promise<string> {\n try {\n const { translate } = await resolveTranslations()\n return translate(RATE_LIMIT_ERROR_KEY, RATE_LIMIT_ERROR_FALLBACK)\n } catch {\n return RATE_LIMIT_ERROR_FALLBACK\n }\n}\n\n/**\n * Per-IP-per-store limit for the public storefront routes (spec 4 \u00A79). Called after the store is\n * resolved, because the key is `ip:storeId`. Fail-open: an unavailable limiter or an unresolvable\n * client IP (no trusted proxy depth configured) serves the request instead of rejecting it; the\n * missing proxy depth is logged once per process so the inactive limit is visible to operators.\n */\nexport async function enforceStorefrontRateLimit(\n container: AppContainer,\n req: Request,\n context: StoreContext,\n endpoint: StorefrontRateLimitEndpoint,\n): Promise<NextResponse | null> {\n const limiter = resolveRateLimiter(container)\n if (!limiter) return null\n try {\n warnOnceWithoutTrustedProxy(limiter.trustProxyDepth)\n const clientIp = getClientIp(req, limiter.trustProxyDepth)\n if (!clientIp) return null\n const limited = await checkRateLimit(\n limiter,\n readStorefrontRateLimitConfig(endpoint),\n buildStorefrontRateLimitKey(clientIp, context.store.id),\n await resolveRateLimitMessage(),\n )\n limited?.headers.set('Cache-Control', STOREFRONT_RATE_LIMITED_CACHE_CONTROL)\n return limited\n } catch (error) {\n logger.warn('Storefront rate limit check failed; serving without a limit', { err: error, endpoint })\n getTelemetryRuntime()?.reportError(error, { module: 'ecommerce', code: 'ecommerce.storefront_rate_limit_failed' })\n return null\n }\n}\n"],
5
+ "mappings": "AAEA,SAAS,2BAA2B;AACpC,SAAS,oBAAoB;AAC7B,SAAS,mCAAmC;AAC5C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,SAAS,2BAA2B;AAS7B,MAAM,yBAAyB;AAAA,EACpC,SAAS,EAAE,WAAW,gCAAgC,QAAQ,KAAK,WAAW,+BAA+B;AAAA,EAC7G,UAAU,EAAE,WAAW,iCAAiC,QAAQ,KAAK,WAAW,gCAAgC;AAAA,EAChH,eAAe;AAAA,IACb,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,WAAW;AAAA,EACb;AAAA,EACA,YAAY,EAAE,WAAW,mCAAmC,QAAQ,KAAK,WAAW,kCAAkC;AAAA,EACtH,iBAAiB;AAAA,IACf,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,WAAW;AAAA,EACb;AAAA,EACA,eAAe;AAAA,IACb,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,WAAW;AAAA,EACb;AACF;AAIA,MAAM,uCAAuC;AAC7C,MAAM,wCAAwC;AAE9C,MAAM,SAAS,aAAa,WAAW,EAAE,MAAM,EAAE,WAAW,wBAAwB,CAAC;AAErF,IAAI,uBAAuB;AAE3B,SAAS,4BAA4B,iBAA+B;AAClE,MAAI,wBAAyB,OAAO,UAAU,eAAe,KAAK,kBAAkB,EAAI;AACxF,yBAAuB;AACvB,SAAO;AAAA,IACL;AAAA,IACA,EAAE,gBAAgB;AAAA,EACpB;AACF;AAEO,SAAS,8BAA8B,UAAwD;AACpG,QAAM,WAAW,uBAAuB,QAAQ;AAChD,SAAO,4BAA4B,SAAS,WAAW;AAAA,IACrD,QAAQ,SAAS;AAAA,IACjB,UAAU;AAAA,IACV,WAAW,SAAS;AAAA,EACtB,CAAC;AACH;AAEO,SAAS,4BAA4B,UAAkB,SAAyB;AACrF,SAAO,GAAG,QAAQ,IAAI,OAAO;AAC/B;AAEA,SAAS,mBAAmB,WAAoD;AAC9E,QAAM,kBAAmB,UAA8D;AACvF,MAAI,OAAO,oBAAoB,cAAc,CAAC,gBAAgB,KAAK,WAAW,oBAAoB,EAAG,QAAO;AAC5G,MAAI;AACF,WAAQ,UAAU,QAAQ,oBAAoB,KAAmC;AAAA,EACnF,SAAS,OAAO;AACd,WAAO,KAAK,gEAAgE,EAAE,KAAK,MAAM,CAAC;AAC1F,WAAO;AAAA,EACT;AACF;AAEA,eAAe,0BAA2C;AACxD,MAAI;AACF,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO,UAAU,sBAAsB,yBAAyB;AAAA,EAClE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAQA,eAAsB,2BACpB,WACA,KACA,SACA,UAC8B;AAC9B,QAAM,UAAU,mBAAmB,SAAS;AAC5C,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI;AACF,gCAA4B,QAAQ,eAAe;AACnD,UAAM,WAAW,YAAY,KAAK,QAAQ,eAAe;AACzD,QAAI,CAAC,SAAU,QAAO;AACtB,UAAM,UAAU,MAAM;AAAA,MACpB;AAAA,MACA,8BAA8B,QAAQ;AAAA,MACtC,4BAA4B,UAAU,QAAQ,MAAM,EAAE;AAAA,MACtD,MAAM,wBAAwB;AAAA,IAChC;AACA,aAAS,QAAQ,IAAI,iBAAiB,qCAAqC;AAC3E,WAAO;AAAA,EACT,SAAS,OAAO;AACd,WAAO,KAAK,+DAA+D,EAAE,KAAK,OAAO,SAAS,CAAC;AACnG,wBAAoB,GAAG,YAAY,OAAO,EAAE,QAAQ,aAAa,MAAM,yCAAyC,CAAC;AACjH,WAAO;AAAA,EACT;AACF;",
6
+ "names": []
7
+ }