@reactionary/hcl 0.9.12 → 0.9.13

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.
@@ -0,0 +1,98 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
+ if (decorator = decorators[i])
7
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
+ if (kind && result)
9
+ __defProp(target, key, result);
10
+ return result;
11
+ };
12
+ import {
13
+ AnalyticsCapability,
14
+ AnalyticsMutationSchema,
15
+ AnalyticsResultSchema,
16
+ Reactionary
17
+ } from "@reactionary/core";
18
+ import { SESSION_KEY_PERSONALIZATION_ID } from "../core/session-keys.js";
19
+ class HclAnalyticsCapability extends AnalyticsCapability {
20
+ constructor(cache, context, config, client) {
21
+ super(cache, context);
22
+ this.config = config;
23
+ this.client = client;
24
+ }
25
+ async track(event) {
26
+ return super.track(event);
27
+ }
28
+ async processProductSummaryView(event) {
29
+ try {
30
+ for (const product of event.products) {
31
+ await this.fireEvent({ productId: product.key });
32
+ }
33
+ return this.accepted();
34
+ } catch {
35
+ return this.rejected();
36
+ }
37
+ }
38
+ async processProductSummaryClick(event) {
39
+ try {
40
+ await this.fireEvent({ productId: event.product.key });
41
+ return this.accepted();
42
+ } catch {
43
+ return this.rejected();
44
+ }
45
+ }
46
+ async processProductDetailsView(event) {
47
+ try {
48
+ await this.fireEvent({ productId: event.product.key });
49
+ return this.accepted();
50
+ } catch {
51
+ return this.rejected();
52
+ }
53
+ }
54
+ async processProductAddToCart(event) {
55
+ try {
56
+ await this.fireEvent({ productId: event.product.key });
57
+ return this.accepted();
58
+ } catch {
59
+ return this.rejected();
60
+ }
61
+ }
62
+ async processPurchase(event) {
63
+ try {
64
+ for (const item of event.order.items) {
65
+ await this.fireEvent({ productId: item.variant.sku });
66
+ }
67
+ return this.accepted();
68
+ } catch {
69
+ return this.rejected();
70
+ }
71
+ }
72
+ async fireEvent(extra) {
73
+ await this.client.callPost(
74
+ this.getEventUrl(),
75
+ this.getEventBody(extra)
76
+ );
77
+ }
78
+ getEventUrl() {
79
+ return `${this.client.transactionBaseUrl}/event`;
80
+ }
81
+ getEventBody(extra) {
82
+ const personalizationID = this.context.session[SESSION_KEY_PERSONALIZATION_ID];
83
+ const body = { ...extra };
84
+ if (personalizationID) {
85
+ body["personalizationID"] = personalizationID;
86
+ }
87
+ return body;
88
+ }
89
+ }
90
+ __decorateClass([
91
+ Reactionary({
92
+ inputSchema: AnalyticsMutationSchema,
93
+ outputSchema: AnalyticsResultSchema
94
+ })
95
+ ], HclAnalyticsCapability.prototype, "track", 1);
96
+ export {
97
+ HclAnalyticsCapability
98
+ };
@@ -0,0 +1,85 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
+ if (decorator = decorators[i])
7
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
+ if (kind && result)
9
+ __defProp(target, key, result);
10
+ return result;
11
+ };
12
+ import {
13
+ PersonalizationProfileCapability,
14
+ PersonalizationProfileQueryGetProfileSchema,
15
+ PersonalizationProfileSchema,
16
+ Reactionary,
17
+ success
18
+ } from "@reactionary/core";
19
+ import { SESSION_KEY_PERSONALIZATION_ID } from "../core/session-keys.js";
20
+ class HclPersonalizationProfileCapability extends PersonalizationProfileCapability {
21
+ constructor(cache, context, config, client, factory) {
22
+ super(cache, context);
23
+ this.config = config;
24
+ this.client = client;
25
+ this.factory = factory;
26
+ }
27
+ async getPersonalizationProfile(_payload) {
28
+ const personalizationId = this.context.session[SESSION_KEY_PERSONALIZATION_ID];
29
+ if (!personalizationId) {
30
+ return success(
31
+ this.factory.parsePersonalizationProfile(this.context, {
32
+ personalizationId: "anonymous",
33
+ segments: []
34
+ })
35
+ );
36
+ }
37
+ const segments = await this.fetchSegments(personalizationId);
38
+ return success(
39
+ this.factory.parsePersonalizationProfile(this.context, {
40
+ personalizationId,
41
+ segments
42
+ })
43
+ );
44
+ }
45
+ /**
46
+ * Calls the WCS segment API to retrieve the customer segments for the given
47
+ * personalization ID. Returns an empty array when the API call fails or
48
+ * returns no segment data.
49
+ */
50
+ async fetchSegments(personalizationId) {
51
+ try {
52
+ const response = await this.client.callGet(
53
+ this.getSegmentsUrl(),
54
+ this.getSegmentsParams(personalizationId),
55
+ { allowUndefined: true }
56
+ );
57
+ if (!response?.MemberGroup) {
58
+ return [];
59
+ }
60
+ return response.MemberGroup.map((g) => g.displayName?.value).filter(
61
+ (name) => name !== void 0 && name.length > 0
62
+ );
63
+ } catch {
64
+ return [];
65
+ }
66
+ }
67
+ getSegmentsUrl() {
68
+ return `${this.client.transactionBaseUrl}/segment`;
69
+ }
70
+ getSegmentsParams(personalizationId) {
71
+ const params = new URLSearchParams();
72
+ params.set("q", "byPersonalizationId");
73
+ params.set("qPersonalizationId", personalizationId);
74
+ return params;
75
+ }
76
+ }
77
+ __decorateClass([
78
+ Reactionary({
79
+ inputSchema: PersonalizationProfileQueryGetProfileSchema,
80
+ outputSchema: PersonalizationProfileSchema
81
+ })
82
+ ], HclPersonalizationProfileCapability.prototype, "getPersonalizationProfile", 1);
83
+ export {
84
+ HclPersonalizationProfileCapability
85
+ };
@@ -0,0 +1,117 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
+ if (decorator = decorators[i])
7
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
+ if (kind && result)
9
+ __defProp(target, key, result);
10
+ return result;
11
+ };
12
+ import {
13
+ ProductAssociationsCapability,
14
+ ProductAssociationSchema,
15
+ ProductAssociationsGetAccessoriesQuerySchema,
16
+ ProductAssociationsGetSparepartsQuerySchema,
17
+ ProductAssociationsGetReplacementsQuerySchema,
18
+ Reactionary,
19
+ success
20
+ } from "@reactionary/core";
21
+ import * as z from "zod";
22
+ class HclProductAssociationsCapability extends ProductAssociationsCapability {
23
+ constructor(cache, context, config, client, factory) {
24
+ super(cache, context);
25
+ this.config = config;
26
+ this.client = client;
27
+ this.factory = factory;
28
+ }
29
+ async getAccessories(query) {
30
+ const associations = await this.fetchAssociations(
31
+ query.forProduct.key,
32
+ this.config.associationTypes.accessories,
33
+ query.numberOfAccessories
34
+ );
35
+ return success(
36
+ associations.map((a) => this.factory.parseAssociation(this.context, a))
37
+ );
38
+ }
39
+ async getSpareparts(query) {
40
+ const associations = await this.fetchAssociations(
41
+ query.forProduct.key,
42
+ this.config.associationTypes.spareparts,
43
+ query.numberOfSpareparts
44
+ );
45
+ return success(
46
+ associations.map((a) => this.factory.parseAssociation(this.context, a))
47
+ );
48
+ }
49
+ async getReplacements(query) {
50
+ const associations = await this.fetchAssociations(
51
+ query.forProduct.key,
52
+ this.config.associationTypes.replacements,
53
+ query.numberOfReplacements
54
+ );
55
+ return success(
56
+ associations.map((a) => this.factory.parseAssociation(this.context, a))
57
+ );
58
+ }
59
+ /**
60
+ * Fetches the product detail for the given part number and returns the
61
+ * `merchandisingAssociations` filtered to the requested association types,
62
+ * capped at `maxCount`.
63
+ */
64
+ async fetchAssociations(partNumber, types, maxCount) {
65
+ const response = await this.client.callGet(
66
+ this.getProductsUrl(),
67
+ this.getProductsParams(partNumber)
68
+ );
69
+ const product = (response.contents ?? response.catalogEntryView ?? [])[0];
70
+ if (!product?.merchandisingAssociations) {
71
+ return [];
72
+ }
73
+ return product.merchandisingAssociations.filter(
74
+ (a) => a.associationCodeType && types.includes(a.associationCodeType)
75
+ ).slice(0, maxCount);
76
+ }
77
+ getProductsUrl() {
78
+ return `${this.client.catalogBaseUrl}/api/v2/products`;
79
+ }
80
+ getProductsParams(partNumber) {
81
+ const params = new URLSearchParams();
82
+ params.set("storeId", this.config.storeId);
83
+ params.append("partNumber", partNumber);
84
+ params.set("profileName", this.config.profiles.product);
85
+ return params;
86
+ }
87
+ }
88
+ __decorateClass([
89
+ Reactionary({
90
+ inputSchema: ProductAssociationsGetAccessoriesQuerySchema,
91
+ outputSchema: z.array(ProductAssociationSchema),
92
+ cache: true,
93
+ cacheTimeToLiveInSeconds: 300,
94
+ currencyDependentCaching: false
95
+ })
96
+ ], HclProductAssociationsCapability.prototype, "getAccessories", 1);
97
+ __decorateClass([
98
+ Reactionary({
99
+ inputSchema: ProductAssociationsGetSparepartsQuerySchema,
100
+ outputSchema: z.array(ProductAssociationSchema),
101
+ cache: true,
102
+ cacheTimeToLiveInSeconds: 300,
103
+ currencyDependentCaching: false
104
+ })
105
+ ], HclProductAssociationsCapability.prototype, "getSpareparts", 1);
106
+ __decorateClass([
107
+ Reactionary({
108
+ inputSchema: ProductAssociationsGetReplacementsQuerySchema,
109
+ outputSchema: z.array(ProductAssociationSchema),
110
+ cache: true,
111
+ cacheTimeToLiveInSeconds: 300,
112
+ currencyDependentCaching: false
113
+ })
114
+ ], HclProductAssociationsCapability.prototype, "getReplacements", 1);
115
+ export {
116
+ HclProductAssociationsCapability
117
+ };
@@ -0,0 +1,143 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
+ if (decorator = decorators[i])
7
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
+ if (kind && result)
9
+ __defProp(target, key, result);
10
+ return result;
11
+ };
12
+ import {
13
+ ProductRecommendationsCapability,
14
+ ProductRecommendationsQuerySchema,
15
+ ProductRecommendationsByCollectionQuerySchema,
16
+ Reactionary,
17
+ success
18
+ } from "@reactionary/core";
19
+ class HclProductRecommendationsCapability extends ProductRecommendationsCapability {
20
+ constructor(cache, context, config, client, factory) {
21
+ super(cache, context);
22
+ this.config = config;
23
+ this.client = client;
24
+ this.factory = factory;
25
+ }
26
+ getRecommendations(...args) {
27
+ return super.getRecommendations(...args);
28
+ }
29
+ async getCollection(query) {
30
+ const params = {
31
+ productId: query.sourceProduct?.[0]?.key,
32
+ categoryId: query.sourceCategory?.key,
33
+ count: String(query.numberOfRecommendations)
34
+ };
35
+ const recommendations = await this.fetchEspot(
36
+ query.collectionName,
37
+ query.collectionName,
38
+ params
39
+ );
40
+ return success(recommendations.slice(0, query.numberOfRecommendations));
41
+ }
42
+ async getFrequentlyBoughtTogetherRecommendations(query) {
43
+ return this.fetchEspot(
44
+ this.config.espotNames.frequentlyBoughtTogether,
45
+ "frequentlyBoughtTogether",
46
+ {
47
+ productId: query.sourceProduct.key,
48
+ count: String(query.numberOfRecommendations)
49
+ }
50
+ );
51
+ }
52
+ async getSimilarProductsRecommendations(query) {
53
+ return this.fetchEspot(this.config.espotNames.similar, "similar", {
54
+ productId: query.sourceProduct.key,
55
+ count: String(query.numberOfRecommendations)
56
+ });
57
+ }
58
+ async getRelatedProductsRecommendations(query) {
59
+ return this.fetchEspot(this.config.espotNames.related, "related", {
60
+ productId: query.sourceProduct.key,
61
+ count: String(query.numberOfRecommendations)
62
+ });
63
+ }
64
+ async getTrendingInCategoryRecommendations(query) {
65
+ return this.fetchEspot(
66
+ this.config.espotNames.trendingInCategory,
67
+ "trendingInCategory",
68
+ {
69
+ categoryId: query.sourceCategory.key,
70
+ count: String(query.numberOfRecommendations)
71
+ }
72
+ );
73
+ }
74
+ async getPopularProductsRecommendations(query) {
75
+ return this.fetchEspot(this.config.espotNames.popular, "popular", {
76
+ count: String(query.numberOfRecommendations)
77
+ });
78
+ }
79
+ async getTopPicksProductsRecommendations(query) {
80
+ return this.fetchEspot(this.config.espotNames.topPicks, "topPicks", {
81
+ count: String(query.numberOfRecommendations)
82
+ });
83
+ }
84
+ async getAlsoViewedProductsRecommendations(query) {
85
+ return this.fetchEspot(this.config.espotNames.alsoViewed, "alsoViewed", {
86
+ productId: query.sourceProduct.key,
87
+ count: String(query.numberOfRecommendations)
88
+ });
89
+ }
90
+ /**
91
+ * Calls an HCL marketing spot (espot) by name and maps its catalog entry
92
+ * activity data to `ProductRecommendation[]` using the factory.
93
+ *
94
+ * Returns an empty array when the espot is not found (404) or returns no
95
+ * catalog entry data — allowing teams to configure only the espots they need.
96
+ */
97
+ async fetchEspot(name, algorithm, params = {}) {
98
+ const response = await this.client.callGet(
99
+ this.getEspotUrl(name),
100
+ this.getEspotParams(params),
101
+ { allowUndefined: true }
102
+ );
103
+ if (!response) {
104
+ return [];
105
+ }
106
+ const activities = response.MarketingSpotData?.[0]?.baseMarketingSpotActivityData ?? [];
107
+ return activities.filter(
108
+ (a) => a.baseMarketingSpotDataType === "CatalogEntry" || a.baseMarketingSpotDataType === "CatalogEntryId"
109
+ ).map(
110
+ (a) => this.factory.parseRecommendation(this.context, {
111
+ activityData: a,
112
+ algorithm
113
+ })
114
+ ).filter((r) => r.product.key !== "");
115
+ }
116
+ getEspotUrl(name) {
117
+ return `${this.client.transactionBaseUrl}/espot/${encodeURIComponent(name)}`;
118
+ }
119
+ getEspotParams(params) {
120
+ const searchParams = new URLSearchParams();
121
+ const count = params["count"];
122
+ if (count)
123
+ searchParams.set("DM_DisplayProducts", count);
124
+ if (params["productId"])
125
+ searchParams.set("productId", params["productId"]);
126
+ if (params["categoryId"])
127
+ searchParams.set("categoryId", params["categoryId"]);
128
+ return searchParams;
129
+ }
130
+ }
131
+ __decorateClass([
132
+ Reactionary({
133
+ inputSchema: ProductRecommendationsQuerySchema
134
+ })
135
+ ], HclProductRecommendationsCapability.prototype, "getRecommendations", 1);
136
+ __decorateClass([
137
+ Reactionary({
138
+ inputSchema: ProductRecommendationsByCollectionQuerySchema
139
+ })
140
+ ], HclProductRecommendationsCapability.prototype, "getCollection", 1);
141
+ export {
142
+ HclProductRecommendationsCapability
143
+ };
@@ -3,6 +3,9 @@ import {
3
3
  IdentitySchema,
4
4
  OrderSchema,
5
5
  OrderSearchResultSchema,
6
+ PersonalizationProfileSchema,
7
+ ProductAssociationSchema,
8
+ ProductRecommendationSchema,
6
9
  ProductSchema,
7
10
  ProfileSchema
8
11
  } from "@reactionary/core";
