@faststore/core 3.98.3 → 3.98.4

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 (43) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +24 -24
  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/8971.js +1 -1
  14. package/.next/server/functions-config-manifest.json +1 -1
  15. package/.next/server/middleware-build-manifest.js +1 -1
  16. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  17. package/.next/server/pages/[...slug].js +1 -1
  18. package/.next/server/pages/en-US/404.html +2 -2
  19. package/.next/server/pages/en-US/500.html +2 -2
  20. package/.next/server/pages/en-US/checkout.html +2 -2
  21. package/.next/server/pages/en-US/login.html +2 -2
  22. package/.next/server/pages/en-US/s.html +2 -2
  23. package/.next/server/pages/en-US.html +2 -2
  24. package/.next/server/pages/s.js +1 -1
  25. package/.next/server/pages-manifest.json +1 -1
  26. package/.next/static/{AJEYfu-vyOzSdU3O67WyN → 9lFuk39f-HZcrM7Ul1Rlh}/_buildManifest.js +1 -1
  27. package/.next/static/chunks/{2927.5a79877943a6bf7c.js → 2927.23bae2c79f0ac0f3.js} +1 -1
  28. package/.next/static/chunks/6656-7e6ea87d91e44b10.js +1 -0
  29. package/.next/static/chunks/{UIToast.19a8664c01a00d3a.js → UIToast.de15325248043ce5.js} +1 -1
  30. package/.next/static/chunks/pages/{_app-735c745509e1e8d6.js → _app-8be996b3105d8c26.js} +1 -1
  31. package/.next/static/chunks/{webpack-fc37ce1526507eaf.js → webpack-fd706604cbbdc60f.js} +1 -1
  32. package/.next/trace +142 -142
  33. package/.turbo/turbo-build.log +11 -11
  34. package/.turbo/turbo-test.log +8 -8
  35. package/CHANGELOG.md +4 -0
  36. package/package.json +2 -2
  37. package/src/sdk/graphql/request.ts +1 -1
  38. package/src/sdk/product/usePageProductsQuery.ts +35 -4
  39. package/src/sdk/product/useProductGalleryQuery.ts +6 -1
  40. package/src/utils/cookieCacheBusting.ts +79 -19
  41. package/test/utils/cookieCacheBusting.test.ts +167 -75
  42. package/.next/static/chunks/6656-efc464f20ffeaa51.js +0 -1
  43. /package/.next/static/{AJEYfu-vyOzSdU3O67WyN → 9lFuk39f-HZcrM7Ul1Rlh}/_ssgManifest.js +0 -0
@@ -1,23 +1,23 @@
1
1
 
2
- > @faststore/core@3.98.2 prebuild /home/runner/work/faststore/faststore/packages/core
2
+ > @faststore/core@3.98.3 prebuild /home/runner/work/faststore/faststore/packages/core
3
3
  > na run partytown && na run generate
4
4
 
5
5
 
6
- > @faststore/core@3.98.2 partytown /home/runner/work/faststore/faststore/packages/core
6
+ > @faststore/core@3.98.3 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.98.2 generate /home/runner/work/faststore/faststore/packages/core
11
+ > @faststore/core@3.98.3 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.98.2 generate:schema /home/runner/work/faststore/faststore/packages/core
15
+ > @faststore/core@3.98.3 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.98.2 generate:codegen /home/runner/work/faststore/faststore/packages/core
20
+ > @faststore/core@3.98.3 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.98.2 format:generated /home/runner/work/faststore/faststore/packages/core
40
+ > @faststore/core@3.98.3 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.98.2 build /home/runner/work/faststore/faststore/packages/core
44
+ > @faststore/core@3.98.3 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,8 +59,8 @@ 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 ...
@@ -81,7 +81,7 @@ Route (pages) Size First Load JS
81
81
  ├ λ /api/health/live 0 B 109 kB
82
82
  ├ λ /api/health/ready 0 B 109 kB
83
83
  ├ λ /api/preview 0 B 109 kB
84
- ├ ● /checkout 745 B 158 kB
84
+ ├ ● /checkout 745 B 159 kB
85
85
  ├ ● /login 1.7 kB 159 kB
