@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.cjs CHANGED
@@ -342,7 +342,6 @@ var GET_PRODUCTS = (
342
342
  ) {
343
343
  id
344
344
  related_product_id
345
- display_label
346
345
  sort_order
347
346
  related_product {
348
347
  id
@@ -364,6 +363,52 @@ var GET_PRODUCTS = (
364
363
  }
365
364
  }
366
365
  }
366
+ product_option_group_members(limit: 1) {
367
+ group {
368
+ id
369
+ display_title
370
+ members(
371
+ where: {
372
+ product: {
373
+ product_website_publications: {
374
+ website_id: { _eq: $websiteId }
375
+ is_visible: { _eq: true }
376
+ }
377
+ chain_products: {
378
+ chain_id: { _eq: $chainId }
379
+ is_enabled: { _eq: true }
380
+ status: { _eq: active }
381
+ }
382
+ }
383
+ }
384
+ order_by: { sort_order: asc }
385
+ ) {
386
+ id
387
+ product_id
388
+ display_label
389
+ sort_order
390
+ product {
391
+ id
392
+ slug
393
+ name
394
+ sku
395
+ product_media(
396
+ where: { file: { mimeType: { _ilike: "image/%" } } }
397
+ order_by: { order: asc }
398
+ limit: 1
399
+ ) {
400
+ id
401
+ file_id
402
+ order
403
+ file {
404
+ mimeType
405
+ name
406
+ }
407
+ }
408
+ }
409
+ }
410
+ }
411
+ }
367
412
  product_attributes {
368
413
  id
369
414
  value
@@ -448,6 +493,52 @@ var GET_PRODUCT_BY_PK = (
448
493
  limit: 1
449
494
  ) {
450
495
  id
496
+ product_option_group_members(limit: 1) {
497
+ group {
498
+ id
499
+ display_title
500
+ members(
501
+ where: {
502
+ product: {
503
+ product_website_publications: {
504
+ website_id: { _eq: $websiteId }
505
+ is_visible: { _eq: true }
506
+ }
507
+ chain_products: {
508
+ chain_id: { _eq: $chainId }
509
+ is_enabled: { _eq: true }
510
+ status: { _eq: active }
511
+ }
512
+ }
513
+ }
514
+ order_by: { sort_order: asc }
515
+ ) {
516
+ id
517
+ product_id
518
+ display_label
519
+ sort_order
520
+ product {
521
+ id
522
+ slug
523
+ name
524
+ sku
525
+ product_media(
526
+ where: { file: { mimeType: { _ilike: "image/%" } } }
527
+ order_by: { order: asc }
528
+ limit: 1
529
+ ) {
530
+ id
531
+ file_id
532
+ order
533
+ file {
534
+ mimeType
535
+ name
536
+ }
537
+ }
538
+ }
539
+ }
540
+ }
541
+ }
451
542
  name
452
543
  description
453
544
  long_description
@@ -534,7 +625,6 @@ var GET_PRODUCT_BY_PK = (
534
625
  ) {
535
626
  id
536
627
  related_product_id
537
- display_label
538
628
  sort_order
539
629
  related_product {
540
630
  id
@@ -908,6 +998,24 @@ var WEBSITE_GET_PRODUCT_RECOMMENDATIONS = (
908
998
  }
909
999
  `
910
1000
  );
1001
+ var WEBSITE_GET_PRODUCT_SPECIFICATIONS = (
1002
+ /* GraphQL */
1003
+ `
1004
+ mutation WebsiteGetProductSpecifications(
1005
+ $websiteId: uuid!
1006
+ $productIds: [uuid!]!
1007
+ ) {
1008
+ result: websiteGetProductSpecifications(
1009
+ website_id: $websiteId
1010
+ product_ids: $productIds
1011
+ ) {
1012
+ success
1013
+ specifications
1014
+ errors
1015
+ }
1016
+ }
1017
+ `
1018
+ );
911
1019
  var GET_BRANDS = (
912
1020
  /* GraphQL */
913
1021
  `
@@ -1016,10 +1124,15 @@ var WEBSITE_GET_PRODUCT_CONTENT = (
1016
1124
  var CHECK_AVAILABILITY = (
1017
1125
  /* GraphQL */
1018
1126
  `
1019
- query CheckAvailability($variantIds: [uuid!]!, $chainId: uuid!) {
1127
+ query CheckAvailability(
1128
+ $variantIds: [uuid!]!
1129
+ $chainId: uuid!
1130
+ $websiteId: uuid
1131
+ ) {
1020
1132
  websiteProductAvailability(
1021
1133
  product_variant_ids: $variantIds
1022
1134
  chain_id: $chainId
1135
+ website_id: $websiteId
1023
1136
  ) {
1024
1137
  product_variant_id
1025
1138
  in_stock
@@ -1075,6 +1188,7 @@ var GET_PRODUCT_LIVE_STATE = (
1075
1188
  websiteProductAvailability(
1076
1189
  product_variant_ids: $variantIds
1077
1190
  chain_id: $chainId
1191
+ website_id: $websiteId
1078
1192
  ) {
1079
1193
  product_variant_id
1080
1194
  in_stock
@@ -1112,10 +1226,28 @@ var CREATE_CART = (
1112
1226
  total_with_discount
1113
1227
  created_at
1114
1228
  updated_at
1229
+ volume_pricing {
1230
+ enabled
1231
+ eligibleQuantity
1232
+ appliedPriceType
1233
+ appliedMinQuantity
1234
+ nextPriceType
1235
+ nextMinQuantity
1236
+ remainingQuantity
1237
+ totalSavings
1238
+ }
1115
1239
  items {
1116
1240
  id
1117
1241
  product_variant_id
1118
1242
  quantity
1243
+ unit_price
1244
+ line_total
1245
+ base_unit_price
1246
+ unit_savings
1247
+ line_savings
1248
+ volume_pricing_eligible
1249
+ resolved_price_type
1250
+ volume_pricing_min_quantity
1119
1251
  product_variant {
1120
1252
  id
1121
1253
  sku
@@ -1155,10 +1287,28 @@ var GET_CART = (
1155
1287
  total_with_discount
1156
1288
  created_at
1157
1289
  updated_at
1290
+ volume_pricing {
1291
+ enabled
1292
+ eligibleQuantity
1293
+ appliedPriceType
1294
+ appliedMinQuantity
1295
+ nextPriceType
1296
+ nextMinQuantity
1297
+ remainingQuantity
1298
+ totalSavings
1299
+ }
1158
1300
  items {
1159
1301
  id
1160
1302
  product_variant_id
1161
1303
  quantity
1304
+ unit_price
1305
+ line_total
1306
+ base_unit_price
1307
+ unit_savings
1308
+ line_savings
1309
+ volume_pricing_eligible
1310
+ resolved_price_type
1311
+ volume_pricing_min_quantity
1162
1312
  product_variant {
1163
1313
  id
1164
1314
  sku
@@ -1212,10 +1362,28 @@ var START_GUEST_CHECKOUT = (
1212
1362
  total_with_discount
1213
1363
  created_at
1214
1364
  updated_at
1365
+ volume_pricing {
1366
+ enabled
1367
+ eligibleQuantity
1368
+ appliedPriceType
1369
+ appliedMinQuantity
1370
+ nextPriceType
1371
+ nextMinQuantity
1372
+ remainingQuantity
1373
+ totalSavings
1374
+ }
1215
1375
  items {
1216
1376
  id
1217
1377
  product_variant_id
1218
1378
  quantity
1379
+ unit_price
1380
+ line_total
1381
+ base_unit_price
1382
+ unit_savings
1383
+ line_savings
1384
+ volume_pricing_eligible
1385
+ resolved_price_type
1386
+ volume_pricing_min_quantity
1219
1387
  product_variant {
1220
1388
  id
1221
1389
  sku
@@ -1265,10 +1433,28 @@ var SET_CART_ITEM = (
1265
1433
  total_with_discount
1266
1434
  created_at
1267
1435
  updated_at
1436
+ volume_pricing {
1437
+ enabled
1438
+ eligibleQuantity
1439
+ appliedPriceType
1440
+ appliedMinQuantity
1441
+ nextPriceType
1442
+ nextMinQuantity
1443
+ remainingQuantity
1444
+ totalSavings
1445
+ }
1268
1446
  items {
1269
1447
  id
1270
1448
  product_variant_id
1271
1449
  quantity
1450
+ unit_price
1451
+ line_total
1452
+ base_unit_price
1453
+ unit_savings
1454
+ line_savings
1455
+ volume_pricing_eligible
1456
+ resolved_price_type
1457
+ volume_pricing_min_quantity
1272
1458
  product_variant {
1273
1459
  id
1274
1460
  sku
@@ -1316,10 +1502,28 @@ var REMOVE_CART_ITEM = (
1316
1502
  total_with_discount
1317
1503
  created_at
1318
1504
  updated_at
1505
+ volume_pricing {
1506
+ enabled
1507
+ eligibleQuantity
1508
+ appliedPriceType
1509
+ appliedMinQuantity
1510
+ nextPriceType
1511
+ nextMinQuantity
1512
+ remainingQuantity
1513
+ totalSavings
1514
+ }
1319
1515
  items {
1320
1516
  id
1321
1517
  product_variant_id
1322
1518
  quantity
1519
+ unit_price
1520
+ line_total
1521
+ base_unit_price
1522
+ unit_savings
1523
+ line_savings
1524
+ volume_pricing_eligible
1525
+ resolved_price_type
1526
+ volume_pricing_min_quantity
1323
1527
  product_variant {
1324
1528
  id
1325
1529
  sku
@@ -1341,6 +1545,19 @@ var REMOVE_CART_ITEM = (
1341
1545
  }
1342
1546
  `
1343
1547
  );
1548
+ var DISCARD_CART = (
1549
+ /* GraphQL */
1550
+ `
1551
+ mutation DiscardCart($orderId: uuid!, $cartToken: String!) {
1552
+ websiteDiscardCart(order_id: $orderId, cart_token: $cartToken) {
1553
+ success
1554
+ order_id
1555
+ website_cart_status
1556
+ errors
1557
+ }
1558
+ }
1559
+ `
1560
+ );
1344
1561
  var CHECKOUT = (
1345
1562
  /* GraphQL */
1346
1563
  `
@@ -2711,7 +2928,6 @@ function mapRelatedProduct(raw, storageUrl) {
2711
2928
  slug: raw.related_product.slug,
2712
2929
  name: raw.related_product.name,
2713
2930
  sku: raw.related_product.sku,
2714
- displayLabel: raw.display_label,
2715
2931
  sortOrder: raw.sort_order ?? 0,
2716
2932
  media: raw.related_product.product_media.map(
2717
2933
  (m) => mapMedia(m, storageUrl)
@@ -2722,6 +2938,23 @@ function mapProduct(raw, storageUrl, labels, chainId) {
2722
2938
  const detailVariants = raw.product_variants.map((variant) => mapVariant(variant, labels, chainId)).filter((variant) => variant.pricing.currentPrice != null);
2723
2939
  const listingVariantIds = raw.listing_variant_ids ? new Set(raw.listing_variant_ids) : null;
2724
2940
  const variants = listingVariantIds ? detailVariants.filter((variant) => listingVariantIds.has(variant.id)) : detailVariants;
2941
+ const rawOptionGroup = raw.product_option_group_members?.[0]?.group;
2942
+ const optionGroup = rawOptionGroup && rawOptionGroup.members.length >= 2 ? {
2943
+ id: rawOptionGroup.id,
2944
+ title: rawOptionGroup.display_title,
2945
+ members: rawOptionGroup.members.map((member) => ({
2946
+ id: member.id,
2947
+ productId: member.product_id,
2948
+ slug: member.product.slug,
2949
+ name: member.product.name,
2950
+ sku: member.product.sku,
2951
+ displayLabel: member.display_label,
2952
+ sortOrder: member.sort_order ?? 0,
2953
+ media: member.product.product_media.map(
2954
+ (media) => mapMedia(media, storageUrl)
2955
+ )
2956
+ }))
2957
+ } : null;
2725
2958
  return {
2726
2959
  id: raw.id,
2727
2960
  name: raw.name,
@@ -2746,7 +2979,9 @@ function mapProduct(raw, storageUrl, labels, chainId) {
2746
2979
  ),
2747
2980
  relatedProducts: (raw.product_related_products ?? []).map(
2748
2981
  (relation) => mapRelatedProduct(relation, storageUrl)
2749
- )
2982
+ ),
2983
+ optionGroup,
2984
+ specification: raw.product_specification ?? null
2750
2985
  };
2751
2986
  }
2752
2987
  function mapCategory(raw) {
@@ -3077,7 +3312,7 @@ function hasMeaningfulCategoryListFilters(filters) {
3077
3312
  )
3078
3313
  );
3079
3314
  }
3080
- async function getWebsiteAvailabilityByVariantId(graphqlUrl, getHeaders, variantIds, chainId) {
3315
+ async function getWebsiteAvailabilityByVariantId(graphqlUrl, getHeaders, variantIds, chainId, websiteId) {
3081
3316
  const uniqueVariantIds = uniqueValues(variantIds);
3082
3317
  const availabilityByVariantId = /* @__PURE__ */ new Map();
3083
3318
  for (const chunk of chunkArray(
@@ -3087,7 +3322,7 @@ async function getWebsiteAvailabilityByVariantId(graphqlUrl, getHeaders, variant
3087
3322
  const data = await executeGraphQL(
3088
3323
  graphqlUrl,
3089
3324
  CHECK_AVAILABILITY,
3090
- { variantIds: chunk, chainId },
3325
+ { variantIds: chunk, chainId, websiteId },
3091
3326
  getHeaders
3092
3327
  );
3093
3328
  for (const item of data.websiteProductAvailability ?? []) {
@@ -3098,7 +3333,7 @@ async function getWebsiteAvailabilityByVariantId(graphqlUrl, getHeaders, variant
3098
3333
  }
3099
3334
  return availabilityByVariantId;
3100
3335
  }
3101
- async function filterProductsForWebsiteStock(graphqlUrl, getHeaders, products, chainId) {
3336
+ async function filterProductsForWebsiteStock(graphqlUrl, getHeaders, products, chainId, websiteId) {
3102
3337
  const variantIds = products.flatMap(
3103
3338
  (product) => product.variants.map((variant) => variant.id)
3104
3339
  );
@@ -3109,7 +3344,8 @@ async function filterProductsForWebsiteStock(graphqlUrl, getHeaders, products, c
3109
3344
  graphqlUrl,
3110
3345
  getHeaders,
3111
3346
  variantIds,
3112
- chainId
3347
+ chainId,
3348
+ websiteId
3113
3349
  )).entries()
3114
3350
  ).filter(([, item]) => item.inStock).map(([variantId]) => variantId)
3115
3351
  );
@@ -3127,7 +3363,7 @@ async function filterProductsForWebsiteStock(graphqlUrl, getHeaders, products, c
3127
3363
  )
3128
3364
  })).filter((product) => product.variants.length > 0);
3129
3365
  }
3130
- async function filterSeoProductsForWebsiteStock(graphqlUrl, getHeaders, products, chainId) {
3366
+ async function filterSeoProductsForWebsiteStock(graphqlUrl, getHeaders, products, chainId, websiteId) {
3131
3367
  const variantIds = products.flatMap(
3132
3368
  (product) => product.product_variants.map((variant) => variant.id)
3133
3369
  );
@@ -3138,7 +3374,8 @@ async function filterSeoProductsForWebsiteStock(graphqlUrl, getHeaders, products
3138
3374
  graphqlUrl,
3139
3375
  getHeaders,
3140
3376
  variantIds,
3141
- chainId
3377
+ chainId,
3378
+ websiteId
3142
3379
  )).entries()
3143
3380
  ).filter(([, item]) => item.inStock).map(([variantId]) => variantId)
3144
3381
  );
@@ -3169,6 +3406,32 @@ function mapProductSeoEntry(raw, storageUrl) {
3169
3406
  };
3170
3407
  }
3171
3408
  function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
3409
+ const loadProductSpecifications = async (productIds) => {
3410
+ const ctx = getContext();
3411
+ const uniqueProductIds = [...new Set(productIds)];
3412
+ const specifications = {};
3413
+ for (let offset = 0; offset < uniqueProductIds.length; offset += 100) {
3414
+ const batch = uniqueProductIds.slice(offset, offset + 100);
3415
+ try {
3416
+ const data = await executeGraphQL(
3417
+ graphqlUrl,
3418
+ WEBSITE_GET_PRODUCT_SPECIFICATIONS,
3419
+ { websiteId: ctx.websiteId, productIds: batch },
3420
+ getHeaders
3421
+ );
3422
+ if (!data.result?.success) continue;
3423
+ Object.assign(
3424
+ specifications,
3425
+ parseActionJson(
3426
+ data.result.specifications,
3427
+ {}
3428
+ )
3429
+ );
3430
+ } catch {
3431
+ }
3432
+ }
3433
+ return specifications;
3434
+ };
3172
3435
  const mapSingleProductResult = async (data) => {
3173
3436
  const ctx = getContext();
3174
3437
  if (!ctx.chainId) {
@@ -3192,12 +3455,15 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
3192
3455
  graphqlUrl,
3193
3456
  getHeaders,
3194
3457
  [mappedProduct],
3195
- ctx.chainId
3458
+ ctx.chainId,
3459
+ ctx.websiteId
3196
3460
  );
3197
3461
  if (!filteredProducts[0]) return null;
3462
+ const specifications = await loadProductSpecifications([product.id]);
3198
3463
  return {
3199
3464
  ...mappedProduct,
3200
- variants: getProductDetailVariants(mappedProduct)
3465
+ variants: getProductDetailVariants(mappedProduct),
3466
+ specification: specifications[product.id] ?? null
3201
3467
  };
3202
3468
  };
3203
3469
  const loadCatalogPage = async (opts, retryRateLimits = false) => {
@@ -3218,6 +3484,19 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
3218
3484
  min_price: opts.minPrice,
3219
3485
  max_price: opts.maxPrice,
3220
3486
  attributes_by_slug: opts.attributesBySlug,
3487
+ technical_by_field: opts.technicalByField ? Object.fromEntries(
3488
+ Object.entries(opts.technicalByField).map(([fieldId, filter]) => [
3489
+ fieldId,
3490
+ {
3491
+ option_ids: filter.optionIds,
3492
+ boolean_values: filter.booleanValues,
3493
+ min: filter.min,
3494
+ max: filter.max,
3495
+ date_from: filter.dateFrom,
3496
+ date_to: filter.dateTo
3497
+ }
3498
+ ])
3499
+ ) : void 0,
3221
3500
  sort_by: opts.sortBy,
3222
3501
  limit: opts.limit,
3223
3502
  offset: opts.offset
@@ -3409,7 +3688,8 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
3409
3688
  graphqlUrl,
3410
3689
  getHeaders,
3411
3690
  data.products ?? [],
3412
- ctx.chainId
3691
+ ctx.chainId,
3692
+ ctx.websiteId
3413
3693
  );
3414
3694
  const entries = availableProducts.slice(0, limit).map((product) => mapProductSeoEntry(product, storageUrl)).filter((entry) => Boolean(entry));
3415
3695
  return {
@@ -3485,8 +3765,16 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
3485
3765
  (data.products ?? []).map(
3486
3766
  (p) => mapProduct(p, storageUrl, publicAttributeLabels, chainId)
3487
3767
  ),
3488
- chainId
3768
+ chainId,
3769
+ ctx.websiteId
3770
+ );
3771
+ const specifications = await loadProductSpecifications(
3772
+ products.map((product) => product.id)
3489
3773
  );
3774
+ products = products.map((product) => ({
3775
+ ...product,
3776
+ specification: specifications[product.id] ?? null
3777
+ }));
3490
3778
  if (opts?.minPrice !== void 0 || opts?.maxPrice !== void 0) {
3491
3779
  products = products.filter(
3492
3780
  (product) => product.variants.some((variant) => {
@@ -3597,7 +3885,8 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
3597
3885
  graphqlUrl,
3598
3886
  getHeaders,
3599
3887
  variantIds,
3600
- chainId
3888
+ chainId,
3889
+ ctx.websiteId
3601
3890
  );
3602
3891
  const applicableCategories = /* @__PURE__ */ new Set();
3603
3892
  const applicableSubcategoryIds = /* @__PURE__ */ new Set();
@@ -3715,7 +4004,8 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
3715
4004
  graphqlUrl,
3716
4005
  getHeaders,
3717
4006
  variantIds,
3718
- chainId
4007
+ chainId,
4008
+ ctx.websiteId
3719
4009
  );
3720
4010
  const publicAttributeLabels = buildPublicAttributeLabels(
3721
4011
  data.catalog_attribute_lists
@@ -3820,7 +4110,8 @@ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
3820
4110
  min: minPrice,
3821
4111
  max: maxPrice
3822
4112
  },
3823
- attributes
4113
+ attributes,
4114
+ technical: []
3824
4115
  };
3825
4116
  },
3826
4117
  async getSearchSuggestions(opts) {
@@ -4096,6 +4387,21 @@ function useCart() {
4096
4387
  },
4097
4388
  [client, refreshCart]
4098
4389
  );
4390
+ const clear = (0, import_react2.useCallback)(async () => {
4391
+ if (!client) throw new NotInitializedError();
4392
+ setLoading(true);
4393
+ try {
4394
+ await client.cart.clear();
4395
+ setCart(null);
4396
+ setError(null);
4397
+ } catch (err) {
4398
+ const nextError = err instanceof Error ? err : new Error(String(err));
4399
+ setError(nextError);
4400
+ throw nextError;
4401
+ } finally {
4402
+ setLoading(false);
4403
+ }
4404
+ }, [client]);
4099
4405
  const getOrCreate = (0, import_react2.useCallback)(
4100
4406
  async (contact, fulfillmentFamily) => {
4101
4407
  if (!client) throw new NotInitializedError();
@@ -4123,6 +4429,7 @@ function useCart() {
4123
4429
  addItem,
4124
4430
  updateItem,
4125
4431
  removeItem,
4432
+ clear,
4126
4433
  refresh: refreshCart
4127
4434
  };
4128
4435
  }
@@ -4212,6 +4519,7 @@ var ALLOWED_MARKETING_PARAMETERS = /* @__PURE__ */ new Set([
4212
4519
  "utm_content",
4213
4520
  "utm_source_platform",
4214
4521
  "utm_id",
4522
+ "bacano_origin",
4215
4523
  "gclid",
4216
4524
  "gbraid",
4217
4525
  "wbraid",
@@ -4246,6 +4554,7 @@ function getAllowedMarketingParameters(input, baseUrl) {
4246
4554
  var STORAGE_PREFIX = "bacano:marketing:attribution:v1";
4247
4555
  var GA4_CLIENT_ID_PATTERN = /^\d+\.\d+$/;
4248
4556
  var META_BROWSER_ID_PATTERN = /^fb\.\d+\.\d+\.[A-Za-z0-9_-]+$/;
4557
+ var SALES_ORIGIN_KEY_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
4249
4558
  function storageKey(websiteId) {
4250
4559
  return `${STORAGE_PREFIX}:${websiteId}`;
4251
4560
  }
@@ -4288,6 +4597,10 @@ function optional(value) {
4288
4597
  const normalized = value?.trim();
4289
4598
  return normalized ? normalized.slice(0, 500) : void 0;
4290
4599
  }
4600
+ function validSalesOriginKey(value) {
4601
+ const normalized = value?.trim().toLowerCase();
4602
+ return normalized && normalized.length <= 100 && SALES_ORIGIN_KEY_PATTERN.test(normalized) ? normalized : void 0;
4603
+ }
4291
4604
  function buildTouchpoint() {
4292
4605
  if (typeof window === "undefined") return null;
4293
4606
  const params = getAllowedMarketingParameters(window.location.href);
@@ -4311,6 +4624,7 @@ function buildTouchpoint() {
4311
4624
  content: optional(params.utm_content),
4312
4625
  sourcePlatform: optional(params.utm_source_platform),
4313
4626
  campaignId: optional(params.utm_id),
4627
+ salesOriginKey: validSalesOriginKey(params.bacano_origin),
4314
4628
  gclid: optional(params.gclid),
4315
4629
  gbraid: optional(params.gbraid),
4316
4630
  wbraid: optional(params.wbraid),
@@ -4321,7 +4635,7 @@ function buildTouchpoint() {
4321
4635
  }
4322
4636
  function isNonDirect(touchpoint) {
4323
4637
  return Boolean(
4324
- touchpoint.source || touchpoint.medium || touchpoint.campaign || touchpoint.gclid || touchpoint.gbraid || touchpoint.wbraid || touchpoint.fbclid || touchpoint.ttclid || touchpoint.msclkid || touchpoint.referrerHost
4638
+ touchpoint.salesOriginKey || touchpoint.source || touchpoint.medium || touchpoint.campaign || touchpoint.gclid || touchpoint.gbraid || touchpoint.wbraid || touchpoint.fbclid || touchpoint.ttclid || touchpoint.msclkid || touchpoint.referrerHost
4325
4639
  );
4326
4640
  }
4327
4641
  function toPublic(stored) {
@@ -4412,6 +4726,7 @@ function serializeTouchpoint(touchpoint) {
4412
4726
  content: touchpoint.content,
4413
4727
  source_platform: touchpoint.sourcePlatform,
4414
4728
  campaign_id: touchpoint.campaignId,
4729
+ sales_origin_key: touchpoint.salesOriginKey,
4415
4730
  gclid: touchpoint.gclid,
4416
4731
  gbraid: touchpoint.gbraid,
4417
4732
  wbraid: touchpoint.wbraid,
@@ -4935,6 +5250,10 @@ async function getWebsitePublicConfig(options) {
4935
5250
  consent_policy: null,
4936
5251
  destinations: { ga4: null, meta: null, clarity: null }
4937
5252
  };
5253
+ const branding = raw.branding ?? {
5254
+ schema_version: 1,
5255
+ favicon: null
5256
+ };
4938
5257
  const legal = raw.legal ?? {
4939
5258
  schema_version: 1,
4940
5259
  version: 0,
@@ -4982,6 +5301,11 @@ async function getWebsitePublicConfig(options) {
4982
5301
  desktop_image: null,
4983
5302
  mobile_image: null
4984
5303
  };
5304
+ const volumePricing = raw.volume_pricing ?? {
5305
+ schema_version: 1,
5306
+ enabled: false,
5307
+ tiers: []
5308
+ };
4985
5309
  return {
4986
5310
  websiteId: raw.website_id,
4987
5311
  companyId: raw.company_id,
@@ -5007,6 +5331,20 @@ async function getWebsitePublicConfig(options) {
5007
5331
  })
5008
5332
  )
5009
5333
  },
5334
+ branding: {
5335
+ schemaVersion: branding.schema_version,
5336
+ favicon: branding.favicon ? {
5337
+ fileId: branding.favicon.file_id,
5338
+ name: branding.favicon.name,
5339
+ altText: branding.favicon.alt_text,
5340
+ width: branding.favicon.width,
5341
+ height: branding.favicon.height,
5342
+ originalUrl: buildStorageFileUrl(
5343
+ `${apiUrl}/storage/v1`,
5344
+ branding.favicon.file_id
5345
+ )
5346
+ } : null
5347
+ },
5010
5348
  marketing: {
5011
5349
  schemaVersion: marketing.schema_version,
5012
5350
  enabled: marketing.enabled,
@@ -5063,6 +5401,14 @@ async function getWebsitePublicConfig(options) {
5063
5401
  message: construction.message,
5064
5402
  desktopImage: mapConstructionImage(construction.desktop_image),
5065
5403
  mobileImage: mapConstructionImage(construction.mobile_image)
5404
+ },
5405
+ volumePricing: {
5406
+ schemaVersion: volumePricing.schema_version,
5407
+ enabled: volumePricing.enabled,
5408
+ tiers: volumePricing.tiers.map((tier) => ({
5409
+ priceType: tier.price_type,
5410
+ minQuantity: tier.min_quantity
5411
+ }))
5066
5412
  }
5067
5413
  };
5068
5414
  }
@@ -5109,7 +5455,8 @@ function createAvailabilityModule(graphqlUrl, getHeaders, getContext) {
5109
5455
  CHECK_AVAILABILITY,
5110
5456
  {
5111
5457
  variantIds: chunk,
5112
- chainId: ctx.chainId
5458
+ chainId: ctx.chainId,
5459
+ websiteId: ctx.websiteId
5113
5460
  },
5114
5461
  getHeaders
5115
5462
  );
@@ -5122,12 +5469,40 @@ function createAvailabilityModule(graphqlUrl, getHeaders, getContext) {
5122
5469
  };
5123
5470
  }
5124
5471
 
5472
+ // libs/sdk/src/modules/cart-errors.ts
5473
+ var RECOVERABLE_CART_SESSION_ERRORS = /* @__PURE__ */ new Set([
5474
+ "INVALID_CART_TOKEN",
5475
+ "CART_NOT_FOUND"
5476
+ ]);
5477
+ function firstStableErrorCode(errors) {
5478
+ const candidate = errors?.[0]?.split(":", 1)[0]?.trim();
5479
+ return candidate && /^[A-Z][A-Z0-9_]+$/.test(candidate) ? candidate : void 0;
5480
+ }
5481
+ function getStableCartActionErrorCode(errors, fallbackCode) {
5482
+ return firstStableErrorCode(errors) ?? fallbackCode;
5483
+ }
5484
+ function hasRecoverableCartSessionError(errors) {
5485
+ return Boolean(
5486
+ errors?.some(
5487
+ (error) => RECOVERABLE_CART_SESSION_ERRORS.has(error.split(":", 1)[0]?.trim())
5488
+ )
5489
+ );
5490
+ }
5491
+
5125
5492
  // libs/sdk/src/modules/cart.ts
5126
5493
  function mapCartItem(raw) {
5127
5494
  return {
5128
5495
  id: raw.id,
5129
5496
  productVariantId: raw.product_variant_id,
5130
5497
  quantity: raw.quantity,
5498
+ unitPrice: raw.unit_price ?? null,
5499
+ lineTotal: raw.line_total ?? null,
5500
+ baseUnitPrice: raw.base_unit_price ?? raw.unit_price ?? null,
5501
+ unitSavings: raw.unit_savings ?? 0,
5502
+ lineSavings: raw.line_savings ?? 0,
5503
+ volumePricingEligible: raw.volume_pricing_eligible ?? false,
5504
+ resolvedPriceType: raw.resolved_price_type ?? null,
5505
+ volumePricingMinQuantity: raw.volume_pricing_min_quantity ?? null,
5131
5506
  productVariant: {
5132
5507
  sku: raw.product_variant.sku,
5133
5508
  product: raw.product_variant.product,
@@ -5153,13 +5528,29 @@ function mapCart(raw, cartToken) {
5153
5528
  },
5154
5529
  status: raw.status,
5155
5530
  createdAt: raw.created_at,
5156
- updatedAt: raw.updated_at
5531
+ updatedAt: raw.updated_at,
5532
+ volumePricing: raw.volume_pricing ? {
5533
+ ...raw.volume_pricing,
5534
+ totalSavings: raw.volume_pricing.totalSavings ?? 0
5535
+ } : {
5536
+ enabled: false,
5537
+ eligibleQuantity: 0,
5538
+ appliedPriceType: null,
5539
+ appliedMinQuantity: null,
5540
+ nextPriceType: null,
5541
+ nextMinQuantity: null,
5542
+ remainingQuantity: null,
5543
+ totalSavings: 0
5544
+ }
5157
5545
  };
5158
5546
  }
5159
5547
  function assertActionSuccess(result, fallbackCode) {
5160
5548
  if (!result.success || !result.order_id || !result.cart_token || !result.cart) {
5161
5549
  const message = result.errors?.join("; ") || "Cart action failed";
5162
- throw new WebsiteError(message, fallbackCode);
5550
+ throw new WebsiteError(
5551
+ message,
5552
+ getStableCartActionErrorCode(result.errors, fallbackCode)
5553
+ );
5163
5554
  }
5164
5555
  }
5165
5556
  function requireCartSession(storage) {
@@ -5177,7 +5568,7 @@ function createCartModule(graphqlUrl, getHeaders, getContext, cartSessionStorage
5177
5568
  const data = await executeGraphQL(graphqlUrl, GET_CART, { orderId, cartToken }, getHeaders);
5178
5569
  const result = data.websiteGetCart;
5179
5570
  if (!result.success) {
5180
- if (result.errors?.includes("CART_NOT_FOUND")) {
5571
+ if (hasRecoverableCartSessionError(result.errors)) {
5181
5572
  cartSessionStorage.clear();
5182
5573
  return null;
5183
5574
  }
@@ -5230,24 +5621,31 @@ function createCartModule(graphqlUrl, getHeaders, getContext, cartSessionStorage
5230
5621
  "NO_CHAIN"
5231
5622
  );
5232
5623
  }
5624
+ const requestStartCheckout = async (cartToken) => {
5625
+ const data = await executeGraphQL(
5626
+ graphqlUrl,
5627
+ START_GUEST_CHECKOUT,
5628
+ {
5629
+ websiteId: ctx.websiteId,
5630
+ cartToken,
5631
+ contact: input.contact,
5632
+ fulfillmentFamily: input.fulfillmentFamily,
5633
+ items: input.items.map((item) => ({
5634
+ product_variant_id: item.productVariantId,
5635
+ quantity: item.quantity
5636
+ })),
5637
+ marketingAttribution: input.marketingAttribution ? serializeWebsiteMarketingAttribution(input.marketingAttribution) : void 0
5638
+ },
5639
+ getHeaders
5640
+ );
5641
+ return data.websiteStartGuestCheckout;
5642
+ };
5233
5643
  const existing = cartSessionStorage.get();
5234
- const data = await executeGraphQL(
5235
- graphqlUrl,
5236
- START_GUEST_CHECKOUT,
5237
- {
5238
- websiteId: ctx.websiteId,
5239
- cartToken: existing?.cartToken,
5240
- contact: input.contact,
5241
- fulfillmentFamily: input.fulfillmentFamily,
5242
- items: input.items.map((item) => ({
5243
- product_variant_id: item.productVariantId,
5244
- quantity: item.quantity
5245
- })),
5246
- marketingAttribution: input.marketingAttribution ? serializeWebsiteMarketingAttribution(input.marketingAttribution) : void 0
5247
- },
5248
- getHeaders
5249
- );
5250
- const result = data.websiteStartGuestCheckout;
5644
+ let result = await requestStartCheckout(existing?.cartToken);
5645
+ if (existing && !result.success && hasRecoverableCartSessionError(result.errors)) {
5646
+ cartSessionStorage.clear();
5647
+ result = await requestStartCheckout();
5648
+ }
5251
5649
  assertActionSuccess(result, "CHECKOUT_START_ERROR");
5252
5650
  cartSessionStorage.set({
5253
5651
  orderId: result.order_id,
@@ -5270,7 +5668,7 @@ function createCartModule(graphqlUrl, getHeaders, getContext, cartSessionStorage
5270
5668
  const session = cartSessionStorage.get();
5271
5669
  if (!session) {
5272
5670
  throw new WebsiteError(
5273
- "Start checkout with contact before syncing cart items to Bacano",
5671
+ "Create or recover a Bacano cart session before syncing cart items",
5274
5672
  "CART_SESSION_MISSING"
5275
5673
  );
5276
5674
  }
@@ -5338,11 +5736,37 @@ function createCartModule(graphqlUrl, getHeaders, getContext, cartSessionStorage
5338
5736
  );
5339
5737
  const result = data.websiteRemoveCartItem;
5340
5738
  assertActionSuccess(result, "CART_ITEM_ERROR");
5739
+ const cart = mapCart(result.cart, result.cart_token);
5740
+ if (cart.items.length === 0) {
5741
+ cartSessionStorage.clear();
5742
+ return cart;
5743
+ }
5341
5744
  cartSessionStorage.set({
5342
5745
  orderId: result.order_id,
5343
5746
  cartToken: result.cart_token
5344
5747
  });
5345
- return mapCart(result.cart, result.cart_token);
5748
+ return cart;
5749
+ },
5750
+ async clear() {
5751
+ const session = cartSessionStorage.get();
5752
+ if (!session) return;
5753
+ const data = await executeGraphQL(
5754
+ graphqlUrl,
5755
+ DISCARD_CART,
5756
+ {
5757
+ orderId: session.orderId,
5758
+ cartToken: session.cartToken
5759
+ },
5760
+ getHeaders
5761
+ );
5762
+ const result = data.websiteDiscardCart;
5763
+ if (!result.success && !hasRecoverableCartSessionError(result.errors)) {
5764
+ throw new WebsiteError(
5765
+ result.errors?.join("; ") || "Cart could not be discarded",
5766
+ getStableCartActionErrorCode(result.errors, "CART_DISCARD_ERROR")
5767
+ );
5768
+ }
5769
+ cartSessionStorage.clear();
5346
5770
  }
5347
5771
  };
5348
5772
  }
@@ -5423,6 +5847,14 @@ function mapCartItem2(raw) {
5423
5847
  id: raw.id,
5424
5848
  productVariantId: raw.product_variant_id,
5425
5849
  quantity: raw.quantity,
5850
+ unitPrice: raw.unit_price ?? null,
5851
+ lineTotal: raw.line_total ?? null,
5852
+ baseUnitPrice: raw.base_unit_price ?? raw.unit_price ?? null,
5853
+ unitSavings: raw.unit_savings ?? 0,
5854
+ lineSavings: raw.line_savings ?? 0,
5855
+ volumePricingEligible: raw.volume_pricing_eligible ?? false,
5856
+ resolvedPriceType: raw.resolved_price_type ?? null,
5857
+ volumePricingMinQuantity: raw.volume_pricing_min_quantity ?? null,
5426
5858
  productVariant: {
5427
5859
  sku: raw.product_variant.sku,
5428
5860
  product: raw.product_variant.product,
@@ -5447,7 +5879,20 @@ function mapCart2(raw) {
5447
5879
  },
5448
5880
  status: raw.status,
5449
5881
  createdAt: raw.created_at,
5450
- updatedAt: raw.updated_at
5882
+ updatedAt: raw.updated_at,
5883
+ volumePricing: raw.volume_pricing ? {
5884
+ ...raw.volume_pricing,
5885
+ totalSavings: raw.volume_pricing.totalSavings ?? 0
5886
+ } : {
5887
+ enabled: false,
5888
+ eligibleQuantity: 0,
5889
+ appliedPriceType: null,
5890
+ appliedMinQuantity: null,
5891
+ nextPriceType: null,
5892
+ nextMinQuantity: null,
5893
+ remainingQuantity: null,
5894
+ totalSavings: 0
5895
+ }
5451
5896
  };
5452
5897
  }
5453
5898
  function mapCouponResult(raw) {
@@ -6637,7 +7082,8 @@ function createBacanoClient(config) {
6637
7082
  setItem: cartModule.setItem.bind(cartModule),
6638
7083
  addItem: cartModule.addItem.bind(cartModule),
6639
7084
  updateItem: cartModule.updateItem.bind(cartModule),
6640
- removeItem: cartModule.removeItem.bind(cartModule)
7085
+ removeItem: cartModule.removeItem.bind(cartModule),
7086
+ clear: cartModule.clear.bind(cartModule)
6641
7087
  },
6642
7088
  checkout: {
6643
7089
  getLocationOptions: checkoutModule.getLocationOptions.bind(checkoutModule),
@@ -6761,13 +7207,14 @@ function clearPreviewToken(websiteSlug) {
6761
7207
  function BacanoConstructionScreen({
6762
7208
  construction
6763
7209
  }) {
6764
- const desktop = construction.desktopImage;
7210
+ const desktop = construction.desktopImage ?? construction.mobileImage;
6765
7211
  const mobile = construction.mobileImage ?? desktop;
6766
- if (!desktop) return null;
7212
+ const title = construction.title?.trim() || "Sitio en construcci\xF3n";
7213
+ const message = construction.message?.trim() || null;
6767
7214
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
6768
7215
  "main",
6769
7216
  {
6770
- "aria-label": construction.title ?? "Sitio en construcci\xF3n",
7217
+ "aria-label": title,
6771
7218
  style: {
6772
7219
  background: "#111827",
6773
7220
  color: "#ffffff",
@@ -6779,7 +7226,7 @@ function BacanoConstructionScreen({
6779
7226
  zIndex: 2147483647
6780
7227
  },
6781
7228
  children: [
6782
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("picture", { style: { inset: 0, position: "absolute" }, children: [
7229
+ desktop ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("picture", { style: { inset: 0, position: "absolute" }, children: [
6783
7230
  mobile ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6784
7231
  "source",
6785
7232
  {
@@ -6791,7 +7238,7 @@ function BacanoConstructionScreen({
6791
7238
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6792
7239
  "img",
6793
7240
  {
6794
- alt: desktop.altText ?? construction.title ?? "Sitio en construcci\xF3n",
7241
+ alt: desktop.altText ?? title,
6795
7242
  sizes: "100vw",
6796
7243
  src: desktop.heroUrl || desktop.originalUrl,
6797
7244
  srcSet: formatMediaSrcSet(desktop.responsiveSources),
@@ -6803,12 +7250,12 @@ function BacanoConstructionScreen({
6803
7250
  }
6804
7251
  }
6805
7252
  )
6806
- ] }),
6807
- construction.title || construction.message ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
7253
+ ] }) : null,
7254
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
6808
7255
  "section",
6809
7256
  {
6810
7257
  style: {
6811
- background: "rgba(0, 0, 0, 0.48)",
7258
+ background: desktop ? "rgba(0, 0, 0, 0.48)" : "transparent",
6812
7259
  borderRadius: "1rem",
6813
7260
  left: "50%",
6814
7261
  maxWidth: "42rem",
@@ -6820,17 +7267,11 @@ function BacanoConstructionScreen({
6820
7267
  width: "calc(100% - 2rem)"
6821
7268
  },
6822
7269
  children: [
6823
- construction.title ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h1", { style: { fontSize: "clamp(1.75rem, 5vw, 3.5rem)", margin: 0 }, children: construction.title }) : null,
6824
- construction.message ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
6825
- "p",
6826
- {
6827
- style: { fontSize: "clamp(1rem, 2vw, 1.25rem)", marginBottom: 0 },
6828
- children: construction.message
6829
- }
6830
- ) : null
7270
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h1", { style: { fontSize: "clamp(1.75rem, 5vw, 3.5rem)", margin: 0 }, children: title }),
7271
+ message ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: { fontSize: "clamp(1rem, 2vw, 1.25rem)", marginBottom: 0 }, children: message }) : null
6831
7272
  ]
6832
7273
  }
6833
- ) : null
7274
+ )
6834
7275
  ]
6835
7276
  }
6836
7277
  );
@@ -6879,6 +7320,11 @@ function BacanoWebsiteGate({
6879
7320
  if (isConstructionEnforced && publicConfig) {
6880
7321
  return constructionFallback ? constructionFallback(publicConfig.construction) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(BacanoConstructionScreen, { construction: publicConfig.construction });
6881
7322
  }
6882
- if (isLoading) return loadingFallback;
7323
+ if (isLoading) {
7324
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
7325
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("noscript", { "data-bacano-static-storefront": "true", children }),
7326
+ loadingFallback
7327
+ ] });
7328
+ }
6883
7329
  return children;
6884
7330
  }