From f961cb2e9bcfec30df528683b303fa341a2159d8 Mon Sep 17 00:00:00 2001 From: ylakhdar Date: Tue, 10 Dec 2024 16:37:12 -0500 Subject: [PATCH] feat(headless SSR): fetchStaticState should only accept solutiontype-specific controllers (#4769) This is a complex one. It introduces type changes only to ensure that props for solution type-specific controllers are only required in their respective engine definitions. ### Key changes: Recommendation props are now only required in recommendationEngineDefinition. The same logic is applied to other solution type-specific controllers. Additionally, unit tests have been added to verify that type inference and conditional prop types function correctly. https://coveord.atlassian.net/browse/KIT-3787 --------- Co-authored-by: Frederic Beaudoin --- .../src/ssr-commerce/providers.tsx | 3 +- .../commerce-engine.ssr.test.ts | 83 ++++++ .../app/commerce-ssr-engine/types/build.ts | 15 +- .../app/commerce-ssr-engine/types/common.ts | 262 +++++++++++++++++- .../commerce-ssr-engine/types/core-engine.ts | 12 +- .../types/fetch-static-state.ts | 11 +- .../types/hydrate-static-state.ts | 15 +- .../src/app/ssr-engine/types/common.ts | 16 ++ .../context/cart/headless-cart.ssr.ts | 8 +- .../commerce/context/headless-context.ssr.ts | 8 +- .../app/(listing)/[category]/page.tsx | 6 +- .../headless-ssr-commerce/app/cart/page.tsx | 6 +- .../app/products/[productId]/page.tsx | 2 - .../headless-ssr-commerce/app/search/page.tsx | 2 - 14 files changed, 411 insertions(+), 38 deletions(-) create mode 100644 packages/headless/src/app/commerce-engine/commerce-engine.ssr.test.ts diff --git a/packages/headless-react/src/ssr-commerce/providers.tsx b/packages/headless-react/src/ssr-commerce/providers.tsx index 6b9e15388d9..50789f8e03a 100644 --- a/packages/headless-react/src/ssr-commerce/providers.tsx +++ b/packages/headless-react/src/ssr-commerce/providers.tsx @@ -1,6 +1,7 @@ 'use client'; import { + HydrateStaticStateOptions, InferHydratedState, InferStaticState, NavigatorContext, @@ -67,7 +68,7 @@ export function buildProviderWithDefinition(looseDefinition: LooseDefinition) { ...controllers, ...hydrateControllers, }, - }) + } as HydrateStaticStateOptions<{type: string}>) .then(({engine, controllers}) => { setHydratedState({engine, controllers}); }); diff --git a/packages/headless/src/app/commerce-engine/commerce-engine.ssr.test.ts b/packages/headless/src/app/commerce-engine/commerce-engine.ssr.test.ts new file mode 100644 index 00000000000..38212a98178 --- /dev/null +++ b/packages/headless/src/app/commerce-engine/commerce-engine.ssr.test.ts @@ -0,0 +1,83 @@ +import {defineCart} from '../../controllers/commerce/context/cart/headless-cart.ssr.js'; +import {defineParameterManager} from '../../controllers/commerce/core/parameter-manager/headless-core-parameter-manager.ssr.js'; +import {defineSummary} from '../../controllers/commerce/core/summary/headless-core-summary.ssr.js'; +import {defineRecommendations} from '../../controllers/commerce/recommendations/headless-recommendations.ssr.js'; +import {defineDidYouMean} from '../../controllers/commerce/search/did-you-mean/headless-did-you-mean.ssr.js'; +import {getSampleCommerceEngineConfiguration} from './commerce-engine-configuration.js'; +import {defineCommerceEngine} from './commerce-engine.ssr.js'; + +describe('Commerce Engine SSR', () => { + const { + listingEngineDefinition, + recommendationEngineDefinition, + searchEngineDefinition, + standaloneEngineDefinition, + } = defineCommerceEngine({ + configuration: { + ...getSampleCommerceEngineConfiguration(), + }, + controllers: { + summary: defineSummary(), + didYouMean: defineDidYouMean(), + paramManager: defineParameterManager({listing: false}), + cart: defineCart(), + popularViewed: defineRecommendations({ + options: { + slotId: 'd73afbd2-8521-4ee6-a9b8-31f064721e73', + }, + }), + }, + }); + + it('should only require cart options for standaloneEngineDefinition', () => { + expect(() => + standaloneEngineDefinition.fetchStaticState({ + controllers: { + cart: {initialState: {items: []}}, + }, + }) + ).not.toThrow(); + }); + + it('should only require cart options for listingEngineDefinition', () => { + expect(() => + listingEngineDefinition.fetchStaticState({ + controllers: { + cart: {initialState: {items: []}}, + }, + }) + ).not.toThrow(); + }); + + it('should only require cart and paramManager options for searchEngineDefinition', () => { + expect(() => + searchEngineDefinition.fetchStaticState({ + controllers: { + cart: {initialState: {items: []}}, + paramManager: {initialState: {parameters: {}}}, + }, + }) + ).not.toThrow(); + }); + + it('should only require cart options for recommendationEngineDefinition', () => { + expect(() => + recommendationEngineDefinition.fetchStaticState({ + controllers: { + cart: {initialState: {items: []}}, + }, + }) + ).not.toThrow(); + }); + + it('should allow optional recommendation controller options for recommendationEngineDefinition', () => { + expect(() => + recommendationEngineDefinition.fetchStaticState({ + controllers: { + cart: {initialState: {items: []}}, + popularViewed: {enabled: true, productId: 'some-product-id'}, + }, + }) + ).not.toThrow(); + }); +}); diff --git a/packages/headless/src/app/commerce-ssr-engine/types/build.ts b/packages/headless/src/app/commerce-ssr-engine/types/build.ts index 22bf4f74829..239bcdb46b7 100644 --- a/packages/headless/src/app/commerce-ssr-engine/types/build.ts +++ b/packages/headless/src/app/commerce-ssr-engine/types/build.ts @@ -1,3 +1,4 @@ +import {Controller} from '../../../controllers/controller/headless-controller.js'; import type { ControllersMap, ControllersPropsMap, @@ -6,7 +7,11 @@ import type { OptionsTuple, } from '../../ssr-engine/types/common.js'; import {SSRCommerceEngine} from '../factories/build-factory.js'; -import {EngineDefinitionControllersPropsOption} from './common.js'; +import { + ControllerDefinitionsMap, + EngineDefinitionControllersPropsOption, + SolutionType, +} from './common.js'; export interface BuildOptions { extend?: OptionsExtender; @@ -16,6 +21,8 @@ export type Build< TEngineOptions, TControllersMap extends ControllersMap, TControllersProps extends ControllersPropsMap, + TControllersDefinitionsMap extends ControllerDefinitionsMap, + TSolutionType extends SolutionType, > = { /** * Initializes an engine and controllers from the definition. @@ -23,7 +30,11 @@ export type Build< ( ...params: OptionsTuple< BuildOptions & - EngineDefinitionControllersPropsOption + EngineDefinitionControllersPropsOption< + TControllersDefinitionsMap, + TControllersProps, + TSolutionType + > > ): Promise>; }; diff --git a/packages/headless/src/app/commerce-ssr-engine/types/common.ts b/packages/headless/src/app/commerce-ssr-engine/types/common.ts index bbc14b366ad..0a8b8cdff36 100644 --- a/packages/headless/src/app/commerce-ssr-engine/types/common.ts +++ b/packages/headless/src/app/commerce-ssr-engine/types/common.ts @@ -9,19 +9,19 @@ import type { EngineDefinitionBuildResult, HydratedState, OptionsTuple, - EngineDefinitionControllersPropsOption, - InferControllerPropsMapFromDefinitions, + ControllersPropsMap, + HasKeys, + HasRequiredKeys, + HasOptionalKeys, } from '../../ssr-engine/types/common.js'; import {SSRCommerceEngine} from '../factories/build-factory.js'; export type { EngineDefinitionBuildResult, - EngineDefinitionControllersPropsOption, HydratedState, OptionsTuple, InferControllerStaticStateFromController, InferControllerStaticStateMapFromControllers, - InferControllerPropsMapFromDefinitions, }; export enum SolutionType { @@ -39,8 +39,7 @@ export const recommendationInternalOptionKey = Symbol.for( export type RecommendationControllerSettings = { /** * Toggle to enable or disable the recommendation controller. - * When set to `true`, the controller will be built and will perform a recommendation request server-side. - * Otherwise, the controller will not be available in the client-side. + * When set to `true`, the controller will perform a recommendation request server-side. * * @default false */ @@ -111,6 +110,16 @@ export type InferControllerPropsFromDefinition< : RecommendationControllerSettings : unknown; +export type InferControllerPropsMapFromDefinitions< + TControllers extends ControllerDefinitionsMap, +> = { + [K in keyof TControllers as HasKeys< + InferControllerPropsFromDefinition + > extends false + ? never + : K]: InferControllerPropsFromDefinition; +}; + export type InferControllerFromDefinition< TDefinition extends ControllerDefinition, > = @@ -144,6 +153,216 @@ export type InferControllerStaticStateMapFromDefinitionsWithSolutionType< >; }; +/** + * This type defines the required and optional controller props for the engine definition. + */ +export type EngineDefinitionControllersPropsOption< + TControllers extends ControllerDefinitionsMap, + TControllersPropsMap extends ControllersPropsMap, + TSolutionType extends SolutionType, +> = OptionalEngineDefinitionControllersPropsOption< + TControllers, + TControllersPropsMap, + TSolutionType +> & + RequiredEngineDefinitionControllersPropsOption< + TControllers, + TControllersPropsMap, + TSolutionType + >; + +/** + * Represents an optional engine definition for controller properties. + * + * This type is used to define a map of optional controller properties based on the provided + * controller definitions, controller properties map, and solution type. + * + * @template TControllers - A map of controller definitions. + * @template TControllersPropsMap - A map of controller properties. + * @template TSolutionType - The type of solution. + * + * The type iterates over the controller keys (defined in the engine definition) and includes only those keys where: + * 1. The controller can be used for a specific solution type (HasKey is not 'never'). + * 2. The controller properties have optional options (HasOptionalKeys> is true). + * + * @example + * Given the following controller definitions: + * ``` + * const {recommendationEngineDefinition} = defineCommerceEngine({ + * controllers: { + * popularViewed: defineRecommendations({ + * options: {slotId: 'slot-id'} + * }) + * } + * }); + * ``` + * + * The following code will not throw an error since the 'popularViewed' controller props are optional + * ``` + * recommendationEngineDefinition.fetchStaticState({ + * controllers: { + * popularViewed: {enabled: true, productId: 'some-product-id'} // This is optional + * } + * }) + * ``` + * + * The following code (with no arguments) is also valid because the 'popularViewed' controller props are optional, and there are no other required controller props in the engine definition. + * ``` + * recommendationEngineDefinition.fetchStaticState() + * ``` + */ +export type OptionalEngineDefinitionControllersPropsOption< + TControllers extends ControllerDefinitionsMap, + TControllersPropsMap extends ControllersPropsMap, + TSolutionType extends SolutionType, +> = { + [K in keyof TControllers as HasKey< + TControllers[K], + TSolutionType + > extends never + ? never + : HasOptionalKeys< + ConditionalControllerProps< + TControllers, + TControllersPropsMap, + TSolutionType, + K + > + > extends false + ? never + : 'controllers']?: ConditionalControllerProps< + TControllers, + TControllersPropsMap, + TSolutionType, + K + >; +}; + +/** + * Represents a type that defines the required controller properties for engine definition controllers. + * + * @template TControllers - A map of controller definitions. + * @template TControllersPropsMap - A map of controller properties. + * @template TSolutionType - The type of solution being used. + * + * The type iterates over the controller keys (defined in the engine definition) and includes only those keys where: + * 1. The controller can be used for a specific solution type (HasKey is not 'never'). + * 2. The controller properties have required options (HasRequiredKeys> is true). + * + * The resulting type maps the valid keys to their corresponding conditional controller properties. + * + * @example + * Given the following controller definitions: + * ``` + * const {standaloneEngineDefinition} = defineCommerceEngine({ + * controllers: {cart: defineCart()}, + * }); + * ``` + * + * The following code will not throw an error since the 'cart' controller props are required for the standalone engine definition: + * ``` + * standaloneEngineDefinition.fetchStaticState({ + * controllers: { + * cart: {initialState: {items: []}}, + * }, + * }) + * ``` + * + */ +export type RequiredEngineDefinitionControllersPropsOption< + TControllers extends ControllerDefinitionsMap, + TControllersPropsMap extends ControllersPropsMap, + TSolutionType extends SolutionType, +> = { + [K in keyof TControllers as HasKey< + TControllers[K], + TSolutionType + > extends never + ? never + : HasRequiredKeys< + ConditionalControllerProps< + TControllers, + TControllersPropsMap, + TSolutionType, + K + > + > extends false + ? never + : 'controllers']: ConditionalControllerProps< + TControllers, + TControllersPropsMap, + TSolutionType, + K + >; +}; + +type IsRecommendationController< + TController extends ControllerDefinition, +> = HasKey; + +/** + * This type ensures that recommendation controller props are optional, while other controller props remain required. + * + * It works by checking if the controller definition includes the `recommendationInternalOptionKey`. + * + * - If the key is present, the controller props are made optional. + * - If the key is absent, the controller props are required. + * + * Example: + * + * ```typescript + * type ControllerProps = InferControllerPropsFromDefinition<{ + * recommendation: defineRecommendation({}), + * cart: defineCart(), + * }>; + * + * // In this example: + * // - The `recommendation` controller props are optional. + * // - The `cart` controller props are required. + * + * const props: ControllerProps = { + * cart: {initialState: {items: []}}, + * // recommendation props can be omitted + * }; + * ``` + */ +type RecommendationControllerProps< + TControllers extends ControllerDefinitionsMap, + TControllersPropsMap extends ControllersPropsMap, + K extends keyof TControllers, +> = { + [I in keyof TControllersPropsMap as I extends K + ? IsRecommendationController extends never + ? never + : I + : never]?: TControllersPropsMap[I]; +} & { + [I in keyof TControllersPropsMap as I extends K + ? IsRecommendationController extends never + ? I + : never + : never]: TControllersPropsMap[I]; +}; + +type DefaultControllerProps< + TControllers extends ControllerDefinitionsMap, + TControllersPropsMap extends ControllersPropsMap, + K extends keyof TControllers, +> = { + [I in keyof TControllersPropsMap as I extends K + ? I + : never]: TControllersPropsMap[I]; +}; + +type ConditionalControllerProps< + TControllers extends ControllerDefinitionsMap, + TControllersPropsMap extends ControllersPropsMap, + TSolutionType extends SolutionType, + K extends keyof TControllers, +> = TSolutionType extends SolutionType.recommendation + ? RecommendationControllerProps + : DefaultControllerProps; + export interface ControllerDefinitionOption { /** * Whether the controller will be used in a product listing context. @@ -157,6 +376,21 @@ export interface ControllerDefinitionOption { search?: boolean; } +interface NonRecommendationController { + /** + * @internal + */ + [SolutionType.search]: true; + /** + * @internal + */ + [SolutionType.listing]: true; + /** + * @internal + */ + [SolutionType.standalone]: true; +} + interface UniversalController { /** * @internal @@ -170,6 +404,10 @@ interface UniversalController { * @internal */ [SolutionType.standalone]: true; + /** + * @internal + */ + [SolutionType.recommendation]: true; } interface SearchOnlyController { @@ -251,11 +489,21 @@ export type RecommendationOnlyControllerDefinitionWithProps< export type NonRecommendationControllerDefinitionWithoutProps< TController extends Controller, -> = ControllerDefinitionWithoutProps & UniversalController; +> = ControllerDefinitionWithoutProps & NonRecommendationController; export type NonRecommendationControllerDefinitionWithProps< TController extends Controller, TProps, +> = ControllerDefinitionWithProps & + NonRecommendationController; + +export type UniversalControllerDefinitionWithoutProps< + TController extends Controller, +> = ControllerDefinitionWithoutProps & UniversalController; + +export type UniversalControllerDefinitionWithProps< + TController extends Controller, + TProps, > = ControllerDefinitionWithProps & UniversalController; export type SearchAndListingControllerDefinitionWithoutProps< diff --git a/packages/headless/src/app/commerce-ssr-engine/types/core-engine.ts b/packages/headless/src/app/commerce-ssr-engine/types/core-engine.ts index 5a0c8b62223..f7f40f674c6 100644 --- a/packages/headless/src/app/commerce-ssr-engine/types/core-engine.ts +++ b/packages/headless/src/app/commerce-ssr-engine/types/core-engine.ts @@ -58,7 +58,9 @@ export interface EngineDefinition< TControllers, TSolutionType >, - InferControllerPropsMapFromDefinitions + InferControllerPropsMapFromDefinitions, + TControllers, + TSolutionType >; /** * Fetches the hydrated state on the client side using your engine definition and the static state. @@ -66,7 +68,9 @@ export interface EngineDefinition< hydrateStaticState: HydrateStaticState< InferControllersMapFromDefinition, UnknownAction, - InferControllerPropsMapFromDefinitions + InferControllerPropsMapFromDefinitions, + TControllers, + TSolutionType >; /** * Builds an engine and its controllers from an engine definition. @@ -74,7 +78,9 @@ export interface EngineDefinition< build: Build< TEngineOptions, InferControllersMapFromDefinition, - InferControllerPropsMapFromDefinitions + InferControllerPropsMapFromDefinitions, + TControllers, + TSolutionType >; /** diff --git a/packages/headless/src/app/commerce-ssr-engine/types/fetch-static-state.ts b/packages/headless/src/app/commerce-ssr-engine/types/fetch-static-state.ts index 0090a8f8958..00be303758b 100644 --- a/packages/headless/src/app/commerce-ssr-engine/types/fetch-static-state.ts +++ b/packages/headless/src/app/commerce-ssr-engine/types/fetch-static-state.ts @@ -1,7 +1,10 @@ import type {UnknownAction} from '@reduxjs/toolkit'; +import {Controller} from '../../../controllers/controller/headless-controller.js'; import type { + ControllerDefinitionsMap, EngineDefinitionControllersPropsOption, EngineStaticState, + SolutionType, } from '../../commerce-ssr-engine/types/common.js'; import type { ControllersMap, @@ -18,6 +21,8 @@ export type FetchStaticState< TSearchAction extends UnknownAction, TControllersStaticState extends ControllerStaticStateMap, TControllersProps extends ControllersPropsMap, + TControllersDefinitionsMap extends ControllerDefinitionsMap, + TSolutionType extends SolutionType, > = { /** * Executes only the initial search for a given configuration, then returns a resumable snapshot of engine state along with the state of the controllers. @@ -27,7 +32,11 @@ export type FetchStaticState< ( ...params: OptionsTuple< FetchStaticStateOptions & - EngineDefinitionControllersPropsOption + EngineDefinitionControllersPropsOption< + TControllersDefinitionsMap, + TControllersProps, + TSolutionType + > > ): Promise>; diff --git a/packages/headless/src/app/commerce-ssr-engine/types/hydrate-static-state.ts b/packages/headless/src/app/commerce-ssr-engine/types/hydrate-static-state.ts index 95ba9983d8c..19d14d39cb4 100644 --- a/packages/headless/src/app/commerce-ssr-engine/types/hydrate-static-state.ts +++ b/packages/headless/src/app/commerce-ssr-engine/types/hydrate-static-state.ts @@ -1,4 +1,5 @@ import type {UnknownAction} from '@reduxjs/toolkit'; +import {Controller} from '../../../controllers/controller/headless-controller.js'; import type { ControllersMap, ControllersPropsMap, @@ -6,7 +7,11 @@ import type { OptionsTuple, } from '../../ssr-engine/types/common.js'; import {SSRCommerceEngine} from '../factories/build-factory.js'; -import {EngineDefinitionControllersPropsOption} from './common.js'; +import { + ControllerDefinitionsMap, + EngineDefinitionControllersPropsOption, + SolutionType, +} from './common.js'; import {FromBuildResult} from './from-build-result.js'; export interface HydrateStaticStateOptions { @@ -17,6 +22,8 @@ export type HydrateStaticState< TControllers extends ControllersMap, TSearchAction extends UnknownAction, TControllersProps extends ControllersPropsMap, + TControllersDefinitionsMap extends ControllerDefinitionsMap, + TSolutionType extends SolutionType, > = { /** * Creates a new engine from the snapshot of the engine created in SSR with fetchStaticState. @@ -26,7 +33,11 @@ export type HydrateStaticState< ( ...params: OptionsTuple< HydrateStaticStateOptions & - EngineDefinitionControllersPropsOption + EngineDefinitionControllersPropsOption< + TControllersDefinitionsMap, + TControllersProps, + TSolutionType + > > ): Promise>; diff --git a/packages/headless/src/app/ssr-engine/types/common.ts b/packages/headless/src/app/ssr-engine/types/common.ts index 831b23ec423..0a0d9eb757b 100644 --- a/packages/headless/src/app/ssr-engine/types/common.ts +++ b/packages/headless/src/app/ssr-engine/types/common.ts @@ -16,6 +16,22 @@ export type HasKeys = TObject extends {} : true : boolean; +export type HasRequiredKeys = TObject extends {} + ? { + [K in keyof TObject]-?: {} extends Pick ? never : K; + }[keyof TObject] extends never + ? false + : true + : boolean; + +export type HasOptionalKeys = TObject extends {} + ? { + [K in keyof TObject]-?: {} extends Pick ? K : never; + }[keyof TObject] extends never + ? false + : true + : boolean; + export type ExtractRequiredOptions = { [TKey in keyof TOptions as Pick extends Required< Pick diff --git a/packages/headless/src/controllers/commerce/context/cart/headless-cart.ssr.ts b/packages/headless/src/controllers/commerce/context/cart/headless-cart.ssr.ts index ac1c12864ea..b6956ccf4ec 100644 --- a/packages/headless/src/controllers/commerce/context/cart/headless-cart.ssr.ts +++ b/packages/headless/src/controllers/commerce/context/cart/headless-cart.ssr.ts @@ -1,4 +1,4 @@ -import {NonRecommendationControllerDefinitionWithProps} from '../../../../app/commerce-ssr-engine/types/common.js'; +import {UniversalControllerDefinitionWithProps} from '../../../../app/commerce-ssr-engine/types/common.js'; import {Cart, buildCart, CartInitialState} from './headless-cart.js'; export type {CartState, CartItem, CartProps} from './headless-cart.js'; @@ -9,10 +9,7 @@ export interface CartBuildProps { } export interface CartDefinition - extends NonRecommendationControllerDefinitionWithProps< - Cart, - CartBuildProps - > {} + extends UniversalControllerDefinitionWithProps {} /** * Defines a `Cart` controller instance. @@ -27,6 +24,7 @@ export function defineCart(): CartDefinition { listing: true, search: true, standalone: true, + recommendation: true, buildWithProps: (engine, props) => buildCart(engine, {initialState: props.initialState}), }; diff --git a/packages/headless/src/controllers/commerce/context/headless-context.ssr.ts b/packages/headless/src/controllers/commerce/context/headless-context.ssr.ts index dbacda42a17..67e3e250dc8 100644 --- a/packages/headless/src/controllers/commerce/context/headless-context.ssr.ts +++ b/packages/headless/src/controllers/commerce/context/headless-context.ssr.ts @@ -1,4 +1,4 @@ -import {NonRecommendationControllerDefinitionWithProps} from '../../../app/commerce-ssr-engine/types/common.js'; +import {UniversalControllerDefinitionWithProps} from '../../../app/commerce-ssr-engine/types/common.js'; import { Context, buildContext, @@ -11,10 +11,7 @@ export type {ContextState, Context, ContextProps} from './headless-context.js'; export type {View, UserLocation, ContextOptions}; export interface ContextDefinition - extends NonRecommendationControllerDefinitionWithProps< - Context, - ContextOptions - > {} + extends UniversalControllerDefinitionWithProps {} /** * Defines a `Context` controller instance. @@ -29,6 +26,7 @@ export function defineContext(): ContextDefinition { listing: true, search: true, standalone: true, + recommendation: true, buildWithProps: (engine, props) => buildContext(engine, {options: props}), }; } diff --git a/packages/samples/headless-ssr-commerce/app/(listing)/[category]/page.tsx b/packages/samples/headless-ssr-commerce/app/(listing)/[category]/page.tsx index 6fc31efb8a5..11786734624 100644 --- a/packages/samples/headless-ssr-commerce/app/(listing)/[category]/page.tsx +++ b/packages/samples/headless-ssr-commerce/app/(listing)/[category]/page.tsx @@ -50,8 +50,6 @@ export default async function Listing({params}: {params: {category: string}}) { const staticState = await listingEngineDefinition.fetchStaticState({ controllers: { cart: {initialState: {items}}, - popularBought: {}, // TODO:: KIT-3782: should not be required on listing engine definition - popularViewed: {}, // TODO:: KIT-3782: should not be required on listing engine definition context: { language: defaultContext.language, country: defaultContext.country, @@ -66,8 +64,8 @@ export default async function Listing({params}: {params: {category: string}}) { const recsStaticState = await recommendationEngineDefinition.fetchStaticState( { controllers: { - popularBought: {}, // TODO: KIT-3782: should be optional - popularViewed: {}, // TODO: KIT-3782: should be optional + popularBought: {enabled: true}, + popularViewed: {enabled: true}, cart: {initialState: {items}}, context: { language: defaultContext.language, diff --git a/packages/samples/headless-ssr-commerce/app/cart/page.tsx b/packages/samples/headless-ssr-commerce/app/cart/page.tsx index 1c4aacef84d..f0cd66ab72b 100644 --- a/packages/samples/headless-ssr-commerce/app/cart/page.tsx +++ b/packages/samples/headless-ssr-commerce/app/cart/page.tsx @@ -29,8 +29,6 @@ export default async function Search() { const staticState = await standaloneEngineDefinition.fetchStaticState({ controllers: { cart: {initialState: {items}}, - popularBought: {}, // TODO:: KIT-3782: should not be required on listing engine definition - popularViewed: {}, // TODO:: KIT-3782: should not be required on listing engine definition context: { language: defaultContext.language, country: defaultContext.country, @@ -45,8 +43,8 @@ export default async function Search() { const recsStaticState = await recommendationEngineDefinition.fetchStaticState( { controllers: { - popularBought: {}, // TODO: KIT-3782: should be optional - popularViewed: {}, // TODO: KIT-3782: should be optional + popularBought: {enabled: true}, + popularViewed: {enabled: true}, cart: {initialState: {items}}, context: { language: defaultContext.language, diff --git a/packages/samples/headless-ssr-commerce/app/products/[productId]/page.tsx b/packages/samples/headless-ssr-commerce/app/products/[productId]/page.tsx index a943bfbbaec..5c5fac01fda 100644 --- a/packages/samples/headless-ssr-commerce/app/products/[productId]/page.tsx +++ b/packages/samples/headless-ssr-commerce/app/products/[productId]/page.tsx @@ -27,8 +27,6 @@ export default async function ProductDescriptionPage({ const staticState = await standaloneEngineDefinition.fetchStaticState({ controllers: { cart: {initialState: {items}}, - popularBought: {}, // TODO:: KIT-3782: should not be required on listing engine definition - popularViewed: {}, // TODO:: KIT-3782: should not be required on listing engine definition context: { language: defaultContext.language, country: defaultContext.country, diff --git a/packages/samples/headless-ssr-commerce/app/search/page.tsx b/packages/samples/headless-ssr-commerce/app/search/page.tsx index 855ddde3c71..50b32f3310e 100644 --- a/packages/samples/headless-ssr-commerce/app/search/page.tsx +++ b/packages/samples/headless-ssr-commerce/app/search/page.tsx @@ -25,8 +25,6 @@ export default async function Search() { const staticState = await searchEngineDefinition.fetchStaticState({ controllers: { cart: {initialState: {items}}, - popularBought: {}, // TODO:: KIT-3782: should not be required on listing engine definition - popularViewed: {}, // TODO:: KIT-3782: should not be required on listing engine definition context: { language: defaultContext.language, country: defaultContext.country,