@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.
Files changed (36) hide show
  1. package/README.md +11 -0
  2. package/capabilities/category.capability.js +170 -0
  3. package/capabilities/product-search.capability.js +89 -0
  4. package/capabilities/product.capability.js +157 -0
  5. package/core/client.js +120 -0
  6. package/core/initialize.js +97 -0
  7. package/core/initialize.types.js +8 -0
  8. package/core/locale-params.js +9 -0
  9. package/factories/category/category.factory.js +60 -0
  10. package/factories/index.js +3 -0
  11. package/factories/product/product.factory.js +154 -0
  12. package/factories/product-search/product-search.factory.js +71 -0
  13. package/index.js +11 -0
  14. package/package.json +14 -0
  15. package/schema/capabilities.schema.js +27 -0
  16. package/schema/category.schema.js +9 -0
  17. package/schema/configuration.schema.js +33 -0
  18. package/schema/hcl.schema.js +0 -0
  19. package/src/capabilities/category.capability.d.ts +15 -0
  20. package/src/capabilities/product-search.capability.d.ts +14 -0
  21. package/src/capabilities/product.capability.d.ts +20 -0
  22. package/src/core/client.d.ts +19 -0
  23. package/src/core/initialize.d.ts +5 -0
  24. package/src/core/initialize.types.d.ts +42 -0
  25. package/src/core/locale-params.d.ts +6 -0
  26. package/src/factories/category/category.factory.d.ts +12 -0
  27. package/src/factories/index.d.ts +3 -0
  28. package/src/factories/product/product.factory.d.ts +8 -0
  29. package/src/factories/product-search/product-search.factory.d.ts +11 -0
  30. package/src/index.d.ts +11 -0
  31. package/src/schema/capabilities.schema.d.ts +63 -0
  32. package/src/schema/category.schema.d.ts +27 -0
  33. package/src/schema/configuration.schema.d.ts +18 -0
  34. package/src/schema/hcl.schema.d.ts +312 -0
  35. package/src/test/test-utils.d.ts +241 -0
  36. 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,3 @@
1
+ export * from "./product/product.factory.js";
2
+ export * from "./category/category.factory.js";
3
+ export * from "./product-search/product-search.factory.js";
@@ -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,9 @@
1
+ import { CategorySchema } from "@reactionary/core";
2
+ import * as z from "zod";
3
+ const HclCategorySchema = CategorySchema.extend({
4
+ uniqueId: z.string().optional(),
5
+ parentUniqueId: z.string().optional()
6
+ });
7
+ export {
8
+ HclCategorySchema
9
+ };
@@ -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,3 @@
1
+ export * from './product/product.factory.js';
2
+ export * from './category/category.factory.js';
3
+ export * from './product-search/product-search.factory.js';
@@ -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>;