@faststore/core 3.99.0-dev.2 → 3.99.0-dev.5

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 (58) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +38 -38
  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 +6 -6
  12. package/.next/routes-manifest.json +1 -1
  13. package/.next/server/chunks/1741.js +2 -2
  14. package/.next/server/chunks/5133.js +1 -1
  15. package/.next/server/chunks/5212.js +1 -1
  16. package/.next/server/chunks/7121.js +1 -1
  17. package/.next/server/chunks/8971.js +5 -4
  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/api/graphql.js +3 -3
  22. package/.next/server/pages/en-US/404.html +2 -2
  23. package/.next/server/pages/en-US/500.html +2 -2
  24. package/.next/server/pages/en-US/checkout.html +2 -2
  25. package/.next/server/pages/en-US/login.html +2 -2
  26. package/.next/server/pages/en-US/s.html +2 -2
  27. package/.next/server/pages/en-US.html +2 -2
  28. package/.next/server/pages/pvt/account/403.js +1 -1
  29. package/.next/server/pages-manifest.json +1 -1
  30. package/.next/static/{OZ8Lk7JM4bzozJAoz6cqt → T_jYtdcAXYxkLOxLasDa4}/_buildManifest.js +1 -1
  31. package/.next/static/chunks/2221-3b8af0bc108994c0.js +2 -0
  32. package/.next/static/chunks/{2927.5a79877943a6bf7c.js → 2927.23bae2c79f0ac0f3.js} +1 -1
  33. package/.next/static/chunks/{UIToast.19a8664c01a00d3a.js → UIToast.de15325248043ce5.js} +1 -1
  34. package/.next/static/chunks/pages/_app-ec4b3e60344d1bab.js +1 -0
  35. package/.next/static/chunks/pages/pvt/account/{403-d44b70591d2c86dc.js → 403-f3cb4fbbbb801a00.js} +1 -1
  36. package/.next/static/chunks/{webpack-fc37ce1526507eaf.js → webpack-da79ff7bed51ca28.js} +1 -1
  37. package/.next/trace +143 -142
  38. package/.turbo/turbo-build.log +16 -16
  39. package/.turbo/turbo-test.log +11 -9
  40. package/@generated/gql.ts +2 -2
  41. package/@generated/graphql.ts +6 -1
  42. package/@generated/persisted-documents.json +1 -1
  43. package/@generated/schema.graphql +2 -0
  44. package/CHANGELOG.md +19 -0
  45. package/discovery.config.default.js +1 -0
  46. package/package.json +7 -7
  47. package/src/components/account/MyAccountDrawer/OrganizationDrawer/useReloadAfterLogoutReturn.ts +6 -1
  48. package/src/pages/api/graphql.ts +10 -22
  49. package/src/sdk/account/useRefreshToken.ts +3 -14
  50. package/src/sdk/cart/index.ts +12 -2
  51. package/src/sdk/session/index.ts +106 -59
  52. package/src/sdk/session/storageKeys.ts +2 -0
  53. package/src/sdk/useStore.ts +7 -1
  54. package/src/utils/validateSessionRefreshToken.ts +29 -0
  55. package/test/utils/validateSessionRefreshToken.test.ts +69 -0
  56. package/.next/static/chunks/2221-b085ff74b21a2c2c.js +0 -2
  57. package/.next/static/chunks/pages/_app-be3fecd37c6b273c.js +0 -1
  58. /package/.next/static/{OZ8Lk7JM4bzozJAoz6cqt → T_jYtdcAXYxkLOxLasDa4}/_ssgManifest.js +0 -0
@@ -1,23 +1,23 @@
1
1
 
2
- > @faststore/core@3.99.0-dev.1 prebuild /home/runner/work/faststore/faststore/packages/core
2
+ > @faststore/core@3.99.0-dev.4 prebuild /home/runner/work/faststore/faststore/packages/core
3
3
  > na run partytown && na run generate
4
4
 
5
5
 
6
- > @faststore/core@3.99.0-dev.1 partytown /home/runner/work/faststore/faststore/packages/core
6
+ > @faststore/core@3.99.0-dev.4 partytown /home/runner/work/faststore/faststore/packages/core
7
7
  > partytown copylib ./public/~partytown
