@faststore/core 3.90.0 → 3.91.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 (112) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +25 -25
  3. package/.next/cache/.tsbuildinfo +1 -1
  4. package/.next/cache/config.json +3 -3
  5. package/.next/cache/webpack/client-production/0.pack +0 -0
  6. package/.next/cache/webpack/client-production/index.pack +0 -0
  7. package/.next/cache/webpack/server-production/0.pack +0 -0
  8. package/.next/cache/webpack/server-production/index.pack +0 -0
  9. package/.next/prerender-manifest.js +1 -1
  10. package/.next/prerender-manifest.json +1 -1
  11. package/.next/react-loadable-manifest.json +7 -7
  12. package/.next/routes-manifest.json +1 -1
  13. package/.next/server/chunks/4168.js +1 -1
  14. package/.next/server/chunks/7098.js +2 -2
  15. package/.next/server/chunks/870.js +1 -1
  16. package/.next/server/chunks/9563.js +3 -3
  17. package/.next/server/chunks/9630.js +3 -3
  18. package/.next/server/functions-config-manifest.json +1 -1
  19. package/.next/server/middleware-build-manifest.js +1 -1
  20. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  21. package/.next/server/pages/404.js.nft.json +1 -1
  22. package/.next/server/pages/500.js.nft.json +1 -1
  23. package/.next/server/pages/[...slug].js.nft.json +1 -1
  24. package/.next/server/pages/[slug]/p.js.nft.json +1 -1
  25. package/.next/server/pages/_app.js.nft.json +1 -1
  26. package/.next/server/pages/_document.js.nft.json +1 -1
  27. package/.next/server/pages/_error.js.nft.json +1 -1
  28. package/.next/server/pages/api/graphql.js +3 -3
  29. package/.next/server/pages/api/graphql.js.nft.json +1 -1
  30. package/.next/server/pages/api/health/live.js.nft.json +1 -1
  31. package/.next/server/pages/api/health/ready.js.nft.json +1 -1
  32. package/.next/server/pages/api/preview.js.nft.json +1 -1
  33. package/.next/server/pages/checkout.js.nft.json +1 -1
  34. package/.next/server/pages/en-US/404.html +1 -1
  35. package/.next/server/pages/en-US/404.json +1 -1
  36. package/.next/server/pages/en-US/500.html +1 -1
  37. package/.next/server/pages/en-US/500.json +1 -1
  38. package/.next/server/pages/en-US/checkout.html +1 -1
  39. package/.next/server/pages/en-US/checkout.json +1 -1
  40. package/.next/server/pages/en-US/login.html +1 -1
  41. package/.next/server/pages/en-US/login.json +1 -1
  42. package/.next/server/pages/en-US/s.html +1 -1
  43. package/.next/server/pages/en-US/s.json +1 -1
  44. package/.next/server/pages/en-US.html +1 -1
  45. package/.next/server/pages/en-US.json +1 -1
  46. package/.next/server/pages/index.js +1 -1
  47. package/.next/server/pages/index.js.nft.json +1 -1
  48. package/.next/server/pages/login.js.nft.json +1 -1
  49. package/.next/server/pages/pvt/account/403.js +1 -1
  50. package/.next/server/pages/pvt/account/403.js.nft.json +1 -1
  51. package/.next/server/pages/pvt/account/404.js +1 -1
  52. package/.next/server/pages/pvt/account/404.js.nft.json +1 -1
  53. package/.next/server/pages/pvt/account/[...unknown].js.nft.json +1 -1
  54. package/.next/server/pages/pvt/account/orders/[id].js +1 -1
  55. package/.next/server/pages/pvt/account/orders/[id].js.nft.json +1 -1
  56. package/.next/server/pages/pvt/account/orders.js +1 -1
  57. package/.next/server/pages/pvt/account/orders.js.nft.json +1 -1
  58. package/.next/server/pages/pvt/account/profile.js +1 -1
  59. package/.next/server/pages/pvt/account/profile.js.nft.json +1 -1
  60. package/.next/server/pages/pvt/account/security.js +1 -1
  61. package/.next/server/pages/pvt/account/security.js.nft.json +1 -1
  62. package/.next/server/pages/pvt/account/user-details.js +1 -1
  63. package/.next/server/pages/pvt/account/user-details.js.nft.json +1 -1
  64. package/.next/server/pages/pvt/account.js +1 -1
  65. package/.next/server/pages/pvt/account.js.nft.json +1 -1
  66. package/.next/server/pages/s.js.nft.json +1 -1
  67. package/.next/server/pages-manifest.json +1 -1
  68. package/.next/static/{g8OBiuzAGVQYHn1HPDL_Y → FslBA_VdamNtaqLHejDIJ}/_buildManifest.js +1 -1
  69. package/.next/static/chunks/{2500.e2623cac49e0ccb8.js → 2500.fa0f80184d3cde4f.js} +1 -1
  70. package/.next/static/chunks/5859.0b4ee873dc86bef7.js +1 -0
  71. package/.next/static/chunks/ProductShelf.24f22900bc10896e.js +1 -0
  72. package/.next/static/chunks/pages/[slug]/p-323813ea2a7455fb.js +1 -0
  73. package/.next/static/chunks/pages/_app-43bcb55c76e1d67e.js +1 -0
  74. package/.next/static/chunks/pages/index-5c3eb58eefd7cb0f.js +1 -0
  75. package/.next/static/chunks/pages/pvt/account/403-91597100f8956385.js +1 -0
  76. package/.next/static/chunks/{webpack-27e109703d78aaaa.js → webpack-083c0d59a2221fa9.js} +1 -1
  77. package/.next/trace +138 -138
  78. package/.turbo/turbo-build.log +12 -12
  79. package/.turbo/turbo-test.log +5 -5
  80. package/@generated/schema.graphql +18 -16
  81. package/CHANGELOG.md +12 -0
  82. package/discovery.config.default.js +1 -1
  83. package/package.json +3 -3
  84. package/src/components/sections/ProductTiles/ProductTiles.tsx +4 -7
  85. package/src/components/ui/ProductShelf/ProductShelf.tsx +7 -6
  86. package/src/experimental/{myAccountSeverSideProps.ts → myAccountServerSideProps.ts} +18 -6
  87. package/src/pages/api/graphql.ts +20 -11
  88. package/src/pages/pvt/account/403.tsx +67 -48
  89. package/src/pages/pvt/account/404.tsx +16 -12
  90. package/src/pages/pvt/account/[...unknown].tsx +1 -1
  91. package/src/pages/pvt/account/index.tsx +0 -12
  92. package/src/pages/pvt/account/orders/[id].tsx +8 -15
  93. package/src/pages/pvt/account/orders/index.tsx +9 -15
  94. package/src/pages/pvt/account/profile.tsx +14 -21
  95. package/src/pages/pvt/account/security.tsx +7 -14
  96. package/src/pages/pvt/account/user-details.tsx +7 -14
  97. package/src/sdk/account/getMyAccountRoutes.ts +3 -2
  98. package/src/sdk/account/refreshToken.ts +50 -0
  99. package/src/sdk/account/useRefreshToken.ts +67 -0
  100. package/src/sdk/account/validateUser.ts +56 -12
  101. package/src/sdk/deliveryPromise/useDeliveryPromiseFacets.ts +25 -0
  102. package/src/sdk/session/index.ts +6 -37
  103. package/src/server/index.ts +7 -1
  104. package/src/utils/utilities.ts +0 -7
  105. package/.next/server/chunks/7947.js +0 -1
  106. package/.next/static/chunks/5859.cf66c492751f5680.js +0 -1
  107. package/.next/static/chunks/ProductShelf.319b97d6c822340b.js +0 -1
  108. package/.next/static/chunks/pages/[slug]/p-49a7aaba6898f796.js +0 -1
  109. package/.next/static/chunks/pages/_app-04e8430b2da487f2.js +0 -1
  110. package/.next/static/chunks/pages/index-7a9bc631c89fd3f2.js +0 -1
  111. package/.next/static/chunks/pages/pvt/account/403-f2858569fde3873b.js +0 -1
  112. /package/.next/static/{g8OBiuzAGVQYHn1HPDL_Y → FslBA_VdamNtaqLHejDIJ}/_ssgManifest.js +0 -0
