@decocms/apps 0.23.3 → 0.25.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 (111) 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 +24 -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/productListShelf.ts +159 -0
  64. package/vtex/inline-loaders/productListingPage.ts +10 -34
  65. package/vtex/inline-loaders/relatedProducts.ts +1 -3
  66. package/vtex/inline-loaders/suggestions.ts +36 -39
  67. package/vtex/inline-loaders/workflowProducts.ts +45 -49
  68. package/vtex/invoke.ts +159 -194
  69. package/vtex/loaders/address.ts +49 -54
  70. package/vtex/loaders/brands.ts +19 -26
  71. package/vtex/loaders/cart.ts +24 -21
  72. package/vtex/loaders/catalog.ts +51 -53
  73. package/vtex/loaders/collections.ts +25 -27
  74. package/vtex/loaders/legacy.ts +487 -534
  75. package/vtex/loaders/logistics.ts +33 -37
  76. package/vtex/loaders/navbar.ts +5 -8
  77. package/vtex/loaders/orders.ts +28 -39
  78. package/vtex/loaders/pageType.ts +41 -35
  79. package/vtex/loaders/payment.ts +27 -37
  80. package/vtex/loaders/profile.ts +38 -38
  81. package/vtex/loaders/promotion.ts +5 -8
  82. package/vtex/loaders/search.ts +56 -59
  83. package/vtex/loaders/session.ts +22 -30
  84. package/vtex/loaders/user.ts +39 -41
  85. package/vtex/loaders/wishlist.ts +35 -35
  86. package/vtex/loaders/wishlistProducts.ts +3 -15
  87. package/vtex/loaders/workflow.ts +220 -227
  88. package/vtex/middleware.ts +116 -119
  89. package/vtex/types.ts +201 -201
  90. package/vtex/utils/batch.ts +13 -16
  91. package/vtex/utils/cookies.ts +76 -80
  92. package/vtex/utils/enrichment.ts +62 -42
  93. package/vtex/utils/fetchCache.ts +1 -4
  94. package/vtex/utils/index.ts +6 -6
  95. package/vtex/utils/intelligentSearch.ts +48 -57
  96. package/vtex/utils/legacy.ts +108 -124
  97. package/vtex/utils/pickAndOmit.ts +15 -20
  98. package/vtex/utils/proxy.ts +136 -146
  99. package/vtex/utils/resourceRange.ts +3 -3
  100. package/vtex/utils/segment.ts +100 -111
  101. package/vtex/utils/similars.ts +1 -2
  102. package/vtex/utils/sitemap.ts +91 -91
  103. package/vtex/utils/slugCache.ts +2 -6
  104. package/vtex/utils/slugify.ts +9 -9
  105. package/vtex/utils/transform.ts +1178 -1105
  106. package/vtex/utils/types.ts +1381 -1381
  107. package/vtex/utils/vtexId.ts +44 -47
  108. package/.github/workflows/release.yml +0 -34
  109. package/.releaserc.json +0 -28
  110. package/knip.json +0 -19
  111. package/tsconfig.json +0 -11
@@ -9,7 +9,7 @@
9
9
  *
10
10
  * @see https://developers.vtex.com/docs/api-reference/intelligent-search-api
11
11
  */
12
- import { intelligentSearch, getVtexConfig } from "../client";
12
+ import { getVtexConfig, intelligentSearch } from "../client";
13
13
  import type { Suggestion } from "../utils/types";
14
14
 
15
15
  // ---------------------------------------------------------------------------
@@ -22,12 +22,12 @@ import type { Suggestion } from "../utils/types";
22
22
  * @param locale - BCP-47 locale (defaults to the configured locale or "pt-BR")
23
23
  */
24
24
  export async function getTopSearches(locale?: string): Promise<Suggestion> {
25
- const cfg = getVtexConfig();
26
- const effectiveLocale = locale ?? cfg.locale ?? "pt-BR";
25
+ const cfg = getVtexConfig();
26
+ const effectiveLocale = locale ?? cfg.locale ?? "pt-BR";
27
27
 
28
- return intelligentSearch<Suggestion>("/top_searches", {
29
- locale: effectiveLocale,
30
- });
28
+ return intelligentSearch<Suggestion>("/top_searches", {
29
+ locale: effectiveLocale,
30
+ });
31
31
  }