8
8
 
9
9
  Partytown lib copied to: /home/runner/work/faststore/faststore/packages/core/public/~partytown
10
10
 
11
- > @faststore/core@3.99.0-dev.1 generate /home/runner/work/faststore/faststore/packages/core
11
+ > @faststore/core@3.99.0-dev.4 generate /home/runner/work/faststore/faststore/packages/core
12
12
  > na run generate:schema && na run generate:codegen && na run format:generated
13
13
 
14
14
 
15
- > @faststore/core@3.99.0-dev.1 generate:schema /home/runner/work/faststore/faststore/packages/core
15
+ > @faststore/core@3.99.0-dev.4 generate:schema /home/runner/work/faststore/faststore/packages/core
16
16
  > tsx src/server/generator/generateGraphQLSchemaFile.ts
17
17
 
18
18
  Schema GraphQL file generated successfully
19
19
 
20
- > @faststore/core@3.99.0-dev.1 generate:codegen /home/runner/work/faststore/faststore/packages/core
20
+ > @faststore/core@3.99.0-dev.4 generate:codegen /home/runner/work/faststore/faststore/packages/core
21
21
  > graphql-codegen
22
22
 
23
23
  [STARTED] Parse Configuration
@@ -37,11 +37,11 @@ Running lifecycle hook "afterStart" scripts...
37
37
  [CLI] Loading Documents
38
38
  [CLI] Generating output
39
39
 
40
- > @faststore/core@3.99.0-dev.1 format:generated /home/runner/work/faststore/faststore/packages/core
40
+ > @faststore/core@3.99.0-dev.4 format:generated /home/runner/work/faststore/faststore/packages/core
41
41
  > prettier --write "@generated/**/*.{ts,js,tsx,jsx,json}" --loglevel error
42
42
 
43
43
 
44
- > @faststore/core@3.99.0-dev.1 build /home/runner/work/faststore/faststore/packages/core
44
+ > @faststore/core@3.99.0-dev.4 build /home/runner/work/faststore/faststore/packages/core
45
45
  > next build
46
46
 
47
47
  ⚠ No build cache found. Please configure build caching for faster rebuilds. Read more: https://nextjs.org/docs/messages/no-cache
@@ -59,23 +59,23 @@ https://nextjs.org/telemetry
59
59
  Collecting page data ...
60
60
  Generating static pages (0/6) ...
61
61
 
62
62
  Generating static pages (1/6)
63
-
64
63
  Generating static pages (2/6)
65
64
  Warning: Dynamic Content not found for the page: home. Refer to the Dynamic Content documentation at https://developers.vtex.com/docs/guides/faststore/dynamic-content-overview for mapping the page and the corresponding data-fetching function.
65
+
66
66
  Generating static pages (2/6)
67
67
 
68
68
  Generating static pages (4/6)
69
69
 
70
70
  ✓ Generating static pages (6/6)
71
71
  Finalizing page optimization ...
72
72
  Collecting build traces ...
73
73
 
74
74
  Route (pages) Size First Load JS
75
- ┌ ● / 7.39 kB 165 kB
75
+ ┌ ● / 7.39 kB 166 kB
76
76
  ├ └ css/02259c549b2179f2.css 3.1 kB
77
77
  ├ /_app 0 B 109 kB
78
78
  ├ ● /[...slug] 2.55 kB 175 kB
79
- ├ ● /[slug]/p 97.3 kB 255 kB
79
+ ├ ● /[slug]/p 97.3 kB 256 kB
80
80
  ├ ├ css/e68c313e4ce2fd1b.css 6.34 kB
81
81
  ├ └ css/9b4c21dcd3bc4a17.css 16.7 kB
82
- ├ ○ /404 1.58 kB 159 kB
83
- ├ ● /500 1.57 kB 159 kB
82
+ ├ ○ /404 1.58 kB 160 kB
83
+ ├ ● /500 1.57 kB 160 kB
84
84
  ├ λ /api/fs/logout 0 B 109 kB
85
85
  ├ λ /api/graphql 0 B 109 kB
86
86
  ├ λ /api/health/live 0 B 109 kB
