@reactionary/hcl 0.9.8 → 0.9.12

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 (55) hide show
  1. package/capabilities/cart.capability.js +411 -0
  2. package/capabilities/category.capability.js +111 -35
  3. package/capabilities/checkout.capability.js +337 -0
  4. package/capabilities/identity.capability.js +214 -0
  5. package/capabilities/inventory.capability.js +75 -0
  6. package/capabilities/order-search.capability.js +79 -0
  7. package/capabilities/order.capability.js +62 -0
  8. package/capabilities/price.capability.js +122 -0
  9. package/capabilities/product-search.capability.js +64 -13
  10. package/capabilities/product.capability.js +114 -28
  11. package/capabilities/profile.capability.js +215 -0
  12. package/core/client.js +81 -90
  13. package/core/initialize.js +170 -7
  14. package/core/session-keys.js +14 -0
  15. package/factories/cart/cart.factory.js +135 -0
  16. package/factories/checkout/checkout.factory.js +131 -0
  17. package/factories/identity/identity.factory.js +12 -0
  18. package/factories/index.js +8 -0
  19. package/factories/inventory/inventory.factory.js +38 -0
  20. package/factories/order/order.factory.js +124 -0
  21. package/factories/order-search/order-search.factory.js +59 -0
  22. package/factories/price/price.factory.js +39 -0
  23. package/factories/profile/profile.factory.js +49 -0
  24. package/index.js +8 -0
  25. package/package.json +8 -5
  26. package/schema/capabilities.schema.js +10 -2
  27. package/schema/configuration.schema.js +9 -1
  28. package/src/capabilities/cart.capability.d.ts +69 -0
  29. package/src/capabilities/category.capability.d.ts +20 -1
  30. package/src/capabilities/checkout.capability.d.ts +41 -0
  31. package/src/capabilities/identity.capability.d.ts +29 -0
  32. package/src/capabilities/inventory.capability.d.ts +13 -0
  33. package/src/capabilities/order-search.capability.d.ts +13 -0
  34. package/src/capabilities/order.capability.d.ts +13 -0
  35. package/src/capabilities/price.capability.d.ts +16 -0
  36. package/src/capabilities/product-search.capability.d.ts +7 -2
  37. package/src/capabilities/product.capability.d.ts +14 -2
  38. package/src/capabilities/profile.capability.d.ts +31 -0
  39. package/src/core/client.d.ts +41 -14
  40. package/src/core/initialize.types.d.ts +17 -5
  41. package/src/core/session-keys.d.ts +14 -0
  42. package/src/factories/cart/cart.factory.d.ts +11 -0
  43. package/src/factories/checkout/checkout.factory.d.ts +11 -0
  44. package/src/factories/identity/identity.factory.d.ts +7 -0
  45. package/src/factories/index.d.ts +8 -0
  46. package/src/factories/inventory/inventory.factory.d.ts +29 -0
  47. package/src/factories/order/order.factory.d.ts +11 -0
  48. package/src/factories/order-search/order-search.factory.d.ts +9 -0
  49. package/src/factories/price/price.factory.d.ts +26 -0
  50. package/src/factories/profile/profile.factory.d.ts +9 -0
  51. package/src/index.d.ts +8 -0
  52. package/src/schema/capabilities.schema.d.ts +25 -1
  53. package/src/schema/configuration.schema.d.ts +6 -0
  54. package/src/schema/hcl.schema.d.ts +384 -0
  55. package/src/test/test-utils.d.ts +5 -1