32
32
 
33
33
  // ---------------------------------------------------------------------------
@@ -35,12 +35,12 @@ export async function getTopSearches(locale?: string): Promise<Suggestion> {
35
35
  // ---------------------------------------------------------------------------
36
36
 
37
37
  export interface FacetsSearchProps {
38
- query?: string;
39
- facets?: string;
40
- sort?: string;
41
- count?: number;
42
- page?: number;
43
- locale?: string;
38
+ query?: string;
39
+ facets?: string;
40
+ sort?: string;
41
+ count?: number;
42
+ page?: number;
43
+ locale?: string;
44
44
  }
45
45
 
46
46
  /**
@@ -53,17 +53,17 @@ export interface FacetsSearchProps {
53
53
  * Returns the raw IS response or `null` when nothing is found.
54
54
  */
55
55
  export async function validateProductSearch<T = unknown>(
56
- props: FacetsSearchProps,
57
- fetcher: (props: FacetsSearchProps) => Promise<T[] | null>,
56
+ props: FacetsSearchProps,
57
+ fetcher: (props: FacetsSearchProps) => Promise<T[] | null>,
58
58
  ): Promise<T[] | null> {
59
- const results = await fetcher(props);
60
- if (results !== null && results.length > 0) return results;
59
+ const results = await fetcher(props);
60
+ if (results !== null && results.length > 0) return results;
61
61
 
62
- if (props.facets) {
63
- return fetcher({ ...props, facets: "" });
64
- }
62
+ if (props.facets) {
63
+ return fetcher({ ...props, facets: "" });
64
+ }
65
65
 
66
- return null;
66
+ return null;
67
67
  }
68
68
 
69
69
  // ---------------------------------------------------------------------------
@@ -71,29 +71,29 @@ export async function validateProductSearch<T = unknown>(
71
71
  // ---------------------------------------------------------------------------
72
72
 
73
73
  interface ProductIdOption {
74
- value: string;
75
- label: string;
76
- image?: string;
74
+ value: string;
75
+ label: string;
76
+ image?: string;
77
77
  }
78
78
 
79
79
  interface ISSuggestionProduct {
80
- productId: string;
81
- productName: string;
82
- brand: string;
83
- linkText: string;
84
- items: Array<{
85
- itemId: string;
86
- name: string;
87
- images: Array<{ imageUrl: string; imageText: string }>;
88
- sellers: Array<{
89
- commertialOffer: { Price: number; ListPrice: number };
90
- }>;
91
- }>;
80
+ productId: string;
81
+ productName: string;
82
+ brand: string;
83
+ linkText: string;
84
+ items: Array<{
85
+ itemId: string;
86
+ name: string;
87
+ images: Array<{ imageUrl: string; imageText: string }>;
88
+ sellers: Array<{
89
+ commertialOffer: { Price: number; ListPrice: number };
90
+ }>;
91
+ }>;
92
92
  }
93
93
 
94
94
  interface ISSuggestionResponse {
95
- searches: Array<{ term: string; count: number }>;
96
- products: ISSuggestionProduct[];
95
+ searches: Array<{ term: string; count: number }>;
96
+ products: ISSuggestionProduct[];
97
97
  }
98
98
 
99
99
  /**
@@ -102,26 +102,23 @@ interface ISSuggestionResponse {
102
102
  *
103
103
  * Hits the IS autocomplete_suggestions endpoint.
104
104
  */
105
- export async function getProductIdByTerm(
106
- term?: string,
107
- ): Promise<ProductIdOption[]> {
108
- const query = (term ?? "").trim();
109
- if (!query) return [];
110
-
111
- const data = await intelligentSearch<ISSuggestionResponse>(
112
- "/autocomplete_suggestions/",
113
- { query },
114
- );
115
-
116
- if (!data.products?.length) {
117
- return [{ value: "No products found", label: "No products found" }];
118
- }
119
-
120
- return data.products.flatMap((product) =>
121
- (product.items ?? []).map((item) => ({
122
- value: item.itemId,
123
- label: `${item.itemId} - ${product.productName} ${item.name} - ${product.productId}`,
124
- image: item.images?.[0]?.imageUrl,
125
- })),
126
- );
105
+ export async function getProductIdByTerm(term?: string): Promise<ProductIdOption[]> {
106
+ const query = (term ?? "").trim();
107
+ if (!query) return [];
108
+
109
+ const data = await intelligentSearch<ISSuggestionResponse>("/autocomplete_suggestions/", {
110
+ query,
111
+ });
112
+
113
+ if (!data.products?.length) {
114
+ return [{ value: "No products found", label: "No products found" }];
115
+ }
116
+
117
+ return data.products.flatMap((product) =>
118
+ (product.items ?? []).map((item) => ({
119
+ value: item.itemId,
120
+ label: `${item.itemId} - ${product.productName} ${item.name} - ${product.productId}`,
121
+ image: item.images?.[0]?.imageUrl,
122
+ })),
123
+ );
127
124
  }
@@ -23,14 +23,11 @@ import { vtexFetch, vtexIOGraphQL } from "../client";
23
23
  *
24
24
  * @see https://developers.vtex.com/docs/api-reference/session-manager-api#get-/api/sessions
25
25
  */
26
- export async function getSession<T = any>(
27
- items: string[] = ["*"],
28
- authCookie: string,
29
- ): Promise<T> {
30
- const qs = new URLSearchParams({ items: items.join(",") });
31
- return vtexFetch<T>(`/api/sessions?${qs}`, {
32
- headers: { cookie: authCookie },
33
- });
26
+ export async function getSession<T = any>(items: string[] = ["*"], authCookie: string): Promise<T> {
27
+ const qs = new URLSearchParams({ items: items.join(",") });
28
+ return vtexFetch<T>(`/api/sessions?${qs}`, {
29
+ headers: { cookie: authCookie },
30
+ });
34
31
  }
35
32
 
36
33
  // ---------------------------------------------------------------------------
@@ -38,21 +35,21 @@ export async function getSession<T = any>(
38
35
  // ---------------------------------------------------------------------------
39
36
 
40
37
  export interface LoginSession {
41
- id: string;
42
- cacheId: string;
43
- deviceType: string;
44
- city: string;
45
- lastAccess: string;
46
- browser: string;
47
- os: string;
48
- ip: string;
49
- fullAddress: string;
50
- firstAccess: string;
38
+ id: string;
39
+ cacheId: string;
40
+ deviceType: string;
41
+ city: string;
42
+ lastAccess: string;
43
+ browser: string;
44
+ os: string;
45
+ ip: string;
46
+ fullAddress: string;
47
+ firstAccess: string;
51
48
  }
52
49
 
53
50
  export interface LoginSessionsInfo {
54
- currentLoginSessionId: string;
55
- loginSessions: LoginSession[];
51
+ currentLoginSessionId: string;
52
+ loginSessions: LoginSession[];
56
53
  }
57
54
 
58
55
  const USER_SESSIONS_QUERY = `query getUserSessions {
@@ -77,15 +74,10 @@ const USER_SESSIONS_QUERY = `query getUserSessions {
77
74
  * Fetch all active login sessions for the currently authenticated user.
78
75
  * Requires a valid VTEX auth cookie.
79
76
  */
80
- export async function getUserSessions(
81
- authCookie: string,
82
- ): Promise<LoginSessionsInfo> {
83
- const { loginSessionsInfo } = await vtexIOGraphQL<{
84
- loginSessionsInfo: LoginSessionsInfo;
85
- }>(
86
- { query: USER_SESSIONS_QUERY },
87
- { cookie: authCookie },
88
- );
77
+ export async function getUserSessions(authCookie: string): Promise<LoginSessionsInfo> {
78
+ const { loginSessionsInfo } = await vtexIOGraphQL<{
79
+ loginSessionsInfo: LoginSessionsInfo;
80
+ }>({ query: USER_SESSIONS_QUERY }, { cookie: authCookie });
89
81
 
90
- return loginSessionsInfo;
82
+ return loginSessionsInfo;
91
83
  }
@@ -14,26 +14,26 @@ import { vtexIOGraphQL } from "../client";
14
14
  // ---------------------------------------------------------------------------
15
15
 
16
16
  export interface Person {
17
- "@id": string;
18
- email: string;
19
- givenName?: string;
20
- familyName?: string;
21
- taxID?: string;
22
- gender?: string;
23
- telephone?: string;
17
+ "@id": string;
18
+ email: string;
19
+ givenName?: string;
20
+ familyName?: string;
21
+ taxID?: string;
22
+ gender?: string;
23
+ telephone?: string;
24
24
  }
25
25
 
26
26
  interface VtexUser {
27
- id: string;
28
- userId: string;
29
- email: string;
30
- firstName?: string;
31
- lastName?: string;
32
- profilePicture?: string;
33
- gender?: string;
34
- document?: string;
35
- homePhone?: string;
36
- businessPhone?: string;
27
+ id: string;
28
+ userId: string;
29
+ email: string;
30
+ firstName?: string;
31
+ lastName?: string;
32
+ profilePicture?: string;
33
+ gender?: string;
34
+ document?: string;
35
+ homePhone?: string;
36
+ businessPhone?: string;
37
37
  }
38
38
 
39
39
  // ---------------------------------------------------------------------------
@@ -61,29 +61,27 @@ const USER_QUERY = `query getUserProfile {
61
61
  *
62
62
  * @param authCookie - Raw `cookie` header value from the user request.
63
63
  */
64
- export async function getUser(
65
- authCookie: string,
66
- ): Promise<Person | null> {
67
- try {
68
- const { profile: user } = await vtexIOGraphQL<{ profile: VtexUser }>(
69
- { query: USER_QUERY },
70
- { cookie: authCookie },
71
- );
64
+ export async function getUser(authCookie: string): Promise<Person | null> {
65
+ try {
66
+ const { profile: user } = await vtexIOGraphQL<{ profile: VtexUser }>(
67
+ { query: USER_QUERY },
68
+ { cookie: authCookie },
69
+ );
72
70
 
73
- return {
74
- "@id": user.userId ?? user.id,
75
- email: user.email,
76
- givenName: user.firstName,
77
- familyName: user.lastName,
78
- taxID: user.document?.replace(/[^\d]/g, ""),
79
- gender: user.gender
80
- ? user.gender === "f"
81
- ? "https://schema.org/Female"
82
- : "https://schema.org/Male"
83
- : undefined,
84
- telephone: user.homePhone ?? user.businessPhone,
85
- };
86
- } catch {
87
- return null;
88
- }
71
+ return {
72
+ "@id": user.userId ?? user.id,
73
+ email: user.email,
74
+ givenName: user.firstName,
75
+ familyName: user.lastName,
76
+ taxID: user.document?.replace(/[^\d]/g, ""),
77
+ gender: user.gender
78
+ ? user.gender === "f"
79
+ ? "https://schema.org/Female"
80
+ : "https://schema.org/Male"
81
+ : undefined,
82
+ telephone: user.homePhone ?? user.businessPhone,
83
+ };
84
+ } catch {
85
+ return null;
86
+ }
89
87
  }
@@ -14,17 +14,17 @@ import { vtexIOGraphQL } from "../client";
14
14
  // ---------------------------------------------------------------------------
15
15
 
16
16
  export interface WishlistItem {
17
- id: string;
18
- productId: string;
19
- sku: string;
20
- title: string;
17
+ id: string;
18
+ productId: string;
19
+ sku: string;
20
+ title: string;
21
21
  }
22
22
 
23
23
  export interface GetWishlistOpts {
24
- shopperId: string;
25
- count?: number;
26
- page?: number;
27
- allRecords?: boolean;
24
+ shopperId: string;
25
+ count?: number;
26
+ page?: number;
27
+ allRecords?: boolean;
28
28
  }
29
29
 
30
30
  // ---------------------------------------------------------------------------
@@ -55,35 +55,35 @@ const WISHLIST_QUERY = `query GetWishlist($shopperId: String!, $name: String!, $
55
55
  * @param opts.allRecords - When true, ignores pagination and returns every item.
56
56
  */
57
57
  export async function getWishlist(
58
- authCookie: string,
59
- opts: GetWishlistOpts,
58
+ authCookie: string,
59
+ opts: GetWishlistOpts,
60
60
  ): Promise<WishlistItem[]> {
61
- try {
62
- const { viewList } = await vtexIOGraphQL<{
63
- viewList: { name?: string; data: WishlistItem[] };
64
- }>(
65
- {
66
- operationName: "GetWishlist",
67
- query: WISHLIST_QUERY,
68
- variables: {
69
- name: "Wishlist",
70
- shopperId: opts.shopperId,
71
- },
72
- },
73
- { cookie: authCookie },
74
- );
61
+ try {
62
+ const { viewList } = await vtexIOGraphQL<{
63
+ viewList: { name?: string; data: WishlistItem[] };
64
+ }>(
65
+ {
66
+ operationName: "GetWishlist",
67
+ query: WISHLIST_QUERY,
68
+ variables: {
69
+ name: "Wishlist",
70
+ shopperId: opts.shopperId,
71
+ },
72
+ },
73
+ { cookie: authCookie },
74
+ );
75
75
 
76
- const data = viewList.data ?? [];
76
+ const data = viewList.data ?? [];
77
77
 
78
- if (opts.allRecords) return data;
78
+ if (opts.allRecords) return data;
79
79
 
80
- const count = opts.count ?? Infinity;
81
- const page = opts.page ?? 0;
82
- return data.slice(count * page, count * (page + 1));
83
- } catch (error) {
84
- if (error instanceof DOMException && error.name === "AbortError") {
85
- throw error;
86
- }
87
- return [];
88
- }
80
+ const count = opts.count ?? Infinity;
81
+ const page = opts.page ?? 0;
82
+ return data.slice(count * page, count * (page + 1));
83
+ } catch (error) {
84
+ if (error instanceof DOMException && error.name === "AbortError") {
85
+ throw error;
86
+ }
87
+ return [];
88
+ }
89
89
  }
@@ -4,10 +4,7 @@
4
4
  *
5
5
  * Ported from deco-cx/apps vtex/loaders/product/wishlist.ts
6
6
  */
7
- import type {
8
- Product,
9
- ProductListingPage,
10
- } from "../../commerce/types/commerce";
7
+ import type { Product, ProductListingPage } from "../../commerce/types/commerce";
11
8
  import { getWishlist } from "./wishlist";
12
9
 
13
10
  export interface WishlistProductsProps {
@@ -32,19 +29,10 @@ function withPage(baseUrl: string, page: number): string {
32
29
  export async function wishlistProducts(
33
30
  props: WishlistProductsProps,
34
31
  ): Promise<ProductListingPage | null> {
35
- const {
36
- count: recordPerPage = 12,
37
- offset = 0,
38
- authCookie,
39
- shopperId,
40
- url: rawUrl,
41
- } = props;
32
+ const { count: recordPerPage = 12, offset = 0, authCookie, shopperId, url: rawUrl } = props;
42
33
 
43
34
  const url = new URL(rawUrl);
44
- const page = Math.max(
45
- 0,
46
- Number(url.searchParams.get("page") ?? offset) - offset,
47
- );
35
+ const page = Math.max(0, Number(url.searchParams.get("page") ?? offset) - offset);
48
36
  const items = await getWishlist(authCookie, { shopperId, allRecords: true });
49
37
  const records = items.length;
50
38
  const start = page * recordPerPage;