@@ -85,13 +85,13 @@ Route (pages) Size First Load JS
85
85
  ├ ● /login 1.7 kB 160 kB
86
86
  ├ λ /pvt/account 245 B 109 kB
87
87
  ├ ● /pvt/account/[...unknown] 286 B 109 kB
88
- ├ λ /pvt/account/403 2.99 kB 161 kB
88
+ ├ λ /pvt/account/403 2.98 kB 161 kB
89
89
  ├ └ css/094ad5cc00c74e64.css 4.82 kB
90
90
  ├ λ /pvt/account/404 2.18 kB 160 kB
91
91
  ├ └ css/72f154a00f1cf621.css 4.88 kB
92
92
  ├ λ /pvt/account/orders 9.89 kB 168 kB
93
93
  ├ └ css/4b2af1aaeb961465.css 14.4 kB
94
- ├ λ /pvt/account/orders/[id] 13.6 kB 171 kB
94
+ ├ λ /pvt/account/orders/[id] 13.6 kB 172 kB
95
95
  ├ └ css/482e6fa149885bc2.css 13.9 kB
96
96
  ├ λ /pvt/account/profile 1.99 kB 160 kB
97
97
  ├ └ css/5cd37b7508dd5190.css 4.56 kB
@@ -103,8 +103,8 @@ Route (pages) Size First Load JS
103
103
  + First Load JS shared by all 118 kB
104
104
  ├ chunks/framework-d514426edf885c68.js 45.4 kB
105
105
  ├ chunks/main-edeb2c6a5c48bb3f.js 33.2 kB
106
- ├ chunks/pages/_app-be3fecd37c6b273c.js 26.3 kB
107
- ├ chunks/webpack-fc37ce1526507eaf.js 3.88 kB
106
+ ├ chunks/pages/_app-ec4b3e60344d1bab.js 26.6 kB
107
+ ├ chunks/webpack-da79ff7bed51ca28.js 3.88 kB
108
108
  └ css/8a513f4b536b0ab0.css 9.18 kB
109
109
 
110
110
  λ (Server) server-side renders at runtime (uses getInitialProps or getServerSideProps)
@@ -1,16 +1,18 @@
1
1
 
2
- > @faststore/core@3.99.0-dev.1 test /home/runner/work/faststore/faststore/packages/core
2
+ > @faststore/core@3.99.0-dev.4 test /home/runner/work/faststore/faststore/packages/core
3
3
  > jest
4
4
 
5
- PASS test/utils/multipleTemplates.test.ts (12.866 s)
6
- PASS test/utils/clearCookies.test.ts (12.663 s)
7
- PASS test/utils/cookieCacheBusting.test.ts (15.452 s)
5
+ PASS test/utils/multipleTemplates.test.ts (11.101 s)
6
+ PASS test/utils/clearCookies.test.ts (10.671 s)
8
7
  PASS test/server/cms/global.test.ts
9
- PASS test/server/cms/index.test.ts (6.552 s)
10
- PASS test/server/index.test.ts (13.304 s)
8
+ PASS test/utils/cookieCacheBusting.test.ts (14.531 s)
9
+ PASS test/utils/validateSessionRefreshToken.test.ts
10
+ PASS test/server/cms/index.test.ts (6.215 s)
11
+ PASS test/server/index.test.ts (12.984 s)
12
+ A worker process has failed to exit gracefully and has been force exited. This is likely caused by tests leaking due to improper teardown. Try running with --detectOpenHandles to find leaks. Active timers can also cause this, ensure that .unref() was called on them.
11
13
 
12
- Test Suites: 6 passed, 6 total
13
- Tests: 52 passed, 52 total
14
+ Test Suites: 7 passed, 7 total
15
+ Tests: 58 passed, 58 total
14
16
  Snapshots: 0 total
15
- Time: 27.288 s
17
+ Time: 25.438 s
16
18
  Ran all test suites.
