@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.
- package/.turbo/turbo-build.log +2 -2
- package/dist/generated/entities/catalog_price_history_entry/index.js +53 -0
- package/dist/generated/entities/catalog_price_history_entry/index.js.map +7 -0
- package/dist/generated/entities/customer_group_terms/index.js +2 -0
- package/dist/generated/entities/customer_group_terms/index.js.map +2 -2
- package/dist/generated/entities/ecommerce_store/index.js +33 -0
- package/dist/generated/entities/ecommerce_store/index.js.map +7 -0
- package/dist/generated/entities/ecommerce_store_channel_binding/index.js +29 -0
- package/dist/generated/entities/ecommerce_store_channel_binding/index.js.map +7 -0
- package/dist/generated/entities/ecommerce_store_domain_binding/index.js +23 -0
- package/dist/generated/entities/ecommerce_store_domain_binding/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +8 -1
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +72 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/helpers/integration/customerGroupsFixtures.js.map +2 -2
- package/dist/modules/catalog/acl.js +8 -1
- package/dist/modules/catalog/acl.js.map +2 -2
- package/dist/modules/catalog/api/config/omnibus/route.js +184 -0
- package/dist/modules/catalog/api/config/omnibus/route.js.map +7 -0
- package/dist/modules/catalog/api/prices/history/route.js +131 -0
- package/dist/modules/catalog/api/prices/history/route.js.map +7 -0
- package/dist/modules/catalog/api/prices/omnibus-preview/route.js +184 -0
- package/dist/modules/catalog/api/prices/omnibus-preview/route.js.map +7 -0
- package/dist/modules/catalog/api/products/route.js +54 -245
- package/dist/modules/catalog/api/products/route.js.map +3 -3
- package/dist/modules/catalog/api/tags/route.js +6 -0
- package/dist/modules/catalog/api/tags/route.js.map +2 -2
- package/dist/modules/catalog/backend/config/catalog/page.js +3 -1
- package/dist/modules/catalog/backend/config/catalog/page.js.map +2 -2
- package/dist/modules/catalog/cli.js +80 -2
- package/dist/modules/catalog/cli.js.map +2 -2
- package/dist/modules/catalog/commands/categories.js +51 -2
- package/dist/modules/catalog/commands/categories.js.map +2 -2
- package/dist/modules/catalog/commands/prices.js +43 -1
- package/dist/modules/catalog/commands/prices.js.map +2 -2
- package/dist/modules/catalog/commands/products.js +71 -0
- package/dist/modules/catalog/commands/products.js.map +2 -2
- package/dist/modules/catalog/commands/variants.js +7 -0
- package/dist/modules/catalog/commands/variants.js.map +2 -2
- package/dist/modules/catalog/components/OmnibusSettings.js +703 -0
- package/dist/modules/catalog/components/OmnibusSettings.js.map +7 -0
- package/dist/modules/catalog/components/PriceEditorOmnibusRow.js +152 -0
- package/dist/modules/catalog/components/PriceEditorOmnibusRow.js.map +7 -0
- package/dist/modules/catalog/components/lookupSources.js +31 -0
- package/dist/modules/catalog/components/lookupSources.js.map +7 -0
- package/dist/modules/catalog/components/prices/PriceScopeSelectors.js +8 -2
- package/dist/modules/catalog/components/prices/PriceScopeSelectors.js.map +2 -2
- package/dist/modules/catalog/components/products/VariantBuilder.js +11 -1
- package/dist/modules/catalog/components/products/VariantBuilder.js.map +2 -2
- package/dist/modules/catalog/data/entities.js +122 -1
- package/dist/modules/catalog/data/entities.js.map +2 -2
- package/dist/modules/catalog/data/validators.js +78 -0
- package/dist/modules/catalog/data/validators.js.map +2 -2
- package/dist/modules/catalog/di.js +18 -0
- package/dist/modules/catalog/di.js.map +2 -2
- package/dist/modules/catalog/lib/omnibus.js +285 -0
- package/dist/modules/catalog/lib/omnibus.js.map +7 -0
- package/dist/modules/catalog/lib/omnibusBackfill.js +146 -0
- package/dist/modules/catalog/lib/omnibusBackfill.js.map +7 -0
- package/dist/modules/catalog/lib/omnibusCache.js +89 -0
- package/dist/modules/catalog/lib/omnibusCache.js.map +7 -0
- package/dist/modules/catalog/lib/omnibusConfig.js +64 -0
- package/dist/modules/catalog/lib/omnibusConfig.js.map +7 -0
- package/dist/modules/catalog/lib/omnibusHistoryQueries.js +191 -0
- package/dist/modules/catalog/lib/omnibusHistoryQueries.js.map +7 -0
- package/dist/modules/catalog/lib/omnibusPresentedEntry.js +73 -0
- package/dist/modules/catalog/lib/omnibusPresentedEntry.js.map +7 -0
- package/dist/modules/catalog/lib/omnibusProductListEnrichment.js +94 -0
- package/dist/modules/catalog/lib/omnibusProductListEnrichment.js.map +7 -0
- package/dist/modules/catalog/lib/omnibusTypes.js +115 -0
- package/dist/modules/catalog/lib/omnibusTypes.js.map +7 -0
- package/dist/modules/catalog/lib/optionSchemaTranslations.js +55 -0
- package/dist/modules/catalog/lib/optionSchemaTranslations.js.map +7 -0
- package/dist/modules/catalog/lib/priceHistoryQuery.js +103 -0
- package/dist/modules/catalog/lib/priceHistoryQuery.js.map +7 -0
- package/dist/modules/catalog/lib/pricing.js +26 -2
- package/dist/modules/catalog/lib/pricing.js.map +2 -2
- package/dist/modules/catalog/lib/productFilters.js +273 -0
- package/dist/modules/catalog/lib/productFilters.js.map +7 -0
- package/dist/modules/catalog/lib/productScopeKeys.js +105 -0
- package/dist/modules/catalog/lib/productScopeKeys.js.map +7 -0
- package/dist/modules/catalog/lib/productScopeReindex.js +73 -0
- package/dist/modules/catalog/lib/productScopeReindex.js.map +7 -0
- package/dist/modules/catalog/migrations/Migration20261005105352_catalog.js +39 -0
- package/dist/modules/catalog/migrations/Migration20261005105352_catalog.js.map +7 -0
- package/dist/modules/catalog/migrations/Migration20261005150824_catalog.js +39 -0
- package/dist/modules/catalog/migrations/Migration20261005150824_catalog.js.map +7 -0
- package/dist/modules/catalog/services/catalogOmnibusService.js +480 -0
- package/dist/modules/catalog/services/catalogOmnibusService.js.map +7 -0
- package/dist/modules/catalog/setup.js +3 -1
- package/dist/modules/catalog/setup.js.map +2 -2
- package/dist/modules/catalog/subscribers/product-scope-keys-category-deleted.js +16 -0
- package/dist/modules/catalog/subscribers/product-scope-keys-category-deleted.js.map +7 -0
- package/dist/modules/catalog/subscribers/product-scope-keys-category-updated.js +17 -0
- package/dist/modules/catalog/subscribers/product-scope-keys-category-updated.js.map +7 -0
- package/dist/modules/catalog/translations.js +10 -0
- package/dist/modules/catalog/translations.js.map +2 -2
- package/dist/modules/configs/lib/upgrade-actions.js +28 -0
- package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
- package/dist/modules/customer_groups/api/customer-groups/[id]/terms/route.js +5 -0
- package/dist/modules/customer_groups/api/customer-groups/[id]/terms/route.js.map +2 -2
- package/dist/modules/customer_groups/components/CustomerGroupTermsSection.js +111 -3
- package/dist/modules/customer_groups/components/CustomerGroupTermsSection.js.map +2 -2
- package/dist/modules/customer_groups/components/customerGroupAssortmentScope.js +43 -0
- package/dist/modules/customer_groups/components/customerGroupAssortmentScope.js.map +7 -0
- package/dist/modules/customer_groups/data/entities.js +3 -0
- package/dist/modules/customer_groups/data/entities.js.map +2 -2
- package/dist/modules/customer_groups/data/validators.js +9 -0
- package/dist/modules/customer_groups/data/validators.js.map +2 -2
- package/dist/modules/customer_groups/migrations/Migration20261005110250_customer_groups.js +17 -0
- package/dist/modules/customer_groups/migrations/Migration20261005110250_customer_groups.js.map +7 -0
- package/dist/modules/customer_groups/services/customerGroupsService.js +62 -15
- package/dist/modules/customer_groups/services/customerGroupsService.js.map +2 -2
- package/dist/modules/ecommerce/acl.js +13 -0
- package/dist/modules/ecommerce/acl.js.map +7 -0
- package/dist/modules/ecommerce/api/domain-mappings/route.js +72 -0
- package/dist/modules/ecommerce/api/domain-mappings/route.js.map +7 -0
- package/dist/modules/ecommerce/api/openapi.js +27 -0
- package/dist/modules/ecommerce/api/openapi.js.map +7 -0
- package/dist/modules/ecommerce/api/store-channel-bindings/[id]/assortment-count/route.js +109 -0
- package/dist/modules/ecommerce/api/store-channel-bindings/[id]/assortment-count/route.js.map +7 -0
- package/dist/modules/ecommerce/api/store-channel-bindings/crud.js +265 -0
- package/dist/modules/ecommerce/api/store-channel-bindings/crud.js.map +7 -0
- package/dist/modules/ecommerce/api/store-channel-bindings/route.js +67 -0
- package/dist/modules/ecommerce/api/store-channel-bindings/route.js.map +7 -0
- package/dist/modules/ecommerce/api/store-domain-bindings/crud.js +296 -0
- package/dist/modules/ecommerce/api/store-domain-bindings/crud.js.map +7 -0
- package/dist/modules/ecommerce/api/store-domain-bindings/route.js +73 -0
- package/dist/modules/ecommerce/api/store-domain-bindings/route.js.map +7 -0
- package/dist/modules/ecommerce/api/storefront/categories/[slug]/route.js +87 -0
- package/dist/modules/ecommerce/api/storefront/categories/[slug]/route.js.map +7 -0
- package/dist/modules/ecommerce/api/storefront/categories/openapiSchemas.js +50 -0
- package/dist/modules/ecommerce/api/storefront/categories/openapiSchemas.js.map +7 -0
- package/dist/modules/ecommerce/api/storefront/categories/route.js +75 -0
- package/dist/modules/ecommerce/api/storefront/categories/route.js.map +7 -0
- package/dist/modules/ecommerce/api/storefront/context/route.js +163 -0
- package/dist/modules/ecommerce/api/storefront/context/route.js.map +7 -0
- package/dist/modules/ecommerce/api/storefront/products/[idOrHandle]/route.js +87 -0
- package/dist/modules/ecommerce/api/storefront/products/[idOrHandle]/route.js.map +7 -0
- package/dist/modules/ecommerce/api/storefront/products/openapiSchemas.js +177 -0
- package/dist/modules/ecommerce/api/storefront/products/openapiSchemas.js.map +7 -0
- package/dist/modules/ecommerce/api/storefront/products/route.js +78 -0
- package/dist/modules/ecommerce/api/storefront/products/route.js.map +7 -0
- package/dist/modules/ecommerce/api/storefront/search/suggest/openapiSchemas.js +25 -0
- package/dist/modules/ecommerce/api/storefront/search/suggest/openapiSchemas.js.map +7 -0
- package/dist/modules/ecommerce/api/storefront/search/suggest/route.js +75 -0
- package/dist/modules/ecommerce/api/storefront/search/suggest/route.js.map +7 -0
- package/dist/modules/ecommerce/api/storefront/storefrontRouteSupport.js +58 -0
- package/dist/modules/ecommerce/api/storefront/storefrontRouteSupport.js.map +7 -0
- package/dist/modules/ecommerce/api/stores/[id]/branding/route.js +183 -0
- package/dist/modules/ecommerce/api/stores/[id]/branding/route.js.map +7 -0
- package/dist/modules/ecommerce/api/stores/[id]/preview-branding/route.js +92 -0
- package/dist/modules/ecommerce/api/stores/[id]/preview-branding/route.js.map +7 -0
- package/dist/modules/ecommerce/api/stores/crud.js +401 -0
- package/dist/modules/ecommerce/api/stores/crud.js.map +7 -0
- package/dist/modules/ecommerce/api/stores/route.js +64 -0
- package/dist/modules/ecommerce/api/stores/route.js.map +7 -0
- package/dist/modules/ecommerce/backend/config/ecommerce/[id]/page.js +9 -0
- package/dist/modules/ecommerce/backend/config/ecommerce/[id]/page.js.map +7 -0
- package/dist/modules/ecommerce/backend/config/ecommerce/[id]/page.meta.js +16 -0
- package/dist/modules/ecommerce/backend/config/ecommerce/[id]/page.meta.js.map +7 -0
- package/dist/modules/ecommerce/backend/config/ecommerce/page.js +9 -0
- package/dist/modules/ecommerce/backend/config/ecommerce/page.js.map +7 -0
- package/dist/modules/ecommerce/backend/config/ecommerce/page.meta.js +16 -0
- package/dist/modules/ecommerce/backend/config/ecommerce/page.meta.js.map +7 -0
- package/dist/modules/ecommerce/commands/stores.js +110 -0
- package/dist/modules/ecommerce/commands/stores.js.map +7 -0
- package/dist/modules/ecommerce/components/ChannelAssortmentCount.js +90 -0
- package/dist/modules/ecommerce/components/ChannelAssortmentCount.js.map +7 -0
- package/dist/modules/ecommerce/components/ChannelBindingFormSections.js +33 -0
- package/dist/modules/ecommerce/components/ChannelBindingFormSections.js.map +7 -0
- package/dist/modules/ecommerce/components/ChannelLookupPicker.js +11 -0
- package/dist/modules/ecommerce/components/ChannelLookupPicker.js.map +7 -0
- package/dist/modules/ecommerce/components/DomainStatusWarning.js +24 -0
- package/dist/modules/ecommerce/components/DomainStatusWarning.js.map +7 -0
- package/dist/modules/ecommerce/components/StoreAvailabilityDefaultsSection.js +190 -0
- package/dist/modules/ecommerce/components/StoreAvailabilityDefaultsSection.js.map +7 -0
- package/dist/modules/ecommerce/components/StoreBrandingControls.js +67 -0
- package/dist/modules/ecommerce/components/StoreBrandingControls.js.map +7 -0
- package/dist/modules/ecommerce/components/StoreBrandingPreview.js +69 -0
- package/dist/modules/ecommerce/components/StoreBrandingPreview.js.map +7 -0
- package/dist/modules/ecommerce/components/StoreBrandingTab.js +218 -0
- package/dist/modules/ecommerce/components/StoreBrandingTab.js.map +7 -0
- package/dist/modules/ecommerce/components/StoreChannelBindingDialog.js +270 -0
- package/dist/modules/ecommerce/components/StoreChannelBindingDialog.js.map +7 -0
- package/dist/modules/ecommerce/components/StoreChannelsTab.js +313 -0
- package/dist/modules/ecommerce/components/StoreChannelsTab.js.map +7 -0
- package/dist/modules/ecommerce/components/StoreCreateDialog.js +164 -0
- package/dist/modules/ecommerce/components/StoreCreateDialog.js.map +7 -0
- package/dist/modules/ecommerce/components/StoreDomainBindingDialog.js +162 -0
- package/dist/modules/ecommerce/components/StoreDomainBindingDialog.js.map +7 -0
- package/dist/modules/ecommerce/components/StoreDomainsTab.js +357 -0
- package/dist/modules/ecommerce/components/StoreDomainsTab.js.map +7 -0
- package/dist/modules/ecommerce/components/StoreEditPage.js +155 -0
- package/dist/modules/ecommerce/components/StoreEditPage.js.map +7 -0
- package/dist/modules/ecommerce/components/StoreGeneralTab.js +167 -0
- package/dist/modules/ecommerce/components/StoreGeneralTab.js.map +7 -0
- package/dist/modules/ecommerce/components/StoreListPage.js +290 -0
- package/dist/modules/ecommerce/components/StoreListPage.js.map +7 -0
- package/dist/modules/ecommerce/components/StoreSeoTab.js +132 -0
- package/dist/modules/ecommerce/components/StoreSeoTab.js.map +7 -0
- package/dist/modules/ecommerce/components/StoreStatusBadge.js +20 -0
- package/dist/modules/ecommerce/components/StoreStatusBadge.js.map +7 -0
- package/dist/modules/ecommerce/components/channelLookups.js +28 -0
- package/dist/modules/ecommerce/components/channelLookups.js.map +7 -0
- package/dist/modules/ecommerce/components/storeAdmin.js +41 -0
- package/dist/modules/ecommerce/components/storeAdmin.js.map +7 -0
- package/dist/modules/ecommerce/components/storeBrandingForm.js +197 -0
- package/dist/modules/ecommerce/components/storeBrandingForm.js.map +7 -0
- package/dist/modules/ecommerce/components/storeChannels.js +140 -0
- package/dist/modules/ecommerce/components/storeChannels.js.map +7 -0
- package/dist/modules/ecommerce/components/storeDomains.js +167 -0
- package/dist/modules/ecommerce/components/storeDomains.js.map +7 -0
- package/dist/modules/ecommerce/components/storeEditTabs.js +62 -0
- package/dist/modules/ecommerce/components/storeEditTabs.js.map +7 -0
- package/dist/modules/ecommerce/components/storeGeneral.js +95 -0
- package/dist/modules/ecommerce/components/storeGeneral.js.map +7 -0
- package/dist/modules/ecommerce/components/storeSeo.js +69 -0
- package/dist/modules/ecommerce/components/storeSeo.js.map +7 -0
- package/dist/modules/ecommerce/components/useChannelAssortmentCount.js +45 -0
- package/dist/modules/ecommerce/components/useChannelAssortmentCount.js.map +7 -0
- package/dist/modules/ecommerce/components/useStoreAccess.js +28 -0
- package/dist/modules/ecommerce/components/useStoreAccess.js.map +7 -0
- package/dist/modules/ecommerce/data/enrichers.js +157 -0
- package/dist/modules/ecommerce/data/enrichers.js.map +7 -0
- package/dist/modules/ecommerce/data/entities.js +197 -0
- package/dist/modules/ecommerce/data/entities.js.map +7 -0
- package/dist/modules/ecommerce/data/validators.js +406 -0
- package/dist/modules/ecommerce/data/validators.js.map +7 -0
- package/dist/modules/ecommerce/di.js +11 -0
- package/dist/modules/ecommerce/di.js.map +7 -0
- package/dist/modules/ecommerce/events.js +27 -0
- package/dist/modules/ecommerce/events.js.map +7 -0
- package/dist/modules/ecommerce/index.js +16 -0
- package/dist/modules/ecommerce/index.js.map +7 -0
- package/dist/modules/ecommerce/lib/assortmentCount.js +112 -0
- package/dist/modules/ecommerce/lib/assortmentCount.js.map +7 -0
- package/dist/modules/ecommerce/lib/brandingStyles.js +184 -0
- package/dist/modules/ecommerce/lib/brandingStyles.js.map +7 -0
- package/dist/modules/ecommerce/lib/buyerContext.js +284 -0
- package/dist/modules/ecommerce/lib/buyerContext.js.map +7 -0
- package/dist/modules/ecommerce/lib/cacheInvalidation.js +186 -0
- package/dist/modules/ecommerce/lib/cacheInvalidation.js.map +7 -0
- package/dist/modules/ecommerce/lib/cacheKeys.js +236 -0
- package/dist/modules/ecommerce/lib/cacheKeys.js.map +7 -0
- package/dist/modules/ecommerce/lib/createConflictRecheck.js +29 -0
- package/dist/modules/ecommerce/lib/createConflictRecheck.js.map +7 -0
- package/dist/modules/ecommerce/lib/crudEvents.js +94 -0
- package/dist/modules/ecommerce/lib/crudEvents.js.map +7 -0
- package/dist/modules/ecommerce/lib/crudSupport.js +64 -0
- package/dist/modules/ecommerce/lib/crudSupport.js.map +7 -0
- package/dist/modules/ecommerce/lib/domainBindingRebind.js +99 -0
- package/dist/modules/ecommerce/lib/domainBindingRebind.js.map +7 -0
- package/dist/modules/ecommerce/lib/domainMappingSummaries.js +39 -0
- package/dist/modules/ecommerce/lib/domainMappingSummaries.js.map +7 -0
- package/dist/modules/ecommerce/lib/exclusiveFlag.js +57 -0
- package/dist/modules/ecommerce/lib/exclusiveFlag.js.map +7 -0
- package/dist/modules/ecommerce/lib/references.js +70 -0
- package/dist/modules/ecommerce/lib/references.js.map +7 -0
- package/dist/modules/ecommerce/lib/seedDraftStore.js +99 -0
- package/dist/modules/ecommerce/lib/seedDraftStore.js.map +7 -0
- package/dist/modules/ecommerce/lib/storeBindingCascade.js +109 -0
- package/dist/modules/ecommerce/lib/storeBindingCascade.js.map +7 -0
- package/dist/modules/ecommerce/lib/storeBranding.js +40 -0
- package/dist/modules/ecommerce/lib/storeBranding.js.map +7 -0
- package/dist/modules/ecommerce/lib/storeBrandingRoute.js +76 -0
- package/dist/modules/ecommerce/lib/storeBrandingRoute.js.map +7 -0
- package/dist/modules/ecommerce/lib/storeContext.js +393 -0
- package/dist/modules/ecommerce/lib/storeContext.js.map +7 -0
- package/dist/modules/ecommerce/lib/storeContextService.js +43 -0
- package/dist/modules/ecommerce/lib/storeContextService.js.map +7 -0
- package/dist/modules/ecommerce/lib/storeLocale.js +60 -0
- package/dist/modules/ecommerce/lib/storeLocale.js.map +7 -0
- package/dist/modules/ecommerce/lib/storeNotifications.js +41 -0
- package/dist/modules/ecommerce/lib/storeNotifications.js.map +7 -0
- package/dist/modules/ecommerce/lib/storefrontCatalogSupport.js +248 -0
- package/dist/modules/ecommerce/lib/storefrontCatalogSupport.js.map +7 -0
- package/dist/modules/ecommerce/lib/storefrontCategories.js +241 -0
- package/dist/modules/ecommerce/lib/storefrontCategories.js.map +7 -0
- package/dist/modules/ecommerce/lib/storefrontCategoryCache.js +71 -0
- package/dist/modules/ecommerce/lib/storefrontCategoryCache.js.map +7 -0
- package/dist/modules/ecommerce/lib/storefrontDetail.js +447 -0
- package/dist/modules/ecommerce/lib/storefrontDetail.js.map +7 -0
- package/dist/modules/ecommerce/lib/storefrontFacets.js +441 -0
- package/dist/modules/ecommerce/lib/storefrontFacets.js.map +7 -0
- package/dist/modules/ecommerce/lib/storefrontOptionFilter.js +15 -0
- package/dist/modules/ecommerce/lib/storefrontOptionFilter.js.map +7 -0
- package/dist/modules/ecommerce/lib/storefrontPricing.js +393 -0
- package/dist/modules/ecommerce/lib/storefrontPricing.js.map +7 -0
- package/dist/modules/ecommerce/lib/storefrontProductCache.js +90 -0
- package/dist/modules/ecommerce/lib/storefrontProductCache.js.map +7 -0
- package/dist/modules/ecommerce/lib/storefrontProductScope.js +107 -0
- package/dist/modules/ecommerce/lib/storefrontProductScope.js.map +7 -0
- package/dist/modules/ecommerce/lib/storefrontProducts.js +670 -0
- package/dist/modules/ecommerce/lib/storefrontProducts.js.map +7 -0
- package/dist/modules/ecommerce/lib/storefrontQuery.js +156 -0
- package/dist/modules/ecommerce/lib/storefrontQuery.js.map +7 -0
- package/dist/modules/ecommerce/lib/storefrontRateLimit.js +99 -0
- package/dist/modules/ecommerce/lib/storefrontRateLimit.js.map +7 -0
- package/dist/modules/ecommerce/lib/storefrontSearch.js +303 -0
- package/dist/modules/ecommerce/lib/storefrontSearch.js.map +7 -0
- package/dist/modules/ecommerce/lib/storefrontSearchCache.js +35 -0
- package/dist/modules/ecommerce/lib/storefrontSearchCache.js.map +7 -0
- package/dist/modules/ecommerce/lib/subscriberSupport.js +33 -0
- package/dist/modules/ecommerce/lib/subscriberSupport.js.map +7 -0
- package/dist/modules/ecommerce/lib/types.js +1 -0
- package/dist/modules/ecommerce/lib/types.js.map +7 -0
- package/dist/modules/ecommerce/migrations/Migration20261005113233_ecommerce.js +32 -0
- package/dist/modules/ecommerce/migrations/Migration20261005113233_ecommerce.js.map +7 -0
- package/dist/modules/ecommerce/migrations/Migration20261006112733_ecommerce.js +17 -0
- package/dist/modules/ecommerce/migrations/Migration20261006112733_ecommerce.js.map +7 -0
- package/dist/modules/ecommerce/notifications.client.js +47 -0
- package/dist/modules/ecommerce/notifications.client.js.map +7 -0
- package/dist/modules/ecommerce/notifications.js +48 -0
- package/dist/modules/ecommerce/notifications.js.map +7 -0
- package/dist/modules/ecommerce/search.js +83 -0
- package/dist/modules/ecommerce/search.js.map +7 -0
- package/dist/modules/ecommerce/setup.js +17 -0
- package/dist/modules/ecommerce/setup.js.map +7 -0
- package/dist/modules/ecommerce/subscribers/assortment-empty-notifications.js +14 -0
- package/dist/modules/ecommerce/subscribers/assortment-empty-notifications.js.map +7 -0
- package/dist/modules/ecommerce/subscribers/availability-policy-cache-invalidation.js +14 -0
- package/dist/modules/ecommerce/subscribers/availability-policy-cache-invalidation.js.map +7 -0
- package/dist/modules/ecommerce/subscribers/catalog-category-cache-invalidation.js +14 -0
- package/dist/modules/ecommerce/subscribers/catalog-category-cache-invalidation.js.map +7 -0
- package/dist/modules/ecommerce/subscribers/catalog-price-cache-invalidation.js +14 -0
- package/dist/modules/ecommerce/subscribers/catalog-price-cache-invalidation.js.map +7 -0
- package/dist/modules/ecommerce/subscribers/catalog-product-cache-invalidation.js +14 -0
- package/dist/modules/ecommerce/subscribers/catalog-product-cache-invalidation.js.map +7 -0
- package/dist/modules/ecommerce/subscribers/catalog-variant-cache-invalidation.js +14 -0
- package/dist/modules/ecommerce/subscribers/catalog-variant-cache-invalidation.js.map +7 -0
- package/dist/modules/ecommerce/subscribers/customer-group-cache-invalidation.js +14 -0
- package/dist/modules/ecommerce/subscribers/customer-group-cache-invalidation.js.map +7 -0
- package/dist/modules/ecommerce/subscribers/customer-group-membership-cache-invalidation.js +14 -0
- package/dist/modules/ecommerce/subscribers/customer-group-membership-cache-invalidation.js.map +7 -0
- package/dist/modules/ecommerce/subscribers/customer-group-terms-cache-invalidation.js +14 -0
- package/dist/modules/ecommerce/subscribers/customer-group-terms-cache-invalidation.js.map +7 -0
- package/dist/modules/ecommerce/subscribers/customer-user-deleted-cache-invalidation.js +14 -0
- package/dist/modules/ecommerce/subscribers/customer-user-deleted-cache-invalidation.js.map +7 -0
- package/dist/modules/ecommerce/subscribers/customer-user-updated-cache-invalidation.js +14 -0
- package/dist/modules/ecommerce/subscribers/customer-user-updated-cache-invalidation.js.map +7 -0
- package/dist/modules/ecommerce/subscribers/domain-binding-rebind.js +16 -0
- package/dist/modules/ecommerce/subscribers/domain-binding-rebind.js.map +7 -0
- package/dist/modules/ecommerce/subscribers/domain-mapping-cache-invalidation.js +14 -0
- package/dist/modules/ecommerce/subscribers/domain-mapping-cache-invalidation.js.map +7 -0
- package/dist/modules/ecommerce/subscribers/store-cache-invalidation.js +14 -0
- package/dist/modules/ecommerce/subscribers/store-cache-invalidation.js.map +7 -0
- package/dist/modules/ecommerce/subscribers/store-channel-binding-cache-invalidation.js +14 -0
- package/dist/modules/ecommerce/subscribers/store-channel-binding-cache-invalidation.js.map +7 -0
- package/dist/modules/ecommerce/subscribers/store-domain-binding-cache-invalidation.js +14 -0
- package/dist/modules/ecommerce/subscribers/store-domain-binding-cache-invalidation.js.map +7 -0
- package/dist/modules/ecommerce/subscribers/store-notifications.js +14 -0
- package/dist/modules/ecommerce/subscribers/store-notifications.js.map +7 -0
- package/dist/modules/query_index/lib/batch.js +23 -9
- package/dist/modules/query_index/lib/batch.js.map +2 -2
- package/dist/modules/query_index/lib/doc-enrichers.js +212 -0
- package/dist/modules/query_index/lib/doc-enrichers.js.map +7 -0
- package/dist/modules/query_index/lib/document.js +2 -0
- package/dist/modules/query_index/lib/document.js.map +2 -2
- package/dist/modules/query_index/lib/engine.js +68 -1
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/dist/modules/query_index/lib/indexer.js +7 -0
- package/dist/modules/query_index/lib/indexer.js.map +2 -2
- package/dist/modules/query_index/lib/search-tokens.js +2 -0
- package/dist/modules/query_index/lib/search-tokens.js.map +2 -2
- package/dist/modules/query_index/migrations/Migration20261005201500_query_index.js +17 -0
- package/dist/modules/query_index/migrations/Migration20261005201500_query_index.js.map +7 -0
- package/dist/modules/translations/components/TranslationManager.js +3 -3
- package/dist/modules/translations/components/TranslationManager.js.map +2 -2
- package/dist/modules/translations/data/validators.js +1 -1
- package/dist/modules/translations/data/validators.js.map +1 -1
- package/dist/modules/translations/lib/batch.js +21 -1
- package/dist/modules/translations/lib/batch.js.map +2 -2
- package/dist/modules/translations/lib/helpers.js +3 -0
- package/dist/modules/translations/lib/helpers.js.map +2 -2
- package/dist/modules/translations/lib/resolve-field-list.js +14 -2
- package/dist/modules/translations/lib/resolve-field-list.js.map +2 -2
- package/generated/entities/catalog_price_history_entry/index.ts +25 -0
- package/generated/entities/customer_group_terms/index.ts +1 -0
- package/generated/entities/ecommerce_store/index.ts +15 -0
- package/generated/entities/ecommerce_store_channel_binding/index.ts +13 -0
- package/generated/entities/ecommerce_store_domain_binding/index.ts +10 -0
- package/generated/entities.ids.generated.ts +8 -1
- package/generated/entity-fields-registry.ts +72 -0
- package/package.json +7 -7
- package/src/helpers/integration/customerGroupsFixtures.ts +2 -0
- package/src/modules/auth/i18n/de.json +8 -0
- package/src/modules/auth/i18n/en.json +8 -0
- package/src/modules/auth/i18n/es.json +8 -0
- package/src/modules/auth/i18n/ko.json +8 -0
- package/src/modules/auth/i18n/pl.json +8 -0
- package/src/modules/catalog/AGENTS.md +5 -2
- package/src/modules/catalog/acl.ts +7 -0
- package/src/modules/catalog/api/config/omnibus/route.ts +204 -0
- package/src/modules/catalog/api/prices/history/route.ts +139 -0
- package/src/modules/catalog/api/prices/omnibus-preview/route.ts +202 -0
- package/src/modules/catalog/api/products/route.ts +53 -308
- package/src/modules/catalog/api/tags/route.ts +6 -0
- package/src/modules/catalog/backend/config/catalog/page.tsx +2 -0
- package/src/modules/catalog/cli.ts +84 -1
- package/src/modules/catalog/commands/categories.ts +54 -2
- package/src/modules/catalog/commands/prices.ts +43 -1
- package/src/modules/catalog/commands/products.ts +84 -1
- package/src/modules/catalog/commands/variants.ts +7 -0
- package/src/modules/catalog/components/OmnibusSettings.tsx +832 -0
- package/src/modules/catalog/components/PriceEditorOmnibusRow.tsx +219 -0
- package/src/modules/catalog/components/lookupSources.ts +29 -0
- package/src/modules/catalog/components/prices/PriceScopeSelectors.tsx +9 -0
- package/src/modules/catalog/components/products/VariantBuilder.tsx +9 -0
- package/src/modules/catalog/data/entities.ts +123 -1
- package/src/modules/catalog/data/validators.ts +118 -0
- package/src/modules/catalog/di.ts +23 -0
- package/src/modules/catalog/i18n/de.json +77 -0
- package/src/modules/catalog/i18n/en.json +77 -0
- package/src/modules/catalog/i18n/es.json +77 -0
- package/src/modules/catalog/i18n/ko.json +77 -0
- package/src/modules/catalog/i18n/pl.json +77 -0
- package/src/modules/catalog/lib/omnibus.ts +406 -0
- package/src/modules/catalog/lib/omnibusBackfill.ts +198 -0
- package/src/modules/catalog/lib/omnibusCache.ts +111 -0
- package/src/modules/catalog/lib/omnibusConfig.ts +73 -0
- package/src/modules/catalog/lib/omnibusHistoryQueries.ts +248 -0
- package/src/modules/catalog/lib/omnibusPresentedEntry.ts +76 -0
- package/src/modules/catalog/lib/omnibusProductListEnrichment.ts +125 -0
- package/src/modules/catalog/lib/omnibusTypes.ts +202 -0
- package/src/modules/catalog/lib/optionSchemaTranslations.ts +55 -0
- package/src/modules/catalog/lib/priceHistoryQuery.ts +155 -0
- package/src/modules/catalog/lib/pricing.ts +42 -4
- package/src/modules/catalog/lib/productFilters.ts +349 -0
- package/src/modules/catalog/lib/productScopeKeys.ts +162 -0
- package/src/modules/catalog/lib/productScopeReindex.ts +139 -0
- package/src/modules/catalog/migrations/.snapshot-open-mercato.json +572 -0
- package/src/modules/catalog/migrations/Migration20261005105352_catalog.ts +39 -0
- package/src/modules/catalog/migrations/Migration20261005150824_catalog.ts +39 -0
- package/src/modules/catalog/services/catalogOmnibusService.ts +642 -0
- package/src/modules/catalog/setup.ts +2 -0
- package/src/modules/catalog/subscribers/product-scope-keys-category-deleted.ts +18 -0
- package/src/modules/catalog/subscribers/product-scope-keys-category-updated.ts +19 -0
- package/src/modules/catalog/translations.ts +12 -0
- package/src/modules/configs/i18n/de.json +5 -1
- package/src/modules/configs/i18n/en.json +5 -1
- package/src/modules/configs/i18n/es.json +5 -1
- package/src/modules/configs/i18n/ko.json +5 -1
- package/src/modules/configs/i18n/pl.json +5 -1
- package/src/modules/configs/lib/upgrade-actions.ts +35 -0
- package/src/modules/customer_groups/api/customer-groups/[id]/terms/route.ts +7 -0
- package/src/modules/customer_groups/components/CustomerGroupTermsSection.tsx +124 -2
- package/src/modules/customer_groups/components/customerGroupAssortmentScope.ts +46 -0
- package/src/modules/customer_groups/data/entities.ts +4 -0
- package/src/modules/customer_groups/data/validators.ts +14 -0
- package/src/modules/customer_groups/i18n/de.json +12 -0
- package/src/modules/customer_groups/i18n/en.json +12 -0
- package/src/modules/customer_groups/i18n/es.json +12 -0
- package/src/modules/customer_groups/i18n/ko.json +12 -0
- package/src/modules/customer_groups/i18n/pl.json +12 -0
- package/src/modules/customer_groups/migrations/.snapshot-open-mercato.json +17 -0
- package/src/modules/customer_groups/migrations/Migration20261005110250_customer_groups.ts +15 -0
- package/src/modules/customer_groups/services/customerGroupsService.ts +74 -23
- package/src/modules/ecommerce/acl.ts +9 -0
- package/src/modules/ecommerce/api/domain-mappings/route.ts +76 -0
- package/src/modules/ecommerce/api/openapi.ts +28 -0
- package/src/modules/ecommerce/api/store-channel-bindings/[id]/assortment-count/route.ts +141 -0
- package/src/modules/ecommerce/api/store-channel-bindings/crud.ts +283 -0
- package/src/modules/ecommerce/api/store-channel-bindings/route.ts +67 -0
- package/src/modules/ecommerce/api/store-domain-bindings/crud.ts +324 -0
- package/src/modules/ecommerce/api/store-domain-bindings/route.ts +73 -0
- package/src/modules/ecommerce/api/storefront/categories/[slug]/route.ts +94 -0
- package/src/modules/ecommerce/api/storefront/categories/openapiSchemas.ts +63 -0
- package/src/modules/ecommerce/api/storefront/categories/route.ts +78 -0
- package/src/modules/ecommerce/api/storefront/context/route.ts +203 -0
- package/src/modules/ecommerce/api/storefront/products/[idOrHandle]/route.ts +94 -0
- package/src/modules/ecommerce/api/storefront/products/openapiSchemas.ts +193 -0
- package/src/modules/ecommerce/api/storefront/products/route.ts +81 -0
- package/src/modules/ecommerce/api/storefront/search/suggest/openapiSchemas.ts +25 -0
- package/src/modules/ecommerce/api/storefront/search/suggest/route.ts +78 -0
- package/src/modules/ecommerce/api/storefront/storefrontRouteSupport.ts +68 -0
- package/src/modules/ecommerce/api/stores/[id]/branding/route.ts +205 -0
- package/src/modules/ecommerce/api/stores/[id]/preview-branding/route.ts +99 -0
- package/src/modules/ecommerce/api/stores/crud.ts +457 -0
- package/src/modules/ecommerce/api/stores/route.ts +66 -0
- package/src/modules/ecommerce/backend/config/ecommerce/[id]/page.meta.ts +12 -0
- package/src/modules/ecommerce/backend/config/ecommerce/[id]/page.tsx +5 -0
- package/src/modules/ecommerce/backend/config/ecommerce/page.meta.ts +12 -0
- package/src/modules/ecommerce/backend/config/ecommerce/page.tsx +5 -0
- package/src/modules/ecommerce/commands/stores.ts +154 -0
- package/src/modules/ecommerce/components/ChannelAssortmentCount.tsx +138 -0
- package/src/modules/ecommerce/components/ChannelBindingFormSections.tsx +40 -0
- package/src/modules/ecommerce/components/ChannelLookupPicker.tsx +5 -0
- package/src/modules/ecommerce/components/DomainStatusWarning.tsx +32 -0
- package/src/modules/ecommerce/components/StoreAvailabilityDefaultsSection.tsx +219 -0
- package/src/modules/ecommerce/components/StoreBrandingControls.tsx +73 -0
- package/src/modules/ecommerce/components/StoreBrandingPreview.tsx +77 -0
- package/src/modules/ecommerce/components/StoreBrandingTab.tsx +242 -0
- package/src/modules/ecommerce/components/StoreChannelBindingDialog.tsx +306 -0
- package/src/modules/ecommerce/components/StoreChannelsTab.tsx +375 -0
- package/src/modules/ecommerce/components/StoreCreateDialog.tsx +204 -0
- package/src/modules/ecommerce/components/StoreDomainBindingDialog.tsx +198 -0
- package/src/modules/ecommerce/components/StoreDomainsTab.tsx +426 -0
- package/src/modules/ecommerce/components/StoreEditPage.tsx +193 -0
- package/src/modules/ecommerce/components/StoreGeneralTab.tsx +189 -0
- package/src/modules/ecommerce/components/StoreListPage.tsx +326 -0
- package/src/modules/ecommerce/components/StoreSeoTab.tsx +152 -0
- package/src/modules/ecommerce/components/StoreStatusBadge.tsx +22 -0
- package/src/modules/ecommerce/components/channelLookups.ts +24 -0
- package/src/modules/ecommerce/components/storeAdmin.ts +56 -0
- package/src/modules/ecommerce/components/storeBrandingForm.ts +230 -0
- package/src/modules/ecommerce/components/storeChannels.ts +210 -0
- package/src/modules/ecommerce/components/storeDomains.ts +221 -0
- package/src/modules/ecommerce/components/storeEditTabs.tsx +76 -0
- package/src/modules/ecommerce/components/storeGeneral.ts +155 -0
- package/src/modules/ecommerce/components/storeSeo.ts +78 -0
- package/src/modules/ecommerce/components/useChannelAssortmentCount.ts +67 -0
- package/src/modules/ecommerce/components/useStoreAccess.ts +36 -0
- package/src/modules/ecommerce/data/enrichers.ts +213 -0
- package/src/modules/ecommerce/data/entities.ts +168 -0
- package/src/modules/ecommerce/data/validators.ts +587 -0
- package/src/modules/ecommerce/di.ts +9 -0
- package/src/modules/ecommerce/events.ts +27 -0
- package/src/modules/ecommerce/i18n/de.json +341 -0
- package/src/modules/ecommerce/i18n/en.json +341 -0
- package/src/modules/ecommerce/i18n/es.json +341 -0
- package/src/modules/ecommerce/i18n/ko.json +341 -0
- package/src/modules/ecommerce/i18n/pl.json +341 -0
- package/src/modules/ecommerce/index.ts +14 -0
- package/src/modules/ecommerce/lib/assortmentCount.ts +175 -0
- package/src/modules/ecommerce/lib/brandingStyles.ts +215 -0
- package/src/modules/ecommerce/lib/buyerContext.ts +401 -0
- package/src/modules/ecommerce/lib/cacheInvalidation.ts +302 -0
- package/src/modules/ecommerce/lib/cacheKeys.ts +340 -0
- package/src/modules/ecommerce/lib/createConflictRecheck.ts +31 -0
- package/src/modules/ecommerce/lib/crudEvents.ts +128 -0
- package/src/modules/ecommerce/lib/crudSupport.ts +85 -0
- package/src/modules/ecommerce/lib/domainBindingRebind.ts +138 -0
- package/src/modules/ecommerce/lib/domainMappingSummaries.ts +71 -0
- package/src/modules/ecommerce/lib/exclusiveFlag.ts +90 -0
- package/src/modules/ecommerce/lib/references.ts +93 -0
- package/src/modules/ecommerce/lib/seedDraftStore.ts +125 -0
- package/src/modules/ecommerce/lib/storeBindingCascade.ts +138 -0
- package/src/modules/ecommerce/lib/storeBranding.ts +48 -0
- package/src/modules/ecommerce/lib/storeBrandingRoute.ts +91 -0
- package/src/modules/ecommerce/lib/storeContext.ts +669 -0
- package/src/modules/ecommerce/lib/storeContextService.ts +71 -0
- package/src/modules/ecommerce/lib/storeLocale.ts +88 -0
- package/src/modules/ecommerce/lib/storeNotifications.ts +55 -0
- package/src/modules/ecommerce/lib/storefrontCatalogSupport.ts +427 -0
- package/src/modules/ecommerce/lib/storefrontCategories.ts +419 -0
- package/src/modules/ecommerce/lib/storefrontCategoryCache.ts +113 -0
- package/src/modules/ecommerce/lib/storefrontDetail.ts +654 -0
- package/src/modules/ecommerce/lib/storefrontFacets.ts +697 -0
- package/src/modules/ecommerce/lib/storefrontOptionFilter.ts +44 -0
- package/src/modules/ecommerce/lib/storefrontPricing.ts +583 -0
- package/src/modules/ecommerce/lib/storefrontProductCache.ts +126 -0
- package/src/modules/ecommerce/lib/storefrontProductScope.ts +181 -0
- package/src/modules/ecommerce/lib/storefrontProducts.ts +963 -0
- package/src/modules/ecommerce/lib/storefrontQuery.ts +215 -0
- package/src/modules/ecommerce/lib/storefrontRateLimit.ts +126 -0
- package/src/modules/ecommerce/lib/storefrontSearch.ts +454 -0
- package/src/modules/ecommerce/lib/storefrontSearchCache.ts +49 -0
- package/src/modules/ecommerce/lib/subscriberSupport.ts +41 -0
- package/src/modules/ecommerce/lib/types.ts +69 -0
- package/src/modules/ecommerce/migrations/.snapshot-open-mercato.json +829 -0
- package/src/modules/ecommerce/migrations/Migration20261005113233_ecommerce.ts +32 -0
- package/src/modules/ecommerce/migrations/Migration20261006112733_ecommerce.ts +15 -0
- package/src/modules/ecommerce/notifications.client.ts +46 -0
- package/src/modules/ecommerce/notifications.ts +46 -0
- package/src/modules/ecommerce/search.ts +93 -0
- package/src/modules/ecommerce/setup.ts +15 -0
- package/src/modules/ecommerce/subscribers/assortment-empty-notifications.ts +12 -0
- package/src/modules/ecommerce/subscribers/availability-policy-cache-invalidation.ts +12 -0
- package/src/modules/ecommerce/subscribers/catalog-category-cache-invalidation.ts +12 -0
- package/src/modules/ecommerce/subscribers/catalog-price-cache-invalidation.ts +12 -0
- package/src/modules/ecommerce/subscribers/catalog-product-cache-invalidation.ts +12 -0
- package/src/modules/ecommerce/subscribers/catalog-variant-cache-invalidation.ts +12 -0
- package/src/modules/ecommerce/subscribers/customer-group-cache-invalidation.ts +12 -0
- package/src/modules/ecommerce/subscribers/customer-group-membership-cache-invalidation.ts +12 -0
- package/src/modules/ecommerce/subscribers/customer-group-terms-cache-invalidation.ts +12 -0
- package/src/modules/ecommerce/subscribers/customer-user-deleted-cache-invalidation.ts +12 -0
- package/src/modules/ecommerce/subscribers/customer-user-updated-cache-invalidation.ts +12 -0
- package/src/modules/ecommerce/subscribers/domain-binding-rebind.ts +14 -0
- package/src/modules/ecommerce/subscribers/domain-mapping-cache-invalidation.ts +12 -0
- package/src/modules/ecommerce/subscribers/store-cache-invalidation.ts +12 -0
- package/src/modules/ecommerce/subscribers/store-channel-binding-cache-invalidation.ts +12 -0
- package/src/modules/ecommerce/subscribers/store-domain-binding-cache-invalidation.ts +12 -0
- package/src/modules/ecommerce/subscribers/store-notifications.ts +12 -0
- package/src/modules/query_index/lib/batch.ts +25 -9
- package/src/modules/query_index/lib/doc-enrichers.ts +306 -0
- package/src/modules/query_index/lib/document.ts +2 -0
- package/src/modules/query_index/lib/engine.ts +58 -1
- package/src/modules/query_index/lib/indexer.ts +8 -0
- package/src/modules/query_index/lib/search-tokens.ts +2 -0
- package/src/modules/query_index/migrations/Migration20261005201500_query_index.ts +42 -0
- package/src/modules/translations/components/TranslationManager.tsx +3 -3
- package/src/modules/translations/data/validators.ts +1 -1
- package/src/modules/translations/lib/batch.ts +47 -0
- package/src/modules/translations/lib/helpers.ts +4 -0
- package/src/modules/translations/lib/resolve-field-list.ts +16 -2
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { withCreateConflictRecheck } from '../../lib/createConflictRecheck'
|
|
3
|
+
import { domainBindingCrud, domainBindingListQuerySchema, domainBindingRouteMetadata } from './crud'
|
|
4
|
+
import {
|
|
5
|
+
ecommerceStoreDomainBindingCreateSchema,
|
|
6
|
+
ecommerceStoreDomainBindingUpdateSchema,
|
|
7
|
+
} from '../../data/validators'
|
|
8
|
+
import { createEcommerceCrudOpenApi, createPagedListResponseSchema, defaultOkResponseSchema } from '../openapi'
|
|
9
|
+
|
|
10
|
+
export const metadata = domainBindingRouteMetadata
|
|
11
|
+
|
|
12
|
+
export const GET = domainBindingCrud.GET
|
|
13
|
+
export const POST = withCreateConflictRecheck(domainBindingCrud.POST)
|
|
14
|
+
export const PUT = domainBindingCrud.PUT
|
|
15
|
+
export const DELETE = domainBindingCrud.DELETE
|
|
16
|
+
|
|
17
|
+
const domainBindingMappingSchema = z.discriminatedUnion('state', [
|
|
18
|
+
z.object({
|
|
19
|
+
state: z.literal('found'),
|
|
20
|
+
hostname: z.string(),
|
|
21
|
+
status: z.string().describe('pending, verified, active, dns_failed or tls_failed. Only active serves.'),
|
|
22
|
+
lastDnsCheckAt: z.string().nullable(),
|
|
23
|
+
dnsFailureReason: z.string().nullable(),
|
|
24
|
+
tlsFailureReason: z.string().nullable(),
|
|
25
|
+
}),
|
|
26
|
+
z.object({ state: z.literal('removed') }),
|
|
27
|
+
z.object({ state: z.literal('unavailable') }),
|
|
28
|
+
])
|
|
29
|
+
|
|
30
|
+
export const domainBindingListItemSchema = z.object({
|
|
31
|
+
id: z.string().uuid(),
|
|
32
|
+
organizationId: z.string().uuid().nullable(),
|
|
33
|
+
tenantId: z.string().uuid().nullable(),
|
|
34
|
+
storeId: z.string().uuid(),
|
|
35
|
+
domainMappingId: z.string().uuid(),
|
|
36
|
+
pathPrefix: z.string().nullable(),
|
|
37
|
+
isPrimary: z.boolean(),
|
|
38
|
+
createdAt: z.string().nullable(),
|
|
39
|
+
updatedAt: z.string().nullable(),
|
|
40
|
+
_domainMapping: domainBindingMappingSchema
|
|
41
|
+
.optional()
|
|
42
|
+
.describe(
|
|
43
|
+
'Added by the ecommerce.store-domain-binding-mapping response enricher: the bound customer_accounts domain mapping as read through domainMappingService. state is removed when the mapping no longer exists and unavailable when it could not be read.',
|
|
44
|
+
),
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
export const openApi = createEcommerceCrudOpenApi({
|
|
48
|
+
resourceName: 'StoreDomainBinding',
|
|
49
|
+
pluralName: 'StoreDomainBindings',
|
|
50
|
+
querySchema: domainBindingListQuerySchema,
|
|
51
|
+
listResponseSchema: createPagedListResponseSchema(domainBindingListItemSchema),
|
|
52
|
+
create: {
|
|
53
|
+
schema: ecommerceStoreDomainBindingCreateSchema,
|
|
54
|
+
responseSchema: z.object({
|
|
55
|
+
id: z.string().uuid().nullable(),
|
|
56
|
+
isPrimary: z
|
|
57
|
+
.boolean()
|
|
58
|
+
.describe('Whether the created binding is the primary binding of its store. False when a concurrent request won the promotion.'),
|
|
59
|
+
}),
|
|
60
|
+
description:
|
|
61
|
+
'Binds a customer_accounts domain mapping (any status) and optional path prefix to a store of the same organization. A duplicate domain and path prefix is rejected with a field-level 409. Requesting isPrimary clears the previous primary binding of the store.',
|
|
62
|
+
},
|
|
63
|
+
update: {
|
|
64
|
+
schema: ecommerceStoreDomainBindingUpdateSchema,
|
|
65
|
+
responseSchema: defaultOkResponseSchema,
|
|
66
|
+
description: 'Updates a store domain binding by id (id in the body). Setting isPrimary clears the previous primary binding of the store.',
|
|
67
|
+
},
|
|
68
|
+
del: {
|
|
69
|
+
schema: z.object({ id: z.string().uuid() }),
|
|
70
|
+
responseSchema: defaultOkResponseSchema,
|
|
71
|
+
description: 'Soft-deletes a store domain binding by id (?id= query parameter).',
|
|
72
|
+
},
|
|
73
|
+
})
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
3
|
+
import type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
4
|
+
import { rateLimitErrorSchema } from '@open-mercato/shared/lib/ratelimit/helpers'
|
|
5
|
+
import {
|
|
6
|
+
ecommerceStorefrontCategoryLandingQuerySchema,
|
|
7
|
+
ecommerceStorefrontCategoryParamsSchema,
|
|
8
|
+
} from '../../../../data/validators'
|
|
9
|
+
import { enforceStorefrontRateLimit } from '../../../../lib/storefrontRateLimit'
|
|
10
|
+
import type { StoreContextService } from '../../../../lib/storeContextService'
|
|
11
|
+
import { cachedGetStorefrontCategoryLanding } from '../../../../lib/storefrontCategoryCache'
|
|
12
|
+
import { parseStorefrontCategoryLandingQuery } from '../../../../lib/storefrontQuery'
|
|
13
|
+
import {
|
|
14
|
+
storefrontErrorResponse,
|
|
15
|
+
storefrontErrorSchema,
|
|
16
|
+
storefrontInvalidQueryErrorSchema,
|
|
17
|
+
storefrontRouteErrorResponse,
|
|
18
|
+
storefrontSuccessHeaders,
|
|
19
|
+
runInStoreCacheTenant,
|
|
20
|
+
} from '../../storefrontRouteSupport'
|
|
21
|
+
import { storefrontCategoryLandingResponseSchema } from '../openapiSchemas'
|
|
22
|
+
|
|
23
|
+
export const metadata = {
|
|
24
|
+
path: '/ecommerce/storefront/categories/[slug]',
|
|
25
|
+
GET: {
|
|
26
|
+
requireAuth: false,
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const ANONYMOUS_CACHE_CONTROL = 'public, max-age=30, stale-while-revalidate=30'
|
|
31
|
+
const CATEGORY_NOT_FOUND = { error: 'category_not_found' } as const
|
|
32
|
+
|
|
33
|
+
type CategoryRouteParams = { slug?: string }
|
|
34
|
+
|
|
35
|
+
type CategoryRouteContext = { params?: CategoryRouteParams | Promise<CategoryRouteParams> }
|
|
36
|
+
|
|
37
|
+
export async function GET(req: Request, routeContext: CategoryRouteContext = {}) {
|
|
38
|
+
try {
|
|
39
|
+
const query = parseStorefrontCategoryLandingQuery(new URL(req.url).searchParams)
|
|
40
|
+
const container = await createRequestContainer()
|
|
41
|
+
const service = container.resolve('storeContextService') as StoreContextService
|
|
42
|
+
const context = await service.resolve(req, { pathname: query.path ?? '/' })
|
|
43
|
+
return await runInStoreCacheTenant(context, async () => {
|
|
44
|
+
const rateLimited = await enforceStorefrontRateLimit(container, req, context, 'categoryLanding')
|
|
45
|
+
if (rateLimited) return rateLimited
|
|
46
|
+
const params = ecommerceStorefrontCategoryParamsSchema.safeParse((await routeContext.params) ?? {})
|
|
47
|
+
if (!params.success) return storefrontErrorResponse(404, { ...CATEGORY_NOT_FOUND })
|
|
48
|
+
const landing = await cachedGetStorefrontCategoryLanding(container, context, params.data.slug, query)
|
|
49
|
+
if (!landing) return storefrontErrorResponse(404, { ...CATEGORY_NOT_FOUND })
|
|
50
|
+
const headers = storefrontSuccessHeaders(context, ANONYMOUS_CACHE_CONTROL)
|
|
51
|
+
if (landing.products.sortApproximate) headers['X-Sort-Approximate'] = 'true'
|
|
52
|
+
if (landing.products.sortUnavailable) headers['X-Sort-Unavailable'] = 'true'
|
|
53
|
+
return NextResponse.json(landing, { headers })
|
|
54
|
+
})
|
|
55
|
+
} catch (error) {
|
|
56
|
+
return storefrontRouteErrorResponse(error, {
|
|
57
|
+
message: 'Storefront category landing failed',
|
|
58
|
+
code: 'ecommerce.storefront_category_landing_failed',
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export default GET
|
|
64
|
+
|
|
65
|
+
const storefrontCategoryTag = 'Ecommerce'
|
|
66
|
+
|
|
67
|
+
const storefrontCategoryGetDoc: OpenApiMethodDoc = {
|
|
68
|
+
summary: 'Get one storefront category landing for the current buyer',
|
|
69
|
+
description:
|
|
70
|
+
'Public. Resolves the store from the Host header and the optional portal session, then returns the category by slug with its breadcrumb, visible non-empty children and descendant-inclusive `productCount` within the buyer\'s assortment (null past 10 000 products in the assortment), plus a full `GET /products` response filtered to the category (`products`, same query grammar except `categoryId` / `categorySlug`). A slug that does not exist, is inactive, deleted, of another tenant, under an inactive ancestor or outside the buyer\'s effective assortment answers the same 404 `{ "error": "category_not_found" }`. `seo` fields are null until the catalog stores category SEO data. Anonymous responses are `public, max-age=30, stale-while-revalidate=30`; authenticated responses are `private, no-store`. The category block is cached server-side for 60 s per assortment scope; the embedded listing shares the `GET /products` cache. Rate limited per IP and store.',
|
|
71
|
+
tags: [storefrontCategoryTag],
|
|
72
|
+
pathParams: ecommerceStorefrontCategoryParamsSchema,
|
|
73
|
+
query: ecommerceStorefrontCategoryLandingQuerySchema,
|
|
74
|
+
responses: [
|
|
75
|
+
{ status: 200, description: 'The category landing as this buyer sees it.', schema: storefrontCategoryLandingResponseSchema },
|
|
76
|
+
],
|
|
77
|
+
errors: [
|
|
78
|
+
{ status: 400, description: 'Unknown, repeated or malformed query parameters', schema: storefrontInvalidQueryErrorSchema },
|
|
79
|
+
{ status: 401, description: 'Portal session invalid or issued for another tenant or organization', schema: storefrontErrorSchema },
|
|
80
|
+
{ status: 403, description: 'Draft store (only when OM_ECOMMERCE_DEV_STORE_SLUG=true)', schema: storefrontErrorSchema },
|
|
81
|
+
{ status: 404, description: 'No store serves this host or path, or the category is not visible to this buyer', schema: storefrontErrorSchema },
|
|
82
|
+
{ status: 410, description: 'Store archived', schema: storefrontErrorSchema },
|
|
83
|
+
{ status: 429, description: 'Too many requests', schema: rateLimitErrorSchema },
|
|
84
|
+
{ status: 503, description: 'Store misconfigured (no default channel binding)', schema: storefrontErrorSchema },
|
|
85
|
+
],
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const openApi: OpenApiRouteDoc = {
|
|
89
|
+
tag: storefrontCategoryTag,
|
|
90
|
+
summary: 'Public storefront category landing',
|
|
91
|
+
methods: {
|
|
92
|
+
GET: storefrontCategoryGetDoc,
|
|
93
|
+
},
|
|
94
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { storefrontProductListResponseSchema } from '../products/openapiSchemas'
|
|
3
|
+
|
|
4
|
+
export type StorefrontCategoryNodeSchema = {
|
|
5
|
+
id: string
|
|
6
|
+
name: string
|
|
7
|
+
slug: string | null
|
|
8
|
+
description: string | null
|
|
9
|
+
depth: number
|
|
10
|
+
parentId: string | null
|
|
11
|
+
productCount: number | null
|
|
12
|
+
hasChildren: boolean
|
|
13
|
+
children: StorefrontCategoryNodeSchema[]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const storefrontCategoryNodeSchema: z.ZodType<StorefrontCategoryNodeSchema> = z.lazy(() =>
|
|
17
|
+
z.object({
|
|
18
|
+
id: z.string().uuid(),
|
|
19
|
+
name: z.string(),
|
|
20
|
+
slug: z.string().nullable(),
|
|
21
|
+
description: z.string().nullable(),
|
|
22
|
+
depth: z.number().int(),
|
|
23
|
+
parentId: z.string().uuid().nullable(),
|
|
24
|
+
productCount: z
|
|
25
|
+
.number()
|
|
26
|
+
.int()
|
|
27
|
+
.nullable()
|
|
28
|
+
.describe(
|
|
29
|
+
'Products of the buyer\'s effective assortment in this category or a descendant; null when the assortment is too large to count',
|
|
30
|
+
),
|
|
31
|
+
hasChildren: z.boolean(),
|
|
32
|
+
children: z.array(storefrontCategoryNodeSchema),
|
|
33
|
+
}),
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
export const storefrontCategoryTreeResponseSchema = z.object({
|
|
37
|
+
tree: z.array(storefrontCategoryNodeSchema),
|
|
38
|
+
effectiveLocale: z.string(),
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
export const storefrontCategoryLandingResponseSchema = z.object({
|
|
42
|
+
category: z.object({
|
|
43
|
+
id: z.string().uuid(),
|
|
44
|
+
name: z.string(),
|
|
45
|
+
slug: z.string().nullable(),
|
|
46
|
+
description: z.string().nullable(),
|
|
47
|
+
depth: z.number().int(),
|
|
48
|
+
parentId: z.string().uuid().nullable(),
|
|
49
|
+
ancestorIds: z.array(z.string().uuid()),
|
|
50
|
+
breadcrumb: z.array(z.object({ id: z.string().uuid(), name: z.string(), slug: z.string().nullable() })),
|
|
51
|
+
children: z.array(
|
|
52
|
+
z.object({ id: z.string().uuid(), name: z.string(), slug: z.string().nullable(), productCount: z.number().int().nullable() }),
|
|
53
|
+
),
|
|
54
|
+
productCount: z.number().int().nullable(),
|
|
55
|
+
seo: z.object({
|
|
56
|
+
title: z.string().nullable(),
|
|
57
|
+
description: z.string().nullable(),
|
|
58
|
+
canonicalUrl: z.string().nullable(),
|
|
59
|
+
}),
|
|
60
|
+
}),
|
|
61
|
+
products: storefrontProductListResponseSchema.describe('Identical to GET /products with the category filter applied'),
|
|
62
|
+
effectiveLocale: z.string(),
|
|
63
|
+
})
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
3
|
+
import type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
4
|
+
import { rateLimitErrorSchema } from '@open-mercato/shared/lib/ratelimit/helpers'
|
|
5
|
+
import { ecommerceStorefrontCategoryTreeQuerySchema } from '../../../data/validators'
|
|
6
|
+
import { enforceStorefrontRateLimit } from '../../../lib/storefrontRateLimit'
|
|
7
|
+
import type { StoreContextService } from '../../../lib/storeContextService'
|
|
8
|
+
import { cachedGetStorefrontCategoryTree } from '../../../lib/storefrontCategoryCache'
|
|
9
|
+
import { parseStorefrontCategoryTreeQuery } from '../../../lib/storefrontQuery'
|
|
10
|
+
import {
|
|
11
|
+
storefrontErrorSchema,
|
|
12
|
+
storefrontInvalidQueryErrorSchema,
|
|
13
|
+
storefrontRouteErrorResponse,
|
|
14
|
+
storefrontSuccessHeaders,
|
|
15
|
+
runInStoreCacheTenant,
|
|
16
|
+
} from '../storefrontRouteSupport'
|
|
17
|
+
import { storefrontCategoryTreeResponseSchema } from './openapiSchemas'
|
|
18
|
+
|
|
19
|
+
export const metadata = {
|
|
20
|
+
path: '/ecommerce/storefront/categories',
|
|
21
|
+
GET: {
|
|
22
|
+
requireAuth: false,
|
|
23
|
+
},
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const ANONYMOUS_CACHE_CONTROL = 'public, max-age=300, stale-while-revalidate=60'
|
|
27
|
+
|
|
28
|
+
export async function GET(req: Request) {
|
|
29
|
+
try {
|
|
30
|
+
const query = parseStorefrontCategoryTreeQuery(new URL(req.url).searchParams)
|
|
31
|
+
const container = await createRequestContainer()
|
|
32
|
+
const service = container.resolve('storeContextService') as StoreContextService
|
|
33
|
+
const context = await service.resolve(req, { pathname: query.path ?? '/' })
|
|
34
|
+
return await runInStoreCacheTenant(context, async () => {
|
|
35
|
+
const rateLimited = await enforceStorefrontRateLimit(container, req, context, 'categories')
|
|
36
|
+
if (rateLimited) return rateLimited
|
|
37
|
+
const body = await cachedGetStorefrontCategoryTree(container, context, query)
|
|
38
|
+
return NextResponse.json(body, { headers: storefrontSuccessHeaders(context, ANONYMOUS_CACHE_CONTROL) })
|
|
39
|
+
})
|
|
40
|
+
} catch (error) {
|
|
41
|
+
return storefrontRouteErrorResponse(error, {
|
|
42
|
+
message: 'Storefront category tree failed',
|
|
43
|
+
code: 'ecommerce.storefront_categories_failed',
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export default GET
|
|
49
|
+
|
|
50
|
+
const storefrontCategoriesTag = 'Ecommerce'
|
|
51
|
+
|
|
52
|
+
const storefrontCategoriesGetDoc: OpenApiMethodDoc = {
|
|
53
|
+
summary: 'List the storefront category tree for the current buyer',
|
|
54
|
+
description:
|
|
55
|
+
'Public. Resolves the store from the Host header and the optional portal session, then returns the visible category tree below `parentId` (the roots when omitted), `depth` levels deep (all levels when omitted). `productCount` is descendant-inclusive and counts only products inside the buyer\'s effective assortment; categories the buyer cannot browse are absent, and empty categories are absent unless `includeEmpty=true`. Past 10 000 products in the assortment the counts are not computed: every `productCount` is null and no category is dropped as empty. A `parentId` that is unknown or not visible to the buyer yields an empty tree, identical to a nonexistent one. A nested node beyond `depth` has empty `children` and `hasChildren: true`. Anonymous responses are `public, max-age=300, stale-while-revalidate=60`; authenticated responses are `private, no-store`. Cached server-side for 300 s per assortment scope. Rate limited per IP and store.',
|
|
56
|
+
tags: [storefrontCategoriesTag],
|
|
57
|
+
query: ecommerceStorefrontCategoryTreeQuerySchema,
|
|
58
|
+
responses: [
|
|
59
|
+
{ status: 200, description: 'The category tree within the buyer\'s assortment.', schema: storefrontCategoryTreeResponseSchema },
|
|
60
|
+
],
|
|
61
|
+
errors: [
|
|
62
|
+
{ status: 400, description: 'Unknown, repeated or malformed query parameters', schema: storefrontInvalidQueryErrorSchema },
|
|
63
|
+
{ status: 401, description: 'Portal session invalid or issued for another tenant or organization', schema: storefrontErrorSchema },
|
|
64
|
+
{ status: 403, description: 'Draft store (only when OM_ECOMMERCE_DEV_STORE_SLUG=true)', schema: storefrontErrorSchema },
|
|
65
|
+
{ status: 404, description: 'No store serves this host or path', schema: storefrontErrorSchema },
|
|
66
|
+
{ status: 410, description: 'Store archived', schema: storefrontErrorSchema },
|
|
67
|
+
{ status: 429, description: 'Too many requests', schema: rateLimitErrorSchema },
|
|
68
|
+
{ status: 503, description: 'Store misconfigured (no default channel binding)', schema: storefrontErrorSchema },
|
|
69
|
+
],
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export const openApi: OpenApiRouteDoc = {
|
|
73
|
+
tag: storefrontCategoriesTag,
|
|
74
|
+
summary: 'Public storefront category tree',
|
|
75
|
+
methods: {
|
|
76
|
+
GET: storefrontCategoriesGetDoc,
|
|
77
|
+
},
|
|
78
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
4
|
+
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
5
|
+
import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
|
|
6
|
+
import type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
7
|
+
import { rateLimitErrorSchema } from '@open-mercato/shared/lib/ratelimit/helpers'
|
|
8
|
+
import { CustomerUser } from '@open-mercato/core/modules/customer_accounts/data/entities'
|
|
9
|
+
import { CustomerEntity } from '@open-mercato/core/modules/customers/data/entities'
|
|
10
|
+
import { ecommercePriceDisplayModeSchema, ecommerceStorefrontContextQuerySchema } from '../../../data/validators'
|
|
11
|
+
import { parseStorefrontContextQuery } from '../../../lib/storefrontQuery'
|
|
12
|
+
import { enforceStorefrontRateLimit } from '../../../lib/storefrontRateLimit'
|
|
13
|
+
import type { StoreContextService } from '../../../lib/storeContextService'
|
|
14
|
+
import type { StoreContext } from '../../../lib/types'
|
|
15
|
+
import {
|
|
16
|
+
runInStoreCacheTenant,
|
|
17
|
+
storefrontErrorSchema,
|
|
18
|
+
storefrontInvalidQueryErrorSchema,
|
|
19
|
+
storefrontRouteErrorResponse,
|
|
20
|
+
storefrontSuccessHeaders,
|
|
21
|
+
} from '../storefrontRouteSupport'
|
|
22
|
+
|
|
23
|
+
export const metadata = {
|
|
24
|
+
path: '/ecommerce/storefront/context',
|
|
25
|
+
GET: {
|
|
26
|
+
requireAuth: false,
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const ANONYMOUS_CACHE_CONTROL = 'public, max-age=60'
|
|
31
|
+
|
|
32
|
+
export type StorefrontBuyerNames = {
|
|
33
|
+
displayName: string | null
|
|
34
|
+
companyName: string | null
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type StorefrontContextResponse = {
|
|
38
|
+
store: {
|
|
39
|
+
id: string
|
|
40
|
+
code: string
|
|
41
|
+
name: string
|
|
42
|
+
slug: string
|
|
43
|
+
status: 'active'
|
|
44
|
+
defaultLocale: string
|
|
45
|
+
supportedLocales: string[]
|
|
46
|
+
defaultCurrencyCode: string
|
|
47
|
+
settings: StoreContext['store']['settings']
|
|
48
|
+
}
|
|
49
|
+
effectiveLocale: string
|
|
50
|
+
requestedLocale: string | null
|
|
51
|
+
supportedLocales: string[]
|
|
52
|
+
currencyCode: string
|
|
53
|
+
buyer: {
|
|
54
|
+
isAuthenticated: boolean
|
|
55
|
+
taxMode: StoreContext['buyer']['taxMode']
|
|
56
|
+
displayName: string | null
|
|
57
|
+
companyName: string | null
|
|
58
|
+
allowPurchaseOnAccount: boolean
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function projectStorefrontContext(context: StoreContext, names: StorefrontBuyerNames): StorefrontContextResponse {
|
|
63
|
+
const { store, buyer } = context
|
|
64
|
+
return {
|
|
65
|
+
store: {
|
|
66
|
+
id: store.id,
|
|
67
|
+
code: store.code,
|
|
68
|
+
name: store.name,
|
|
69
|
+
slug: store.slug,
|
|
70
|
+
status: store.status,
|
|
71
|
+
defaultLocale: store.defaultLocale,
|
|
72
|
+
supportedLocales: [...store.supportedLocales],
|
|
73
|
+
defaultCurrencyCode: store.defaultCurrencyCode,
|
|
74
|
+
settings: store.settings,
|
|
75
|
+
},
|
|
76
|
+
effectiveLocale: context.effectiveLocale,
|
|
77
|
+
requestedLocale: context.requestedLocale,
|
|
78
|
+
supportedLocales: [...store.supportedLocales],
|
|
79
|
+
currencyCode: context.currencyCode,
|
|
80
|
+
buyer: {
|
|
81
|
+
isAuthenticated: buyer.isAuthenticated,
|
|
82
|
+
taxMode: buyer.taxMode,
|
|
83
|
+
displayName: buyer.isAuthenticated ? names.displayName : null,
|
|
84
|
+
companyName: buyer.isAuthenticated ? names.companyName : null,
|
|
85
|
+
allowPurchaseOnAccount: buyer.allowPurchaseOnAccount,
|
|
86
|
+
},
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function nonEmptyOrNull(value: string | null | undefined): string | null {
|
|
91
|
+
return typeof value === 'string' && value.trim().length > 0 ? value : null
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function loadBuyerNames(em: EntityManager, context: StoreContext): Promise<StorefrontBuyerNames> {
|
|
95
|
+
const { buyer } = context
|
|
96
|
+
if (!buyer.isAuthenticated || !buyer.customerUserId) return { displayName: null, companyName: null }
|
|
97
|
+
const scope = { tenantId: context.tenantId, organizationId: context.organizationId }
|
|
98
|
+
const user = await findOneWithDecryption(
|
|
99
|
+
em,
|
|
100
|
+
CustomerUser,
|
|
101
|
+
{ id: buyer.customerUserId, ...scope, deletedAt: null },
|
|
102
|
+
undefined,
|
|
103
|
+
scope,
|
|
104
|
+
)
|
|
105
|
+
const company = buyer.companyId
|
|
106
|
+
? await findOneWithDecryption(
|
|
107
|
+
em,
|
|
108
|
+
CustomerEntity,
|
|
109
|
+
{ id: buyer.companyId, ...scope, deletedAt: null },
|
|
110
|
+
undefined,
|
|
111
|
+
scope,
|
|
112
|
+
)
|
|
113
|
+
: null
|
|
114
|
+
return {
|
|
115
|
+
displayName: nonEmptyOrNull(user?.displayName),
|
|
116
|
+
companyName: nonEmptyOrNull(company?.displayName),
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export async function GET(req: Request) {
|
|
121
|
+
try {
|
|
122
|
+
const query = parseStorefrontContextQuery(new URL(req.url).searchParams)
|
|
123
|
+
const container = await createRequestContainer()
|
|
124
|
+
const service = container.resolve('storeContextService') as StoreContextService
|
|
125
|
+
const context = await service.resolve(req, { pathname: query.path ?? '/' })
|
|
126
|
+
return await runInStoreCacheTenant(context, async () => {
|
|
127
|
+
const rateLimited = await enforceStorefrontRateLimit(container, req, context, 'context')
|
|
128
|
+
if (rateLimited) return rateLimited
|
|
129
|
+
const em = container.resolve('em') as EntityManager
|
|
130
|
+
const names = await loadBuyerNames(em, context)
|
|
131
|
+
const body = projectStorefrontContext(context, names)
|
|
132
|
+
return NextResponse.json(body, { headers: storefrontSuccessHeaders(context, ANONYMOUS_CACHE_CONTROL) })
|
|
133
|
+
})
|
|
134
|
+
} catch (error) {
|
|
135
|
+
return storefrontRouteErrorResponse(error, {
|
|
136
|
+
message: 'Storefront context resolution failed',
|
|
137
|
+
code: 'ecommerce.storefront_context_failed',
|
|
138
|
+
})
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export default GET
|
|
143
|
+
|
|
144
|
+
const storefrontContextTag = 'Ecommerce'
|
|
145
|
+
|
|
146
|
+
export const storefrontContextResponseSchema = z.object({
|
|
147
|
+
store: z.object({
|
|
148
|
+
id: z.string().uuid(),
|
|
149
|
+
code: z.string(),
|
|
150
|
+
name: z.string(),
|
|
151
|
+
slug: z.string(),
|
|
152
|
+
status: z.literal('active'),
|
|
153
|
+
defaultLocale: z.string(),
|
|
154
|
+
supportedLocales: z.array(z.string()),
|
|
155
|
+
defaultCurrencyCode: z.string(),
|
|
156
|
+
settings: z.record(z.string(), z.unknown()),
|
|
157
|
+
}),
|
|
158
|
+
effectiveLocale: z.string(),
|
|
159
|
+
requestedLocale: z.string().nullable(),
|
|
160
|
+
supportedLocales: z.array(z.string()),
|
|
161
|
+
currencyCode: z.string(),
|
|
162
|
+
buyer: z.object({
|
|
163
|
+
isAuthenticated: z.boolean(),
|
|
164
|
+
taxMode: ecommercePriceDisplayModeSchema,
|
|
165
|
+
displayName: z.string().nullable(),
|
|
166
|
+
companyName: z.string().nullable(),
|
|
167
|
+
allowPurchaseOnAccount: z.boolean(),
|
|
168
|
+
}),
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
const storefrontContextBadRequestSchema = z.union([storefrontInvalidQueryErrorSchema, storefrontErrorSchema])
|
|
172
|
+
|
|
173
|
+
const storefrontContextGetDoc: OpenApiMethodDoc = {
|
|
174
|
+
summary: 'Resolve the storefront context for the current host',
|
|
175
|
+
description:
|
|
176
|
+
'Public, unauthenticated. Resolves the store from the Host header (or `storeSlug` when OM_ECOMMERCE_DEV_STORE_SLUG=true) and the optional portal session, and returns a safe projection of the store and buyer context. Anonymous responses are `public, max-age=60`; authenticated responses are `private, no-store`. Rate limited per IP and store.',
|
|
177
|
+
tags: [storefrontContextTag],
|
|
178
|
+
query: ecommerceStorefrontContextQuerySchema,
|
|
179
|
+
responses: [
|
|
180
|
+
{ status: 200, description: 'Storefront context resolved.', schema: storefrontContextResponseSchema },
|
|
181
|
+
],
|
|
182
|
+
errors: [
|
|
183
|
+
{
|
|
184
|
+
status: 400,
|
|
185
|
+
description: 'Unknown, repeated or malformed query parameters, or storeSlug used while the development flag is off',
|
|
186
|
+
schema: storefrontContextBadRequestSchema,
|
|
187
|
+
},
|
|
188
|
+
{ status: 401, description: 'Portal session invalid or issued for another tenant or organization', schema: storefrontErrorSchema },
|
|
189
|
+
{ status: 403, description: 'Draft store (only when OM_ECOMMERCE_DEV_STORE_SLUG=true)', schema: storefrontErrorSchema },
|
|
190
|
+
{ status: 404, description: 'No store serves this host or path', schema: storefrontErrorSchema },
|
|
191
|
+
{ status: 410, description: 'Store archived', schema: storefrontErrorSchema },
|
|
192
|
+
{ status: 429, description: 'Too many requests', schema: rateLimitErrorSchema },
|
|
193
|
+
{ status: 503, description: 'Store misconfigured (no default channel binding)', schema: storefrontErrorSchema },
|
|
194
|
+
],
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export const openApi: OpenApiRouteDoc = {
|
|
198
|
+
tag: storefrontContextTag,
|
|
199
|
+
summary: 'Public storefront context',
|
|
200
|
+
methods: {
|
|
201
|
+
GET: storefrontContextGetDoc,
|
|
202
|
+
},
|
|
203
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
3
|
+
import type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
|
|
4
|
+
import { rateLimitErrorSchema } from '@open-mercato/shared/lib/ratelimit/helpers'
|
|
5
|
+
import {
|
|
6
|
+
ecommerceStorefrontProductDetailQuerySchema,
|
|
7
|
+
ecommerceStorefrontProductParamsSchema,
|
|
8
|
+
} from '../../../../data/validators'
|
|
9
|
+
import { enforceStorefrontRateLimit } from '../../../../lib/storefrontRateLimit'
|
|
10
|
+
import type { StoreContextService } from '../../../../lib/storeContextService'
|
|
11
|
+
import { cachedGetStorefrontProductDetail } from '../../../../lib/storefrontProductCache'
|
|
12
|
+
import { parseStorefrontProductDetailQuery } from '../../../../lib/storefrontQuery'
|
|
13
|
+
import {
|
|
14
|
+
storefrontErrorResponse,
|
|
15
|
+
storefrontErrorSchema,
|
|
16
|
+
storefrontInvalidQueryErrorSchema,
|
|
17
|
+
storefrontRouteErrorResponse,
|
|
18
|
+
storefrontSuccessHeaders,
|
|
19
|
+
runInStoreCacheTenant,
|
|
20
|
+
} from '../../storefrontRouteSupport'
|
|
21
|
+
import { storefrontProductDetailResponseSchema } from '../openapiSchemas'
|
|
22
|
+
|
|
23
|
+
export const metadata = {
|
|
24
|
+
path: '/ecommerce/storefront/products/[idOrHandle]',
|
|
25
|
+
GET: {
|
|
26
|
+
requireAuth: false,
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const ANONYMOUS_CACHE_CONTROL = 'public, max-age=60'
|
|
31
|
+
const PRODUCT_NOT_FOUND = { error: 'product_not_found' } as const
|
|
32
|
+
|
|
33
|
+
type ProductRouteParams = { idOrHandle?: string }
|
|
34
|
+
|
|
35
|
+
type ProductRouteContext = { params?: ProductRouteParams | Promise<ProductRouteParams> }
|
|
36
|
+
|
|
37
|
+
export async function GET(req: Request, routeContext: ProductRouteContext = {}) {
|
|
38
|
+
try {
|
|
39
|
+
const query = parseStorefrontProductDetailQuery(new URL(req.url).searchParams)
|
|
40
|
+
const container = await createRequestContainer()
|
|
41
|
+
const service = container.resolve('storeContextService') as StoreContextService
|
|
42
|
+
const context = await service.resolve(req, { pathname: query.path ?? '/' })
|
|
43
|
+
return await runInStoreCacheTenant(context, async () => {
|
|
44
|
+
const rateLimited = await enforceStorefrontRateLimit(container, req, context, 'productDetail')
|
|
45
|
+
if (rateLimited) return rateLimited
|
|
46
|
+
const params = ecommerceStorefrontProductParamsSchema.safeParse((await routeContext.params) ?? {})
|
|
47
|
+
if (!params.success) return storefrontErrorResponse(404, { ...PRODUCT_NOT_FOUND })
|
|
48
|
+
const detail = await cachedGetStorefrontProductDetail(container, context, params.data.idOrHandle, {
|
|
49
|
+
variantId: query.variantId ?? null,
|
|
50
|
+
locale: query.locale ?? null,
|
|
51
|
+
})
|
|
52
|
+
if (!detail) return storefrontErrorResponse(404, { ...PRODUCT_NOT_FOUND })
|
|
53
|
+
return NextResponse.json(detail, { headers: storefrontSuccessHeaders(context, ANONYMOUS_CACHE_CONTROL) })
|
|
54
|
+
})
|
|
55
|
+
} catch (error) {
|
|
56
|
+
return storefrontRouteErrorResponse(error, {
|
|
57
|
+
message: 'Storefront product detail failed',
|
|
58
|
+
code: 'ecommerce.storefront_product_detail_failed',
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export default GET
|
|
64
|
+
|
|
65
|
+
const storefrontProductTag = 'Ecommerce'
|
|
66
|
+
|
|
67
|
+
const storefrontProductGetDoc: OpenApiMethodDoc = {
|
|
68
|
+
summary: 'Get one storefront product for the current buyer',
|
|
69
|
+
description:
|
|
70
|
+
'Public. Resolves the store from the Host header and the optional portal session, then returns the product by UUID or handle with buyer-resolved prices per variant. A product that does not exist, is inactive, deleted, of another tenant or outside the buyer\'s effective assortment answers the same 404 `{ "error": "product_not_found" }`. `variantId` preselects a variant; top-level `priceTiers` are the selected variant\'s tiers. Anonymous responses are `public, max-age=60`; authenticated responses are `private, no-store`. Cached server-side for 60 s per buyer context digest. Rate limited per IP and store.',
|
|
71
|
+
tags: [storefrontProductTag],
|
|
72
|
+
pathParams: ecommerceStorefrontProductParamsSchema,
|
|
73
|
+
query: ecommerceStorefrontProductDetailQuerySchema,
|
|
74
|
+
responses: [
|
|
75
|
+
{ status: 200, description: 'The product as this buyer sees it.', schema: storefrontProductDetailResponseSchema },
|
|
76
|
+
],
|
|
77
|
+
errors: [
|
|
78
|
+
{ status: 400, description: 'Unknown, repeated or malformed query parameters', schema: storefrontInvalidQueryErrorSchema },
|
|
79
|
+
{ status: 401, description: 'Portal session invalid or issued for another tenant or organization', schema: storefrontErrorSchema },
|
|
80
|
+
{ status: 403, description: 'Draft store (only when OM_ECOMMERCE_DEV_STORE_SLUG=true)', schema: storefrontErrorSchema },
|
|
81
|
+
{ status: 404, description: 'No store serves this host or path, or the product is not visible to this buyer', schema: storefrontErrorSchema },
|
|
82
|
+
{ status: 410, description: 'Store archived', schema: storefrontErrorSchema },
|
|
83
|
+
{ status: 429, description: 'Too many requests', schema: rateLimitErrorSchema },
|
|
84
|
+
{ status: 503, description: 'Store misconfigured (no default channel binding)', schema: storefrontErrorSchema },
|
|
85
|
+
],
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const openApi: OpenApiRouteDoc = {
|
|
89
|
+
tag: storefrontProductTag,
|
|
90
|
+
summary: 'Public storefront product detail',
|
|
91
|
+
methods: {
|
|
92
|
+
GET: storefrontProductGetDoc,
|
|
93
|
+
},
|
|
94
|
+
}
|