@@ -0,0 +1,135 @@
1
+ import {
2
+ CartIdentifierSchema,
3
+ CartPaginatedSearchResultSchema
4
+ } from "@reactionary/core";
5
+ class HclCartFactory {
6
+ constructor(cartSchema, cartIdentifierSchema = CartIdentifierSchema, cartPaginatedSearchResultSchema = CartPaginatedSearchResultSchema) {
7
+ this.cartSchema = cartSchema;
8
+ this.cartIdentifierSchema = cartIdentifierSchema;
9
+ this.cartPaginatedSearchResultSchema = cartPaginatedSearchResultSchema;
10
+ }
11
+ parseCart(context, data) {
12
+ const cart = data;
13
+ const currency = cart.totalProductPriceCurrency ?? cart.grandTotalCurrency ?? "USD";
14
+ return this.cartSchema.parse({
15
+ identifier: { key: cart.orderId },
16
+ user: { userId: cart.buyerId ?? "" },
17
+ company: cart.orgUniqueID ? { id: cart.orgUniqueID } : void 0,
18
+ name: cart.orderDescription ?? context.session["hcl.cartName"] ?? "",
19
+ description: "",
20
+ items: (cart.orderItem ?? []).map((item) => ({
21
+ identifier: { key: item.orderItemId },
22
+ product: { key: item.productId ?? "" },
23
+ variant: { sku: item.partNumber },
24
+ quantity: Number(item.quantity),
25
+ price: {
26
+ unitPrice: {
27
+ value: Number(item.unitPrice),
28
+ currency: item.currency
29
+ },
30
+ totalPrice: {
31
+ value: Number(item.orderItemPrice),
32
+ currency: item.currency
33
+ },
34
+ unitDiscount: { value: 0, currency: item.currency },
35
+ totalDiscount: { value: 0, currency: item.currency }
36
+ }
37
+ })),
38
+ price: {
39
+ grandTotal: {
40
+ value: Number(cart.grandTotal ?? "0"),
41
+ currency
42
+ },
43
+ totalProductPrice: {
44
+ value: Number(cart.totalProductPrice ?? "0"),
45
+ currency
46
+ },
47
+ totalShipping: {
48
+ value: Number(cart.totalShippingCharge ?? "0"),
49
+ currency
50
+ },
51
+ totalTax: {
52
+ value: Number(cart.totalSalesTax ?? "0"),
53
+ currency
54
+ },
55
+ totalDiscount: {
56
+ value: Math.abs(Number(cart.totalAdjustment ?? "0")),
57
+ currency
58
+ },
59
+ totalSurcharge: { value: 0, currency }
60
+ },
61
+ appliedPromotions: (cart.adjustment ?? []).filter((adj) => adj.usage === "Discount").map((adj) => ({
62
+ code: adj.code,
63
+ isCouponCode: (cart.promotionCode ?? []).some(
64
+ (pc) => pc.code === adj.code
65
+ ),
66
+ name: adj.description ?? adj.code,
67
+ description: adj.description ?? "",
68
+ amount: {
69
+ value: Math.abs(Number(adj.amount)),
70
+ currency: adj.currency
71
+ }
72
+ }))
73
+ });
74
+ }
75
+ parseCartIdentifier(_context, data) {
76
+ const cart = data;
77
+ return this.cartIdentifierSchema.parse({
78
+ key: cart.orderId
79
+ });
80
+ }
81
+ parseCartPaginatedSearchResult(context, data, _query) {
82
+ const pageSize = _query.search.paginationOptions.pageSize;
83
+ const pageNumber = _query.search.paginationOptions.pageNumber;
84
+ const orderList = data;
85
+ if (Array.isArray(orderList.Order)) {
86
+ const orders = orderList.Order;
87
+ const total = Number(orderList.recordSetTotal ?? orders.length);
88
+ const items = orders.map((order) => {
89
+ const parsed = this.parseCart(context, order);
90
+ return {
91
+ ...parsed,
92
+ numItems: (order.orderItem ?? []).length,
93
+ lastModifiedDate: order.lastUpdateDate ?? ""
94
+ };
95
+ });
96
+ return this.cartPaginatedSearchResultSchema.parse({
97
+ identifier: _query.search,
98
+ items,
99
+ totalCount: total,
100
+ totalPages: Math.ceil(total / Math.max(1, pageSize)),
101
+ pageSize,
102
+ pageNumber
103
+ });
104
+ }
105
+ const cart = data;
106
+ if (!cart.orderId) {
107
+ return this.cartPaginatedSearchResultSchema.parse({
108
+ identifier: _query.search,
109
+ items: [],
110
+ totalCount: 0,
111
+ totalPages: 0,
112
+ pageSize,
113
+ pageNumber
114
+ });
115
+ }
116
+ const parsedCart = this.parseCart(context, cart);
117
+ return this.cartPaginatedSearchResultSchema.parse({
118
+ identifier: _query.search,
119
+ items: [
120
+ {
121
+ ...parsedCart,
122
+ numItems: (cart.orderItem ?? []).length,
123
+ lastModifiedDate: cart.lastUpdateDate ?? ""
124
+ }
125
+ ],
126
+ totalCount: 1,
127
+ totalPages: 1,
128
+ pageSize,
129
+ pageNumber
130
+ });
131
+ }
132
+ }
133
+ export {
134
+ HclCartFactory
135
+ };
@@ -0,0 +1,131 @@
1
+ import {
2
+ ShippingMethodSchema,
3
+ PaymentMethodSchema
4
+ } from "@reactionary/core";
5
+ class HclCheckoutFactory {
6
+ constructor(checkoutSchema, shippingMethodSchema = ShippingMethodSchema, paymentMethodSchema = PaymentMethodSchema) {
7
+ this.checkoutSchema = checkoutSchema;
8
+ this.shippingMethodSchema = shippingMethodSchema;
9
+ this.paymentMethodSchema = paymentMethodSchema;
10
+ }
11
+ parseCheckout(context, data) {
12
+ const cart = data;
13
+ const currency = cart.totalProductPriceCurrency ?? cart.grandTotalCurrency ?? "USD";
14
+ const shipModeId = cart.shipModeId ?? (cart.orderItem ?? [])[0]?.shipModeId;
15
+ const shippingInstruction = shipModeId ? {
16
+ shippingMethod: { key: shipModeId },
17
+ pickupPoint: "",
18
+ instructions: "",
19
+ consentForUnattendedDelivery: false
20
+ } : void 0;
21
+ const billingEmail = cart.x_billingAddress?.email1 ?? (cart.paymentInstruction ?? []).flatMap((pi) => pi.protocolData ?? []).find((pd) => pd.name === "email1")?.value ?? context.session["hcl.notificationEmail"] ?? "noreply@checkout.example.com";
22
+ const paymentInstructions = (cart.paymentInstruction ?? []).map((pi) => ({
23
+ identifier: { key: pi.piId },
24
+ paymentMethod: {
25
+ method: pi.payMethodId,
26
+ name: pi.payMethodId,
27
+ paymentProcessor: pi.payMethodId
28
+ },
29
+ amount: {
30
+ value: Number(pi.piAmount ?? "0"),
31
+ currency: pi.currency ?? currency
32
+ },
33
+ protocolData: (pi.protocolData ?? []).map((pd) => ({
34
+ key: pd.name,
35
+ value: pd.value
36
+ })),
37
+ status: "pending"
38
+ }));
39
+ const parseAddress = (addr) => {
40
+ if (!addr)
41
+ return void 0;
42
+ return {
43
+ identifier: { key: addr.addressId ?? "" },
44
+ firstName: addr.firstName ?? "",
45
+ lastName: addr.lastName ?? "",
46
+ streetAddress: (addr.addressLine ?? [])[0] ?? "",
47
+ streetNumber: "",
48
+ city: addr.city ?? "",
49
+ region: addr.state ?? "",
50
+ postalCode: addr.zipCode ?? "",
51
+ countryCode: addr.country ?? ""
52
+ };
53
+ };
54
+ return this.checkoutSchema.parse({
55
+ identifier: { key: cart.orderId },
56
+ originalCartReference: { key: cart.orderId },
57
+ resultingOrder: cart.resultingOrderKey ? { key: cart.resultingOrderKey } : void 0,
58
+ items: (cart.orderItem ?? []).map((item) => ({
59
+ identifier: { key: item.orderItemId },
60
+ variant: { sku: item.partNumber },
61
+ quantity: Number(item.quantity),
62
+ price: {
63
+ unitPrice: {
64
+ value: Number(item.unitPrice),
65
+ currency: item.currency
66
+ },
67
+ totalPrice: {
68
+ value: Number(item.orderItemPrice),
69
+ currency: item.currency
70
+ },
71
+ unitDiscount: { value: 0, currency: item.currency },
72
+ totalDiscount: { value: 0, currency: item.currency }
73
+ }
74
+ })),
75
+ price: {
76
+ grandTotal: { value: Number(cart.grandTotal ?? "0"), currency },
77
+ totalProductPrice: {
78
+ value: Number(cart.totalProductPrice ?? "0"),
79
+ currency
80
+ },
81
+ totalShipping: {
82
+ value: Number(cart.totalShippingCharge ?? "0"),
83
+ currency
84
+ },
85
+ totalTax: { value: Number(cart.totalSalesTax ?? "0"), currency },
86
+ totalDiscount: {
87
+ value: Math.abs(Number(cart.totalAdjustment ?? "0")),
88
+ currency
89
+ },
90
+ totalSurcharge: { value: 0, currency }
91
+ },
92
+ name: context.session["hcl.cartName"] ?? "",
93
+ description: "",
94
+ shippingAddress: parseAddress(cart.x_shippingAddress),
95
+ billingAddress: parseAddress(cart.x_billingAddress),
96
+ shippingInstruction,
97
+ paymentInstructions,
98
+ pointOfContact: {
99
+ email: billingEmail,
100
+ phone: cart.x_billingAddress?.phone1
101
+ },
102
+ readyForFinalization: paymentInstructions.length > 0 && !!cart.shipModeId
103
+ });
104
+ }
105
+ parseShippingMethod(_context, data) {
106
+ const mode = data;
107
+ return this.shippingMethodSchema.parse({
108
+ identifier: { key: mode.shipModeId },
109
+ name: mode.shipModeDescription || mode.shipModeCode,
110
+ description: mode.shipModeDescription || mode.shipModeCode,
111
+ price: { value: 0, currency: "USD" },
112
+ deliveryTime: mode.field2 ?? "",
113
+ carrier: mode.carrier
114
+ });
115
+ }
116
+ parsePaymentMethod(_context, data) {
117
+ const method = data;
118
+ return this.paymentMethodSchema.parse({
119
+ identifier: {
120
+ method: method.paymentMethodName,
121
+ name: method.description ?? method.paymentMethodName,
122
+ paymentProcessor: method.paymentMethodName
123
+ },
124
+ description: method.description ?? method.paymentMethodName,
125
+ isPunchOut: false
126
+ });
127
+ }
128
+ }
129
+ export {
130
+ HclCheckoutFactory
131
+ };
@@ -0,0 +1,12 @@
1
+ class HclIdentityFactory {
2
+ identitySchema;
3
+ constructor(identitySchema) {
4
+ this.identitySchema = identitySchema;
5
+ }
6
+ parseIdentity(_context, data) {
7
+ return this.identitySchema.parse(data);
8
+ }
9
+ }
10
+ export {
11
+ HclIdentityFactory
12
+ };
@@ -1,3 +1,11 @@
1
+ export * from "./cart/cart.factory.js";
2
+ export * from "./checkout/checkout.factory.js";
1
3
  export * from "./product/product.factory.js";
