@decocms/apps 0.23.2 → 0.24.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.
Files changed (110) hide show
  1. package/LICENSE +21 -0
  2. package/commerce/components/Image.tsx +129 -143
  3. package/commerce/components/JsonLd.tsx +192 -201
  4. package/commerce/components/Picture.tsx +65 -75
  5. package/commerce/sdk/analytics.ts +15 -15
  6. package/commerce/sdk/formatPrice.ts +13 -16
  7. package/commerce/sdk/url.ts +7 -7
  8. package/commerce/sdk/useOffer.ts +46 -57
  9. package/commerce/sdk/useVariantPossibilities.ts +25 -25
  10. package/commerce/types/commerce.ts +868 -875
  11. package/commerce/utils/canonical.ts +5 -8
  12. package/commerce/utils/constants.ts +5 -6
  13. package/commerce/utils/filters.ts +4 -4
  14. package/commerce/utils/productToAnalyticsItem.ts +52 -56
  15. package/commerce/utils/stateByZip.ts +42 -42
  16. package/package.json +23 -4
  17. package/shopify/actions/cart/addItems.ts +24 -25
  18. package/shopify/actions/cart/updateCoupons.ts +19 -20
  19. package/shopify/actions/cart/updateItems.ts +19 -20
  20. package/shopify/actions/user/signIn.ts +25 -30
  21. package/shopify/actions/user/signUp.ts +19 -24
  22. package/shopify/client.ts +24 -24
  23. package/shopify/index.ts +20 -18
  24. package/shopify/init.ts +18 -21
  25. package/shopify/loaders/ProductDetailsPage.ts +16 -20
  26. package/shopify/loaders/ProductList.ts +66 -69
  27. package/shopify/loaders/ProductListingPage.ts +150 -158
  28. package/shopify/loaders/RelatedProducts.ts +24 -27
  29. package/shopify/loaders/cart.ts +53 -52
  30. package/shopify/loaders/shop.ts +22 -27
  31. package/shopify/loaders/user.ts +27 -32
  32. package/shopify/utils/admin/admin.ts +33 -34
  33. package/shopify/utils/admin/queries.ts +2 -2
  34. package/shopify/utils/cart.ts +18 -14
  35. package/shopify/utils/cookies.ts +62 -65
  36. package/shopify/utils/enums.ts +424 -424
  37. package/shopify/utils/graphql.ts +44 -55
  38. package/shopify/utils/storefront/queries.ts +24 -29
  39. package/shopify/utils/storefront/storefront.graphql.gen.ts +55 -55
  40. package/shopify/utils/transform.ts +370 -376
  41. package/shopify/utils/types.ts +118 -118
  42. package/shopify/utils/user.ts +11 -11
  43. package/shopify/utils/utils.ts +135 -140
  44. package/vtex/actions/address.ts +86 -86
  45. package/vtex/actions/auth.ts +14 -27
  46. package/vtex/actions/checkout.ts +36 -49
  47. package/vtex/actions/masterData.ts +10 -27
  48. package/vtex/actions/misc.ts +101 -111
  49. package/vtex/actions/newsletter.ts +48 -52
  50. package/vtex/actions/orders.ts +13 -16
  51. package/vtex/actions/profile.ts +55 -55
  52. package/vtex/actions/session.ts +36 -35
  53. package/vtex/actions/trigger.ts +25 -25
  54. package/vtex/actions/wishlist.ts +51 -53
  55. package/vtex/client.ts +14 -42
  56. package/vtex/hooks/index.ts +4 -4
  57. package/vtex/hooks/useAutocomplete.ts +42 -48
  58. package/vtex/hooks/useCart.ts +153 -165
  59. package/vtex/hooks/useUser.ts +40 -40
  60. package/vtex/hooks/useWishlist.ts +70 -70
  61. package/vtex/inline-loaders/productDetailsPage.ts +1 -3
  62. package/vtex/inline-loaders/productList.ts +121 -127
  63. package/vtex/inline-loaders/productListingPage.ts +10 -34
  64. package/vtex/inline-loaders/relatedProducts.ts +1 -3
  65. package/vtex/inline-loaders/suggestions.ts +36 -39
  66. package/vtex/inline-loaders/workflowProducts.ts +45 -49
  67. package/vtex/invoke.ts +159 -194
  68. package/vtex/loaders/address.ts +49 -54
  69. package/vtex/loaders/brands.ts +19 -26
  70. package/vtex/loaders/cart.ts +24 -21
  71. package/vtex/loaders/catalog.ts +51 -53
  72. package/vtex/loaders/collections.ts +25 -27
  73. package/vtex/loaders/legacy.ts +487 -534
  74. package/vtex/loaders/logistics.ts +33 -37
  75. package/vtex/loaders/navbar.ts +5 -8
  76. package/vtex/loaders/orders.ts +28 -39
  77. package/vtex/loaders/pageType.ts +41 -35
  78. package/vtex/loaders/payment.ts +27 -37
  79. package/vtex/loaders/profile.ts +38 -38
  80. package/vtex/loaders/promotion.ts +5 -8
  81. package/vtex/loaders/search.ts +56 -59
  82. package/vtex/loaders/session.ts +22 -30
  83. package/vtex/loaders/user.ts +39 -41
  84. package/vtex/loaders/wishlist.ts +35 -35
  85. package/vtex/loaders/wishlistProducts.ts +3 -15
  86. package/vtex/loaders/workflow.ts +220 -227
  87. package/vtex/middleware.ts +116 -119
  88. package/vtex/types.ts +201 -201
  89. package/vtex/utils/batch.ts +13 -16
  90. package/vtex/utils/cookies.ts +76 -80
  91. package/vtex/utils/enrichment.ts +62 -42
  92. package/vtex/utils/fetchCache.ts +1 -4
  93. package/vtex/utils/index.ts +6 -6
  94. package/vtex/utils/intelligentSearch.ts +48 -57
  95. package/vtex/utils/legacy.ts +108 -124
  96. package/vtex/utils/pickAndOmit.ts +15 -20
  97. package/vtex/utils/proxy.ts +136 -146
  98. package/vtex/utils/resourceRange.ts +3 -3
  99. package/vtex/utils/segment.ts +100 -111
  100. package/vtex/utils/similars.ts +1 -2
  101. package/vtex/utils/sitemap.ts +91 -91
  102. package/vtex/utils/slugCache.ts +2 -6
  103. package/vtex/utils/slugify.ts +9 -9
  104. package/vtex/utils/transform.ts +1012 -1105
  105. package/vtex/utils/types.ts +1381 -1381
  106. package/vtex/utils/vtexId.ts +44 -47
  107. package/.github/workflows/release.yml +0 -34
  108. package/.releaserc.json +0 -28
  109. package/knip.json +0 -19
  110. package/tsconfig.json +0 -11
