@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
@@ -2,8 +2,9 @@
2
2
  * VTEX Sessions API actions.
3
3
  * All session-mutating actions return Set-Cookie headers for propagation.
4
4
  */
5
- import { vtexFetchWithCookies, vtexIOGraphQL, getVtexConfig } from "../client";
5
+
6
6
  import type { VtexFetchResult } from "../client";
7
+ import { getVtexConfig, vtexFetchWithCookies, vtexIOGraphQL } from "../client";
7
8
  import { buildAuthCookieHeader } from "../utils/vtexId";
8
9
 
9
10
  // ---------------------------------------------------------------------------
@@ -11,21 +12,21 @@ import { buildAuthCookieHeader } from "../utils/vtexId";
11
12
  // ---------------------------------------------------------------------------
12
13
 
13
14
  export interface SessionData {
14
- id: string;
15
- namespaces: Record<string, Record<string, { value: string }>>;
15
+ id: string;
16
+ namespaces: Record<string, Record<string, { value: string }>>;
16
17
  }
17
18
 
18
19
  export async function createSession(
19
- data: Record<string, any>,
20
- cookieHeader?: string,
20
+ data: Record<string, any>,
21
+ cookieHeader?: string,
21
22
  ): Promise<VtexFetchResult<SessionData>> {
22
- const headers: Record<string, string> = {};
23
- if (cookieHeader) headers["cookie"] = cookieHeader;
24
- return vtexFetchWithCookies<SessionData>("/api/sessions", {
25
- method: "POST",
26
- body: JSON.stringify(data),
27
- headers,
28
- });
23
+ const headers: Record<string, string> = {};
24
+ if (cookieHeader) headers.cookie = cookieHeader;
25
+ return vtexFetchWithCookies<SessionData>("/api/sessions", {
26
+ method: "POST",
27
+ body: JSON.stringify(data),
28
+ headers,
29
+ });
29
30
  }
30
31
 
31
32
  // ---------------------------------------------------------------------------