2
4
  export * from "./category/category.factory.js";
3
5
  export * from "./product-search/product-search.factory.js";
6
+ export * from "./price/price.factory.js";
7
+ export * from "./inventory/inventory.factory.js";
8
+ export * from "./identity/identity.factory.js";
9
+ export * from "./profile/profile.factory.js";
10
+ export * from "./order/order.factory.js";
11
+ export * from "./order-search/order-search.factory.js";
@@ -0,0 +1,38 @@
1
+ const HCL_STATUS_AVAILABLE = "Available";
2
+ class HclInventoryFactory {
3
+ inventorySchema;
4
+ constructor(inventorySchema) {
5
+ this.inventorySchema = inventorySchema;
6
+ }
7
+ /**
8
+ * Map an HclInventoryAvailabilityItem to an Inventory model.
9
+ * The `data.sku` is used for the identifier since WCS items carry only an
10
+ * internal `productId`, not the human-readable part number.
11
+ */
12
+ parseInventory(_context, data) {
13
+ const { item, sku, fulfilmentCenterKey } = data;
14
+ const identifier = {
15
+ variant: { sku },
16
+ fulfillmentCenter: { key: fulfilmentCenterKey || "online" }
17
+ };
18
+ return this.inventorySchema.parse({
19
+ identifier,
20
+ quantity: Number(item.availableQuantity ?? "0"),
21
+ status: item.inventoryStatus === HCL_STATUS_AVAILABLE ? "inStock" : "outOfStock"
22
+ });
23
+ }
24
+ /** Create an empty (out-of-stock) inventory when no matching record was found. */
25
+ createEmpty(sku, fulfilmentCenterKey) {
26
+ return this.inventorySchema.parse({
27
+ identifier: {
28
+ variant: { sku },
29
+ fulfillmentCenter: { key: fulfilmentCenterKey || "online" }
30
+ },
31
+ quantity: 0,
32
+ status: "outOfStock"
33
+ });
34
+ }
35
+ }
36
+ export {
37
+ HclInventoryFactory
38
+ };
@@ -0,0 +1,124 @@
1
+ function mapWcsOrderStatus(status) {
2
+ switch (status) {
3
+ case "S":
4
+ return "Shipped";
5
+ case "X":
6
+ case "R":
7
+ case "D":
8
+ return "Cancelled";
9
+ case "M":
10
+ case "G":
11
+ case "F":
12
+ return "ReleasedToFulfillment";
13
+ default:
14
+ return "AwaitingPayment";
15
+ }
16
+ }
17
+ class HclOrderFactory {
18
+ orderSchema;
19
+ constructor(orderSchema) {
20
+ this.orderSchema = orderSchema;
21
+ }
22
+ parseOrder(context, data) {
23
+ const currency = data.grandTotalCurrency ?? data.totalProductPriceCurrency ?? "USD";
24
+ const orderStatus = mapWcsOrderStatus(data.orderStatus ?? "");
25
+ const inventoryStatus = orderStatus === "Shipped" ? "Allocated" : "NotAllocated";
26
+ const userId = { userId: data.buyerId ?? "" };
27
+ const result = {
28
+ identifier: { key: data.orderId },
29
+ userId,
30
+ items: (data.orderItem ?? []).map(
31
+ (item) => this.parseOrderItem(context, item, currency)
32
+ ),
33
+ price: this.parseCostBreakdown(context, data, currency),
34
+ orderStatus,
35
+ inventoryStatus,
36
+ paymentInstructions: (data.paymentInstruction ?? []).map(
37
+ (pi) => this.parsePaymentInstruction(context, pi, currency)
38
+ ),
39
+ shippingAddress: data.x_shippingAddress ? {
40
+ identifier: {
41
+ nickName: data.x_shippingAddress.nickName ?? ""
42
+ },
43
+ firstName: data.x_shippingAddress.firstName ?? "",
44
+ lastName: data.x_shippingAddress.lastName ?? "",
45
+ streetAddress: data.x_shippingAddress.addressLine?.[0] ?? data.x_shippingAddress.address1 ?? "",
46
+ streetNumber: data.x_shippingAddress.addressLine?.[1] ?? "",
47
+ city: data.x_shippingAddress.city ?? "",
48
+ region: data.x_shippingAddress.state ?? "",
49
+ postalCode: data.x_shippingAddress.zipCode ?? "",
50
+ countryCode: data.x_shippingAddress.country ?? ""
51
+ } : void 0,
52
+ billingAddress: data.x_billingAddress ? {
53
+ identifier: {
54
+ nickName: data.x_billingAddress.nickName ?? ""
55
+ },
56
+ firstName: data.x_billingAddress.firstName ?? "",
57
+ lastName: data.x_billingAddress.lastName ?? "",
58
+ streetAddress: data.x_billingAddress.addressLine?.[0] ?? data.x_billingAddress.address1 ?? "",
59
+ streetNumber: data.x_billingAddress.addressLine?.[1] ?? "",
60
+ city: data.x_billingAddress.city ?? "",
61
+ region: data.x_billingAddress.state ?? "",
62
+ postalCode: data.x_billingAddress.zipCode ?? "",
63
+ countryCode: data.x_billingAddress.country ?? ""
64
+ } : void 0
65
+ };
66
+ return this.orderSchema.parse(result);
67
+ }
68
+ parseOrderItem(_context, item, defaultCurrency) {
69
+ const currency = item.currency ?? defaultCurrency;
70
+ const price = {
71
+ unitPrice: { value: Number(item.unitPrice), currency },
72
+ totalPrice: { value: Number(item.orderItemPrice), currency },
73
+ unitDiscount: { value: 0, currency },
74
+ totalDiscount: { value: 0, currency }
75
+ };
76
+ return {
77
+ identifier: { key: item.orderItemId },
78
+ variant: { sku: item.partNumber },
79
+ quantity: Number(item.quantity),
80
+ price,
81
+ inventoryStatus: "Allocated"
82
+ };
83
+ }
84
+ parseCostBreakdown(_context, data, currency) {
85
+ return {
86
+ grandTotal: { value: Number(data.grandTotal ?? "0"), currency },
87
+ totalProductPrice: {
88
+ value: Number(data.totalProductPrice ?? "0"),
89
+ currency
90
+ },
91
+ totalShipping: {
92
+ value: Number(data.totalShippingCharge ?? "0"),
93
+ currency
94
+ },
95
+ totalTax: { value: Number(data.totalSalesTax ?? "0"), currency },
96
+ totalDiscount: {
97
+ value: Math.abs(Number(data.totalAdjustment ?? "0")),
98
+ currency
99
+ },
100
+ totalSurcharge: { value: 0, currency }
101
+ };
102
+ }
103
+ parsePaymentInstruction(_context, pi, defaultCurrency) {
104
+ const currency = pi.currency ?? defaultCurrency;
105
+ const paymentMethod = {
106
+ method: pi.payMethodId,
107
+ name: pi.piDescription ?? pi.payMethodId,
108
+ paymentProcessor: pi.payMethodId
109
+ };
110
+ return {
111
+ identifier: { key: pi.piId },
112
+ amount: { value: Number(pi.piAmount ?? "0"), currency },
113
+ paymentMethod,
114
+ protocolData: (pi.protocolData ?? []).map((pd) => ({
115
+ key: pd.name,
116
+ value: pd.value
117
+ })),
118
+ status: "pending"
119
+ };
120
+ }
121
+ }
122
+ export {
123
+ HclOrderFactory
124
+ };
@@ -0,0 +1,59 @@
1
+ function mapWcsOrderStatus(status) {
2
+ switch (status) {
3
+ case "S":
4
+ return "Shipped";
5
+ case "X":
6
+ case "R":
7
+ case "D":
8
+ return "Cancelled";
9
+ case "M":
10
+ case "G":
11
+ case "F":
12
+ return "ReleasedToFulfillment";
13
+ default:
14
+ return "AwaitingPayment";
15
+ }
16
+ }
17
+ class HclOrderSearchFactory {
18
+ orderSearchResultSchema;
19
+ constructor(orderSearchResultSchema) {
20
+ this.orderSearchResultSchema = orderSearchResultSchema;
21
+ }
22
+ parseOrderSearchResult(context, data, query) {
23
+ const items = (data.Order ?? []).map(
24
+ (o) => this.parseOrderSearchResultItem(context, o)
25
+ );
26
+ const totalCount = Number(data.recordSetTotal ?? items.length);
27
+ const { pageSize, pageNumber } = query.search.paginationOptions;
28
+ const result = {
29
+ identifier: { ...query.search },
30
+ items,
31
+ totalCount,
32
+ pageSize,
33
+ pageNumber,
34
+ totalPages: Math.ceil(totalCount / Math.max(1, pageSize))
35
+ };
36
+ return this.orderSearchResultSchema.parse(result);
37
+ }
38
+ parseOrderSearchResultItem(_context, item) {
39
+ const orderStatus = mapWcsOrderStatus(item.orderStatus ?? "");
40
+ const inventoryStatus = orderStatus === "Shipped" ? "Allocated" : "NotAllocated";
41
+ const userId = { userId: item.buyerId ?? "" };
42
+ const totalAmount = {
43
+ value: Number(item.grandTotal ?? "0"),
44
+ currency: item.grandTotalCurrency ?? "USD"
45
+ };
46
+ return {
47
+ identifier: { key: item.orderId },
48
+ userId,
49
+ customerName: "",
50
+ orderDate: item.lastUpdateDate ?? "",
51
+ orderStatus,
52
+ inventoryStatus,
53
+ totalAmount
54
+ };
55
+ }
56
+ }
57
+ export {
58
+ HclOrderSearchFactory
59
+ };
@@ -0,0 +1,39 @@
1
+ class HclPriceFactory {
2
+ priceSchema;
3
+ constructor(priceSchema) {
4
+ this.priceSchema = priceSchema;
5
+ }
6
+ /**
7
+ * Map a price response item to a Price model.
8
+ * Supports both /price (EntitledPrice with capital `UnitPrice`) and
9
+ * /display_price (resultList with lowercase `unitPrice`) response shapes.
10
+ * The `item.partNumber` is used as the variant SKU identifier.
11
+ */
12
+ parsePrice(_context, data) {
13
+ const { item } = data;
14
+ const entitledItem = item;
15
+ const displayItem = item;
16
+ const priceEntry = entitledItem.UnitPrice?.[0]?.price ?? displayItem.unitPrice?.[0]?.price;
17
+ return this.priceSchema.parse({
18
+ identifier: { variant: { sku: item.partNumber ?? "" } },
19
+ unitPrice: {
20
+ value: priceEntry?.value ?? -1,
21
+ currency: priceEntry?.currency ?? ""
22
+ },
23
+ onSale: false,
24
+ tieredPrices: []
25
+ });
26
+ }
27
+ /** Create an empty (placeholder) price when no result was found for a SKU. */
28
+ createEmpty(sku, currency) {
29
+ return this.priceSchema.parse({
30
+ identifier: { variant: { sku } },
31
+ unitPrice: { value: -1, currency },
32
+ onSale: false,
33
+ tieredPrices: []
34
+ });
35
+ }
36
+ }
37
+ export {
38
+ HclPriceFactory
39
+ };
@@ -0,0 +1,49 @@
1
+ const BILLING_NICK_NAME = "Billing";
2
+ class HclProfileFactory {
3
+ profileSchema;
4
+ constructor(profileSchema) {
5
+ this.profileSchema = profileSchema;
6
+ }
7
+ parseProfile(context, data) {
8
+ const contacts = data.contact ?? [];
9
+ const billingContact = contacts.find(
10
+ (c) => c.nickName === BILLING_NICK_NAME
11
+ );
12
+ const shippingContacts = contacts.filter(
13
+ (c) => c.nickName !== BILLING_NICK_NAME
14
+ );
15
+ const defaultShipping = shippingContacts.find((c) => c.primary === "1") ?? shippingContacts[0];
16
+ const alternates = shippingContacts.filter((c) => c !== defaultShipping);
17
+ const result = {
18
+ identifier: { userId: data.userId },
19
+ email: data.email1 ?? data.logonId ?? "",
20
+ phone: data.phone1 ?? "",
21
+ emailVerified: false,
22
+ phoneVerified: false,
23
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
24
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
25
+ billingAddress: billingContact ? this.parseAddress(context, billingContact) : void 0,
26
+ shippingAddress: defaultShipping ? this.parseAddress(context, defaultShipping) : void 0,
27
+ alternateShippingAddresses: alternates.map(
28
+ (c) => this.parseAddress(context, c)
29
+ )
30
+ };
31
+ return this.profileSchema.parse(result);
32
+ }
33
+ parseAddress(_context, contact) {
34
+ return {
35
+ identifier: { nickName: contact.nickName },
36
+ firstName: contact.firstName ?? "",
37
+ lastName: contact.lastName ?? "",
38
+ streetAddress: contact.addressLine?.[0] ?? contact.address1 ?? "",
39
+ streetNumber: contact.addressLine?.[1] ?? "",
40
+ city: contact.city ?? "",
41
+ region: contact.stateOrProvinceName ?? "",
42
+ postalCode: contact.zipCode ?? "",
43
+ countryCode: contact.country ?? ""
44
+ };
45
+ }
46
+ }
47
+ export {
48
+ HclProfileFactory
49
+ };
package/index.js CHANGED
@@ -6,6 +6,14 @@ export * from "./core/client.js";
6
6
  export * from "./core/initialize.js";