86
86
  ├ λ /pvt/account 245 B 109 kB
87
87
  ├ ● /pvt/account/[...unknown] 286 B 109 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-735c745509e1e8d6.js 26.1 kB
107
- ├ chunks/webpack-fc37ce1526507eaf.js 3.88 kB
106
+ ├ chunks/pages/_app-8be996b3105d8c26.js 26.3 kB
107
+ ├ chunks/webpack-fd706604cbbdc60f.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,16 @@
1
1
 
2
- > @faststore/core@3.98.2 test /home/runner/work/faststore/faststore/packages/core
2
+ > @faststore/core@3.98.3 test /home/runner/work/faststore/faststore/packages/core
3
3
  > jest
4
4
 
5
- PASS test/utils/multipleTemplates.test.ts (12.426 s)
6
- PASS test/utils/clearCookies.test.ts (11.827 s)
5
+ PASS test/utils/clearCookies.test.ts (12.005 s)
6
+ PASS test/utils/multipleTemplates.test.ts (13.659 s)
7
7
  PASS test/server/cms/global.test.ts
8
- PASS test/utils/cookieCacheBusting.test.ts
9
- PASS test/server/cms/index.test.ts (6.051 s)
10
- PASS test/server/index.test.ts (24.09 s)
8
+ PASS test/utils/cookieCacheBusting.test.ts (15.415 s)
9
+ PASS test/server/cms/index.test.ts (6.077 s)
10
+ PASS test/server/index.test.ts (12.996 s)
11
11
 
12
12
  Test Suites: 6 passed, 6 total
13
- Tests: 47 passed, 47 total
13
+ Tests: 52 passed, 52 total
14
14
  Snapshots: 0 total
15
- Time: 25.195 s
15
+ Time: 27.695 s
16
16
  Ran all test suites.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,10 @@
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.98.4](https://github.com/vtex/faststore/compare/v3.98.3...v3.98.4) (2026-04-15)
7
+
8
+ **Note:** Version bump only for package @faststore/core
9
+
6
10
  ## [3.98.3](https://github.com/vtex/faststore/compare/v3.98.2...v3.98.3) (2026-04-09)
7
11
 
8
12
  ### Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faststore/core",
3
- "version": "3.98.3",
3
+ "version": "3.98.4",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -124,5 +124,5 @@
124
124
  "ts-jest": "29.1.1",
125
125
  "typescript": "5.3.2"
126
126
  },
127
- "gitHead": "de14e7d1dded5c80a2580cfd9df597420956369b"
127
+ "gitHead": "ee710a0c31c409363948a3c3b7c7c1f26acb25cb"
128
128
  }