@@ -33,8 +34,8 @@ export async function createSession(
33
34
  // ---------------------------------------------------------------------------
34
35
 
35
36
  export interface EditSessionResponse {
36
- id: string;
37
- namespaces: Record<string, Record<string, { value: string }>>;
37
+ id: string;
38
+ namespaces: Record<string, Record<string, { value: string }>>;
38
39
  }
39
40
 
40
41
  /**
@@ -42,17 +43,17 @@ export interface EditSessionResponse {
42
43
  * Returns data + Set-Cookie headers.
43
44
  */
44
45
  export async function editSession(
45
- publicProperties: Record<string, { value: string }>,
46
- cookieHeader?: string,
46
+ publicProperties: Record<string, { value: string }>,
47
+ cookieHeader?: string,
47
48
  ): Promise<VtexFetchResult<EditSessionResponse>> {
48
- const headers: Record<string, string> = {};
49
- if (cookieHeader) headers["cookie"] = cookieHeader;
49
+ const headers: Record<string, string> = {};
50
+ if (cookieHeader) headers.cookie = cookieHeader;
50
51
 
51
- return vtexFetchWithCookies<EditSessionResponse>("/api/sessions", {
52
- method: "PATCH",
53
- body: JSON.stringify({ public: { ...publicProperties } }),
54
- headers,
55
- });
52
+ return vtexFetchWithCookies<EditSessionResponse>("/api/sessions", {
53
+ method: "PATCH",
54
+ body: JSON.stringify({ public: { ...publicProperties } }),
55
+ headers,
56
+ });
56
57
  }
57
58
 
58
59
  // ---------------------------------------------------------------------------
@@ -60,7 +61,7 @@ export async function editSession(
60
61
  // ---------------------------------------------------------------------------
61
62
 
62
63
  export interface DeleteSessionResponse {
63
- logOutFromSession: string;
64
+ logOutFromSession: string;
64
65
  }
65
66
 
66
67
  const DELETE_SESSION_MUTATION = `mutation LogOutFromSession($sessionId: ID) {
@@ -72,16 +73,16 @@ const DELETE_SESSION_MUTATION = `mutation LogOutFromSession($sessionId: ID) {
72
73
  * Requires a valid auth cookie.
73
74
  */
74
75
  export async function deleteSession(
75
- sessionId: string,
76
- authCookie: string,
76
+ sessionId: string,
77
+ authCookie: string,
77
78
  ): Promise<DeleteSessionResponse> {
78
- if (!authCookie) throw new Error("Auth cookie is required to delete session");
79
- const { account } = getVtexConfig();
80
- return vtexIOGraphQL<DeleteSessionResponse>(
81
- {
82
- query: DELETE_SESSION_MUTATION,
83
- variables: { sessionId },
84
- },
85
- { cookie: buildAuthCookieHeader(authCookie, account) },
86
- );
79
+ if (!authCookie) throw new Error("Auth cookie is required to delete session");
80
+ const { account } = getVtexConfig();
81
+ return vtexIOGraphQL<DeleteSessionResponse>(
82
+ {
83
+ query: DELETE_SESSION_MUTATION,
84
+ variables: { sessionId },
85
+ },
86
+ { cookie: buildAuthCookieHeader(authCookie, account) },
87
+ );
87
88
  }
@@ -9,29 +9,29 @@
9
9
 
10
10
  /** Payload sent by VTEX when a SKU changes in the catalog. */
11
11
  export interface VTEXNotificationPayload {
12
- /** SKU ID in VTEX. */
13
- IdSku: string;
14
- /** Seller's account name in VTEX (visible in the store's Admin URL). */
15
- An: string;
16
- /** Affiliate ID generated automatically in the configuration. */
17
- IdAffiliate: string;
18
- /** Product ID in VTEX. */
19
- ProductId: number;
20
- /** Date when the item was updated. */
21
- DateModified: string;
22
- /**
23
- * Whether the product is active. `false` means the product was
24
- * deactivated and should be blocked / zeroed in the marketplace.
25
- */
26
- IsActive: boolean;
27
- /** Inventory level has changed -- run a Fulfillment Simulation to refresh. */
28
- StockModified: boolean;
29
- /** Price has changed -- run a Fulfillment Simulation to refresh. */
30
- PriceModified: boolean;
31
- /** Product/SKU registration data changed (name, description, weight, etc.). */
32
- HasStockKeepingUnitModified: boolean;
33
- /** Product is no longer associated with the trade policy. */
34
- HasStockKeepingUnitRemovedFromAffiliate: boolean;
12
+ /** SKU ID in VTEX. */
13
+ IdSku: string;
14
+ /** Seller's account name in VTEX (visible in the store's Admin URL). */
15
+ An: string;
16
+ /** Affiliate ID generated automatically in the configuration. */
17
+ IdAffiliate: string;
18
+ /** Product ID in VTEX. */
19
+ ProductId: number;
20
+ /** Date when the item was updated. */
21
+ DateModified: string;
22
+ /**
23
+ * Whether the product is active. `false` means the product was
24
+ * deactivated and should be blocked / zeroed in the marketplace.
25
+ */
26
+ IsActive: boolean;
27
+ /** Inventory level has changed -- run a Fulfillment Simulation to refresh. */
28
+ StockModified: boolean;
29
+ /** Price has changed -- run a Fulfillment Simulation to refresh. */
30
+ PriceModified: boolean;
31
+ /** Product/SKU registration data changed (name, description, weight, etc.). */
32
+ HasStockKeepingUnitModified: boolean;
33
+ /** Product is no longer associated with the trade policy. */
34
+ HasStockKeepingUnitRemovedFromAffiliate: boolean;
35
35
  }
36
36
 
37
37
  /**
@@ -39,5 +39,5 @@ export interface VTEXNotificationPayload {
39
39
  * Implement this in your framework layer (e.g. TanStack Start API route).
40
40
  */
41
41
  export type VTEXTriggerHandler = (
42
- payload: VTEXNotificationPayload,
43
- ) => Promise<{ id: string } | void>;
42
+ payload: VTEXNotificationPayload,
43
+ ) => Promise<{ id: string } | undefined>;
@@ -5,31 +5,29 @@
5
5
  * - vtex/actions/wishlist/removeItem.ts
6
6
  * @see https://developers.vtex.com/docs/apps/vtex.wish-list
7
7
  */
8
- import { vtexFetch, getVtexConfig, vtexIOGraphQL } from "../client";
8
+ import { getVtexConfig, vtexIOGraphQL } from "../client";
9
9
  import { buildAuthCookieHeader } from "../utils/vtexId";
10
10
 
11
+ /** Maximum wishlist items to fetch in a single query. */
12
+ const WISHLIST_MAX_ITEMS = 500;
13
+
11
14
  // ---------------------------------------------------------------------------
12
15
  // Types
13
16
  // ---------------------------------------------------------------------------
14
17
 
15
18
  export interface WishlistItem {
16
- id?: string;
17
- productId: string;
18
- sku: string;
19
- title?: string;
19
+ id?: string;
20
+ productId: string;
21
+ sku: string;
22
+ title?: string;
20
23
  }
21
24
 
22
25
  // ---------------------------------------------------------------------------
23
26
  // GraphQL helper (myvtex.com private graphql)
24
27
  // ---------------------------------------------------------------------------
25
28
 
26
- interface GqlResponse<T> {
27
- data: T;
28
- errors?: Array<{ message: string }>;
29
- }
30
-
31
29
  function buildCookieHeader(authCookie: string): string {
32
- return buildAuthCookieHeader(authCookie, getVtexConfig().account);
30
+ return buildAuthCookieHeader(authCookie, getVtexConfig().account);
33
31
  }
34
32
 
35
33
  // ---------------------------------------------------------------------------
@@ -55,16 +53,16 @@ const VIEW_WISHLIST = `query ViewList($shopperId: String!, $name: String!, $from
55
53
  // ---------------------------------------------------------------------------
56
54
 
57
55
  async function fetchWishlist(shopperId: string, authCookie: string): Promise<WishlistItem[]> {
58
- const data = await vtexIOGraphQL<{
59
- viewList: { data: WishlistItem[] | null };
60
- }>(
61
- {
62
- query: VIEW_WISHLIST,
63
- variables: { shopperId, name: "Wishlist", from: 0, to: 500 },
64
- },
65
- { Cookie: buildCookieHeader(authCookie) },
66
- );
67
- return data.viewList?.data ?? [];
56
+ const data = await vtexIOGraphQL<{
57
+ viewList: { data: WishlistItem[] | null };
58
+ }>(
59
+ {
60
+ query: VIEW_WISHLIST,
61
+ variables: { shopperId, name: "Wishlist", from: 0, to: WISHLIST_MAX_ITEMS },
62
+ },
63
+ { Cookie: buildCookieHeader(authCookie) },
64
+ );
65
+ return data.viewList?.data ?? [];
68
66
  }
69
67
 
70
68
  /**
@@ -72,23 +70,23 @@ async function fetchWishlist(shopperId: string, authCookie: string): Promise<Wis
72
70
  * Returns the updated full wishlist.
73
71
  */
74
72
  export async function addItem(
75
- item: { productId: string; sku: string; title?: string },
76
- shopperId: string,
77
- authCookie: string,
73
+ item: { productId: string; sku: string; title?: string },
74
+ shopperId: string,
75
+ authCookie: string,
78
76
  ): Promise<WishlistItem[]> {
79
- if (!authCookie) throw new Error("User must be logged in to add to wishlist");
80
- await vtexIOGraphQL<unknown>(
81
- {
82
- query: ADD_TO_WISHLIST,
83
- variables: {
84
- name: "Wishlist",
85
- shopperId,
86
- listItem: item,
87
- },
88
- },
89
- { Cookie: buildCookieHeader(authCookie) },
90
- );
91
- return fetchWishlist(shopperId, authCookie);
77
+ if (!authCookie) throw new Error("User must be logged in to add to wishlist");
78
+ await vtexIOGraphQL<unknown>(
79
+ {
80
+ query: ADD_TO_WISHLIST,
81
+ variables: {
82
+ name: "Wishlist",
83
+ shopperId,
84
+ listItem: item,
85
+ },
86
+ },
87
+ { Cookie: buildCookieHeader(authCookie) },
88
+ );
89
+ return fetchWishlist(shopperId, authCookie);
92
90
  }
93
91
 
94
92
  /**
@@ -96,21 +94,21 @@ export async function addItem(
96
94
  * Returns the updated full wishlist.
97
95
  */
98
96
  export async function removeItem(
99
- id: string,
100
- shopperId: string,
101
- authCookie: string,
97
+ id: string,
98
+ shopperId: string,
99
+ authCookie: string,
102
100
  ): Promise<WishlistItem[]> {
103
- if (!authCookie) throw new Error("User must be logged in to remove from wishlist");
104
- await vtexIOGraphQL<unknown>(
105
- {
106
- query: REMOVE_FROM_WISHLIST,
107
- variables: {
108
- id,
109
- name: "Wishlist",
110
- shopperId,
111
- },
112
- },
113
- { Cookie: buildCookieHeader(authCookie) },
114
- );
115
- return fetchWishlist(shopperId, authCookie);
101
+ if (!authCookie) throw new Error("User must be logged in to remove from wishlist");
102
+ await vtexIOGraphQL<unknown>(
103
+ {
104
+ query: REMOVE_FROM_WISHLIST,
105
+ variables: {
106
+ id,
107
+ name: "Wishlist",
108
+ shopperId,
109
+ },
110
+ },
111
+ { Cookie: buildCookieHeader(authCookie) },
112
+ );
113
+ return fetchWishlist(shopperId, authCookie);
116
114
  }
package/vtex/client.ts CHANGED
@@ -4,14 +4,13 @@
4
4
  */
5
5
 
6
6
  import { type FetchCacheOptions, fetchWithCache } from "./utils/fetchCache";
7
- import { SEGMENT_COOKIE_NAME, parseSegment } from "./utils/segment";
7
+ import { parseSegment, SEGMENT_COOKIE_NAME } from "./utils/segment";
8
8
 
9
9
  // ---------------------------------------------------------------------------
10
10
  // URL sanitization (ported from deco-cx/apps vtex/utils/fetchVTEX.ts)
11
11
  // ---------------------------------------------------------------------------
12
12
 
13
- const removeNonLatin1Chars = (str: string): string =>
14
- str.replace(/[^\x00-\x7F]|["']/g, "");
13
+ const removeNonLatin1Chars = (str: string): string => str.replace(/[^\x00-\x7F]|["']/g, "");
15
14
 
16
15
  const removeScriptChars = (str: string): string => {
17
16
  return str
@@ -138,8 +137,7 @@ export function setResponseCookieForwarder(fn: (cookies: string[]) => void) {
138
137
  }
139
138
 
140
139
  export function getVtexConfig(): VtexConfig {
141
- if (!_config)
142
- throw new Error("VTEX not configured. Call configureVtex() first.");
140
+ if (!_config) throw new Error("VTEX not configured. Call configureVtex() first.");
143
141
  return _config;
144
142
  }
145
143
 
@@ -147,10 +145,7 @@ export function getVtexConfig(): VtexConfig {
147
145
  * Build the VTEX hostname for a given environment.
148
146
  * Centralizes `{account}.{env}.{domain}` so nothing is hardcoded.
149
147
  */
150
- export function vtexHost(
151
- environment: string = "vtexcommercestable",
152
- config?: VtexConfig,
153
- ): string {
148
+ export function vtexHost(environment: string = "vtexcommercestable", config?: VtexConfig): string {
154
149
  const c = config ?? getVtexConfig();
155
150
  const domain = c.domain ?? "com.br";
156
151
  return `${c.account}.${environment}.${domain}`;
@@ -185,18 +180,13 @@ function extractRegionIdFromCookies(): string | null {
185
180
  if (!_getCookieHeader) return null;
186
181
  const cookies = _getCookieHeader();
187
182
  if (!cookies) return null;
188
- const match = cookies.match(
189
- new RegExp(`(?:^|;\\s*)${SEGMENT_COOKIE_NAME}=([^;]+)`),
190
- );
183
+ const match = cookies.match(new RegExp(`(?:^|;\\s*)${SEGMENT_COOKIE_NAME}=([^;]+)`));
191
184
  if (!match?.[1]) return null;
192
185
  const segment = parseSegment(match[1]);
193
186
  return segment?.regionId ?? null;
194
187
  }
195
188
 
196
- export async function vtexFetchResponse(
197
- path: string,
198
- init?: RequestInit,
199
- ): Promise<Response> {
189
+ export async function vtexFetchResponse(path: string, init?: RequestInit): Promise<Response> {
200
190
  const raw = path.startsWith("http") ? path : `${baseUrl()}${path}`;
201
191
  const url = sanitizeUrl(raw);
202
192
  const response = await _fetch(url, {
@@ -204,17 +194,12 @@ export async function vtexFetchResponse(
204
194
  headers: { ...authHeaders(), ...init?.headers },
205
195
  });
206
196
  if (!response.ok) {
207
- throw new Error(
208
- `VTEX API error: ${response.status} ${response.statusText} - ${url}`,
209
- );
197
+ throw new Error(`VTEX API error: ${response.status} ${response.statusText} - ${url}`);
210
198
  }
211
199
  return response;
212
200
  }
213
201
 
214
- export async function vtexFetch<T>(
215
- path: string,
216
- init?: RequestInit,
217
- ): Promise<T> {
202
+ export async function vtexFetch<T>(path: string, init?: RequestInit): Promise<T> {
218
203
  const response = await vtexFetchResponse(path, init);
219
204
  return response.json();
220
205
  }
@@ -370,9 +355,7 @@ export async function vtexIOGraphQL<T>(
370
355
  },
371
356
  );
372
357
  if (res.errors?.length) {
373
- throw new Error(
374
- `VTEX IO GraphQL error: ${res.errors.map((e) => e.message).join(", ")}`,
375
- );
358
+ throw new Error(`VTEX IO GraphQL error: ${res.errors.map((e) => e.message).join(", ")}`);
376
359
  }
377
360
  return res.data;
378
361
  }
@@ -408,10 +391,7 @@ const PAGE_TYPE_TO_MAP_PARAM: Record<string, string | null> = {
408
391
  FullText: null,
409
392
  };
410
393
 
411
- function pageTypeToMapParam(
412
- type: PageType["pageType"],
413
- index: number,
414
- ): string | null {
394
+ function pageTypeToMapParam(type: PageType["pageType"], index: number): string | null {
415
395
  if (type === "Category" || type === "Department" || type === "SubCategory") {
416
396
  return `category-${index + 1}`;
417
397
  }
@@ -419,9 +399,7 @@ function pageTypeToMapParam(
419
399
  }
420
400
 
421
401
  function cachedPageType(term: string): Promise<PageType | null> {
422
- return vtexCachedFetch<PageType>(
423
- `/api/catalog_system/pub/portal/pagetype/${term}`,
424
- );
402
+ return vtexCachedFetch<PageType>(`/api/catalog_system/pub/portal/pagetype/${term}`);
425
403
  }
426
404
 
427
405
  /**
@@ -453,9 +431,7 @@ const slugify = (str: string) =>
453
431
  * Convert page types to selectedFacets with correct IS facet keys.
454
432
  * Mirrors deco-cx/apps `filtersFromPathname`.
455
433
  */
456
- export function filtersFromPageTypes(
457
- pageTypes: PageType[],
458
- ): Array<{ key: string; value: string }> {
434
+ export function filtersFromPageTypes(pageTypes: PageType[]): Array<{ key: string; value: string }> {
459
435
  return pageTypes
460
436
  .map((page, index) => {
461
437
  const key = pageTypeToMapParam(page.pageType, index);
@@ -469,12 +445,8 @@ export function filtersFromPageTypes(
469
445
  * Build the IS facet path string from selectedFacets.
470
446
  * Mirrors deco-cx/apps `toPath`.
471
447
  */
472
- export function toFacetPath(
473
- facets: Array<{ key: string; value: string }>,
474
- ): string {
475
- return facets
476
- .map(({ key, value }) => (key ? `${key}/${value}` : value))
477
- .join("/");
448
+ export function toFacetPath(facets: Array<{ key: string; value: string }>): string {
449
+ return facets.map(({ key, value }) => (key ? `${key}/${value}` : value)).join("/");
478
450
  }
479
451
 
480
452
  export function initVtexFromBlocks(blocks: Record<string, any>) {
@@ -1,4 +1,4 @@
1
- export { useCart, type UseCartOptions, type OrderForm, type CartItem } from "./useCart";
2
- export { useUser, type UseUserOptions, type VtexUser } from "./useUser";
3
- export { useWishlist, type UseWishlistOptions, type WishlistItem } from "./useWishlist";
4
- export { useAutocomplete, type UseAutocompleteOptions } from "./useAutocomplete";
1
+ export { type UseAutocompleteOptions, useAutocomplete } from "./useAutocomplete";
2
+ export { type CartItem, type OrderForm, type UseCartOptions, useCart } from "./useCart";
3
+ export { type UseUserOptions, useUser, type VtexUser } from "./useUser";
4
+ export { type UseWishlistOptions, useWishlist, type WishlistItem } from "./useWishlist";
@@ -20,70 +20,64 @@
20
20
  * ```
21
21
  */
22
22
 
23
- import { useState, useCallback, useRef, useEffect } from "react";
24
23
  import { useQuery } from "@tanstack/react-query";
24
+ import { useCallback, useEffect, useState } from "react";
25
25
  import type { Suggestion } from "../../commerce/types/commerce";
26
26
 
27
27
  export interface UseAutocompleteOptions {
28
- /** Debounce delay in ms @default 250 */
29
- debounceMs?: number;
30
- /** Max products to return @default 4 */
31
- count?: number;
32
- /** Custom fetch function — defaults to calling the inline-loader on the server */
33
- fetchSuggestions?: (query: string, count: number) => Promise<Suggestion | null>;
28
+ /** Debounce delay in ms @default 250 */
29
+ debounceMs?: number;
30
+ /** Max products to return @default 4 */
31
+ count?: number;
32
+ /** Custom fetch function — defaults to calling the inline-loader on the server */
33
+ fetchSuggestions?: (query: string, count: number) => Promise<Suggestion | null>;
34
34
  }
35
35
 
36
36
  const AUTOCOMPLETE_QUERY_KEY = "vtex-autocomplete";
37
37
 
38
38
  function useDebounce<T>(value: T, delay: number): T {
39
- const [debounced, setDebounced] = useState(value);
40
- useEffect(() => {
41
- const timer = setTimeout(() => setDebounced(value), delay);
42
- return () => clearTimeout(timer);
43
- }, [value, delay]);
44
- return debounced;
39
+ const [debounced, setDebounced] = useState(value);
40
+ useEffect(() => {
41
+ const timer = setTimeout(() => setDebounced(value), delay);
42
+ return () => clearTimeout(timer);
43
+ }, [value, delay]);
44
+ return debounced;
45
45
  }
46
46
 
47
- async function defaultFetchSuggestions(
48
- query: string,
49
- _count: number,
50
- ): Promise<Suggestion | null> {
51
- const params = new URLSearchParams({ query });
52
- const res = await fetch(`/api/vtex/suggestions?${params}`);
53
- if (!res.ok) return null;
54
- return res.json();
47
+ async function defaultFetchSuggestions(query: string, _count: number): Promise<Suggestion | null> {
48
+ const params = new URLSearchParams({ query });
49
+ const res = await fetch(`/api/vtex/suggestions?${params}`);
50
+ if (!res.ok) return null;
51
+ return res.json();
55
52
  }
56
53
 
57
54
  export function useAutocomplete(opts?: UseAutocompleteOptions) {
58
- const debounceMs = opts?.debounceMs ?? 250;
59
- const count = opts?.count ?? 4;
60
- const fetchFn = opts?.fetchSuggestions ?? defaultFetchSuggestions;
55
+ const debounceMs = opts?.debounceMs ?? 250;
56
+ const count = opts?.count ?? 4;
57
+ const fetchFn = opts?.fetchSuggestions ?? defaultFetchSuggestions;
61
58
 
62
- const [rawQuery, setRawQuery] = useState("");
63
- const debouncedQuery = useDebounce(rawQuery.trim(), debounceMs);
59
+ const [rawQuery, setRawQuery] = useState("");
60
+ const debouncedQuery = useDebounce(rawQuery.trim(), debounceMs);
64
61
 
65
- const { data, isLoading, isFetching } = useQuery({
66
- queryKey: [AUTOCOMPLETE_QUERY_KEY, debouncedQuery, count],
67
- queryFn: () => fetchFn(debouncedQuery, count),
68
- enabled: debouncedQuery.length > 0,
69
- staleTime: 60_000,
70
- });
62
+ const { data, isLoading, isFetching } = useQuery({
63
+ queryKey: [AUTOCOMPLETE_QUERY_KEY, debouncedQuery, count],
64
+ queryFn: () => fetchFn(debouncedQuery, count),
65
+ enabled: debouncedQuery.length > 0,
66
+ staleTime: 60_000,
67
+ });
71
68
 
72
- const setSearch = useCallback(
73
- (query: string) => {
74
- setRawQuery(query);
75
- },
76
- [],
77
- );
69
+ const setSearch = useCallback((query: string) => {
70
+ setRawQuery(query);
71
+ }, []);
78
72
 
79
- return {
80
- /** Set the search query (will be debounced automatically) */
81
- setSearch,
82
- /** Current raw (un-debounced) query */
83
- query: rawQuery,
84
- /** Suggestion result (searches + products) */
85
- suggestions: data ?? null,
86
- /** True while initial fetch is in progress */
87
- loading: isLoading || isFetching,
88
- };
73
+ return {
74
+ /** Set the search query (will be debounced automatically) */
75
+ setSearch,
76
+ /** Current raw (un-debounced) query */
77
+ query: rawQuery,
78
+ /** Suggestion result (searches + products) */
79
+ suggestions: data ?? null,
80
+ /** True while initial fetch is in progress */
81
+ loading: isLoading || isFetching,
82
+ };
89
83
  }