7
7
  export * from "./core/initialize.types.js";
8
8
  export * from "./factories/index.js";
9
+ export * from "./capabilities/cart.capability.js";
10
+ export * from "./capabilities/checkout.capability.js";
9
11
  export * from "./capabilities/product.capability.js";
10
12
  export * from "./capabilities/category.capability.js";
11
13
  export * from "./capabilities/product-search.capability.js";
14
+ export * from "./capabilities/price.capability.js";
15
+ export * from "./capabilities/inventory.capability.js";
16
+ export * from "./capabilities/identity.capability.js";
17
+ export * from "./capabilities/profile.capability.js";
18
+ export * from "./capabilities/order.capability.js";
19
+ export * from "./capabilities/order-search.capability.js";
package/package.json CHANGED
@@ -1,14 +1,17 @@
1
1
  {
2
2
  "name": "@reactionary/hcl",
3
- "version": "0.9.8",
3
+ "version": "0.9.12",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "types": "./src/index.d.ts",
7
7
  "dependencies": {
8
- "@reactionary/core": "0.9.8",
9
- "zod": "4.1.9",
10
- "vitest": "^4.0.9",
11
- "@nx/vite": "22.4.5"
8
+ "@reactionary/core": "0.9.12",
9
+ "debug": "^4.4.3",
10
+ "zod": "4.1.9"
11
+ },
12
+ "devDependencies": {
13
+ "@nx/vite": "22.4.5",
14
+ "vitest": "^4.0.9"
12
15
  },
13
16
  "sideEffects": false
14
17
  }