package/@generated/gql.ts CHANGED
@@ -62,7 +62,7 @@ const documents = {
62
62
  types.ProcessOrderAuthorizationMutationDocument,
63
63
  '\n query ValidateUser {\n validateUser {\n isValid\n }\n }\n':
64
64
  types.ValidateUserDocument,
65
- '\n mutation ValidateCartMutation($cart: IStoreCart!, $session: IStoreSession!) {\n validateCart(cart: $cart, session: $session) {\n order {\n orderNumber\n acceptedOffer {\n ...CartItem\n }\n shouldSplitItem\n }\n messages {\n ...CartMessage\n }\n }\n }\n\n fragment CartMessage on StoreCartMessage {\n text\n status\n }\n\n fragment CartItem on StoreOffer {\n seller {\n identifier\n }\n quantity\n price\n priceWithTaxes\n listPrice\n listPriceWithTaxes\n itemOffered {\n ...CartProductItem\n }\n }\n\n fragment CartProductItem on StoreProduct {\n sku\n name\n unitMultiplier\n image {\n url\n alternateName\n }\n brand {\n name\n }\n isVariantOf {\n productGroupID\n name\n skuVariants {\n activeVariations\n slugsMap\n availableVariations\n }\n }\n gtin\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n }\n':
65
+ '\n mutation ValidateCartMutation($cart: IStoreCart!, $session: IStoreSession!) {\n validateCart(cart: $cart, session: $session) {\n order {\n orderNumber\n acceptedOffer {\n ...CartItem\n }\n shouldSplitItem\n }\n messages {\n ...CartMessage\n }\n }\n }\n\n fragment CartMessage on StoreCartMessage {\n text\n status\n }\n\n fragment CartItem on StoreOffer {\n seller {\n identifier\n }\n quantity\n price\n priceWithTaxes\n listPrice\n listPriceWithTaxes\n isGift\n itemOffered {\n ...CartProductItem\n }\n }\n\n fragment CartProductItem on StoreProduct {\n sku\n name\n unitMultiplier\n image {\n url\n alternateName\n }\n brand {\n name\n }\n isVariantOf {\n productGroupID\n name\n skuVariants {\n activeVariations\n slugsMap\n availableVariations\n }\n }\n gtin\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n }\n':
66
66
  types.ValidateCartMutationDocument,
67
67
  '\n query ClientPickupPointsQuery(\n $geoCoordinates: IStoreGeoCoordinates\n ) {\n pickupPoints(geoCoordinates: $geoCoordinates) {\n pickupPointDistances {\n pickupId\n distance\n pickupName\n isActive\n address {\n city\n state\n number\n postalCode\n street\n }\n }\n }\n }\n':
68
68
  types.ClientPickupPointsQueryDocument,
@@ -250,7 +250,7 @@ export function gql(
250
250
  * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
251
251
  */
252
252
  export function gql(
253
- source: '\n mutation ValidateCartMutation($cart: IStoreCart!, $session: IStoreSession!) {\n validateCart(cart: $cart, session: $session) {\n order {\n orderNumber\n acceptedOffer {\n ...CartItem\n }\n shouldSplitItem\n }\n messages {\n ...CartMessage\n }\n }\n }\n\n fragment CartMessage on StoreCartMessage {\n text\n status\n }\n\n fragment CartItem on StoreOffer {\n seller {\n identifier\n }\n quantity\n price\n priceWithTaxes\n listPrice\n listPriceWithTaxes\n itemOffered {\n ...CartProductItem\n }\n }\n\n fragment CartProductItem on StoreProduct {\n sku\n name\n unitMultiplier\n image {\n url\n alternateName\n }\n brand {\n name\n }\n isVariantOf {\n productGroupID\n name\n skuVariants {\n activeVariations\n slugsMap\n availableVariations\n }\n }\n gtin\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n }\n'
253
+ source: '\n mutation ValidateCartMutation($cart: IStoreCart!, $session: IStoreSession!) {\n validateCart(cart: $cart, session: $session) {\n order {\n orderNumber\n acceptedOffer {\n ...CartItem\n }\n shouldSplitItem\n }\n messages {\n ...CartMessage\n }\n }\n }\n\n fragment CartMessage on StoreCartMessage {\n text\n status\n }\n\n fragment CartItem on StoreOffer {\n seller {\n identifier\n }\n quantity\n price\n priceWithTaxes\n listPrice\n listPriceWithTaxes\n isGift\n itemOffered {\n ...CartProductItem\n }\n }\n\n fragment CartProductItem on StoreProduct {\n sku\n name\n unitMultiplier\n image {\n url\n alternateName\n }\n brand {\n name\n }\n isVariantOf {\n productGroupID\n name\n skuVariants {\n activeVariations\n slugsMap\n availableVariations\n }\n }\n gtin\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n }\n'
254
254
  ): typeof import('./graphql').ValidateCartMutationDocument
255
255
  /**
256
256
  * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
@@ -1348,6 +1348,8 @@ export type StoreMarketingData = {
1348
1348
  export type StoreOffer = {
1349
1349
  /** Offer item availability. */
1350
1350
  availability: Scalars['String']['output']
1351
+ /** Whether this offer is a gift (e.g. free promotional item). */
1352
+ isGift: Maybe<Scalars['Boolean']['output']>
1351
1353
  /** Offer item condition. */
1352
1354
  itemCondition: Scalars['String']['output']
1353
1355
  /** Information on the item being offered. */
@@ -3143,6 +3145,7 @@ export type ValidateCartMutationMutation = {
3143
3145
  priceWithTaxes: number
3144
3146
  listPrice: number
3145
3147
  listPriceWithTaxes: number
3148
+ isGift: boolean | null
3146
3149
  seller: { identifier: string }
3147
3150
  itemOffered: {
3148
3151
  sku: string
@@ -3181,6 +3184,7 @@ export type CartItemFragment = {
3181
3184
  priceWithTaxes: number
3182
3185
  listPrice: number
3183
3186
  listPriceWithTaxes: number
3187
+ isGift: boolean | null
3184
3188
  seller: { identifier: string }
3185
3189
  itemOffered: {
3186
3190
  sku: string
@@ -4390,6 +4394,7 @@ export const CartItemFragmentDoc = new TypedDocumentString(
4390
4394
  priceWithTaxes
4391
4395
  listPrice
4392
4396
  listPriceWithTaxes
4397
+ isGift
4393
4398
  itemOffered {
4394
4399
  ...CartProductItem
4395
4400
  }
@@ -4536,7 +4541,7 @@ export const ValidateUserDocument = {
4536
4541
  export const ValidateCartMutationDocument = {
4537
4542
  __meta__: {
4538
4543
  operationName: 'ValidateCartMutation',
4539
- operationHash: 'c2b3f8bff73ebf6ac79d758c66cabbc21ba9fcc0',
4544
+ operationHash: '32c15f8888ca34f223def7972b7f19090808435a',
4540
4545
  },
4541
4546
  } as unknown as TypedDocumentString<
4542
4547
  ValidateCartMutationMutation,
@@ -10,7 +10,7 @@
10
10
  "e2b06da6840614d3c72768e56579b9d3b8e80802": "mutation CancelOrderMutation($data: IUserOrderCancel!) { cancelOrder(data: $data) { data } }",
11
11
  "8c25d37c8d6e7c20ab21bb8a4f4e6a2fe320ea8d": "mutation ProcessOrderAuthorizationMutation($data: IProcessOrderAuthorization!) { processOrderAuthorization(data: $data) { isPendingForOtherAuthorizer ruleForAuthorization { dimensionId orderAuthorizationId rule { authorizationData { authorizers { authorizationDate email id type } requireAllApprovals } authorizedEmails doId id isUserAuthorized isUserNextAuthorizer name notification priority scoreInterval { accept deny } status timeout trigger { condition { conditionType description expression greatherThan lessThan } effect { description effectType funcPath } } } } } }",
12
12
  "32f99c73c3de958b64d6bece1afe800469f54548": "query ValidateUser { validateUser { isValid } }",
13
- "c2b3f8bff73ebf6ac79d758c66cabbc21ba9fcc0": "fragment CartItem on StoreOffer { itemOffered { ...CartProductItem } listPrice listPriceWithTaxes price priceWithTaxes quantity seller { identifier } } fragment CartMessage on StoreCartMessage { status text } fragment CartProductItem on StoreProduct { additionalProperty { name propertyID value valueReference } brand { name } gtin image { alternateName url } isVariantOf { name productGroupID skuVariants { activeVariations availableVariations slugsMap } } name sku unitMultiplier } mutation ValidateCartMutation($cart: IStoreCart!, $session: IStoreSession!) { validateCart(cart: $cart, session: $session) { messages { ...CartMessage } order { acceptedOffer { ...CartItem } orderNumber shouldSplitItem } } }",
13
+ "32c15f8888ca34f223def7972b7f19090808435a": "fragment CartItem on StoreOffer { isGift itemOffered { ...CartProductItem } listPrice listPriceWithTaxes price priceWithTaxes quantity seller { identifier } } fragment CartMessage on StoreCartMessage { status text } fragment CartProductItem on StoreProduct { additionalProperty { name propertyID value valueReference } brand { name } gtin image { alternateName url } isVariantOf { name productGroupID skuVariants { activeVariations availableVariations slugsMap } } name sku unitMultiplier } mutation ValidateCartMutation($cart: IStoreCart!, $session: IStoreSession!) { validateCart(cart: $cart, session: $session) { messages { ...CartMessage } order { acceptedOffer { ...CartItem } orderNumber shouldSplitItem } } }",
14
14
  "3fa04e88c811fcb5ece7206fd5aa745bdbc143a8": "query ClientPickupPointsQuery($geoCoordinates: IStoreGeoCoordinates) { pickupPoints(geoCoordinates: $geoCoordinates) { pickupPointDistances { address { city number postalCode state street } distance isActive pickupId pickupName } } }",
15
15
  "feb7005103a859e2bc8cf2360d568806fd88deba": "mutation SubscribeToNewsletter($data: IPersonNewsletter!) { subscribeToNewsletter(data: $data) { id } }",
16
16
  "dc912e7272e3d9f5ced206837df87f544d39d0a5": "query ClientProductCountQuery($term: String) { productCount(term: $term) { total } }",
@@ -288,6 +288,8 @@ type StoreOffer {
288
288
  itemOffered: StoreProduct!
289
289
  """Number of items offered."""
290
290
  quantity: Int!
291
+ """Whether this offer is a gift (e.g. free promotional item)."""
292
+ isGift: Boolean
291
293
  }
292
294
 
293
295
  """Offer input."""
package/CHANGELOG.md CHANGED
@@ -3,6 +3,25 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # 3.99.0-dev.5 (2026-05-04)
7
+
8
+ ### Bug Fixes
9
+
10
+ - Enhance session validation logic to skip token refresh in local … ([#3285](https://github.com/vtex/faststore/issues/3285)) ([41a330a](https://github.com/vtex/faststore/commit/41a330a9704a29369141e6e78b1f6d01e5a2e1a5))
11
+
12
+ # 3.99.0-dev.4 (2026-04-28)
13
+
14
+ ### Bug Fixes
15
+
16
+ - Refactor token refresh logic in GraphQL API handler - SFS-3104 ([#3263](https://github.com/vtex/faststore/issues/3263)) ([695ab6a](https://github.com/vtex/faststore/commit/695ab6a9413eb53ef457cfc890c74e41c29c1ab8))
17
+
18
+ # 3.99.0-dev.3 (2026-04-23)
19
+
20
+ ### Features
21
+
22
+ - add isGift field to StoreOffer type and resolvers - SFS-3040 ([#3220](https://github.com/vtex/faststore/issues/3220)) ([e10fc92](https://github.com/vtex/faststore/commit/e10fc92633a5296a5b03286028d31d9c280561c6))
23
+ - Enhance session management with logout - SFS-3105 ([#3268](https://github.com/vtex/faststore/issues/3268)) ([9468e32](https://github.com/vtex/faststore/commit/9468e32a3531ca3c91c7f82a34ac1c750c6a26ef))
24
+
6
25
  # [3.99.0-dev.2](https://github.com/vtex/faststore/compare/v3.99.0-dev.1...v3.99.0-dev.2) (2026-04-21)
7
26
 
8
27
  **Note:** Version bump only for package @faststore/core
@@ -148,6 +148,7 @@ module.exports = {
148
148
  enableRedirects: false,
149
149
  enableSearchSSR: false,
150
150
  enableFaststoreMyAccount: false,
151
+ useIsGiftFromOrderForm: false,
151
152
  graphqlCacheControl: {
152
153
  maxAge: 0, // 0 disables cache, 5 * 60 enable cache control maxAge 5 minutes
153
154
  staleWhileRevalidate: 60 * 60, // 1 hour
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faststore/core",
3
- "version": "3.99.0-dev.2",
3
+ "version": "3.99.0-dev.5",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -51,11 +51,11 @@
51
51
  "@envelop/graphql-jit": "^8.0.3",
52
52
  "@envelop/parser-cache": "^6.0.2",
53
53
  "@envelop/validation-cache": "^6.0.2",
54
- "@faststore/api": "3.98.1-dev.0",
55
- "@faststore/graphql-utils": "^3.98.1-dev.0",
56
- "@faststore/lighthouse": "3.98.1-dev.0",
57
- "@faststore/sdk": "3.99.0-dev.2",
58
- "@faststore/ui": "3.99.0-dev.1",
54
+ "@faststore/api": "3.99.0-dev.5",
55
+ "@faststore/graphql-utils": "^3.99.0-dev.5",
56
+ "@faststore/lighthouse": "3.99.0-dev.5",
57
+ "@faststore/sdk": "3.99.0-dev.5",
58
+ "@faststore/ui": "3.99.0-dev.5",
59
59
  "@graphql-codegen/cli": "5.0.2",
60
60
  "@graphql-codegen/client-preset": "4.2.6",
61
61
  "@graphql-codegen/typescript": "4.0.7",
@@ -124,5 +124,5 @@
124
124
  "ts-jest": "29.1.1",
125
125
  "typescript": "5.3.2"
126
126
  },
127
- "gitHead": "21f24e9d59e24647024710ae8c1b0273eec018b9"
127
+ "gitHead": "aae7a4fc1fe2b819b410d400f8b2ea70fedcdcfe"
128
128
  }
@@ -12,7 +12,10 @@ import { useEffect } from 'react'
12
12
  * We handle both: check the flag on mount (fresh load) and on pageshow (bfcache).
13
13
  */
14
14
 
15
- export const RELOAD_AFTER_LOGOUT_KEY = 'faststore_reload_after_logout_return'
15
+ import {
16
+ RELOAD_AFTER_LOGOUT_KEY,
17
+ SESSION_READY_KEY,
18
+ } from '../../../../sdk/session/storageKeys'
16
19
 
17
20
  /**
18
21
  * Call before redirecting to logout. When the user returns to the store, the app
@@ -43,6 +46,8 @@ const checkAndReloadIfReturnedFromLogout = (): void => {
43
46
  try {
44
47
  if (sessionStorage.getItem(RELOAD_AFTER_LOGOUT_KEY)) {
45
48
  sessionStorage.removeItem(RELOAD_AFTER_LOGOUT_KEY)
49
+ // Pre-set session ready so the reloaded page starts without a skeleton.
50
+ sessionStorage.setItem(SESSION_READY_KEY, 'true')
46
51
  setTimeout(forceRefreshWithoutCache, RELOAD_DELAY_MS)
47
52
  }
48
53
  } catch {
@@ -8,7 +8,8 @@ import { parse } from 'cookie'
8
8
  import type { NextApiHandler, NextApiRequest } from 'next'
9
9
 
10
10
  import discoveryConfig from 'discovery.config'
11
- import { getJWTAutCookie, isExpired } from 'src/utils/getCookie'
11
+ import { getJWTAutCookie } from 'src/utils/getCookie'
12
+ import { shouldForceRefreshTokenForValidateSession } from 'src/utils/validateSessionRefreshToken'
12
13
  import { execute } from '../../server'
13
14
 
14
15
  const DEFAULT_MAX_AGE = 5 * 60 // 5 minutes
@@ -160,7 +161,11 @@ const handler: NextApiHandler = async (request, response) => {
160
161
  // value is used to cache bust the request if there is a VtexIdclientAutCookie
161
162
  const { operation, variables, query, v: value } = parseRequest(request)
162
163
 
164
+ const hostname = request.headers.host?.split(':')[0]
165
+ const isLocal = hostname === 'localhost' || hostname === '127.0.0.1'
166
+
163
167
  if (
168
+ !isLocal &&
164
169
  operation.__meta__.operationName === 'ValidateSession' &&
165
170
  discoveryConfig.experimental?.refreshToken
166
171
  ) {
@@ -169,27 +174,10 @@ const handler: NextApiHandler = async (request, response) => {
169
174
  account: discoveryConfig.api.storeId,
170
175
  })
171
176
 
172
- const tokenExpired = Boolean(jwt && isExpired(Number(jwt?.exp)))
173
-
174
- const refreshAfterExist = !!variables?.session?.refreshAfter
175
-
176
- const refreshAfterExpired =
177
- refreshAfterExist && isExpired(Number(variables.session.refreshAfter))
178
-
179
- const tokenExistAndIsFirstRefreshTokenRequest =
180
- !!jwt && !refreshAfterExist
181
-
182
- // when token expired, browser clears the cookie, but we still have the refreshAfter in session and the refresh token cookie
183
- const tokenNotExistAndRefreshAfterExistAndIsExpired =
184
- !jwt && !!refreshAfterExist && refreshAfterExpired
185
-
186
- const tokenExpiredAndRefreshAfterIsNullOrExpired =
187
- tokenExpired && (!refreshAfterExist || refreshAfterExpired)
188
-
189
- const shouldRefreshToken =
190
- tokenExistAndIsFirstRefreshTokenRequest ||
191
- tokenNotExistAndRefreshAfterExistAndIsExpired ||
192
- tokenExpiredAndRefreshAfterIsNullOrExpired
177
+ const shouldRefreshToken = shouldForceRefreshTokenForValidateSession({
178
+ jwt,
179
+ sessionRefreshAfter: variables?.session?.refreshAfter,
180
+ })
193
181
 
194
182
  if (shouldRefreshToken) {
195
183
  throw new UnauthorizedError(
@@ -1,5 +1,5 @@
1
1
  import { useEffect, useState } from 'react'
2
- import { sessionStore } from '../session'
2
+ import { logoutAndClearSession, sessionStore } from '../session'
3
3
  import { isRefreshTokenSuccessful, refreshTokenRequest } from './refreshToken'
4
4
 
5
5
  export const useRefreshToken = (
@@ -37,23 +37,12 @@ export const useRefreshToken = (
37
37
  url.searchParams.set('_refresh', Date.now().toString())
38
38
  window.location.href = url.toString()
39
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
-
40
+ await logoutAndClearSession(currentSession)
46
41
  setShouldShow403(true)
47
42
  }
48
43
  } catch (error) {
49
44
  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
-
45
+ await logoutAndClearSession(currentSession)
57
46
  setShouldShow403(true)
58
47
  }
59
48
  }
@@ -16,7 +16,11 @@ import { request } from '../graphql/request'
16
16
  import { sessionStore } from '../session'
17
17
  import { createValidationStore, useStore } from '../useStore'
18
18
 
19
- export interface CartItem extends SDKCartItem, CartItemFragment {}
19
+ export interface CartItem
20
+ extends SDKCartItem,
21
+ Omit<CartItemFragment, 'isGift'> {
22
+ isGift?: boolean | null
23
+ }
20
24
 
21
25
  export interface Cart extends SDKCart<CartItem> {
22
26
  messages?: CartMessageFragment[]
@@ -53,6 +57,7 @@ export const ValidateCartMutation = gql(`
53
57
  priceWithTaxes
54
58
  listPrice
55
59
  listPriceWithTaxes
60
+ isGift
56
61
  itemOffered {
57
62
  ...CartProductItem
58
63
  }
@@ -88,7 +93,12 @@ export const ValidateCartMutation = gql(`
88
93
  }
89
94
  `)
90
95
 
91
- const isGift = (item: CartItem) => item.price === 0
96
+ const isGift = (item: CartItem) => {
97
+ if (storeConfig.experimental?.useIsGiftFromOrderForm) {
98
+ return item?.isGift ?? false
99
+ }
100
+ return item.price === 0
101
+ }
92
102
 
93
103
  const getItemId = (item: Pick<CartItem, 'itemOffered' | 'seller' | 'price'>) =>
94
104
  [