@decocms/apps 0.23.3 → 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
package/vtex/middleware.ts
CHANGED
|
@@ -24,57 +24,62 @@
|
|
|
24
24
|
* ```
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
+
import { ANONYMOUS_COOKIE, SESSION_COOKIE } from "./utils/intelligentSearch";
|
|
27
28
|
import {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
type WrappedSegment,
|
|
29
|
+
buildSegmentFromParams,
|
|
30
|
+
DEFAULT_SEGMENT,
|
|
31
|
+
parseSegment,
|
|
32
|
+
SALES_CHANNEL_COOKIE,
|
|
33
|
+
SEGMENT_COOKIE_NAME,
|
|
34
|
+
serializeSegment,
|
|
35
35
|
} from "./utils/segment";
|
|
36
|
-
import { SESSION_COOKIE, ANONYMOUS_COOKIE } from "./utils/intelligentSearch";
|
|
37
|
-
import { isVtexLoggedIn, extractVtexAuthCookie, parseVtexAuthToken } from "./utils/vtexId";
|
|
38
36
|
import type { Segment } from "./utils/types";
|
|
37
|
+
import { extractVtexAuthCookie, parseVtexAuthToken } from "./utils/vtexId";
|
|
39
38
|
|
|
40
39
|
// -------------------------------------------------------------------------
|
|
41
40
|
// Types
|
|
42
41
|
// -------------------------------------------------------------------------
|
|
43
42
|
|
|
44
43
|
export interface VtexRequestContext {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
44
|
+
/** Decoded segment from cookie or URL params. */
|
|
45
|
+
segment: Partial<Segment>;
|
|
46
|
+
/** Serialized segment token for cache key use. */
|
|
47
|
+
segmentToken: string;
|
|
48
|
+
/** Whether the user has a valid (non-expired) VTEX auth cookie. */
|
|
49
|
+
isLoggedIn: boolean;
|
|
50
|
+
/** Extracted email from the auth JWT, if available. */
|
|
51
|
+
email?: string;
|
|
52
|
+
/** Sales channel derived from segment. */
|
|
53
|
+
salesChannel: string;
|
|
54
|
+
/**
|
|
55
|
+
* VTEX region ID from the segment cookie.
|
|
56
|
+
* Present when the user has set a postal code (CEP) for regionalization.
|
|
57
|
+
* Null when no region is set (anonymous default segment).
|
|
58
|
+
*/
|
|
59
|
+
regionId: string | null;
|
|
60
|
+
/** Whether this request carries price tables (B2B). */
|
|
61
|
+
hasCustomPricing: boolean;
|
|
62
|
+
/** Intelligent Search session cookie. */
|
|
63
|
+
isSessionId: string;
|
|
64
|
+
/** Intelligent Search anonymous cookie. */
|
|
65
|
+
isAnonymousId: string;
|
|
67
66
|
}
|
|
68
67
|
|
|
69
68
|
// -------------------------------------------------------------------------
|
|
70
69
|
// Cookie helpers
|
|
71
70
|
// -------------------------------------------------------------------------
|
|
72
71
|
|
|
73
|
-
const
|
|
72
|
+
const _IS_COOKIE_PREFIX = "vtex_is_";
|
|
73
|
+
|
|
74
|
+
/** Seconds in one day (86 400). Used for cookie Max-Age and stale-if-error. */
|
|
75
|
+
const ONE_DAY_SECONDS = 86_400;
|
|
76
|
+
|
|
77
|
+
/** Seconds in one year (~365 days). Used for long-lived IS cookie Max-Age. */
|
|
78
|
+
const ONE_YEAR_SECONDS = 365 * 24 * 60 * 60;
|
|
74
79
|
|
|
75
80
|
function getCookieValue(cookieHeader: string, name: string): string | null {
|
|
76
|
-
|
|
77
|
-
|
|
81
|
+
const match = cookieHeader.match(new RegExp(`(?:^|;\\s*)${name}=([^;]+)`));
|
|
82
|
+
return match?.[1] ?? null;
|
|
78
83
|
}
|
|
79
84
|
|
|
80
85
|
// -------------------------------------------------------------------------
|
|
@@ -88,54 +93,52 @@ function getCookieValue(cookieHeader: string, name: string): string | null {
|
|
|
88
93
|
* to build a complete picture of the user's VTEX session state.
|
|
89
94
|
*/
|
|
90
95
|
function generateUUID(): string {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
97
|
+
return crypto.randomUUID();
|
|
98
|
+
}
|
|
99
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
100
|
+
const r = (Math.random() * 16) | 0;
|
|
101
|
+
return (c === "x" ? r : (r & 0x3) | 0x8).toString(16);
|
|
102
|
+
});
|
|
98
103
|
}
|
|
99
104
|
|
|
100
105
|
export function extractVtexContext(request: Request): VtexRequestContext {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
isAnonymousId,
|
|
138
|
-
};
|
|
106
|
+
const cookies = request.headers.get("cookie") ?? "";
|
|
107
|
+
const url = new URL(request.url);
|
|
108
|
+
|
|
109
|
+
const segmentCookie = getCookieValue(cookies, SEGMENT_COOKIE_NAME);
|
|
110
|
+
const cookieSegment = segmentCookie ? parseSegment(segmentCookie) : null;
|
|
111
|
+
|
|
112
|
+
const paramSegment = buildSegmentFromParams(url.searchParams);
|
|
113
|
+
|
|
114
|
+
const vtexsc = getCookieValue(cookies, SALES_CHANNEL_COOKIE);
|
|
115
|
+
|
|
116
|
+
const segment: Partial<Segment> = {
|
|
117
|
+
...DEFAULT_SEGMENT,
|
|
118
|
+
...cookieSegment,
|
|
119
|
+
...paramSegment,
|
|
120
|
+
};
|
|
121
|
+
if (vtexsc) segment.channel = vtexsc;
|
|
122
|
+
|
|
123
|
+
const segmentToken = serializeSegment(segment);
|
|
124
|
+
|
|
125
|
+
const authToken = extractVtexAuthCookie(cookies);
|
|
126
|
+
const authInfo = authToken ? parseVtexAuthToken(authToken) : null;
|
|
127
|
+
|
|
128
|
+
const isSessionId = getCookieValue(cookies, SESSION_COOKIE) ?? generateUUID();
|
|
129
|
+
const isAnonymousId = getCookieValue(cookies, ANONYMOUS_COOKIE) ?? generateUUID();
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
segment,
|
|
133
|
+
segmentToken,
|
|
134
|
+
isLoggedIn: authInfo?.isLoggedIn ?? false,
|
|
135
|
+
email: authInfo?.email,
|
|
136
|
+
salesChannel: segment.channel ?? "1",
|
|
137
|
+
regionId: segment.regionId ?? null,
|
|
138
|
+
hasCustomPricing: Boolean(segment.priceTables && segment.priceTables.length > 0),
|
|
139
|
+
isSessionId,
|
|
140
|
+
isAnonymousId,
|
|
141
|
+
};
|
|
139
142
|
}
|
|
140
143
|
|
|
141
144
|
// -------------------------------------------------------------------------
|
|
@@ -151,22 +154,22 @@ export function extractVtexContext(request: Request): VtexRequestContext {
|
|
|
151
154
|
* - Anonymous default segment: public with CDN caching
|
|
152
155
|
*/
|
|
153
156
|
export function vtexCacheControl(
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
157
|
+
ctx: VtexRequestContext,
|
|
158
|
+
options?: {
|
|
159
|
+
/** Max age for public (anonymous) responses in seconds. @default 60 */
|
|
160
|
+
publicMaxAge?: number;
|
|
161
|
+
/** Stale-while-revalidate for public responses in seconds. @default 3600 */
|
|
162
|
+
publicSWR?: number;
|
|
163
|
+
},
|
|
161
164
|
): string {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
+
if (ctx.isLoggedIn || ctx.hasCustomPricing) {
|
|
166
|
+
return "private, no-cache, no-store, must-revalidate";
|
|
167
|
+
}
|
|
165
168
|
|
|
166
|
-
|
|
167
|
-
|
|
169
|
+
const maxAge = options?.publicMaxAge ?? 60;
|
|
170
|
+
const swr = options?.publicSWR ?? 3600;
|
|
168
171
|
|
|
169
|
-
|
|
172
|
+
return `public, s-maxage=${maxAge}, stale-while-revalidate=${swr}, stale-if-error=${ONE_DAY_SECONDS}`;
|
|
170
173
|
}
|
|
171
174
|
|
|
172
175
|
// -------------------------------------------------------------------------
|
|
@@ -180,19 +183,16 @@ export function vtexCacheControl(
|
|
|
180
183
|
* If not, new UUIDs from the context are set. This ensures
|
|
181
184
|
* every user has IS cookies for personalization and analytics.
|
|
182
185
|
*/
|
|
183
|
-
export function propagateISCookies(
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
"Set-Cookie",
|
|
194
|
-
`${ANONYMOUS_COOKIE}=${ctx.isAnonymousId}; Path=/; SameSite=Lax; Max-Age=${maxAge}`,
|
|
195
|
-
);
|
|
186
|
+
export function propagateISCookies(ctx: VtexRequestContext, response: Response): void {
|
|
187
|
+
const maxAge = ONE_YEAR_SECONDS;
|
|
188
|
+
response.headers.append(
|
|
189
|
+
"Set-Cookie",
|
|
190
|
+
`${SESSION_COOKIE}=${ctx.isSessionId}; Path=/; SameSite=Lax; Max-Age=${maxAge}`,
|
|
191
|
+
);
|
|
192
|
+
response.headers.append(
|
|
193
|
+
"Set-Cookie",
|
|
194
|
+
`${ANONYMOUS_COOKIE}=${ctx.isAnonymousId}; Path=/; SameSite=Lax; Max-Age=${maxAge}`,
|
|
195
|
+
);
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
/**
|
|
@@ -201,14 +201,11 @@ export function propagateISCookies(
|
|
|
201
201
|
* Use this when URL params change the segment (e.g., ?sc=2) so the
|
|
202
202
|
* browser persists the new segment for subsequent requests.
|
|
203
203
|
*/
|
|
204
|
-
export function buildSegmentSetCookie(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
let cookie = `${SEGMENT_COOKIE_NAME}=${token}; Path=/; SameSite=Lax; Max-Age=86400`;
|
|
210
|
-
if (domain) cookie += `; Domain=${domain}`;
|
|
211
|
-
return cookie;
|
|
204
|
+
export function buildSegmentSetCookie(segment: Partial<Segment>, domain?: string): string {
|
|
205
|
+
const token = serializeSegment(segment);
|
|
206
|
+
let cookie = `${SEGMENT_COOKIE_NAME}=${token}; Path=/; SameSite=Lax; Max-Age=${ONE_DAY_SECONDS}`;
|
|
207
|
+
if (domain) cookie += `; Domain=${domain}`;
|
|
208
|
+
return cookie;
|
|
212
209
|
}
|
|
213
210
|
|
|
214
211
|
// -------------------------------------------------------------------------
|
|
@@ -223,16 +220,16 @@ export function buildSegmentSetCookie(
|
|
|
223
220
|
* get the same cache key; a logged-in user gets a unique (uncached) key.
|
|
224
221
|
*/
|
|
225
222
|
export function vtexCacheKeySuffix(ctx: VtexRequestContext): string {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
223
|
+
if (ctx.isLoggedIn) return "__vtex_auth";
|
|
224
|
+
const parts = [`sc=${ctx.salesChannel}`];
|
|
225
|
+
if (ctx.regionId) parts.push(`r=${ctx.regionId}`);
|
|
226
|
+
return `__vtex_${parts.join("_")}`;
|
|
230
227
|
}
|
|
231
228
|
|
|
232
229
|
// -------------------------------------------------------------------------
|
|
233
230
|
// Re-exports for convenience
|
|
234
231
|
// -------------------------------------------------------------------------
|
|
235
232
|
|
|
236
|
-
export { isVtexLoggedIn } from "./utils/vtexId";
|
|
237
|
-
export type { VtexAuthInfo } from "./utils/vtexId";
|
|
238
233
|
export type { Segment } from "./utils/types";
|
|
234
|
+
export type { VtexAuthInfo } from "./utils/vtexId";
|
|
235
|
+
export { isVtexLoggedIn } from "./utils/vtexId";
|