@@ -25,8 +28,11 @@ import {
25
28
  HclInventoryFactory,
26
29
  HclOrderFactory,
27
30
  HclOrderSearchFactory,
31
+ HclPersonalizationProfileFactory,
28
32
  HclPriceFactory,
33
+ HclProductAssociationsFactory,
29
34
  HclProductFactory,
35
+ HclProductRecommendationsFactory,
30
36
  HclProductSearchFactory,
31
37
  HclProfileFactory
32
38
  } from "../factories/index.js";
@@ -41,6 +47,10 @@ import { HclIdentityCapability } from "../capabilities/identity.capability.js";
41
47
  import { HclProfileCapability } from "../capabilities/profile.capability.js";
42
48
  import { HclOrderCapability } from "../capabilities/order.capability.js";
43
49
  import { HclOrderSearchCapability } from "../capabilities/order-search.capability.js";
50
+ import { HclAnalyticsCapability } from "../capabilities/analytics.capability.js";
51
+ import { HclProductAssociationsCapability } from "../capabilities/product-associations.capability.js";
52
+ import { HclProductRecommendationsCapability } from "../capabilities/product-recommendations.capability.js";
53
+ import { HclPersonalizationProfileCapability } from "../capabilities/personalization-profile.capability.js";
44
54
  import {
45
55
  CartIdentifierSchema,
46
56
  CartPaginatedSearchResultSchema,
@@ -252,6 +262,73 @@ function withHclCapabilities(configuration, capabilities) {
252
262
  buildCapabilityArgs
253
263
  );
254
264
  }
