@reactionary/hcl 0.9.2
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/README.md +11 -0
- package/capabilities/category.capability.js +170 -0
- package/capabilities/product-search.capability.js +89 -0
- package/capabilities/product.capability.js +157 -0
- package/core/client.js +120 -0
- package/core/initialize.js +97 -0
- package/core/initialize.types.js +8 -0
- package/core/locale-params.js +9 -0
- package/factories/category/category.factory.js +60 -0
- package/factories/index.js +3 -0
- package/factories/product/product.factory.js +154 -0
- package/factories/product-search/product-search.factory.js +71 -0
- package/index.js +11 -0
- package/package.json +14 -0
- package/schema/capabilities.schema.js +27 -0
- package/schema/category.schema.js +9 -0
- package/schema/configuration.schema.js +33 -0
- package/schema/hcl.schema.js +0 -0
- package/src/capabilities/category.capability.d.ts +15 -0
- package/src/capabilities/product-search.capability.d.ts +14 -0
- package/src/capabilities/product.capability.d.ts +20 -0
- package/src/core/client.d.ts +19 -0
- package/src/core/initialize.d.ts +5 -0
- package/src/core/initialize.types.d.ts +42 -0
- package/src/core/locale-params.d.ts +6 -0
- package/src/factories/category/category.factory.d.ts +12 -0
- package/src/factories/index.d.ts +3 -0
- package/src/factories/product/product.factory.d.ts +8 -0
- package/src/factories/product-search/product-search.factory.d.ts +11 -0
- package/src/index.d.ts +11 -0
- package/src/schema/capabilities.schema.d.ts +63 -0
- package/src/schema/category.schema.d.ts +27 -0
- package/src/schema/configuration.schema.d.ts +18 -0
- package/src/schema/hcl.schema.d.ts +312 -0
- package/src/test/test-utils.d.ts +241 -0
- package/test/test-utils.js +30 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {} from "@reactionary/core";
|
|
2
|
+
class HclCategoryFactory {
|
|
3
|
+
categorySchema;
|
|
4
|
+
categoryPaginatedResultSchema;
|
|
5
|
+
constructor(categorySchema, categoryPaginatedResultSchema) {
|
|
6
|
+
this.categorySchema = categorySchema;
|
|
7
|
+
this.categoryPaginatedResultSchema = categoryPaginatedResultSchema;
|
|
8
|
+
}
|
|
9
|
+
parseCategory(_context, data) {
|
|
10
|
+
const identifier = { key: data.identifier };
|
|
11
|
+
const slug = data.seo?.href?.split("/").filter(Boolean).pop() ?? data.identifier ?? "";
|
|
12
|
+
const pathSegments = (typeof data.parentCatalogGroupID === "string" ? data.parentCatalogGroupID : data.parentCatalogGroupID?.[0] ?? "").split("/").filter(Boolean);
|
|
13
|
+
const parentUniqueId = pathSegments.length > 1 ? pathSegments[pathSegments.length - 2] : void 0;
|
|
14
|
+
const images = [
|
|
15
|
+
data.fullImage && {
|
|
16
|
+
sourceUrl: data.fullImage,
|
|
17
|
+
altText: data.name,
|
|
18
|
+
width: 0,
|
|
19
|
+
height: 0
|
|
20
|
+
},
|
|
21
|
+
data.thumbnail && data.thumbnail !== data.fullImage && {
|
|
22
|
+
sourceUrl: data.thumbnail,
|
|
23
|
+
altText: data.name,
|
|
24
|
+
width: 0,
|
|
25
|
+
height: 0
|
|
26
|
+
}
|
|
27
|
+
].filter(Boolean);
|
|
28
|
+
const result = {
|
|
29
|
+
identifier,
|
|
30
|
+
name: data.name ?? "",
|
|
31
|
+
slug,
|
|
32
|
+
text: data.shortDescription ?? data.description ?? "",
|
|
33
|
+
images
|
|
34
|
+
};
|
|
35
|
+
return this.categorySchema.parse({
|
|
36
|
+
...result,
|
|
37
|
+
uniqueId: data.uniqueID,
|
|
38
|
+
parentUniqueId
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
parseCategoryPaginatedResult(context, data, query) {
|
|
42
|
+
const { pageNumber, pageSize } = query.paginationOptions;
|
|
43
|
+
const totalCount = data.length;
|
|
44
|
+
const totalPages = Math.ceil(totalCount / pageSize);
|
|
45
|
+
const offset = (pageNumber - 1) * pageSize;
|
|
46
|
+
const pageItems = data.slice(offset, offset + pageSize);
|
|
47
|
+
const items = pageItems.map((c) => this.parseCategory(context, c));
|
|
48
|
+
const result = {
|
|
49
|
+
pageNumber,
|
|
50
|
+
pageSize,
|
|
51
|
+
totalCount,
|
|
52
|
+
totalPages,
|
|
53
|
+
items
|
|
54
|
+
};
|
|
55
|
+
return this.categoryPaginatedResultSchema.parse(result);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export {
|
|
59
|
+
HclCategoryFactory
|
|
60
|
+
};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CategoryIdentifierSchema,
|
|
3
|
+
ImageSchema,
|
|
4
|
+
ProductAttributeIdentifierSchema,
|
|
5
|
+
ProductAttributeSchema,
|
|
6
|
+
ProductAttributeValueIdentifierSchema,
|
|
7
|
+
ProductAttributeValueSchema,
|
|
8
|
+
ProductOptionSchema,
|
|
9
|
+
ProductOptionValueSchema,
|
|
10
|
+
ProductVariantOptionSchema
|
|
11
|
+
} from "@reactionary/core";
|
|
12
|
+
const USAGE_DEFINING = "Defining";
|
|
13
|
+
const USAGE_DESCRIPTIVE = "Descriptive";
|
|
14
|
+
function flattenAttributeValues(attr) {
|
|
15
|
+
return attr.values.flatMap((v) => {
|
|
16
|
+
if (!Array.isArray(v.id)) {
|
|
17
|
+
const value = Array.isArray(v.value) ? v.value[0] ?? "" : v.value ?? "";
|
|
18
|
+
const key = Array.isArray(v.identifier) ? v.identifier[0] ?? "" : v.identifier ?? "";
|
|
19
|
+
return [{ key: String(key), value: String(value), label: String(value) }];
|
|
20
|
+
} else {
|
|
21
|
+
return v.id.map((_id, index) => {
|
|
22
|
+
const key = Array.isArray(v.identifier) ? String(v.identifier[index] ?? "") : String(v.identifier ?? "");
|
|
23
|
+
const value = Array.isArray(v.value) ? String(v.value[index] ?? "") : String(v.value ?? "");
|
|
24
|
+
return { key, value, label: value };
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function parseSharedAttributes(data) {
|
|
30
|
+
return (data.attributes ?? []).filter(
|
|
31
|
+
(attr) => attr.usage === USAGE_DESCRIPTIVE && attr.displayable === "true" && attr.storeDisplay !== "true"
|
|
32
|
+
).map((attr) => {
|
|
33
|
+
const flatValues = flattenAttributeValues(attr);
|
|
34
|
+
return ProductAttributeSchema.parse({
|
|
35
|
+
identifier: ProductAttributeIdentifierSchema.parse({
|
|
36
|
+
key: attr.identifier
|
|
37
|
+
}),
|
|
38
|
+
group: "",
|
|
39
|
+
name: attr.name,
|
|
40
|
+
values: flatValues.map(
|
|
41
|
+
(fv) => ProductAttributeValueSchema.parse({
|
|
42
|
+
identifier: ProductAttributeValueIdentifierSchema.parse({
|
|
43
|
+
key: fv.key
|
|
44
|
+
}),
|
|
45
|
+
value: fv.value,
|
|
46
|
+
label: fv.label
|
|
47
|
+
})
|
|
48
|
+
)
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function parseOptions(skus) {
|
|
53
|
+
const optionMap = /* @__PURE__ */ new Map();
|
|
54
|
+
for (const sku of skus) {
|
|
55
|
+
for (const attr of (sku.attributes ?? []).filter(
|
|
56
|
+
(a) => a.usage === USAGE_DEFINING
|
|
57
|
+
)) {
|
|
58
|
+
if (!optionMap.has(attr.identifier)) {
|
|
59
|
+
optionMap.set(attr.identifier, { name: attr.name, values: /* @__PURE__ */ new Map() });
|
|
60
|
+
}
|
|
61
|
+
const option = optionMap.get(attr.identifier);
|
|
62
|
+
if (!option)
|
|
63
|
+
continue;
|
|
64
|
+
for (const fv of flattenAttributeValues(attr)) {
|
|
65
|
+
option.values.set(fv.key, { key: fv.key, label: fv.label });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return Array.from(optionMap.entries()).map(([attrId, { name, values }]) => {
|
|
70
|
+
const optionIdentifier = { key: attrId };
|
|
71
|
+
return ProductOptionSchema.parse({
|
|
72
|
+
identifier: optionIdentifier,
|
|
73
|
+
name,
|
|
74
|
+
values: Array.from(values.values()).map(
|
|
75
|
+
(v) => ProductOptionValueSchema.parse({
|
|
76
|
+
identifier: { key: v.key, option: optionIdentifier },
|
|
77
|
+
label: v.label
|
|
78
|
+
})
|
|
79
|
+
)
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
function parseVariant(data, productName) {
|
|
84
|
+
const images = [];
|
|
85
|
+
if (data.fullImage) {
|
|
86
|
+
images.push(
|
|
87
|
+
ImageSchema.parse({ sourceUrl: data.fullImage, altText: productName })
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
if (data.thumbnail && data.thumbnail !== data.fullImage) {
|
|
91
|
+
images.push(
|
|
92
|
+
ImageSchema.parse({ sourceUrl: data.thumbnail, altText: productName })
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
const options = (data.attributes ?? []).filter((attr) => attr.usage === USAGE_DEFINING).flatMap((attr) => {
|
|
96
|
+
const optionIdentifier = { key: attr.identifier };
|
|
97
|
+
return flattenAttributeValues(attr).map(
|
|
98
|
+
(fv) => ProductVariantOptionSchema.parse({
|
|
99
|
+
identifier: optionIdentifier,
|
|
100
|
+
name: attr.name,
|
|
101
|
+
value: {
|
|
102
|
+
identifier: { key: fv.key, option: optionIdentifier },
|
|
103
|
+
label: fv.label
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
return {
|
|
109
|
+
identifier: { sku: data.partNumber },
|
|
110
|
+
name: data.name || productName,
|
|
111
|
+
images,
|
|
112
|
+
ean: "",
|
|
113
|
+
gtin: "",
|
|
114
|
+
upc: "",
|
|
115
|
+
barcode: "",
|
|
116
|
+
options
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
class HclProductFactory {
|
|
120
|
+
productSchema;
|
|
121
|
+
constructor(productSchema) {
|
|
122
|
+
this.productSchema = productSchema;
|
|
123
|
+
}
|
|
124
|
+
parseProduct(_context, data) {
|
|
125
|
+
const name = data.name;
|
|
126
|
+
const slug = data.seo?.href?.split("/").filter(Boolean).pop() ?? data.seo?.tokenValue ?? data.partNumber;
|
|
127
|
+
const rawIds = Array.isArray(data.parentCatalogGroupID) ? data.parentCatalogGroupID : data.parentCatalogGroupID ? [data.parentCatalogGroupID] : [];
|
|
128
|
+
const parentCategories = rawIds.map((p) => p.split("/").filter(Boolean).at(-1) ?? p).filter(Boolean).map((id) => CategoryIdentifierSchema.parse({ key: id }));
|
|
129
|
+
const sharedAttributes = parseSharedAttributes(data);
|
|
130
|
+
const skus = !data.hasSingleSKU && data.items?.length > 0 ? data.items : [data];
|
|
131
|
+
const options = parseOptions(skus);
|
|
132
|
+
const mainVariant = parseVariant(skus[0], name);
|
|
133
|
+
const variants = skus.slice(1).map((sku) => parseVariant(sku, name));
|
|
134
|
+
const result = {
|
|
135
|
+
identifier: { key: data.partNumber },
|
|
136
|
+
name,
|
|
137
|
+
slug,
|
|
138
|
+
description: data.shortDescription ?? "",
|
|
139
|
+
longDescription: data.longDescription ?? "",
|
|
140
|
+
brand: data.manufacturer ?? "",
|
|
141
|
+
manufacturer: data.manufacturer ?? "",
|
|
142
|
+
published: data.buyable === "true",
|
|
143
|
+
parentCategories,
|
|
144
|
+
sharedAttributes,
|
|
145
|
+
options,
|
|
146
|
+
mainVariant,
|
|
147
|
+
variants
|
|
148
|
+
};
|
|
149
|
+
return this.productSchema.parse(result);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
export {
|
|
153
|
+
HclProductFactory
|
|
154
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
class HclProductSearchFactory {
|
|
2
|
+
productSearchResultSchema;
|
|
3
|
+
constructor(productSearchResultSchema) {
|
|
4
|
+
this.productSearchResultSchema = productSearchResultSchema;
|
|
5
|
+
}
|
|
6
|
+
parseSearchResult(_context, data, query) {
|
|
7
|
+
const rawItems = data.contents ?? data.catalogEntryView ?? [];
|
|
8
|
+
const { pageNumber, pageSize } = query.search.paginationOptions;
|
|
9
|
+
const totalCount = data.total ?? data.recordSetTotal ?? rawItems.length;
|
|
10
|
+
const items = rawItems.map(
|
|
11
|
+
(p) => this.parseSearchItem(p)
|
|
12
|
+
);
|
|
13
|
+
const facets = (data.facets ?? []).map(
|
|
14
|
+
(f) => this.parseFacet(f, query)
|
|
15
|
+
);
|
|
16
|
+
const result = {
|
|
17
|
+
identifier: query.search,
|
|
18
|
+
pageNumber,
|
|
19
|
+
pageSize,
|
|
20
|
+
totalCount,
|
|
21
|
+
totalPages: Math.ceil(totalCount / pageSize),
|
|
22
|
+
items,
|
|
23
|
+
facets
|
|
24
|
+
};
|
|
25
|
+
return this.productSearchResultSchema.parse(result);
|
|
26
|
+
}
|
|
27
|
+
parseSearchItem(p) {
|
|
28
|
+
const slug = p.seo?.href?.split("/").filter(Boolean).pop() ?? p.seo?.tokenValue ?? p.partNumber;
|
|
29
|
+
const variants = [
|
|
30
|
+
{
|
|
31
|
+
variant: { sku: p.partNumber },
|
|
32
|
+
image: {
|
|
33
|
+
sourceUrl: p.fullImage || p.thumbnail || "",
|
|
34
|
+
altText: p.name,
|
|
35
|
+
width: 0,
|
|
36
|
+
height: 0
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
];
|
|
40
|
+
return {
|
|
41
|
+
identifier: { key: p.partNumber },
|
|
42
|
+
name: p.name ?? "",
|
|
43
|
+
slug,
|
|
44
|
+
variants
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
parseFacet(f, query) {
|
|
48
|
+
const facetId = { key: f.value };
|
|
49
|
+
const values = (f.entry ?? []).map(
|
|
50
|
+
(entry) => {
|
|
51
|
+
const isActive = query.search.facets.some(
|
|
52
|
+
(sel) => sel.facet.key === f.value && sel.key === entry.value
|
|
53
|
+
);
|
|
54
|
+
return {
|
|
55
|
+
identifier: { facet: facetId, key: entry.value },
|
|
56
|
+
name: entry.label || entry.name || entry.value,
|
|
57
|
+
count: Number(entry.count) || 0,
|
|
58
|
+
active: isActive
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
return {
|
|
63
|
+
identifier: facetId,
|
|
64
|
+
name: f.name || f.value,
|
|
65
|
+
values
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export {
|
|
70
|
+
HclProductSearchFactory
|
|
71
|
+
};
|
package/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from "./schema/configuration.schema.js";
|
|
2
|
+
export * from "./schema/capabilities.schema.js";
|
|
3
|
+
export * from "./schema/hcl.schema.js";
|
|
4
|
+
export * from "./schema/category.schema.js";
|
|
5
|
+
export * from "./core/client.js";
|
|
6
|
+
export * from "./core/initialize.js";
|
|
7
|
+
export * from "./core/initialize.types.js";
|
|
8
|
+
export * from "./factories/index.js";
|
|
9
|
+
export * from "./capabilities/product.capability.js";
|
|
10
|
+
export * from "./capabilities/category.capability.js";
|
|
11
|
+
export * from "./capabilities/product-search.capability.js";
|
package/package.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@reactionary/hcl",
|
|
3
|
+
"version": "0.9.2",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./index.js",
|
|
6
|
+
"types": "./src/index.d.ts",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@reactionary/core": "0.9.2",
|
|
9
|
+
"zod": "4.1.9",
|
|
10
|
+
"vitest": "^4.0.9",
|
|
11
|
+
"@nx/vite": "22.4.5"
|
|
12
|
+
},
|
|
13
|
+
"sideEffects": false
|
|
14
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { CapabilitiesSchema } from "@reactionary/core";
|
|
2
|
+
import * as z from "zod";
|
|
3
|
+
const OverridableCapabilitySchema = z.looseObject({
|
|
4
|
+
enabled: z.boolean(),
|
|
5
|
+
factory: z.unknown().optional(),
|
|
6
|
+
capability: z.unknown().optional()
|
|
7
|
+
});
|
|
8
|
+
const HclCapabilitiesSchema = CapabilitiesSchema.pick({
|
|
9
|
+
cart: true,
|
|
10
|
+
checkout: true,
|
|
11
|
+
category: true,
|
|
12
|
+
product: true,
|
|
13
|
+
price: true,
|
|
14
|
+
inventory: true,
|
|
15
|
+
productSearch: true
|
|
16
|
+
}).extend({
|
|
17
|
+
cart: OverridableCapabilitySchema.optional(),
|
|
18
|
+
checkout: OverridableCapabilitySchema.optional(),
|
|
19
|
+
category: OverridableCapabilitySchema.optional(),
|
|
20
|
+
product: OverridableCapabilitySchema.optional(),
|
|
21
|
+
price: OverridableCapabilitySchema.optional(),
|
|
22
|
+
inventory: OverridableCapabilitySchema.optional(),
|
|
23
|
+
productSearch: OverridableCapabilitySchema.optional()
|
|
24
|
+
}).partial();
|
|
25
|
+
export {
|
|
26
|
+
HclCapabilitiesSchema
|
|
27
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
const HclProfilesSchema = z.looseObject({
|
|
3
|
+
product: z.string().default("HCL_V2_findProductByPartNumber_Details").meta({
|
|
4
|
+
description: "Profile name for product detail lookups (getById, getBySlug, getBySKU)."
|
|
5
|
+
}),
|
|
6
|
+
productSearch: z.string().default("HCL_V2_findProductsBySearchTermWithPrice").meta({ description: "Profile name for product search by term." }),
|
|
7
|
+
categoryBrowse: z.string().default("HCL_V2_findProductsByCategoryWithPriceRange").meta({
|
|
8
|
+
description: "Profile name for product search filtered by category."
|
|
9
|
+
})
|
|
10
|
+
}).default(() => ({
|
|
11
|
+
product: "HCL_V2_findProductByPartNumber_Details",
|
|
12
|
+
productSearch: "HCL_V2_findProductsBySearchTermWithPrice",
|
|
13
|
+
categoryBrowse: "HCL_V2_findProductsByCategoryWithPriceRange"
|
|
14
|
+
}));
|
|
15
|
+
const HclConfigurationSchema = z.looseObject({
|
|
16
|
+
apiUrl: z.string().meta({
|
|
17
|
+
description: "The base origin URL for the HCL Commerce server (e.g. https://example.com)."
|
|
18
|
+
}),
|
|
19
|
+
storeId: z.string().meta({ description: "The HCL Commerce store identifier." }),
|
|
20
|
+
catalogId: z.string().optional().meta({ description: "The HCL Commerce catalog identifier." }),
|
|
21
|
+
/**
|
|
22
|
+
* Maps BCP 47 locale strings (from RequestContext) to HCL Commerce langId values.
|
|
23
|
+
* HCL uses numeric language identifiers (e.g. -1 for English, -11 for Finnish).
|
|
24
|
+
* Add an entry for each locale your store supports.
|
|
25
|
+
*/
|
|
26
|
+
localeMap: z.record(z.string(), z.string()).default({ "en-US": "-1" }).meta({
|
|
27
|
+
description: "Mapping from BCP 47 locale (RequestContext.languageContext.locale) to HCL langId."
|
|
28
|
+
}),
|
|
29
|
+
profiles: HclProfilesSchema
|
|
30
|
+
});
|
|
31
|
+
export {
|
|
32
|
+
HclConfigurationSchema
|
|
33
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { CategoryCapability, type Cache, type CategoryFactory, type CategoryFactoryCategoryOutput, type CategoryFactoryPaginatedOutput, type CategoryFactoryWithOutput, type CategoryQueryById, type CategoryQueryBySlug, type CategoryQueryForBreadcrumb, type CategoryQueryForChildCategories, type CategoryQueryForTopCategories, type NotFoundError, 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 { HclCategoryFactory } from '../factories/category/category.factory.js';
|
|
5
|
+
export declare class HclCategoryCapability<TFactory extends CategoryFactory = HclCategoryFactory> extends CategoryCapability<CategoryFactoryCategoryOutput<TFactory>, CategoryFactoryPaginatedOutput<TFactory>> {
|
|
6
|
+
protected readonly config: HclConfiguration;
|
|
7
|
+
protected readonly client: HclClient;
|
|
8
|
+
protected readonly factory: CategoryFactoryWithOutput<TFactory>;
|
|
9
|
+
constructor(cache: Cache, context: RequestContext, config: HclConfiguration, client: HclClient, factory: CategoryFactoryWithOutput<TFactory>);
|
|
10
|
+
getById(payload: CategoryQueryById): Promise<Result<CategoryFactoryCategoryOutput<TFactory>, NotFoundError>>;
|
|
11
|
+
getBySlug(payload: CategoryQueryBySlug): Promise<Result<CategoryFactoryCategoryOutput<TFactory>, NotFoundError>>;
|
|
12
|
+
getBreadcrumbPathToCategory(payload: CategoryQueryForBreadcrumb): Promise<Result<Array<CategoryFactoryCategoryOutput<TFactory>>>>;
|
|
13
|
+
findChildCategories(payload: CategoryQueryForChildCategories): Promise<Result<CategoryFactoryPaginatedOutput<TFactory>>>;
|
|
14
|
+
findTopCategories(payload: CategoryQueryForTopCategories): Promise<Result<CategoryFactoryPaginatedOutput<TFactory>>>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ProductSearchCapability, type Cache, type FacetValueIdentifier, type ProductSearchFactory, type ProductSearchFactoryOutput, type ProductSearchFactoryWithOutput, type ProductSearchQueryByTerm, type ProductSearchQueryCreateNavigationFilter, 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 { HclProductSearchFactory } from '../factories/product-search/product-search.factory.js';
|
|
5
|
+
import type { HclFindProductsQuery } from '../schema/hcl.schema.js';
|
|
6
|
+
export declare class HclProductSearchCapability<TFactory extends ProductSearchFactory = HclProductSearchFactory> extends ProductSearchCapability<ProductSearchFactoryOutput<TFactory>> {
|
|
7
|
+
protected readonly config: HclConfiguration;
|
|
8
|
+
protected readonly client: HclClient;
|
|
9
|
+
protected readonly factory: ProductSearchFactoryWithOutput<TFactory>;
|
|
10
|
+
constructor(cache: Cache, context: RequestContext, config: HclConfiguration, client: HclClient, factory: ProductSearchFactoryWithOutput<TFactory>);
|
|
11
|
+
protected queryByTermPayload(payload: ProductSearchQueryByTerm): HclFindProductsQuery;
|
|
12
|
+
queryByTerm(payload: ProductSearchQueryByTerm): Promise<Result<ProductSearchFactoryOutput<TFactory>>>;
|
|
13
|
+
createCategoryNavigationFilter(payload: ProductSearchQueryCreateNavigationFilter): Promise<Result<FacetValueIdentifier>>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ProductCapability, type Cache, type NotFoundError, type ProductFactory, type ProductFactoryOutput, type ProductFactoryWithOutput, type ProductQueryById, type ProductQueryBySKU, type ProductQueryBySlug, 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 { HclProductFactory } from '../factories/product/product.factory.js';
|
|
5
|
+
import type { HclProductResponse } from '../schema/hcl.schema.js';
|
|
6
|
+
export declare class HclProductCapability<TFactory extends ProductFactory = HclProductFactory> extends ProductCapability<ProductFactoryOutput<TFactory>> {
|
|
7
|
+
protected readonly config: HclConfiguration;
|
|
8
|
+
protected readonly client: HclClient;
|
|
9
|
+
protected readonly factory: ProductFactoryWithOutput<TFactory>;
|
|
10
|
+
constructor(cache: Cache, context: RequestContext, config: HclConfiguration, client: HclClient, factory: ProductFactoryWithOutput<TFactory>);
|
|
11
|
+
getById(payload: ProductQueryById): Promise<Result<ProductFactoryOutput<TFactory>, NotFoundError>>;
|
|
12
|
+
getBySlug(payload: ProductQueryBySlug): Promise<Result<ProductFactoryOutput<TFactory>, NotFoundError>>;
|
|
13
|
+
getBySKU(payload: ProductQueryBySKU): Promise<Result<ProductFactoryOutput<TFactory>, NotFoundError>>;
|
|
14
|
+
/**
|
|
15
|
+
* Resolves `parentCatalogGroupID` path strings (e.g. "/10505/10507") to external
|
|
16
|
+
* category identifiers (e.g. "LivingRoomFurniture") so the factory receives
|
|
17
|
+
* human-readable keys rather than internal uniqueIDs.
|
|
18
|
+
*/
|
|
19
|
+
protected withResolvedParentCategories(data: HclProductResponse, langId: string): Promise<HclProductResponse>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { HclConfiguration } from '../schema/configuration.schema.js';
|
|
2
|
+
import type { HclCategoryQueryResponse, HclFindCategoriesQuery, HclFindProductsQuery, HclProductQueryResponse, HclUrlResponse } from '../schema/hcl.schema.js';
|
|
3
|
+
export declare class HclClient {
|
|
4
|
+
private readonly config;
|
|
5
|
+
private readonly baseUrl;
|
|
6
|
+
constructor(config: HclConfiguration);
|
|
7
|
+
findProducts(query: HclFindProductsQuery): Promise<HclProductQueryResponse>;
|
|
8
|
+
/**
|
|
9
|
+
* Resolve a URL slug to an HCL token (product partNumber, category ID, etc.).
|
|
10
|
+
* Calls GET /api/v2/urls?storeId=X&identifier=<slug>
|
|
11
|
+
* Returns undefined when the slug is not found (404).
|
|
12
|
+
*/
|
|
13
|
+
resolveSlug(slug: string, langId?: string): Promise<HclUrlResponse | undefined>;
|
|
14
|
+
/**
|
|
15
|
+
* Query categories from the HCL Commerce Query Service.
|
|
16
|
+
* Calls GET /api/v2/categories with the given query parameters.
|
|
17
|
+
*/
|
|
18
|
+
findCategories(query: HclFindCategoriesQuery): Promise<HclCategoryQueryResponse>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Cache, RequestContext } from '@reactionary/core';
|
|
2
|
+
import { type HclCapabilities } from '../schema/capabilities.schema.js';
|
|
3
|
+
import { type HclConfiguration } from '../schema/configuration.schema.js';
|
|
4
|
+
import { type HclClientFromCapabilities } from './initialize.types.js';
|
|
5
|
+
export declare function withHclCapabilities<T extends HclCapabilities>(configuration: HclConfiguration, capabilities: T): (cache: Cache, context: RequestContext) => HclClientFromCapabilities<T>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ClientFromCapabilities } from '@reactionary/core';
|
|
2
|
+
import type { HclCapabilities } from '../schema/capabilities.schema.js';
|
|
3
|
+
import type { HclProductCapability } from '../capabilities/product.capability.js';
|
|
4
|
+
import type { HclCategoryCapability } from '../capabilities/category.capability.js';
|
|
5
|
+
import type { HclProductSearchCapability } from '../capabilities/product-search.capability.js';
|
|
6
|
+
type OverridableCapabilityKey = 'product' | 'productSearch' | 'cart' | 'checkout' | 'category' | 'price' | 'inventory';
|
|
7
|
+
type EnabledCapability<TCapability> = TCapability extends {
|
|
8
|
+
enabled: true;
|
|
9
|
+
} ? true : false;
|
|
10
|
+
type NormalizeConfiguredCapabilities<T extends HclCapabilities> = {
|
|
11
|
+
[K in OverridableCapabilityKey]?: EnabledCapability<T[K]>;
|
|
12
|
+
};
|
|
13
|
+
type ExtractCapabilityImplementation<TCapability, TDefaultCapability> = TCapability extends {
|
|
14
|
+
enabled: true;
|
|
15
|
+
capability?: infer TCapabilityFactory;
|
|
16
|
+
} ? TCapabilityFactory extends (...args: unknown[]) => infer TResolvedCapability ? TResolvedCapability : TDefaultCapability : TDefaultCapability;
|
|
17
|
+
type DefaultCapabilityMap = {
|
|
18
|
+
product: HclProductCapability;
|
|
19
|
+
productSearch: HclProductSearchCapability;
|
|
20
|
+
cart: never;
|
|
21
|
+
checkout: never;
|
|
22
|
+
category: HclCategoryCapability;
|
|
23
|
+
price: never;
|
|
24
|
+
inventory: never;
|
|
25
|
+
};
|
|
26
|
+
type CapabilityImplementationMap<T extends HclCapabilities> = {
|
|
27
|
+
[K in OverridableCapabilityKey]: ExtractCapabilityImplementation<T[K], DefaultCapabilityMap[K]>;
|
|
28
|
+
};
|
|
29
|
+
type EnabledCapabilityOverrideMap<T extends HclCapabilities> = {
|
|
30
|
+
[K in OverridableCapabilityKey as T[K] extends {
|
|
31
|
+
enabled: true;
|
|
32
|
+
} ? K : never]: CapabilityImplementationMap<T>[K];
|
|
33
|
+
};
|
|
34
|
+
export type HclClientFromCapabilities<T extends HclCapabilities> = Omit<ClientFromCapabilities<NormalizeConfiguredCapabilities<T>>, OverridableCapabilityKey> & EnabledCapabilityOverrideMap<T>;
|
|
35
|
+
export declare function resolveCapabilityWithFactory<TFactory, TResolvedCapability, TCapabilityArgs>(capability: {
|
|
36
|
+
factory?: TFactory;
|
|
37
|
+
capability?: (args: TCapabilityArgs) => TResolvedCapability;
|
|
38
|
+
} | undefined, defaults: {
|
|
39
|
+
factory: TFactory;
|
|
40
|
+
capability: (args: TCapabilityArgs) => TResolvedCapability;
|
|
41
|
+
}, buildCapabilityArgs: (factory: TFactory) => TCapabilityArgs): TResolvedCapability;
|
|
42
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { RequestContext } from '@reactionary/core';
|
|
2
|
+
import type { HclConfiguration } from '../schema/configuration.schema.js';
|
|
3
|
+
export declare function getLocaleParams(config: HclConfiguration, context: RequestContext): {
|
|
4
|
+
langId: string;
|
|
5
|
+
currency: string;
|
|
6
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AnyCategoryPaginatedResultSchema, AnyCategorySchema, CategoryFactory, RequestContext, CategoryQueryForTopCategories, CategoryQueryForChildCategories } from '@reactionary/core';
|
|
2
|
+
import type * as z from 'zod';
|
|
3
|
+
import type { HclCategoryResponse } from '../../schema/hcl.schema.js';
|
|
4
|
+
import { type CategoryPaginatedResultSchema } from '@reactionary/core';
|
|
5
|
+
import type { HclCategorySchema } from '../../schema/category.schema.js';
|
|
6
|
+
export declare class HclCategoryFactory<TCategorySchema extends AnyCategorySchema = typeof HclCategorySchema, TCategoryPaginatedSchema extends AnyCategoryPaginatedResultSchema = typeof CategoryPaginatedResultSchema> implements CategoryFactory<TCategorySchema, TCategoryPaginatedSchema> {
|
|
7
|
+
readonly categorySchema: TCategorySchema;
|
|
8
|
+
readonly categoryPaginatedResultSchema: TCategoryPaginatedSchema;
|
|
9
|
+
constructor(categorySchema: TCategorySchema, categoryPaginatedResultSchema: TCategoryPaginatedSchema);
|
|
10
|
+
parseCategory(_context: RequestContext, data: HclCategoryResponse): z.output<TCategorySchema>;
|
|
11
|
+
parseCategoryPaginatedResult(context: RequestContext, data: HclCategoryResponse[], query: CategoryQueryForTopCategories | CategoryQueryForChildCategories): z.output<TCategoryPaginatedSchema>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type ProductSchema, type AnyProductSchema, type ProductFactory, type RequestContext } from '@reactionary/core';
|
|
2
|
+
import type * as z from 'zod';
|
|
3
|
+
import type { HclProductResponse } from '../../schema/hcl.schema.js';
|
|
4
|
+
export declare class HclProductFactory<TProductSchema extends AnyProductSchema = typeof ProductSchema> implements ProductFactory<TProductSchema> {
|
|
5
|
+
readonly productSchema: TProductSchema;
|
|
6
|
+
constructor(productSchema: TProductSchema);
|
|
7
|
+
parseProduct(_context: RequestContext, data: HclProductResponse): z.output<TProductSchema>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AnyProductSearchResultSchema, ProductSearchFactory, ProductSearchResultFacet, ProductSearchResultItem, RequestContext, ProductSearchQueryByTerm } from '@reactionary/core';
|
|
2
|
+
import type * as z from 'zod';
|
|
3
|
+
import type { ProductSearchResultSchema } from '@reactionary/core';
|
|
4
|
+
import type { HclFacet, HclProductQueryResponse, HclProductSearchItem } from '../../schema/hcl.schema.js';
|
|
5
|
+
export declare class HclProductSearchFactory<TProductSearchResultSchema extends AnyProductSearchResultSchema = typeof ProductSearchResultSchema> implements ProductSearchFactory<TProductSearchResultSchema> {
|
|
6
|
+
readonly productSearchResultSchema: TProductSearchResultSchema;
|
|
7
|
+
constructor(productSearchResultSchema: TProductSearchResultSchema);
|
|
8
|
+
parseSearchResult(_context: RequestContext, data: HclProductQueryResponse, query: ProductSearchQueryByTerm): z.output<TProductSearchResultSchema>;
|
|
9
|
+
protected parseSearchItem(p: HclProductSearchItem): ProductSearchResultItem;
|
|
10
|
+
protected parseFacet(f: HclFacet, query: ProductSearchQueryByTerm): ProductSearchResultFacet;
|
|
11
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './schema/configuration.schema.js';
|
|
2
|
+
export * from './schema/capabilities.schema.js';
|
|
3
|
+
export * from './schema/hcl.schema.js';
|
|
4
|
+
export * from './schema/category.schema.js';
|
|
5
|
+
export * from './core/client.js';
|
|
6
|
+
export * from './core/initialize.js';
|
|
7
|
+
export * from './core/initialize.types.js';
|
|
8
|
+
export * from './factories/index.js';
|
|
9
|
+
export * from './capabilities/product.capability.js';
|
|
10
|
+
export * from './capabilities/category.capability.js';
|
|
11
|
+
export * from './capabilities/product-search.capability.js';
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Cache, RequestContext, CartFactory, CartFactoryWithOutput, CartCapability, CategoryFactory, CategoryFactoryWithOutput, CategoryCapability, CheckoutFactory, CheckoutFactoryWithOutput, CheckoutCapability, InventoryFactory, InventoryFactoryWithOutput, InventoryCapability, PriceFactory, PriceFactoryWithOutput, PriceCapability, ProductFactory, ProductFactoryWithOutput, ProductCapability, ProductSearchFactory, ProductSearchFactoryWithOutput, ProductSearchCapability } from '@reactionary/core';
|
|
2
|
+
import type { HclConfiguration } from './configuration.schema.js';
|
|
3
|
+
import type { HclClient } from '../core/client.js';
|
|
4
|
+
import * as z from 'zod';
|
|
5
|
+
export declare const HclCapabilitiesSchema: z.ZodObject<{
|
|
6
|
+
cart: z.ZodOptional<z.ZodOptional<z.ZodObject<{
|
|
7
|
+
enabled: z.ZodBoolean;
|
|
8
|
+
factory: z.ZodOptional<z.ZodUnknown>;
|
|
9
|
+
capability: z.ZodOptional<z.ZodUnknown>;
|
|
10
|
+
}, z.core.$loose>>>;
|
|
11
|
+
checkout: z.ZodOptional<z.ZodOptional<z.ZodObject<{
|
|
12
|
+
enabled: z.ZodBoolean;
|
|
13
|
+
factory: z.ZodOptional<z.ZodUnknown>;
|
|
14
|
+
capability: z.ZodOptional<z.ZodUnknown>;
|
|
15
|
+
}, z.core.$loose>>>;
|
|
16
|
+
category: z.ZodOptional<z.ZodOptional<z.ZodObject<{
|
|
17
|
+
enabled: z.ZodBoolean;
|
|
18
|
+
factory: z.ZodOptional<z.ZodUnknown>;
|
|
19
|
+
capability: z.ZodOptional<z.ZodUnknown>;
|
|
20
|
+
}, z.core.$loose>>>;
|
|
21
|
+
product: z.ZodOptional<z.ZodOptional<z.ZodObject<{
|
|
22
|
+
enabled: z.ZodBoolean;
|
|
23
|
+
factory: z.ZodOptional<z.ZodUnknown>;
|
|
24
|
+
capability: z.ZodOptional<z.ZodUnknown>;
|
|
25
|
+
}, z.core.$loose>>>;
|
|
26
|
+
price: z.ZodOptional<z.ZodOptional<z.ZodObject<{
|
|
27
|
+
enabled: z.ZodBoolean;
|
|
28
|
+
factory: z.ZodOptional<z.ZodUnknown>;
|
|
29
|
+
capability: z.ZodOptional<z.ZodUnknown>;
|
|
30
|
+
}, z.core.$loose>>>;
|
|
31
|
+
inventory: z.ZodOptional<z.ZodOptional<z.ZodObject<{
|
|
32
|
+
enabled: z.ZodBoolean;
|
|
33
|
+
factory: z.ZodOptional<z.ZodUnknown>;
|
|
34
|
+
capability: z.ZodOptional<z.ZodUnknown>;
|
|
35
|
+
}, z.core.$loose>>>;
|
|
36
|
+
productSearch: z.ZodOptional<z.ZodOptional<z.ZodObject<{
|
|
37
|
+
enabled: z.ZodBoolean;
|
|
38
|
+
factory: z.ZodOptional<z.ZodUnknown>;
|
|
39
|
+
capability: z.ZodOptional<z.ZodUnknown>;
|
|
40
|
+
}, z.core.$loose>>>;
|
|
41
|
+
}, z.core.$loose>;
|
|
42
|
+
export type HclCapabilities = z.infer<typeof HclCapabilitiesSchema>;
|
|
43
|
+
export interface HclCapabilityFactoryArgs {
|
|
44
|
+
cache: Cache;
|
|
45
|
+
context: RequestContext;
|
|
46
|
+
config: HclConfiguration;
|
|
47
|
+
hclClient: HclClient;
|
|
48
|
+
}
|
|
49
|
+
export interface HclFactoryCapabilityArgs<TFactory> extends HclCapabilityFactoryArgs {
|
|
50
|
+
factory: TFactory;
|
|
51
|
+
}
|
|
52
|
+
export interface HclCapabilityConfig<TFactory, TCapability> {
|
|
53
|
+
enabled: boolean;
|
|
54
|
+
factory?: TFactory;
|
|
55
|
+
capability?: (args: HclFactoryCapabilityArgs<TFactory>) => TCapability;
|
|
56
|
+
}
|
|
57
|
+
export type HclCartCapabilityConfig = HclCapabilityConfig<CartFactoryWithOutput<CartFactory>, CartCapability>;
|
|
58
|
+
export type HclCategoryCapabilityConfig = HclCapabilityConfig<CategoryFactoryWithOutput<CategoryFactory>, CategoryCapability>;
|
|
59
|
+
export type HclCheckoutCapabilityConfig = HclCapabilityConfig<CheckoutFactoryWithOutput<CheckoutFactory>, CheckoutCapability>;
|
|
60
|
+
export type HclInventoryCapabilityConfig = HclCapabilityConfig<InventoryFactoryWithOutput<InventoryFactory>, InventoryCapability>;
|
|
61
|
+
export type HclPriceCapabilityConfig = HclCapabilityConfig<PriceFactoryWithOutput<PriceFactory>, PriceCapability>;
|
|
62
|
+
export type HclProductCapabilityConfig = HclCapabilityConfig<ProductFactoryWithOutput<ProductFactory>, ProductCapability>;
|
|
63
|
+
export type HclProductSearchCapabilityConfig = HclCapabilityConfig<ProductSearchFactoryWithOutput<ProductSearchFactory>, ProductSearchCapability>;
|