@@ -12,7 +12,11 @@ const HclCapabilitiesSchema = CapabilitiesSchema.pick({
12
12
  product: true,
13
13
  price: true,
14
14
  inventory: true,
15
- productSearch: true
15
+ identity: true,
16
+ productSearch: true,
17
+ profile: true,
18
+ order: true,
19
+ orderSearch: true
16
20
  }).extend({
17
21
  cart: OverridableCapabilitySchema.optional(),
18
22
  checkout: OverridableCapabilitySchema.optional(),
@@ -20,7 +24,11 @@ const HclCapabilitiesSchema = CapabilitiesSchema.pick({
20
24
  product: OverridableCapabilitySchema.optional(),
21
25
  price: OverridableCapabilitySchema.optional(),
22
26
  inventory: OverridableCapabilitySchema.optional(),
23
- productSearch: OverridableCapabilitySchema.optional()
27
+ identity: OverridableCapabilitySchema.optional(),
28
+ productSearch: OverridableCapabilitySchema.optional(),
29
+ profile: OverridableCapabilitySchema.optional(),
30
+ order: OverridableCapabilitySchema.optional(),
31
+ orderSearch: OverridableCapabilitySchema.optional()
24
32
  }).partial();
25
33
  export {
26
34
  HclCapabilitiesSchema
@@ -29,7 +29,15 @@ const HclConfigurationSchema = z.looseObject({
29
29
  localeMap: z.record(z.string(), z.string()).default({ "en-US": "-1" }).meta({
30
30
  description: "Mapping from BCP 47 locale (RequestContext.languageContext.locale) to HCL langId."
31
31
  }),
32
- profiles: HclProfilesSchema
32
+ profiles: HclProfilesSchema,
33
+ /**
34
+ * Optional price rule ID passed to /display_price?q=byPartNumbersAndPriceRuleId.
35
+ * When omitted the server applies its default configured price rule.
36
+ * In Karkkainen this is '10003'.
37
+ */
38
+ priceRuleId: z.string().optional().meta({
39
+ description: "Price rule ID for /display_price WCS calls. Optional \u2014 the server uses its default rule when absent."
40
+ })
33
41
  });
34
42
  export {
35
43
  HclConfigurationSchema