From f2184e67a98cf93e38dcd62353aae05ccfcc2f8a Mon Sep 17 00:00:00 2001 From: Alex Prudhomme <78121423+alexprudhomme@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:07:14 -0500 Subject: [PATCH] only 2 errors --- .../commerce-engine/commerce-engine.ssr.ts | 190 ++++++++++++++---- .../commerce-ssr-engine/types/core-engine.ts | 108 ++++++---- .../src/app/ssr-engine/types/build.ts | 3 + 3 files changed, 228 insertions(+), 73 deletions(-) diff --git a/packages/headless/src/app/commerce-engine/commerce-engine.ssr.ts b/packages/headless/src/app/commerce-engine/commerce-engine.ssr.ts index d8d55bf05c..503207a4b2 100644 --- a/packages/headless/src/app/commerce-engine/commerce-engine.ssr.ts +++ b/packages/headless/src/app/commerce-engine/commerce-engine.ssr.ts @@ -28,6 +28,7 @@ import { import { EngineDefinition, EngineDefinitionOptions, + RecommendationEngineDefinition, } from '../commerce-ssr-engine/types/core-engine.js'; import {buildLogger} from '../logger.js'; import {NavigatorContextProvider} from '../navigatorContextProvider.js'; @@ -137,6 +138,14 @@ function buildSSRCommerceEngine( }; } +interface RecommendationCommerceEngineDefinition< + TControllers extends ControllerDefinitionsMap, +> extends RecommendationEngineDefinition< + SSRCommerceEngine, + TControllers, + CommerceEngineOptions + > {} + export interface CommerceEngineDefinition< TControllers extends ControllerDefinitionsMap, TSolutionType extends SolutionType, @@ -173,10 +182,7 @@ export function defineCommerceEngine< TControllerDefinitions, SolutionType.standalone >; - recommendationEngineDefinition: CommerceEngineDefinition< - TControllerDefinitions, - SolutionType.recommendation - >; + recommendationEngineDefinition: RecommendationCommerceEngineDefinition; } { const {controllers: controllerDefinitions, ...engineOptions} = options; type Definition = CommerceEngineDefinition< @@ -199,6 +205,22 @@ export function defineCommerceEngine< type HydrateStaticStateFromBuildResultParameters = Parameters; + type RecommendationDefinition = + RecommendationCommerceEngineDefinition; + type RecommendationBuildFunction = RecommendationDefinition['build']; + type RecommendationFetchStaticStateFunction = + RecommendationDefinition['fetchStaticState']; + type RecommendationHydrateStaticStateFunction = + RecommendationDefinition['hydrateStaticState']; + type RecommendationFetchStaticStateFromBuildResultFunction = + RecommendationFetchStaticStateFunction['fromBuildResult']; + type RecommendationHydrateStaticStateFromBuildResultFunction = + RecommendationHydrateStaticStateFunction['fromBuildResult']; + type RecommendationBuildParameters = Parameters; + type RecommendationFetchStaticStateParameters = + Parameters; + type RecommendationFetchStaticFromBuildResultsParameters = + Parameters; const recommendationFilter = buildRecommendationFilter( controllerDefinitions ?? {} ); @@ -222,6 +244,11 @@ export function defineCommerceEngine< '[WARNING] Missing navigator context in server-side code. Make sure to set it with `setNavigatorContextProvider` before calling fetchStaticState()' ); } + + // These are the recs.. + // But why do I even need it in the build factory ????? + // logger.warn(buildOptions?.c); + const engine = buildSSRCommerceEngine( solutionType, buildOptions?.extend @@ -249,6 +276,7 @@ export function defineCommerceEngine< ) => FetchStaticStateFunction = (solutionType: SolutionType) => composeFunction( async (...params: FetchStaticStateParameters) => { + // I can't do it all, I need to split them all const buildResult = await buildFactory(solutionType)(...params); const staticState = await fetchStaticStateFactory( solutionType @@ -327,6 +355,93 @@ export function defineCommerceEngine< }, } ); + + const recommendationBuildFactory = + () => + async (...[buildOptions]: RecommendationBuildParameters) => { + const logger = buildLogger(options.loggerOptions); + if (!getOptions().navigatorContextProvider) { + logger.warn( + '[WARNING] Missing navigator context in server-side code. Make sure to set it with `setNavigatorContextProvider` before calling fetchStaticState()' + ); + } + + // These are the recs.. + // But why do I even need it in the build factory ????? + // logger.warn(buildOptions?.c); + + const engine = buildSSRCommerceEngine( + SolutionType.recommendation, + buildOptions?.extend + ? await buildOptions.extend(getOptions()) + : getOptions(), + recommendationFilter.count + ); + const controllers = buildControllerDefinitions({ + definitionsMap: (controllerDefinitions ?? {}) as TControllerDefinitions, + engine, + solutionType: SolutionType.recommendation, + propsMap: (buildOptions && 'controllers' in buildOptions + ? buildOptions.controllers + : {}) as InferControllerPropsMapFromDefinitions, + }); + + return { + engine, + controllers, + }; + }; + + const recommendationFetchStaticStateFactory: () => RecommendationFetchStaticStateFunction = + (solutionType: SolutionType) => + composeFunction( + async (...params: RecommendationFetchStaticStateParameters) => { + const buildResult = await recommendationBuildFactory()(...params); + // I can't do it all, I need to split them all + //What the hell, this function calls itself ? + const staticState = + await recommendationFetchStaticStateFactory().fromBuildResult({ + buildResult, + }); + return staticState; + }, + { + fromBuildResult: async ( + ...params: RecommendationFetchStaticFromBuildResultsParameters + ) => { + const [ + { + buildResult: {engine, controllers}, + }, + ] = params; + + if (solutionType === SolutionType.listing) { + buildProductListing(engine).executeFirstRequest(); + } else if (solutionType === SolutionType.search) { + buildSearch(engine).executeFirstSearch(); + } else if (solutionType === SolutionType.recommendation) { + // here build the filter and refresh them all + // build every recommendation and refresh them all ? + // buildRecommendations(engine).refresh(); + recommendationFilter.refresh(controllers); + } + + const searchAction = await engine.waitForRequestCompletedAction(); + + return createStaticState({ + searchAction, + controllers, + }) as EngineStaticState< + UnknownAction, + InferControllerStaticStateMapFromDefinitionsWithSolutionType< + TControllerDefinitions, + SolutionType.recommendation + > + >; + }, + } + ); + return { listingEngineDefinition: { build: buildFactory(SolutionType.listing), @@ -350,45 +465,42 @@ export function defineCommerceEngine< SolutionType.standalone >, recommendationEngineDefinition: { - build: buildFactory(SolutionType.recommendation), - fetchStaticState: fetchStaticStateFactory(SolutionType.recommendation), + build: recommendationBuildFactory(), + fetchStaticState: recommendationFetchStaticStateFactory(), hydrateStaticState: hydrateStaticStateFactory( SolutionType.recommendation ), setNavigatorContextProvider, - } as CommerceEngineDefinition< - TControllerDefinitions, - SolutionType.recommendation - >, + } as RecommendationCommerceEngineDefinition, }; } /// Sandbox -// const { -// recommendationEngineDefinition, -// searchEngineDefinition, -// standaloneEngineDefinition, -// } = defineCommerceEngine({ -// configuration: getSampleCommerceEngineConfiguration(), -// controllers: { -// standaloneSearchBox: defineStandaloneSearchBox({ -// options: {redirectionUrl: 'rest'}, -// }), -// facets: defineFacetGenerator(), -// trending: defineRecommendations({ -// options: {slotId: 'ttt'}, -// }), -// popular: defineRecommendations({ -// options: {slotId: 'ppp'}, -// }), -// }, -// }); - -// // TODO: should have a way to select which recommendation to fetch -// const r = await standaloneEngineDefinition.fetchStaticState(); -// r.controllers.standaloneSearchBox; - -// const b = await recommendationEngineDefinition.fetchStaticState(['trending']); -// b.controllers.trending; - -// const a = await searchEngineDefinition.fetchStaticState(); -// a.controllers; // TODO: should throw an error since it's not defined in search +const { + recommendationEngineDefinition, + searchEngineDefinition, + standaloneEngineDefinition, +} = defineCommerceEngine({ + configuration: getSampleCommerceEngineConfiguration(), + controllers: { + standaloneSearchBox: defineStandaloneSearchBox({ + options: {redirectionUrl: 'rest'}, + }), + facets: defineFacetGenerator(), + trending: defineRecommendations({ + options: {slotId: 'ttt'}, + }), + popular: defineRecommendations({ + options: {slotId: 'ppp'}, + }), + }, +}); + +// TODO: should have a way to select which recommendation to fetch +const r = await standaloneEngineDefinition.fetchStaticState(); +r.controllers.standaloneSearchBox; + +const b = await recommendationEngineDefinition.fetchStaticState(['popular']); +b.controllers.trending; + +const a = await searchEngineDefinition.fetchStaticState(); +a.controllers; // TODO: should throw an error since it's not defined in search 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 fc2af464a7..5e43667a39 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 @@ -40,27 +40,16 @@ export interface EngineDefinition< /** * Fetches the static state on the server side using your engine definition. */ - fetchStaticState: TSolutionType extends SolutionType.recommendation - ? FetchStaticStateWithList< - TEngine, - InferControllersMapFromDefinition, - UnknownAction, - InferControllerStaticStateMapFromDefinitionsWithSolutionType< - TControllers, - TSolutionType - >, - InferControllerPropsMapFromDefinitions - > - : FetchStaticState< - TEngine, - InferControllersMapFromDefinition, - UnknownAction, - InferControllerStaticStateMapFromDefinitionsWithSolutionType< - TControllers, - TSolutionType - >, - InferControllerPropsMapFromDefinitions - >; + fetchStaticState: FetchStaticState< + TEngine, + InferControllersMapFromDefinition, + UnknownAction, + InferControllerStaticStateMapFromDefinitionsWithSolutionType< + TControllers, + TSolutionType + >, + InferControllerPropsMapFromDefinitions + >; /** * Fetches the hydrated state on the client side using your engine definition and the static state. */ @@ -73,19 +62,70 @@ export interface EngineDefinition< /** * Builds an engine and its controllers from an engine definition. */ - build: TSolutionType extends SolutionType.recommendation - ? BuildWithList< - TEngine, - TEngineOptions, - InferControllersMapFromDefinition, - InferControllerPropsMapFromDefinitions - > - : Build< - TEngine, - TEngineOptions, - InferControllersMapFromDefinition, - InferControllerPropsMapFromDefinitions - >; + build: Build< + TEngine, + TEngineOptions, + InferControllersMapFromDefinition, + InferControllerPropsMapFromDefinitions + >; + + /** + * Sets the navigator context provider. + * This provider is essential for retrieving navigation-related data such as referrer, userAgent, location, and clientId, which are crucial for handling both server-side and client-side API requests effectively. + * + * Note: The implementation specifics of the navigator context provider depend on the Node.js framework being utilized. It is the developer's responsibility to appropriately define and implement the navigator context provider to ensure accurate navigation context is available throughout the application. If the user fails to provide a navigator context provider, a warning will be logged either on the server or the browser console. + */ + setNavigatorContextProvider: ( + navigatorContextProvider: NavigatorContextProvider + ) => void; +} + +export interface RecommendationEngineDefinition< + TEngine extends CoreEngine | CoreEngineNext, + TControllers extends ControllerDefinitionsMap, + TEngineOptions, +> { + /** + * Fetches the static state on the server side using your engine definition. + */ + fetchStaticState: FetchStaticStateWithList< + TEngine, + InferControllersMapFromDefinition< + TControllers, + SolutionType.recommendation + >, + UnknownAction, + InferControllerStaticStateMapFromDefinitionsWithSolutionType< + TControllers, + SolutionType.recommendation + >, + InferControllerPropsMapFromDefinitions + >; + + /** + * Fetches the hydrated state on the client side using your engine definition and the static state. + */ + hydrateStaticState: HydrateStaticState< + TEngine, + InferControllersMapFromDefinition< + TControllers, + SolutionType.recommendation + >, + UnknownAction, + InferControllerPropsMapFromDefinitions + >; + /** + * Builds an engine and its controllers from an engine definition. + */ + build: BuildWithList< + TEngine, + TEngineOptions, + InferControllersMapFromDefinition< + TControllers, + SolutionType.recommendation + >, + InferControllerPropsMapFromDefinitions + >; /** * Sets the navigator context provider. diff --git a/packages/headless/src/app/ssr-engine/types/build.ts b/packages/headless/src/app/ssr-engine/types/build.ts index bb6c9eca00..65d5072e17 100644 --- a/packages/headless/src/app/ssr-engine/types/build.ts +++ b/packages/headless/src/app/ssr-engine/types/build.ts @@ -8,7 +8,10 @@ import { OptionsTuple, } from './common.js'; +// can I build them like so ? export interface BuildOptions { + // c: (keyof TControllersMap)[]; // list of controllers to build, but how did that work ?? + //Why is it optional ????? extend?: OptionsExtender; }