@@ -29,7 +29,7 @@ export const request = async <Query = unknown, Variables = unknown>(
29
29
  variables: Variables,
30
30
  options?: RequestOptions
31
31
  ) => {
32
- // Get cache busting value based person?.id from session
32
+ // Get cache busting value based on auth state and postal code
33
33
  const value = getClientCacheBustingValue()
34
34
 
35
35
  const { data, errors } = await baseRequest<Variables, Query>('/api/graphql', {
@@ -1,3 +1,4 @@
1
+ import storeConfig from 'discovery.config'
1
2
  import { useSearch } from '@faststore/sdk'
2
3
  import { gql } from '@generated'
3
4
  import type {
@@ -15,6 +16,7 @@ import {
15
16
  useState,
16
17
  } from 'react'
17
18
  import { useQuery } from 'src/sdk/graphql/useQuery'
19
+ import { useSession } from 'src/sdk/session'
18
20
  import { generatedBuildTime } from '../../../next-seo.config'
19
21
  import { useLocalizedVariables } from './useLocalizedVariables'
20
22
  import { useShouldFetchFirstPage } from './useShouldFetchFirstPage'
@@ -80,8 +82,23 @@ export const useCreateUseGalleryPage = (
80
82
  params?: UseCreateUseGalleryPageProps
81
83
  ) => {
82
84
  const initialPages = params?.initialPages?.search ? [params.initialPages] : []
85
+
86
+ // Seed the cache with the same region-aware key shape that useGalleryPage uses,
87
+ // so hasSameVariables correctly matches on first mount and avoids an unnecessary
88
+ // re-fetch of the SSR-loaded page.
89
+ const isDeliveryPromiseEnabled = storeConfig.deliveryPromise?.enabled ?? false
90
+ const { postalCode } = useSession()
83
91
  const initialVariables = params?.serverManyProductsVariables
84
- ? [getKey(params.serverManyProductsVariables)]
92
+ ? [
93
+ getKey(
94
+ isDeliveryPromiseEnabled
95
+ ? {
96
+ ...params.serverManyProductsVariables,
97
+ _postalCode: postalCode ?? '',
98
+ }
99
+ : params.serverManyProductsVariables
100
+ ),
101
+ ]
85
102
  : []
86
103
 
87
104
  const [pages, setPages] =
@@ -97,6 +114,8 @@ export const useCreateUseGalleryPage = (
97
114
  itemsPerPage,
98
115
  } = useSearch()
99
116
 
117
+ const { postalCode, isValidating: isSessionValidating } = useSession()
118
+
100
119
  const localizedVariables = useLocalizedVariables({
101
120
  first: itemsPerPage,
102
121
  after: (itemsPerPage * page).toString(),
@@ -105,9 +124,20 @@ export const useCreateUseGalleryPage = (
105
124
  selectedFacets,
106
125
  })
107
126
 
127
+ // Include postalCode in the cache key so pages are invalidated on region change.
128
+ // _postalCode is not sent to the API — it only affects the local cache comparison.
129
+ // Only applied when deliveryPromise is enabled to avoid unnecessary cache fragmentation.
130
+ const localizedVariablesWithRegion = useMemo(
131
+ () =>
132
+ isDeliveryPromiseEnabled
133
+ ? { ...localizedVariables, _postalCode: postalCode ?? '' }
134
+ : localizedVariables,
135
+ [localizedVariables, postalCode]
136
+ )
137
+
108
138
  const hasSameVariables = deepEquals(
109
139
  pagesCache.current[page],
110
- getKey(localizedVariables)
140
+ getKey(localizedVariablesWithRegion)
111
141
  )
112
142
 
113
143
  const shouldFetchFirstPage = useShouldFetchFirstPage({
@@ -123,13 +153,14 @@ export const useCreateUseGalleryPage = (
123
153
  >(query, localizedVariables, {
124
154
  fallbackData: null,
125
155
  suspense: true,
126
- doNotRun: !shouldFetch,
156
+ doNotRun:
157
+ !shouldFetch || (isDeliveryPromiseEnabled && isSessionValidating),
127
158
  })
128
159
 
129
160
  const shouldUpdatePages = data !== null
130
161
 
131
162
  if (shouldUpdatePages) {
132
- pagesCache.current[page] = getKey(localizedVariables)
163
+ pagesCache.current[page] = getKey(localizedVariablesWithRegion)
133
164
 
134
165
  // Update refs
135
166
  const newPages = [...pagesRef.current]
@@ -1,3 +1,4 @@
1
+ import storeConfig from 'discovery.config'
1
2
  import { gql } from '@generated'
2
3
  import { useQuery } from 'src/sdk/graphql/useQuery'
3
4
  import { useSession } from 'src/sdk/session'
@@ -138,7 +139,7 @@ export const useProductGalleryQuery = ({
138
139
  selectedFacets,
139
140
  itemsPerPage,
140
141
  }: ProductGalleryQueryOptions) => {
141
- const { locale } = useSession()
142
+ const { locale, isValidating: isSessionValidating } = useSession()
142
143
  const { state, setState } = useSearch()
143
144
  const localizedVariables = useLocalizedVariables({
144
145
  first: itemsPerPage,
@@ -148,7 +149,10 @@ export const useProductGalleryQuery = ({
148
149
  selectedFacets,
149
150
  })
150
151
 
152
+ const isDeliveryPromiseEnabled = storeConfig.deliveryPromise?.enabled ?? false
153
+
151
154
  const queryResult = useQuery<Query, Variables>(query, localizedVariables, {
155
+ doNotRun: isDeliveryPromiseEnabled && isSessionValidating,
152
156
  onSuccess: (data: Query) => {
153
157
  const updatedFuzzyFacetValue = data.search.metadata?.fuzzy
154
158
  const updatedOperatorFacetValue = data.search.metadata?.logicalOperator
@@ -196,6 +200,7 @@ export const useProductGalleryQuery = ({
196
200
  const operatorFacetValue = findFacetValue(selectedFacets, 'operator')
197
201
  const shouldRefetchQuery =
198
202
  !queryResult.error && (!fuzzyFacetValue || !operatorFacetValue)
203
+
199
204
  if (shouldRefetchQuery) {
200
205
  // The first result is not relevant, return null data to avoid rendering the page while the query is being re-fetched
201
206
  return { ...queryResult, isValidating: true, data: null }
@@ -1,6 +1,8 @@
1
+ import storeConfig from 'discovery.config'
1
2
  import { sessionStore } from 'src/sdk/session'
2
3
 
3
4
  export const STORAGE_KEY_PERSON_ID = 'faststore_person_id'
5
+ export const STORAGE_KEY_POSTAL_CODE = 'faststore_postal_code'
4
6
  export const STORAGE_KEY_CACHE_BUST_LAST_VALUE =
5
7
  'faststore_cache_bust_last_value'
6
8
 
@@ -17,8 +19,19 @@ const getPersonId = (): string | null => {
17
19
  }
18
20
 
19
21
  /**
20
- * Gets the stored person id from sessionStorage (client-side only)
22
+ * Gets postalCode from session when deliveryPromise is enabled.
23
+ * Reads session store directly since vtex_segment is httpOnly and inaccessible via JavaScript.
24
+ * Returns null for stores that don't use delivery promise — postal code has no effect
25
+ * on their facets, so there is no need to fragment the CDN cache by region.
21
26
  */
27
+ const getPostalCode = (): string | null => {
28
+ if (typeof window === 'undefined' || !storeConfig.deliveryPromise?.enabled) {
29
+ return null
30
+ }
31
+ const session = sessionStore.read() ?? sessionStore.readInitial()
32
+ return session?.postalCode ?? null
33
+ }
34
+
22
35
  const getStoredPersonId = (): string | null => {
23
36
  if (typeof sessionStorage === 'undefined') {
24
37
  return null
@@ -31,6 +44,21 @@ const getStoredPersonId = (): string | null => {
31
44
  }
32
45
  }
33
46
 
47
+ /**
48
+ * Gets the postal code from sessionStorage (client-side only)
49
+ */
50
+ const getStoredPostalCode = (): string | null => {
51
+ if (typeof sessionStorage === 'undefined') {
52
+ return null
53
+ }
54
+
55
+ try {
56
+ return sessionStorage.getItem(STORAGE_KEY_POSTAL_CODE)
57
+ } catch {
58
+ return null
59
+ }
60
+ }
61
+
34
62
  /**
35
63
  * Stores the person id in sessionStorage (client-side only)
36
64
  */
@@ -50,9 +78,22 @@ const storePersonId = (value: string | null): void => {
50
78
  }
51
79
  }
52
80
 
53
- /**
54
- * Gets the last cache busting value from sessionStorage (client-side only)
55
- */
81
+ const storePostalCode = (value: string | null): void => {
82
+ if (typeof sessionStorage === 'undefined') {
83
+ return
84
+ }
85
+
86
+ try {
87
+ if (value === null) {
88
+ sessionStorage.removeItem(STORAGE_KEY_POSTAL_CODE)
89
+ } else {
90
+ sessionStorage.setItem(STORAGE_KEY_POSTAL_CODE, value)
91
+ }
92
+ } catch {
93
+ // Ignore storage errors
94
+ }
95
+ }
96
+
56
97
  const getLastValue = (): string | null => {
57
98
  if (typeof sessionStorage === 'undefined') {
58
99
  return null
@@ -66,7 +107,7 @@ const getLastValue = (): string | null => {
66
107
  }
67
108
 
68
109
  /**
69
- * Stores the last cache busting value in sessionStorage (client-side only)
110
+ * Stores the last value in sessionStorage (client-side only)
70
111
  */
71
112
  const storeLastValue = (value: string): void => {
72
113
  if (typeof sessionStorage === 'undefined') {
@@ -81,7 +122,7 @@ const storeLastValue = (value: string): void => {
81
122
  }
82
123
 
83
124
  /**
84
- * Clears all cache busting related data from sessionStorage (client-side only)
125
+ * Clears the cache busting related data from sessionStorage (client-side only)
85
126
  */
86
127
  const clearCacheBustingStorage = (): void => {
87
128
  if (typeof sessionStorage === 'undefined') {
@@ -90,46 +131,65 @@ const clearCacheBustingStorage = (): void => {
90
131
 
91
132
  try {
92
133
  sessionStorage.removeItem(STORAGE_KEY_PERSON_ID)
134
+ sessionStorage.removeItem(STORAGE_KEY_POSTAL_CODE)
93
135
  sessionStorage.removeItem(STORAGE_KEY_CACHE_BUST_LAST_VALUE)
94
136
  } catch {
95
137
  // Ignore storage errors
96
138
  }
97
139
  }
98
140
 
141
+ const buildValue = (
142
+ personId: string | null,
143
+ postalCode: string | null
144
+ ): string => {
145
+ const timestamp = Date.now().toString()
146
+ return [timestamp, personId, postalCode].filter(Boolean).join('::')
147
+ }
148
+
99
149
  /**
100
- * Gets cache busting value for client-side based on auth state changes.
101
- * Uses person?.id from session (via useSession/ValidateSession) since auth cookies
102
- * are now httpOnly and cannot be accessed via JavaScript.
150
+ * Gets cache busting value for client-side GET requests based on session state.
151
+ *
152
+ * Covers two axes that make GraphQL responses user/region-specific:
153
+ * - Auth state (person.id): logged-in users see personalised prices and availability.
154
+ * - Postal code: delivery-promise facets (shipping, delivery-options) differ per region.
155
+ *
156
+ * The value is appended as the `v` query param so the CDN treats each
157
+ * combination as a distinct cache entry. Returns null when neither axis is set,
158
+ * meaning the public CDN cache is shared across all anonymous visitors with no
159
+ * location — which is the correct behaviour for stores without delivery promise.
103
160
  */
104
161
  export const getClientCacheBustingValue = (): string | null => {
105
162
  const currentPersonId = getPersonId()
163
+ const currentPostalCode = getPostalCode()
106
164
 
107
- // Guard clause: if user is not logged in (no person?.id), clear storage and don't proceed with cache busting logic
108
- if (currentPersonId === null) {
165
+ // No session-specific data: clear storage and allow shared public CDN caching (don't proceed with cache busting logic)
166
+ if (!currentPersonId && !currentPostalCode) {
109
167
  clearCacheBustingStorage()
110
168
  return null
111
169
  }
112
170
 
113
171
  const storedPersonId = getStoredPersonId()
172
+ const storedPostalCode = getStoredPostalCode()
114
173
 
115
- // If person changed (login/logout or different user), update stored value and return new value
116
- if (currentPersonId !== storedPersonId) {
174
+ // Either axis changed: persist new state and generate a fresh cache-busting value
175
+ if (
176
+ currentPersonId !== storedPersonId ||
177
+ currentPostalCode !== storedPostalCode
178
+ ) {
117
179
  storePersonId(currentPersonId)
118
- const timestamp = Date.now().toString()
119
- const value = `${timestamp}::${currentPersonId}`
180
+ storePostalCode(currentPostalCode)
181
+ const value = buildValue(currentPersonId, currentPostalCode)
120
182
  storeLastValue(value)
121
183
  return value
122
184
  }
123
185
 
124
- // Person hasn't changed, return last value or create one if it doesn't exist
186
+ // Nothing changed: reuse existing value (or create one if storage was cleared)
125
187
  const lastValue = getLastValue()
126
188
  if (lastValue) {
127
189
  return lastValue
128
190
  }
129
191
 
130
- // Fallback: if no last value, create one
131
- const timestamp = Date.now().toString()
132
- const value = `${timestamp}::${currentPersonId}`
192
+ const value = buildValue(currentPersonId, currentPostalCode)
133
193
  storeLastValue(value)
134
194
  return value
135
195
  }