265
+ if (caps.analytics?.enabled) {
266
+ const analyticsCap = capabilities.analytics;
267
+ if (analyticsCap?.capability) {
268
+ client.analytics = analyticsCap.capability(
269
+ buildCapabilityArgs(void 0)
270
+ );
271
+ } else {
272
+ client.analytics = new HclAnalyticsCapability(
273
+ cache,
274
+ context,
275
+ config,
276
+ hclClient
277
+ );
278
+ }
279
+ }
280
+ if (caps.productAssociations?.enabled) {
281
+ client.productAssociations = resolveCapabilityWithFactory(
282
+ capabilities.productAssociations,
283
+ {
284
+ factory: new HclProductAssociationsFactory(ProductAssociationSchema),
285
+ capability: (args) => new HclProductAssociationsCapability(
286
+ args.cache,
287
+ args.context,
288
+ args.config,
289
+ args.hclClient,
290
+ args.factory
291
+ )
292
+ },
293
+ buildCapabilityArgs
294
+ );
295
+ }
296
+ if (caps.productRecommendations?.enabled) {
297
+ client.productRecommendations = resolveCapabilityWithFactory(
298
+ capabilities.productRecommendations,
299
+ {
300
+ factory: new HclProductRecommendationsFactory(
301
+ ProductRecommendationSchema
302
+ ),
303
+ capability: (args) => new HclProductRecommendationsCapability(
304
+ args.cache,
305
+ args.context,
306
+ args.config,
307
+ args.hclClient,
308
+ args.factory
309
+ )
310
+ },
311
+ buildCapabilityArgs
312
+ );
313
+ }
314
+ if (caps.personalizationProfile?.enabled) {
315
+ client.personalizationProfile = resolveCapabilityWithFactory(
316
+ capabilities.personalizationProfile,
317
+ {
318
+ factory: new HclPersonalizationProfileFactory(
319
+ PersonalizationProfileSchema
320
+ ),
321
+ capability: (args) => new HclPersonalizationProfileCapability(
322
+ args.cache,
323
+ args.context,
324
+ args.config,
325
+ args.hclClient,
326
+ args.factory
327
+ )
328
+ },
329
+ buildCapabilityArgs
330
+ );
331
+ }
255
332
  return client;