@@ -1,30 +1,25 @@
1
- export function pick<
2
- T extends object,
3
- K extends keyof T = keyof T,
4
- >(
5
- keys: K[],
6
- obj: T | null | undefined,
1
+ export function pick<T extends object, K extends keyof T = keyof T>(
2
+ keys: K[],
3
+ obj: T | null | undefined,
7
4
  ): Pick<T, K> | null {
8
- if (!keys.length || !obj) {
9
- return null;
10
- }
5
+ if (!keys.length || !obj) {
6
+ return null;
7
+ }
11
8
 
12
- const entries = keys.map((key) => [key, obj[key]]);
9
+ const entries = keys.map((key) => [key, obj[key]]);
13
10
 
14
- return Object.fromEntries(entries);
11
+ return Object.fromEntries(entries);
15
12
  }
16
13
 
17
14
  export function omit<T extends object, K extends keyof T>(
18
- keys: K[],
19
- obj: T | null | undefined,
15
+ keys: K[],
16
+ obj: T | null | undefined,
20
17
  ): Omit<T, K> | null {
21
- if (!keys.length || !obj) {
22
- return null;
23
- }
18
+ if (!keys.length || !obj) {
19
+ return null;
20
+ }
24
21
 
25
- const pickedKeys = (Object.keys(obj) as K[]).filter(
26
- (key) => !keys.includes(key),
27
- );
22
+ const pickedKeys = (Object.keys(obj) as K[]).filter((key) => !keys.includes(key));
28
23
 
29
- return pick(pickedKeys, obj) as unknown as Omit<T, K>;
24
+ return pick(pickedKeys, obj) as unknown as Omit<T, K>;
30
25
  }
@@ -8,100 +8,93 @@
8
8
  * fetch handlers.
9
9
  */
10
10
 
11
- import { getVtexConfig, vtexHost, type VtexConfig } from "../client";
11
+ import { getVtexConfig, type VtexConfig, vtexHost } from "../client";
12
12
  import { proxySetCookie } from "./cookies";
13
13
 
14
14
  export interface VtexProxyOptions {
15
- /**
16
- * VTEX environment suffix.
17
- * @default "vtexcommercestable"
18
- */
19
- environment?: "vtexcommercestable" | "vtexcommercebeta";
20
-
21
- /**
22
- * Additional path prefixes to proxy beyond the defaults.
23
- * Example: ["/custom-api/"]
24
- */
25
- extraPaths?: string[];
26
-
27
- /**
28
- * Paths that should NOT be proxied even if they match a prefix.
29
- */
30
- excludePaths?: string[];
31
-
32
- /**
33
- * Whether to rewrite Set-Cookie domains to the storefront's domain.
34
- * @default true
35
- */
36
- rewriteCookieDomain?: boolean;
37
-
38
- /**
39
- * Custom headers to inject into every proxied request.
40
- */
41
- extraHeaders?: Record<string, string>;
15
+ /**
16
+ * VTEX environment suffix.
17
+ * @default "vtexcommercestable"
18
+ */
19
+ environment?: "vtexcommercestable" | "vtexcommercebeta";
20
+
21
+ /**
22
+ * Additional path prefixes to proxy beyond the defaults.
23
+ * Example: ["/custom-api/"]
24
+ */
25
+ extraPaths?: string[];
26
+
27
+ /**
28
+ * Paths that should NOT be proxied even if they match a prefix.
29
+ */
30
+ excludePaths?: string[];
31
+
32
+ /**
33
+ * Whether to rewrite Set-Cookie domains to the storefront's domain.
34
+ * @default true
35
+ */
36
+ rewriteCookieDomain?: boolean;
37
+
38
+ /**
39
+ * Custom headers to inject into every proxied request.
40
+ */
41
+ extraHeaders?: Record<string, string>;
42
42
  }
43
43
 
44
44
  const DEFAULT_PROXY_PATHS = [
45
- "/checkout",
46
- "/checkout/",
47
- "/account",
48
- "/account/",
49
- "/api/",
50
- "/files/",
51
- "/arquivos/",
52
- "/checkout/changeToAnonymousUser/",
53
- "/_v/",
54
- "/no-cache/",
55
- "/graphql/",
56
- "/login",
57
- "/login/",
58
- "/logout",
59
- "/logout/",
60
- "/assets/",
61
- "/_secure/account",
62
- "/XMLData/",
45
+ "/checkout",
46
+ "/checkout/",
47
+ "/account",
48
+ "/account/",
49
+ "/api/",
50
+ "/files/",
51
+ "/arquivos/",
52
+ "/checkout/changeToAnonymousUser/",
53
+ "/_v/",
54
+ "/no-cache/",
55
+ "/graphql/",
56
+ "/login",
57
+ "/login/",
58
+ "/logout",
59
+ "/logout/",
60
+ "/assets/vtex",
61
+ "/_secure/account",
62
+ "/XMLData/",
63
63
  ] as const;
64
64
 
65
65
  const HOP_BY_HOP_HEADERS = new Set([
66
- "connection",
67
- "keep-alive",
68
- "proxy-authenticate",
69
- "proxy-authorization",
70
- "te",
71
- "trailers",
72
- "transfer-encoding",
73
- "upgrade",
66
+ "connection",
67
+ "keep-alive",
68
+ "proxy-authenticate",
69
+ "proxy-authorization",
70
+ "te",
71
+ "trailers",
72
+ "transfer-encoding",
73
+ "upgrade",
74
74
  ]);
75
75
 
76
76
  /**
77
77
  * Returns all path prefixes that should be proxied to VTEX.
78
78
  */
79
79
  export function getVtexProxyPaths(options?: VtexProxyOptions): string[] {
80
- return [...DEFAULT_PROXY_PATHS, ...(options?.extraPaths ?? [])];
80
+ return [...DEFAULT_PROXY_PATHS, ...(options?.extraPaths ?? [])];
81
81
  }
82
82
 
83
83
  /**
84
84
  * Check if a request path should be proxied to VTEX.
85
85
  */
86
- export function shouldProxyToVtex(
87
- pathname: string,
88
- options?: VtexProxyOptions,
89
- ): boolean {
90
- const paths = getVtexProxyPaths(options);
91
- const excluded = options?.excludePaths ?? [];
92
-
93
- if (excluded.some((ex) => pathname.startsWith(ex))) return false;
94
- return paths.some((prefix) => pathname.startsWith(prefix));
86
+ export function shouldProxyToVtex(pathname: string, options?: VtexProxyOptions): boolean {
87
+ const paths = getVtexProxyPaths(options);
88
+ const excluded = options?.excludePaths ?? [];
89
+
90
+ if (excluded.some((ex) => pathname.startsWith(ex))) return false;
91
+ return paths.some((prefix) => pathname.startsWith(prefix));
95
92
  }
96
93
 
97
- function buildOriginUrl(
98
- request: Request,
99
- config: VtexConfig,
100
- environment: string,
101
- ): URL {
102
- const url = new URL(request.url);
103
- const originHost = vtexHost(environment, config);
104
- return new URL(`https://${originHost}${url.pathname}${url.search}`);
94
+ function buildOriginUrl(request: Request, config: VtexConfig, environment: string): URL {
95
+ const url = new URL(request.url);
96
+ const originHost = vtexHost(environment, config);
97
+ return new URL(`https://${originHost}${url.pathname}${url.search}`);
105
98
  }
106
99
 
107
100
  /**
@@ -113,15 +106,15 @@ function buildOriginUrl(
113
106
  * separately using Headers.getSetCookie() for correct multi-cookie support.
114
107
  */
115
108
  function filterHeaders(headers: Headers): Headers {
116
- const filtered = new Headers();
117
- headers.forEach((value, key) => {
118
- const lower = key.toLowerCase();
119
- if (lower === "set-cookie") return;
120
- if (!HOP_BY_HOP_HEADERS.has(lower)) {
121
- filtered.set(key, value);
122
- }
123
- });
124
- return filtered;
109
+ const filtered = new Headers();
110
+ headers.forEach((value, key) => {
111
+ const lower = key.toLowerCase();
112
+ if (lower === "set-cookie") return;
113
+ if (!HOP_BY_HOP_HEADERS.has(lower)) {
114
+ filtered.set(key, value);
115
+ }
116
+ });
117
+ return filtered;
125
118
  }
126
119
 
127
120
  /**
@@ -138,69 +131,66 @@ function filterHeaders(headers: Headers): Headers {
138
131
  * }
139
132
  * ```
140
133
  */
141
- export async function proxyToVtex(
142
- request: Request,
143
- options?: VtexProxyOptions,
144
- ): Promise<Response> {
145
- const config = getVtexConfig();
146
- const environment = options?.environment ?? "vtexcommercestable";
147
-
148
- const originUrl = buildOriginUrl(request, config, environment);
149
- const forwardHeaders = filterHeaders(new Headers(request.headers));
150
-
151
- forwardHeaders.set("Host", originUrl.hostname);
152
- forwardHeaders.set("X-Forwarded-Host", new URL(request.url).hostname);
153
- forwardHeaders.set("X-Forwarded-Proto", "https");
154
-
155
- if (options?.extraHeaders) {
156
- for (const [k, v] of Object.entries(options.extraHeaders)) {
157
- forwardHeaders.set(k, v);
158
- }
159
- }
160
-
161
- if (typeof config.appKey === "string" && typeof config.appToken === "string") {
162
- forwardHeaders.set("X-VTEX-API-AppKey", config.appKey);
163
- forwardHeaders.set("X-VTEX-API-AppToken", config.appToken);
164
- }
165
-
166
- const init: RequestInit = {
167
- method: request.method,
168
- headers: forwardHeaders,
169
- redirect: "manual",
170
- };
171
-
172
- if (request.method !== "GET" && request.method !== "HEAD") {
173
- init.body = request.body;
174
- // @ts-expect-error -- needed for streaming body in Workers
175
- init.duplex = "half";
176
- }
177
-
178
- const originResponse = await fetch(originUrl.toString(), init);
179
-
180
- const responseHeaders = filterHeaders(new Headers(originResponse.headers));
181
-
182
- proxySetCookie(
183
- originResponse.headers,
184
- responseHeaders,
185
- options?.rewriteCookieDomain !== false
186
- ? new URL(request.url).origin
187
- : undefined,
188
- );
189
-
190
- if (originResponse.status >= 300 && originResponse.status < 400) {
191
- const location = originResponse.headers.get("location");
192
- if (location) {
193
- const originVtexHost = vtexHost(environment, config);
194
- const storefrontOrigin = new URL(request.url).origin;
195
- const vtexOrigin = `https://${originVtexHost}`;
196
- const rewritten = location.replace(vtexOrigin, storefrontOrigin);
197
- responseHeaders.set("location", rewritten);
198
- }
199
- }
200
-
201
- return new Response(originResponse.body, {
202
- status: originResponse.status,
203
- statusText: originResponse.statusText,
204
- headers: responseHeaders,
205
- });
134
+ export async function proxyToVtex(request: Request, options?: VtexProxyOptions): Promise<Response> {
135
+ const config = getVtexConfig();
136
+ const environment = options?.environment ?? "vtexcommercestable";
137
+
138
+ const originUrl = buildOriginUrl(request, config, environment);
139
+ const forwardHeaders = filterHeaders(new Headers(request.headers));
140
+
141
+ const requestUrl = new URL(request.url);
142
+ forwardHeaders.set("origin", request.headers.get("origin") ?? requestUrl.origin);
143
+ forwardHeaders.set("Host", originUrl.hostname);
144
+ forwardHeaders.set("X-Forwarded-Host", requestUrl.host);
145
+ forwardHeaders.set("X-Forwarded-Proto", "https");
146
+
147
+ if (options?.extraHeaders) {
148
+ for (const [k, v] of Object.entries(options.extraHeaders)) {
149
+ forwardHeaders.set(k, v);
150
+ }
151
+ }
152
+
153
+ if (typeof config.appKey === "string" && typeof config.appToken === "string") {
154
+ forwardHeaders.set("X-VTEX-API-AppKey", config.appKey);
155
+ forwardHeaders.set("X-VTEX-API-AppToken", config.appToken);
156
+ }
157
+
158
+ const init: RequestInit = {
159
+ method: request.method,
160
+ headers: forwardHeaders,
161
+ redirect: "manual",
162
+ };
163
+
164
+ if (request.method !== "GET" && request.method !== "HEAD") {
165
+ init.body = request.body;
166
+ // @ts-expect-error -- needed for streaming body in Workers
167
+ init.duplex = "half";
168
+ }
169
+
170
+ const originResponse = await fetch(originUrl.toString(), init);
171
+
172
+ const responseHeaders = filterHeaders(new Headers(originResponse.headers));
173
+
174
+ proxySetCookie(
175
+ originResponse.headers,
176
+ responseHeaders,
177
+ options?.rewriteCookieDomain !== false ? requestUrl.origin : undefined,
178
+ );
179
+
180
+ if (originResponse.status >= 300 && originResponse.status < 400) {
181
+ const location = originResponse.headers.get("location");
182
+ if (location) {
183
+ const originVtexHost = vtexHost(environment, config);
184
+ const storefrontOrigin = requestUrl.origin;
185
+ const vtexOrigin = `https://${originVtexHost}`;
186
+ const rewritten = location.replace(vtexOrigin, storefrontOrigin);
187
+ responseHeaders.set("location", rewritten);
188
+ }
189
+ }
190
+
191
+ return new Response(originResponse.body, {
192
+ status: originResponse.status,
193
+ statusText: originResponse.statusText,
194
+ headers: responseHeaders,
195
+ });
206
196
  }
@@ -3,8 +3,8 @@
3
3
  * Ported from deco-cx/apps vtex/utils/resourceRange.ts
4
4
  */
5
5
  export function resourceRange(skip: number, take: number) {
6
- const from = Math.max(skip, 0);
7
- const to = from + Math.min(100, take);
6
+ const from = Math.max(skip, 0);
7
+ const to = from + Math.min(100, take);
8
8
 
9
- return { from, to };
9
+ return { from, to };
10
10
  }
@@ -1,28 +1,27 @@
1
1
  import type { Segment } from "./types";
2
2
 
3
- const removeNonLatin1Chars = (str: string) =>
4
- str.replace(/[^\x00-\xFF]/g, "");
3
+ const removeNonLatin1Chars = (str: string) => str.replace(/[^\x00-\xFF]/g, "");
5
4
 
6
5
  export const SEGMENT_COOKIE_NAME = "vtex_segment";
7
6
  export const SALES_CHANNEL_COOKIE = "VTEXSC";
8
7
 
9
8
  export interface WrappedSegment {
10
- payload: Partial<Segment>;
11
- token: string;
9
+ payload: Partial<Segment>;
10
+ token: string;
12
11
  }
13
12
 
14
13
  export const DEFAULT_SEGMENT: Partial<Segment> = {
15
- utmi_campaign: null,
16
- utmi_page: null,
17
- utmi_part: null,
18
- utm_campaign: null,
19
- utm_source: null,
20
- utm_medium: null,
21
- channel: "1",
22
- cultureInfo: "pt-BR",
23
- currencyCode: "BRL",
24
- currencySymbol: "R$",
25
- countryCode: "BRA",
14
+ utmi_campaign: null,
15
+ utmi_page: null,
16
+ utmi_part: null,
17
+ utm_campaign: null,
18
+ utm_source: null,
19
+ utm_medium: null,
20
+ channel: "1",
21
+ cultureInfo: "pt-BR",
22
+ currencyCode: "BRL",
23
+ currencySymbol: "R$",
24
+ countryCode: "BRA",
26
25
  };
27
26
 
28
27
  /**
@@ -32,95 +31,87 @@ export const DEFAULT_SEGMENT: Partial<Segment> = {
32
31
  * value will be the same. This improves cache hits.
33
32
  */
34
33
  export const serializeSegment = ({
35
- campaigns,
36
- channel,
37
- priceTables,
38
- regionId,
39
- utm_campaign,
40
- utm_source,
41
- utm_medium,
42
- utmi_campaign,
43
- utmi_page,
44
- utmi_part,
45
- currencyCode,
46
- currencySymbol,
47
- countryCode,
48
- cultureInfo,
49
- channelPrivacy,
34
+ campaigns,
35
+ channel,
36
+ priceTables,
37
+ regionId,
38
+ utm_campaign,
39
+ utm_source,
40
+ utm_medium,
41
+ utmi_campaign,
42
+ utmi_page,
43
+ utmi_part,
44
+ currencyCode,
45
+ currencySymbol,
46
+ countryCode,
47
+ cultureInfo,
48
+ channelPrivacy,
50
49
  }: Partial<Segment>): string => {
51
- const seg = {
52
- campaigns,
53
- channel,
54
- priceTables,
55
- regionId,
56
- utm_campaign: utm_campaign &&
57
- removeNonLatin1Chars(utm_campaign).replace(/[\/\[\]{}()<>.]/g, ""),
58
- utm_source: utm_source &&
59
- removeNonLatin1Chars(utm_source).replace(/[\/\[\]{}()<>.]/g, ""),
60
- utm_medium: utm_medium &&
61
- removeNonLatin1Chars(utm_medium).replace(/[\/\[\]{}()<>.]/g, ""),
62
- utmi_campaign: utmi_campaign && removeNonLatin1Chars(utmi_campaign),
63
- utmi_page: utmi_page && removeNonLatin1Chars(utmi_page),
64
- utmi_part: utmi_part && removeNonLatin1Chars(utmi_part),
65
- currencyCode,
66
- currencySymbol,
67
- countryCode,
68
- cultureInfo,
69
- channelPrivacy,
70
- };
71
- return btoa(JSON.stringify(seg));
50
+ const seg = {
51
+ campaigns,
52
+ channel,
53
+ priceTables,
54
+ regionId,
55
+ utm_campaign: utm_campaign && removeNonLatin1Chars(utm_campaign).replace(/[/[\]{}()<>.]/g, ""),
56
+ utm_source: utm_source && removeNonLatin1Chars(utm_source).replace(/[/[\]{}()<>.]/g, ""),
57
+ utm_medium: utm_medium && removeNonLatin1Chars(utm_medium).replace(/[/[\]{}()<>.]/g, ""),
58
+ utmi_campaign: utmi_campaign && removeNonLatin1Chars(utmi_campaign),
59
+ utmi_page: utmi_page && removeNonLatin1Chars(utmi_page),
60
+ utmi_part: utmi_part && removeNonLatin1Chars(utmi_part),
61
+ currencyCode,
62
+ currencySymbol,
63
+ countryCode,
64
+ cultureInfo,
65
+ channelPrivacy,
66
+ };
67
+ return btoa(JSON.stringify(seg));
72
68
  };
73
69
 
74
70
  export const parseSegment = (cookie: string): Partial<Segment> | null => {
75
- try {
76
- return JSON.parse(atob(cookie));
77
- } catch {
78
- return null;
79
- }
71
+ try {
72
+ return JSON.parse(atob(cookie));
73
+ } catch {
74
+ return null;
75
+ }
80
76
  };
81
77
 
82
78
  const SEGMENT_QUERY_PARAMS = [
83
- "utmi_campaign",
84
- "utmi_page",
85
- "utmi_part",
86
- "utm_campaign",
87
- "utm_source",
88
- "utm_medium",
79
+ "utmi_campaign",
80
+ "utmi_page",
81
+ "utmi_part",
82
+ "utm_campaign",
83
+ "utm_source",
84
+ "utm_medium",
89
85
  ] as const;
90
86
 
91
- export const buildSegmentFromParams = (
92
- searchParams: URLSearchParams,
93
- ): Partial<Segment> => {
94
- const partialSegment: Partial<Segment> = {};
95
- for (const qs of SEGMENT_QUERY_PARAMS) {
96
- const param = searchParams.get(qs);
97
- if (param) {
98
- partialSegment[qs] = param;
99
- }
100
- }
101
-
102
- const sc = searchParams.get("sc");
103
- if (sc) {
104
- partialSegment.channel = sc;
105
- }
106
-
107
- return partialSegment;
87
+ export const buildSegmentFromParams = (searchParams: URLSearchParams): Partial<Segment> => {
88
+ const partialSegment: Partial<Segment> = {};
89
+ for (const qs of SEGMENT_QUERY_PARAMS) {
90
+ const param = searchParams.get(qs);
91
+ if (param) {
92
+ partialSegment[qs] = param;
93
+ }
94
+ }
95
+
96
+ const sc = searchParams.get("sc");
97
+ if (sc) {
98
+ partialSegment.channel = sc;
99
+ }
100
+
101
+ return partialSegment;
108
102
  };
109
103
 
110
- export const withSegmentCookie = (
111
- segment: WrappedSegment,
112
- headers?: Headers,
113
- ): Headers => {
114
- const h = new Headers(headers);
115
- if (!segment) return h;
104
+ export const withSegmentCookie = (segment: WrappedSegment, headers?: Headers): Headers => {
105
+ const h = new Headers(headers);
106
+ if (!segment) return h;
116
107
 
117
- h.set("cookie", `${SEGMENT_COOKIE_NAME}=${segment.token}`);
118
- return h;
108
+ h.set("cookie", `${SEGMENT_COOKIE_NAME}=${segment.token}`);
109
+ return h;
119
110
  };
120
111
 
121
112
  function getCookieValue(cookieHeader: string, name: string): string | null {
122
- const match = cookieHeader.match(new RegExp(`(?:^|;\\s*)${name}=([^;]+)`));
123
- return match?.[1] ?? null;
113
+ const match = cookieHeader.match(new RegExp(`(?:^|;\\s*)${name}=([^;]+)`));
114
+ return match?.[1] ?? null;
124
115
  }
125
116
 
126
117
  /**
@@ -128,20 +119,18 @@ function getCookieValue(cookieHeader: string, name: string): string | null {
128
119
  * Reads both vtex_segment and VTEXSC cookies.
129
120
  * VTEXSC contains the sales channel and overrides the segment channel.
130
121
  */
131
- export const buildSegmentFromCookies = (
132
- cookieHeader: string,
133
- ): Partial<Segment> => {
134
- const segmentCookie = getCookieValue(cookieHeader, SEGMENT_COOKIE_NAME);
135
- const vtexsc = getCookieValue(cookieHeader, SALES_CHANNEL_COOKIE);
122
+ export const buildSegmentFromCookies = (cookieHeader: string): Partial<Segment> => {
123
+ const segmentCookie = getCookieValue(cookieHeader, SEGMENT_COOKIE_NAME);
124
+ const vtexsc = getCookieValue(cookieHeader, SALES_CHANNEL_COOKIE);
136
125
 
137
- const base = segmentCookie ? parseSegment(segmentCookie) : null;
138
- const segment: Partial<Segment> = { ...DEFAULT_SEGMENT, ...base };
126
+ const base = segmentCookie ? parseSegment(segmentCookie) : null;
127
+ const segment: Partial<Segment> = { ...DEFAULT_SEGMENT, ...base };
139
128
 
140
- if (vtexsc) {
141
- segment.channel = vtexsc;
142
- }
129
+ if (vtexsc) {
130
+ segment.channel = vtexsc;
131
+ }
143
132
 
144
- return segment;
133
+ return segment;
145
134
  };
146
135
 
147
136
  /**
@@ -149,15 +138,15 @@ export const buildSegmentFromCookies = (
149
138
  * (no campaigns, no UTMs, no regionId, no custom priceTables).
150
139
  */
151
140
  export const isAnonymous = (segment: Partial<Segment>): boolean => {
152
- return (
153
- !segment.campaigns &&
154
- !segment.utm_campaign &&
155
- !segment.utm_source &&
156
- !segment.utm_medium &&
157
- !segment.utmi_campaign &&
158
- !segment.utmi_page &&
159
- !segment.utmi_part &&
160
- !segment.regionId &&
161
- !segment.priceTables
162
- );
141
+ return (
142
+ !segment.campaigns &&
143
+ !segment.utm_campaign &&
144
+ !segment.utm_source &&
145
+ !segment.utm_medium &&
146
+ !segment.utmi_campaign &&
147
+ !segment.utmi_page &&
148
+ !segment.utmi_part &&
149
+ !segment.regionId &&
150
+ !segment.priceTables
151
+ );
163
152
  };
@@ -4,8 +4,7 @@ import { pickSku, toProduct } from "./transform";
4
4
  import type { LegacyProduct } from "./types";
5
5
 
6
6
  export const withIsSimilarTo = async (product: Product): Promise<Product> => {
7
- const id =
8
- product.isVariantOf?.productGroupID ?? product.inProductGroupWithID;
7
+ const id = product.isVariantOf?.productGroupID ?? product.inProductGroupWithID;
9
8
 
10
9
  if (!id) {
11
10
  return product;