@bacano/sdk 1.1.0 → 1.1.8

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/react.js CHANGED
@@ -286,7 +286,6 @@ var GET_PRODUCTS = (
286
286
  ) {
287
287
  id
288
288
  related_product_id
289
- display_label
290
289
  sort_order
291
290
  related_product {
292
291
  id
@@ -308,6 +307,52 @@ var GET_PRODUCTS = (
308
307
  }
309
308
  }
310
309
  }
310
+ product_option_group_members(limit: 1) {
311
+ group {
312
+ id
313
+ display_title
314
+ members(
315
+ where: {
316
+ product: {
317
+ product_website_publications: {
318
+ website_id: { _eq: $websiteId }
319
+ is_visible: { _eq: true }
320
+ }
321
+ chain_products: {
322
+ chain_id: { _eq: $chainId }
323
+ is_enabled: { _eq: true }
324
+ status: { _eq: active }
325
+ }
326
+ }
327
+ }
328
+ order_by: { sort_order: asc }
329
+ ) {
330
+ id
331
+ product_id
332
+ display_label
333
+ sort_order
334
+ product {
335
+ id
336
+ slug
337
+ name
338
+ sku
339
+ product_media(
340
+ where: { file: { mimeType: { _ilike: "image/%" } } }
341
+ order_by: { order: asc }
342
+ limit: 1
343
+ ) {
344
+ id
345
+ file_id
346
+ order
347
+ file {
348
+ mimeType
349
+ name
350
+ }
351
+ }
352
+ }
353
+ }
354
+ }
355
+ }
311
356
  product_attributes {
312
357
  id
313
358
  value
@@ -392,6 +437,52 @@ var GET_PRODUCT_BY_PK = (
392
437
  limit: 1
393
438
  ) {
394
439
  id
440
+ product_option_group_members(limit: 1) {
441
+ group {
442
+ id
443
+ display_title
444
+ members(
445
+ where: {
446
+ product: {
447
+ product_website_publications: {
448
+ website_id: { _eq: $websiteId }
449
+ is_visible: { _eq: true }
450
+ }
451
+ chain_products: {
452
+ chain_id: { _eq: $chainId }
453
+ is_enabled: { _eq: true }
454
+ status: { _eq: active }
455
+ }
456
+ }
457
+ }
458
+ order_by: { sort_order: asc }
459
+ ) {
460
+ id
461
+ product_id
462
+ display_label
463
+ sort_order
464
+ product {
465
+ id
466
+ slug
467
+ name
468
+ sku
469
+ product_media(
470
+ where: { file: { mimeType: { _ilike: "image/%" } } }
471
+ order_by: { order: asc }
472
+ limit: 1
473
+ ) {
474
+ id
475
+ file_id
476
+ order
477
+ file {
478
+ mimeType
479
+ name
480
+ }
481
+ }
482
+ }
483
+ }
484
+ }
485
+ }
395
486
  name
396
487
  description
397
488
  long_description
@@ -478,7 +569,6 @@ var GET_PRODUCT_BY_PK = (
478
569
  ) {
479
570
  id
480
571
  related_product_id
481
- display_label
482
572
  sort_order
483
573
  related_product {
484
574
  id
@@ -852,6 +942,24 @@ var WEBSITE_GET_PRODUCT_RECOMMENDATIONS = (
852
942
  }
853
943
  `
854
944
  );
945
+ var WEBSITE_GET_PRODUCT_SPECIFICATIONS = (
946
+ /* GraphQL */
947
+ `
948
+ mutation WebsiteGetProductSpecifications(
949
+ $websiteId: uuid!
950
+ $productIds: [uuid!]!
951
+ ) {
952
+ result: websiteGetProductSpecifications(
953
+ website_id: $websiteId
954
+ product_ids: $productIds
955
+ ) {
956
+ success
957
+ specifications
958
+ errors
959
+ }
960
+ }
961
+ `
962
+ );
855
963
  var GET_BRANDS = (
856
964
  /* GraphQL */
857
965
  `
@@ -960,10 +1068,15 @@ var WEBSITE_GET_PRODUCT_CONTENT = (
960
1068
  var CHECK_AVAILABILITY = (
961
1069
  /* GraphQL */
962
1070
  `
963
- query CheckAvailability($variantIds: [uuid!]!, $chainId: uuid!) {
1071
+ query CheckAvailability(
1072
+ $variantIds: [uuid!]!
1073
+ $chainId: uuid!
1074
+ $websiteId: uuid
1075
+ ) {
964
1076
  websiteProductAvailability(
965
1077
  product_variant_ids: $variantIds
966
1078
  chain_id: $chainId
1079
+ website_id: $websiteId
967
1080
  ) {
968
1081
  product_variant_id
969
1082
  in_stock
@@ -1019,6 +1132,7 @@ var GET_PRODUCT_LIVE_STATE = (
1019
1132
  websiteProductAvailability(
1020
1133
  product_variant_ids: $variantIds
1021
1134
  chain_id: $chainId
1135
+ website_id: $websiteId
1022
1136
  ) {
1023
1137
  product_variant_id
1024
1138
  in_stock
@@ -1056,10 +1170,28 @@ var CREATE_CART = (
1056
1170
  total_with_discount
1057
1171
  created_at
1058
1172
  updated_at
1173
+ volume_pricing {
1174
+ enabled
1175
+ eligibleQuantity
1176
+ appliedPriceType
1177
+ appliedMinQuantity
1178
+ nextPriceType
1179
+ nextMinQuantity
1180
+ remainingQuantity
1181
+ totalSavings
1182
+ }
1059
1183
  items {
1060
1184
  id
1061
1185
  product_variant_id
1062
1186
  quantity
1187
+ unit_price
1188
+ line_total
1189
+ base_unit_price
1190
+ unit_savings
1191
+ line_savings
1192
+ volume_pricing_eligible
1193
+ resolved_price_type
1194
+ volume_pricing_min_quantity
1063
1195
  product_variant {
1064
1196
  id
1065
1197
  sku
@@ -1099,10 +1231,28 @@ var GET_CART = (
1099
1231
  total_with_discount
1100
1232
  created_at
1101
1233
  updated_at
1234
+ volume_pricing {
1235
+ enabled
1236
+ eligibleQuantity
1237
+ appliedPriceType
1238
+ appliedMinQuantity
1239
+ nextPriceType
1240
+ nextMinQuantity
1241
+ remainingQuantity
1242
+ totalSavings
1243
+ }
1102
1244
  items {
1103
1245
  id
1104
1246
  product_variant_id
1105
1247
  quantity
1248
+ unit_price
1249
+ line_total
1250
+ base_unit_price
1251
+ unit_savings
1252
+ line_savings
1253
+ volume_pricing_eligible
1254
+ resolved_price_type
1255
+ volume_pricing_min_quantity
1106
1256
  product_variant {
1107
1257
  id
1108
1258
  sku
@@ -1156,10 +1306,28 @@ var START_GUEST_CHECKOUT = (
1156
1306
  total_with_discount
1157
1307
  created_at
1158
1308
  updated_at
1309
+ volume_pricing {
1310
+ enabled
1311
+ eligibleQuantity
1312
+ appliedPriceType
1313
+ appliedMinQuantity
1314
+ nextPriceType
1315
+ nextMinQuantity
1316
+ remainingQuantity
1317
+ totalSavings
1318
+ }
1159
1319
  items {
1160
1320
  id
1161
1321
  product_variant_id
1162
1322
  quantity
1323
+ unit_price
1324
+ line_total
1325
+ base_unit_price
1326
+ unit_savings
1327
+ line_savings
1328
+ volume_pricing_eligible
1329
+ resolved_price_type
1330
+ volume_pricing_min_quantity
1163
1331
  product_variant {
1164
1332
  id
1165
1333
  sku
@@ -1209,10 +1377,28 @@ var SET_CART_ITEM = (
1209
1377
  total_with_discount
1210
1378
  created_at
1211
1379
  updated_at
1380
+ volume_pricing {
1381
+ enabled
1382
+ eligibleQuantity
1383
+ appliedPriceType
1384
+ appliedMinQuantity
1385
+ nextPriceType
1386
+ nextMinQuantity
1387
+ remainingQuantity
1388
+ totalSavings
1389
+ }
1212
1390
  items {
1213
1391
  id
1214
1392
  product_variant_id
1215
1393
  quantity
1394
+ unit_price
1395
+ line_total
1396
+ base_unit_price
1397
+ unit_savings
1398
+ line_savings
1399
+ volume_pricing_eligible
1400
+ resolved_price_type
1401
+ volume_pricing_min_quantity
1216
1402
  product_variant {
1217
1403
  id
1218
1404
  sku
@@ -1260,10 +1446,28 @@ var REMOVE_CART_ITEM = (
1260
1446
  total_with_discount
1261
1447
  created_at
1262
1448
  updated_at
1449
+ volume_pricing {
1450
+ enabled
1451
+ eligibleQuantity
1452
+ appliedPriceType
1453
+ appliedMinQuantity
1454
+ nextPriceType
1455
+ nextMinQuantity
1456
+ remainingQuantity
1457
+ totalSavings
1458
+ }
1263
1459
  items {
1264
1460
  id
1265
1461
  product_variant_id
1266
1462
  quantity
1463
+ unit_price
1464
+ line_total
1465
+ base_unit_price
1466
+ unit_savings
1467
+ line_savings
1468
+ volume_pricing_eligible
1469
+ resolved_price_type
1470
+ volume_pricing_min_quantity
1267
1471
  product_variant {
1268
1472
  id
1269
1473
  sku
@@ -1285,6 +1489,19 @@ var REMOVE_CART_ITEM = (
1285
1489
  }
1286
1490
  `
1287
1491
  );
1492
+ var DISCARD_CART = (
1493
+ /* GraphQL */
1494
+ `
1495
+ mutation DiscardCart($orderId: uuid!, $cartToken: String!) {
1496
+ websiteDiscardCart(order_id: $orderId, cart_token: $cartToken) {
1497
+ success
1498
+ order_id
1499
+ website_cart_status
1500
+ errors
1501
+ }
1502
+ }
1503
+ `
1504
+ );
1288
1505
  var CHECKOUT = (
1289
1506
  /* GraphQL */
1290
1507
  `
@@ -2655,7 +2872,6 @@ function mapRelatedProduct(raw, storageUrl) {
2655
2872
  slug: raw.related_product.slug,
2656
2873
  name: raw.related_product.name,
2657
2874
  sku: raw.related_product.sku,
2658
- displayLabel: raw.display_label,
2659
2875
  sortOrder: raw.sort_order ?? 0,
2660
2876
  media: raw.related_product.product_media.map(
2661
2877
  (m) => mapMedia(m, storageUrl)
@@ -2666,6 +2882,23 @@ function mapProduct(raw, storageUrl, labels, chainId) {
2666
2882
  const detailVariants = raw.product_variants.map((variant) => mapVariant(variant, labels, chainId)).filter((variant) => variant.pricing.currentPrice != null);
2667
2883
  const listingVariantIds = raw.listing_variant_ids ? new Set(raw.listing_variant_ids) : null;
2668
2884
  const variants = listingVariantIds ? detailVariants.filter((variant) => listingVariantIds.has(variant.id)) : detailVariants;
2885
+ const rawOptionGroup = raw.product_option_group_members?.[0]?.group;
2886
+ const optionGroup = rawOptionGroup && rawOptionGroup.members.length >= 2 ? {
2887
+ id: rawOptionGroup.id,
2888
+ title: rawOptionGroup.display_title,
2889
+ members: rawOptionGroup.members.map((member) => ({
2890
+ id: member.id,
2891
+ productId: member.product_id,
2892
+ slug: member.product.slug,
2893
+ name: member.product.name,
2894
+ sku: member.product.sku,
2895
+ displayLabel: member.display_label,
2896
+ sortOrder: member.sort_order ?? 0,
2897
+ media: member.product.product_media.map(
2898
+ (media) => mapMedia(media, storageUrl)
2899
+ )
2900
+ }))
2901
+ } : null;
2669
2902
  return {
2670
2903
  id: raw.id,
2671
2904
  name: raw.name,
@@ -2690,7 +2923,9 @@ function mapProduct(raw, storageUrl, labels, chainId) {
2690
2923
  ),
2691
2924
  relatedProducts: (raw.product_related_products ?? []).map(
2692
2925
  (relation) => mapRelatedProduct(relation, storageUrl)
2693
- )
2926
+ ),
2927
+ optionGroup,
2928
+ specification: raw.product_specification ?? null
2694
2929
  };
2695
2930
  }
2696
2931
  function mapCategory(raw) {
@@ -3021,7 +3256,7 @@ function hasMeaningfulCategoryListFilters(filters) {
3021
3256
  )
3022
3257
  );
3023
3258
  }
3024
- async function getWebsiteAvailabilityByVariantId(graphqlUrl, getHeaders, variantIds, chainId) {
3259
+ async function getWebsiteAvailabilityByVariantId(graphqlUrl, getHeaders, variantIds, chainId, websiteId) {
3025
3260
  const uniqueVariantIds = uniqueValues(variantIds);
3026
3261
  const availabilityByVariantId = /* @__PURE__ */ new Map();
3027
3262
  for (const chunk of chunkArray(
@@ -3031,7 +3266,7 @@ async function getWebsiteAvailabilityByVariantId(graphqlUrl, getHeaders, variant
3031
3266
  const data = await executeGraphQL(
3032
3267
  graphqlUrl,
3033
3268
  CHECK_AVAILABILITY,
3034
- { variantIds: chunk, chainId },
3269
+ { variantIds: chunk, chainId, websiteId },
3035
3270
  getHeaders
3036
3271
  );
3037
3272
  for (const item of data.websiteProductAvailability ?? []) {
@@ -3042,7 +3277,7 @@ async function getWebsiteAvailabilityByVariantId(graphqlUrl, getHeaders, variant
3042
3277
  }
3043
3278
  return availabilityByVariantId;
3044
3279
  }
3045
- async function filterProductsForWebsiteStock(graphqlUrl, getHeaders, products, chainId) {
3280
+ async function filterProductsForWebsiteStock(graphqlUrl, getHeaders, products, chainId, websiteId) {
3046
3281
  const variantIds = products.flatMap(
3047
3282
  (product) => product.variants.map((variant) => variant.id)
3048
3283
  );
@@ -3053,7 +3288,8 @@ async function filterProductsForWebsiteStock(graphqlUrl, getHeaders, products, c
3053
3288
  graphqlUrl,
3054
3289
  getHeaders,
3055
3290
  variantIds,
3056
- chainId
3291
+ chainId,
3292
+ websiteId
3057
3293
  )).entries()
3058
3294
  ).filter(([, item]) => item.inStock).map(([variantId]) => variantId)
3059
3295
  );
@@ -3071,7 +3307,7 @@ async function filterProductsForWebsiteStock(graphqlUrl, getHeaders, products, c
3071
3307
  )
3072
3308
  })).filter((product) => product.variants.length > 0);
3073
3309
  }
3074
- async function filterSeoProductsForWebsiteStock(graphqlUrl, getHeaders, products, chainId) {
3310
+ async function filterSeoProductsForWebsiteStock(graphqlUrl, getHeaders, products, chainId, websiteId) {
3075
3311
  const variantIds = products.flatMap(
3076
3312
  (product) => product.product_variants.map((variant) => variant.id)
3077
3313
  );
@@ -3082,7 +3318,8 @@ async function filterSeoProductsForWebsiteStock(graphqlUrl, getHeaders, products
3082
3318
  graphqlUrl,
3083
3319
  getHeaders,
3084
3320
  variantIds,
3085
- chainId
3321
+ chainId,
3322
+ websiteId
3086
3323
  )).entries()
3087
3324
  ).filter(([, item]) => item.inStock).map(([variantId]) => variantId)
3088
3325
  );
@@ -3113,6 +3350,32 @@ function mapProductSeoEntry(raw, storageUrl) {
3113
3350
  };
3114
3351
  }
3115
3352
  function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
3353
+ const loadProductSpecifications = async (productIds) => {
3354
+ const ctx = getContext();
3355
+ const uniqueProductIds = [...new Set(productIds)];
3356
+ const specifications = {};
3357
+ for (let offset = 0; offset < uniqueProductIds.length; offset += 100) {
3358
+ const batch = uniqueProductIds.slice(offset, offset + 100);
3359
+ try {
3360
+ const data = await executeGraphQL(
3361
+ graphqlUrl,
3362
+ WEBSITE_GET_PRODUCT_SPECIFICATIONS,
3363
+ { websiteId: ctx.websiteId, productIds: batch },
3364
+ getHeaders
3365
+ );
3366
+ if (!data.result?.success) continue;
3367
+ Object.assign(
3368
+ specifications,
3369
+ parseActionJson(
3370
+ data.result.specifications,
3371
+ {}
3372
+ )
3373
+ );
3374
+ } catch {
3375
+ }
3376
+ }
3377
+ return specifications;
3378
+ };
3116
3379
  const mapSingleProductResult = async (data) => {
3117
3380
  const ctx = getContext();
3118
3381
  if (!ctx.chainId) {
@@ -3136,12 +3399,15 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
3136
3399
  graphqlUrl,
3137
3400
  getHeaders,
3138
3401
  [mappedProduct],
3139
- ctx.chainId
3402
+ ctx.chainId,
3403
+ ctx.websiteId
3140
3404
  );
3141
3405
  if (!filteredProducts[0]) return null;
3406
+ const specifications = await loadProductSpecifications([product.id]);
3142
3407
  return {
3143
3408
  ...mappedProduct,
3144
- variants: getProductDetailVariants(mappedProduct)
3409
+ variants: getProductDetailVariants(mappedProduct),
3410
+ specification: specifications[product.id] ?? null
3145
3411
  };
3146
3412
  };
3147
3413
  const loadCatalogPage = async (opts, retryRateLimits = false) => {
@@ -3162,6 +3428,19 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
3162
3428
  min_price: opts.minPrice,
3163
3429
  max_price: opts.maxPrice,
3164
3430
  attributes_by_slug: opts.attributesBySlug,
3431
+ technical_by_field: opts.technicalByField ? Object.fromEntries(
3432
+ Object.entries(opts.technicalByField).map(([fieldId, filter]) => [
3433
+ fieldId,
3434
+ {
3435
+ option_ids: filter.optionIds,
3436
+ boolean_values: filter.booleanValues,
3437
+ min: filter.min,
3438
+ max: filter.max,
3439
+ date_from: filter.dateFrom,
3440
+ date_to: filter.dateTo
3441
+ }
3442
+ ])
3443
+ ) : void 0,
3165
3444
  sort_by: opts.sortBy,
3166
3445
  limit: opts.limit,
3167
3446
  offset: opts.offset
@@ -3353,7 +3632,8 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
3353
3632
  graphqlUrl,
3354
3633
  getHeaders,
3355
3634
  data.products ?? [],
3356
- ctx.chainId
3635
+ ctx.chainId,
3636
+ ctx.websiteId
3357
3637
  );
3358
3638
  const entries = availableProducts.slice(0, limit).map((product) => mapProductSeoEntry(product, storageUrl)).filter((entry) => Boolean(entry));
3359
3639
  return {
@@ -3429,8 +3709,16 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
3429
3709
  (data.products ?? []).map(
3430
3710
  (p) => mapProduct(p, storageUrl, publicAttributeLabels, chainId)
3431
3711
  ),
3432
- chainId
3712
+ chainId,
3713
+ ctx.websiteId
3714
+ );
3715
+ const specifications = await loadProductSpecifications(
3716
+ products.map((product) => product.id)
3433
3717
  );
3718
+ products = products.map((product) => ({
3719
+ ...product,
3720
+ specification: specifications[product.id] ?? null
3721
+ }));
3434
3722
  if (opts?.minPrice !== void 0 || opts?.maxPrice !== void 0) {
3435
3723
  products = products.filter(
3436
3724
  (product) => product.variants.some((variant) => {
@@ -3541,7 +3829,8 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
3541
3829
  graphqlUrl,
3542
3830
  getHeaders,
3543
3831
  variantIds,
3544
- chainId
3832
+ chainId,
3833
+ ctx.websiteId
3545
3834
  );
3546
3835
  const applicableCategories = /* @__PURE__ */ new Set();
3547
3836
  const applicableSubcategoryIds = /* @__PURE__ */ new Set();
@@ -3659,7 +3948,8 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
3659
3948
  graphqlUrl,
3660
3949
  getHeaders,
3661
3950
  variantIds,
3662
- chainId
3951
+ chainId,
3952
+ ctx.websiteId
3663
3953
  );
3664
3954
  const publicAttributeLabels = buildPublicAttributeLabels(
3665
3955
  data.catalog_attribute_lists
@@ -3764,7 +4054,8 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
3764
4054
  min: minPrice,
3765
4055
  max: maxPrice
3766
4056
  },
3767
- attributes
4057
+ attributes,
4058
+ technical: []
3768
4059
  };
3769
4060
  },
3770
4061
  async getSearchSuggestions(opts) {
@@ -4040,6 +4331,21 @@ function useCart() {
4040
4331
  },
4041
4332
  [client, refreshCart]
4042
4333
  );
4334
+ const clear = useCallback(async () => {
4335
+ if (!client) throw new NotInitializedError();
4336
+ setLoading(true);
4337
+ try {
4338
+ await client.cart.clear();
4339
+ setCart(null);
4340
+ setError(null);
4341
+ } catch (err) {
4342
+ const nextError = err instanceof Error ? err : new Error(String(err));
4343
+ setError(nextError);
4344
+ throw nextError;
4345
+ } finally {
4346
+ setLoading(false);
4347
+ }
4348
+ }, [client]);
4043
4349
  const getOrCreate = useCallback(
4044
4350
  async (contact, fulfillmentFamily) => {
4045
4351
  if (!client) throw new NotInitializedError();
@@ -4067,6 +4373,7 @@ function useCart() {
4067
4373
  addItem,
4068
4374
  updateItem,
4069
4375
  removeItem,
4376
+ clear,
4070
4377
  refresh: refreshCart
4071
4378
  };
4072
4379
  }
@@ -4160,6 +4467,7 @@ var ALLOWED_MARKETING_PARAMETERS = /* @__PURE__ */ new Set([
4160
4467
  "utm_content",
4161
4468
  "utm_source_platform",
4162
4469
  "utm_id",
4470
+ "bacano_origin",
4163
4471
  "gclid",
4164
4472
  "gbraid",
4165
4473
  "wbraid",
@@ -4194,6 +4502,7 @@ function getAllowedMarketingParameters(input, baseUrl) {
4194
4502
  var STORAGE_PREFIX = "bacano:marketing:attribution:v1";
4195
4503
  var GA4_CLIENT_ID_PATTERN = /^\d+\.\d+$/;
4196
4504
  var META_BROWSER_ID_PATTERN = /^fb\.\d+\.\d+\.[A-Za-z0-9_-]+$/;
4505
+ var SALES_ORIGIN_KEY_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
4197
4506
  function storageKey(websiteId) {
4198
4507
  return `${STORAGE_PREFIX}:${websiteId}`;
4199
4508
  }
@@ -4236,6 +4545,10 @@ function optional(value) {
4236
4545
  const normalized = value?.trim();
4237
4546
  return normalized ? normalized.slice(0, 500) : void 0;
4238
4547
  }
4548
+ function validSalesOriginKey(value) {
4549
+ const normalized = value?.trim().toLowerCase();
4550
+ return normalized && normalized.length <= 100 && SALES_ORIGIN_KEY_PATTERN.test(normalized) ? normalized : void 0;
4551
+ }
4239
4552
  function buildTouchpoint() {
4240
4553
  if (typeof window === "undefined") return null;
4241
4554
  const params = getAllowedMarketingParameters(window.location.href);
@@ -4259,6 +4572,7 @@ function buildTouchpoint() {
4259
4572
  content: optional(params.utm_content),
4260
4573
  sourcePlatform: optional(params.utm_source_platform),
4261
4574
  campaignId: optional(params.utm_id),
4575
+ salesOriginKey: validSalesOriginKey(params.bacano_origin),
4262
4576
  gclid: optional(params.gclid),
4263
4577
  gbraid: optional(params.gbraid),
4264
4578
  wbraid: optional(params.wbraid),
@@ -4269,7 +4583,7 @@ function buildTouchpoint() {
4269
4583
  }
4270
4584
  function isNonDirect(touchpoint) {
4271
4585
  return Boolean(
4272
- touchpoint.source || touchpoint.medium || touchpoint.campaign || touchpoint.gclid || touchpoint.gbraid || touchpoint.wbraid || touchpoint.fbclid || touchpoint.ttclid || touchpoint.msclkid || touchpoint.referrerHost
4586
+ touchpoint.salesOriginKey || touchpoint.source || touchpoint.medium || touchpoint.campaign || touchpoint.gclid || touchpoint.gbraid || touchpoint.wbraid || touchpoint.fbclid || touchpoint.ttclid || touchpoint.msclkid || touchpoint.referrerHost
4273
4587
  );
4274
4588
  }
4275
4589
  function toPublic(stored) {
@@ -4360,6 +4674,7 @@ function serializeTouchpoint(touchpoint) {
4360
4674
  content: touchpoint.content,
4361
4675
  source_platform: touchpoint.sourcePlatform,
4362
4676
  campaign_id: touchpoint.campaignId,
4677
+ sales_origin_key: touchpoint.salesOriginKey,
4363
4678
  gclid: touchpoint.gclid,
4364
4679
  gbraid: touchpoint.gbraid,
4365
4680
  wbraid: touchpoint.wbraid,
@@ -4883,6 +5198,10 @@ async function getWebsitePublicConfig(options) {
4883
5198
  consent_policy: null,
4884
5199
  destinations: { ga4: null, meta: null, clarity: null }
4885
5200
  };
5201
+ const branding = raw.branding ?? {
5202
+ schema_version: 1,
5203
+ favicon: null
5204
+ };
4886
5205
  const legal = raw.legal ?? {
4887
5206
  schema_version: 1,
4888
5207
  version: 0,
@@ -4930,6 +5249,11 @@ async function getWebsitePublicConfig(options) {
4930
5249
  desktop_image: null,
4931
5250
  mobile_image: null
4932
5251
  };
5252
+ const volumePricing = raw.volume_pricing ?? {
5253
+ schema_version: 1,
5254
+ enabled: false,
5255
+ tiers: []
5256
+ };
4933
5257
  return {
4934
5258
  websiteId: raw.website_id,
4935
5259
  companyId: raw.company_id,
@@ -4955,6 +5279,20 @@ async function getWebsitePublicConfig(options) {
4955
5279
  })
4956
5280
  )
4957
5281
  },
5282
+ branding: {
5283
+ schemaVersion: branding.schema_version,
5284
+ favicon: branding.favicon ? {
5285
+ fileId: branding.favicon.file_id,
5286
+ name: branding.favicon.name,
5287
+ altText: branding.favicon.alt_text,
5288
+ width: branding.favicon.width,
5289
+ height: branding.favicon.height,
5290
+ originalUrl: buildStorageFileUrl(
5291
+ `${apiUrl}/storage/v1`,
5292
+ branding.favicon.file_id
5293
+ )
5294
+ } : null
5295
+ },
4958
5296
  marketing: {
4959
5297
  schemaVersion: marketing.schema_version,
4960
5298
  enabled: marketing.enabled,
@@ -5011,6 +5349,14 @@ async function getWebsitePublicConfig(options) {
5011
5349
  message: construction.message,
5012
5350
  desktopImage: mapConstructionImage(construction.desktop_image),
5013
5351
  mobileImage: mapConstructionImage(construction.mobile_image)
5352
+ },
5353
+ volumePricing: {
5354
+ schemaVersion: volumePricing.schema_version,
5355
+ enabled: volumePricing.enabled,
5356
+ tiers: volumePricing.tiers.map((tier) => ({
5357
+ priceType: tier.price_type,
5358
+ minQuantity: tier.min_quantity
5359
+ }))
5014
5360
  }
5015
5361
  };
5016
5362
  }
@@ -5057,7 +5403,8 @@ function createAvailabilityModule(graphqlUrl, getHeaders, getContext) {
5057
5403
  CHECK_AVAILABILITY,
5058
5404
  {
5059
5405
  variantIds: chunk,
5060
- chainId: ctx.chainId
5406
+ chainId: ctx.chainId,
5407
+ websiteId: ctx.websiteId
5061
5408
  },
5062
5409
  getHeaders
5063
5410
  );
@@ -5070,12 +5417,40 @@ function createAvailabilityModule(graphqlUrl, getHeaders, getContext) {
5070
5417
  };
5071
5418
  }
5072
5419
 
5420
+ // libs/sdk/src/modules/cart-errors.ts
5421
+ var RECOVERABLE_CART_SESSION_ERRORS = /* @__PURE__ */ new Set([
5422
+ "INVALID_CART_TOKEN",
5423
+ "CART_NOT_FOUND"
5424
+ ]);
5425
+ function firstStableErrorCode(errors) {
5426
+ const candidate = errors?.[0]?.split(":", 1)[0]?.trim();
5427
+ return candidate && /^[A-Z][A-Z0-9_]+$/.test(candidate) ? candidate : void 0;
5428
+ }
5429
+ function getStableCartActionErrorCode(errors, fallbackCode) {
5430
+ return firstStableErrorCode(errors) ?? fallbackCode;
5431
+ }
5432
+ function hasRecoverableCartSessionError(errors) {
5433
+ return Boolean(
5434
+ errors?.some(
5435
+ (error) => RECOVERABLE_CART_SESSION_ERRORS.has(error.split(":", 1)[0]?.trim())
5436
+ )
5437
+ );
5438
+ }
5439
+
5073
5440
  // libs/sdk/src/modules/cart.ts
5074
5441
  function mapCartItem(raw) {
5075
5442
  return {
5076
5443
  id: raw.id,
5077
5444
  productVariantId: raw.product_variant_id,
5078
5445
  quantity: raw.quantity,
5446
+ unitPrice: raw.unit_price ?? null,
5447
+ lineTotal: raw.line_total ?? null,
5448
+ baseUnitPrice: raw.base_unit_price ?? raw.unit_price ?? null,
5449
+ unitSavings: raw.unit_savings ?? 0,
5450
+ lineSavings: raw.line_savings ?? 0,
5451
+ volumePricingEligible: raw.volume_pricing_eligible ?? false,
5452
+ resolvedPriceType: raw.resolved_price_type ?? null,
5453
+ volumePricingMinQuantity: raw.volume_pricing_min_quantity ?? null,
5079
5454
  productVariant: {
5080
5455
  sku: raw.product_variant.sku,
5081
5456
  product: raw.product_variant.product,
@@ -5101,13 +5476,29 @@ function mapCart(raw, cartToken) {
5101
5476
  },
5102
5477
  status: raw.status,
5103
5478
  createdAt: raw.created_at,
5104
- updatedAt: raw.updated_at
5479
+ updatedAt: raw.updated_at,
5480
+ volumePricing: raw.volume_pricing ? {
5481
+ ...raw.volume_pricing,
5482
+ totalSavings: raw.volume_pricing.totalSavings ?? 0
5483
+ } : {
5484
+ enabled: false,
5485
+ eligibleQuantity: 0,
5486
+ appliedPriceType: null,
5487
+ appliedMinQuantity: null,
5488
+ nextPriceType: null,
5489
+ nextMinQuantity: null,
5490
+ remainingQuantity: null,
5491
+ totalSavings: 0
5492
+ }
5105
5493
  };
5106
5494
  }
5107
5495
  function assertActionSuccess(result, fallbackCode) {
5108
5496
  if (!result.success || !result.order_id || !result.cart_token || !result.cart) {
5109
5497
  const message = result.errors?.join("; ") || "Cart action failed";
5110
- throw new WebsiteError(message, fallbackCode);
5498
+ throw new WebsiteError(
5499
+ message,
5500
+ getStableCartActionErrorCode(result.errors, fallbackCode)
5501
+ );
5111
5502
  }
5112
5503
  }
5113
5504
  function requireCartSession(storage) {
@@ -5125,7 +5516,7 @@ function createCartModule(graphqlUrl, getHeaders, getContext, cartSessionStorage
5125
5516
  const data = await executeGraphQL(graphqlUrl, GET_CART, { orderId, cartToken }, getHeaders);
5126
5517
  const result = data.websiteGetCart;
5127
5518
  if (!result.success) {
5128
- if (result.errors?.includes("CART_NOT_FOUND")) {
5519
+ if (hasRecoverableCartSessionError(result.errors)) {
5129
5520
  cartSessionStorage.clear();
5130
5521
  return null;
5131
5522
  }
@@ -5178,24 +5569,31 @@ function createCartModule(graphqlUrl, getHeaders, getContext, cartSessionStorage
5178
5569
  "NO_CHAIN"
5179
5570
  );
5180
5571
  }
5572
+ const requestStartCheckout = async (cartToken) => {
5573
+ const data = await executeGraphQL(
5574
+ graphqlUrl,
5575
+ START_GUEST_CHECKOUT,
5576
+ {
5577
+ websiteId: ctx.websiteId,
5578
+ cartToken,
5579
+ contact: input.contact,
5580
+ fulfillmentFamily: input.fulfillmentFamily,
5581
+ items: input.items.map((item) => ({
5582
+ product_variant_id: item.productVariantId,
5583
+ quantity: item.quantity
5584
+ })),
5585
+ marketingAttribution: input.marketingAttribution ? serializeWebsiteMarketingAttribution(input.marketingAttribution) : void 0
5586
+ },
5587
+ getHeaders
5588
+ );
5589
+ return data.websiteStartGuestCheckout;
5590
+ };
5181
5591
  const existing = cartSessionStorage.get();
5182
- const data = await executeGraphQL(
5183
- graphqlUrl,
5184
- START_GUEST_CHECKOUT,
5185
- {
5186
- websiteId: ctx.websiteId,
5187
- cartToken: existing?.cartToken,
5188
- contact: input.contact,
5189
- fulfillmentFamily: input.fulfillmentFamily,
5190
- items: input.items.map((item) => ({
5191
- product_variant_id: item.productVariantId,
5192
- quantity: item.quantity
5193
- })),
5194
- marketingAttribution: input.marketingAttribution ? serializeWebsiteMarketingAttribution(input.marketingAttribution) : void 0
5195
- },
5196
- getHeaders
5197
- );
5198
- const result = data.websiteStartGuestCheckout;
5592
+ let result = await requestStartCheckout(existing?.cartToken);
5593
+ if (existing && !result.success && hasRecoverableCartSessionError(result.errors)) {
5594
+ cartSessionStorage.clear();
5595
+ result = await requestStartCheckout();
5596
+ }
5199
5597
  assertActionSuccess(result, "CHECKOUT_START_ERROR");
5200
5598
  cartSessionStorage.set({
5201
5599
  orderId: result.order_id,
@@ -5218,7 +5616,7 @@ function createCartModule(graphqlUrl, getHeaders, getContext, cartSessionStorage
5218
5616
  const session = cartSessionStorage.get();
5219
5617
  if (!session) {
5220
5618
  throw new WebsiteError(
5221
- "Start checkout with contact before syncing cart items to Bacano",
5619
+ "Create or recover a Bacano cart session before syncing cart items",
5222
5620
  "CART_SESSION_MISSING"
5223
5621
  );
5224
5622
  }
@@ -5286,11 +5684,37 @@ function createCartModule(graphqlUrl, getHeaders, getContext, cartSessionStorage
5286
5684
  );
5287
5685
  const result = data.websiteRemoveCartItem;
5288
5686
  assertActionSuccess(result, "CART_ITEM_ERROR");
5687
+ const cart = mapCart(result.cart, result.cart_token);
5688
+ if (cart.items.length === 0) {
5689
+ cartSessionStorage.clear();
5690
+ return cart;
5691
+ }
5289
5692
  cartSessionStorage.set({
5290
5693
  orderId: result.order_id,
5291
5694
  cartToken: result.cart_token
5292
5695
  });
5293
- return mapCart(result.cart, result.cart_token);
5696
+ return cart;
5697
+ },
5698
+ async clear() {
5699
+ const session = cartSessionStorage.get();
5700
+ if (!session) return;
5701
+ const data = await executeGraphQL(
5702
+ graphqlUrl,
5703
+ DISCARD_CART,
5704
+ {
5705
+ orderId: session.orderId,
5706
+ cartToken: session.cartToken
5707
+ },
5708
+ getHeaders
5709
+ );
5710
+ const result = data.websiteDiscardCart;
5711
+ if (!result.success && !hasRecoverableCartSessionError(result.errors)) {
5712
+ throw new WebsiteError(
5713
+ result.errors?.join("; ") || "Cart could not be discarded",
5714
+ getStableCartActionErrorCode(result.errors, "CART_DISCARD_ERROR")
5715
+ );
5716
+ }
5717
+ cartSessionStorage.clear();
5294
5718
  }
5295
5719
  };
5296
5720
  }
@@ -5371,6 +5795,14 @@ function mapCartItem2(raw) {
5371
5795
  id: raw.id,
5372
5796
  productVariantId: raw.product_variant_id,
5373
5797
  quantity: raw.quantity,
5798
+ unitPrice: raw.unit_price ?? null,
5799
+ lineTotal: raw.line_total ?? null,
5800
+ baseUnitPrice: raw.base_unit_price ?? raw.unit_price ?? null,
5801
+ unitSavings: raw.unit_savings ?? 0,
5802
+ lineSavings: raw.line_savings ?? 0,
5803
+ volumePricingEligible: raw.volume_pricing_eligible ?? false,
5804
+ resolvedPriceType: raw.resolved_price_type ?? null,
5805
+ volumePricingMinQuantity: raw.volume_pricing_min_quantity ?? null,
5374
5806
  productVariant: {
5375
5807
  sku: raw.product_variant.sku,
5376
5808
  product: raw.product_variant.product,
@@ -5395,7 +5827,20 @@ function mapCart2(raw) {
5395
5827
  },
5396
5828
  status: raw.status,
5397
5829
  createdAt: raw.created_at,
5398
- updatedAt: raw.updated_at
5830
+ updatedAt: raw.updated_at,
5831
+ volumePricing: raw.volume_pricing ? {
5832
+ ...raw.volume_pricing,
5833
+ totalSavings: raw.volume_pricing.totalSavings ?? 0
5834
+ } : {
5835
+ enabled: false,
5836
+ eligibleQuantity: 0,
5837
+ appliedPriceType: null,
5838
+ appliedMinQuantity: null,
5839
+ nextPriceType: null,
5840
+ nextMinQuantity: null,
5841
+ remainingQuantity: null,
5842
+ totalSavings: 0
5843
+ }
5399
5844
  };
5400
5845
  }
5401
5846
  function mapCouponResult(raw) {
@@ -6585,7 +7030,8 @@ function createBacanoClient(config) {
6585
7030
  setItem: cartModule.setItem.bind(cartModule),
6586
7031
  addItem: cartModule.addItem.bind(cartModule),
6587
7032
  updateItem: cartModule.updateItem.bind(cartModule),
6588
- removeItem: cartModule.removeItem.bind(cartModule)
7033
+ removeItem: cartModule.removeItem.bind(cartModule),
7034
+ clear: cartModule.clear.bind(cartModule)
6589
7035
  },
6590
7036
  checkout: {
6591
7037
  getLocationOptions: checkoutModule.getLocationOptions.bind(checkoutModule),
@@ -6667,7 +7113,7 @@ function BacanoProvider({
6667
7113
 
6668
7114
  // libs/sdk/src/react/website-gate.tsx
6669
7115
  import { useEffect as useEffect4, useState as useState4 } from "react";
6670
- import { jsx as jsx3, jsxs } from "react/jsx-runtime";
7116
+ import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
6671
7117
  var PREVIEW_HASH_KEY = "bacano-preview";
6672
7118
  function previewStorageKey(websiteSlug) {
6673
7119
  return `bacano:construction-preview:${websiteSlug}`;
@@ -6709,13 +7155,14 @@ function clearPreviewToken(websiteSlug) {
6709
7155
  function BacanoConstructionScreen({
6710
7156
  construction
6711
7157
  }) {
6712
- const desktop = construction.desktopImage;
7158
+ const desktop = construction.desktopImage ?? construction.mobileImage;
6713
7159
  const mobile = construction.mobileImage ?? desktop;
6714
- if (!desktop) return null;
7160
+ const title = construction.title?.trim() || "Sitio en construcci\xF3n";
7161
+ const message = construction.message?.trim() || null;
6715
7162
  return /* @__PURE__ */ jsxs(
6716
7163
  "main",
6717
7164
  {
6718
- "aria-label": construction.title ?? "Sitio en construcci\xF3n",
7165
+ "aria-label": title,
6719
7166
  style: {
6720
7167
  background: "#111827",
6721
7168
  color: "#ffffff",
@@ -6727,7 +7174,7 @@ function BacanoConstructionScreen({
6727
7174
  zIndex: 2147483647
6728
7175
  },
6729
7176
  children: [
6730
- /* @__PURE__ */ jsxs("picture", { style: { inset: 0, position: "absolute" }, children: [
7177
+ desktop ? /* @__PURE__ */ jsxs("picture", { style: { inset: 0, position: "absolute" }, children: [
6731
7178
  mobile ? /* @__PURE__ */ jsx3(
6732
7179
  "source",
6733
7180
  {
@@ -6739,7 +7186,7 @@ function BacanoConstructionScreen({
6739
7186
  /* @__PURE__ */ jsx3(
6740
7187
  "img",
6741
7188
  {
6742
- alt: desktop.altText ?? construction.title ?? "Sitio en construcci\xF3n",
7189
+ alt: desktop.altText ?? title,
6743
7190
  sizes: "100vw",
6744
7191
  src: desktop.heroUrl || desktop.originalUrl,
6745
7192
  srcSet: formatMediaSrcSet(desktop.responsiveSources),
@@ -6751,12 +7198,12 @@ function BacanoConstructionScreen({
6751
7198
  }
6752
7199
  }
6753
7200
  )
6754
- ] }),
6755
- construction.title || construction.message ? /* @__PURE__ */ jsxs(
7201
+ ] }) : null,
7202
+ /* @__PURE__ */ jsxs(
6756
7203
  "section",
6757
7204
  {
6758
7205
  style: {
6759
- background: "rgba(0, 0, 0, 0.48)",
7206
+ background: desktop ? "rgba(0, 0, 0, 0.48)" : "transparent",
6760
7207
  borderRadius: "1rem",
6761
7208
  left: "50%",
6762
7209
  maxWidth: "42rem",
@@ -6768,17 +7215,11 @@ function BacanoConstructionScreen({
6768
7215
  width: "calc(100% - 2rem)"
6769
7216
  },
6770
7217
  children: [
6771
- construction.title ? /* @__PURE__ */ jsx3("h1", { style: { fontSize: "clamp(1.75rem, 5vw, 3.5rem)", margin: 0 }, children: construction.title }) : null,
6772
- construction.message ? /* @__PURE__ */ jsx3(
6773
- "p",
6774
- {
6775
- style: { fontSize: "clamp(1rem, 2vw, 1.25rem)", marginBottom: 0 },
6776
- children: construction.message
6777
- }
6778
- ) : null
7218
+ /* @__PURE__ */ jsx3("h1", { style: { fontSize: "clamp(1.75rem, 5vw, 3.5rem)", margin: 0 }, children: title }),
7219
+ message ? /* @__PURE__ */ jsx3("p", { style: { fontSize: "clamp(1rem, 2vw, 1.25rem)", marginBottom: 0 }, children: message }) : null
6779
7220
  ]
6780
7221
  }
6781
- ) : null
7222
+ )
6782
7223
  ]
6783
7224
  }
6784
7225
  );
@@ -6827,7 +7268,12 @@ function BacanoWebsiteGate({
6827
7268
  if (isConstructionEnforced && publicConfig) {
6828
7269
  return constructionFallback ? constructionFallback(publicConfig.construction) : /* @__PURE__ */ jsx3(BacanoConstructionScreen, { construction: publicConfig.construction });
6829
7270
  }
6830
- if (isLoading) return loadingFallback;
7271
+ if (isLoading) {
7272
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
7273
+ /* @__PURE__ */ jsx3("noscript", { "data-bacano-static-storefront": "true", children }),
7274
+ loadingFallback
7275
+ ] });
7276
+ }
6831
7277
  return children;
6832
7278
  }
6833
7279
  export {