@@ -1,5 +1,4 @@
1
1
  import type { GetServerSideProps, NextPage } from 'next'
2
- import { validateUser } from 'src/sdk/account/validateUser'
3
2
  import { getMyAccountRedirect } from 'src/utils/myAccountRedirect'
4
3
 
5
4
  const MyAccountRedirectPage: NextPage = () => {
@@ -10,17 +9,6 @@ export const getServerSideProps: GetServerSideProps = async ({
10
9
  query,
11
10
  req,
12
11
  }) => {
13
- const isValid = await validateUser({ query, req } as any)
14
-
15
- if (!isValid) {
16
- return {
17
- redirect: {
18
- destination: '/login',
19
- permanent: false,
20
- },
21
- }
22
- }
23
-
24
12
  const { isFaststoreMyAccountEnabled, redirect } = getMyAccountRedirect({
25
13
  query,
26
14
  })
@@ -7,8 +7,7 @@ import MyAccountOrderDetails from 'src/components/account/orders/MyAccountOrderD
7
7
  import RenderSections from 'src/components/cms/RenderSections'
8
8
  import { default as GLOBAL_COMPONENTS } from 'src/components/cms/global/Components'
9
9
  import CUSTOM_COMPONENTS from 'src/customizations/src/components'
10
- import type { MyAccountProps } from 'src/experimental/myAccountSeverSideProps'
11
- import { validateUser } from 'src/sdk/account/validateUser'
10
+ import type { MyAccountProps } from 'src/experimental/myAccountServerSideProps'
12
11
 
13
12
  import { gql } from '@generated'
14
13
  import type {
@@ -268,17 +267,6 @@ export const getServerSideProps: GetServerSideProps<
268
267
  Record<string, string>,
269
268
  Locator
270
269
  > = async (context) => {
271
- const isValid = await validateUser(context)
272
-
273
- if (!isValid) {
274
- return {
275
- redirect: {
276
- destination: '/login',
277
- permanent: false,
278
- },
279
- }
280
- }
281
-
282
270
  const isRepresentative = getIsRepresentative({
283
271
  headers: context.req.headers as Record<string, string>,
284
272
  account: storeConfig.api.storeId,
@@ -328,11 +316,16 @@ export const getServerSideProps: GetServerSideProps<
328
316
  console.error(...orderDetails.errors)
329
317
  const status = extractStatusFromError(orderDetails.errors?.[0])
330
318
 
331
- const isForbidden = status === 403 || status === 401
319
+ // Redirect to 403 for authentication errors (401/403) to handle token refresh
320
+ // Redirect to 404 for other errors
321
+ const destination =
322
+ status === 403 || status === 401
323
+ ? `/pvt/account/403?from=${encodeURIComponent(`/pvt/account/orders/${context.params?.id}`)}`
324
+ : '/pvt/account/404'
332
325
 
333
326
  return {
334
327
  redirect: {
335
- destination: isForbidden ? '/pvt/account/403' : '/pvt/account/404',
328
+ destination,
336
329
  permanent: false,
337
330
  },
338
331
  }
@@ -16,7 +16,7 @@ import type {
16
16
  } from '@generated/graphql'
17
17
  import { default as AfterSection } from 'src/customizations/src/myAccount/extensions/orders/after'
18
18
  import { default as BeforeSection } from 'src/customizations/src/myAccount/extensions/orders/before'
19
- import type { MyAccountProps } from 'src/experimental/myAccountSeverSideProps'
19
+ import type { MyAccountProps } from 'src/experimental/myAccountServerSideProps'
20
20
  import { execute } from 'src/server'
21
21
  import { injectGlobalSections } from 'src/server/cms/global'
22
22
  import { getMyAccountRedirect } from 'src/utils/myAccountRedirect'
@@ -25,7 +25,6 @@ import { groupOrderStatusByLabel } from 'src/utils/userOrderStatus'
25
25
  import storeConfig from 'discovery.config'
26
26
  import { MyAccountListOrders } from 'src/components/account/orders/MyAccountListOrders'
27
27
  import { getIsRepresentative } from 'src/sdk/account/getIsRepresentative'
28
- import { validateUser } from 'src/sdk/account/validateUser'
29
28
  import PageProvider from 'src/sdk/overrides/PageProvider'
30
29
  import { extractStatusFromError } from 'src/utils/utilities'
31
30
 
@@ -131,17 +130,6 @@ export const getServerSideProps: GetServerSideProps<
131
130
  Record<string, string>,
132
131
  Locator
133
132
  > = async (context) => {
134
- const isValid = await validateUser(context)
135
-
136
- if (!isValid) {
137
- return {
138
- redirect: {
139
- destination: '/login',
140
- permanent: false,
141
- },
142
- }
143
- }
144
-
145
133
  const isRepresentative = getIsRepresentative({
146
134
  headers: context.req.headers as Record<string, string>,
147
135
  account: storeConfig.api.storeId,
@@ -225,11 +213,17 @@ export const getServerSideProps: GetServerSideProps<
225
213
  console.error(...listOrders.errors)
226
214
 
227
215
  const status = extractStatusFromError(listOrders.errors[0])
228
- const isForbidden = status === 403 || status === 401
216
+
217
+ // Redirect to 403 for authentication errors (401/403) to handle token refresh
218
+ // Redirect to 404 for other errors
219
+ const destination =
220
+ status === 403 || status === 401
221
+ ? `/pvt/account/403?from=${encodeURIComponent('/pvt/account/orders')}`
222
+ : '/pvt/account/404'
229
223
 
230
224
  return {
231
225
  redirect: {
232
- destination: isForbidden ? '/pvt/account/403' : '/pvt/account/404',
226
+ destination,
233
227
  permanent: false,
234
228
  },
235
229
  }
@@ -17,9 +17,8 @@ import type {
17
17
  } from '@generated/graphql'
18
18
  import { default as AfterSection } from 'src/customizations/src/myAccount/extensions/profile/after'
19
19
  import { default as BeforeSection } from 'src/customizations/src/myAccount/extensions/profile/before'
20
- import type { MyAccountProps } from 'src/experimental/myAccountSeverSideProps'
20
+ import type { MyAccountProps } from 'src/experimental/myAccountServerSideProps'
21
21
  import { getIsRepresentative } from 'src/sdk/account/getIsRepresentative'
22
- import { validateUser } from 'src/sdk/account/validateUser'
23
22
  import { injectGlobalSections } from 'src/server/cms/global'
24
23
  import { getMyAccountRedirect } from 'src/utils/myAccountRedirect'
25
24
 
@@ -33,7 +32,7 @@ const COMPONENTS: Record<string, ComponentType<any>> = {
33
32
  ...CUSTOM_COMPONENTS,
34
33
  }
35
34
 
36
- type ProfilePagePros = {
35
+ type ProfilePageProps = {
37
36
  accountProfile: {
38
37
  name: string | null
39
38
  email: string | null
@@ -46,7 +45,7 @@ export default function Profile({
46
45
  accountProfile,
47
46
  accountName,
48
47
  isRepresentative,
49
- }: ProfilePagePros) {
48
+ }: ProfilePageProps) {
50
49
  const { sections: globalSections, settings: globalSettings } =
51
50
  globalSectionsProp ?? {}
52
51
 
@@ -83,22 +82,6 @@ export const getServerSideProps: GetServerSideProps<
83
82
  Record<string, string>,
84
83
  Locator
85
84
  > = async (context) => {
86
- const isValid = await validateUser(context)
87
-
88
- if (!isValid) {
89
- return {
90
- redirect: {
91
- destination: '/login',
92
- permanent: false,
93
- },
94
- }
95
- }
96
-
97
- const isRepresentative = getIsRepresentative({
98
- headers: context.req.headers as Record<string, string>,
99
- account: storeConfig.api.storeId,
100
- })
101
-
102
85
  const { isFaststoreMyAccountEnabled, redirect } = getMyAccountRedirect({
103
86
  query: context.query,
104
87
  })
@@ -107,6 +90,11 @@ export const getServerSideProps: GetServerSideProps<
107
90
  return { redirect }
108
91
  }
109
92
 
93
+ const isRepresentative = getIsRepresentative({
94
+ headers: context.req.headers as Record<string, string>,
95
+ account: storeConfig.api.storeId,
96
+ })
97
+
110
98
  const [
111
99
  globalSectionsPromise,
112
100
  globalSectionsHeaderPromise,
@@ -131,8 +119,13 @@ export const getServerSideProps: GetServerSideProps<
131
119
  console.error(...profile.errors)
132
120
 
133
121
  const statusCode: number = (profile.errors[0] as any)?.extensions?.status
122
+
123
+ // Redirect to 403 for authentication errors (401/403) to handle token refresh
124
+ // Redirect to 404 for other errors
134
125
  const destination: string =
135
- statusCode === 403 ? '/pvt/account/403' : '/pvt/account/404'
126
+ statusCode === 401 || statusCode === 403
127
+ ? `/pvt/account/403?from=${encodeURIComponent('/pvt/account/profile')}`
128
+ : '/pvt/account/404'
136
129
 
137
130
  return {
138
131
  redirect: {
@@ -17,13 +17,12 @@ import type {
17
17
  } from '@generated/graphql'
18
18
  import { default as AfterSection } from 'src/customizations/src/myAccount/extensions/security/after'
19
19
  import { default as BeforeSection } from 'src/customizations/src/myAccount/extensions/security/before'
20
- import type { MyAccountProps } from 'src/experimental/myAccountSeverSideProps'
20
+ import type { MyAccountProps } from 'src/experimental/myAccountServerSideProps'
21
21
  import { getIsRepresentative } from 'src/sdk/account/getIsRepresentative'
22
22
  import { execute } from 'src/server'
23
23
  import { injectGlobalSections } from 'src/server/cms/global'
24
24
  import { getMyAccountRedirect } from 'src/utils/myAccountRedirect'
25
25
 
26
- import { validateUser } from 'src/sdk/account/validateUser'
27
26
  import PageProvider from 'src/sdk/overrides/PageProvider'
28
27
 
29
28
  import storeConfig from 'discovery.config'
@@ -83,17 +82,6 @@ export const getServerSideProps: GetServerSideProps<
83
82
  Record<string, string>,
84
83
  Locator
85
84
  > = async (context) => {
86
- const isValid = await validateUser(context)
87
-
88
- if (!isValid) {
89
- return {
90
- redirect: {
91
- destination: '/login',
92
- permanent: false,
93
- },
94
- }
95
- }
96
-
97
85
  const isRepresentative = getIsRepresentative({
98
86
  headers: context.req.headers as Record<string, string>,
99
87
  account: storeConfig.api.storeId,
@@ -130,8 +118,13 @@ export const getServerSideProps: GetServerSideProps<
130
118
  if (security.errors) {
131
119
  console.error(...security.errors)
132
120
  const statusCode: number = (security.errors[0] as any)?.extensions?.status
121
+
122
+ // Redirect to 403 for authentication errors (401/403) to handle token refresh
123
+ // Redirect to 404 for other errors
133
124
  const destination: string =
134
- statusCode === 403 ? '/pvt/account/403' : '/pvt/account/404'
125
+ statusCode === 401 || statusCode === 403
126
+ ? `/pvt/account/403?from=${encodeURIComponent('/pvt/account/security')}`
127
+ : '/pvt/account/404'
135
128
 
136
129
  return {
137
130
  redirect: {
@@ -19,9 +19,8 @@ import storeConfig from 'discovery.config'
19
19
  import MyAccountUserDetails from 'src/components/account/MyAccountUserDetails/MyAccountUserDetails'
20
20
  import { default as AfterSection } from 'src/customizations/src/myAccount/extensions/user-details/after'
21
21
  import { default as BeforeSection } from 'src/customizations/src/myAccount/extensions/user-details/before'
22
- import type { MyAccountProps } from 'src/experimental/myAccountSeverSideProps'
22
+ import type { MyAccountProps } from 'src/experimental/myAccountServerSideProps'
23
23
  import { getIsRepresentative } from 'src/sdk/account/getIsRepresentative'
24
- import { validateUser } from 'src/sdk/account/validateUser'
25
24
  import PageProvider from 'src/sdk/overrides/PageProvider'
26
25
  import { execute } from 'src/server'
27
26
  import { injectGlobalSections } from 'src/server/cms/global'
@@ -88,17 +87,6 @@ export const getServerSideProps: GetServerSideProps<
88
87
  Record<string, string>,
89
88
  Locator
90
89
  > = async (context) => {
91
- const isValid = await validateUser(context)
92
-
93
- if (!isValid) {
94
- return {
95
- redirect: {
96
- destination: '/login',
97
- permanent: false,
98
- },
99
- }
100
- }
101
-
102
90
  const isRepresentative = getIsRepresentative({
103
91
  headers: context.req.headers as Record<string, string>,
104
92
  account: storeConfig.api.storeId,
@@ -153,8 +141,13 @@ export const getServerSideProps: GetServerSideProps<
153
141
 
154
142
  const statusCode: number = (userDetails.errors[0] as any)?.extensions
155
143
  ?.status
144
+
145
+ // Redirect to 403 for authentication errors (401/403) to handle token refresh
146
+ // Redirect to 404 for other errors
156
147
  const destination: string =
157
- statusCode === 403 ? '/pvt/account/403' : '/pvt/account/404'
148
+ statusCode === 401 || statusCode === 403
149
+ ? `/pvt/account/403?from=${encodeURIComponent('/pvt/account/user-details')}`
150
+ : '/pvt/account/404'
158
151
 
159
152
  return {
160
153
  redirect: {
@@ -10,15 +10,16 @@ interface GetMyAccountRouteParams {
10
10
  routes: Route[]
11
11
  }
12
12
 
13
- export const USER_DETAILS_ROUTE = '/pvt/account/user-details'
13
+ export const PROFILE_ROUTE = '/pvt/account/profile'
14
14
  export const ORDERS_ROUTE = '/pvt/account/orders'
15
+ export const USER_DETAILS_ROUTE = '/pvt/account/user-details'
15
16
  export const SECURITY_ROUTE = '/pvt/account/security'
16
17
 
17
18
  // This is the default route list for My Account, we should add then as the feature is implemented
18
19
  const DEFAULT_ROUTES: Route[] = [
19
20
  {
20
21
  title: 'Profile',
21
- route: '/pvt/account/profile',
22
+ route: PROFILE_ROUTE,
22
23
  },
23
24
  {
24
25
  title: 'Orders',
@@ -0,0 +1,50 @@
1
+ import discoveryConfig from 'discovery.config'
2
+ import fetch from 'isomorphic-unfetch'
3
+ import { sanitizeHost } from 'src/utils/utilities'
4
+
5
+ const REFRESH_TOKEN_URL = `${discoveryConfig.storeUrl}/api/vtexid/refreshtoken/webstore`
6
+
7
+ export interface RefreshTokenResponse {
8
+ status?: string
9
+ refreshAfter?: string
10
+ }
11
+
12
+ async function fetchWithRetry(
13
+ url: RequestInfo | URL,
14
+ init?: RequestInit,
15
+ maxRetries = 3
16
+ ): Promise<RefreshTokenResponse | undefined> {
17
+ for (let i = 0; i < maxRetries; i++) {
18
+ try {
19
+ const res = await fetch(url, init)
20
+ if (res.status !== 200) continue
21
+
22
+ const data = await res.json()
23
+ return data
24
+ } catch {}
25
+ }
26
+
27
+ return undefined
28
+ }
29
+
30
+ export const refreshTokenRequest = async (): Promise<
31
+ RefreshTokenResponse | undefined
32
+ > => {
33
+ const headers: HeadersInit = {
34
+ 'content-type': 'application/json',
35
+ Host: `${sanitizeHost(discoveryConfig.storeUrl)}`,
36
+ }
37
+
38
+ return await fetchWithRetry(REFRESH_TOKEN_URL, {
39
+ credentials: 'include',
40
+ headers,
41
+ body: JSON.stringify({}),
42
+ method: 'POST',
43
+ })
44
+ }
45
+
46
+ export const isRefreshTokenSuccessful = (
47
+ result: RefreshTokenResponse | undefined
48
+ ): boolean => {
49
+ return result?.status?.toLowerCase?.() === 'success'
50
+ }
@@ -0,0 +1,67 @@
1
+ import { useEffect, useState } from 'react'
2
+ import { sessionStore } from '../session'
3
+ import { isRefreshTokenSuccessful, refreshTokenRequest } from './refreshToken'
4
+
5
+ export const useRefreshToken = (
6
+ needsRefreshToken?: boolean,
7
+ fromPage?: string
8
+ ) => {
9
+ const [shouldShow403, setShouldShow403] = useState(false)
10
+
11
+ useEffect(() => {
12
+ const handleRefreshTokenAndUpdateSession = async () => {
13
+ if (!needsRefreshToken) return
14
+
15
+ const currentSession = sessionStore.read() ?? sessionStore.readInitial()
16
+
17
+ try {
18
+ const result = await refreshTokenRequest()
19
+
20
+ if (isRefreshTokenSuccessful(result)) {
21
+ // Update session with new refreshAfter timestamp
22
+ const refreshAfter = String(
23
+ Math.floor(new Date(result?.refreshAfter).getTime() / 1000)
24
+ )
25
+
26
+ sessionStore.set({
27
+ ...currentSession,
28
+ refreshAfter,
29
+ })
30
+
31
+ // Refresh token successful, go back to the page that called 403
32
+ // Forces complete navigation through getServerSideProps by adding a cache-busting parameter
33
+ const previousPage = fromPage || '/pvt/account'
34
+
35
+ // Add timestamp to force server-side navigation and avoid browser cache
36
+ const url = new URL(previousPage, window.location.origin)
37
+ url.searchParams.set('_refresh', Date.now().toString())
38
+ window.location.href = url.toString()
39
+ } else {
40
+ // If refresh token failed, set refreshAfter to now + 1 hour
41
+ sessionStore.set({
42
+ ...currentSession,
43
+ refreshAfter: String(Math.floor(Date.now() / 1000) + 1 * 60 * 60), // now + 1 hour
44
+ })
45
+
46
+ setShouldShow403(true)
47
+ }
48
+ } catch (error) {
49
+ console.error('Error during refresh token process:', error)
50
+
51
+ // Set refreshAfter to postpone future requests and redirect to login
52
+ sessionStore.set({
53
+ ...currentSession,
54
+ refreshAfter: String(Math.floor(Date.now() / 1000) + 1 * 60 * 60), // now + 1 hour
55
+ })
56
+
57
+ setShouldShow403(true)
58
+ }
59
+ }
60
+
61
+ handleRefreshTokenAndUpdateSession()
62
+ }, [needsRefreshToken, fromPage])
63
+
64
+ return {
65
+ shouldShow403,
66
+ }
67
+ }
@@ -15,18 +15,62 @@ const query = gql(`
15
15
  `)
16
16
 
17
17
  export async function validateUser(context: GetServerSidePropsContext) {
18
- const validateUserResult = await execute<
19
- ValidateUserQueryVariables,
20
- ValidateUserQuery
21
- >(
22
- {
23
- variables: {},
24
- operation: query,
25
- },
26
- {
27
- headers: { ...context.req.headers },
18
+ try {
19
+ const validateUserResult = await execute<
20
+ ValidateUserQueryVariables,
21
+ ValidateUserQuery
22
+ >(
23
+ {
24
+ variables: {},
25
+ operation: query,
26
+ },
27
+ {
28
+ headers: { ...context.req.headers },
29
+ }
30
+ )
31
+
32
+ if (validateUserResult?.errors && validateUserResult.errors.length > 0) {
33
+ const hasUnauthorizedError = validateUserResult.errors.some(
34
+ (error: any) => {
35
+ const statusCode = error?.extensions?.status || error?.status
36
+ const errorType = error?.extensions?.type
37
+ return statusCode === 401 || errorType === 'UnauthorizedError'
38
+ }
39
+ )
40
+
41
+ if (hasUnauthorizedError) {
42
+ return {
43
+ isValid: false,
44
+ needsRefresh: true,
45
+ }
46
+ }
47
+
48
+ // For other errors, return as invalid without refresh
49
+ return {
50
+ isValid: false,
51
+ needsRefresh: false,
52
+ }
53
+ }
54
+
55
+ return {
56
+ isValid: !!validateUserResult?.data?.validateUser?.isValid,
57
+ needsRefresh: false,
28
58
  }
29
- )
59
+ } catch (error: any) {
60
+ const statusCode = error?.extensions?.status || error?.status
61
+ const errorType = error?.extensions?.type
30
62
 
31
- return validateUserResult?.data?.validateUser?.isValid
63
+ if (statusCode === 401 || errorType === 'UnauthorizedError') {
64
+ return {
65
+ isValid: false,
66
+ needsRefresh: true,
67
+ }
68
+ }
69
+
70
+ // For other errors, return as invalid without refresh
71
+ return {
72
+ isValid: false,
73
+ needsRefresh: false,
74
+ }
75
+ }
32
76
  }
@@ -1,4 +1,10 @@
1
1
  import { useSearch } from '@faststore/sdk'
2
+ import {
3
+ PICKUP_IN_POINT_FACET_VALUE,
4
+ PICKUP_POINT_FACET_KEY,
5
+ SHIPPING_FACET_KEY,
6
+ useDeliveryPromiseContext,
7
+ } from '.'
2
8
 
3
9
  export function useDeliveryPromiseFacets() {
4
10
  const { state: searchState } = useSearch()
@@ -14,6 +20,25 @@ export function useDeliveryPromiseFacets() {
14
20
  }
15
21
  }
16
22
 
23
+ export function useDeliveryPromiseGlobalFacets() {
24
+ const { globalPickupPoint } = useDeliveryPromiseContext()
25
+
26
+ return {
27
+ globalDeliveryFacets: globalPickupPoint
28
+ ? [
29
+ {
30
+ key: PICKUP_POINT_FACET_KEY,
31
+ value: globalPickupPoint.id,
32
+ },
33
+ {
34
+ key: SHIPPING_FACET_KEY,
35
+ value: PICKUP_IN_POINT_FACET_VALUE,
36
+ },
37
+ ]
38
+ : [],
39
+ }
40
+ }
41
+
17
42
  const pickByKey =
18
43
  (keys: Array<string>) =>
19
44
  ({ key }: { key: string }) =>
@@ -1,6 +1,5 @@
1
1
  import type { Session } from '@faststore/sdk'
2
2
  import { createSessionStore } from '@faststore/sdk'
3
- import fetch from 'isomorphic-unfetch'
4
3
  import { useMemo } from 'react'
5
4
 
6
5
  import { gql } from '@generated'
@@ -8,17 +7,17 @@ import type {
8
7
  ValidateSessionMutation,
9
8
  ValidateSessionMutationVariables,
10
9
  } from '@generated/graphql'
11
- import discoveryConfig from 'discovery.config'
12
10
  import deepEqual from 'fast-deep-equal'
13
- import { sanitizeHost } from 'src/utils/utilities'
14
11
  import storeConfig from '../../../discovery.config'
12
+ import {
13
+ isRefreshTokenSuccessful,
14
+ refreshTokenRequest,
15
+ } from '../account/refreshToken'
15
16
  import { cartStore } from '../cart'
16
17
  import { request } from '../graphql/request'
17
18
  import { createValidationStore, useStore } from '../useStore'
18
19
  import { getPostalCode } from '../userLocation/index'
19
20
 
20
- const REFRESH_TOKEN_URL = `${discoveryConfig.storeUrl}/api/vtexid/refreshtoken/webstore`
21
-
22
21
  export const mutation = gql(`
23
22
  mutation ValidateSession($session: IStoreSession!, $search: String!) {
24
23
  validateSession(session: $session, search: $search) {
@@ -112,19 +111,9 @@ export const validateSession = async (session: Session) => {
112
111
  error?.status === 401 && storeConfig.experimental?.refreshToken
113
112
 
114
113
  if (shouldRefreshToken) {
115
- const headers: HeadersInit = {
116
- 'content-type': 'application/json',
117
- Host: `${sanitizeHost(discoveryConfig.storeUrl)}`,
118
- }
114
+ const result = await refreshTokenRequest()
119
115
 
120
- const result = await fetchWithRetry(REFRESH_TOKEN_URL, {
121
- credentials: 'include',
122
- headers,
123
- body: JSON.stringify({}),
124
- method: 'POST',
125
- })
126
-
127
- if (result?.status?.toLowerCase?.() === 'success') {
116
+ if (isRefreshTokenSuccessful(result)) {
128
117
  const refreshAfter = String(
129
118
  Math.floor(new Date(result?.refreshAfter).getTime() / 1000)
130
119
  )
@@ -197,23 +186,3 @@ export const useSession = ({ filter }: SessionOptions = { filter: true }) => {
197
186
  [isValidating, session, channel]
198
187
  )
199
188
  }
200
-
201
- async function fetchWithRetry(
202
- url: RequestInfo | URL,
203
- init?: RequestInit,
204
- maxRetries = 3
205
- ) {
206
- for (let i = 0; i < maxRetries; i++) {
207
- try {
208
- const res = await fetch(url, init)
209
- if (res.status !== 200) continue
210
-
211
- const data = await res.json()
212
- if (data.status?.toLowerCase?.() === 'success') {
213
- return data
214
- }
215
- } catch {}
216
- }
217
-
218
- return
219
- }
@@ -11,6 +11,8 @@ import { useParserCache } from '@envelop/parser-cache'
11
11
  import { useValidationCache } from '@envelop/validation-cache'
12
12
  import type { CacheControl, Maybe } from '@faststore/api'
13
13
  import {
14
+ authDirective,
15
+ cacheControlDirective,
14
16
  BadRequestError,
15
17
  getContextFactory,
16
18
  getResolvers,
@@ -61,10 +63,14 @@ function getFinalAPISchema() {
61
63
  const generatedSchema = loadGeneratedSchema()
62
64
  const nativeResolvers = getResolvers(apiOptions)
63
65
 
64
- return makeExecutableSchema({
66
+ const schema = makeExecutableSchema({
65
67
  typeDefs: generatedSchema,
66
68
  resolvers: [nativeResolvers, vtexExtensionsResolvers, thirdPartyResolvers],
67
69
  })
70
+
71
+ // Apply directive transformations
72
+ const directives = [cacheControlDirective, authDirective]
73
+ return directives.reduce((s, d) => d.transformer(s), schema)
68
74
  }
69
75
 
70
76
  export const getEnvelop = async () =>
@@ -108,10 +108,3 @@ export const buildFormData = (
108
108
 
109
109
  export const toArray = <T>(x: T[] | T | undefined) =>
110
110
  Array.isArray(x) ? x : x ? [x] : []
111
-
112
- /**
113
- * Array merging strategy from deepmerge that makes source arrays overwrite destination array
114
- *
115
- * @see https://www.npmjs.com/package/deepmerge
116
- */
117
- export const overwriteMerge = (_: any[], sourceArray: any[]) => sourceArray
@@ -1 +0,0 @@
1
- "use strict";exports.id=7947,exports.ids=[7947],exports.modules={7947:(e,r,t)=>{t.a(e,async(e,n)=>{try{t.d(r,{h:()=>validateUser});var a=t(25487),o=t(99563),i=e([o]);function ownKeys(e,r){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);r&&(n=n.filter(function(r){return Object.getOwnPropertyDescriptor(e,r).enumerable})),t.push.apply(t,n)}return t}o=(i.then?(await i)():i)[0];let c=a.fW;async function validateUser(e){let r=await (0,o.h)({variables:{},operation:c},{headers:function(e){for(var r=1;r<arguments.length;r++){var t=null!=arguments[r]?arguments[r]:{};r%2?ownKeys(Object(t),!0).forEach(function(r){var n;n=t[r],r in e?Object.defineProperty(e,r,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[r]=n}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):ownKeys(Object(t)).forEach(function(r){Object.defineProperty(e,r,Object.getOwnPropertyDescriptor(t,r))})}return e}({},e.req.headers)});return r?.data?.validateUser?.isValid}n()}catch(e){n(e)}})}};