@reactionary/provider-commercetools 0.0.83 → 0.0.85

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/core/client.js CHANGED
@@ -5,7 +5,6 @@ import {
5
5
  import { randomUUID } from "crypto";
6
6
  import {
7
7
  AnonymousIdentitySchema,
8
- GuestIdentitySchema,
9
8
  RegisteredIdentitySchema
10
9
  } from "@reactionary/core";
11
10
  import * as crypto from "crypto";
@@ -113,7 +112,17 @@ class CommercetoolsClient {
113
112
  }
114
113
  async logout() {
115
114
  await this.cache.set({ token: "", refreshToken: "", expirationTime: 0 });
116
- return AnonymousIdentitySchema.parse({});
115
+ const identity = {
116
+ meta: {
117
+ cache: {
118
+ hit: false,
119
+ key: ""
120
+ },
121
+ placeholder: false
122
+ },
123
+ type: "Anonymous"
124
+ };
125
+ return identity;
117
126
  }
118
127
  // FIXME: This can fail if the short-lived access token has expired. In other words, probably missing a token refresh.
119
128
  async introspect() {
@@ -188,7 +197,16 @@ class CommercetoolsClient {
188
197
  };
189
198
  return identity;
190
199
  }
191
- return AnonymousIdentitySchema.parse({});
200
+ return {
201
+ type: "Anonymous",
202
+ meta: {
203
+ cache: {
204
+ hit: false,
205
+ key: ""
206
+ },
207
+ placeholder: false
208
+ }
209
+ };
192
210
  }