256
333
  };
257
334
  }
@@ -9,3 +9,6 @@ export * from "./identity/identity.factory.js";
9
9
  export * from "./profile/profile.factory.js";
10
10
  export * from "./order/order.factory.js";
11
11
  export * from "./order-search/order-search.factory.js";
12
+ export * from "./product-associations/product-associations.factory.js";
13
+ export * from "./product-recommendations/product-recommendations.factory.js";
14
+ export * from "./personalization-profile/personalization-profile.factory.js";
@@ -0,0 +1,16 @@
1
+ class HclPersonalizationProfileFactory {
2
+ personalizationProfileSchema;
3
+ constructor(personalizationProfileSchema) {
4
+ this.personalizationProfileSchema = personalizationProfileSchema;
5
+ }
6
+ parsePersonalizationProfile(_context, data) {
7
+ return this.personalizationProfileSchema.parse({
8
+ identifier: { key: data.personalizationId },
9
+ segments: data.segments,
10
+ blurb: ""
11
+ });
12
+ }
13
+ }
14
+ export {
15
+ HclPersonalizationProfileFactory
16
+ };
@@ -0,0 +1,16 @@
1
+ class HclProductAssociationsFactory {
2
+ productAssociationSchema;
3
+ constructor(productAssociationSchema) {
4
+ this.productAssociationSchema = productAssociationSchema;
5
+ }
6
+ parseAssociation(_context, data) {
7
+ return this.productAssociationSchema.parse({
8
+ associationIdentifier: { key: data.partNumber },
9
+ associationReturnType: "idOnly",
10
+ product: { key: data.partNumber }
11
+ });
12
+ }
13
+ }
14
+ export {
15
+ HclProductAssociationsFactory
16
+ };
@@ -0,0 +1,17 @@
1
+ class HclProductRecommendationsFactory {
2
+ productRecommendationSchema;
3
+ constructor(productRecommendationSchema) {
4
+ this.productRecommendationSchema = productRecommendationSchema;
5
+ }
6
+ parseRecommendation(_context, data) {
7
+ const key = data.activityData.partNumber ?? data.activityData.contentId ?? "";
8
+ return this.productRecommendationSchema.parse({
9
+ recommendationIdentifier: { key, algorithm: data.algorithm },
10
+ recommendationReturnType: "idOnly",
11
+ product: { key }
12
+ });
13
+ }
14
+ }
15
+ export {
16
+ HclProductRecommendationsFactory
17
+ };
package/index.js CHANGED
@@ -17,3 +17,7 @@ export * from "./capabilities/identity.capability.js";
17
17
  export * from "./capabilities/profile.capability.js";
18
18
  export * from "./capabilities/order.capability.js";
19
19
  export * from "./capabilities/order-search.capability.js";
20
+ export * from "./capabilities/analytics.capability.js";
21
+ export * from "./capabilities/product-associations.capability.js";
22
+ export * from "./capabilities/product-recommendations.capability.js";
23
+ export * from "./capabilities/personalization-profile.capability.js";
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@reactionary/hcl",
3
- "version": "0.9.12",
3
+ "version": "0.9.13",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "types": "./src/index.d.ts",
7
7
  "dependencies": {
8
- "@reactionary/core": "0.9.12",
8
+ "@reactionary/core": "0.9.13",
9
9
  "debug": "^4.4.3",
10
10
  "zod": "4.1.9"
11
11
  },
