@decocms/apps-vtex 8.1.0-next.0 → 8.1.0-next.2

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 (163) hide show
  1. package/dist/index.d.ts +6 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +6 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/utils/constants.d.ts +28 -0
  6. package/dist/utils/constants.d.ts.map +1 -0
  7. package/dist/utils/constants.js +17 -0
  8. package/dist/utils/constants.js.map +1 -0
  9. package/dist/utils/cookieSanitizer.d.ts +87 -0
  10. package/dist/utils/cookieSanitizer.d.ts.map +1 -0
  11. package/dist/utils/cookieSanitizer.js +136 -0
  12. package/dist/utils/cookieSanitizer.js.map +1 -0
  13. package/dist/utils/operationRouter.d.ts +25 -0
  14. package/dist/utils/operationRouter.d.ts.map +1 -0
  15. package/dist/utils/operationRouter.js +120 -0
  16. package/dist/utils/operationRouter.js.map +1 -0
  17. package/dist/utils/types.d.ts +944 -0
  18. package/dist/utils/types.d.ts.map +1 -0
  19. package/dist/utils/types.js +25 -0
  20. package/dist/utils/types.js.map +1 -0
  21. package/dist/vtexClient.d.ts +173 -0
  22. package/dist/vtexClient.d.ts.map +1 -0
  23. package/dist/vtexClient.js +242 -0
  24. package/dist/vtexClient.js.map +1 -0
  25. package/package.json +16 -57
  26. package/src/index.ts +11 -29
  27. package/src/utils/constants.ts +2 -56
  28. package/src/utils/cookieSanitizer.ts +59 -59
  29. package/src/utils/operationRouter.ts +75 -65
  30. package/src/utils/types.ts +709 -1557
  31. package/src/vtexClient.ts +358 -347
  32. package/src/README.md +0 -6
  33. package/src/__conformance__/upstream.test.ts +0 -158
  34. package/src/__tests__/client-segment-cookie.test.ts +0 -255
  35. package/src/__tests__/client-set-cookie-forward.test.ts +0 -257
  36. package/src/__tests__/schemas.test.ts +0 -71
  37. package/src/actions/address.ts +0 -250
  38. package/src/actions/analytics/sendEvent.ts +0 -86
  39. package/src/actions/auth.ts +0 -333
  40. package/src/actions/checkout.ts +0 -659
  41. package/src/actions/index.ts +0 -11
  42. package/src/actions/masterData.ts +0 -168
  43. package/src/actions/misc.ts +0 -188
  44. package/src/actions/newsletter.ts +0 -105
  45. package/src/actions/orders.ts +0 -34
  46. package/src/actions/profile.ts +0 -201
  47. package/src/actions/session.ts +0 -85
  48. package/src/actions/trigger.ts +0 -43
  49. package/src/actions/wishlist.ts +0 -114
  50. package/src/client.ts +0 -724
  51. package/src/commerceLoaders.ts +0 -267
  52. package/src/hooks/__tests__/createCart.render.test.ts +0 -114
  53. package/src/hooks/__tests__/createCart.test.ts +0 -92
  54. package/src/hooks/__tests__/createUseCart.test.ts +0 -184
  55. package/src/hooks/__tests__/createUseUser.test.ts +0 -48
  56. package/src/hooks/__tests__/createUseWishlist.test.ts +0 -87
  57. package/src/hooks/cartQuery.ts +0 -204
  58. package/src/hooks/createCart.ts +0 -427
  59. package/src/hooks/createUseCart.ts +0 -360
  60. package/src/hooks/createUseUser.ts +0 -153
  61. package/src/hooks/createUseWishlist.ts +0 -242
  62. package/src/hooks/index.ts +0 -25
  63. package/src/hooks/useAutocomplete.ts +0 -83
  64. package/src/hooks/useCart.ts +0 -243
  65. package/src/hooks/useUser.ts +0 -78
  66. package/src/hooks/useWishlist.ts +0 -119
  67. package/src/invoke.ts +0 -235
  68. package/src/loaders/ProductDetailsPage.ts +0 -1
  69. package/src/loaders/ProductList.ts +0 -1
  70. package/src/loaders/ProductListingPage.ts +0 -1
  71. package/src/loaders/__tests__/legacyProductList.test.ts +0 -157
  72. package/src/loaders/address.ts +0 -115
  73. package/src/loaders/autocomplete.ts +0 -56
  74. package/src/loaders/brands.ts +0 -44
  75. package/src/loaders/cart/attachments.ts +0 -53
  76. package/src/loaders/cart/full.ts +0 -67
  77. package/src/loaders/cart/gifts.ts +0 -43
  78. package/src/loaders/cart/orderFormId.ts +0 -31
  79. package/src/loaders/cart/shipping.test.ts +0 -125
  80. package/src/loaders/cart/shipping.ts +0 -136
  81. package/src/loaders/cart/summary.ts +0 -36
  82. package/src/loaders/cart.ts +0 -52
  83. package/src/loaders/catalog.ts +0 -163
  84. package/src/loaders/collections.ts +0 -55
  85. package/src/loaders/index.ts +0 -20
  86. package/src/loaders/intelligentSearch/__tests__/productListingPage.test.ts +0 -51
  87. package/src/loaders/intelligentSearch/productDetailsPage.ts +0 -111
  88. package/src/loaders/intelligentSearch/productList.ts +0 -170
  89. package/src/loaders/intelligentSearch/productListingPage.ts +0 -525
  90. package/src/loaders/intelligentSearch/suggestions.ts +0 -44
  91. package/src/loaders/legacy/productDetailsPage.ts +0 -1
  92. package/src/loaders/legacy/productList.ts +0 -1
  93. package/src/loaders/legacy/relatedProductsLoader.ts +0 -79
  94. package/src/loaders/legacy.ts +0 -710
  95. package/src/loaders/logistics.ts +0 -111
  96. package/src/loaders/minicart.ts +0 -78
  97. package/src/loaders/navbar.ts +0 -26
  98. package/src/loaders/orders.ts +0 -92
  99. package/src/loaders/pageType.ts +0 -68
  100. package/src/loaders/paths/PDPDefaultPath.ts +0 -51
  101. package/src/loaders/payment.ts +0 -97
  102. package/src/loaders/productListFull.ts +0 -157
  103. package/src/loaders/profile.ts +0 -138
  104. package/src/loaders/promotion.ts +0 -30
  105. package/src/loaders/search.ts +0 -124
  106. package/src/loaders/session.ts +0 -83
  107. package/src/loaders/user.ts +0 -87
  108. package/src/loaders/wishlist.ts +0 -89
  109. package/src/loaders/wishlistProducts.ts +0 -69
  110. package/src/loaders/workflow/products.ts +0 -62
  111. package/src/loaders/workflow.ts +0 -316
  112. package/src/logo.png +0 -0
  113. package/src/manifest.gen.ts +0 -89
  114. package/src/middleware.cacheHeaders.test.ts +0 -44
  115. package/src/middleware.ts +0 -240
  116. package/src/mod.ts +0 -173
  117. package/src/registry.ts +0 -9
  118. package/src/schemas.gen.ts +0 -3191
  119. package/src/schemas.ts +0 -30
  120. package/src/types.ts +0 -248
  121. package/src/utils/__tests__/cartProjection.test.ts +0 -118
  122. package/src/utils/__tests__/cookieSanitizer.test.ts +0 -162
  123. package/src/utils/__tests__/fetch.test.ts +0 -80
  124. package/src/utils/__tests__/fetchCache.test.ts +0 -155
  125. package/src/utils/__tests__/instrumentedFetch.test.ts +0 -158
  126. package/src/utils/__tests__/intelligentSearch.test.ts +0 -88
  127. package/src/utils/__tests__/minicart.test.ts +0 -184
  128. package/src/utils/__tests__/operationRouter.test.ts +0 -227
  129. package/src/utils/__tests__/resilience.test.ts +0 -205
  130. package/src/utils/__tests__/resourceRange.test.ts +0 -32
  131. package/src/utils/__tests__/simulationCache.test.ts +0 -40
  132. package/src/utils/__tests__/sitemap.test.ts +0 -185
  133. package/src/utils/__tests__/slugify.test.ts +0 -31
  134. package/src/utils/__tests__/storefrontBaseUrl.test.ts +0 -61
  135. package/src/utils/__tests__/transform.test.ts +0 -1111
  136. package/src/utils/accountLoaders.ts +0 -203
  137. package/src/utils/authHelpers.ts +0 -85
  138. package/src/utils/batch.ts +0 -18
  139. package/src/utils/buildOfferShelf.test.ts +0 -59
  140. package/src/utils/cartProjection.ts +0 -96
  141. package/src/utils/cookies.ts +0 -167
  142. package/src/utils/enrichment.ts +0 -556
  143. package/src/utils/fetch.ts +0 -111
  144. package/src/utils/fetchCache.ts +0 -55
  145. package/src/utils/index.ts +0 -19
  146. package/src/utils/instrumentedFetch.ts +0 -98
  147. package/src/utils/intelligentSearch.ts +0 -96
  148. package/src/utils/legacy.ts +0 -139
  149. package/src/utils/minicart.ts +0 -139
  150. package/src/utils/pickAndOmit.ts +0 -25
  151. package/src/utils/proxy.ts +0 -495
  152. package/src/utils/resilience.ts +0 -352
  153. package/src/utils/resourceRange.ts +0 -10
  154. package/src/utils/segment.ts +0 -152
  155. package/src/utils/similars.ts +0 -35
  156. package/src/utils/simulationCache.ts +0 -73
  157. package/src/utils/sitemap.ts +0 -283
  158. package/src/utils/slugCache.ts +0 -28
  159. package/src/utils/slugify.ts +0 -13
  160. package/src/utils/transform.ts +0 -1691
  161. package/src/utils/vtexId.ts +0 -151
  162. package/src/vtexClient.test.ts +0 -204
  163. package/tsconfig.json +0 -7
