@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.
- package/LICENSE +21 -0
- package/commerce/components/Image.tsx +129 -143
- package/commerce/components/JsonLd.tsx +192 -201
- package/commerce/components/Picture.tsx +65 -75
- package/commerce/sdk/analytics.ts +15 -15
- package/commerce/sdk/formatPrice.ts +13 -16
- package/commerce/sdk/url.ts +7 -7
- package/commerce/sdk/useOffer.ts +46 -57
- package/commerce/sdk/useVariantPossibilities.ts +25 -25
- package/commerce/types/commerce.ts +868 -875
- package/commerce/utils/canonical.ts +5 -8
- package/commerce/utils/constants.ts +5 -6
- package/commerce/utils/filters.ts +4 -4
- package/commerce/utils/productToAnalyticsItem.ts +52 -56
- package/commerce/utils/stateByZip.ts +42 -42
- package/package.json +23 -4
- package/shopify/actions/cart/addItems.ts +24 -25
- package/shopify/actions/cart/updateCoupons.ts +19 -20
- package/shopify/actions/cart/updateItems.ts +19 -20
- package/shopify/actions/user/signIn.ts +25 -30
- package/shopify/actions/user/signUp.ts +19 -24
- package/shopify/client.ts +24 -24
- package/shopify/index.ts +20 -18
- package/shopify/init.ts +18 -21
- package/shopify/loaders/ProductDetailsPage.ts +16 -20
- package/shopify/loaders/ProductList.ts +66 -69
- package/shopify/loaders/ProductListingPage.ts +150 -158
- package/shopify/loaders/RelatedProducts.ts +24 -27
- package/shopify/loaders/cart.ts +53 -52
- package/shopify/loaders/shop.ts +22 -27
- package/shopify/loaders/user.ts +27 -32
- package/shopify/utils/admin/admin.ts +33 -34
- package/shopify/utils/admin/queries.ts +2 -2
- package/shopify/utils/cart.ts +18 -14
- package/shopify/utils/cookies.ts +62 -65
- package/shopify/utils/enums.ts +424 -424
- package/shopify/utils/graphql.ts +44 -55
- package/shopify/utils/storefront/queries.ts +24 -29
- package/shopify/utils/storefront/storefront.graphql.gen.ts +55 -55
- package/shopify/utils/transform.ts +370 -376
- package/shopify/utils/types.ts +118 -118
- package/shopify/utils/user.ts +11 -11
- package/shopify/utils/utils.ts +135 -140
- package/vtex/actions/address.ts +86 -86
- package/vtex/actions/auth.ts +14 -27
- package/vtex/actions/checkout.ts +36 -49
- package/vtex/actions/masterData.ts +10 -27
- package/vtex/actions/misc.ts +101 -111
- package/vtex/actions/newsletter.ts +48 -52
- package/vtex/actions/orders.ts +13 -16
- package/vtex/actions/profile.ts +55 -55
- package/vtex/actions/session.ts +36 -35
- package/vtex/actions/trigger.ts +25 -25
- package/vtex/actions/wishlist.ts +51 -53
- package/vtex/client.ts +14 -42
- package/vtex/hooks/index.ts +4 -4
- package/vtex/hooks/useAutocomplete.ts +42 -48
- package/vtex/hooks/useCart.ts +153 -165
- package/vtex/hooks/useUser.ts +40 -40
- package/vtex/hooks/useWishlist.ts +70 -70
- package/vtex/inline-loaders/productDetailsPage.ts +1 -3
- package/vtex/inline-loaders/productList.ts +121 -127
- package/vtex/inline-loaders/productListingPage.ts +10 -34
- package/vtex/inline-loaders/relatedProducts.ts +1 -3
- package/vtex/inline-loaders/suggestions.ts +36 -39
- package/vtex/inline-loaders/workflowProducts.ts +45 -49
- package/vtex/invoke.ts +159 -194
- package/vtex/loaders/address.ts +49 -54
- package/vtex/loaders/brands.ts +19 -26
- package/vtex/loaders/cart.ts +24 -21
- package/vtex/loaders/catalog.ts +51 -53
- package/vtex/loaders/collections.ts +25 -27
- package/vtex/loaders/legacy.ts +487 -534
- package/vtex/loaders/logistics.ts +33 -37
- package/vtex/loaders/navbar.ts +5 -8
- package/vtex/loaders/orders.ts +28 -39
- package/vtex/loaders/pageType.ts +41 -35
- package/vtex/loaders/payment.ts +27 -37
- package/vtex/loaders/profile.ts +38 -38
- package/vtex/loaders/promotion.ts +5 -8
- package/vtex/loaders/search.ts +56 -59
- package/vtex/loaders/session.ts +22 -30
- package/vtex/loaders/user.ts +39 -41
- package/vtex/loaders/wishlist.ts +35 -35
- package/vtex/loaders/wishlistProducts.ts +3 -15
- package/vtex/loaders/workflow.ts +220 -227
- package/vtex/middleware.ts +116 -119
- package/vtex/types.ts +201 -201
- package/vtex/utils/batch.ts +13 -16
- package/vtex/utils/cookies.ts +76 -80
- package/vtex/utils/enrichment.ts +62 -42
- package/vtex/utils/fetchCache.ts +1 -4
- package/vtex/utils/index.ts +6 -6
- package/vtex/utils/intelligentSearch.ts +48 -57
- package/vtex/utils/legacy.ts +108 -124
- package/vtex/utils/pickAndOmit.ts +15 -20
- package/vtex/utils/proxy.ts +136 -146
- package/vtex/utils/resourceRange.ts +3 -3
- package/vtex/utils/segment.ts +100 -111
- package/vtex/utils/similars.ts +1 -2
- package/vtex/utils/sitemap.ts +91 -91
- package/vtex/utils/slugCache.ts +2 -6
- package/vtex/utils/slugify.ts +9 -9
- package/vtex/utils/transform.ts +1012 -1105
- package/vtex/utils/types.ts +1381 -1381
- package/vtex/utils/vtexId.ts +44 -47
- package/.github/workflows/release.yml +0 -34
- package/.releaserc.json +0 -28
- package/knip.json +0 -19
- package/tsconfig.json +0 -11
|
@@ -1,30 +1,25 @@
|
|
|
1
|
-
export function pick<
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
if (!keys.length || !obj) {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
11
8
|
|
|
12
|
-
|
|
9
|
+
const entries = keys.map((key) => [key, obj[key]]);
|
|
13
10
|
|
|
14
|
-
|
|
11
|
+
return Object.fromEntries(entries);
|
|
15
12
|
}
|
|
16
13
|
|
|
17
14
|
export function omit<T extends object, K extends keyof T>(
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
keys: K[],
|
|
16
|
+
obj: T | null | undefined,
|
|
20
17
|
): Omit<T, K> | null {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
if (!keys.length || !obj) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
24
21
|
|
|
25
|
-
|
|
26
|
-
(key) => !keys.includes(key),
|
|
27
|
-
);
|
|
22
|
+
const pickedKeys = (Object.keys(obj) as K[]).filter((key) => !keys.includes(key));
|
|
28
23
|
|
|
29
|
-
|
|
24
|
+
return pick(pickedKeys, obj) as unknown as Omit<T, K>;
|
|
30
25
|
}
|
package/vtex/utils/proxy.ts
CHANGED
|
@@ -8,100 +8,93 @@
|
|
|
8
8
|
* fetch handlers.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { getVtexConfig,
|
|
11
|
+
import { getVtexConfig, type VtexConfig, vtexHost } from "../client";
|
|
12
12
|
import { proxySetCookie } from "./cookies";
|
|
13
13
|
|
|
14
14
|
export interface VtexProxyOptions {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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
|
-
|
|
7
|
-
|
|
6
|
+
const from = Math.max(skip, 0);
|
|
7
|
+
const to = from + Math.min(100, take);
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
return { from, to };
|
|
10
10
|
}
|
package/vtex/utils/segment.ts
CHANGED
|
@@ -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
|
-
|
|
11
|
-
|
|
9
|
+
payload: Partial<Segment>;
|
|
10
|
+
token: string;
|
|
12
11
|
}
|
|
13
12
|
|
|
14
13
|
export const DEFAULT_SEGMENT: Partial<Segment> = {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
112
|
-
|
|
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
|
-
|
|
118
|
-
|
|
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
|
-
|
|
123
|
-
|
|
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
|
-
|
|
133
|
-
|
|
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
|
-
|
|
138
|
-
|
|
126
|
+
const base = segmentCookie ? parseSegment(segmentCookie) : null;
|
|
127
|
+
const segment: Partial<Segment> = { ...DEFAULT_SEGMENT, ...base };
|
|
139
128
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
129
|
+
if (vtexsc) {
|
|
130
|
+
segment.channel = vtexsc;
|
|
131
|
+
}
|
|
143
132
|
|
|
144
|
-
|
|
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
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
};
|
package/vtex/utils/similars.ts
CHANGED
|
@@ -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;
|