@bacano/sdk 1.0.0

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/index.js ADDED
@@ -0,0 +1,4933 @@
1
+ // libs/sdk/src/errors.ts
2
+ var WebsiteError = class extends Error {
3
+ code;
4
+ constructor(message, code = "WEBSITE_ERROR") {
5
+ super(message);
6
+ this.name = "WebsiteError";
7
+ this.code = code;
8
+ }
9
+ };
10
+ var NotInitializedError = class extends WebsiteError {
11
+ constructor() {
12
+ super(
13
+ "BacanoClient has not been initialized. Call client.init() first.",
14
+ "NOT_INITIALIZED"
15
+ );
16
+ this.name = "NotInitializedError";
17
+ }
18
+ };
19
+ var AuthError = class extends WebsiteError {
20
+ constructor(message) {
21
+ super(message, "AUTH_ERROR");
22
+ this.name = "AuthError";
23
+ }
24
+ };
25
+ var CheckoutError = class extends WebsiteError {
26
+ errors;
27
+ constructor(errors) {
28
+ super(errors.join("; "), "CHECKOUT_ERROR");
29
+ this.name = "CheckoutError";
30
+ this.errors = errors;
31
+ }
32
+ };
33
+ var GraphQLRequestError = class extends WebsiteError {
34
+ graphqlErrors;
35
+ constructor(errors) {
36
+ const message = errors.map((e) => e.message).join("; ");
37
+ super(message, "GRAPHQL_ERROR");
38
+ this.name = "GraphQLRequestError";
39
+ this.graphqlErrors = errors;
40
+ }
41
+ };
42
+
43
+ // libs/sdk/src/modules/auth.ts
44
+ function createAuthHeaders(accessToken) {
45
+ return accessToken ? { Authorization: `Bearer ${accessToken}` } : {};
46
+ }
47
+ function createRequestHeadersProvider(websiteSlug, getAccessToken) {
48
+ return async () => {
49
+ const headers = { "X-Bacano-Website-Slug": websiteSlug };
50
+ if (!getAccessToken) return headers;
51
+ try {
52
+ return {
53
+ ...headers,
54
+ ...createAuthHeaders(await getAccessToken())
55
+ };
56
+ } catch (error) {
57
+ throw new AuthError(
58
+ `Could not obtain the buyer access token: ${error instanceof Error ? error.message : String(error)}`
59
+ );
60
+ }
61
+ };
62
+ }
63
+ async function getWebsitePublicConfig(options) {
64
+ const apiUrl = options.apiUrl.replace(/\/+$/, "");
65
+ const headers = options.origin ? { Origin: options.origin } : void 0;
66
+ const params = new URLSearchParams({ websiteSlug: options.websiteSlug });
67
+ let response;
68
+ try {
69
+ response = await fetch(
70
+ `${apiUrl}/api/v1/website/public-config?${params.toString()}`,
71
+ {
72
+ headers,
73
+ cache: "no-store"
74
+ }
75
+ );
76
+ } catch (error) {
77
+ throw new WebsiteError(
78
+ `Could not load website auth configuration: ${error instanceof Error ? error.message : String(error)}`,
79
+ "NETWORK_ERROR"
80
+ );
81
+ }
82
+ if (!response.ok) {
83
+ throw new WebsiteError(
84
+ `Could not load website auth configuration (status ${response.status})`,
85
+ response.status === 403 ? "ORIGIN_NOT_ALLOWED" : "INVALID_RESPONSE"
86
+ );
87
+ }
88
+ const raw = await response.json();
89
+ const customerIdentification = raw.customer_identification ?? {
90
+ primary_identifier: "AUTO",
91
+ require_email: false,
92
+ require_phone: false,
93
+ require_document: false,
94
+ document_types: []
95
+ };
96
+ return {
97
+ websiteId: raw.website_id,
98
+ companyId: raw.company_id,
99
+ slug: raw.slug,
100
+ name: raw.name,
101
+ domain: raw.domain,
102
+ buyerAccountsEnabled: raw.buyer_accounts_enabled,
103
+ clerkPublishableKey: raw.clerk_publishable_key,
104
+ customerIdentification: {
105
+ primaryIdentifier: customerIdentification.primary_identifier,
106
+ requireEmail: customerIdentification.require_email,
107
+ requirePhone: customerIdentification.require_phone,
108
+ requireDocument: customerIdentification.require_document,
109
+ documentTypes: customerIdentification.document_types.map(
110
+ (documentType) => ({
111
+ code: documentType.code,
112
+ name: documentType.name,
113
+ shortLabel: documentType.short_label,
114
+ isDefault: documentType.is_default
115
+ })
116
+ )
117
+ }
118
+ };
119
+ }
120
+
121
+ // libs/sdk/src/transport/graphql.ts
122
+ async function executeGraphQL(url, query, variables, headers) {
123
+ const resolvedHeaders = typeof headers === "function" ? await headers() : headers ?? {};
124
+ let res;
125
+ try {
126
+ res = await fetch(url, {
127
+ method: "POST",
128
+ headers: {
129
+ "Content-Type": "application/json",
130
+ ...resolvedHeaders
131
+ },
132
+ body: JSON.stringify({ query, variables })
133
+ });
134
+ } catch (err) {
135
+ throw new WebsiteError(
136
+ `Network error connecting to ${url}: ${err instanceof Error ? err.message : String(err)}`,
137
+ "NETWORK_ERROR"
138
+ );
139
+ }
140
+ if (res.status === 429) {
141
+ throw new WebsiteError(
142
+ "Rate limit exceeded. Please try again later.",
143
+ "RATE_LIMITED"
144
+ );
145
+ }
146
+ let body;
147
+ try {
148
+ body = await res.json();
149
+ } catch {
150
+ throw new WebsiteError(
151
+ `Non-JSON response from ${url} (status ${res.status})`,
152
+ "INVALID_RESPONSE"
153
+ );
154
+ }
155
+ if (body.errors && body.errors.length > 0) {
156
+ throw new GraphQLRequestError(body.errors);
157
+ }
158
+ if (!body.data) {
159
+ throw new WebsiteError(
160
+ "GraphQL response contains no data",
161
+ "EMPTY_RESPONSE"
162
+ );
163
+ }
164
+ return body.data;
165
+ }
166
+
167
+ // libs/sdk/src/transport/documents.ts
168
+ var RESOLVE_WEBSITE_BY_SLUG = (
169
+ /* GraphQL */
170
+ `
171
+ query ResolveWebsiteBySlug($slug: String!) {
172
+ websites(
173
+ where: { slug: { _eq: $slug }, is_active: { _eq: true } }
174
+ limit: 1
175
+ ) {
176
+ id
177
+ company_id
178
+ chain_id
179
+ name
180
+ slug
181
+ domain
182
+ config
183
+ }
184
+ }
185
+ `
186
+ );
187
+ var GET_PRODUCTS = (
188
+ /* GraphQL */
189
+ `
190
+ query GetProducts(
191
+ $companyId: uuid!
192
+ $chainId: uuid!
193
+ $websiteId: uuid!
194
+ $limit: Int = 20
195
+ $offset: Int = 0
196
+ $where: products_bool_exp = {}
197
+ $orderBy: [products_order_by!] = [{ name: asc }]
198
+ ) {
199
+ products(
200
+ where: {
201
+ _and: [
202
+ { company_id: { _eq: $companyId } }
203
+ {
204
+ product_website_publications: {
205
+ website_id: { _eq: $websiteId }
206
+ is_visible: { _eq: true }
207
+ }
208
+ }
209
+ {
210
+ chain_products: {
211
+ chain_id: { _eq: $chainId }
212
+ is_enabled: { _eq: true }
213
+ status: { _eq: active }
214
+ }
215
+ }
216
+ $where
217
+ ]
218
+ }
219
+ limit: $limit
220
+ offset: $offset
221
+ order_by: $orderBy
222
+ ) {
223
+ id
224
+ name
225
+ description
226
+ long_description
227
+ slug
228
+ seo_title
229
+ seo_description
230
+ seo_image_file_id
231
+ category
232
+ subcategory
233
+ color
234
+ measurement_unit
235
+ created_at
236
+ updated_at
237
+ brand {
238
+ id
239
+ name
240
+ }
241
+ seo_image_file {
242
+ mimeType
243
+ name
244
+ }
245
+ product_variants(
246
+ where: { is_disabled: { _eq: false } }
247
+ order_by: { order: asc }
248
+ ) {
249
+ id
250
+ sku
251
+ barcode
252
+ order
253
+ weight
254
+ depth
255
+ height
256
+ length
257
+ product_variant_prices(
258
+ where: {
259
+ price_type: { _in: [CLIENT, SALE, WHOLESALE, WHOLESALE_2] }
260
+ _or: [
261
+ { chain_id: { _eq: $chainId } }
262
+ { chain_id: { _is_null: true } }
263
+ ]
264
+ }
265
+ ) {
266
+ id
267
+ price
268
+ price_type
269
+ chain_id
270
+ }
271
+ product_variant_attributes(order_by: { order: asc }) {
272
+ id
273
+ value
274
+ order
275
+ attribute_value {
276
+ id
277
+ code
278
+ sort_order
279
+ metadata
280
+ }
281
+ attribute {
282
+ id
283
+ name
284
+ }
285
+ }
286
+ }
287
+ product_media(order_by: { order: asc }) {
288
+ id
289
+ file_id
290
+ order
291
+ file {
292
+ mimeType
293
+ name
294
+ }
295
+ }
296
+ product_related_products(
297
+ where: {
298
+ related_product: {
299
+ chain_products: {
300
+ chain_id: { _eq: $chainId }
301
+ is_enabled: { _eq: true }
302
+ status: { _eq: active }
303
+ }
304
+ }
305
+ }
306
+ order_by: { sort_order: asc }
307
+ ) {
308
+ id
309
+ related_product_id
310
+ display_label
311
+ sort_order
312
+ related_product {
313
+ id
314
+ slug
315
+ name
316
+ sku
317
+ product_media(
318
+ where: { file: { mimeType: { _ilike: "image/%" } } }
319
+ order_by: { order: asc }
320
+ limit: 1
321
+ ) {
322
+ id
323
+ file_id
324
+ order
325
+ file {
326
+ mimeType
327
+ name
328
+ }
329
+ }
330
+ }
331
+ }
332
+ product_attributes {
333
+ id
334
+ value
335
+ attribute_value {
336
+ id
337
+ code
338
+ sort_order
339
+ metadata
340
+ }
341
+ attribute {
342
+ id
343
+ name
344
+ }
345
+ }
346
+ }
347
+ catalog_attribute_lists(
348
+ where: { company_id: { _eq: $companyId }, status: { _eq: published } }
349
+ order_by: { updated_at: desc }
350
+ ) {
351
+ id
352
+ key
353
+ name
354
+ description
355
+ status
356
+ items(
357
+ where: { is_visible: { _eq: true } }
358
+ order_by: [{ sort_order: asc }, { public_label: asc }]
359
+ ) {
360
+ id
361
+ attribute_id
362
+ public_label
363
+ public_slug
364
+ sort_order
365
+ attribute {
366
+ id
367
+ name
368
+ type
369
+ }
370
+ values(
371
+ where: { is_visible: { _eq: true } }
372
+ order_by: [{ sort_order: asc }, { public_label: asc }]
373
+ ) {
374
+ id
375
+ attribute_value_id
376
+ public_label
377
+ public_slug
378
+ sort_order
379
+ attribute_value {
380
+ id
381
+ code
382
+ metadata
383
+ }
384
+ }
385
+ }
386
+ }
387
+ }
388
+ `
389
+ );
390
+ var GET_PRODUCT_BY_PK = (
391
+ /* GraphQL */
392
+ `
393
+ query GetProductByPk(
394
+ $productId: uuid!
395
+ $companyId: uuid!
396
+ $chainId: uuid!
397
+ $websiteId: uuid!
398
+ ) {
399
+ products(
400
+ where: {
401
+ id: { _eq: $productId }
402
+ company_id: { _eq: $companyId }
403
+ product_website_publications: {
404
+ website_id: { _eq: $websiteId }
405
+ is_visible: { _eq: true }
406
+ }
407
+ chain_products: {
408
+ chain_id: { _eq: $chainId }
409
+ is_enabled: { _eq: true }
410
+ status: { _eq: active }
411
+ }
412
+ }
413
+ limit: 1
414
+ ) {
415
+ id
416
+ name
417
+ description
418
+ long_description
419
+ slug
420
+ seo_title
421
+ seo_description
422
+ seo_image_file_id
423
+ category
424
+ subcategory
425
+ color
426
+ measurement_unit
427
+ created_at
428
+ updated_at
429
+ brand {
430
+ id
431
+ name
432
+ }
433
+ seo_image_file {
434
+ mimeType
435
+ name
436
+ }
437
+ product_variants(
438
+ where: { is_disabled: { _eq: false } }
439
+ order_by: { order: asc }
440
+ ) {
441
+ id
442
+ sku
443
+ barcode
444
+ order
445
+ weight
446
+ depth
447
+ height
448
+ length
449
+ product_variant_prices(
450
+ where: {
451
+ price_type: { _in: [CLIENT, SALE, WHOLESALE, WHOLESALE_2] }
452
+ _or: [
453
+ { chain_id: { _eq: $chainId } }
454
+ { chain_id: { _is_null: true } }
455
+ ]
456
+ }
457
+ ) {
458
+ id
459
+ price
460
+ price_type
461
+ chain_id
462
+ }
463
+ product_variant_attributes(order_by: { order: asc }) {
464
+ id
465
+ value
466
+ order
467
+ attribute_value {
468
+ id
469
+ code
470
+ sort_order
471
+ metadata
472
+ }
473
+ attribute {
474
+ id
475
+ name
476
+ }
477
+ }
478
+ }
479
+ product_media(order_by: { order: asc }) {
480
+ id
481
+ file_id
482
+ order
483
+ file {
484
+ mimeType
485
+ name
486
+ }
487
+ }
488
+ product_related_products(
489
+ where: {
490
+ related_product: {
491
+ chain_products: {
492
+ chain_id: { _eq: $chainId }
493
+ is_enabled: { _eq: true }
494
+ status: { _eq: active }
495
+ }
496
+ }
497
+ }
498
+ order_by: { sort_order: asc }
499
+ ) {
500
+ id
501
+ related_product_id
502
+ display_label
503
+ sort_order
504
+ related_product {
505
+ id
506
+ slug
507
+ name
508
+ sku
509
+ product_media(
510
+ where: { file: { mimeType: { _ilike: "image/%" } } }
511
+ order_by: { order: asc }
512
+ limit: 1
513
+ ) {
514
+ id
515
+ file_id
516
+ order
517
+ file {
518
+ mimeType
519
+ name
520
+ }
521
+ }
522
+ }
523
+ }
524
+ product_attributes {
525
+ id
526
+ value
527
+ attribute_value {
528
+ id
529
+ code
530
+ sort_order
531
+ metadata
532
+ }
533
+ attribute {
534
+ id
535
+ name
536
+ }
537
+ }
538
+ }
539
+ catalog_attribute_lists(
540
+ where: { company_id: { _eq: $companyId }, status: { _eq: published } }
541
+ order_by: { updated_at: desc }
542
+ ) {
543
+ id
544
+ key
545
+ name
546
+ description
547
+ status
548
+ items(
549
+ where: { is_visible: { _eq: true } }
550
+ order_by: [{ sort_order: asc }, { public_label: asc }]
551
+ ) {
552
+ id
553
+ attribute_id
554
+ public_label
555
+ public_slug
556
+ sort_order
557
+ attribute {
558
+ id
559
+ name
560
+ type
561
+ }
562
+ values(
563
+ where: { is_visible: { _eq: true } }
564
+ order_by: [{ sort_order: asc }, { public_label: asc }]
565
+ ) {
566
+ id
567
+ attribute_value_id
568
+ public_label
569
+ public_slug
570
+ sort_order
571
+ attribute_value {
572
+ id
573
+ code
574
+ metadata
575
+ }
576
+ }
577
+ }
578
+ }
579
+ }
580
+ `
581
+ );
582
+ var GET_PRODUCT_BY_SLUG = (
583
+ /* GraphQL */
584
+ GET_PRODUCT_BY_PK.replace(
585
+ "query GetProductByPk(",
586
+ "query GetProductBySlug("
587
+ ).replace("$productId: uuid!", "$slug: String!").replace("id: { _eq: $productId }", "slug: { _eq: $slug }")
588
+ );
589
+ var GET_PRODUCT_SEO_ENTRIES = (
590
+ /* GraphQL */
591
+ `
592
+ query GetProductSeoEntries(
593
+ $companyId: uuid!
594
+ $chainId: uuid!
595
+ $websiteId: uuid!
596
+ $limit: Int!
597
+ $offset: Int!
598
+ ) {
599
+ products(
600
+ where: {
601
+ company_id: { _eq: $companyId }
602
+ slug: { _is_null: false }
603
+ product_website_publications: {
604
+ website_id: { _eq: $websiteId }
605
+ is_visible: { _eq: true }
606
+ }
607
+ chain_products: {
608
+ chain_id: { _eq: $chainId }
609
+ is_enabled: { _eq: true }
610
+ status: { _eq: active }
611
+ }
612
+ }
613
+ limit: $limit
614
+ offset: $offset
615
+ order_by: [{ updated_at: desc }, { created_at: desc }]
616
+ ) {
617
+ id
618
+ name
619
+ slug
620
+ seo_title
621
+ seo_description
622
+ seo_image_file_id
623
+ category
624
+ subcategory
625
+ created_at
626
+ updated_at
627
+ seo_image_file {
628
+ mimeType
629
+ name
630
+ }
631
+ product_media(
632
+ where: { file: { mimeType: { _ilike: "image/%" } } }
633
+ order_by: { order: asc }
634
+ limit: 1
635
+ ) {
636
+ id
637
+ file_id
638
+ order
639
+ file {
640
+ mimeType
641
+ name
642
+ }
643
+ }
644
+ product_variants(
645
+ where: { is_disabled: { _eq: false } }
646
+ order_by: { order: asc }
647
+ ) {
648
+ id
649
+ product_variant_prices(
650
+ where: {
651
+ price_type: { _in: [CLIENT, SALE, WHOLESALE, WHOLESALE_2] }
652
+ _or: [
653
+ { chain_id: { _eq: $chainId } }
654
+ { chain_id: { _is_null: true } }
655
+ ]
656
+ }
657
+ ) {
658
+ id
659
+ price
660
+ price_type
661
+ chain_id
662
+ }
663
+ }
664
+ }
665
+ }
666
+ `
667
+ );
668
+ var GET_CATEGORIES = (
669
+ /* GraphQL */
670
+ `
671
+ query GetCategories {
672
+ product_categories {
673
+ name
674
+ description
675
+ product_subcategories {
676
+ id
677
+ name
678
+ description
679
+ }
680
+ }
681
+ }
682
+ `
683
+ );
684
+ var GET_CATALOG_CATEGORY_LIST = (
685
+ /* GraphQL */
686
+ `
687
+ query GetCatalogCategoryList($companyId: uuid!, $key: String!) {
688
+ list: catalog_category_lists(
689
+ where: {
690
+ company_id: { _eq: $companyId }
691
+ key: { _eq: $key }
692
+ status: { _eq: published }
693
+ }
694
+ limit: 1
695
+ ) {
696
+ id
697
+ key
698
+ name
699
+ description
700
+ status
701
+ items(
702
+ where: { is_visible: { _eq: true } }
703
+ order_by: [{ sort_order: asc }, { public_label: asc }]
704
+ ) {
705
+ id
706
+ source_type
707
+ product_category
708
+ product_subcategory_id
709
+ public_label
710
+ public_slug
711
+ sort_order
712
+ category_ref {
713
+ name
714
+ description
715
+ }
716
+ subcategory_ref {
717
+ id
718
+ name
719
+ description
720
+ category
721
+ }
722
+ }
723
+ }
724
+ }
725
+ `
726
+ );
727
+ var GET_CATALOG_ATTRIBUTE_LIST = (
728
+ /* GraphQL */
729
+ `
730
+ query GetCatalogAttributeList($companyId: uuid!, $key: String!) {
731
+ list: catalog_attribute_lists(
732
+ where: {
733
+ company_id: { _eq: $companyId }
734
+ key: { _eq: $key }
735
+ status: { _eq: published }
736
+ }
737
+ limit: 1
738
+ ) {
739
+ id
740
+ key
741
+ name
742
+ description
743
+ status
744
+ items(
745
+ where: { is_visible: { _eq: true } }
746
+ order_by: [{ sort_order: asc }, { public_label: asc }]
747
+ ) {
748
+ id
749
+ attribute_id
750
+ public_label
751
+ public_slug
752
+ sort_order
753
+ attribute {
754
+ id
755
+ name
756
+ type
757
+ }
758
+ values(
759
+ where: { is_visible: { _eq: true } }
760
+ order_by: [{ sort_order: asc }, { public_label: asc }]
761
+ ) {
762
+ id
763
+ attribute_value_id
764
+ public_label
765
+ public_slug
766
+ sort_order
767
+ attribute_value {
768
+ id
769
+ code
770
+ metadata
771
+ }
772
+ }
773
+ }
774
+ }
775
+ }
776
+ `
777
+ );
778
+ var GET_CONTEXTUAL_CATALOG_ATTRIBUTE_LIST = (
779
+ /* GraphQL */
780
+ `
781
+ mutation GetContextualCatalogAttributeList(
782
+ $websiteId: uuid!
783
+ $key: String!
784
+ $onlyApplicable: Boolean
785
+ $filters: WebsiteCatalogFiltersInput
786
+ ) {
787
+ websiteGetAttributeList(
788
+ website_id: $websiteId
789
+ key: $key
790
+ only_applicable: $onlyApplicable
791
+ filters: $filters
792
+ ) {
793
+ success
794
+ errors
795
+ list {
796
+ id
797
+ key
798
+ name
799
+ description
800
+ status
801
+ items {
802
+ id
803
+ attribute_id
804
+ attribute_name
805
+ attribute_type
806
+ public_label
807
+ public_slug
808
+ sort_order
809
+ values {
810
+ id
811
+ attribute_value_id
812
+ code
813
+ public_label
814
+ public_slug
815
+ sort_order
816
+ metadata
817
+ }
818
+ }
819
+ }
820
+ }
821
+ }
822
+ `
823
+ );
824
+ var WEBSITE_GET_CATALOG_PAGE = (
825
+ /* GraphQL */
826
+ `
827
+ mutation WebsiteGetCatalogPage(
828
+ $websiteId: uuid!
829
+ $categoryListKey: String!
830
+ $attributeListKey: String!
831
+ $filters: WebsiteCatalogPageFiltersInput
832
+ $include: WebsiteCatalogPageIncludeInput
833
+ ) {
834
+ result: websiteGetCatalogPage(
835
+ website_id: $websiteId
836
+ category_list_key: $categoryListKey
837
+ attribute_list_key: $attributeListKey
838
+ filters: $filters
839
+ include: $include
840
+ ) {
841
+ success
842
+ products
843
+ category_list
844
+ attribute_list
845
+ filters
846
+ brands
847
+ pagination
848
+ applied_filters
849
+ debug
850
+ errors
851
+ }
852
+ }
853
+ `
854
+ );
855
+ var WEBSITE_GET_PRODUCT_RECOMMENDATIONS = (
856
+ /* GraphQL */
857
+ `
858
+ mutation WebsiteGetProductRecommendations(
859
+ $websiteId: uuid!
860
+ $productId: uuid!
861
+ $limit: Int
862
+ ) {
863
+ result: websiteGetProductRecommendations(
864
+ website_id: $websiteId
865
+ product_id: $productId
866
+ limit: $limit
867
+ ) {
868
+ success
869
+ products
870
+ strategy
871
+ errors
872
+ }
873
+ }
874
+ `
875
+ );
876
+ var GET_BRANDS = (
877
+ /* GraphQL */
878
+ `
879
+ query GetBrands($companyId: uuid!, $chainId: uuid!) {
880
+ brands(
881
+ where: {
882
+ company_id: { _eq: $companyId }
883
+ products: {
884
+ chain_products: {
885
+ chain_id: { _eq: $chainId }
886
+ is_enabled: { _eq: true }
887
+ status: { _eq: active }
888
+ }
889
+ }
890
+ }
891
+ order_by: { name: asc }
892
+ ) {
893
+ id
894
+ name
895
+ }
896
+ }
897
+ `
898
+ );
899
+ var CONTENT_ENTRY_FIELDS = (
900
+ /* GraphQL */
901
+ `
902
+ id
903
+ key
904
+ category_key
905
+ name
906
+ locale
907
+ status
908
+ tags
909
+ content_json
910
+ resolved_fields
911
+ published_at
912
+ updated_at
913
+ content_type {
914
+ id
915
+ key
916
+ name
917
+ kind
918
+ }
919
+ `
920
+ );
921
+ var WEBSITE_GET_CONTENT = (
922
+ /* GraphQL */
923
+ `
924
+ mutation WebsiteGetContent(
925
+ $websiteId: uuid!
926
+ $key: String
927
+ $categoryKey: String
928
+ $tag: String
929
+ $locale: String
930
+ ) {
931
+ result: websiteGetContent(
932
+ website_id: $websiteId
933
+ key: $key
934
+ category_key: $categoryKey
935
+ tag: $tag
936
+ locale: $locale
937
+ ) {
938
+ success
939
+ errors
940
+ entries {
941
+ ${CONTENT_ENTRY_FIELDS}
942
+ }
943
+ }
944
+ }
945
+ `
946
+ );
947
+ var WEBSITE_GET_PRODUCT_CONTENT = (
948
+ /* GraphQL */
949
+ `
950
+ mutation WebsiteGetProductContent(
951
+ $websiteId: uuid!
952
+ $productId: uuid!
953
+ $locale: String
954
+ ) {
955
+ result: websiteGetProductContent(
956
+ website_id: $websiteId
957
+ product_id: $productId
958
+ locale: $locale
959
+ ) {
960
+ success
961
+ errors
962
+ blocks {
963
+ id
964
+ product_id
965
+ locale
966
+ content_json
967
+ resolved_fields
968
+ sort_order
969
+ updated_at
970
+ content_type {
971
+ id
972
+ key
973
+ name
974
+ kind
975
+ }
976
+ }
977
+ }
978
+ }
979
+ `
980
+ );
981
+ var CHECK_AVAILABILITY = (
982
+ /* GraphQL */
983
+ `
984
+ query CheckAvailability($variantIds: [uuid!]!, $chainId: uuid!) {
985
+ websiteProductAvailability(
986
+ product_variant_ids: $variantIds
987
+ chain_id: $chainId
988
+ ) {
989
+ product_variant_id
990
+ in_stock
991
+ label
992
+ }
993
+ }
994
+ `
995
+ );
996
+ var CREATE_CART = (
997
+ /* GraphQL */
998
+ `
999
+ mutation CreateCart(
1000
+ $websiteId: uuid!
1001
+ $cartToken: String
1002
+ $contact: WebsiteGuestContactInput
1003
+ $fulfillmentFamily: String
1004
+ ) {
1005
+ websiteCreateCart(
1006
+ website_id: $websiteId
1007
+ cart_token: $cartToken
1008
+ contact: $contact
1009
+ fulfillment_family: $fulfillmentFamily
1010
+ ) {
1011
+ success
1012
+ order_id
1013
+ cart_token
1014
+ cart {
1015
+ id
1016
+ status
1017
+ discount
1018
+ tax
1019
+ total_without_tax
1020
+ total_with_tax
1021
+ total_with_discount
1022
+ created_at
1023
+ updated_at
1024
+ items {
1025
+ id
1026
+ product_variant_id
1027
+ quantity
1028
+ product_variant {
1029
+ id
1030
+ sku
1031
+ product {
1032
+ id
1033
+ name
1034
+ }
1035
+ prices {
1036
+ id
1037
+ price
1038
+ price_type
1039
+ chain_id
1040
+ }
1041
+ }
1042
+ }
1043
+ }
1044
+ errors
1045
+ }
1046
+ }
1047
+ `
1048
+ );
1049
+ var GET_CART = (
1050
+ /* GraphQL */
1051
+ `
1052
+ mutation GetCart($orderId: uuid!, $cartToken: String!) {
1053
+ websiteGetCart(order_id: $orderId, cart_token: $cartToken) {
1054
+ success
1055
+ order_id
1056
+ cart_token
1057
+ cart {
1058
+ id
1059
+ status
1060
+ discount
1061
+ tax
1062
+ total_without_tax
1063
+ total_with_tax
1064
+ total_with_discount
1065
+ created_at
1066
+ updated_at
1067
+ items {
1068
+ id
1069
+ product_variant_id
1070
+ quantity
1071
+ product_variant {
1072
+ id
1073
+ sku
1074
+ product {
1075
+ id
1076
+ name
1077
+ }
1078
+ prices {
1079
+ id
1080
+ price
1081
+ price_type
1082
+ chain_id
1083
+ }
1084
+ }
1085
+ }
1086
+ }
1087
+ errors
1088
+ }
1089
+ }
1090
+ `
1091
+ );
1092
+ var START_GUEST_CHECKOUT = (
1093
+ /* GraphQL */
1094
+ `
1095
+ mutation StartGuestCheckout(
1096
+ $websiteId: uuid!
1097
+ $cartToken: String
1098
+ $contact: WebsiteGuestContactInput!
1099
+ $items: [WebsiteCheckoutItemInput!]!
1100
+ $fulfillmentFamily: String
1101
+ ) {
1102
+ websiteStartGuestCheckout(
1103
+ website_id: $websiteId
1104
+ cart_token: $cartToken
1105
+ contact: $contact
1106
+ items: $items
1107
+ fulfillment_family: $fulfillmentFamily
1108
+ ) {
1109
+ success
1110
+ order_id
1111
+ cart_token
1112
+ cart {
1113
+ id
1114
+ status
1115
+ discount
1116
+ tax
1117
+ total_without_tax
1118
+ total_with_tax
1119
+ total_with_discount
1120
+ created_at
1121
+ updated_at
1122
+ items {
1123
+ id
1124
+ product_variant_id
1125
+ quantity
1126
+ product_variant {
1127
+ id
1128
+ sku
1129
+ product {
1130
+ id
1131
+ name
1132
+ }
1133
+ prices {
1134
+ id
1135
+ price
1136
+ price_type
1137
+ chain_id
1138
+ }
1139
+ }
1140
+ }
1141
+ }
1142
+ errors
1143
+ }
1144
+ }
1145
+ `
1146
+ );
1147
+ var SET_CART_ITEM = (
1148
+ /* GraphQL */
1149
+ `
1150
+ mutation SetCartItem(
1151
+ $orderId: uuid!
1152
+ $cartToken: String!
1153
+ $variantId: uuid!
1154
+ $quantity: numeric!
1155
+ ) {
1156
+ websiteSetCartItem(
1157
+ order_id: $orderId
1158
+ cart_token: $cartToken
1159
+ product_variant_id: $variantId
1160
+ quantity: $quantity
1161
+ ) {
1162
+ success
1163
+ order_id
1164
+ cart_token
1165
+ cart {
1166
+ id
1167
+ status
1168
+ discount
1169
+ tax
1170
+ total_without_tax
1171
+ total_with_tax
1172
+ total_with_discount
1173
+ created_at
1174
+ updated_at
1175
+ items {
1176
+ id
1177
+ product_variant_id
1178
+ quantity
1179
+ product_variant {
1180
+ id
1181
+ sku
1182
+ product {
1183
+ id
1184
+ name
1185
+ }
1186
+ prices {
1187
+ id
1188
+ price
1189
+ price_type
1190
+ chain_id
1191
+ }
1192
+ }
1193
+ }
1194
+ }
1195
+ errors
1196
+ }
1197
+ }
1198
+ `
1199
+ );
1200
+ var REMOVE_CART_ITEM = (
1201
+ /* GraphQL */
1202
+ `
1203
+ mutation RemoveCartItem(
1204
+ $orderId: uuid!
1205
+ $cartToken: String!
1206
+ $itemId: uuid!
1207
+ ) {
1208
+ websiteRemoveCartItem(
1209
+ order_id: $orderId
1210
+ cart_token: $cartToken
1211
+ item_id: $itemId
1212
+ ) {
1213
+ success
1214
+ order_id
1215
+ cart_token
1216
+ cart {
1217
+ id
1218
+ status
1219
+ discount
1220
+ tax
1221
+ total_without_tax
1222
+ total_with_tax
1223
+ total_with_discount
1224
+ created_at
1225
+ updated_at
1226
+ items {
1227
+ id
1228
+ product_variant_id
1229
+ quantity
1230
+ product_variant {
1231
+ id
1232
+ sku
1233
+ product {
1234
+ id
1235
+ name
1236
+ }
1237
+ prices {
1238
+ id
1239
+ price
1240
+ price_type
1241
+ chain_id
1242
+ }
1243
+ }
1244
+ }
1245
+ }
1246
+ errors
1247
+ }
1248
+ }
1249
+ `
1250
+ );
1251
+ var CHECKOUT = (
1252
+ /* GraphQL */
1253
+ `
1254
+ mutation Checkout(
1255
+ $orderId: uuid!
1256
+ $cartToken: String!
1257
+ $contact: WebsiteGuestContactInput!
1258
+ $fulfillmentFamily: String
1259
+ $address: WebsiteAddressInput
1260
+ $shippingQuoteId: uuid
1261
+ $paymentMode: String
1262
+ $paymentMethodId: uuid
1263
+ $buyerAccountOptIn: Boolean
1264
+ $initialPayment: WebsiteInitialPaymentInput
1265
+ ) {
1266
+ websiteCheckout(
1267
+ order_id: $orderId
1268
+ cart_token: $cartToken
1269
+ contact: $contact
1270
+ fulfillment_family: $fulfillmentFamily
1271
+ address: $address
1272
+ shipping_quote_id: $shippingQuoteId
1273
+ payment_mode: $paymentMode
1274
+ payment_method_id: $paymentMethodId
1275
+ buyer_account_opt_in: $buyerAccountOptIn
1276
+ initial_payment: $initialPayment
1277
+ ) {
1278
+ success
1279
+ order_id
1280
+ order_number
1281
+ payment_method_id
1282
+ public_order_token
1283
+ requires_payment_session
1284
+ buyer_account_invitation_requested
1285
+ errors
1286
+ }
1287
+ }
1288
+ `
1289
+ );
1290
+ var GET_LOCATION_OPTIONS = (
1291
+ /* GraphQL */
1292
+ `
1293
+ mutation GetLocationOptions($websiteId: uuid!) {
1294
+ websiteGetLocationOptions(website_id: $websiteId) {
1295
+ success
1296
+ country_code
1297
+ departments {
1298
+ department_id
1299
+ department_name
1300
+ department_code
1301
+ municipalities {
1302
+ municipality_id
1303
+ municipality_name
1304
+ municipality_code
1305
+ department_id
1306
+ department_code
1307
+ }
1308
+ }
1309
+ errors
1310
+ }
1311
+ }
1312
+ `
1313
+ );
1314
+ var GET_DELIVERY_OPTIONS = (
1315
+ /* GraphQL */
1316
+ `
1317
+ mutation GetDeliveryOptions($websiteId: uuid!) {
1318
+ websiteGetDeliveryOptions(website_id: $websiteId) {
1319
+ success
1320
+ delivery_options {
1321
+ id
1322
+ fulfillment_family
1323
+ label
1324
+ description
1325
+ is_enabled
1326
+ pickup_info {
1327
+ label
1328
+ address
1329
+ schedule
1330
+ notes
1331
+ }
1332
+ }
1333
+ errors
1334
+ }
1335
+ }
1336
+ `
1337
+ );
1338
+ var GET_SHIPPING_QUOTES = (
1339
+ /* GraphQL */
1340
+ `
1341
+ mutation GetShippingQuotes(
1342
+ $websiteId: uuid!
1343
+ $orderId: uuid
1344
+ $cartToken: String
1345
+ $items: [WebsiteCheckoutItemInput!]
1346
+ $address: WebsiteAddressInput!
1347
+ ) {
1348
+ websiteGetShippingQuotes(
1349
+ website_id: $websiteId
1350
+ order_id: $orderId
1351
+ cart_token: $cartToken
1352
+ items: $items
1353
+ address: $address
1354
+ ) {
1355
+ success
1356
+ quotes {
1357
+ quote_id
1358
+ shipping_service_id
1359
+ label
1360
+ service_name
1361
+ base_amount
1362
+ amount
1363
+ currency
1364
+ collection_mode
1365
+ total_policy
1366
+ estimated_delivery
1367
+ free_shipping_eligible
1368
+ free_shipping_applied
1369
+ free_shipping_threshold
1370
+ free_shipping_remaining
1371
+ free_shipping_eligible_subtotal
1372
+ }
1373
+ errors
1374
+ }
1375
+ }
1376
+ `
1377
+ );
1378
+ var GET_SHIPPING_POLICY = (
1379
+ /* GraphQL */
1380
+ `
1381
+ mutation GetShippingPolicy($websiteId: uuid!) {
1382
+ websiteGetShippingPolicy(website_id: $websiteId) {
1383
+ success
1384
+ policy {
1385
+ free_shipping_enabled
1386
+ free_shipping_threshold
1387
+ currency
1388
+ }
1389
+ errors
1390
+ }
1391
+ }
1392
+ `
1393
+ );
1394
+ var GET_FREE_SHIPPING_PROGRESS = (
1395
+ /* GraphQL */
1396
+ `
1397
+ mutation GetFreeShippingProgress(
1398
+ $websiteId: uuid!
1399
+ $items: [WebsiteCheckoutItemInput!]!
1400
+ ) {
1401
+ websiteGetFreeShippingProgress(website_id: $websiteId, items: $items) {
1402
+ success
1403
+ progress {
1404
+ enabled
1405
+ threshold
1406
+ remaining
1407
+ eligible_subtotal
1408
+ threshold_reached
1409
+ currency
1410
+ }
1411
+ errors
1412
+ }
1413
+ }
1414
+ `
1415
+ );
1416
+ var GET_PAYMENT_METHODS = (
1417
+ /* GraphQL */
1418
+ `
1419
+ mutation GetPaymentMethods($websiteId: uuid!) {
1420
+ websiteGetPaymentMethods(website_id: $websiteId) {
1421
+ success
1422
+ payment_methods {
1423
+ id
1424
+ code
1425
+ label
1426
+ payment_method_type
1427
+ gateway_code
1428
+ description
1429
+ is_enabled
1430
+ sort_order
1431
+ requires_payment_session
1432
+ public_config
1433
+ }
1434
+ errors
1435
+ }
1436
+ }
1437
+ `
1438
+ );
1439
+ var GET_PAYMENT_OPTIONS = (
1440
+ /* GraphQL */
1441
+ `
1442
+ mutation GetPaymentOptions($websiteId: uuid!, $fulfillmentFamily: String!) {
1443
+ websiteGetPaymentOptions(
1444
+ website_id: $websiteId
1445
+ fulfillment_family: $fulfillmentFamily
1446
+ ) {
1447
+ success
1448
+ payment_options {
1449
+ id
1450
+ mode
1451
+ fulfillment_family
1452
+ label
1453
+ description
1454
+ requires_payment_method
1455
+ allows_initial_payment
1456
+ initial_payment_policy
1457
+ requires_initial_payment
1458
+ min_initial_amount
1459
+ suggested_initial_amount
1460
+ sort_order
1461
+ methods {
1462
+ id
1463
+ payment_method_id
1464
+ code
1465
+ label
1466
+ payment_method_type
1467
+ gateway_code
1468
+ description
1469
+ instructions
1470
+ qr_file_id
1471
+ requires_payment_session
1472
+ public_config
1473
+ sort_order
1474
+ }
1475
+ }
1476
+ errors
1477
+ }
1478
+ }
1479
+ `
1480
+ );
1481
+ var VALIDATE_COUPON = (
1482
+ /* GraphQL */
1483
+ `
1484
+ mutation ValidateCoupon(
1485
+ $orderId: uuid!
1486
+ $cartToken: String!
1487
+ $code: String!
1488
+ ) {
1489
+ websiteValidateCoupon(
1490
+ order_id: $orderId
1491
+ cart_token: $cartToken
1492
+ code: $code
1493
+ ) {
1494
+ success
1495
+ order_id
1496
+ coupon_id
1497
+ code
1498
+ name
1499
+ coupon_type
1500
+ discount_amount
1501
+ eligible_subtotal
1502
+ message
1503
+ cart {
1504
+ id
1505
+ status
1506
+ discount
1507
+ total_without_tax
1508
+ tax
1509
+ total_with_tax
1510
+ total_with_discount
1511
+ created_at
1512
+ updated_at
1513
+ items {
1514
+ id
1515
+ product_variant_id
1516
+ quantity
1517
+ product_variant {
1518
+ id
1519
+ sku
1520
+ product {
1521
+ id
1522
+ name
1523
+ }
1524
+ prices {
1525
+ id
1526
+ price
1527
+ price_type
1528
+ chain_id
1529
+ }
1530
+ }
1531
+ }
1532
+ }
1533
+ errors
1534
+ }
1535
+ }
1536
+ `
1537
+ );
1538
+ var APPLY_COUPON = (
1539
+ /* GraphQL */
1540
+ `
1541
+ mutation ApplyCoupon($orderId: uuid!, $cartToken: String!, $code: String!) {
1542
+ websiteApplyCoupon(
1543
+ order_id: $orderId
1544
+ cart_token: $cartToken
1545
+ code: $code
1546
+ ) {
1547
+ success
1548
+ order_id
1549
+ coupon_id
1550
+ code
1551
+ name
1552
+ coupon_type
1553
+ discount_amount
1554
+ eligible_subtotal
1555
+ message
1556
+ cart {
1557
+ id
1558
+ status
1559
+ discount
1560
+ total_without_tax
1561
+ tax
1562
+ total_with_tax
1563
+ total_with_discount
1564
+ created_at
1565
+ updated_at
1566
+ items {
1567
+ id
1568
+ product_variant_id
1569
+ quantity
1570
+ product_variant {
1571
+ id
1572
+ sku
1573
+ product {
1574
+ id
1575
+ name
1576
+ }
1577
+ prices {
1578
+ id
1579
+ price
1580
+ price_type
1581
+ chain_id
1582
+ }
1583
+ }
1584
+ }
1585
+ }
1586
+ errors
1587
+ }
1588
+ }
1589
+ `
1590
+ );
1591
+ var REMOVE_COUPON = (
1592
+ /* GraphQL */
1593
+ `
1594
+ mutation RemoveCoupon(
1595
+ $orderId: uuid!
1596
+ $cartToken: String!
1597
+ $couponId: uuid!
1598
+ ) {
1599
+ websiteRemoveCoupon(
1600
+ order_id: $orderId
1601
+ cart_token: $cartToken
1602
+ coupon_id: $couponId
1603
+ ) {
1604
+ success
1605
+ order_id
1606
+ coupon_id
1607
+ code
1608
+ name
1609
+ coupon_type
1610
+ discount_amount
1611
+ eligible_subtotal
1612
+ message
1613
+ cart {
1614
+ id
1615
+ status
1616
+ discount
1617
+ total_without_tax
1618
+ tax
1619
+ total_with_tax
1620
+ total_with_discount
1621
+ created_at
1622
+ updated_at
1623
+ items {
1624
+ id
1625
+ product_variant_id
1626
+ quantity
1627
+ product_variant {
1628
+ id
1629
+ sku
1630
+ product {
1631
+ id
1632
+ name
1633
+ }
1634
+ prices {
1635
+ id
1636
+ price
1637
+ price_type
1638
+ chain_id
1639
+ }
1640
+ }
1641
+ }
1642
+ }
1643
+ errors
1644
+ }
1645
+ }
1646
+ `
1647
+ );
1648
+ var CREATE_PAYMENT_SESSION = (
1649
+ /* GraphQL */
1650
+ `
1651
+ mutation CreatePaymentSession(
1652
+ $orderId: uuid!
1653
+ $cartToken: String
1654
+ $publicOrderToken: String
1655
+ $paymentMethodId: uuid!
1656
+ $returnUrl: String!
1657
+ ) {
1658
+ websiteCreatePaymentSession(
1659
+ order_id: $orderId
1660
+ cart_token: $cartToken
1661
+ public_order_token: $publicOrderToken
1662
+ payment_method_id: $paymentMethodId
1663
+ return_url: $returnUrl
1664
+ ) {
1665
+ success
1666
+ order_id
1667
+ payment_method_id
1668
+ payment_session_id
1669
+ gateway_code
1670
+ integration_mode
1671
+ status
1672
+ redirect_url
1673
+ public_payload
1674
+ errors
1675
+ }
1676
+ }
1677
+ `
1678
+ );
1679
+ var RECONCILE_PAYMENT_SESSION = (
1680
+ /* GraphQL */
1681
+ `
1682
+ mutation ReconcilePaymentSession(
1683
+ $orderId: uuid!
1684
+ $publicOrderToken: String!
1685
+ $paymentSessionId: uuid!
1686
+ $gatewayTransactionId: String!
1687
+ ) {
1688
+ websiteReconcilePaymentSession(
1689
+ order_id: $orderId
1690
+ public_order_token: $publicOrderToken
1691
+ payment_session_id: $paymentSessionId
1692
+ gateway_transaction_id: $gatewayTransactionId
1693
+ ) {
1694
+ success
1695
+ order_id
1696
+ payment_method_id
1697
+ payment_session_id
1698
+ gateway_code
1699
+ integration_mode
1700
+ status
1701
+ redirect_url
1702
+ public_payload
1703
+ errors
1704
+ }
1705
+ }
1706
+ `
1707
+ );
1708
+ var GET_ORDERS = (
1709
+ /* GraphQL */
1710
+ `
1711
+ query GetOrders($limit: Int = 20, $offset: Int = 0) {
1712
+ orders(
1713
+ where: { order_number: { _is_null: false } }
1714
+ order_by: { created_at: desc }
1715
+ limit: $limit
1716
+ offset: $offset
1717
+ ) {
1718
+ id
1719
+ order_number
1720
+ status
1721
+ fulfillment_family
1722
+ discount
1723
+ tax
1724
+ total_without_tax
1725
+ total_with_tax
1726
+ total_with_discount
1727
+ created_at
1728
+ updated_at
1729
+ order_product_variants {
1730
+ id
1731
+ product_variant_id
1732
+ quantity
1733
+ product_variant {
1734
+ sku
1735
+ product {
1736
+ id
1737
+ name
1738
+ }
1739
+ }
1740
+ }
1741
+ }
1742
+ }
1743
+ `
1744
+ );
1745
+ var GET_ORDER_BY_PK = (
1746
+ /* GraphQL */
1747
+ `
1748
+ query GetOrderByPk($orderId: uuid!) {
1749
+ orders(where: { id: { _eq: $orderId } }) {
1750
+ id
1751
+ order_number
1752
+ status
1753
+ fulfillment_family
1754
+ discount
1755
+ tax
1756
+ total_without_tax
1757
+ total_with_tax
1758
+ total_with_discount
1759
+ created_at
1760
+ updated_at
1761
+ order_product_variants {
1762
+ id
1763
+ product_variant_id
1764
+ quantity
1765
+ product_variant {
1766
+ sku
1767
+ product {
1768
+ id
1769
+ name
1770
+ }
1771
+ }
1772
+ }
1773
+ }
1774
+ }
1775
+ `
1776
+ );
1777
+ var GET_PUBLIC_ORDER = (
1778
+ /* GraphQL */
1779
+ `
1780
+ mutation GetPublicOrder($websiteId: uuid!, $orderId: uuid!, $token: String!) {
1781
+ websiteGetPublicOrder(
1782
+ website_id: $websiteId
1783
+ order_id: $orderId
1784
+ token: $token
1785
+ ) {
1786
+ success
1787
+ order {
1788
+ order_id
1789
+ order_number
1790
+ status
1791
+ payment_status
1792
+ fulfillment_family
1793
+ delivery {
1794
+ fulfillment_family
1795
+ shipping {
1796
+ label
1797
+ amount
1798
+ method_type
1799
+ collection_mode
1800
+ total_policy
1801
+ address
1802
+ }
1803
+ pickup {
1804
+ label
1805
+ }
1806
+ }
1807
+ payment {
1808
+ mode
1809
+ label
1810
+ method {
1811
+ id
1812
+ label
1813
+ payment_method_type
1814
+ gateway_code
1815
+ requires_payment_session
1816
+ }
1817
+ initial_payment {
1818
+ amount
1819
+ payment_method_id
1820
+ method_label
1821
+ payment_method_type
1822
+ gateway_code
1823
+ requires_payment_session
1824
+ }
1825
+ requires_payment_session
1826
+ next_action
1827
+ session {
1828
+ id
1829
+ gateway_code
1830
+ integration_mode
1831
+ status
1832
+ reference
1833
+ amount
1834
+ currency
1835
+ gateway_transaction_id
1836
+ error_message
1837
+ expires_at
1838
+ created_at
1839
+ updated_at
1840
+ }
1841
+ }
1842
+ totals {
1843
+ subtotal
1844
+ tax
1845
+ discount
1846
+ total
1847
+ total_with_discount
1848
+ }
1849
+ items {
1850
+ id
1851
+ product_variant_id
1852
+ sku
1853
+ name
1854
+ image_file_id
1855
+ quantity
1856
+ unit_price
1857
+ line_total
1858
+ }
1859
+ next_steps
1860
+ created_at
1861
+ updated_at
1862
+ }
1863
+ errors
1864
+ }
1865
+ }
1866
+ `
1867
+ );
1868
+ var GET_PROFILE = (
1869
+ /* GraphQL */
1870
+ `
1871
+ query GetProfile {
1872
+ clients {
1873
+ id
1874
+ party_id
1875
+ party {
1876
+ first_name
1877
+ last_name
1878
+ email
1879
+ phone_e164
1880
+ phone_national_number
1881
+ address
1882
+ address_complement
1883
+ birthdate
1884
+ legal_name
1885
+ commercial_name
1886
+ country_code
1887
+ department_code
1888
+ municipality_code
1889
+ geo_country {
1890
+ name
1891
+ }
1892
+ geo_department {
1893
+ name
1894
+ }
1895
+ geo_municipality {
1896
+ name
1897
+ }
1898
+ shipping_addresses(
1899
+ where: { is_primary: { _eq: true } }
1900
+ order_by: { created_at: asc }
1901
+ limit: 1
1902
+ ) {
1903
+ address
1904
+ address_complement
1905
+ country_code
1906
+ department_code
1907
+ municipality_code
1908
+ geo_country {
1909
+ name
1910
+ }
1911
+ geo_department {
1912
+ name
1913
+ }
1914
+ geo_municipality {
1915
+ name
1916
+ }
1917
+ }
1918
+ }
1919
+ }
1920
+ }
1921
+ `
1922
+ );
1923
+ var GET_PROFILE_ADDRESSES = (
1924
+ /* GraphQL */
1925
+ `
1926
+ query GetProfileAddresses {
1927
+ clients {
1928
+ party_id
1929
+ party {
1930
+ first_name
1931
+ last_name
1932
+ display_name
1933
+ phone_e164
1934
+ phone_national_number
1935
+ address
1936
+ address_complement
1937
+ country_code
1938
+ department_code
1939
+ municipality_code
1940
+ geo_country {
1941
+ name
1942
+ }
1943
+ geo_department {
1944
+ name
1945
+ }
1946
+ geo_municipality {
1947
+ name
1948
+ }
1949
+ shipping_addresses(
1950
+ order_by: [{ is_primary: desc }, { created_at: asc }]
1951
+ ) {
1952
+ id
1953
+ label
1954
+ recipient_name
1955
+ phone_e164
1956
+ address
1957
+ address_complement
1958
+ notes
1959
+ is_primary
1960
+ country_code
1961
+ department_code
1962
+ municipality_code
1963
+ geo_country {
1964
+ name
1965
+ }
1966
+ geo_department {
1967
+ name
1968
+ }
1969
+ geo_municipality {
1970
+ name
1971
+ }
1972
+ }
1973
+ }
1974
+ }
1975
+ }
1976
+ `
1977
+ );
1978
+ var UPDATE_PROFILE = (
1979
+ /* GraphQL */
1980
+ `
1981
+ mutation UpdateProfile($partyId: uuid!, $data: business_parties_set_input!) {
1982
+ update_business_parties_by_pk(pk_columns: { id: $partyId }, _set: $data) {
1983
+ email
1984
+ first_name
1985
+ last_name
1986
+ phone_e164
1987
+ phone_national_number
1988
+ address
1989
+ address_complement
1990
+ birthdate
1991
+ legal_name
1992
+ commercial_name
1993
+ country_code
1994
+ department_code
1995
+ municipality_code
1996
+ geo_country {
1997
+ name
1998
+ }
1999
+ geo_department {
2000
+ name
2001
+ }
2002
+ geo_municipality {
2003
+ name
2004
+ }
2005
+ shipping_addresses(
2006
+ where: { is_primary: { _eq: true } }
2007
+ order_by: { created_at: asc }
2008
+ limit: 1
2009
+ ) {
2010
+ address
2011
+ address_complement
2012
+ country_code
2013
+ department_code
2014
+ municipality_code
2015
+ geo_country {
2016
+ name
2017
+ }
2018
+ geo_department {
2019
+ name
2020
+ }
2021
+ geo_municipality {
2022
+ name
2023
+ }
2024
+ }
2025
+ }
2026
+ }
2027
+ `
2028
+ );
2029
+ var GET_PRODUCT_FILTERS = (
2030
+ /* GraphQL */
2031
+ `
2032
+ query GetProductFilters(
2033
+ $companyId: uuid!
2034
+ $chainId: uuid!
2035
+ $where: products_bool_exp = {}
2036
+ $attributeListKey: String!
2037
+ ) {
2038
+ products(
2039
+ where: {
2040
+ _and: [
2041
+ { company_id: { _eq: $companyId } }
2042
+ {
2043
+ chain_products: {
2044
+ chain_id: { _eq: $chainId }
2045
+ is_enabled: { _eq: true }
2046
+ status: { _eq: active }
2047
+ }
2048
+ }
2049
+ $where
2050
+ ]
2051
+ }
2052
+ ) {
2053
+ id
2054
+ category
2055
+ subcategory
2056
+ color
2057
+ brand {
2058
+ id
2059
+ name
2060
+ }
2061
+ product_attributes {
2062
+ id
2063
+ value
2064
+ attribute_value {
2065
+ id
2066
+ code
2067
+ sort_order
2068
+ metadata
2069
+ }
2070
+ attribute {
2071
+ id
2072
+ name
2073
+ }
2074
+ }
2075
+ product_variants(where: { is_disabled: { _eq: false } }) {
2076
+ id
2077
+ product_variant_prices(
2078
+ where: {
2079
+ price_type: { _in: [CLIENT, SALE, WHOLESALE, WHOLESALE_2] }
2080
+ _or: [
2081
+ { chain_id: { _eq: $chainId } }
2082
+ { chain_id: { _is_null: true } }
2083
+ ]
2084
+ }
2085
+ ) {
2086
+ id
2087
+ price
2088
+ price_type
2089
+ chain_id
2090
+ }
2091
+ product_variant_attributes(order_by: { order: asc }) {
2092
+ id
2093
+ value
2094
+ order
2095
+ attribute_value {
2096
+ id
2097
+ code
2098
+ sort_order
2099
+ metadata
2100
+ }
2101
+ attribute {
2102
+ id
2103
+ name
2104
+ }
2105
+ }
2106
+ }
2107
+ }
2108
+ catalog_attribute_lists(
2109
+ where: {
2110
+ company_id: { _eq: $companyId }
2111
+ key: { _eq: $attributeListKey }
2112
+ status: { _eq: published }
2113
+ }
2114
+ limit: 1
2115
+ ) {
2116
+ id
2117
+ key
2118
+ name
2119
+ description
2120
+ status
2121
+ items(
2122
+ where: { is_visible: { _eq: true } }
2123
+ order_by: [{ sort_order: asc }, { public_label: asc }]
2124
+ ) {
2125
+ id
2126
+ attribute_id
2127
+ public_label
2128
+ public_slug
2129
+ sort_order
2130
+ attribute {
2131
+ id
2132
+ name
2133
+ type
2134
+ }
2135
+ values(
2136
+ where: { is_visible: { _eq: true } }
2137
+ order_by: [{ sort_order: asc }, { public_label: asc }]
2138
+ ) {
2139
+ id
2140
+ attribute_value_id
2141
+ public_label
2142
+ public_slug
2143
+ sort_order
2144
+ attribute_value {
2145
+ id
2146
+ code
2147
+ metadata
2148
+ }
2149
+ }
2150
+ }
2151
+ }
2152
+ }
2153
+ `
2154
+ );
2155
+ var GET_SEARCH_SUGGESTIONS = (
2156
+ /* GraphQL */
2157
+ `
2158
+ query GetSearchSuggestions(
2159
+ $companyId: uuid!
2160
+ $chainId: uuid!
2161
+ $term: String!
2162
+ $productLimit: Int = 5
2163
+ $brandLimit: Int = 3
2164
+ ) {
2165
+ products(
2166
+ where: {
2167
+ company_id: { _eq: $companyId }
2168
+ chain_products: {
2169
+ chain_id: { _eq: $chainId }
2170
+ is_enabled: { _eq: true }
2171
+ status: { _eq: active }
2172
+ }
2173
+ name: { _ilike: $term }
2174
+ }
2175
+ limit: $productLimit
2176
+ order_by: { name: asc }
2177
+ ) {
2178
+ id
2179
+ name
2180
+ }
2181
+ brands(
2182
+ where: {
2183
+ company_id: { _eq: $companyId }
2184
+ name: { _ilike: $term }
2185
+ products: {
2186
+ chain_products: {
2187
+ chain_id: { _eq: $chainId }
2188
+ is_enabled: { _eq: true }
2189
+ status: { _eq: active }
2190
+ }
2191
+ }
2192
+ }
2193
+ limit: $brandLimit
2194
+ order_by: { name: asc }
2195
+ ) {
2196
+ id
2197
+ name
2198
+ }
2199
+ }
2200
+ `
2201
+ );
2202
+
2203
+ // libs/sdk/src/modules/availability.ts
2204
+ var MAX_AVAILABILITY_VARIANT_IDS = 100;
2205
+ function mapAvailability(raw) {
2206
+ return {
2207
+ productVariantId: raw.product_variant_id,
2208
+ inStock: raw.in_stock,
2209
+ label: raw.label
2210
+ };
2211
+ }
2212
+ function chunkArray(items, chunkSize) {
2213
+ const chunks = [];
2214
+ for (let index = 0; index < items.length; index += chunkSize) {
2215
+ chunks.push(items.slice(index, index + chunkSize));
2216
+ }
2217
+ return chunks;
2218
+ }
2219
+ function uniqueValues(values) {
2220
+ return Array.from(new Set(values));
2221
+ }
2222
+ function createAvailabilityModule(graphqlUrl, getHeaders, getContext) {
2223
+ return {
2224
+ async checkAvailability(productVariantIds) {
2225
+ const ctx = getContext();
2226
+ if (!ctx.chainId) {
2227
+ throw new WebsiteError(
2228
+ "This website has no chain configured. Stock availability requires a chain_id.",
2229
+ "NO_CHAIN"
2230
+ );
2231
+ }
2232
+ if (productVariantIds.length === 0) return [];
2233
+ const variantIds = uniqueValues(productVariantIds);
2234
+ const results = [];
2235
+ for (const chunk of chunkArray(variantIds, MAX_AVAILABILITY_VARIANT_IDS)) {
2236
+ const data = await executeGraphQL(
2237
+ graphqlUrl,
2238
+ CHECK_AVAILABILITY,
2239
+ {
2240
+ variantIds: chunk,
2241
+ chainId: ctx.chainId
2242
+ },
2243
+ getHeaders
2244
+ );
2245
+ results.push(
2246
+ ...(data.websiteProductAvailability ?? []).map(mapAvailability)
2247
+ );
2248
+ }
2249
+ return results;
2250
+ }
2251
+ };
2252
+ }
2253
+
2254
+ // libs/sdk/src/modules/cart.ts
2255
+ function mapCartItem(raw) {
2256
+ return {
2257
+ id: raw.id,
2258
+ productVariantId: raw.product_variant_id,
2259
+ quantity: raw.quantity,
2260
+ productVariant: {
2261
+ sku: raw.product_variant.sku,
2262
+ product: raw.product_variant.product,
2263
+ prices: raw.product_variant.prices.map((p) => ({
2264
+ id: p.id,
2265
+ price: p.price,
2266
+ priceType: p.price_type,
2267
+ chainId: p.chain_id
2268
+ }))
2269
+ }
2270
+ };
2271
+ }
2272
+ function mapCart(raw, cartToken) {
2273
+ return {
2274
+ id: raw.id,
2275
+ cartToken,
2276
+ items: raw.items.map(mapCartItem),
2277
+ totals: {
2278
+ subtotal: raw.total_without_tax,
2279
+ tax: raw.tax,
2280
+ discount: raw.discount,
2281
+ total: raw.total_with_tax
2282
+ },
2283
+ status: raw.status,
2284
+ createdAt: raw.created_at,
2285
+ updatedAt: raw.updated_at
2286
+ };
2287
+ }
2288
+ function assertActionSuccess(result, fallbackCode) {
2289
+ if (!result.success || !result.order_id || !result.cart_token || !result.cart) {
2290
+ const message = result.errors?.join("; ") || "Cart action failed";
2291
+ throw new WebsiteError(message, fallbackCode);
2292
+ }
2293
+ }
2294
+ function requireCartSession(storage) {
2295
+ const session = storage.get();
2296
+ if (!session) {
2297
+ throw new WebsiteError(
2298
+ "No guest cart session found",
2299
+ "CART_SESSION_MISSING"
2300
+ );
2301
+ }
2302
+ return session;
2303
+ }
2304
+ function createCartModule(graphqlUrl, getHeaders, getContext, cartSessionStorage) {
2305
+ async function fetchCart(orderId, cartToken) {
2306
+ const data = await executeGraphQL(graphqlUrl, GET_CART, { orderId, cartToken }, getHeaders);
2307
+ const result = data.websiteGetCart;
2308
+ if (!result.success) {
2309
+ if (result.errors?.includes("CART_NOT_FOUND")) {
2310
+ cartSessionStorage.clear();
2311
+ return null;
2312
+ }
2313
+ throw new WebsiteError(
2314
+ result.errors?.join("; ") || "Cart could not be retrieved",
2315
+ "CART_FETCH_ERROR"
2316
+ );
2317
+ }
2318
+ assertActionSuccess(result, "CART_FETCH_ERROR");
2319
+ cartSessionStorage.set({
2320
+ orderId: result.order_id,
2321
+ cartToken: result.cart_token
2322
+ });
2323
+ return mapCart(result.cart, result.cart_token);
2324
+ }
2325
+ return {
2326
+ async getOrCreate(contact, fulfillmentFamily) {
2327
+ const ctx = getContext();
2328
+ if (!ctx.chainId) {
2329
+ throw new WebsiteError(
2330
+ "This website has no chain configured. Cart requires a chain_id.",
2331
+ "NO_CHAIN"
2332
+ );
2333
+ }
2334
+ const existing = cartSessionStorage.get();
2335
+ const data = await executeGraphQL(
2336
+ graphqlUrl,
2337
+ CREATE_CART,
2338
+ {
2339
+ websiteId: ctx.websiteId,
2340
+ cartToken: existing?.cartToken,
2341
+ contact,
2342
+ fulfillmentFamily
2343
+ },
2344
+ getHeaders
2345
+ );
2346
+ const result = data.websiteCreateCart;
2347
+ assertActionSuccess(result, "CART_CREATE_ERROR");
2348
+ cartSessionStorage.set({
2349
+ orderId: result.order_id,
2350
+ cartToken: result.cart_token
2351
+ });
2352
+ return mapCart(result.cart, result.cart_token);
2353
+ },
2354
+ async startCheckout(input) {
2355
+ const ctx = getContext();
2356
+ if (!ctx.chainId) {
2357
+ throw new WebsiteError(
2358
+ "This website has no chain configured. Checkout requires a chain_id.",
2359
+ "NO_CHAIN"
2360
+ );
2361
+ }
2362
+ const existing = cartSessionStorage.get();
2363
+ const data = await executeGraphQL(
2364
+ graphqlUrl,
2365
+ START_GUEST_CHECKOUT,
2366
+ {
2367
+ websiteId: ctx.websiteId,
2368
+ cartToken: existing?.cartToken,
2369
+ contact: input.contact,
2370
+ fulfillmentFamily: input.fulfillmentFamily,
2371
+ items: input.items.map((item) => ({
2372
+ product_variant_id: item.productVariantId,
2373
+ quantity: item.quantity
2374
+ }))
2375
+ },
2376
+ getHeaders
2377
+ );
2378
+ const result = data.websiteStartGuestCheckout;
2379
+ assertActionSuccess(result, "CHECKOUT_START_ERROR");
2380
+ cartSessionStorage.set({
2381
+ orderId: result.order_id,
2382
+ cartToken: result.cart_token
2383
+ });
2384
+ return mapCart(result.cart, result.cart_token);
2385
+ },
2386
+ async get() {
2387
+ const session = cartSessionStorage.get();
2388
+ if (!session) return null;
2389
+ return fetchCart(session.orderId, session.cartToken);
2390
+ },
2391
+ async setItem(productVariantId, quantity) {
2392
+ if (quantity <= 0) {
2393
+ throw new WebsiteError(
2394
+ "Quantity must be greater than zero",
2395
+ "INVALID_QUANTITY"
2396
+ );
2397
+ }
2398
+ const session = cartSessionStorage.get();
2399
+ if (!session) {
2400
+ throw new WebsiteError(
2401
+ "Start checkout with contact before syncing cart items to Bacano",
2402
+ "CART_SESSION_MISSING"
2403
+ );
2404
+ }
2405
+ const data = await executeGraphQL(
2406
+ graphqlUrl,
2407
+ SET_CART_ITEM,
2408
+ {
2409
+ orderId: session.orderId,
2410
+ cartToken: session.cartToken,
2411
+ variantId: productVariantId,
2412
+ quantity
2413
+ },
2414
+ getHeaders
2415
+ );
2416
+ const result = data.websiteSetCartItem;
2417
+ assertActionSuccess(result, "CART_ITEM_ERROR");
2418
+ cartSessionStorage.set({
2419
+ orderId: result.order_id,
2420
+ cartToken: result.cart_token
2421
+ });
2422
+ return mapCart(result.cart, result.cart_token);
2423
+ },
2424
+ async addItem(productVariantId, quantity) {
2425
+ const cart = await this.setItem(productVariantId, quantity);
2426
+ const item = cart.items.find(
2427
+ (entry) => entry.productVariantId === productVariantId
2428
+ );
2429
+ if (!item) {
2430
+ throw new WebsiteError(
2431
+ "Cart item was not returned by the server",
2432
+ "CART_ITEM_ERROR"
2433
+ );
2434
+ }
2435
+ return item;
2436
+ },
2437
+ async updateItem(cartItemId, quantity) {
2438
+ const cart = await this.get();
2439
+ const item = cart?.items.find((entry) => entry.id === cartItemId);
2440
+ if (!item) {
2441
+ throw new WebsiteError(
2442
+ "Cart item was not found",
2443
+ "CART_ITEM_NOT_FOUND"
2444
+ );
2445
+ }
2446
+ const updatedCart = await this.setItem(item.productVariantId, quantity);
2447
+ const updatedItem = updatedCart.items.find(
2448
+ (entry) => entry.id === cartItemId
2449
+ );
2450
+ return {
2451
+ id: cartItemId,
2452
+ quantity: updatedItem?.quantity ?? quantity
2453
+ };
2454
+ },
2455
+ async removeItem(cartItemId) {
2456
+ const session = requireCartSession(cartSessionStorage);
2457
+ const data = await executeGraphQL(
2458
+ graphqlUrl,
2459
+ REMOVE_CART_ITEM,
2460
+ {
2461
+ orderId: session.orderId,
2462
+ cartToken: session.cartToken,
2463
+ itemId: cartItemId
2464
+ },
2465
+ getHeaders
2466
+ );
2467
+ const result = data.websiteRemoveCartItem;
2468
+ assertActionSuccess(result, "CART_ITEM_ERROR");
2469
+ cartSessionStorage.set({
2470
+ orderId: result.order_id,
2471
+ cartToken: result.cart_token
2472
+ });
2473
+ return mapCart(result.cart, result.cart_token);
2474
+ }
2475
+ };
2476
+ }
2477
+
2478
+ // libs/sdk/src/modules/cart-session.ts
2479
+ function getStorageKey(slug) {
2480
+ return `@bacano/sdk:cart:${slug}`;
2481
+ }
2482
+ function createCartSessionStorage(kind, getSlug) {
2483
+ let memorySession = null;
2484
+ function readKey() {
2485
+ return getStorageKey(getSlug());
2486
+ }
2487
+ return {
2488
+ get() {
2489
+ if (kind === "memory") return memorySession;
2490
+ if (kind === "cookie") {
2491
+ if (typeof document === "undefined") return null;
2492
+ try {
2493
+ const key = readKey();
2494
+ const match = document.cookie.split("; ").find((row) => row.startsWith(`${key}=`));
2495
+ if (!match) return null;
2496
+ return JSON.parse(decodeURIComponent(match.split("=").slice(1).join("=")));
2497
+ } catch {
2498
+ return null;
2499
+ }
2500
+ }
2501
+ if (typeof localStorage === "undefined") return null;
2502
+ try {
2503
+ const raw = localStorage.getItem(readKey());
2504
+ return raw ? JSON.parse(raw) : null;
2505
+ } catch {
2506
+ return null;
2507
+ }
2508
+ },
2509
+ set(session) {
2510
+ memorySession = session;
2511
+ if (kind === "memory") return;
2512
+ if (kind === "cookie") {
2513
+ if (typeof document === "undefined") return;
2514
+ const maxAge = 30 * 24 * 60 * 60;
2515
+ const value = encodeURIComponent(JSON.stringify(session));
2516
+ document.cookie = `${readKey()}=${value}; max-age=${maxAge}; path=/; SameSite=Lax`;
2517
+ return;
2518
+ }
2519
+ if (typeof localStorage === "undefined") return;
2520
+ localStorage.setItem(readKey(), JSON.stringify(session));
2521
+ },
2522
+ clear() {
2523
+ memorySession = null;
2524
+ if (kind === "memory") return;
2525
+ if (kind === "cookie") {
2526
+ if (typeof document === "undefined") return;
2527
+ document.cookie = `${readKey()}=; max-age=0; path=/; SameSite=Lax`;
2528
+ return;
2529
+ }
2530
+ if (typeof localStorage === "undefined") return;
2531
+ localStorage.removeItem(readKey());
2532
+ }
2533
+ };
2534
+ }
2535
+
2536
+ // libs/sdk/src/media.ts
2537
+ var MEDIA_VARIANTS = {
2538
+ thumbnail: { width: 240, quality: 75, format: "webp" },
2539
+ card: { width: 640, quality: 80, format: "webp" },
2540
+ detail: { width: 1400, quality: 85, format: "webp" }
2541
+ };
2542
+ function toSafeInteger(value) {
2543
+ if (!Number.isFinite(value)) return void 0;
2544
+ return Math.max(1, Math.floor(value));
2545
+ }
2546
+ function buildQuery(options) {
2547
+ const params = new URLSearchParams();
2548
+ const width = toSafeInteger(options.width);
2549
+ const height = toSafeInteger(options.height);
2550
+ const quality = toSafeInteger(options.quality);
2551
+ if (width) params.set("w", String(width));
2552
+ if (height) params.set("h", String(height));
2553
+ if (quality) params.set("q", String(Math.min(100, quality)));
2554
+ if (options.format) params.set("f", options.format);
2555
+ const query = params.toString();
2556
+ return query ? `?${query}` : "";
2557
+ }
2558
+ function buildStorageFileUrl(storageUrl, fileId, variant = "original") {
2559
+ if (!storageUrl || !fileId) return "";
2560
+ const baseUrl = `${storageUrl.replace(/\/+$/, "")}/files/${fileId}`;
2561
+ if (variant === "original") return baseUrl;
2562
+ return `${baseUrl}${buildQuery(MEDIA_VARIANTS[variant])}`;
2563
+ }
2564
+ function buildStorageMediaUrls(storageUrl, fileId) {
2565
+ return {
2566
+ originalUrl: buildStorageFileUrl(storageUrl, fileId, "original"),
2567
+ thumbnailUrl: buildStorageFileUrl(storageUrl, fileId, "thumbnail"),
2568
+ cardUrl: buildStorageFileUrl(storageUrl, fileId, "card"),
2569
+ detailUrl: buildStorageFileUrl(storageUrl, fileId, "detail")
2570
+ };
2571
+ }
2572
+
2573
+ // libs/sdk/src/modules/catalog.ts
2574
+ var MAX_AVAILABILITY_VARIANT_IDS2 = 100;
2575
+ var DEFAULT_FILTER_ATTRIBUTE_LIST_KEY = "atributos";
2576
+ function mapPrice(raw) {
2577
+ return {
2578
+ id: raw.id,
2579
+ price: raw.price,
2580
+ priceType: raw.price_type,
2581
+ chainId: raw.chain_id
2582
+ };
2583
+ }
2584
+ function parseActionJson(value, fallback) {
2585
+ if (value == null) return fallback;
2586
+ if (typeof value !== "string") return value;
2587
+ try {
2588
+ return JSON.parse(value);
2589
+ } catch {
2590
+ return fallback;
2591
+ }
2592
+ }
2593
+ function getProductMediaKind(mimeType) {
2594
+ if (mimeType?.startsWith("image/")) return "image";
2595
+ if (mimeType?.startsWith("video/")) return "video";
2596
+ if (mimeType?.startsWith("application/")) return "document";
2597
+ return "file";
2598
+ }
2599
+ function getProductMediaByKind(media, kind) {
2600
+ return (media ?? []).filter((item) => item.mediaKind === kind);
2601
+ }
2602
+ function getProductImages(media) {
2603
+ return getProductMediaByKind(media, "image");
2604
+ }
2605
+ function getProductVideos(media) {
2606
+ return getProductMediaByKind(media, "video");
2607
+ }
2608
+ function getPrimaryProductImage(media) {
2609
+ return getProductImages(media)[0] ?? null;
2610
+ }
2611
+ function mapMedia(raw, storageUrl) {
2612
+ const mimeType = raw.file?.mimeType ?? null;
2613
+ const fileName = raw.file?.name ?? null;
2614
+ const urls = buildStorageMediaUrls(storageUrl, raw.file_id);
2615
+ return {
2616
+ id: raw.id,
2617
+ fileId: raw.file_id,
2618
+ order: raw.order ?? 0,
2619
+ url: urls.originalUrl,
2620
+ ...urls,
2621
+ mediaKind: getProductMediaKind(mimeType),
2622
+ mimeType,
2623
+ fileName,
2624
+ alt: fileName
2625
+ };
2626
+ }
2627
+ function mapProductRecommendation(raw, storageUrl) {
2628
+ return {
2629
+ id: raw.id,
2630
+ name: raw.name,
2631
+ slug: raw.slug,
2632
+ category: raw.category,
2633
+ subcategory: raw.subcategory,
2634
+ brand: raw.brand,
2635
+ primaryImage: raw.primary_media ? mapMedia(raw.primary_media, storageUrl) : null,
2636
+ pricing: {
2637
+ currentPriceMin: Number(raw.pricing.current_price_min),
2638
+ currentPriceMax: Number(raw.pricing.current_price_max),
2639
+ regularPriceMin: Number(raw.pricing.regular_price_min),
2640
+ regularPriceMax: Number(raw.pricing.regular_price_max),
2641
+ hasDiscount: raw.pricing.has_discount
2642
+ },
2643
+ source: raw.recommendation_source
2644
+ };
2645
+ }
2646
+ function mapSeoMedia(raw, storageUrl) {
2647
+ if (!raw.seo_image_file_id) return null;
2648
+ return mapMedia(
2649
+ {
2650
+ id: `seo:${raw.seo_image_file_id}`,
2651
+ file_id: raw.seo_image_file_id,
2652
+ order: 0,
2653
+ file: raw.seo_image_file ?? null
2654
+ },
2655
+ storageUrl
2656
+ );
2657
+ }
2658
+ function attributeValueCode(raw) {
2659
+ return raw.attribute_value?.code ?? raw.value;
2660
+ }
2661
+ function attributeValueLabelKey(attributeId, valueCode) {
2662
+ return `${attributeId}:${valueCode}`;
2663
+ }
2664
+ function buildPublicAttributeLabels(lists) {
2665
+ const attributeLabelsById = /* @__PURE__ */ new Map();
2666
+ const valueLabelsById = /* @__PURE__ */ new Map();
2667
+ const valueLabelsByAttributeAndCode = /* @__PURE__ */ new Map();
2668
+ for (const list of lists ?? []) {
2669
+ for (const item of list.items ?? []) {
2670
+ if (!attributeLabelsById.has(item.attribute_id)) {
2671
+ attributeLabelsById.set(item.attribute_id, item.public_label);
2672
+ }
2673
+ for (const value of item.values ?? []) {
2674
+ if (!valueLabelsById.has(value.attribute_value_id)) {
2675
+ valueLabelsById.set(value.attribute_value_id, value.public_label);
2676
+ }
2677
+ const valueCode = value.attribute_value.code;
2678
+ const key = attributeValueLabelKey(item.attribute_id, valueCode);
2679
+ if (!valueLabelsByAttributeAndCode.has(key)) {
2680
+ valueLabelsByAttributeAndCode.set(key, value.public_label);
2681
+ }
2682
+ }
2683
+ }
2684
+ }
2685
+ return {
2686
+ attributeLabelsById,
2687
+ valueLabelsById,
2688
+ valueLabelsByAttributeAndCode
2689
+ };
2690
+ }
2691
+ function resolvePublicAttributeValueLabel(raw, labels) {
2692
+ const valueCode = attributeValueCode(raw);
2693
+ if (raw.attribute_value?.public_label) {
2694
+ return raw.attribute_value.public_label;
2695
+ }
2696
+ const byId = raw.attribute_value?.id ? labels.valueLabelsById.get(raw.attribute_value.id) : null;
2697
+ const byAttributeAndCode = labels.valueLabelsByAttributeAndCode.get(
2698
+ attributeValueLabelKey(raw.attribute.id, valueCode)
2699
+ );
2700
+ return byId ?? byAttributeAndCode ?? valueCode;
2701
+ }
2702
+ function mapProductAttribute(raw, labels) {
2703
+ const valueCode = attributeValueCode(raw);
2704
+ return {
2705
+ id: raw.id,
2706
+ name: raw.attribute.public_label ?? labels.attributeLabelsById.get(raw.attribute.id) ?? raw.attribute.name,
2707
+ value: resolvePublicAttributeValueLabel(raw, labels),
2708
+ attributeValueId: raw.attribute_value?.id ?? null,
2709
+ code: raw.attribute.name,
2710
+ valueCode,
2711
+ metadata: raw.attribute_value?.metadata ?? null,
2712
+ sortOrder: raw.attribute_value?.sort_order ?? null
2713
+ };
2714
+ }
2715
+ function mapVariantAttribute(raw, labels) {
2716
+ const valueCode = attributeValueCode(raw);
2717
+ return {
2718
+ id: raw.id,
2719
+ name: raw.attribute.public_label ?? labels.attributeLabelsById.get(raw.attribute.id) ?? raw.attribute.name,
2720
+ value: resolvePublicAttributeValueLabel(raw, labels),
2721
+ order: raw.order,
2722
+ attributeValueId: raw.attribute_value?.id ?? null,
2723
+ code: raw.attribute.name,
2724
+ valueCode,
2725
+ metadata: raw.attribute_value?.metadata ?? null,
2726
+ sortOrder: raw.attribute_value?.sort_order ?? null
2727
+ };
2728
+ }
2729
+ function mapVariant(raw, labels, chainId) {
2730
+ const prices = selectPublicPrices(
2731
+ raw.product_variant_prices.map(mapPrice),
2732
+ chainId
2733
+ );
2734
+ return {
2735
+ id: raw.id,
2736
+ sku: raw.sku,
2737
+ barcode: raw.barcode,
2738
+ order: raw.order,
2739
+ weight: raw.weight,
2740
+ depth: raw.depth,
2741
+ height: raw.height,
2742
+ length: raw.length,
2743
+ prices,
2744
+ pricing: resolveProductPricing(prices),
2745
+ attributes: raw.product_variant_attributes.map(
2746
+ (attribute) => mapVariantAttribute(attribute, labels)
2747
+ )
2748
+ };
2749
+ }
2750
+ function mapRelatedProduct(raw, storageUrl) {
2751
+ return {
2752
+ id: raw.id,
2753
+ productId: raw.related_product_id,
2754
+ slug: raw.related_product.slug,
2755
+ name: raw.related_product.name,
2756
+ sku: raw.related_product.sku,
2757
+ displayLabel: raw.display_label,
2758
+ sortOrder: raw.sort_order ?? 0,
2759
+ media: raw.related_product.product_media.map(
2760
+ (m) => mapMedia(m, storageUrl)
2761
+ )
2762
+ };
2763
+ }
2764
+ function mapProduct(raw, storageUrl, labels, chainId) {
2765
+ return {
2766
+ id: raw.id,
2767
+ name: raw.name,
2768
+ description: raw.description,
2769
+ longDescription: raw.long_description ?? null,
2770
+ slug: raw.slug ?? null,
2771
+ seoTitle: raw.seo_title ?? null,
2772
+ seoDescription: raw.seo_description ?? null,
2773
+ seoImage: mapSeoMedia(raw, storageUrl),
2774
+ color: raw.color,
2775
+ category: raw.category,
2776
+ subcategory: raw.subcategory,
2777
+ brand: raw.brand,
2778
+ measurementUnit: raw.measurement_unit,
2779
+ createdAt: raw.created_at,
2780
+ updatedAt: raw.updated_at ?? raw.created_at,
2781
+ variants: raw.product_variants.map(
2782
+ (variant) => mapVariant(variant, labels, chainId)
2783
+ ),
2784
+ media: raw.product_media.map((m) => mapMedia(m, storageUrl)),
2785
+ attributes: raw.product_attributes.map(
2786
+ (attribute) => mapProductAttribute(attribute, labels)
2787
+ ),
2788
+ relatedProducts: (raw.product_related_products ?? []).map(
2789
+ (relation) => mapRelatedProduct(relation, storageUrl)
2790
+ )
2791
+ };
2792
+ }
2793
+ function mapCategory(raw) {
2794
+ return {
2795
+ name: raw.name,
2796
+ description: raw.description,
2797
+ subcategories: raw.product_subcategories.map((sub) => ({
2798
+ id: sub.id,
2799
+ name: sub.name,
2800
+ description: sub.description
2801
+ }))
2802
+ };
2803
+ }
2804
+ function mapCategoryListItem(raw, categoriesByName) {
2805
+ const parentCategory = raw.subcategory_ref?.category ?? null;
2806
+ const parent = parentCategory ? categoriesByName?.get(parentCategory) ?? null : null;
2807
+ return {
2808
+ id: raw.id,
2809
+ sourceType: raw.source_type,
2810
+ productCategory: raw.product_category,
2811
+ productSubcategoryId: raw.product_subcategory_id,
2812
+ label: raw.public_label,
2813
+ slug: raw.public_slug,
2814
+ sortOrder: raw.sort_order,
2815
+ category: raw.category_ref,
2816
+ subcategory: raw.subcategory_ref,
2817
+ parentCategory,
2818
+ parentId: parent?.id ?? null,
2819
+ parentSlug: parent?.slug ?? null
2820
+ };
2821
+ }
2822
+ function mapCategoryList(raw) {
2823
+ const categoryItems = raw.items.filter((item) => item.source_type === "category" && item.product_category).map((item) => mapCategoryListItem(item));
2824
+ const categoriesByName = new Map(
2825
+ categoryItems.filter((item) => item.productCategory).map((item) => [item.productCategory, item])
2826
+ );
2827
+ const items = raw.items.map(
2828
+ (item) => mapCategoryListItem(item, categoriesByName)
2829
+ );
2830
+ return {
2831
+ id: raw.id,
2832
+ key: raw.key,
2833
+ name: raw.name,
2834
+ description: raw.description,
2835
+ status: raw.status,
2836
+ items,
2837
+ categories: categoryItems.map((category) => ({
2838
+ id: category.id,
2839
+ productCategory: category.productCategory,
2840
+ label: category.label,
2841
+ slug: category.slug,
2842
+ sortOrder: category.sortOrder,
2843
+ category: category.category,
2844
+ subcategories: items.filter(
2845
+ (item) => item.sourceType === "subcategory" && item.parentCategory === category.productCategory
2846
+ ).sort(
2847
+ (a, b) => a.sortOrder === b.sortOrder ? a.label.localeCompare(b.label) : a.sortOrder - b.sortOrder
2848
+ )
2849
+ }))
2850
+ };
2851
+ }
2852
+ function filterCategoryListByApplicability(raw, applicableCategories, applicableSubcategoryIds) {
2853
+ const categoryNamesWithApplicableSubcategories = new Set(
2854
+ raw.items.filter(
2855
+ (item) => item.source_type === "subcategory" && item.product_subcategory_id && applicableSubcategoryIds.has(item.product_subcategory_id)
2856
+ ).map((item) => item.subcategory_ref?.category).filter((category) => Boolean(category))
2857
+ );
2858
+ const filteredItems = raw.items.filter((item) => {
2859
+ if (item.source_type === "category") {
2860
+ const category = item.product_category;
2861
+ return Boolean(
2862
+ category && (applicableCategories.has(category) || categoryNamesWithApplicableSubcategories.has(category))
2863
+ );
2864
+ }
2865
+ const subcategoryId = item.product_subcategory_id;
2866
+ return Boolean(
2867
+ subcategoryId && applicableSubcategoryIds.has(subcategoryId)
2868
+ );
2869
+ });
2870
+ return mapCategoryList({
2871
+ ...raw,
2872
+ items: filteredItems
2873
+ });
2874
+ }
2875
+ function mapAttributeListValue(raw) {
2876
+ return {
2877
+ id: raw.id,
2878
+ attributeValueId: raw.attribute_value_id,
2879
+ code: raw.attribute_value.code,
2880
+ label: raw.public_label,
2881
+ slug: raw.public_slug,
2882
+ sortOrder: raw.sort_order,
2883
+ metadata: raw.attribute_value.metadata
2884
+ };
2885
+ }
2886
+ function mapAttributeListItem(raw) {
2887
+ return {
2888
+ id: raw.id,
2889
+ attributeId: raw.attribute_id,
2890
+ attributeName: raw.attribute.name,
2891
+ attributeType: raw.attribute.type,
2892
+ label: raw.public_label,
2893
+ slug: raw.public_slug,
2894
+ sortOrder: raw.sort_order,
2895
+ values: raw.values.map(mapAttributeListValue)
2896
+ };
2897
+ }
2898
+ function mapAttributeList(raw) {
2899
+ return {
2900
+ id: raw.id,
2901
+ key: raw.key,
2902
+ name: raw.name,
2903
+ description: raw.description,
2904
+ status: raw.status,
2905
+ items: raw.items.map(mapAttributeListItem)
2906
+ };
2907
+ }
2908
+ function mapContextualAttributeListValue(raw) {
2909
+ return {
2910
+ id: raw.id,
2911
+ attributeValueId: raw.attribute_value_id,
2912
+ code: raw.code,
2913
+ label: raw.public_label,
2914
+ slug: raw.public_slug,
2915
+ sortOrder: raw.sort_order,
2916
+ metadata: raw.metadata
2917
+ };
2918
+ }
2919
+ function mapContextualAttributeListItem(raw) {
2920
+ return {
2921
+ id: raw.id,
2922
+ attributeId: raw.attribute_id,
2923
+ attributeName: raw.attribute_name,
2924
+ attributeType: raw.attribute_type,
2925
+ label: raw.public_label,
2926
+ slug: raw.public_slug,
2927
+ sortOrder: raw.sort_order,
2928
+ values: raw.values.map(mapContextualAttributeListValue)
2929
+ };
2930
+ }
2931
+ function mapContextualAttributeList(raw) {
2932
+ return {
2933
+ id: raw.id,
2934
+ key: raw.key,
2935
+ name: raw.name,
2936
+ description: raw.description,
2937
+ status: raw.status,
2938
+ items: raw.items.map(mapContextualAttributeListItem)
2939
+ };
2940
+ }
2941
+ function buildOrderBy(sortBy) {
2942
+ switch (sortBy) {
2943
+ case "name_asc":
2944
+ return [{ name: "asc" }];
2945
+ case "name_desc":
2946
+ return [{ name: "desc" }];
2947
+ case "newest":
2948
+ return [{ created_at: "desc" }];
2949
+ case "price_asc":
2950
+ case "price_desc":
2951
+ return void 0;
2952
+ // Client-side sort after fetch
2953
+ default:
2954
+ return void 0;
2955
+ }
2956
+ }
2957
+ function scoreSearchRelevance(product, search) {
2958
+ const term = search.toLowerCase();
2959
+ const name = product.name.toLowerCase();
2960
+ if (name.startsWith(term)) return 100;
2961
+ if (name.includes(term)) return 80;
2962
+ if (product.brand?.name.toLowerCase().includes(term)) return 60;
2963
+ if (product.attributes.some((a) => a.value.toLowerCase().includes(term)))
2964
+ return 40;
2965
+ if (product.description?.toLowerCase().includes(term)) return 20;
2966
+ return 0;
2967
+ }
2968
+ function getMinVariantPrice(product) {
2969
+ let min = Infinity;
2970
+ for (const v of product.variants) {
2971
+ if (v.pricing.currentPrice != null && v.pricing.currentPrice < min) {
2972
+ min = v.pricing.currentPrice;
2973
+ }
2974
+ }
2975
+ return min === Infinity ? 0 : min;
2976
+ }
2977
+ function resolveProductPricing(prices) {
2978
+ const positivePrice = (priceType) => {
2979
+ const price = prices.find((item) => item.priceType === priceType)?.price;
2980
+ return price != null && Number.isFinite(price) && price > 0 ? price : null;
2981
+ };
2982
+ const regularPrice = positivePrice("CLIENT");
2983
+ const candidateSalePrice = positivePrice("SALE");
2984
+ const salePrice = regularPrice != null && candidateSalePrice != null && candidateSalePrice < regularPrice ? candidateSalePrice : null;
2985
+ const hasDiscount = salePrice != null;
2986
+ const discountPercentage = salePrice != null && regularPrice != null ? Math.round((regularPrice - salePrice) / regularPrice * 1e4) / 100 : null;
2987
+ return {
2988
+ currentPrice: salePrice ?? regularPrice,
2989
+ regularPrice,
2990
+ salePrice,
2991
+ hasDiscount,
2992
+ discountPercentage,
2993
+ wholesalePrice: positivePrice("WHOLESALE"),
2994
+ wholesale2Price: positivePrice("WHOLESALE_2")
2995
+ };
2996
+ }
2997
+ function selectPublicPrices(prices, chainId) {
2998
+ const selected = [];
2999
+ for (const priceType of [
3000
+ "SALE",
3001
+ "CLIENT",
3002
+ "WHOLESALE",
3003
+ "WHOLESALE_2"
3004
+ ]) {
3005
+ const chainPrice = prices.find(
3006
+ (price2) => price2.priceType === priceType && price2.chainId === chainId
3007
+ );
3008
+ const globalPrice = prices.find(
3009
+ (price2) => price2.priceType === priceType && price2.chainId === null
3010
+ );
3011
+ const price = chainPrice ?? globalPrice;
3012
+ if (price) selected.push(price);
3013
+ }
3014
+ return selected;
3015
+ }
3016
+ function hasMeaningfulAttributeListFilters(filters) {
3017
+ if (!filters) return false;
3018
+ return Boolean(
3019
+ filters.categoryName || filters.subcategoryId || filters.brandId || filters.search || filters.minPrice !== void 0 || filters.maxPrice !== void 0 || (filters.tags?.some((tag) => tag.trim().length > 0) ?? false)
3020
+ );
3021
+ }
3022
+ function chunkArray2(items, chunkSize) {
3023
+ const chunks = [];
3024
+ for (let index = 0; index < items.length; index += chunkSize) {
3025
+ chunks.push(items.slice(index, index + chunkSize));
3026
+ }
3027
+ return chunks;
3028
+ }
3029
+ function uniqueValues2(values) {
3030
+ return Array.from(new Set(values));
3031
+ }
3032
+ function buildProductWhere(filters, chainId) {
3033
+ const whereConditions = [];
3034
+ if (filters.categoryName) {
3035
+ whereConditions.push({ category: { _eq: filters.categoryName } });
3036
+ }
3037
+ if (filters.subcategoryId) {
3038
+ whereConditions.push({ subcategory: { _eq: filters.subcategoryId } });
3039
+ }
3040
+ if (filters.brandId) {
3041
+ whereConditions.push({ brand_id: { _eq: filters.brandId } });
3042
+ }
3043
+ if (filters.search) {
3044
+ const term = `%${filters.search}%`;
3045
+ whereConditions.push({
3046
+ _or: [
3047
+ { name: { _ilike: term } },
3048
+ { description: { _ilike: term } },
3049
+ { brand: { name: { _ilike: term } } },
3050
+ { product_attributes: { value: { _ilike: term } } }
3051
+ ]
3052
+ });
3053
+ }
3054
+ if (filters.colors && filters.colors.length > 0) {
3055
+ whereConditions.push({ color: { _in: filters.colors } });
3056
+ }
3057
+ if (filters.tags && filters.tags.length > 0) {
3058
+ const tags = filters.tags.map((tag) => tag.trim()).filter(Boolean);
3059
+ if (tags.length > 0) {
3060
+ whereConditions.push({
3061
+ product_tag_assignments: {
3062
+ tag: {
3063
+ key: { _in: tags },
3064
+ is_active: { _eq: true }
3065
+ }
3066
+ }
3067
+ });
3068
+ }
3069
+ }
3070
+ if (filters.minPrice !== void 0 || filters.maxPrice !== void 0) {
3071
+ const priceCondition = {};
3072
+ if (filters.minPrice !== void 0) priceCondition._gte = filters.minPrice;
3073
+ if (filters.maxPrice !== void 0) priceCondition._lte = filters.maxPrice;
3074
+ whereConditions.push({
3075
+ product_variants: {
3076
+ product_variant_prices: {
3077
+ price: priceCondition,
3078
+ price_type: { _in: ["CLIENT", "SALE"] },
3079
+ _or: [
3080
+ { chain_id: { _eq: chainId } },
3081
+ { chain_id: { _is_null: true } }
3082
+ ]
3083
+ }
3084
+ }
3085
+ });
3086
+ }
3087
+ if (filters.attributes) {
3088
+ for (const [attrName, values] of Object.entries(filters.attributes)) {
3089
+ if (values.length > 0) {
3090
+ whereConditions.push({
3091
+ _or: [
3092
+ {
3093
+ product_attributes: {
3094
+ attribute: { name: { _eq: attrName } },
3095
+ value: { _in: values }
3096
+ }
3097
+ },
3098
+ {
3099
+ product_variants: {
3100
+ product_variant_attributes: {
3101
+ attribute: { name: { _eq: attrName } },
3102
+ value: { _in: values }
3103
+ }
3104
+ }
3105
+ }
3106
+ ]
3107
+ });
3108
+ }
3109
+ }
3110
+ }
3111
+ return whereConditions.length > 0 ? { _and: whereConditions } : {};
3112
+ }
3113
+ function hasMeaningfulCategoryListFilters(filters) {
3114
+ if (!filters) return false;
3115
+ return Boolean(
3116
+ filters.search || filters.brandId || filters.minPrice !== void 0 || filters.maxPrice !== void 0 || (filters.tags?.some((tag) => tag.trim().length > 0) ?? false) || Object.values(filters.attributes ?? {}).some(
3117
+ (values) => values.length > 0
3118
+ )
3119
+ );
3120
+ }
3121
+ async function getWebsiteAvailabilityByVariantId(graphqlUrl, getHeaders, variantIds, chainId) {
3122
+ const uniqueVariantIds = uniqueValues2(variantIds);
3123
+ const availabilityByVariantId = /* @__PURE__ */ new Map();
3124
+ for (const chunk of chunkArray2(
3125
+ uniqueVariantIds,
3126
+ MAX_AVAILABILITY_VARIANT_IDS2
3127
+ )) {
3128
+ const data = await executeGraphQL(
3129
+ graphqlUrl,
3130
+ CHECK_AVAILABILITY,
3131
+ { variantIds: chunk, chainId },
3132
+ getHeaders
3133
+ );
3134
+ for (const item of data.websiteProductAvailability ?? []) {
3135
+ availabilityByVariantId.set(item.product_variant_id, {
3136
+ inStock: item.in_stock
3137
+ });
3138
+ }
3139
+ }
3140
+ return availabilityByVariantId;
3141
+ }
3142
+ async function filterProductsForWebsiteStock(graphqlUrl, getHeaders, products, chainId) {
3143
+ const variantIds = products.flatMap(
3144
+ (product) => product.variants.map((variant) => variant.id)
3145
+ );
3146
+ if (variantIds.length === 0) return [];
3147
+ const inStockVariantIds = new Set(
3148
+ Array.from(
3149
+ (await getWebsiteAvailabilityByVariantId(
3150
+ graphqlUrl,
3151
+ getHeaders,
3152
+ variantIds,
3153
+ chainId
3154
+ )).entries()
3155
+ ).filter(([, item]) => item.inStock).map(([variantId]) => variantId)
3156
+ );
3157
+ return products.map((product) => ({
3158
+ ...product,
3159
+ variants: product.variants.map((variant) => {
3160
+ const prices = selectPublicPrices(variant.prices, chainId);
3161
+ return {
3162
+ ...variant,
3163
+ prices,
3164
+ pricing: resolveProductPricing(prices)
3165
+ };
3166
+ }).filter(
3167
+ (variant) => inStockVariantIds.has(variant.id) && variant.pricing.currentPrice != null
3168
+ )
3169
+ })).filter((product) => product.variants.length > 0);
3170
+ }
3171
+ async function filterSeoProductsForWebsiteStock(graphqlUrl, getHeaders, products, chainId) {
3172
+ const variantIds = products.flatMap(
3173
+ (product) => product.product_variants.map((variant) => variant.id)
3174
+ );
3175
+ if (variantIds.length === 0) return [];
3176
+ const inStockVariantIds = new Set(
3177
+ Array.from(
3178
+ (await getWebsiteAvailabilityByVariantId(
3179
+ graphqlUrl,
3180
+ getHeaders,
3181
+ variantIds,
3182
+ chainId
3183
+ )).entries()
3184
+ ).filter(([, item]) => item.inStock).map(([variantId]) => variantId)
3185
+ );
3186
+ return products.filter(
3187
+ (product) => product.product_variants.some((variant) => {
3188
+ const prices = variant.product_variant_prices.map(mapPrice);
3189
+ const pricing = resolveProductPricing(
3190
+ selectPublicPrices(prices, chainId)
3191
+ );
3192
+ return inStockVariantIds.has(variant.id) && pricing.currentPrice != null;
3193
+ })
3194
+ );
3195
+ }
3196
+ function mapProductSeoEntry(raw, storageUrl) {
3197
+ if (!raw.slug) return null;
3198
+ return {
3199
+ id: raw.id,
3200
+ name: raw.name,
3201
+ slug: raw.slug,
3202
+ createdAt: raw.created_at,
3203
+ updatedAt: raw.updated_at,
3204
+ seoTitle: raw.seo_title ?? null,
3205
+ seoDescription: raw.seo_description ?? null,
3206
+ seoImage: mapSeoMedia(raw, storageUrl),
3207
+ primaryImage: raw.product_media[0] ? mapMedia(raw.product_media[0], storageUrl) : null,
3208
+ category: raw.category,
3209
+ subcategory: raw.subcategory
3210
+ };
3211
+ }
3212
+ function createCatalogModule(graphqlUrl, getHeaders, getContext, storageUrl) {
3213
+ const mapSingleProductResult = async (data) => {
3214
+ const ctx = getContext();
3215
+ if (!ctx.chainId) {
3216
+ throw new WebsiteError(
3217
+ "This website has no chain configured. Catalog requires a chain_id.",
3218
+ "NO_CHAIN"
3219
+ );
3220
+ }
3221
+ const product = data.products?.[0];
3222
+ if (!product) return null;
3223
+ const publicAttributeLabels = buildPublicAttributeLabels(
3224
+ data.catalog_attribute_lists
3225
+ );
3226
+ const filteredProducts = await filterProductsForWebsiteStock(
3227
+ graphqlUrl,
3228
+ getHeaders,
3229
+ [mapProduct(product, storageUrl, publicAttributeLabels, ctx.chainId)],
3230
+ ctx.chainId
3231
+ );
3232
+ return filteredProducts[0] ?? null;
3233
+ };
3234
+ return {
3235
+ async getProductRecommendations(opts) {
3236
+ const ctx = getContext();
3237
+ const limit = opts.limit ?? 16;
3238
+ if (!Number.isInteger(limit) || limit < 1 || limit > 32) {
3239
+ throw new WebsiteError(
3240
+ "Recommendation limit must be an integer between 1 and 32.",
3241
+ "INVALID_RECOMMENDATION_LIMIT"
3242
+ );
3243
+ }
3244
+ const data = await executeGraphQL(
3245
+ graphqlUrl,
3246
+ WEBSITE_GET_PRODUCT_RECOMMENDATIONS,
3247
+ {
3248
+ websiteId: ctx.websiteId,
3249
+ productId: opts.productId,
3250
+ limit
3251
+ },
3252
+ getHeaders
3253
+ );
3254
+ if (!data.result.success) {
3255
+ throw new WebsiteError(
3256
+ data.result.errors.join("; ") || "Unable to resolve product recommendations",
3257
+ "PRODUCT_RECOMMENDATIONS_ERROR"
3258
+ );
3259
+ }
3260
+ const products = parseActionJson(
3261
+ data.result.products,
3262
+ []
3263
+ );
3264
+ return {
3265
+ products: products.map(
3266
+ (product) => mapProductRecommendation(product, storageUrl)
3267
+ ),
3268
+ strategy: data.result.strategy
3269
+ };
3270
+ },
3271
+ async getProductSeoEntries(opts) {
3272
+ const ctx = getContext();
3273
+ if (!ctx.chainId) {
3274
+ throw new WebsiteError(
3275
+ "This website has no chain configured. Catalog requires a chain_id.",
3276
+ "NO_CHAIN"
3277
+ );
3278
+ }
3279
+ const limit = opts?.limit ?? 100;
3280
+ const offset = opts?.offset ?? 0;
3281
+ const data = await executeGraphQL(
3282
+ graphqlUrl,
3283
+ GET_PRODUCT_SEO_ENTRIES,
3284
+ {
3285
+ companyId: ctx.companyId,
3286
+ chainId: ctx.chainId,
3287
+ websiteId: ctx.websiteId,
3288
+ limit: limit + 1,
3289
+ offset
3290
+ },
3291
+ getHeaders
3292
+ );
3293
+ const availableProducts = await filterSeoProductsForWebsiteStock(
3294
+ graphqlUrl,
3295
+ getHeaders,
3296
+ data.products ?? [],
3297
+ ctx.chainId
3298
+ );
3299
+ const entries = availableProducts.slice(0, limit).map((product) => mapProductSeoEntry(product, storageUrl)).filter((entry) => Boolean(entry));
3300
+ return {
3301
+ entries,
3302
+ pagination: {
3303
+ limit,
3304
+ offset,
3305
+ hasMore: (data.products ?? []).length > limit
3306
+ }
3307
+ };
3308
+ },
3309
+ async getCatalogPage(opts) {
3310
+ const ctx = getContext();
3311
+ if (!ctx.chainId) {
3312
+ throw new WebsiteError(
3313
+ "This website has no chain configured. Catalog requires a chain_id.",
3314
+ "NO_CHAIN"
3315
+ );
3316
+ }
3317
+ const chainId = ctx.chainId;
3318
+ const filters = {
3319
+ category_slug: opts.categorySlug,
3320
+ subcategory_slug: opts.subcategorySlug,
3321
+ tags: opts.tags,
3322
+ search: opts.search,
3323
+ brand_id: opts.brandId,
3324
+ min_price: opts.minPrice,
3325
+ max_price: opts.maxPrice,
3326
+ attributes_by_slug: opts.attributesBySlug,
3327
+ sort_by: opts.sortBy,
3328
+ limit: opts.limit,
3329
+ offset: opts.offset
3330
+ };
3331
+ const data = await executeGraphQL(
3332
+ graphqlUrl,
3333
+ WEBSITE_GET_CATALOG_PAGE,
3334
+ {
3335
+ websiteId: ctx.websiteId,
3336
+ categoryListKey: opts.categoryListKey,
3337
+ attributeListKey: opts.attributeListKey,
3338
+ filters,
3339
+ include: opts.include
3340
+ },
3341
+ getHeaders
3342
+ );
3343
+ if (!data.result.success) {
3344
+ throw new WebsiteError(
3345
+ data.result.errors.join("; ") || "Unable to resolve catalog page",
3346
+ "CATALOG_PAGE_ERROR"
3347
+ );
3348
+ }
3349
+ const products = parseActionJson(data.result.products, []);
3350
+ const categoryList = parseActionJson(
3351
+ data.result.category_list,
3352
+ null
3353
+ );
3354
+ const attributeList = parseActionJson(
3355
+ data.result.attribute_list,
3356
+ null
3357
+ );
3358
+ const filtersResult = parseActionJson(
3359
+ data.result.filters,
3360
+ null
3361
+ );
3362
+ const brands = parseActionJson(data.result.brands, []);
3363
+ const pagination = parseActionJson(
3364
+ data.result.pagination,
3365
+ { limit: opts.limit ?? 24, offset: opts.offset ?? 0, total: 0, hasMore: false }
3366
+ );
3367
+ const appliedFilters = parseActionJson(
3368
+ data.result.applied_filters,
3369
+ {}
3370
+ );
3371
+ const debug = parseActionJson(
3372
+ data.result.debug,
3373
+ null
3374
+ );
3375
+ const labels = buildPublicAttributeLabels(
3376
+ attributeList ? [attributeList] : []
3377
+ );
3378
+ return {
3379
+ products: products.map(
3380
+ (product) => mapProduct(product, storageUrl, labels, chainId)
3381
+ ),
3382
+ categories: categoryList ? mapCategoryList(categoryList) : null,
3383
+ attributes: attributeList ? mapAttributeList(attributeList) : null,
3384
+ filters: filtersResult,
3385
+ brands,
3386
+ pagination,
3387
+ appliedFilters,
3388
+ debug
3389
+ };
3390
+ },
3391
+ async getProducts(opts) {
3392
+ const ctx = getContext();
3393
+ if (!ctx.chainId) {
3394
+ throw new WebsiteError(
3395
+ "This website has no chain configured. Catalog requires a chain_id.",
3396
+ "NO_CHAIN"
3397
+ );
3398
+ }
3399
+ const chainId = ctx.chainId;
3400
+ const where = buildProductWhere(opts ?? {}, chainId);
3401
+ const orderBy = buildOrderBy(opts?.sortBy);
3402
+ const variables = {
3403
+ companyId: ctx.companyId,
3404
+ chainId,
3405
+ websiteId: ctx.websiteId,
3406
+ limit: opts?.limit ?? 20,
3407
+ offset: opts?.offset ?? 0,
3408
+ where
3409
+ };
3410
+ if (orderBy) {
3411
+ variables.orderBy = orderBy;
3412
+ }
3413
+ const data = await executeGraphQL(graphqlUrl, GET_PRODUCTS, variables, getHeaders);
3414
+ const publicAttributeLabels = buildPublicAttributeLabels(
3415
+ data.catalog_attribute_lists
3416
+ );
3417
+ let products = await filterProductsForWebsiteStock(
3418
+ graphqlUrl,
3419
+ getHeaders,
3420
+ (data.products ?? []).map(
3421
+ (p) => mapProduct(p, storageUrl, publicAttributeLabels, chainId)
3422
+ ),
3423
+ chainId
3424
+ );
3425
+ if (opts?.minPrice !== void 0 || opts?.maxPrice !== void 0) {
3426
+ products = products.filter(
3427
+ (product) => product.variants.some((variant) => {
3428
+ const currentPrice = variant.pricing.currentPrice;
3429
+ if (currentPrice == null) return false;
3430
+ if (opts.minPrice !== void 0 && currentPrice < opts.minPrice) {
3431
+ return false;
3432
+ }
3433
+ return !(opts.maxPrice !== void 0 && currentPrice > opts.maxPrice);
3434
+ })
3435
+ );
3436
+ }
3437
+ if (opts?.sortBy === "price_asc") {
3438
+ products.sort((a, b) => getMinVariantPrice(a) - getMinVariantPrice(b));
3439
+ } else if (opts?.sortBy === "price_desc") {
3440
+ products.sort((a, b) => getMinVariantPrice(b) - getMinVariantPrice(a));
3441
+ } else if (opts?.search && !opts?.sortBy) {
3442
+ const search = opts.search;
3443
+ products.sort((a, b) => {
3444
+ const diff = scoreSearchRelevance(b, search) - scoreSearchRelevance(a, search);
3445
+ return diff !== 0 ? diff : a.name.localeCompare(b.name);
3446
+ });
3447
+ }
3448
+ return products;
3449
+ },
3450
+ async getProduct(productId) {
3451
+ const ctx = getContext();
3452
+ if (!ctx.chainId) {
3453
+ throw new WebsiteError(
3454
+ "This website has no chain configured. Catalog requires a chain_id.",
3455
+ "NO_CHAIN"
3456
+ );
3457
+ }
3458
+ const data = await executeGraphQL(
3459
+ graphqlUrl,
3460
+ GET_PRODUCT_BY_PK,
3461
+ {
3462
+ productId,
3463
+ companyId: ctx.companyId,
3464
+ chainId: ctx.chainId,
3465
+ websiteId: ctx.websiteId
3466
+ },
3467
+ getHeaders
3468
+ );
3469
+ return mapSingleProductResult(data);
3470
+ },
3471
+ async getProductBySlug(slug) {
3472
+ const ctx = getContext();
3473
+ if (!ctx.chainId) {
3474
+ throw new WebsiteError(
3475
+ "This website has no chain configured. Catalog requires a chain_id.",
3476
+ "NO_CHAIN"
3477
+ );
3478
+ }
3479
+ const data = await executeGraphQL(
3480
+ graphqlUrl,
3481
+ GET_PRODUCT_BY_SLUG,
3482
+ {
3483
+ slug,
3484
+ companyId: ctx.companyId,
3485
+ chainId: ctx.chainId,
3486
+ websiteId: ctx.websiteId
3487
+ },
3488
+ getHeaders
3489
+ );
3490
+ return mapSingleProductResult(data);
3491
+ },
3492
+ async getCategories() {
3493
+ const data = await executeGraphQL(graphqlUrl, GET_CATEGORIES, {}, getHeaders);
3494
+ return (data.product_categories ?? []).map(mapCategory);
3495
+ },
3496
+ async getCategoryList(opts) {
3497
+ const ctx = getContext();
3498
+ const data = await executeGraphQL(
3499
+ graphqlUrl,
3500
+ GET_CATALOG_CATEGORY_LIST,
3501
+ {
3502
+ companyId: ctx.companyId,
3503
+ key: opts.key
3504
+ },
3505
+ getHeaders
3506
+ );
3507
+ const list = data.list?.[0] ?? null;
3508
+ if (!list) return null;
3509
+ if (opts.onlyApplicable || hasMeaningfulCategoryListFilters(opts.filters)) {
3510
+ if (!ctx.chainId) {
3511
+ throw new WebsiteError(
3512
+ "This website has no chain configured. Applicable category lists require a chain_id.",
3513
+ "NO_CHAIN"
3514
+ );
3515
+ }
3516
+ const chainId = ctx.chainId;
3517
+ const applicableProductsData = await executeGraphQL(
3518
+ graphqlUrl,
3519
+ GET_PRODUCT_FILTERS,
3520
+ {
3521
+ companyId: ctx.companyId,
3522
+ chainId,
3523
+ attributeListKey: DEFAULT_FILTER_ATTRIBUTE_LIST_KEY,
3524
+ where: buildProductWhere(opts.filters ?? {}, chainId)
3525
+ },
3526
+ getHeaders
3527
+ );
3528
+ const variantIds = (applicableProductsData.products ?? []).flatMap(
3529
+ (product) => product.product_variants.map((variant) => variant.id)
3530
+ );
3531
+ const availabilityByVariantId = await getWebsiteAvailabilityByVariantId(
3532
+ graphqlUrl,
3533
+ getHeaders,
3534
+ variantIds,
3535
+ chainId
3536
+ );
3537
+ const applicableCategories = /* @__PURE__ */ new Set();
3538
+ const applicableSubcategoryIds = /* @__PURE__ */ new Set();
3539
+ for (const product of applicableProductsData.products ?? []) {
3540
+ const hasVisibleVariant = product.product_variants.some((variant) => {
3541
+ const prices = selectPublicPrices(
3542
+ variant.product_variant_prices.map(mapPrice),
3543
+ chainId
3544
+ );
3545
+ return prices.length > 0 && (availabilityByVariantId.get(variant.id)?.inStock ?? false);
3546
+ });
3547
+ if (!hasVisibleVariant) continue;
3548
+ applicableCategories.add(product.category);
3549
+ if (product.subcategory) {
3550
+ applicableSubcategoryIds.add(product.subcategory);
3551
+ }
3552
+ }
3553
+ return filterCategoryListByApplicability(
3554
+ list,
3555
+ applicableCategories,
3556
+ applicableSubcategoryIds
3557
+ );
3558
+ }
3559
+ return mapCategoryList(list);
3560
+ },
3561
+ async getAttributeList(opts) {
3562
+ const ctx = getContext();
3563
+ if (opts.onlyApplicable || hasMeaningfulAttributeListFilters(opts.filters)) {
3564
+ if (!ctx.websiteId) {
3565
+ throw new WebsiteError(
3566
+ "This client has no website configured. Contextual attribute lists require a website_id.",
3567
+ "NO_WEBSITE"
3568
+ );
3569
+ }
3570
+ const data2 = await executeGraphQL(
3571
+ graphqlUrl,
3572
+ GET_CONTEXTUAL_CATALOG_ATTRIBUTE_LIST,
3573
+ {
3574
+ websiteId: ctx.websiteId,
3575
+ key: opts.key,
3576
+ onlyApplicable: opts.onlyApplicable ? true : void 0,
3577
+ filters: opts.filters
3578
+ },
3579
+ getHeaders
3580
+ );
3581
+ const result = data2.websiteGetAttributeList;
3582
+ if (!result.success) {
3583
+ throw new WebsiteError(
3584
+ result.errors?.join("; ") || "Unable to resolve attribute list",
3585
+ "ATTRIBUTE_LIST_ERROR"
3586
+ );
3587
+ }
3588
+ return result.list ? mapContextualAttributeList(result.list) : null;
3589
+ }
3590
+ const data = await executeGraphQL(
3591
+ graphqlUrl,
3592
+ GET_CATALOG_ATTRIBUTE_LIST,
3593
+ {
3594
+ companyId: ctx.companyId,
3595
+ key: opts.key
3596
+ },
3597
+ getHeaders
3598
+ );
3599
+ const list = data.list?.[0] ?? null;
3600
+ return list ? mapAttributeList(list) : null;
3601
+ },
3602
+ async getBrands() {
3603
+ const ctx = getContext();
3604
+ if (!ctx.chainId) {
3605
+ throw new WebsiteError(
3606
+ "This website has no chain configured. Brands require a chain_id.",
3607
+ "NO_CHAIN"
3608
+ );
3609
+ }
3610
+ const data = await executeGraphQL(
3611
+ graphqlUrl,
3612
+ GET_BRANDS,
3613
+ { companyId: ctx.companyId, chainId: ctx.chainId },
3614
+ getHeaders
3615
+ );
3616
+ return data.brands ?? [];
3617
+ },
3618
+ async getProductFilters(opts) {
3619
+ const ctx = getContext();
3620
+ if (!ctx.chainId) {
3621
+ throw new WebsiteError(
3622
+ "This website has no chain configured. Catalog filters require a chain_id.",
3623
+ "NO_CHAIN"
3624
+ );
3625
+ }
3626
+ const chainId = ctx.chainId;
3627
+ const data = await executeGraphQL(
3628
+ graphqlUrl,
3629
+ GET_PRODUCT_FILTERS,
3630
+ {
3631
+ companyId: ctx.companyId,
3632
+ chainId,
3633
+ attributeListKey: opts?.attributeListKey ?? DEFAULT_FILTER_ATTRIBUTE_LIST_KEY,
3634
+ where: {
3635
+ ...opts?.categoryName ? { category: { _eq: opts.categoryName } } : {},
3636
+ ...opts?.subcategoryId ? { subcategory: { _eq: opts.subcategoryId } } : {}
3637
+ }
3638
+ },
3639
+ getHeaders
3640
+ );
3641
+ const categoryCounts = /* @__PURE__ */ new Map();
3642
+ const colorCounts = /* @__PURE__ */ new Map();
3643
+ const brandCounts = /* @__PURE__ */ new Map();
3644
+ const publicPrices = [];
3645
+ const attributeValueCounts = /* @__PURE__ */ new Map();
3646
+ const variantIds = (data.products ?? []).flatMap(
3647
+ (product) => product.product_variants.map((variant) => variant.id)
3648
+ );
3649
+ const availabilityByVariantId = await getWebsiteAvailabilityByVariantId(
3650
+ graphqlUrl,
3651
+ getHeaders,
3652
+ variantIds,
3653
+ chainId
3654
+ );
3655
+ const publicAttributeLabels = buildPublicAttributeLabels(
3656
+ data.catalog_attribute_lists
3657
+ );
3658
+ const configuredAttributeIds = new Set(
3659
+ (data.catalog_attribute_lists?.[0]?.items ?? []).map(
3660
+ (item) => item.attribute_id
3661
+ )
3662
+ );
3663
+ const registerAttributeValue = (raw) => {
3664
+ if (!configuredAttributeIds.has(raw.attribute.id)) return;
3665
+ const valueCode = attributeValueCode(raw);
3666
+ const value = raw.attribute_value ?? null;
3667
+ const counts = attributeValueCounts.get(raw.attribute.id) ?? /* @__PURE__ */ new Map();
3668
+ const current = counts.get(valueCode);
3669
+ counts.set(valueCode, {
3670
+ count: (current?.count ?? 0) + 1,
3671
+ value: value ?? current?.value ?? null
3672
+ });
3673
+ attributeValueCounts.set(raw.attribute.id, counts);
3674
+ };
3675
+ for (const product of data.products ?? []) {
3676
+ if (opts?.categoryName && product.category !== opts.categoryName) {
3677
+ continue;
3678
+ }
3679
+ const visibleVariants = product.product_variants.map((variant) => ({
3680
+ ...variant,
3681
+ prices: selectPublicPrices(
3682
+ variant.product_variant_prices.map(mapPrice),
3683
+ chainId
3684
+ ),
3685
+ inStock: availabilityByVariantId.get(variant.id)?.inStock ?? false
3686
+ })).filter((variant) => variant.inStock && variant.prices.length > 0);
3687
+ if (visibleVariants.length === 0) continue;
3688
+ categoryCounts.set(
3689
+ product.category,
3690
+ (categoryCounts.get(product.category) ?? 0) + 1
3691
+ );
3692
+ if (product.color) {
3693
+ colorCounts.set(
3694
+ product.color,
3695
+ (colorCounts.get(product.color) ?? 0) + 1
3696
+ );
3697
+ }
3698
+ if (product.brand) {
3699
+ const current = brandCounts.get(product.brand.id);
3700
+ brandCounts.set(product.brand.id, {
3701
+ id: product.brand.id,
3702
+ name: product.brand.name,
3703
+ count: (current?.count ?? 0) + 1
3704
+ });
3705
+ }
3706
+ for (const variant of visibleVariants) {
3707
+ for (const price of variant.prices) {
3708
+ publicPrices.push(price.price);
3709
+ }
3710
+ if (opts?.categoryName) {
3711
+ for (const attribute of variant.product_variant_attributes) {
3712
+ registerAttributeValue(attribute);
3713
+ }
3714
+ }
3715
+ }
3716
+ if (opts?.categoryName) {
3717
+ for (const attribute of product.product_attributes) {
3718
+ registerAttributeValue(attribute);
3719
+ }
3720
+ }
3721
+ }
3722
+ const attributes = opts?.categoryName ? (data.catalog_attribute_lists?.[0]?.items ?? []).map((item) => {
3723
+ const counts = attributeValueCounts.get(item.attribute_id);
3724
+ if (!counts || counts.size === 0) return null;
3725
+ return {
3726
+ id: item.attribute_id,
3727
+ name: item.public_label,
3728
+ values: Array.from(counts.entries()).map(([valueCode, countItem]) => ({
3729
+ value: publicAttributeLabels.valueLabelsById.get(
3730
+ countItem.value?.id ?? ""
3731
+ ) ?? publicAttributeLabels.valueLabelsByAttributeAndCode.get(
3732
+ attributeValueLabelKey(item.attribute_id, valueCode)
3733
+ ) ?? valueCode,
3734
+ count: countItem.count,
3735
+ attributeValueId: countItem.value?.id ?? null,
3736
+ code: countItem.value?.code ?? valueCode,
3737
+ metadata: countItem.value?.metadata ?? null,
3738
+ sortOrder: countItem.value?.sort_order ?? null
3739
+ })).sort((a, b) => {
3740
+ const orderA = a.sortOrder ?? Number.MAX_SAFE_INTEGER;
3741
+ const orderB = b.sortOrder ?? Number.MAX_SAFE_INTEGER;
3742
+ return orderA === orderB ? a.value.localeCompare(b.value) : orderA - orderB;
3743
+ })
3744
+ };
3745
+ }).filter((item) => Boolean(item)) : [];
3746
+ const minPrice = publicPrices.length > 0 ? Math.min(...publicPrices) : 0;
3747
+ const maxPrice = publicPrices.length > 0 ? Math.max(...publicPrices) : 0;
3748
+ return {
3749
+ categories: Array.from(categoryCounts.entries()).map(([name, count]) => ({ name, count })).sort((a, b) => a.name.localeCompare(b.name)),
3750
+ brands: Array.from(brandCounts.values()).sort(
3751
+ (a, b) => a.name.localeCompare(b.name)
3752
+ ),
3753
+ colors: Array.from(colorCounts.entries()).map(([value, count]) => ({ value, count })).sort((a, b) => a.value.localeCompare(b.value)),
3754
+ priceRange: {
3755
+ min: minPrice,
3756
+ max: maxPrice
3757
+ },
3758
+ attributes
3759
+ };
3760
+ },
3761
+ async getSearchSuggestions(opts) {
3762
+ if (!opts.query || opts.query.trim().length === 0) return [];
3763
+ const ctx = getContext();
3764
+ if (!ctx.chainId) {
3765
+ throw new WebsiteError(
3766
+ "This website has no chain configured. Search suggestions require a chain_id.",
3767
+ "NO_CHAIN"
3768
+ );
3769
+ }
3770
+ const term = `%${opts.query}%`;
3771
+ const limit = opts.limit ?? 8;
3772
+ const data = await executeGraphQL(
3773
+ graphqlUrl,
3774
+ GET_SEARCH_SUGGESTIONS,
3775
+ {
3776
+ companyId: ctx.companyId,
3777
+ chainId: ctx.chainId,
3778
+ term,
3779
+ productLimit: Math.min(limit, 5),
3780
+ brandLimit: Math.max(limit - 5, 3)
3781
+ },
3782
+ getHeaders
3783
+ );
3784
+ const suggestions = [];
3785
+ for (const p of data.products ?? []) {
3786
+ suggestions.push({ type: "product", text: p.name, id: p.id });
3787
+ }
3788
+ for (const b of data.brands ?? []) {
3789
+ suggestions.push({ type: "brand", text: b.name, id: b.id });
3790
+ }
3791
+ return suggestions.slice(0, limit);
3792
+ }
3793
+ };
3794
+ }
3795
+
3796
+ // libs/sdk/src/modules/checkout.ts
3797
+ function requireCartSession2(storage) {
3798
+ const session = storage.get();
3799
+ if (!session) {
3800
+ throw new WebsiteError(
3801
+ "No guest cart session found",
3802
+ "CART_SESSION_MISSING"
3803
+ );
3804
+ }
3805
+ return session;
3806
+ }
3807
+ function mapCartItem2(raw) {
3808
+ return {
3809
+ id: raw.id,
3810
+ productVariantId: raw.product_variant_id,
3811
+ quantity: raw.quantity,
3812
+ productVariant: {
3813
+ sku: raw.product_variant.sku,
3814
+ product: raw.product_variant.product,
3815
+ prices: raw.product_variant.prices.map((price) => ({
3816
+ id: price.id,
3817
+ price: price.price,
3818
+ priceType: price.price_type,
3819
+ chainId: price.chain_id
3820
+ }))
3821
+ }
3822
+ };
3823
+ }
3824
+ function mapCart2(raw) {
3825
+ return {
3826
+ id: raw.id,
3827
+ items: raw.items.map(mapCartItem2),
3828
+ totals: {
3829
+ subtotal: raw.total_without_tax,
3830
+ tax: raw.tax,
3831
+ discount: raw.discount,
3832
+ total: raw.total_with_tax
3833
+ },
3834
+ status: raw.status,
3835
+ createdAt: raw.created_at,
3836
+ updatedAt: raw.updated_at
3837
+ };
3838
+ }
3839
+ function mapCouponResult(raw) {
3840
+ return {
3841
+ success: raw.success,
3842
+ orderId: raw.order_id,
3843
+ couponId: raw.coupon_id,
3844
+ code: raw.code,
3845
+ name: raw.name,
3846
+ couponType: raw.coupon_type,
3847
+ discountAmount: Number(raw.discount_amount ?? 0),
3848
+ eligibleSubtotal: Number(raw.eligible_subtotal ?? 0),
3849
+ message: raw.message,
3850
+ cart: raw.cart ? mapCart2(raw.cart) : null,
3851
+ errors: raw.errors
3852
+ };
3853
+ }
3854
+ function createCheckoutModule(graphqlUrl, getHeaders, getContext, cartSessionStorage) {
3855
+ return {
3856
+ async getLocationOptions() {
3857
+ const context = getContext();
3858
+ const data = await executeGraphQL(
3859
+ graphqlUrl,
3860
+ GET_LOCATION_OPTIONS,
3861
+ { websiteId: context.websiteId },
3862
+ getHeaders
3863
+ );
3864
+ const result = data.websiteGetLocationOptions;
3865
+ if (!result.success) {
3866
+ throw new CheckoutError(result.errors ?? ["LOCATION_OPTIONS_FAILED"]);
3867
+ }
3868
+ return {
3869
+ countryCode: result.country_code,
3870
+ departments: result.departments.map((department) => ({
3871
+ departmentId: department.department_id,
3872
+ departmentName: department.department_name,
3873
+ departmentCode: department.department_code,
3874
+ municipalities: department.municipalities.map((municipality) => ({
3875
+ municipalityId: municipality.municipality_id,
3876
+ municipalityName: municipality.municipality_name,
3877
+ municipalityCode: municipality.municipality_code,
3878
+ departmentId: municipality.department_id,
3879
+ departmentCode: municipality.department_code
3880
+ }))
3881
+ }))
3882
+ };
3883
+ },
3884
+ async getDeliveryOptions() {
3885
+ const context = getContext();
3886
+ const data = await executeGraphQL(
3887
+ graphqlUrl,
3888
+ GET_DELIVERY_OPTIONS,
3889
+ { websiteId: context.websiteId },
3890
+ getHeaders
3891
+ );
3892
+ const result = data.websiteGetDeliveryOptions;
3893
+ if (!result.success) {
3894
+ throw new CheckoutError(result.errors ?? ["DELIVERY_OPTIONS_FAILED"]);
3895
+ }
3896
+ return result.delivery_options.map((option) => ({
3897
+ id: option.id,
3898
+ fulfillmentFamily: option.fulfillment_family,
3899
+ label: option.label,
3900
+ description: option.description,
3901
+ isEnabled: option.is_enabled,
3902
+ pickupInfo: option.pickup_info ? {
3903
+ label: option.pickup_info.label,
3904
+ address: option.pickup_info.address,
3905
+ schedule: option.pickup_info.schedule,
3906
+ notes: option.pickup_info.notes
3907
+ } : null
3908
+ }));
3909
+ },
3910
+ async getShippingQuotes(input) {
3911
+ const context = getContext();
3912
+ const session = cartSessionStorage.get();
3913
+ const items = session ? null : input.items?.length ? input.items : null;
3914
+ const data = await executeGraphQL(
3915
+ graphqlUrl,
3916
+ GET_SHIPPING_QUOTES,
3917
+ {
3918
+ websiteId: context.websiteId,
3919
+ orderId: session?.orderId ?? null,
3920
+ cartToken: session?.cartToken ?? null,
3921
+ items: items?.map((item) => ({
3922
+ product_variant_id: item.productVariantId,
3923
+ quantity: item.quantity
3924
+ })),
3925
+ address: input.address
3926
+ },
3927
+ getHeaders
3928
+ );
3929
+ const result = data.websiteGetShippingQuotes;
3930
+ if (!result.success) {
3931
+ throw new CheckoutError(result.errors ?? ["SHIPPING_QUOTES_FAILED"]);
3932
+ }
3933
+ return result.quotes.map((quote) => ({
3934
+ quoteId: quote.quote_id,
3935
+ shippingServiceId: quote.shipping_service_id,
3936
+ label: quote.label,
3937
+ serviceName: quote.service_name,
3938
+ baseAmount: quote.base_amount,
3939
+ amount: quote.amount,
3940
+ currency: quote.currency,
3941
+ collectionMode: quote.collection_mode,
3942
+ totalPolicy: quote.total_policy,
3943
+ estimatedDelivery: quote.estimated_delivery,
3944
+ freeShippingEligible: quote.free_shipping_eligible,
3945
+ freeShippingApplied: quote.free_shipping_applied,
3946
+ freeShippingThreshold: quote.free_shipping_threshold,
3947
+ freeShippingRemaining: quote.free_shipping_remaining,
3948
+ freeShippingEligibleSubtotal: quote.free_shipping_eligible_subtotal
3949
+ }));
3950
+ },
3951
+ async getShippingPolicy() {
3952
+ const context = getContext();
3953
+ const data = await executeGraphQL(
3954
+ graphqlUrl,
3955
+ GET_SHIPPING_POLICY,
3956
+ { websiteId: context.websiteId },
3957
+ getHeaders
3958
+ );
3959
+ const result = data.websiteGetShippingPolicy;
3960
+ if (!result.success || !result.policy) {
3961
+ throw new CheckoutError(result.errors ?? ["SHIPPING_POLICY_FAILED"]);
3962
+ }
3963
+ return {
3964
+ freeShippingEnabled: result.policy.free_shipping_enabled,
3965
+ freeShippingThreshold: result.policy.free_shipping_threshold,
3966
+ currency: result.policy.currency
3967
+ };
3968
+ },
3969
+ async getFreeShippingProgress(input) {
3970
+ if (input.items.length === 0) {
3971
+ throw new WebsiteError(
3972
+ "At least one cart item is required",
3973
+ "CART_ITEMS_REQUIRED"
3974
+ );
3975
+ }
3976
+ const context = getContext();
3977
+ const data = await executeGraphQL(
3978
+ graphqlUrl,
3979
+ GET_FREE_SHIPPING_PROGRESS,
3980
+ {
3981
+ websiteId: context.websiteId,
3982
+ items: input.items.map((item) => ({
3983
+ product_variant_id: item.productVariantId,
3984
+ quantity: item.quantity
3985
+ }))
3986
+ },
3987
+ getHeaders
3988
+ );
3989
+ const result = data.websiteGetFreeShippingProgress;
3990
+ if (!result.success || !result.progress) {
3991
+ throw new CheckoutError(
3992
+ result.errors ?? ["FREE_SHIPPING_PROGRESS_FAILED"]
3993
+ );
3994
+ }
3995
+ return {
3996
+ enabled: result.progress.enabled,
3997
+ threshold: result.progress.threshold,
3998
+ remaining: result.progress.remaining,
3999
+ eligibleSubtotal: result.progress.eligible_subtotal,
4000
+ thresholdReached: result.progress.threshold_reached,
4001
+ currency: result.progress.currency
4002
+ };
4003
+ },
4004
+ async getPaymentMethods() {
4005
+ const context = getContext();
4006
+ const data = await executeGraphQL(
4007
+ graphqlUrl,
4008
+ GET_PAYMENT_METHODS,
4009
+ { websiteId: context.websiteId },
4010
+ getHeaders
4011
+ );
4012
+ const result = data.websiteGetPaymentMethods;
4013
+ if (!result.success) {
4014
+ throw new CheckoutError(result.errors ?? ["PAYMENT_METHODS_FAILED"]);
4015
+ }
4016
+ return result.payment_methods.map((method) => ({
4017
+ id: method.id,
4018
+ code: method.code,
4019
+ label: method.label,
4020
+ paymentMethodType: method.payment_method_type,
4021
+ gatewayCode: method.gateway_code,
4022
+ description: method.description,
4023
+ isEnabled: method.is_enabled,
4024
+ sortOrder: method.sort_order,
4025
+ requiresPaymentSession: method.requires_payment_session,
4026
+ publicConfig: method.public_config
4027
+ }));
4028
+ },
4029
+ async getPaymentOptions(input) {
4030
+ const context = getContext();
4031
+ const data = await executeGraphQL(
4032
+ graphqlUrl,
4033
+ GET_PAYMENT_OPTIONS,
4034
+ {
4035
+ websiteId: context.websiteId,
4036
+ fulfillmentFamily: input.fulfillmentFamily
4037
+ },
4038
+ getHeaders
4039
+ );
4040
+ const result = data.websiteGetPaymentOptions;
4041
+ if (!result.success) {
4042
+ throw new CheckoutError(result.errors ?? ["PAYMENT_OPTIONS_FAILED"]);
4043
+ }
4044
+ return result.payment_options.map((option) => ({
4045
+ id: option.id,
4046
+ mode: option.mode,
4047
+ fulfillmentFamily: option.fulfillment_family,
4048
+ label: option.label,
4049
+ description: option.description,
4050
+ requiresPaymentMethod: option.requires_payment_method,
4051
+ allowsInitialPayment: option.allows_initial_payment,
4052
+ initialPaymentPolicy: option.initial_payment_policy,
4053
+ requiresInitialPayment: option.requires_initial_payment,
4054
+ minInitialAmount: option.min_initial_amount,
4055
+ suggestedInitialAmount: option.suggested_initial_amount,
4056
+ sortOrder: option.sort_order,
4057
+ methods: option.methods.map((method) => ({
4058
+ id: method.id,
4059
+ paymentMethodId: method.payment_method_id,
4060
+ code: method.code,
4061
+ label: method.label,
4062
+ paymentMethodType: method.payment_method_type,
4063
+ gatewayCode: method.gateway_code,
4064
+ description: method.description,
4065
+ instructions: method.instructions,
4066
+ qrFileId: method.qr_file_id,
4067
+ requiresPaymentSession: method.requires_payment_session,
4068
+ publicConfig: method.public_config ?? {},
4069
+ sortOrder: method.sort_order
4070
+ }))
4071
+ }));
4072
+ },
4073
+ async validateCoupon(input) {
4074
+ const session = requireCartSession2(cartSessionStorage);
4075
+ const data = await executeGraphQL(
4076
+ graphqlUrl,
4077
+ VALIDATE_COUPON,
4078
+ {
4079
+ orderId: session.orderId,
4080
+ cartToken: session.cartToken,
4081
+ code: input.code
4082
+ },
4083
+ getHeaders
4084
+ );
4085
+ return mapCouponResult(data.websiteValidateCoupon);
4086
+ },
4087
+ async applyCoupon(input) {
4088
+ const session = requireCartSession2(cartSessionStorage);
4089
+ const data = await executeGraphQL(
4090
+ graphqlUrl,
4091
+ APPLY_COUPON,
4092
+ {
4093
+ orderId: session.orderId,
4094
+ cartToken: session.cartToken,
4095
+ code: input.code
4096
+ },
4097
+ getHeaders
4098
+ );
4099
+ return mapCouponResult(data.websiteApplyCoupon);
4100
+ },
4101
+ async removeCoupon(input) {
4102
+ const session = requireCartSession2(cartSessionStorage);
4103
+ const data = await executeGraphQL(
4104
+ graphqlUrl,
4105
+ REMOVE_COUPON,
4106
+ {
4107
+ orderId: session.orderId,
4108
+ cartToken: session.cartToken,
4109
+ couponId: input.couponId
4110
+ },
4111
+ getHeaders
4112
+ );
4113
+ return mapCouponResult(data.websiteRemoveCoupon);
4114
+ },
4115
+ async submit(input) {
4116
+ const session = requireCartSession2(cartSessionStorage);
4117
+ const data = await executeGraphQL(
4118
+ graphqlUrl,
4119
+ CHECKOUT,
4120
+ {
4121
+ orderId: session.orderId,
4122
+ cartToken: session.cartToken,
4123
+ contact: input.contact,
4124
+ fulfillmentFamily: input.fulfillmentFamily,
4125
+ address: input.address,
4126
+ shippingQuoteId: input.shippingQuoteId,
4127
+ paymentMode: input.paymentMode,
4128
+ paymentMethodId: input.paymentMethodId,
4129
+ buyerAccountOptIn: input.buyerAccountOptIn,
4130
+ initialPayment: input.initialPayment ? {
4131
+ amount: input.initialPayment.amount,
4132
+ payment_method_id: input.initialPayment.paymentMethodId
4133
+ } : void 0
4134
+ },
4135
+ getHeaders
4136
+ );
4137
+ const result = data.websiteCheckout;
4138
+ if (!result.success && result.errors && result.errors.length > 0) {
4139
+ throw new CheckoutError(result.errors);
4140
+ }
4141
+ if (result.success && !result.requires_payment_session) {
4142
+ cartSessionStorage.clear();
4143
+ }
4144
+ return {
4145
+ success: result.success,
4146
+ orderId: result.order_id,
4147
+ orderNumber: result.order_number,
4148
+ paymentMethodId: result.payment_method_id,
4149
+ publicOrderToken: result.public_order_token,
4150
+ requiresPaymentSession: Boolean(result.requires_payment_session),
4151
+ buyerAccountInvitationRequested: Boolean(
4152
+ result.buyer_account_invitation_requested
4153
+ ),
4154
+ errors: result.errors
4155
+ };
4156
+ },
4157
+ async createPaymentSession(input) {
4158
+ const session = cartSessionStorage.get();
4159
+ const orderId = input.orderId ?? session?.orderId;
4160
+ if (!orderId || !input.publicOrderToken && !session?.cartToken) {
4161
+ throw new WebsiteError(
4162
+ "Payment retry requires an order and public order token",
4163
+ "PAYMENT_SESSION_CONTEXT_MISSING"
4164
+ );
4165
+ }
4166
+ const data = await executeGraphQL(
4167
+ graphqlUrl,
4168
+ CREATE_PAYMENT_SESSION,
4169
+ {
4170
+ orderId,
4171
+ cartToken: input.publicOrderToken ? null : session?.cartToken,
4172
+ publicOrderToken: input.publicOrderToken ?? null,
4173
+ paymentMethodId: input.paymentMethodId,
4174
+ returnUrl: input.returnUrl
4175
+ },
4176
+ getHeaders
4177
+ );
4178
+ const result = data.websiteCreatePaymentSession;
4179
+ if (result.success && result.payment_session_id && (result.integration_mode !== "redirect" || !result.redirect_url)) {
4180
+ throw new WebsiteError(
4181
+ "Payment gateway did not provide a redirect URL",
4182
+ "PAYMENT_GATEWAY_REDIRECT_NOT_AVAILABLE"
4183
+ );
4184
+ }
4185
+ if (result.success) {
4186
+ cartSessionStorage.clear();
4187
+ }
4188
+ return {
4189
+ success: result.success,
4190
+ orderId: result.order_id,
4191
+ paymentMethodId: result.payment_method_id,
4192
+ paymentSessionId: result.payment_session_id,
4193
+ gatewayCode: result.gateway_code,
4194
+ integrationMode: result.integration_mode === "redirect" ? "redirect" : null,
4195
+ status: result.status,
4196
+ redirectUrl: result.redirect_url,
4197
+ publicPayload: result.public_payload,
4198
+ errors: result.errors
4199
+ };
4200
+ },
4201
+ async reconcilePaymentSession(input) {
4202
+ const data = await executeGraphQL(
4203
+ graphqlUrl,
4204
+ RECONCILE_PAYMENT_SESSION,
4205
+ {
4206
+ orderId: input.orderId,
4207
+ publicOrderToken: input.publicOrderToken,
4208
+ paymentSessionId: input.paymentSessionId,
4209
+ gatewayTransactionId: input.gatewayTransactionId
4210
+ },
4211
+ getHeaders
4212
+ );
4213
+ const result = data.websiteReconcilePaymentSession;
4214
+ return {
4215
+ success: result.success,
4216
+ orderId: result.order_id,
4217
+ paymentMethodId: result.payment_method_id,
4218
+ paymentSessionId: result.payment_session_id,
4219
+ gatewayCode: result.gateway_code,
4220
+ integrationMode: result.integration_mode === "redirect" ? "redirect" : null,
4221
+ status: result.status,
4222
+ redirectUrl: result.redirect_url,
4223
+ publicPayload: result.public_payload,
4224
+ errors: result.errors
4225
+ };
4226
+ }
4227
+ };
4228
+ }
4229
+
4230
+ // libs/sdk/src/modules/content.ts
4231
+ var DEFAULT_LOCALE = "es-CO";
4232
+ function mapEntry(raw) {
4233
+ return {
4234
+ id: raw.id,
4235
+ key: raw.key,
4236
+ categoryKey: raw.category_key,
4237
+ name: raw.name,
4238
+ locale: raw.locale,
4239
+ status: raw.status,
4240
+ tags: raw.tags ?? [],
4241
+ contentJson: raw.content_json ?? {},
4242
+ resolvedFields: raw.resolved_fields ?? {},
4243
+ publishedAt: raw.published_at,
4244
+ updatedAt: raw.updated_at,
4245
+ contentType: {
4246
+ id: raw.content_type.id,
4247
+ key: raw.content_type.key,
4248
+ name: raw.content_type.name,
4249
+ kind: raw.content_type.kind
4250
+ }
4251
+ };
4252
+ }
4253
+ function baseVariables(ctx, opts) {
4254
+ return {
4255
+ websiteId: ctx.websiteId,
4256
+ locale: opts?.locale ?? DEFAULT_LOCALE
4257
+ };
4258
+ }
4259
+ function isRecord(value) {
4260
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
4261
+ }
4262
+ function readOptionalText(value) {
4263
+ return typeof value === "string" && value.trim() ? value.trim() : null;
4264
+ }
4265
+ function enrichMediaValue(value, storageUrl) {
4266
+ const fileId = typeof value.fileId === "string" && value.fileId.trim() ? value.fileId.trim() : "";
4267
+ if (!fileId) return value;
4268
+ const urls = buildStorageMediaUrls(storageUrl, fileId);
4269
+ return {
4270
+ ...value,
4271
+ ...urls,
4272
+ url: value.url ?? urls.originalUrl
4273
+ };
4274
+ }
4275
+ function normalizeRichTextNode(value, storageUrl) {
4276
+ const type = value.type;
4277
+ const attrs = isRecord(value.attrs) ? value.attrs : null;
4278
+ if (attrs && (type === "bacanoImage" || type === "bacanoVideo") && typeof attrs.fileId === "string") {
4279
+ const urls = buildStorageMediaUrls(storageUrl, attrs.fileId);
4280
+ const altText = readOptionalText(attrs.altText) ?? readOptionalText(attrs.alt);
4281
+ const caption = readOptionalText(attrs.caption);
4282
+ const showCaption = Boolean(caption) && attrs.showCaption !== false;
4283
+ return {
4284
+ ...value,
4285
+ attrs: {
4286
+ ...attrs,
4287
+ ...urls,
4288
+ alt: altText,
4289
+ altText,
4290
+ caption,
4291
+ showCaption,
4292
+ src: type === "bacanoImage" ? urls.detailUrl : urls.originalUrl
4293
+ }
4294
+ };
4295
+ }
4296
+ return value;
4297
+ }
4298
+ function normalizeContentMedia(value, storageUrl) {
4299
+ if (Array.isArray(value)) {
4300
+ return value.map((item) => normalizeContentMedia(item, storageUrl));
4301
+ }
4302
+ if (!isRecord(value)) return value;
4303
+ const normalized = normalizeRichTextNode(value, storageUrl);
4304
+ const enriched = typeof normalized.fileId === "string" && typeof normalized.mediaKind === "string" ? enrichMediaValue(normalized, storageUrl) : normalized;
4305
+ return Object.fromEntries(
4306
+ Object.entries(enriched).map(([key, entryValue]) => [
4307
+ key,
4308
+ normalizeContentMedia(entryValue, storageUrl)
4309
+ ])
4310
+ );
4311
+ }
4312
+ function getEntries(data) {
4313
+ if (!data.result?.success) return [];
4314
+ return data.result.entries ?? [];
4315
+ }
4316
+ function chooseOne(data, storageUrl) {
4317
+ const entry = getEntries(data)[0] ?? null;
4318
+ return entry ? mapEntryWithMedia(entry, storageUrl) : null;
4319
+ }
4320
+ function chooseMany(data, storageUrl) {
4321
+ return getEntries(data).map((entry) => mapEntryWithMedia(entry, storageUrl));
4322
+ }
4323
+ function mapEntryWithMedia(raw, storageUrl) {
4324
+ return {
4325
+ ...mapEntry(raw),
4326
+ contentJson: normalizeContentMedia(
4327
+ raw.content_json ?? {},
4328
+ storageUrl
4329
+ ),
4330
+ resolvedFields: normalizeContentMedia(
4331
+ raw.resolved_fields ?? {},
4332
+ storageUrl
4333
+ )
4334
+ };
4335
+ }
4336
+ function mapProductContentBlock(raw, storageUrl) {
4337
+ return {
4338
+ id: raw.id,
4339
+ productId: raw.product_id,
4340
+ locale: raw.locale,
4341
+ contentJson: normalizeContentMedia(
4342
+ raw.content_json ?? {},
4343
+ storageUrl
4344
+ ),
4345
+ resolvedFields: normalizeContentMedia(
4346
+ raw.resolved_fields ?? {},
4347
+ storageUrl
4348
+ ),
4349
+ sortOrder: raw.sort_order,
4350
+ updatedAt: raw.updated_at,
4351
+ contentType: {
4352
+ id: raw.content_type.id,
4353
+ key: raw.content_type.key,
4354
+ name: raw.content_type.name,
4355
+ kind: raw.content_type.kind
4356
+ }
4357
+ };
4358
+ }
4359
+ function createContentModule(graphqlUrl, getHeaders, getContext, storageUrl) {
4360
+ return {
4361
+ async getByKey(key, opts) {
4362
+ const ctx = getContext();
4363
+ const data = await executeGraphQL(
4364
+ graphqlUrl,
4365
+ WEBSITE_GET_CONTENT,
4366
+ { ...baseVariables(ctx, opts), key },
4367
+ getHeaders
4368
+ );
4369
+ return chooseOne(data, storageUrl);
4370
+ },
4371
+ async getByCategory(categoryKey, opts) {
4372
+ const ctx = getContext();
4373
+ const data = await executeGraphQL(
4374
+ graphqlUrl,
4375
+ WEBSITE_GET_CONTENT,
4376
+ { ...baseVariables(ctx, opts), categoryKey },
4377
+ getHeaders
4378
+ );
4379
+ return chooseMany(data, storageUrl);
4380
+ },
4381
+ async getByTag(tag, opts) {
4382
+ const ctx = getContext();
4383
+ const data = await executeGraphQL(
4384
+ graphqlUrl,
4385
+ WEBSITE_GET_CONTENT,
4386
+ { ...baseVariables(ctx, opts), tag },
4387
+ getHeaders
4388
+ );
4389
+ return chooseMany(data, storageUrl);
4390
+ },
4391
+ async getForProduct(productId, opts) {
4392
+ const ctx = getContext();
4393
+ const data = await executeGraphQL(
4394
+ graphqlUrl,
4395
+ WEBSITE_GET_PRODUCT_CONTENT,
4396
+ {
4397
+ websiteId: ctx.websiteId,
4398
+ productId,
4399
+ locale: opts?.locale ?? DEFAULT_LOCALE
4400
+ },
4401
+ getHeaders
4402
+ );
4403
+ if (!data.result?.success) return [];
4404
+ return (data.result.blocks ?? []).map(
4405
+ (block) => mapProductContentBlock(block, storageUrl)
4406
+ );
4407
+ }
4408
+ };
4409
+ }
4410
+
4411
+ // libs/sdk/src/modules/orders.ts
4412
+ function mapOrderItem(raw) {
4413
+ return {
4414
+ id: raw.id,
4415
+ productVariantId: raw.product_variant_id,
4416
+ quantity: raw.quantity,
4417
+ productVariant: {
4418
+ sku: raw.product_variant.sku,
4419
+ product: raw.product_variant.product
4420
+ }
4421
+ };
4422
+ }
4423
+ function mapOrder(raw) {
4424
+ return {
4425
+ id: raw.id,
4426
+ orderNumber: raw.order_number,
4427
+ status: raw.status,
4428
+ fulfillmentFamily: raw.fulfillment_family,
4429
+ discount: raw.discount,
4430
+ tax: raw.tax,
4431
+ subtotal: raw.total_without_tax,
4432
+ total: raw.total_with_tax,
4433
+ totalWithDiscount: raw.total_with_discount,
4434
+ items: raw.order_product_variants.map(mapOrderItem),
4435
+ createdAt: raw.created_at,
4436
+ updatedAt: raw.updated_at
4437
+ };
4438
+ }
4439
+ function mapPublicOrder(raw) {
4440
+ return {
4441
+ orderId: raw.order_id,
4442
+ orderNumber: raw.order_number,
4443
+ status: raw.status,
4444
+ paymentStatus: raw.payment_status,
4445
+ fulfillmentFamily: raw.fulfillment_family,
4446
+ delivery: {
4447
+ fulfillmentFamily: raw.delivery.fulfillment_family,
4448
+ shipping: raw.delivery.shipping ? {
4449
+ label: raw.delivery.shipping.label,
4450
+ amount: raw.delivery.shipping.amount,
4451
+ methodType: raw.delivery.shipping.method_type,
4452
+ collectionMode: raw.delivery.shipping.collection_mode,
4453
+ totalPolicy: raw.delivery.shipping.total_policy,
4454
+ address: raw.delivery.shipping.address
4455
+ } : null,
4456
+ pickup: raw.delivery.pickup
4457
+ },
4458
+ payment: {
4459
+ mode: raw.payment.mode,
4460
+ label: raw.payment.label,
4461
+ method: raw.payment.method ? {
4462
+ id: raw.payment.method.id,
4463
+ label: raw.payment.method.label,
4464
+ paymentMethodType: raw.payment.method.payment_method_type,
4465
+ gatewayCode: raw.payment.method.gateway_code,
4466
+ requiresPaymentSession: raw.payment.method.requires_payment_session
4467
+ } : null,
4468
+ initialPayment: raw.payment.initial_payment ? {
4469
+ amount: raw.payment.initial_payment.amount,
4470
+ paymentMethodId: raw.payment.initial_payment.payment_method_id,
4471
+ methodLabel: raw.payment.initial_payment.method_label,
4472
+ paymentMethodType: raw.payment.initial_payment.payment_method_type,
4473
+ gatewayCode: raw.payment.initial_payment.gateway_code,
4474
+ requiresPaymentSession: raw.payment.initial_payment.requires_payment_session
4475
+ } : null,
4476
+ requiresPaymentSession: raw.payment.requires_payment_session,
4477
+ nextAction: raw.payment.next_action,
4478
+ session: raw.payment.session ? {
4479
+ id: raw.payment.session.id,
4480
+ gatewayCode: raw.payment.session.gateway_code,
4481
+ integrationMode: raw.payment.session.integration_mode,
4482
+ status: raw.payment.session.status,
4483
+ reference: raw.payment.session.reference,
4484
+ amount: raw.payment.session.amount,
4485
+ currency: raw.payment.session.currency,
4486
+ gatewayTransactionId: raw.payment.session.gateway_transaction_id,
4487
+ errorMessage: raw.payment.session.error_message,
4488
+ expiresAt: raw.payment.session.expires_at,
4489
+ createdAt: raw.payment.session.created_at,
4490
+ updatedAt: raw.payment.session.updated_at
4491
+ } : null
4492
+ },
4493
+ totals: {
4494
+ subtotal: raw.totals.subtotal,
4495
+ tax: raw.totals.tax,
4496
+ discount: raw.totals.discount,
4497
+ total: raw.totals.total,
4498
+ totalWithDiscount: raw.totals.total_with_discount
4499
+ },
4500
+ items: raw.items.map((item) => ({
4501
+ id: item.id,
4502
+ productVariantId: item.product_variant_id,
4503
+ sku: item.sku,
4504
+ name: item.name,
4505
+ imageFileId: item.image_file_id,
4506
+ quantity: item.quantity,
4507
+ unitPrice: item.unit_price,
4508
+ lineTotal: item.line_total
4509
+ })),
4510
+ nextSteps: raw.next_steps,
4511
+ createdAt: raw.created_at,
4512
+ updatedAt: raw.updated_at
4513
+ };
4514
+ }
4515
+ function createOrdersModule(graphqlUrl, getHeaders, getContext) {
4516
+ async function requireAuth() {
4517
+ const headers = await getHeaders();
4518
+ if (!headers.Authorization) {
4519
+ throw new AuthError("You must be logged in to view orders");
4520
+ }
4521
+ return headers;
4522
+ }
4523
+ async function fetchPublicOrder(input) {
4524
+ const context = getContext();
4525
+ const data = await executeGraphQL(
4526
+ graphqlUrl,
4527
+ GET_PUBLIC_ORDER,
4528
+ {
4529
+ websiteId: context.websiteId,
4530
+ orderId: input.orderId,
4531
+ token: input.token
4532
+ },
4533
+ getHeaders
4534
+ );
4535
+ const result = data.websiteGetPublicOrder;
4536
+ if (!result.success) {
4537
+ if (result.errors?.includes("ORDER_NOT_FOUND")) return null;
4538
+ throw new CheckoutError(result.errors ?? ["PUBLIC_ORDER_FAILED"]);
4539
+ }
4540
+ return result.order ? mapPublicOrder(result.order) : null;
4541
+ }
4542
+ return {
4543
+ async list(opts) {
4544
+ const headers = await requireAuth();
4545
+ const data = await executeGraphQL(
4546
+ graphqlUrl,
4547
+ GET_ORDERS,
4548
+ {
4549
+ limit: opts?.limit ?? 20,
4550
+ offset: opts?.offset ?? 0
4551
+ },
4552
+ headers
4553
+ );
4554
+ return (data.orders ?? []).map(mapOrder);
4555
+ },
4556
+ async get(orderId) {
4557
+ const headers = await requireAuth();
4558
+ const data = await executeGraphQL(
4559
+ graphqlUrl,
4560
+ GET_ORDER_BY_PK,
4561
+ { orderId },
4562
+ headers
4563
+ );
4564
+ const order = data.orders?.[0];
4565
+ return order ? mapOrder(order) : null;
4566
+ },
4567
+ async getPublicOrder(input) {
4568
+ return fetchPublicOrder(input);
4569
+ },
4570
+ async waitForPayment(input) {
4571
+ const intervalMs = Math.max(500, input.intervalMs ?? 1500);
4572
+ const timeoutMs = Math.max(intervalMs, input.timeoutMs ?? 12e4);
4573
+ const deadline = Date.now() + timeoutMs;
4574
+ while (Date.now() <= deadline) {
4575
+ const order = await fetchPublicOrder(input);
4576
+ if (!order) {
4577
+ throw new CheckoutError(["ORDER_NOT_FOUND"]);
4578
+ }
4579
+ const sessionStatus = order.payment.session?.status ?? null;
4580
+ if (order.paymentStatus === "PAID" || sessionStatus === "approved" || ["declined", "error", "expired", "cancelled"].includes(
4581
+ sessionStatus ?? ""
4582
+ )) {
4583
+ return order;
4584
+ }
4585
+ await wait(intervalMs);
4586
+ }
4587
+ throw new CheckoutError(["PAYMENT_STATUS_TIMEOUT"]);
4588
+ }
4589
+ };
4590
+ }
4591
+ function wait(ms) {
4592
+ return new Promise((resolve) => {
4593
+ setTimeout(resolve, ms);
4594
+ });
4595
+ }
4596
+
4597
+ // libs/sdk/src/modules/profile.ts
4598
+ function hasCanonicalLocation(address) {
4599
+ return Boolean(
4600
+ address.department_code && address.municipality_code && address.geo_department?.name && address.geo_municipality?.name
4601
+ );
4602
+ }
4603
+ function mapProfile(raw) {
4604
+ const primaryAddress = raw.party.shipping_addresses?.[0];
4605
+ return {
4606
+ id: raw.id,
4607
+ name: raw.party.first_name,
4608
+ lastname: raw.party.last_name,
4609
+ email: raw.party.email,
4610
+ cellphone: raw.party.phone_e164,
4611
+ phone: raw.party.phone_national_number,
4612
+ address: primaryAddress?.address ?? raw.party.address,
4613
+ city: primaryAddress?.geo_municipality?.name ?? raw.party.geo_municipality?.name ?? null,
4614
+ state: primaryAddress?.geo_department?.name ?? raw.party.geo_department?.name ?? null,
4615
+ country: primaryAddress?.geo_country?.name ?? raw.party.geo_country?.name ?? raw.party.country_code ?? null,
4616
+ birthday: raw.party.birthdate,
4617
+ companyName: raw.party.legal_name ?? raw.party.commercial_name
4618
+ };
4619
+ }
4620
+ function mapProfileAddresses(raw) {
4621
+ const party = raw.party;
4622
+ const recipientName = [party.first_name, party.last_name].filter(Boolean).join(" ") || party.display_name;
4623
+ const addresses = [];
4624
+ const shippingAddresses = party.shipping_addresses.filter(hasCanonicalLocation);
4625
+ const preferredShippingAddress = shippingAddresses.find((address) => address.is_primary) ?? shippingAddresses[0];
4626
+ if (party.address?.trim() && hasCanonicalLocation(party)) {
4627
+ addresses.push({
4628
+ id: raw.party_id,
4629
+ type: "PRIMARY",
4630
+ label: "Direcci\xF3n principal",
4631
+ isPrimary: !preferredShippingAddress,
4632
+ recipientName,
4633
+ cellphone: party.phone_e164 ?? party.phone_national_number,
4634
+ line1: party.address,
4635
+ line2: party.address_complement,
4636
+ notes: null,
4637
+ countryCode: party.country_code ?? "CO",
4638
+ countryName: party.geo_country?.name ?? party.country_code ?? "CO",
4639
+ departmentId: party.department_code,
4640
+ departmentCode: party.department_code,
4641
+ departmentName: party.geo_department.name,
4642
+ municipalityId: party.municipality_code,
4643
+ municipalityCode: party.municipality_code,
4644
+ municipalityName: party.geo_municipality.name
4645
+ });
4646
+ }
4647
+ addresses.push(
4648
+ ...shippingAddresses.map((address) => ({
4649
+ id: address.id,
4650
+ type: "SHIPPING",
4651
+ label: address.label,
4652
+ isPrimary: address.id === preferredShippingAddress?.id,
4653
+ recipientName: address.recipient_name,
4654
+ cellphone: address.phone_e164,
4655
+ line1: address.address,
4656
+ line2: address.address_complement,
4657
+ notes: address.notes,
4658
+ countryCode: address.country_code,
4659
+ countryName: address.geo_country?.name ?? address.country_code,
4660
+ departmentId: address.department_code,
4661
+ departmentCode: address.department_code,
4662
+ departmentName: address.geo_department.name,
4663
+ municipalityId: address.municipality_code,
4664
+ municipalityCode: address.municipality_code,
4665
+ municipalityName: address.geo_municipality.name
4666
+ }))
4667
+ );
4668
+ return addresses;
4669
+ }
4670
+ function mapProfileUpdate(data) {
4671
+ return {
4672
+ display_name: [data.name, data.lastname].filter(Boolean).join(" ") || void 0,
4673
+ first_name: data.name,
4674
+ last_name: data.lastname,
4675
+ phone_e164: data.cellphone,
4676
+ phone_national_number: data.phone,
4677
+ address: data.address,
4678
+ address_complement: data.addressComplement,
4679
+ country_code: data.countryCode,
4680
+ department_code: data.departmentId,
4681
+ municipality_code: data.municipalityId,
4682
+ birthdate: data.birthday
4683
+ };
4684
+ }
4685
+ function createProfileModule(graphqlUrl, getHeaders) {
4686
+ async function requireAuth() {
4687
+ const headers = await getHeaders();
4688
+ if (!headers.Authorization) {
4689
+ throw new AuthError("You must be logged in to access your profile");
4690
+ }
4691
+ return headers;
4692
+ }
4693
+ return {
4694
+ async get() {
4695
+ const headers = await requireAuth();
4696
+ const data = await executeGraphQL(
4697
+ graphqlUrl,
4698
+ GET_PROFILE,
4699
+ {},
4700
+ headers
4701
+ );
4702
+ const client = data.clients?.[0];
4703
+ if (!client) {
4704
+ throw new AuthError("Customer profile not found");
4705
+ }
4706
+ return mapProfile(client);
4707
+ },
4708
+ async getAddresses() {
4709
+ const headers = await requireAuth();
4710
+ const data = await executeGraphQL(
4711
+ graphqlUrl,
4712
+ GET_PROFILE_ADDRESSES,
4713
+ {},
4714
+ headers
4715
+ );
4716
+ const client = data.clients?.[0];
4717
+ if (!client) {
4718
+ throw new AuthError("Customer profile not found");
4719
+ }
4720
+ return mapProfileAddresses(client);
4721
+ },
4722
+ async update(updateData) {
4723
+ const headers = await requireAuth();
4724
+ const profileData = await executeGraphQL(
4725
+ graphqlUrl,
4726
+ GET_PROFILE,
4727
+ {},
4728
+ headers
4729
+ );
4730
+ const client = profileData.clients?.[0];
4731
+ if (!client) {
4732
+ throw new AuthError("Customer profile not found");
4733
+ }
4734
+ const data = await executeGraphQL(
4735
+ graphqlUrl,
4736
+ UPDATE_PROFILE,
4737
+ { partyId: client.party_id, data: mapProfileUpdate(updateData) },
4738
+ headers
4739
+ );
4740
+ return mapProfile({
4741
+ id: client.id,
4742
+ party_id: client.party_id,
4743
+ party: data.update_business_parties_by_pk
4744
+ });
4745
+ }
4746
+ };
4747
+ }
4748
+
4749
+ // libs/sdk/src/modules/website.ts
4750
+ function mapWebsite(raw) {
4751
+ return {
4752
+ websiteId: raw.id,
4753
+ companyId: raw.company_id,
4754
+ chainId: raw.chain_id,
4755
+ name: raw.name,
4756
+ slug: raw.slug,
4757
+ domain: raw.domain,
4758
+ config: raw.config
4759
+ };
4760
+ }
4761
+ async function resolveWebsite(graphqlUrl, slug, headers) {
4762
+ if (!slug.trim()) {
4763
+ throw new WebsiteError(
4764
+ "websiteSlug is required",
4765
+ "INVALID_CONFIG"
4766
+ );
4767
+ }
4768
+ const data = await executeGraphQL(
4769
+ graphqlUrl,
4770
+ RESOLVE_WEBSITE_BY_SLUG,
4771
+ { slug },
4772
+ headers
4773
+ );
4774
+ if (!data.websites || data.websites.length === 0) {
4775
+ throw new WebsiteError(
4776
+ `No active website found with slug "${slug}"`,
4777
+ "WEBSITE_NOT_FOUND"
4778
+ );
4779
+ }
4780
+ return mapWebsite(data.websites[0]);
4781
+ }
4782
+
4783
+ // libs/sdk/src/client.ts
4784
+ function normalizeApiUrl(apiUrl) {
4785
+ return apiUrl.replace(/\/+$/, "");
4786
+ }
4787
+ function createBacanoClient(config) {
4788
+ if (!config.apiUrl)
4789
+ throw new WebsiteError("apiUrl is required", "INVALID_CONFIG");
4790
+ if (!config.websiteSlug?.trim())
4791
+ throw new WebsiteError("websiteSlug is required", "INVALID_CONFIG");
4792
+ const apiUrl = normalizeApiUrl(config.apiUrl);
4793
+ const graphqlUrl = `${apiUrl}/api/v1/website/graphql`;
4794
+ const storageUrl = `${apiUrl}/storage/v1`;
4795
+ let context = null;
4796
+ function getContext() {
4797
+ if (!context) throw new NotInitializedError();
4798
+ return context;
4799
+ }
4800
+ const getHeaders = createRequestHeadersProvider(
4801
+ config.websiteSlug,
4802
+ config.getAccessToken
4803
+ );
4804
+ const getWebsiteHeaders = createRequestHeadersProvider(config.websiteSlug);
4805
+ const catalogModule = createCatalogModule(
4806
+ graphqlUrl,
4807
+ getHeaders,
4808
+ getContext,
4809
+ storageUrl
4810
+ );
4811
+ const availabilityModule = createAvailabilityModule(
4812
+ graphqlUrl,
4813
+ getHeaders,
4814
+ getContext
4815
+ );
4816
+ const cartSessionStorage = createCartSessionStorage(
4817
+ config.tokenStorage ?? "localStorage",
4818
+ () => getContext().slug
4819
+ );
4820
+ const cartModule = createCartModule(
4821
+ graphqlUrl,
4822
+ getHeaders,
4823
+ getContext,
4824
+ cartSessionStorage
4825
+ );
4826
+ const checkoutModule = createCheckoutModule(
4827
+ graphqlUrl,
4828
+ getHeaders,
4829
+ getContext,
4830
+ cartSessionStorage
4831
+ );
4832
+ const ordersModule = createOrdersModule(graphqlUrl, getHeaders, getContext);
4833
+ const profileModule = createProfileModule(graphqlUrl, getHeaders);
4834
+ const contentModule = createContentModule(
4835
+ graphqlUrl,
4836
+ getHeaders,
4837
+ getContext,
4838
+ storageUrl
4839
+ );
4840
+ return {
4841
+ async init() {
4842
+ context = await resolveWebsite(
4843
+ graphqlUrl,
4844
+ config.websiteSlug,
4845
+ getWebsiteHeaders
4846
+ );
4847
+ },
4848
+ getContext,
4849
+ auth: {
4850
+ async getAccessToken() {
4851
+ return config.getAccessToken ? await config.getAccessToken() : null;
4852
+ },
4853
+ async isAuthenticated() {
4854
+ return Boolean(
4855
+ config.getAccessToken ? await config.getAccessToken() : null
4856
+ );
4857
+ }
4858
+ },
4859
+ catalog: {
4860
+ getCatalogPage: catalogModule.getCatalogPage.bind(catalogModule),
4861
+ getProductSeoEntries: catalogModule.getProductSeoEntries.bind(catalogModule),
4862
+ getProductRecommendations: catalogModule.getProductRecommendations.bind(catalogModule),
4863
+ getProducts: catalogModule.getProducts.bind(catalogModule),
4864
+ getProduct: catalogModule.getProduct.bind(catalogModule),
4865
+ getProductBySlug: catalogModule.getProductBySlug.bind(catalogModule),
4866
+ getCategories: catalogModule.getCategories.bind(catalogModule),
4867
+ getCategoryList: catalogModule.getCategoryList.bind(catalogModule),
4868
+ getAttributeList: catalogModule.getAttributeList.bind(catalogModule),
4869
+ getBrands: catalogModule.getBrands.bind(catalogModule),
4870
+ getProductFilters: catalogModule.getProductFilters.bind(catalogModule),
4871
+ getSearchSuggestions: catalogModule.getSearchSuggestions.bind(catalogModule),
4872
+ checkAvailability: availabilityModule.checkAvailability.bind(availabilityModule)
4873
+ },
4874
+ content: {
4875
+ getByKey: contentModule.getByKey.bind(contentModule),
4876
+ getByCategory: contentModule.getByCategory.bind(contentModule),
4877
+ getByTag: contentModule.getByTag.bind(contentModule),
4878
+ getForProduct: contentModule.getForProduct.bind(contentModule)
4879
+ },
4880
+ cart: {
4881
+ getOrCreate: cartModule.getOrCreate.bind(cartModule),
4882
+ startCheckout: cartModule.startCheckout.bind(cartModule),
4883
+ get: cartModule.get.bind(cartModule),
4884
+ setItem: cartModule.setItem.bind(cartModule),
4885
+ addItem: cartModule.addItem.bind(cartModule),
4886
+ updateItem: cartModule.updateItem.bind(cartModule),
4887
+ removeItem: cartModule.removeItem.bind(cartModule)
4888
+ },
4889
+ checkout: {
4890
+ getLocationOptions: checkoutModule.getLocationOptions.bind(checkoutModule),
4891
+ getDeliveryOptions: checkoutModule.getDeliveryOptions.bind(checkoutModule),
4892
+ getShippingQuotes: checkoutModule.getShippingQuotes.bind(checkoutModule),
4893
+ getShippingPolicy: checkoutModule.getShippingPolicy.bind(checkoutModule),
4894
+ getFreeShippingProgress: checkoutModule.getFreeShippingProgress.bind(checkoutModule),
4895
+ getPaymentMethods: checkoutModule.getPaymentMethods.bind(checkoutModule),
4896
+ getPaymentOptions: checkoutModule.getPaymentOptions.bind(checkoutModule),
4897
+ validateCoupon: checkoutModule.validateCoupon.bind(checkoutModule),
4898
+ applyCoupon: checkoutModule.applyCoupon.bind(checkoutModule),
4899
+ removeCoupon: checkoutModule.removeCoupon.bind(checkoutModule),
4900
+ submit: checkoutModule.submit.bind(checkoutModule),
4901
+ createPaymentSession: checkoutModule.createPaymentSession.bind(checkoutModule),
4902
+ reconcilePaymentSession: checkoutModule.reconcilePaymentSession.bind(checkoutModule)
4903
+ },
4904
+ orders: {
4905
+ list: ordersModule.list.bind(ordersModule),
4906
+ get: ordersModule.get.bind(ordersModule),
4907
+ getPublicOrder: ordersModule.getPublicOrder.bind(ordersModule),
4908
+ waitForPayment: ordersModule.waitForPayment.bind(ordersModule)
4909
+ },
4910
+ profile: {
4911
+ get: profileModule.get.bind(profileModule),
4912
+ getAddresses: profileModule.getAddresses.bind(profileModule),
4913
+ update: profileModule.update.bind(profileModule)
4914
+ }
4915
+ };
4916
+ }
4917
+ export {
4918
+ AuthError,
4919
+ CheckoutError,
4920
+ GraphQLRequestError,
4921
+ NotInitializedError,
4922
+ WebsiteError,
4923
+ buildStorageFileUrl,
4924
+ buildStorageMediaUrls,
4925
+ createBacanoClient,
4926
+ getPrimaryProductImage,
4927
+ getProductImages,
4928
+ getProductMediaByKind,
4929
+ getProductMediaKind,
4930
+ getProductVideos,
4931
+ getWebsitePublicConfig,
4932
+ resolveProductPricing
4933
+ };