@@ -1,360 +0,0 @@
1
- /**
2
- * Factory for the legacy invoke-based `useCart` hook.
3
- *
4
- * This is the API shape that migrated Fresh sites depend on:
5
- * - module-level singleton state (no QueryClient required)
6
- * - listener-based re-render (`forceRender` on a useState counter)
7
- * - awaitable async actions (`await addItem(...)`) instead of TanStack mutations
8
- * - signal-shaped accessors (`cart.value`, `cart.value = ...`)
9
- *
10
- * It is intentionally separate from the canonical `useCart` in
11
- * `vtex/hooks/useCart.ts`, which is built on TanStack Query and exposes the
12
- * `Minicart` shape. Both can coexist in a single site.
13
- *
14
- * @example
15
- * ```ts
16
- * // src/hooks/useCart.ts
17
- * import { createUseCart } from "@decocms/apps/vtex/hooks/createUseCart";
18
- * import { invoke } from "~/server/invoke";
19
- *
20
- * export const { useCart, resetCart, itemToAnalyticsItem } = createUseCart({
21
- * invoke,
22
- * });
23
- * export type { OrderForm, OrderFormItem } from "@decocms/apps/vtex/types";
24
- * ```
25
- */
26
-
27
- import { useEffect, useState } from "react";
28
- import type { OrderForm, OrderFormItem } from "../types";
29
-
30
- /** Minimal structural shape of the invoke proxy this hook needs. */
31
- export interface CreateUseCartInvoke {
32
- vtex: {
33
- actions: {
34
- getOrCreateCart: (args: { data: { orderFormId?: string } }) => Promise<OrderForm>;
35
- addItemsToCart: (args: {
36
- data: {
37
- orderFormId: string;
38
- orderItems: Array<{ id: string; seller: string; quantity: number }>;
39
- };
40
- }) => Promise<OrderForm>;
41
- updateCartItems: (args: {
42
- data: {
43
- orderFormId: string;
44
- orderItems: Array<{ index: number; quantity: number }>;
45
- };
46
- }) => Promise<OrderForm>;
47
- addCouponToCart: (args: {
48
- data: { orderFormId: string; text: string };
49
- }) => Promise<OrderForm>;
50
- updateOrderFormAttachment: (args: {
51
- data: {
52
- orderFormId: string;
53
- attachment: string;
54
- body: Record<string, unknown>;
55
- };
56
- }) => Promise<OrderForm>;
57
- simulateCart: (args: {
58
- data: {
59
- items: Array<{ id: string; quantity: number; seller: string }>;
60
- postalCode: string;
61
- country: string;
62
- };
63
- }) => Promise<unknown>;
64
- };
65
- };
66
- }
67
-
68
- export interface CreateUseCartOptions {
69
- invoke: CreateUseCartInvoke;
70
- /**
71
- * Override the orderFormId cookie name. VTEX standard is
72
- * `checkout.vtex.com__orderFormId`, which is the default.
73
- */
74
- orderFormCookieName?: string;
75
- /** Override the cookie max-age in seconds. Default: 7 days. */
76
- orderFormCookieMaxAge?: number;
77
- }
78
-
79
- /** Build a per-site `useCart` plus its companions. */
80
- export function createUseCart(opts: CreateUseCartOptions) {
81
- const { invoke } = opts;
82
- const COOKIE_NAME = opts.orderFormCookieName ?? "checkout.vtex.com__orderFormId";
83
- const COOKIE_MAX_AGE = opts.orderFormCookieMaxAge ?? 7 * 24 * 3600;
84
-
85
- let _orderForm: OrderForm | null = null;
86
- let _loading = false;
87
- let _initStarted = false;
88
- let _initFailed = false;
89
- const _listeners = new Set<() => void>();
90
-
91
- function notify() {
92
- for (const fn of _listeners) fn();
93
- }
94
- function setOrderForm(of: OrderForm | null) {
95
- _orderForm = of;
96
- notify();
97
- }
98
- function setLoading(v: boolean) {
99
- _loading = v;
100
- notify();
101
- }
102
-
103
- function escapeRegex(s: string): string {
104
- return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
105
- }
106
-
107
- function getOrderFormIdFromCookie(): string | null {
108
- if (typeof document === "undefined") return null;
109
- const re = new RegExp(`${escapeRegex(COOKIE_NAME)}=([^;]*)`);
110
- const match = document.cookie.match(re);
111
- return match ? decodeURIComponent(match[1]) : null;
112
- }
113
-
114
- function setOrderFormIdCookie(id: string) {
115
- if (typeof document === "undefined") return;
116
- document.cookie = `${COOKIE_NAME}=${encodeURIComponent(id)}; path=/; max-age=${COOKIE_MAX_AGE}; SameSite=Lax`;
117
- }
118
-
119
- async function ensureOrderForm(): Promise<string> {
120
- if (_orderForm?.orderFormId) return _orderForm.orderFormId;
121
-
122
- const existing = getOrderFormIdFromCookie();
123
- const of = await invoke.vtex.actions.getOrCreateCart({
124
- data: { orderFormId: existing || undefined },
125
- });
126
- setOrderForm(of);
127
- if (of?.orderFormId) setOrderFormIdCookie(of.orderFormId);
128
- return of.orderFormId;
129
- }
130
-
131
- function itemToAnalyticsItem(item: OrderFormItem & { coupon?: string }, index: number) {
132
- return {
133
- item_id: item.productId,
134
- item_group_id: item.productId,
135
- item_name: item.name ?? item.skuName ?? "",
136
- item_variant: item.skuName,
137
- item_brand: item.additionalInfo?.brandName ?? "",
138
- price: (item.sellingPrice ?? item.price ?? 0) / 100,
139
- discount: Number(((item.listPrice - item.sellingPrice) / 100).toFixed(2)),
140
- quantity: item.quantity,
141
- coupon: item.coupon,
142
- affiliation: item.seller,
143
- index,
144
- };
145
- }
146
-
147
- /** Reset all module-level cart state so the next useCart() re-fetches. */
148
- function resetCart() {
149
- _orderForm = null;
150
- _loading = false;
151
- _initStarted = false;
152
- _initFailed = false;
153
- notify();
154
- }
155
-
156
- function useCart() {
157
- const [, forceRender] = useState(0);
158
-
159
- useEffect(() => {
160
- const listener = () => forceRender((n) => n + 1);
161
- _listeners.add(listener);
162
-
163
- if (!_orderForm && !_initStarted) {
164
- _initStarted = true;
165
- const ofId = getOrderFormIdFromCookie();
166
- setLoading(true);
167
- invoke.vtex.actions
168
- .getOrCreateCart({ data: { orderFormId: ofId || undefined } })
169
- .then((of) => {
170
- setOrderForm(of);
171
- if (of?.orderFormId) setOrderFormIdCookie(of.orderFormId);
172
- })
173
- .catch((err: unknown) => {
174
- console.error("[useCart] init failed:", err);
175
- // Keep previous orderForm if we had one (e.g. after SPA navigation)
176
- // so user data isn't lost on transient VTEX 503s.
177
- if (!_orderForm) {
178
- _initFailed = true;
179
- notify();
180
- }
181
- })
182
- .finally(() => setLoading(false));
183
- }
184
-
185
- return () => {
186
- _listeners.delete(listener);
187
- };
188
- }, []);
189
-
190
- return {
191
- cart: {
192
- get value() {
193
- return _orderForm;
194
- },
195
- set value(v: OrderForm | null) {
196
- setOrderForm(v);
197
- },
198
- },
199
-
200
- loading: {
201
- get value() {
202
- return _loading;
203
- },
204
- set value(v: boolean) {
205
- setLoading(v);
206
- },
207
- },
208
-
209
- initFailed: {
210
- get value() {
211
- return _initFailed;
212
- },
213
- },
214
-
215
- addItem: async (params: { id: string; seller: string; quantity?: number }) => {
216
- setLoading(true);
217
- try {
218
- const ofId = await ensureOrderForm();
219
- const updated = await invoke.vtex.actions.addItemsToCart({
220
- data: {
221
- orderFormId: ofId,
222
- orderItems: [
223
- {
224
- id: params.id,
225
- seller: params.seller,
226
- quantity: params.quantity ?? 1,
227
- },
228
- ],
229
- },
230
- });
231
- setOrderForm(updated);
232
- if (updated?.orderFormId) setOrderFormIdCookie(updated.orderFormId);
233
- } catch (err) {
234
- console.error("[useCart] addItem failed:", err);
235
- throw err;
236
- } finally {
237
- setLoading(false);
238
- }
239
- },
240
-
241
- addItems: async (params: {
242
- orderItems: Array<{ id: string; seller: string; quantity: number }>;
243
- }) => {
244
- setLoading(true);
245
- try {
246
- const ofId = await ensureOrderForm();
247
- const updated = await invoke.vtex.actions.addItemsToCart({
248
- data: { orderFormId: ofId, orderItems: params.orderItems },
249
- });
250
- setOrderForm(updated);
251
- if (updated?.orderFormId) setOrderFormIdCookie(updated.orderFormId);
252
- } catch (err) {
253
- console.error("[useCart] addItems failed:", err);
254
- throw err;
255
- } finally {
256
- setLoading(false);
257
- }
258
- },
259
-
260
- updateItems: async (params: { orderItems: Array<{ index: number; quantity: number }> }) => {
261
- const ofId = _orderForm?.orderFormId || getOrderFormIdFromCookie();
262
- if (!ofId) return;
263
- setLoading(true);
264
- try {
265
- const updated = await invoke.vtex.actions.updateCartItems({
266
- data: { orderFormId: ofId, orderItems: params.orderItems },
267
- });
268
- setOrderForm(updated);
269
- } catch (err) {
270
- console.error("[useCart] updateItems failed:", err);
271
- } finally {
272
- setLoading(false);
273
- }
274
- },
275
-
276
- removeItem: async (index: number) => {
277
- const ofId = _orderForm?.orderFormId || getOrderFormIdFromCookie();
278
- if (!ofId) return;
279
- setLoading(true);
280
- try {
281
- const updated = await invoke.vtex.actions.updateCartItems({
282
- data: {
283
- orderFormId: ofId,
284
- orderItems: [{ index, quantity: 0 }],
285
- },
286
- });
287
- setOrderForm(updated);
288
- } catch (err) {
289
- console.error("[useCart] removeItem failed:", err);
290
- } finally {
291
- setLoading(false);
292
- }
293
- },
294
-
295
- addCouponsToCart: async ({ text }: { text: string }) => {
296
- const ofId = _orderForm?.orderFormId || getOrderFormIdFromCookie();
297
- if (!ofId) return;
298
- setLoading(true);
299
- try {
300
- const updated = await invoke.vtex.actions.addCouponToCart({
301
- data: { orderFormId: ofId, text },
302
- });
303
- setOrderForm(updated);
304
- } catch (err) {
305
- console.error("[useCart] addCoupon failed:", err);
306
- } finally {
307
- setLoading(false);
308
- }
309
- },
310
-
311
- sendAttachment: async (params: { attachment: string; body: Record<string, unknown> }) => {
312
- const ofId = _orderForm?.orderFormId || getOrderFormIdFromCookie();
313
- if (!ofId) return;
314
- setLoading(true);
315
- try {
316
- const updated = await invoke.vtex.actions.updateOrderFormAttachment({
317
- data: {
318
- orderFormId: ofId,
319
- attachment: params.attachment,
320
- body: params.body,
321
- },
322
- });
323
- setOrderForm(updated);
324
- } catch (err) {
325
- console.error("[useCart] sendAttachment failed:", err);
326
- } finally {
327
- setLoading(false);
328
- }
329
- },
330
-
331
- simulate: async (data: {
332
- items: Array<{ id: string; quantity: number; seller: string }>;
333
- postalCode: string;
334
- country: string;
335
- }) => {
336
- return await invoke.vtex.actions.simulateCart({
337
- data: {
338
- items: data.items.map((i) => ({
339
- id: i.id,
340
- quantity: i.quantity,
341
- seller: i.seller,
342
- })),
343
- postalCode: data.postalCode,
344
- country: data.country,
345
- },
346
- });
347
- },
348
-
349
- mapItemsToAnalyticsItems: (orderForm: OrderForm | null) => {
350
- return (orderForm?.items || []).map((item, index) => itemToAnalyticsItem(item, index));
351
- },
352
- };
353
- }
354
-
355
- return {
356
- useCart,
357
- resetCart,
358
- itemToAnalyticsItem,
359
- };
360
- }
@@ -1,153 +0,0 @@
1
- /**
2
- * Factory for the legacy invoke-based `useUser` hook.
3
- *
4
- * This is the API shape that migrated Fresh sites depend on:
5
- * - module-level singleton state (no QueryClient required)
6
- * - listener-based re-render (`forceRender` on a useState counter)
7
- * - signal-shaped accessors (`user.value`, `loading.value`)
8
- * - awaitable refresh (`await refresh()`)
9
- *
10
- * It is intentionally separate from the canonical `useUser` in
11
- * `vtex/hooks/useUser.ts`, which is built on TanStack Query and exposes
12
- * `{ user, isLoggedIn, isLoading, refetch }`. Both can coexist in a single site.
13
- *
14
- * @example
15
- * ```ts
16
- * // src/hooks/useUser.ts
17
- * import { createUseUser } from "@decocms/apps/vtex/hooks/createUseUser";
18
- * import { invoke } from "~/server/invoke";
19
- *
20
- * export const { useUser, resetUser } = createUseUser({ invoke });
21
- * export type { Person } from "@decocms/apps/vtex/loaders/user";
22
- * ```
23
- */
24
-
25
- import { useEffect, useState } from "react";
26
- import type { Person } from "../loaders/user";
27
-
28
- /** Minimal structural shape of the invoke proxy this hook needs. */
29
- export interface CreateUseUserInvoke {
30
- vtex: {
31
- loaders: {
32
- user: () => Promise<Person | null>;
33
- };
34
- };
35
- }
36
-
37
- export interface CreateUseUserOptions {
38
- invoke: CreateUseUserInvoke;
39
- }
40
-
41
- /** Build a per-site `useUser` plus its companions. */
42
- export function createUseUser(opts: CreateUseUserOptions) {
43
- const { invoke } = opts;
44
-
45
- let _user: Person | null = null;
46
- let _loading = false;
47
- let _initStarted = false;
48
- let _initFailed = false;
49
- const _listeners = new Set<() => void>();
50
-
51
- function notify() {
52
- for (const fn of _listeners) fn();
53
- }
54
- function setUser(u: Person | null) {
55
- _user = u;
56
- notify();
57
- }
58
- function setLoading(v: boolean) {
59
- _loading = v;
60
- notify();
61
- }
62
-
63
- async function refresh(): Promise<Person | null> {
64
- setLoading(true);
65
- try {
66
- const u = await invoke.vtex.loaders.user();
67
- setUser(u);
68
- _initFailed = false;
69
- return u;
70
- } catch (err) {
71
- console.error("[useUser] refresh failed:", err);
72
- _initFailed = true;
73
- notify();
74
- return null;
75
- } finally {
76
- setLoading(false);
77
- }
78
- }
79
-
80
- /** Reset module-level user state so the next useUser() re-fetches. */
81
- function resetUser() {
82
- _user = null;
83
- _loading = false;
84
- _initStarted = false;
85
- _initFailed = false;
86
- notify();
87
- }
88
-
89
- function useUser() {
90
- const [, forceRender] = useState(0);
91
-
92
- useEffect(() => {
93
- const listener = () => forceRender((n) => n + 1);
94
- _listeners.add(listener);
95
-
96
- if (!_user && !_initStarted) {
97
- _initStarted = true;
98
- setLoading(true);
99
- invoke.vtex.loaders
100
- .user()
101
- .then((u) => {
102
- setUser(u);
103
- })
104
- .catch((err: unknown) => {
105
- console.error("[useUser] init failed:", err);
106
- _initFailed = true;
107
- notify();
108
- })
109
- .finally(() => setLoading(false));
110
- }
111
-
112
- return () => {
113
- _listeners.delete(listener);
114
- };
115
- }, []);
116
-
117
- return {
118
- user: {
119
- get value() {
120
- return _user;
121
- },
122
- set value(v: Person | null) {
123
- setUser(v);
124
- },
125
- },
126
-
127
- loading: {
128
- get value() {
129
- return _loading;
130
- },
131
- },
132
-
133
- isLoggedIn: {
134
- get value() {
135
- return !!_user?.email;
136
- },
137
- },
138
-
139
- initFailed: {
140
- get value() {
141
- return _initFailed;
142
- },
143
- },
144
-
145
- refresh,
146
- };
147
- }
148
-
149
- return {
150
- useUser,
151
- resetUser,
152
- };
153
- }