@@ -16,7 +16,11 @@ const HclCapabilitiesSchema = CapabilitiesSchema.pick({
16
16
  productSearch: true,
17
17
  profile: true,
18
18
  order: true,
19
- orderSearch: true
19
+ orderSearch: true,
20
+ analytics: true,
21
+ productAssociations: true,
22
+ productRecommendations: true,
23
+ personalizationProfile: true
20
24
  }).extend({
21
25
  cart: OverridableCapabilitySchema.optional(),
22
26
  checkout: OverridableCapabilitySchema.optional(),
@@ -28,7 +32,11 @@ const HclCapabilitiesSchema = CapabilitiesSchema.pick({
28
32
  productSearch: OverridableCapabilitySchema.optional(),
29
33
  profile: OverridableCapabilitySchema.optional(),
30
34
  order: OverridableCapabilitySchema.optional(),
31
- orderSearch: OverridableCapabilitySchema.optional()
35
+ orderSearch: OverridableCapabilitySchema.optional(),
36
+ analytics: OverridableCapabilitySchema.optional(),
37
+ productAssociations: OverridableCapabilitySchema.optional(),
38
+ productRecommendations: OverridableCapabilitySchema.optional(),
39
+ personalizationProfile: OverridableCapabilitySchema.optional()
32
40
  }).partial();
33
41
  export {
34
42
  HclCapabilitiesSchema
@@ -37,7 +37,43 @@ const HclConfigurationSchema = z.looseObject({
37
37
  */
38
38
  priceRuleId: z.string().optional().meta({
39
39
  description: "Price rule ID for /display_price WCS calls. Optional \u2014 the server uses its default rule when absent."
40
- })
40
+ }),
41
+ /**
42
+ * Maps Reactionary association types to the HCL `associationCodeType` values
43
+ * returned in `merchandisingAssociations` on product detail responses.
44
+ * Override to match custom HCL store configuration.
45
+ */
46
+ associationTypes: z.looseObject({
47
+ accessories: z.array(z.string()).default(["ACCESSORY"]),
48
+ spareparts: z.array(z.string()).default(["SPAREPART"]),
49
+ replacements: z.array(z.string()).default(["REPLACEMENT"])
50
+ }).default(() => ({
51
+ accessories: ["ACCESSORY"],
52
+ spareparts: ["SPAREPART"],
53
+ replacements: ["REPLACEMENT"]
54
+ })),
55
+ /**
56
+ * Names of HCL marketing spots used for product recommendation algorithms.
57
+ * Each algorithm calls the corresponding named espot via the WCS Transaction Service.
58
+ * These espots must be configured in HCL Commerce to return catalog entry data.
59
+ */
60
+ espotNames: z.looseObject({
61
+ frequentlyBoughtTogether: z.string().default("Reactionary_FrequentlyBoughtTogether"),
62
+ similar: z.string().default("Reactionary_SimilarProducts"),
63
+ related: z.string().default("Reactionary_RelatedProducts"),
64
+ trendingInCategory: z.string().default("Reactionary_TrendingInCategory"),
65
+ popular: z.string().default("Reactionary_Popular"),
66
+ topPicks: z.string().default("Reactionary_TopPicks"),
67
+ alsoViewed: z.string().default("Reactionary_AlsoViewed")
68
+ }).default(() => ({
69
+ frequentlyBoughtTogether: "Reactionary_FrequentlyBoughtTogether",
70
+ similar: "Reactionary_SimilarProducts",
71
+ related: "Reactionary_RelatedProducts",
72
+ trendingInCategory: "Reactionary_TrendingInCategory",
73
+ popular: "Reactionary_Popular",
74
+ topPicks: "Reactionary_TopPicks",
75
+ alsoViewed: "Reactionary_AlsoViewed"
76
+ }))
41
77
  });
42
78
  export {
43
79
  HclConfigurationSchema
@@ -0,0 +1,17 @@
1
+ import { AnalyticsCapability, type AnalyticsMutation, type AnalyticsMutationProductAddToCartEvent, type AnalyticsMutationProductDetailsViewEvent, type AnalyticsMutationProductSummaryClickEvent, type AnalyticsMutationProductSummaryViewEvent, type AnalyticsMutationPurchaseEvent, type AnalyticsResult, type Cache, type RequestContext } from '@reactionary/core';
2
+ import type { HclConfiguration } from '../schema/configuration.schema.js';
3
+ import type { HclClient } from '../core/client.js';
4
+ export declare class HclAnalyticsCapability extends AnalyticsCapability {
5
+ protected readonly config: HclConfiguration;
6
+ protected readonly client: HclClient;
7
+ constructor(cache: Cache, context: RequestContext, config: HclConfiguration, client: HclClient);
8
+ track(event: AnalyticsMutation): Promise<AnalyticsResult>;
9
+ protected processProductSummaryView(event: AnalyticsMutationProductSummaryViewEvent): Promise<AnalyticsResult>;
10
+ protected processProductSummaryClick(event: AnalyticsMutationProductSummaryClickEvent): Promise<AnalyticsResult>;
11
+ protected processProductDetailsView(event: AnalyticsMutationProductDetailsViewEvent): Promise<AnalyticsResult>;
12
+ protected processProductAddToCart(event: AnalyticsMutationProductAddToCartEvent): Promise<AnalyticsResult>;
13
+ protected processPurchase(event: AnalyticsMutationPurchaseEvent): Promise<AnalyticsResult>;
14
+ protected fireEvent(extra: Record<string, string>): Promise<void>;
15
+ protected getEventUrl(): string;
16
+ protected getEventBody(extra: Record<string, string>): Record<string, string>;
17
+ }
@@ -0,0 +1,19 @@
1
+ import { PersonalizationProfileCapability, type Cache, type NotFoundError, type PersonalizationProfileFactory, type PersonalizationProfileFactoryOutput, type PersonalizationProfileFactoryWithOutput, type PersonalizationProfileQueryGetProfile, type RequestContext, type Result } from '@reactionary/core';
2
+ import type { HclConfiguration } from '../schema/configuration.schema.js';
3
+ import type { HclClient } from '../core/client.js';
4
+ import type { HclPersonalizationProfileFactory } from '../factories/personalization-profile/personalization-profile.factory.js';
5
+ export declare class HclPersonalizationProfileCapability<TFactory extends PersonalizationProfileFactory = HclPersonalizationProfileFactory> extends PersonalizationProfileCapability<PersonalizationProfileFactoryOutput<TFactory>> {
6
+ protected readonly config: HclConfiguration;
7
+ protected readonly client: HclClient;
8
+ protected readonly factory: PersonalizationProfileFactoryWithOutput<TFactory>;
9
+ constructor(cache: Cache, context: RequestContext, config: HclConfiguration, client: HclClient, factory: PersonalizationProfileFactoryWithOutput<TFactory>);
10
+ getPersonalizationProfile(_payload: PersonalizationProfileQueryGetProfile): Promise<Result<PersonalizationProfileFactoryOutput<TFactory>, NotFoundError>>;
11
+ /**
12
+ * Calls the WCS segment API to retrieve the customer segments for the given
13
+ * personalization ID. Returns an empty array when the API call fails or
14
+ * returns no segment data.
15
+ */
16
+ protected fetchSegments(personalizationId: string): Promise<string[]>;
17
+ protected getSegmentsUrl(): string;
18
+ protected getSegmentsParams(personalizationId: string): URLSearchParams;
19
+ }
@@ -0,0 +1,22 @@
1
+ import { ProductAssociationsCapability, type Cache, type ProductAssociationsFactory, type ProductAssociationsFactoryOutput, type ProductAssociationsFactoryWithOutput, type ProductAssociationsGetAccessoriesQuery, type ProductAssociationsGetReplacementsQuery, type ProductAssociationsGetSparepartsQuery, type RequestContext, type Result } from '@reactionary/core';
2
+ import type { HclConfiguration } from '../schema/configuration.schema.js';
3
+ import type { HclClient } from '../core/client.js';
4
+ import type { HclAssociation } from '../schema/hcl.schema.js';
5
+ import type { HclProductAssociationsFactory } from '../factories/product-associations/product-associations.factory.js';
6
+ export declare class HclProductAssociationsCapability<TFactory extends ProductAssociationsFactory = HclProductAssociationsFactory> extends ProductAssociationsCapability<ProductAssociationsFactoryOutput<TFactory>> {
7
+ protected readonly config: HclConfiguration;
8
+ protected readonly client: HclClient;
9
+ protected readonly factory: ProductAssociationsFactoryWithOutput<TFactory>;
10
+ constructor(cache: Cache, context: RequestContext, config: HclConfiguration, client: HclClient, factory: ProductAssociationsFactoryWithOutput<TFactory>);
11
+ getAccessories(query: ProductAssociationsGetAccessoriesQuery): Promise<Result<ProductAssociationsFactoryOutput<TFactory>[]>>;
12
+ getSpareparts(query: ProductAssociationsGetSparepartsQuery): Promise<Result<ProductAssociationsFactoryOutput<TFactory>[]>>;
13
+ getReplacements(query: ProductAssociationsGetReplacementsQuery): Promise<Result<ProductAssociationsFactoryOutput<TFactory>[]>>;
14
+ /**
15
+ * Fetches the product detail for the given part number and returns the
16
+ * `merchandisingAssociations` filtered to the requested association types,
17
+ * capped at `maxCount`.
18
+ */
19
+ protected fetchAssociations(partNumber: string, types: string[], maxCount: number): Promise<HclAssociation[]>;
20
+ protected getProductsUrl(): string;
21
+ protected getProductsParams(partNumber: string): URLSearchParams;
22
+ }
@@ -0,0 +1,29 @@
1
+ import { ProductRecommendationsCapability, type Cache, type ProductRecommendation, type ProductRecommendationAlgorithmAlsoViewedProductsQuery, type ProductRecommendationAlgorithmFrequentlyBoughtTogetherQuery, type ProductRecommendationAlgorithmPopuplarProductsQuery, type ProductRecommendationAlgorithmRelatedProductsQuery, type ProductRecommendationAlgorithmSimilarProductsQuery, type ProductRecommendationAlgorithmTopPicksProductsQuery, type ProductRecommendationAlgorithmTrendingInCategoryQuery, type ProductRecommendationsByCollectionQuery, type ProductRecommendationsFactory, type ProductRecommendationsFactoryWithOutput, type RequestContext, type Result } from '@reactionary/core';
2
+ import type { HclConfiguration } from '../schema/configuration.schema.js';
3
+ import type { HclClient } from '../core/client.js';
4
+ import type { HclProductRecommendationsFactory } from '../factories/product-recommendations/product-recommendations.factory.js';
5
+ export declare class HclProductRecommendationsCapability<TFactory extends ProductRecommendationsFactory = HclProductRecommendationsFactory> extends ProductRecommendationsCapability {
6
+ protected readonly config: HclConfiguration;
7
+ protected readonly client: HclClient;
8
+ protected readonly factory: ProductRecommendationsFactoryWithOutput<TFactory>;
9
+ constructor(cache: Cache, context: RequestContext, config: HclConfiguration, client: HclClient, factory: ProductRecommendationsFactoryWithOutput<TFactory>);
10
+ getRecommendations(...args: Parameters<ProductRecommendationsCapability['getRecommendations']>): Promise<Result<ProductRecommendation[]>>;
11
+ getCollection(query: ProductRecommendationsByCollectionQuery): Promise<Result<ProductRecommendation[]>>;
12
+ protected getFrequentlyBoughtTogetherRecommendations(query: ProductRecommendationAlgorithmFrequentlyBoughtTogetherQuery): Promise<ProductRecommendation[]>;
13
+ protected getSimilarProductsRecommendations(query: ProductRecommendationAlgorithmSimilarProductsQuery): Promise<ProductRecommendation[]>;
14
+ protected getRelatedProductsRecommendations(query: ProductRecommendationAlgorithmRelatedProductsQuery): Promise<ProductRecommendation[]>;
15
+ protected getTrendingInCategoryRecommendations(query: ProductRecommendationAlgorithmTrendingInCategoryQuery): Promise<ProductRecommendation[]>;
16
+ protected getPopularProductsRecommendations(query: ProductRecommendationAlgorithmPopuplarProductsQuery): Promise<ProductRecommendation[]>;
17
+ protected getTopPicksProductsRecommendations(query: ProductRecommendationAlgorithmTopPicksProductsQuery): Promise<ProductRecommendation[]>;
18
+ protected getAlsoViewedProductsRecommendations(query: ProductRecommendationAlgorithmAlsoViewedProductsQuery): Promise<ProductRecommendation[]>;
19
+ /**
20
+ * Calls an HCL marketing spot (espot) by name and maps its catalog entry
21
+ * activity data to `ProductRecommendation[]` using the factory.
22
+ *
23
+ * Returns an empty array when the espot is not found (404) or returns no
24
+ * catalog entry data — allowing teams to configure only the espots they need.
25
+ */
26
+ protected fetchEspot(name: string, algorithm: string, params?: Record<string, string | undefined>): Promise<ProductRecommendation[]>;
27
+ protected getEspotUrl(name: string): string;
28
+ protected getEspotParams(params: Record<string, string | undefined>): URLSearchParams;
29
+ }
@@ -9,3 +9,6 @@ export * from './identity/identity.factory.js';
9
9
  export * from './profile/profile.factory.js';
10
10
  export * from './order/order.factory.js';
11
11
  export * from './order-search/order-search.factory.js';
12
+ export * from './product-associations/product-associations.factory.js';
13
+ export * from './product-recommendations/product-recommendations.factory.js';
14
+ export * from './personalization-profile/personalization-profile.factory.js';
@@ -0,0 +1,11 @@
1
+ import type { AnyPersonalizationProfileSchema, PersonalizationProfileFactory, PersonalizationProfileSchema, RequestContext } from '@reactionary/core';
2
+ import type * as z from 'zod';
3
+ export interface HclPersonalizationProfileData {
4
+ personalizationId: string;
5
+ segments: string[];
6
+ }
7
+ export declare class HclPersonalizationProfileFactory<TPersonalizationProfileSchema extends AnyPersonalizationProfileSchema = typeof PersonalizationProfileSchema> implements PersonalizationProfileFactory<TPersonalizationProfileSchema> {
8
+ readonly personalizationProfileSchema: TPersonalizationProfileSchema;
9
+ constructor(personalizationProfileSchema: TPersonalizationProfileSchema);
10
+ parsePersonalizationProfile(_context: RequestContext, data: HclPersonalizationProfileData): z.output<TPersonalizationProfileSchema>;
11
+ }
@@ -0,0 +1,8 @@
1
+ import type { AnyProductAssociationSchema, ProductAssociationSchema, ProductAssociationsFactory, RequestContext } from '@reactionary/core';
2
+ import type * as z from 'zod';
3
+ import type { HclAssociation } from '../../schema/hcl.schema.js';
4
+ export declare class HclProductAssociationsFactory<TProductAssociationSchema extends AnyProductAssociationSchema = typeof ProductAssociationSchema> implements ProductAssociationsFactory<TProductAssociationSchema> {
5
+ readonly productAssociationSchema: TProductAssociationSchema;
6
+ constructor(productAssociationSchema: TProductAssociationSchema);
7
+ parseAssociation(_context: RequestContext, data: HclAssociation): z.output<TProductAssociationSchema>;
8
+ }
@@ -0,0 +1,12 @@
1
+ import type { AnyProductRecommendationSchema, ProductRecommendationSchema, ProductRecommendationsFactory, RequestContext } from '@reactionary/core';
2
+ import type * as z from 'zod';
3
+ import type { HclEspotActivityData } from '../../schema/hcl.schema.js';
4
+ export interface HclEspotRecommendationData {
5
+ activityData: HclEspotActivityData;
6
+ algorithm: string;
7
+ }
8
+ export declare class HclProductRecommendationsFactory<TProductRecommendationSchema extends AnyProductRecommendationSchema = typeof ProductRecommendationSchema> implements ProductRecommendationsFactory<TProductRecommendationSchema> {
9
+ readonly productRecommendationSchema: TProductRecommendationSchema;
10
+ constructor(productRecommendationSchema: TProductRecommendationSchema);
11
+ parseRecommendation(_context: RequestContext, data: HclEspotRecommendationData): z.output<TProductRecommendationSchema>;
12
+ }
package/src/index.d.ts CHANGED
@@ -17,3 +17,7 @@ export * from './capabilities/identity.capability.js';
17
17
  export * from './capabilities/profile.capability.js';
18
18
  export * from './capabilities/order.capability.js';
19
19
  export * from './capabilities/order-search.capability.js';
20
+ export * from './capabilities/analytics.capability.js';
21
+ export * from './capabilities/product-associations.capability.js';
22
+ export * from './capabilities/product-recommendations.capability.js';
23
+ export * from './capabilities/personalization-profile.capability.js';
@@ -1,4 +1,4 @@
1
- import type { Cache, RequestContext, CartFactory, CartFactoryWithOutput, CartCapability, CategoryFactory, CategoryFactoryWithOutput, CategoryCapability, CheckoutFactory, CheckoutFactoryWithOutput, CheckoutCapability, IdentityFactory, IdentityFactoryWithOutput, IdentityCapability, InventoryFactory, InventoryFactoryWithOutput, InventoryCapability, OrderFactory, OrderFactoryWithOutput, OrderCapability, OrderSearchFactory, OrderSearchFactoryWithOutput, OrderSearchCapability, PriceFactory, PriceFactoryWithOutput, PriceCapability, ProductFactory, ProductFactoryWithOutput, ProductCapability, ProductSearchFactory, ProductSearchFactoryWithOutput, ProductSearchCapability, ProfileFactory, ProfileFactoryWithOutput, ProfileCapability } from '@reactionary/core';
1
+ import type { Cache, RequestContext, AnalyticsCapability, CartFactory, CartFactoryWithOutput, CartCapability, CategoryFactory, CategoryFactoryWithOutput, CategoryCapability, CheckoutFactory, CheckoutFactoryWithOutput, CheckoutCapability, IdentityFactory, IdentityFactoryWithOutput, IdentityCapability, InventoryFactory, InventoryFactoryWithOutput, InventoryCapability, OrderFactory, OrderFactoryWithOutput, OrderCapability, OrderSearchFactory, OrderSearchFactoryWithOutput, OrderSearchCapability, PersonalizationProfileCapability, PersonalizationProfileFactory, PersonalizationProfileFactoryWithOutput, PriceFactory, PriceFactoryWithOutput, PriceCapability, ProductAssociationsCapability, ProductAssociationsFactory, ProductAssociationsFactoryWithOutput, ProductFactory, ProductFactoryWithOutput, ProductCapability, ProductRecommendationsCapability, ProductRecommendationsFactory, ProductRecommendationsFactoryWithOutput, ProductSearchFactory, ProductSearchFactoryWithOutput, ProductSearchCapability, ProfileFactory, ProfileFactoryWithOutput, ProfileCapability } from '@reactionary/core';
2
2
  import type { HclConfiguration } from './configuration.schema.js';
3
3
  import type { HclClient } from '../core/client.js';
4
4
  import * as z from 'zod';
@@ -58,6 +58,26 @@ export declare const HclCapabilitiesSchema: z.ZodObject<{
58
58
  factory: z.ZodOptional<z.ZodUnknown>;
59
59
  capability: z.ZodOptional<z.ZodUnknown>;
60
60
  }, z.core.$loose>>>;
61
+ analytics: z.ZodOptional<z.ZodOptional<z.ZodObject<{
62
+ enabled: z.ZodBoolean;
63
+ factory: z.ZodOptional<z.ZodUnknown>;
64
+ capability: z.ZodOptional<z.ZodUnknown>;
65
+ }, z.core.$loose>>>;
66
+ productAssociations: z.ZodOptional<z.ZodOptional<z.ZodObject<{
67
+ enabled: z.ZodBoolean;
68
+ factory: z.ZodOptional<z.ZodUnknown>;
69
+ capability: z.ZodOptional<z.ZodUnknown>;
70
+ }, z.core.$loose>>>;
71
+ productRecommendations: z.ZodOptional<z.ZodOptional<z.ZodObject<{
72
+ enabled: z.ZodBoolean;
73
+ factory: z.ZodOptional<z.ZodUnknown>;
74
+ capability: z.ZodOptional<z.ZodUnknown>;
75
+ }, z.core.$loose>>>;
76
+ personalizationProfile: z.ZodOptional<z.ZodOptional<z.ZodObject<{
77
+ enabled: z.ZodBoolean;
78
+ factory: z.ZodOptional<z.ZodUnknown>;
79
+ capability: z.ZodOptional<z.ZodUnknown>;
80
+ }, z.core.$loose>>>;
61
81
  }, z.core.$loose>;
62
82
  export type HclCapabilities = z.infer<typeof HclCapabilitiesSchema>;
63
83
  export interface HclCapabilityFactoryArgs {
@@ -85,3 +105,9 @@ export type HclIdentityCapabilityConfig = HclCapabilityConfig<IdentityFactoryWit
85
105
  export type HclProfileCapabilityConfig = HclCapabilityConfig<ProfileFactoryWithOutput<ProfileFactory>, ProfileCapability>;
86
106
  export type HclOrderCapabilityConfig = HclCapabilityConfig<OrderFactoryWithOutput<OrderFactory>, OrderCapability>;
87
107
  export type HclOrderSearchCapabilityConfig = HclCapabilityConfig<OrderSearchFactoryWithOutput<OrderSearchFactory>, OrderSearchCapability>;
108
+ /** Analytics does not use a factory — the capability writes events directly. */
109
+ export type HclAnalyticsCapabilityConfig = HclCapabilityConfig<never, AnalyticsCapability>;
110
+ export type HclProductAssociationsCapabilityConfig = HclCapabilityConfig<ProductAssociationsFactoryWithOutput<ProductAssociationsFactory>, ProductAssociationsCapability>;
111
+ /** Product recommendations uses a factory to parse espot activity data into recommendations. */
112
+ export type HclProductRecommendationsCapabilityConfig = HclCapabilityConfig<ProductRecommendationsFactoryWithOutput<ProductRecommendationsFactory>, ProductRecommendationsCapability>;
113
+ export type HclPersonalizationProfileCapabilityConfig = HclCapabilityConfig<PersonalizationProfileFactoryWithOutput<PersonalizationProfileFactory>, PersonalizationProfileCapability>;
@@ -21,5 +21,29 @@ export declare const HclConfigurationSchema: z.ZodObject<{
21
21
  * In Karkkainen this is '10003'.
22
22
  */
23
23
  priceRuleId: z.ZodOptional<z.ZodString>;
24
+ /**
25
+ * Maps Reactionary association types to the HCL `associationCodeType` values
26
+ * returned in `merchandisingAssociations` on product detail responses.
27
+ * Override to match custom HCL store configuration.
28
+ */
29
+ associationTypes: z.ZodDefault<z.ZodObject<{
30
+ accessories: z.ZodDefault<z.ZodArray<z.ZodString>>;
31
+ spareparts: z.ZodDefault<z.ZodArray<z.ZodString>>;
32
+ replacements: z.ZodDefault<z.ZodArray<z.ZodString>>;
33
+ }, z.core.$loose>>;
34
+ /**
35
+ * Names of HCL marketing spots used for product recommendation algorithms.
36
+ * Each algorithm calls the corresponding named espot via the WCS Transaction Service.
37
+ * These espots must be configured in HCL Commerce to return catalog entry data.
38
+ */
39
+ espotNames: z.ZodDefault<z.ZodObject<{
40
+ frequentlyBoughtTogether: z.ZodDefault<z.ZodString>;
41
+ similar: z.ZodDefault<z.ZodString>;
42
+ related: z.ZodDefault<z.ZodString>;
43
+ trendingInCategory: z.ZodDefault<z.ZodString>;
44
+ popular: z.ZodDefault<z.ZodString>;
45
+ topPicks: z.ZodDefault<z.ZodString>;
46
+ alsoViewed: z.ZodDefault<z.ZodString>;
47
+ }, z.core.$loose>>;
24
48
  }, z.core.$loose>;
25
49
  export type HclConfiguration = z.infer<typeof HclConfigurationSchema>;
@@ -234,7 +234,7 @@ export interface HclProductResponse extends HclProductSearchItem {
234
234
  components?: HclProductResponse[];
235
235
  /** Quantity — only present on components array elements */
236
236
  quantity?: string;
237
- merchandisingAssociations?: HclProductResponse[];
237
+ merchandisingAssociations?: HclAssociation[];
238
238
  attachments?: HclAttachment[];
239
239
  images?: HclImage[];
240
240
  /**
@@ -694,3 +694,61 @@ export interface HclWcsOrderHistoryResponse {
694
694
  recordSetStartNumber?: string;
695
695
  recordSetComplete?: string;
696
696
  }
697
+ /**
698
+ * A product item returned within a `merchandisingAssociations` array on a
699
+ * HCL detail-profile product response. Extends the search item shape with
700
+ * an `associationCodeType` that identifies the semantic relationship.
701
+ *
702
+ * Known values: 'ACCESSORY', 'SPAREPART', 'REPLACEMENT', 'CROSSSELL', 'UPSELL', 'UPGRADE'
703
+ */
704
+ export interface HclAssociation extends HclProductSearchItem {
705
+ /** The association type code. e.g. 'ACCESSORY', 'SPAREPART', 'REPLACEMENT'. */
706
+ associationCodeType?: string;
707
+ /** Display sequence for ordering associations within a type. */
708
+ associationSequence?: string;
709
+ }
710
+ /**
711
+ * A single activity data entry within a marketing spot response.
712
+ * When `baseMarketingSpotDataType` is 'CatalogEntry' or 'CatalogEntryId',
713
+ * the entry represents a product recommendation from the spot.
714
+ */
715
+ export interface HclEspotActivityData {
716
+ /** 'CatalogEntry' | 'CatalogEntryId' | 'CatalogGroupId' | 'MarketingContent' */
717
+ baseMarketingSpotDataType?: string;
718
+ /** Internal HCL product uniqueID (numeric string). Present for CatalogEntry/CatalogEntryId types. */
719
+ contentId?: string;
720
+ /** Part number of the product, if returned inline by the spot configuration. */
721
+ partNumber?: string;
722
+ /** Display name of the product or content entry. */
723
+ contentName?: string;
724
+ }
725
+ /** A single marketing spot entry in a WCS espot response. */
726
+ export interface HclEspotMarketingSpot {
727
+ eSpotName?: string;
728
+ baseMarketingSpotActivityData?: HclEspotActivityData[];
729
+ }
730
+ /** Top-level response from GET /wcs/resources/store/{storeId}/espot/{name}. */
731
+ export interface HclEspotResponse {
732
+ MarketingSpotData?: HclEspotMarketingSpot[];
733
+ }
734
+ /** A single customer segment (member group) entry in a WCS segment response. */
735
+ export interface HclSegmentMemberGroup {
736
+ id: string;
737
+ displayName?: {
738
+ language: number;
739
+ value: string;
740
+ };
741
+ description?: {
742
+ language: number;
743
+ value: string;
744
+ };
745
+ usage?: string[];
746
+ }
747
+ /** Top-level response from GET /wcs/resources/store/{storeId}/segment. */
748
+ export interface HclSegmentResponse {
749
+ MemberGroup?: HclSegmentMemberGroup[];
750
+ recordSetTotal?: number;
751
+ recordSetCount?: number;
752
+ recordSetStartNumber?: number;
753
+ recordSetComplete?: boolean;
754
+ }