193
211
  async becomeGuest() {
194
212
  const credentials = Buffer.from(
@@ -21,7 +21,6 @@ class RequestContextTokenCache {
21
21
  };
22
22
  }
23
23
  async set(cache, tokenCacheOptions) {
24
- console.log("TokenCache set session:", this.context.session["PROVIDER_COMMERCETOOLS"], "with token:", cache);
25
24
  const session = CommercetoolsSessionSchema.parse(
26
25
  this.context.session["PROVIDER_COMMERCETOOLS"] || {}
27
26
  );
@@ -29,7 +28,6 @@ class RequestContextTokenCache {
29
28
  session.refreshToken = cache.refreshToken;
30
29
  session.token = cache.token;
31
30
  session.expirationTime = cache.expirationTime;
32
- console.log("TokenCache updated session:", this.context.session["PROVIDER_COMMERCETOOLS"]);
33
31
  }
34
32
  }
35
33
  export {
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@reactionary/provider-commercetools",
3
- "version": "0.0.83",
3
+ "version": "0.0.85",
4
4
  "main": "index.js",
5
5
  "types": "src/index.d.ts",
6
6
  "dependencies": {
7
- "@reactionary/core": "0.0.83",
7
+ "@reactionary/core": "0.0.85",
8
8
  "debug": "^4.4.3",
9
9
  "zod": "4.1.9",
10
10
  "@commercetools/ts-client": "^4.2.1",
@@ -225,6 +225,44 @@ class CommercetoolsCartProvider extends CartProvider {
225
225
  orders: clientWithProject.me().orders()
226
226
  };
227
227
  }
228
+ parseCartItem(remoteItem) {
229
+ const unitPrice = remoteItem.price.value.centAmount;
230
+ const totalPrice = remoteItem.totalPrice.centAmount || 0;
231
+ const totalDiscount = remoteItem.price.discounted?.value.centAmount || 0;
232
+ const unitDiscount = totalDiscount / remoteItem.quantity;
233
+ const currency = remoteItem.price.value.currencyCode.toUpperCase();
234
+ const item = {
235
+ identifier: {
236
+ key: remoteItem.id
237
+ },
238
+ product: {
239
+ key: remoteItem.productId
240
+ },
241
+ variant: {
242
+ sku: remoteItem.variant.sku || ""
243
+ },
244
+ quantity: remoteItem.quantity,
245
+ price: {
246
+ unitPrice: {
247
+ value: unitPrice / 100,
248
+ currency
249
+ },
250
+ unitDiscount: {
251
+ value: unitDiscount / 100,
252
+ currency
253
+ },
254
+ totalPrice: {
255
+ value: (totalPrice || 0) / 100,
256
+ currency
257
+ },
258
+ totalDiscount: {
259
+ value: totalDiscount / 100,
260
+ currency
261
+ }
262
+ }
263
+ };
264
+ return CartItemSchema.parse(item);
265
+ }
228
266
  parseSingle(remote) {
229
267
  const identifier = {
230
268
  key: remote.id,
@@ -252,7 +290,7 @@ class CommercetoolsCartProvider extends CartProvider {
252
290
  },
253
291
  totalShipping: {
254
292
  value: shippingTotal / 100,
255
- currency: remote.shippingInfo?.price.currencyCode
293
+ currency
256
294
  },
257
295
  totalProductPrice: {
258
296
  value: productTotal / 100,
@@ -265,33 +303,7 @@ class CommercetoolsCartProvider extends CartProvider {
265
303
  };
266
304
  const items = new Array();
267
305
  for (const remoteItem of remote.lineItems) {
268
- const item = CartItemSchema.parse({});
269
- item.identifier.key = remoteItem.id;
270
- item.product.key = remoteItem.productId;
271
- item.variant.sku = remoteItem.variant.sku || "";
272
- item.quantity = remoteItem.quantity;
273
- const unitPrice = remoteItem.price.value.centAmount;
274
- const totalPrice = remoteItem.totalPrice.centAmount || 0;
275
- const totalDiscount = remoteItem.price.discounted?.value.centAmount || 0;
276
- const unitDiscount = totalDiscount / remoteItem.quantity;
277
- item.price = {
278
- unitPrice: {
279
- value: unitPrice / 100,
280
- currency
281
- },
282
- unitDiscount: {
283
- value: unitDiscount / 100,
284
- currency
285
- },
286
- totalPrice: {
287
- value: (totalPrice || 0) / 100,
288
- currency
289
- },
290
- totalDiscount: {
291
- value: totalDiscount / 100,
292
- currency
293
- }
294
- };
306
+ const item = this.parseCartItem(remoteItem);
295
307
  items.push(item);
296
308
  }
297
309
  const cart = {
@@ -50,7 +50,7 @@ class CommercetoolsCategoryProvider extends CategoryProvider {
50
50
  hit: false,
51
51
  key: ""
52
52
  },
53
- placeholder: false
53
+ placeholder: true
54
54
  }
55
55
  };
56
56
  return dummyCategory;
@@ -22,12 +22,8 @@ import {
22
22
  CheckoutQueryForAvailablePaymentMethodsSchema,
23
23
  CheckoutQueryForAvailableShippingMethodsSchema,
24
24
  CheckoutSchema,
25
- PaymentInstructionIdentifierSchema,
26
- PaymentInstructionSchema,
27
- PaymentMethodIdentifierSchema,
28
25
  PaymentMethodSchema,
29
26
  Reactionary,
30
- ShippingInstructionSchema,
31
27
  ShippingMethodSchema
32
28
  } from "@reactionary/core";
33
29
  import z from "zod";
@@ -170,17 +166,21 @@ class CommercetoolsCheckoutProvider extends CheckoutProvider {
170
166
  const result = [];
171
167
  const inputShippingMethods = shippingMethodsResponse.body.results;
172
168
  for (const sm of inputShippingMethods) {
173
- const shippingMethod = ShippingMethodSchema.parse({
174
- identifier: {
175
- key: sm.key
176
- },
177
- name: sm.name,
178
- description: sm.localizedDescription?.[this.context.languageContext.locale] || "",
169
+ const identifier = {
170
+ key: sm.key
171
+ };
172
+ const name = sm.name;
173
+ const description = sm.localizedDescription?.[this.context.languageContext.locale] || "";
174
+ const shippingMethod = {
175
+ deliveryTime: "",
176
+ description,
177
+ identifier,
178
+ name,
179
179
  price: sm.zoneRates[0].shippingRates[0].price ? {
180
180
  value: (sm.zoneRates[0].shippingRates[0].price.centAmount || 0) / 100,
181
181
  currency: sm.zoneRates[0].shippingRates[0].price.currencyCode || this.context.languageContext.currencyCode
182
182
  } : { value: 0, currency: this.context.languageContext.currencyCode }
183
- });
183
+ };
184
184
  result.push(shippingMethod);
185
185
  }
186
186
  return result;
@@ -316,6 +316,41 @@ class CommercetoolsCheckoutProvider extends CheckoutProvider {
316
316
  getStaticPaymentMethods(_checkout) {
317
317
  return this.config.paymentMethods || [];
318
318
  }
319
+ parseCheckoutItem(remoteItem) {
320
+ const unitPrice = remoteItem.price.value.centAmount;
321
+ const totalPrice = remoteItem.totalPrice.centAmount || 0;
322
+ const totalDiscount = remoteItem.price.discounted?.value.centAmount || 0;
323
+ const unitDiscount = totalDiscount / remoteItem.quantity;
324
+ const currency = remoteItem.price.value.currencyCode.toUpperCase();
325
+ const item = {
326
+ identifier: {
327
+ key: remoteItem.id
328
+ },
329
+ variant: {
330
+ sku: remoteItem.variant.sku || ""
331
+ },
332
+ quantity: remoteItem.quantity,
333
+ price: {
334
+ unitPrice: {
335
+ value: unitPrice / 100,
336
+ currency
337
+ },
338
+ unitDiscount: {
339
+ value: unitDiscount / 100,
340
+ currency
341
+ },
342
+ totalPrice: {
343
+ value: (totalPrice || 0) / 100,
344
+ currency
345
+ },
346
+ totalDiscount: {
347
+ value: totalDiscount / 100,
348
+ currency
349
+ }
350
+ }
351
+ };
352
+ return CheckoutItemSchema.parse(item);
353
+ }
319
354
  parseSingle(remote) {
320
355
  const identifier = {
321
356
  key: remote.id,
@@ -361,7 +396,7 @@ class CommercetoolsCheckoutProvider extends CheckoutProvider {
361
396
  },
362
397
  totalShipping: {
363
398
  value: shippingTotal / 100,
364
- currency: remote.shippingInfo?.price.currencyCode
399
+ currency
365
400
  },
366
401
  totalProductPrice: {
367
402
  value: productTotal / 100,
@@ -374,36 +409,17 @@ class CommercetoolsCheckoutProvider extends CheckoutProvider {
374
409
  };
375
410
  const items = new Array();
376
411
  for (const remoteItem of remote.lineItems) {
377
- const item = CheckoutItemSchema.parse({});
378
- item.identifier.key = remoteItem.id;
379
- item.variant.sku = remoteItem.variant.sku || "";
380
- item.quantity = remoteItem.quantity;
381
- const unitPrice = remoteItem.price.value.centAmount;
382
- const totalPrice = remoteItem.totalPrice.centAmount || 0;
383
- const totalDiscount = remoteItem.price.discounted?.value.centAmount || 0;
384
- const unitDiscount = totalDiscount / remoteItem.quantity;
385
- item.price = {
386
- unitPrice: {
387
- value: unitPrice / 100,
388
- currency
389
- },
390
- unitDiscount: {
391
- value: unitDiscount / 100,
392
- currency
393
- },
394
- totalPrice: {
395
- value: (totalPrice || 0) / 100,
396
- currency
397
- },
398
- totalDiscount: {
399
- value: totalDiscount / 100,
400
- currency
401
- }
402
- };
412
+ const item = this.parseCheckoutItem(remoteItem);
403
413
  items.push(item);
404
414
  }
405
415
  const shippingInstruction = this.parseShippingInstruction(remote);
406
- const readyForFinalization = this.isReadyForFinalization(price, paymentInstructions, billingAddress, shippingAddress, shippingInstruction);
416
+ const readyForFinalization = this.isReadyForFinalization(
417
+ price,
418
+ paymentInstructions,
419
+ billingAddress,
420
+ shippingAddress,
421
+ shippingInstruction
422
+ );
407
423
  const result = {
408
424
  identifier,
409
425
  originalCartReference,
@@ -441,35 +457,50 @@ class CommercetoolsCheckoutProvider extends CheckoutProvider {
441
457
  return true;
442
458
  }
443
459
  parsePaymentInstruction(remote) {
444
- const newModel = PaymentInstructionSchema.parse({});
445
- newModel.identifier = PaymentInstructionIdentifierSchema.parse({
446
- key: remote.id || ""
447
- });
448
- newModel.amount = {
460
+ const identifier = {
461
+ key: remote.id
462
+ };
463
+ const amount = {
449
464
  value: remote.amountPlanned.centAmount / 100,
450
465
  currency: remote.amountPlanned.currencyCode
451
466
  };
452
467
  const method = remote.paymentMethodInfo?.method || "unknown";
453
468
  const paymentProcessor = remote.paymentMethodInfo?.paymentInterface || method;
454
469
  const paymentName = remote.paymentMethodInfo.name[this.context.languageContext.locale];
455
- newModel.paymentMethod = PaymentMethodIdentifierSchema.parse({
470
+ const paymentMethod = {
456
471
  method,
457
472
  paymentProcessor,
458
473
  name: paymentName || method || "Unknown"
459
- });
474
+ };
460
475
  const customData = remote.custom?.fields || {};
461
- newModel.protocolData = Object.keys(customData).map((x) => ({ key: x, value: customData[x] })) || [];
476
+ const protocolData = Object.keys(customData).map((x) => ({ key: x, value: customData[x] })) || [];
477
+ let status = "pending";
462
478
  if (remote.transactions && remote.transactions.length > 0) {
463
479
  const lastTransaction = remote.transactions[remote.transactions.length - 1];
464
480
  if (lastTransaction.type === "Authorization" && lastTransaction.state === "Pending") {
465
- newModel.status = "pending";
481
+ status = "pending";
466
482
  } else if (lastTransaction.type === "Authorization" && lastTransaction.state === "Success") {
467
- newModel.status = "authorized";
483
+ status = "authorized";
468
484
  }
469
485
  } else {
470
- newModel.status = "pending";
486
+ status = "pending";
471
487
  }
472
- return PaymentInstructionSchema.parse(newModel);
488
+ const meta = {
489
+ cache: {
490
+ hit: false,
491
+ key: ""
492
+ },
493
+ placeholder: false
494
+ };
495
+ const result = {
496
+ amount,
497
+ identifier,
498
+ meta,
499
+ paymentMethod,
500
+ protocolData,
501
+ status
502
+ };
503
+ return result;
473
504
  }
474
505
  parseAddress(remote) {
475
506
  return {
@@ -499,18 +530,21 @@ class CommercetoolsCheckoutProvider extends CheckoutProvider {
499
530
  const instructions = remote.custom?.fields["shippingInstruction"] || "";
500
531
  const consentForUnattendedDelivery = remote.custom?.fields["consentForUnattendedDelivery"] === "true" || false;
501
532
  const pickupPoint = remote.custom?.fields["pickupPointId"] || "";
502
- const shippingInstruction = ShippingInstructionSchema.parse({
503
- amount: {
504
- value: (remote.shippingInfo.price.centAmount || 0) / 100,
505
- currency: remote.shippingInfo.price.currencyCode
506
- },
533
+ const shippingInstruction = {
507
534
  shippingMethod: {
508
535
  key: remote.shippingInfo.shippingMethod?.obj?.key || ""
509
536
  },
510
537
  pickupPoint: pickupPoint || "",
511
538
  instructions: instructions || "",
512
- consentForUnattendedDelivery: consentForUnattendedDelivery || false
513
- });
539
+ consentForUnattendedDelivery: consentForUnattendedDelivery || false,
540
+ meta: {
541
+ cache: {
542
+ hit: false,
543
+ key: ""
544
+ },
545
+ placeholder: false
546
+ }
547
+ };
514
548
  return shippingInstruction;
515
549
  }
516
550
  }
@@ -20,8 +20,6 @@ import {
20
20
  ProductQueryBySKUSchema,
21
21
  ProductQueryBySlugSchema,
22
22
  ProductSchema,
23
- ProductVariantIdentifierSchema,
24
- ProductVariantSchema,
25
23
  Reactionary
26
24
  } from "@reactionary/core";
27
25
  class CommercetoolsProductProvider extends ProductProvider {
@@ -79,7 +77,10 @@ class CommercetoolsProductProvider extends ProductProvider {
79
77
  }
80
78
  const variantLevelAttributes = data.masterVariant.attributes?.map((x) => this.parseAttribute(x)) || [];
81
79
  const productLevelAttributes = data.attributes.map((x) => this.parseAttribute(x)) || [];
82
- const sharedAttributes = [...productLevelAttributes, ...variantLevelAttributes];
80
+ const sharedAttributes = [
81
+ ...productLevelAttributes,
82
+ ...variantLevelAttributes
83
+ ];
83
84
  const mainVariant = this.parseVariant(data.masterVariant, data);
84
85
  const meta = {
85
86
  cache: { hit: false, key: this.generateCacheKeySingle(identifier) },
@@ -102,22 +103,58 @@ class CommercetoolsProductProvider extends ProductProvider {
102
103
  };
103
104
  return result;
104
105
  }
106
+ /**
107
+ * Return true, if the attribute is a defining attribute (ie an option)
108
+ * @param attr a variant attribute
109
+ * @returns true if the attribute is an option
110
+ */
111
+ isVariantAttributeAnOption(attr) {
112
+ return true;
113
+ }
105
114
  parseVariant(variant, product) {
106
- const result = ProductVariantSchema.parse({
107
- identifier: ProductVariantIdentifierSchema.parse({
108
- sku: variant.sku
109
- }),
110
- images: [
111
- ...(variant.images || []).map(
112
- (img) => ImageSchema.parse({
113
- sourceUrl: img.url,
114
- altText: img.label || "",
115
- width: img.dimensions?.w,
116
- height: img.dimensions?.h
117
- })
118
- )
119
- ]
120
- });
115
+ const identifier = {
116
+ sku: variant.sku
117
+ };
118
+ const images = [
119
+ ...(variant.images || []).map(
120
+ (img) => ImageSchema.parse({
121
+ sourceUrl: img.url,
122
+ altText: img.label || "",
123
+ width: img.dimensions?.w,
124
+ height: img.dimensions?.h
125
+ })
126
+ )
127
+ ];
128
+ const options = (variant.attributes ?? []).filter((attr) => this.isVariantAttributeAnOption(attr)).map(
129
+ (attr) => {
130
+ const attrVal = this.parseAttributeValue(attr);
131
+ const optionIdentifier = {
132
+ key: attr.name
133
+ };
134
+ const option = {
135
+ identifier: optionIdentifier,
136
+ name: attr.name,
137
+ value: {
138
+ identifier: {
139
+ key: attrVal.value,
140
+ option: optionIdentifier
141
+ },
142
+ label: attrVal.label
143
+ }
144
+ };
145
+ return option;
146
+ }
147
+ ) || [];
148
+ const result = {
149
+ identifier,
150
+ images,
151
+ barcode: "",
152
+ ean: "",
153
+ gtin: "",
154
+ name: "",
155
+ options,
156
+ upc: ""
157
+ };
121
158
  return result;
122
159
  }
123
160
  parseAttribute(attr) {
@@ -49,15 +49,12 @@ export declare class CommercetoolsClient {
49
49
  type: "Registered";
50
50
  }>;
51
51
  logout(): Promise<{
52
- [x: string]: unknown;
53
52
  meta: {
54
- [x: string]: unknown;
55
53
  cache: {
56
- [x: string]: unknown;
57
- hit: boolean;
54
+ hit: false;
58
55
  key: string;
59
56
  };
60
- placeholder: boolean;
57
+ placeholder: false;
61
58
  };
62
59
  type: "Anonymous";
63
60
  }>;
@@ -1,7 +1,7 @@
1
1
  import { CartProvider } from '@reactionary/core';
2
- import type { CartMutationItemAdd, CartMutationItemQuantityChange, CartMutationItemRemove, CartQueryById, CartIdentifier, CartMutationApplyCoupon, CartMutationDeleteCart, CartMutationRemoveCoupon, CartMutationChangeCurrency, RequestContext, Cart, Cache } from '@reactionary/core';
2
+ import type { CartItem, CartMutationItemAdd, CartMutationItemQuantityChange, CartMutationItemRemove, CartQueryById, CartIdentifier, CartMutationApplyCoupon, CartMutationDeleteCart, CartMutationRemoveCoupon, CartMutationChangeCurrency, RequestContext, Cart, Cache } from '@reactionary/core';
3
3
  import type { CommercetoolsConfiguration } from '../schema/configuration.schema.js';
4
- import type { Cart as CTCart, MyCartUpdateAction } from '@commercetools/platform-sdk';
4
+ import type { Cart as CTCart, LineItem, MyCartUpdateAction } from '@commercetools/platform-sdk';
5
5
  import type { CommercetoolsClient } from '../core/client.js';
6
6
  export declare class CommercetoolsCartProvider extends CartProvider {
7
7
  protected config: CommercetoolsConfiguration;
@@ -28,5 +28,6 @@ export declare class CommercetoolsCartProvider extends CartProvider {
28
28
  activeCart: import("@commercetools/platform-sdk").ByProjectKeyMeActiveCartRequestBuilder;
29
29
  orders: import("@commercetools/platform-sdk").ByProjectKeyMeOrdersRequestBuilder;
30
30
  }>;
31
+ protected parseCartItem(remoteItem: LineItem): CartItem;
31
32
  protected parseSingle(remote: CTCart): Cart;
32
33
  }
@@ -1,9 +1,8 @@
1
- import type { Cache, Checkout, RequestContext, PaymentMethod, ShippingMethod, CheckoutMutationInitiateCheckout, CheckoutMutationSetShippingAddress, CheckoutMutationFinalizeCheckout, CheckoutMutationAddPaymentInstruction, CheckoutMutationRemovePaymentInstruction, CheckoutMutationSetShippingInstruction, CheckoutQueryById, CheckoutQueryForAvailablePaymentMethods, CheckoutQueryForAvailableShippingMethods, CheckoutIdentifier, ShippingInstruction, PaymentInstruction, Address, CostBreakDown } from '@reactionary/core';
1
+ import type { Address as CTAddress, Cart as CTCart, Payment as CTPayment, LineItem, MyCartUpdateAction } from '@commercetools/platform-sdk';
2
+ import type { Address, Cache, Checkout, CheckoutIdentifier, CheckoutItem, CheckoutMutationAddPaymentInstruction, CheckoutMutationFinalizeCheckout, CheckoutMutationInitiateCheckout, CheckoutMutationRemovePaymentInstruction, CheckoutMutationSetShippingAddress, CheckoutMutationSetShippingInstruction, CheckoutQueryById, CheckoutQueryForAvailablePaymentMethods, CheckoutQueryForAvailableShippingMethods, CostBreakDown, PaymentInstruction, PaymentMethod, RequestContext, ShippingInstruction, ShippingMethod } from '@reactionary/core';
2
3
  import { CheckoutProvider } from '@reactionary/core';
3
- import type { CommercetoolsConfiguration } from '../schema/configuration.schema.js';
4
- import type { MyCartUpdateAction } from '@commercetools/platform-sdk';
5
- import type { Address as CTAddress, Payment as CTPayment, Cart as CTCart } from '@commercetools/platform-sdk';
6
4
  import type { CommercetoolsClient } from '../core/client.js';
5
+ import type { CommercetoolsConfiguration } from '../schema/configuration.schema.js';
7
6
  export declare class CheckoutNotReadyForFinalizationError extends Error {
8
7
  checkoutIdentifier: CheckoutIdentifier;
9
8
  constructor(checkoutIdentifier: CheckoutIdentifier);
@@ -35,6 +34,7 @@ export declare class CommercetoolsCheckoutProvider extends CheckoutProvider {
35
34
  * @returns
36
35
  */
37
36
  protected getStaticPaymentMethods(_checkout: CheckoutIdentifier): PaymentMethod[];
37
+ protected parseCheckoutItem(remoteItem: LineItem): CheckoutItem;
38
38
  protected parseSingle(remote: CTCart): Checkout;
39
39
  protected isReadyForFinalization(price: CostBreakDown, paymentInstructions: Array<PaymentInstruction>, billingAddress?: Address, shippingAddress?: Address, shippingInstruction?: ShippingInstruction): boolean;
40
40
  protected parsePaymentInstruction(remote: CTPayment): PaymentInstruction;
@@ -13,6 +13,12 @@ export declare class CommercetoolsProductProvider extends ProductProvider {
13
13
  getBySlug(payload: ProductQueryBySlug): Promise<Product | null>;
14
14
  getBySKU(payload: ProductQueryBySKU): Promise<Product>;
15
15
  protected parseSingle(data: ProductProjection): Product;
16
+ /**
17
+ * Return true, if the attribute is a defining attribute (ie an option)
18
+ * @param attr a variant attribute
19
+ * @returns true if the attribute is an option
20
+ */
21
+ protected isVariantAttributeAnOption(attr: CTAttribute): boolean;
16
22
  protected parseVariant(variant: CTProductVariant, product: ProductProjection): ProductVariant;
17
23
  protected parseAttribute(attr: CTAttribute): ProductAttribute;
18
24
  protected parseAttributeValue(attr: CTAttribute): ProductAttributeValue;