@faststore/core 4.5.0-dev.6 → 4.5.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.
@@ -1,9 +1,9 @@
1
1
 
2
- > @faststore/core@4.5.0-dev.5 generate /home/runner/work/faststore/faststore/packages/core
2
+ > @faststore/core@4.5.0-dev.7 generate /home/runner/work/faststore/faststore/packages/core
3
3
  > pnpm run gen-types && pnpm run cache-graphql
4
4
 
5
5
 
6
- > @faststore/core@4.5.0-dev.5 gen-types /home/runner/work/faststore/faststore/packages/core
6
+ > @faststore/core@4.5.0-dev.7 gen-types /home/runner/work/faststore/faststore/packages/core
7
7
  > node ../cli/bin/run generate-types .
8
8
 
9
9
  [STARTED] Parse Configuration
@@ -19,7 +19,7 @@
19
19
  [COMPLETED] Generate to /home/runner/work/faststore/faststore/packages/core/@generated/
20
20
  [COMPLETED] Generate outputs
21
21
 
22
- > @faststore/core@4.5.0-dev.5 cache-graphql /home/runner/work/faststore/faststore/packages/core
22
+ > @faststore/core@4.5.0-dev.7 cache-graphql /home/runner/work/faststore/faststore/packages/core
23
23
  > node ../cli/bin/run cache-graphql --config=./discovery.config.default.js --queries=./@generated/persisted-documents.json
24
24
 
25
25
  [Info] - Config file location: /home/runner/work/faststore/faststore/packages/core/discovery.config.default.js
@@ -1,11 +1,11 @@
1
1
 
2
- > @faststore/core@4.5.0-dev.5 test /home/runner/work/faststore/faststore/packages/core
2
+ > @faststore/core@4.5.0-dev.7 test /home/runner/work/faststore/faststore/packages/core
3
3
  > vitest run
4
4
 
5
5
 
6
6
   RUN  v4.0.7 /home/runner/work/faststore/faststore/packages/core
7
7
 
8
- ✓  browser  test/sdk/session/reconcileSessionLocale.browser.test.ts (5 tests) 28ms
8
+ ✓  browser  test/sdk/session/reconcileSessionLocale.browser.test.ts (5 tests) 27ms
9
9
  stderr | test/components/RecommendationShelf/RecommendationShelf.browser.test.tsx
10
10
  Error in optimistic validation: TypeError: Failed to parse URL from /api/graphql?operationName=ValidateCartMutation&operationHash=8199d03debb0a6751ec53d8ee8987b78d0292acb
11
11
   at node:internal/deps/undici/undici:16416:13
@@ -32,12 +32,14 @@
32
32
  }
33
33
  }
34
34
 
35
+ Browserslist: browsers data (caniuse-lite) is 6 months old. Please run:
36
+ npx update-browserslist-db@latest
37
+ Why you should do it regularly: https://github.com/browserslist/update-db#readme
38
+ ✓  browser  test/components/account/ContractSwitcher.browser.test.tsx (13 tests) 792ms
35
39
  stderr | test/components/RecommendationShelf/RecommendationShelf.browser.test.tsx > RecommendationShelf > renders nothing when there is an error
36
40
  Error fetching recommendations undefined boom for campaign vrn:recommendations:acc:rec-top-items-v2:campaign-1
37
41
 
38
- ✓  browser  test/components/RecommendationShelf/RecommendationShelf.browser.test.tsx (8 tests) 311ms
39
- ✓  browser  test/components/account/ContractSwitcher.browser.test.tsx (13 tests) 1942ms
40
- ✓ shows an error state when the list fails to load  487ms
42
+ ✓  browser  test/components/RecommendationShelf/RecommendationShelf.browser.test.tsx (8 tests) 122ms
41
43
  stderr | test/pages/password-protection.browser.test.tsx > PasswordProtectionLogin > posts the password and redirects to the requested return path
42
44
  Warning: An update to PasswordProtectionLogin inside a test was not wrapped in act(...).
43
45
 
@@ -75,6 +77,7 @@ act(() => {
75
77
  This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
76
78
  at PasswordProtectionLogin (/home/runner/work/faststore/faststore/packages/core/src/pages/password-protection/index.tsx:19:69)
77
79
 
80
+ ✓  browser  test/sdk/session/getInitialSession.browser.test.ts (6 tests) 16ms
78
81
  stderr | test/pages/password-protection.browser.test.tsx > PasswordProtectionLogin > defaults returnTo to root and shows API validation errors
79
82
  Warning: An update to PasswordProtectionLogin inside a test was not wrapped in act(...).
80
83
 
@@ -219,7 +222,6 @@ act(() => {
219
222
  This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
220
223
  at PasswordProtectionLogin (/home/runner/work/faststore/faststore/packages/core/src/pages/password-protection/index.tsx:19:69)
221
224
 
222
- ✓  browser  test/sdk/session/getInitialSession.browser.test.ts (6 tests) 34ms
223
225
  stderr | test/pages/password-protection.browser.test.tsx > PasswordProtectionLogin > shows service unavailable when the login request fails
224
226
  Warning: An update to PasswordProtectionLogin inside a test was not wrapped in act(...).
225
227
 
@@ -268,14 +270,11 @@ act(() => {
268
270
  This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
269
271
  at PasswordProtectionLogin (/home/runner/work/faststore/faststore/packages/core/src/pages/password-protection/index.tsx:19:69)
270
272
 
271
- ✓  browser  test/pages/password-protection.browser.test.tsx (6 tests) 2030ms
272
- ✓ renders the password protection form  836ms
273
- ✓ posts the password and redirects to the requested return path  316ms
274
- ✓  browser  test/components/account/OrganizationDrawer.browser.test.tsx (5 tests) 1955ms
275
- ✓ renders the menu view with contract details and manage footer  957ms
276
- ✓ opens the contract switcher and returns to the menu on back  413ms
277
- ✓ resets to the menu when the drawer closes  310ms
278
- ✓  browser  test/sdk/account/changeContractToken.browser.test.ts (9 tests) 46ms
273
+ ✓  browser  test/pages/password-protection.browser.test.tsx (6 tests) 1373ms
274
+ ✓ renders the password protection form  697ms
275
+ ✓  browser  test/sdk/account/changeContractToken.browser.test.ts (9 tests) 55ms
276
+ ✓  browser  test/components/account/OrganizationDrawer.browser.test.tsx (5 tests) 1214ms
277
+ ✓ renders the menu view with contract details and manage footer  756ms
279
278
  stderr | test/sdk/localization/store-url.browser.test.ts
280
279
  Error in optimistic validation: TypeError: Cannot read properties of undefined (reading 'read')
281
280
  at validateCart (/home/runner/work/faststore/faststore/packages/core/src/sdk/cart/index.ts:121:27)
@@ -283,9 +282,8 @@ This ensures that you're testing the behavior the user would see in the browser.
283
282
  at a (/home/runner/work/faststore/faststore/packages/sdk/dist/es/index.mjs:354:23)
284
283
   at processTicksAndRejections (node:internal/process/task_queues:103:5)
285
284
 
286
- ✓  browser  test/sdk/localization/store-url.browser.test.ts (3 tests) 32ms
287
- ✓  browser  test/sdk/account/useAvailableContracts.browser.test.ts (6 tests) 71ms
288
- ✓  browser  test/sdk/account/useSwitchContract.browser.test.ts (4 tests) 34ms
285
+ ✓  browser  test/sdk/localization/store-url.browser.test.ts (3 tests) 6ms
286
+ ✓  browser  test/sdk/account/useAvailableContracts.browser.test.ts (6 tests) 94ms
289
287
  stderr | test/components/account/doLogout.browser.test.ts
290
288
  Error in optimistic validation: TypeError: Cannot read properties of undefined (reading 'read')
291
289
  at validateCart (/home/runner/work/faststore/faststore/packages/core/src/sdk/cart/index.ts:121:27)
@@ -293,8 +291,9 @@ This ensures that you're testing the behavior the user would see in the browser.
293
291
  at a (/home/runner/work/faststore/faststore/packages/sdk/dist/es/index.mjs:354:23)
294
292
   at processTicksAndRejections (node:internal/process/task_queues:103:5)
295
293
 
296
- ✓  browser  test/components/account/doLogout.browser.test.ts (1 test) 43ms
297
- ✓  browser  test/components/account/OrganizationDrawerHeader.browser.test.tsx (3 tests) 280ms
294
+ ✓  browser  test/sdk/account/useSwitchContract.browser.test.ts (4 tests) 47ms
295
+ ✓  browser  test/components/account/doLogout.browser.test.ts (1 test) 17ms
296
+ ✓  browser  test/components/account/OrganizationDrawerHeader.browser.test.tsx (3 tests) 195ms
298
297
  stderr | test/sdk/account/clearPersistedSessionState.browser.test.ts
299
298
  Error in optimistic validation: TypeError: Cannot read properties of undefined (reading 'read')
300
299
  at validateCart (/home/runner/work/faststore/faststore/packages/core/src/sdk/cart/index.ts:121:27)
@@ -302,12 +301,11 @@ This ensures that you're testing the behavior the user would see in the browser.
302
301
  at a (/home/runner/work/faststore/faststore/packages/sdk/dist/es/index.mjs:354:23)
303
302
   at processTicksAndRejections (node:internal/process/task_queues:103:5)
304
303
 
305
- ✓  browser  test/sdk/account/clearPersistedSessionState.browser.test.ts (1 test) 9ms
306
- ✓  browser  test/components/account/OrganizationDrawerFooter.browser.test.tsx (3 tests) 338ms
307
- ✓  browser  test/utils/isLocalHost.browser.test.ts (3 tests) 5ms
308
- ✓  node  test/utils/localization/bindingPaths.test.ts (71 tests) 19ms
309
- ✓  browser  test/sdk/session/reconcileSessionLocale.disabled.browser.test.ts (1 test) 3ms
310
- ✓  node  test/server/password-protection-service.test.ts (22 tests) 42ms
304
+ ✓  browser  test/sdk/account/clearPersistedSessionState.browser.test.ts (1 test) 6ms
305
+ ✓  browser  test/components/account/OrganizationDrawerFooter.browser.test.tsx (3 tests) 208ms
306
+ ✓  browser  test/utils/isLocalHost.browser.test.ts (3 tests) 3ms
307
+ ✓  node  test/utils/localization/bindingPaths.test.ts (71 tests) 15ms
308
+ ✓  browser  test/sdk/session/reconcileSessionLocale.disabled.browser.test.ts (1 test) 2ms
311
309
  stderr | test/sdk/account/refreshToken.browser.test.ts
312
310
  Error in optimistic validation: TypeError: Cannot read properties of undefined (reading 'read')
313
311
  at validateCart (/home/runner/work/faststore/faststore/packages/core/src/sdk/cart/index.ts:121:27)
@@ -315,11 +313,12 @@ This ensures that you're testing the behavior the user would see in the browser.
315
313
  at a (/home/runner/work/faststore/faststore/packages/sdk/dist/es/index.mjs:354:23)
316
314
   at processTicksAndRejections (node:internal/process/task_queues:103:5)
317
315
 
318
- ✓  browser  test/sdk/account/refreshToken.browser.test.ts (1 test) 5ms
319
- ✓  node  test/utils/match-url.test.ts (10 tests) 8ms
320
- ✓  node  test/sdk/localization/useBindingSelector.test.tsx (22 tests) 16ms
321
- ✓  node  test/pages/api/unlock.test.ts (15 tests) 308ms
322
- ✓  node  test/sdk/localization/bindingSelector.test.ts (18 tests) 11ms
316
+ ✓  node  test/server/password-protection-service.test.ts (22 tests) 24ms
317
+ ✓  browser  test/sdk/account/refreshToken.browser.test.ts (1 test) 6ms
318
+ ✓  node  test/utils/match-url.test.ts (10 tests) 5ms
319
+ ✓  node  test/sdk/localization/useBindingSelector.test.tsx (22 tests) 7ms
320
+ ✓  node  test/pages/api/unlock.test.ts (15 tests) 149ms
321
+ ✓  node  test/sdk/localization/bindingSelector.test.ts (18 tests) 5ms
323
322
  stderr | test/components/search/SearchInputComponent.test.tsx
324
323
  Warning: forwardRef render functions accept exactly two parameters: props and ref. Did you forget to use the ref parameter?
325
324
 
@@ -383,23 +382,23 @@ Warning: React does not recognize the `providerProps` prop on a DOM element. If
383
382
  at Suspense
384
383
  at Wrapper (/home/runner/work/faststore/faststore/packages/core/test/components/search/SearchInputComponent.test.tsx:106:20)
385
384
 
386
- ✓  node  test/components/search/SearchInputComponent.test.tsx (6 tests) 73ms
387
- ✓  node  test/sdk/search/useSearchHistory.test.ts (13 tests) 59ms
388
- ✓  node  test/utils/cookieCacheBusting.test.ts (10 tests) 11ms
389
- ✓  node  test/utils/clearCookies.test.ts (20 tests) 23ms
385
+ ✓  node  test/components/search/SearchInputComponent.test.tsx (6 tests) 60ms
386
+ ✓  node  test/utils/cookieCacheBusting.test.ts (10 tests) 7ms
387
+ ✓  node  test/sdk/search/useSearchHistory.test.ts (13 tests) 32ms
388
+ ✓  node  test/experimental/myAccountCmsServerSideProps.test.ts (5 tests) 4ms
389
+ ✓  node  test/utils/clearCookies.test.ts (20 tests) 20ms
390
390
  stderr | test/sdk/account/useSetPassword.test.ts > useSetPassword > maps wrongcredentials error responses to the existing message
391
391
  Set password request failed: Bad Request
392
392
 
393
- ✓  node  test/sdk/account/useSetPassword.test.ts (6 tests) 38ms
394
- ✓  node  test/components/quotes/MyAccountListQuotesTable.test.tsx (12 tests) 365ms
395
- ✓  node  test/components/quotes/quoteFilters.test.ts (23 tests) 25ms
396
- ✓  node  test/utils/multipleTemplates.test.ts (8 tests) 7ms
397
- ✓  node  test/components/quotes/MyAccountListQuotes.test.tsx (9 tests) 835ms
398
- ✓ navigates with the search term on blur  360ms
399
- ✓ navigates without a label param when the search is cleared  358ms
400
- ✓  node  test/utils/localization/localizeRedirectDestination.test.ts (11 tests) 7ms
401
- ✓  node  test/sdk/orderEntry/useOrderEntryOperation.test.ts (8 tests) 43ms
402
- ✓  node  test/sdk/orderEntry/useOrderEntryUpload.test.ts (8 tests) 44ms
393
+ ✓  node  test/sdk/account/useSetPassword.test.ts (6 tests) 27ms
394
+ ✓  node  test/components/quotes/MyAccountListQuotesTable.test.tsx (12 tests) 276ms
395
+ ✓  node  test/components/quotes/quoteFilters.test.ts (23 tests) 20ms
396
+ ✓  node  test/utils/multipleTemplates.test.ts (8 tests) 3ms
397
+ ✓  node  test/utils/localization/localizeRedirectDestination.test.ts (11 tests) 6ms
398
+ ✓  node  test/sdk/orderEntry/useOrderEntryOperation.test.ts (8 tests) 22ms
399
+ ✓  node  test/components/quotes/MyAccountListQuotes.test.tsx (9 tests) 792ms
400
+ ✓ navigates with the search term on blur  358ms
401
+ ✓ navigates without a label param when the search is cleared  357ms
403
402
  stderr | test/pages/api/graphql.test.ts > /api/graphql error status propagation > propagates the upstream 400 from a wrapped BadRequestError instead of 500
404
403
  Graphql execution returned with error: [
405
404
  {
@@ -439,6 +438,7 @@ Warning: React does not recognize the `providerProps` prop on a DOM element. If
439
438
  stderr | test/pages/api/graphql.test.ts > /api/graphql error status propagation > omits the free-text message from the body in production
440
439
  Graphql execution returned with error: [ { message: 'invalid CEP', status: 400, type: 'BadRequestError' } ]
441
440
 
441
+ ✓  node  test/pages/api/graphql.test.ts (10 tests) 22ms
442
442
  stderr | test/pages/api/graphql.test.ts > /api/graphql error status propagation > uses the first recoverable FastStoreError when several errors exist
443
443
  Graphql execution returned with error: [
444
444
  { message: 'plain error', status: undefined, type: undefined },
@@ -446,47 +446,47 @@ Warning: React does not recognize the `providerProps` prop on a DOM element. If
446
446
  { message: 'bad', status: 400, type: 'BadRequestError' }
447
447
  ]
448
448
 
449
- ✓  node  test/pages/api/graphql.test.ts (10 tests) 31ms
449
+ ✓  node  test/sdk/orderEntry/useOrderEntryUpload.test.ts (8 tests) 36ms
450
+ ✓  node  test/components/quotes/MyAccountQuotesFilterSlider.test.tsx (5 tests) 108ms
450
451
  ✓  node  test/sdk/analytics/useStartRecommendationSession.test.ts (6 tests) 40ms
451
- ✓  node  test/components/quotes/MyAccountQuotesFilterSlider.test.tsx (5 tests) 153ms
452
- ✓  node  test/proxy.localization.test.ts (5 tests) 45ms
453
- ✓  node  test/server/index.test.ts (7 tests) 1256ms
454
- ✓ should return a valid merged GraphQL schema  306ms
455
- ✓ should handle options and execute  689ms
456
- ✓  node  test/sdk/orderEntry/useOrderEntry.test.ts (8 tests) 48ms
457
- ✓  node  test/proxy.test.ts (4 tests) 51ms
458
- ✓  node  test/components/quotes/MyAccountQuotesSelectedTags.test.tsx (8 tests) 100ms
459
- ✓  node  test/server/webops-api.test.ts (9 tests) 22ms
460
- ✓  node  test/sdk/account/refreshToken.test.ts (5 tests) 9ms
461
- ✓  node  test/sdk/orderEntry/useOrderFormItems.test.ts (7 tests) 44ms
462
- ✓  node  test/server/content/service.test.ts (5 tests) 5ms
463
- ✓  node  test/server/cms/global.test.ts (3 tests) 5ms
464
- ✓  node  test/sdk/account/getMyAccountRoutes.test.ts (8 tests) 8ms
465
- ✓  node  test/sdk/localization/store-url.test.ts (1 test) 4ms
466
- ✓  node  test/sdk/analytics/hasEnabledRecommendationShelf.test.ts (6 tests) 5ms
467
- ✓  node  test/utils/getRequestHostname.test.ts (12 tests) 5ms
468
- ✓  node  test/sdk/auth/useAuth.test.ts (5 tests) 38ms
469
- ✓  node  test/components/quotes/MyAccountQuotesStatusSelector.test.tsx (6 tests) 129ms
470
- ✓  node  test/pages/api/preview.test.ts (2 tests) 9ms
471
- ✓  node  test/utils/validateSessionRefreshToken.test.ts (6 tests) 4ms
472
- ✓  node  test/components/search/SearchInput.test.ts (6 tests) 7ms
473
- ✓  node  test/components/RecommendationShelf/vrn.test.ts (5 tests) 6ms
452
+ ✓  node  test/proxy.localization.test.ts (5 tests) 46ms
453
+ ✓  node  test/sdk/account/getMyAccountRoutes.test.ts (10 tests) 6ms
454
+ ✓  node  test/sdk/orderEntry/useOrderEntry.test.ts (8 tests) 28ms
455
+ ✓  node  test/server/index.test.ts (7 tests) 816ms
456
+ ✓ should handle options and execute  386ms
457
+ ✓  node  test/proxy.test.ts (4 tests) 29ms
458
+ ✓  node  test/server/webops-api.test.ts (9 tests) 20ms
459
+ ✓  node  test/components/quotes/MyAccountQuotesSelectedTags.test.tsx (8 tests) 62ms
460
+ ✓  node  test/sdk/account/refreshToken.test.ts (5 tests) 6ms
461
+ ✓  node  test/sdk/orderEntry/useOrderFormItems.test.ts (7 tests) 24ms
462
+ ✓  node  test/server/content/service.test.ts (5 tests) 4ms
463
+ ✓  node  test/server/cms/global.test.ts (3 tests) 4ms
464
+ ✓  node  test/sdk/localization/store-url.test.ts (1 test) 2ms
465
+ ✓  node  test/sdk/analytics/hasEnabledRecommendationShelf.test.ts (6 tests) 3ms
466
+ ✓  node  test/utils/getRequestHostname.test.ts (12 tests) 4ms
467
+ ✓  node  test/sdk/auth/useAuth.test.ts (5 tests) 15ms
468
+ ✓  node  test/components/quotes/MyAccountQuotesStatusSelector.test.tsx (6 tests) 86ms
469
+ ✓  node  test/utils/validateSessionRefreshToken.test.ts (6 tests) 3ms
470
+ ✓  node  test/pages/api/preview.test.ts (2 tests) 6ms
471
+ ✓  node  test/components/search/SearchInput.test.ts (6 tests) 5ms
472
+ ✓  node  test/components/RecommendationShelf/vrn.test.ts (5 tests) 3ms
474
473
  ✓  node  test/server/cms/index.test.ts (2 tests) 4ms
475
- ✓  node  test/utils/retry.test.ts (5 tests) 14ms
476
- ✓  node  test/components/account/Layout.test.tsx (4 tests) 39ms
477
- ✓  node  test/components/RecommendationShelf/useRecommendationUserId.test.ts (4 tests) 190ms
478
- ✓  node  test/utils/unlockResponse.test.ts (11 tests) 6ms
479
- ✓  node  test/components/RecommendationShelf/useRecommendations.test.ts (2 tests) 4ms
480
- ✓  node  test/utils/isLocalHost.test.ts (7 tests) 5ms
481
- ✓  node  test/utils/userOrderStatus.test.ts (3 tests) 5ms
482
- ✓  node  test/components/account/OrganizationDrawerBody.test.tsx (3 tests) 51ms
483
- ✓  node  test/sdk/analytics/utils.test.ts (2 tests) 8ms
484
- ✓  node  test/components/auth/ProfileChallenge.test.tsx (3 tests) 44ms
485
- ✓  node  test/sdk/account/clearPersistedSessionState.test.ts (1 test) 11ms
486
- ✓  node  test/components/quotes/MyAccountQuoteStatusBadge.test.tsx (2 tests) 24ms
487
-
488
-  Test Files  71 passed (71)
489
-  Tests  558 passed (558)
490
-  Start at  20:03:32
491
-  Duration  36.20s (transform 8.20s, setup 0ms, collect 28.89s, tests 11.53s, environment 47.49s, prepare 1.20s)
474
+ ✓  node  test/utils/retry.test.ts (5 tests) 10ms
475
+ ✓  node  test/components/account/Layout.test.tsx (4 tests) 26ms
476
+ ✓  node  test/components/RecommendationShelf/useRecommendationUserId.test.ts (4 tests) 180ms
477
+ ✓  node  test/utils/unlockResponse.test.ts (11 tests) 4ms
478
+ ✓  node  test/server/cms/myAccountDefaultSections.test.ts (5 tests) 4ms
479
+ ✓  node  test/components/RecommendationShelf/useRecommendations.test.ts (2 tests) 2ms
480
+ ✓  node  test/utils/isLocalHost.test.ts (7 tests) 3ms
481
+ ✓  node  test/utils/userOrderStatus.test.ts (3 tests) 2ms
482
+ ✓  node  test/components/account/OrganizationDrawerBody.test.tsx (3 tests) 32ms
483
+ ✓  node  test/sdk/analytics/utils.test.ts (2 tests) 3ms
484
+ ✓  node  test/components/auth/ProfileChallenge.test.tsx (3 tests) 27ms
485
+ ✓  node  test/sdk/account/clearPersistedSessionState.test.ts (1 test) 7ms
486
+ ✓  node  test/components/quotes/MyAccountQuoteStatusBadge.test.tsx (2 tests) 16ms
487
+
488
+  Test Files  73 passed (73)
489
+  Tests  570 passed (570)
490
+  Start at  22:22:18
491
+  Duration  23.36s (transform 6.57s, setup 0ms, collect 19.79s, tests 7.36s, environment 29.70s, prepare 711ms)
492
492
 
package/CHANGELOG.md CHANGED
@@ -3,6 +3,16 @@
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
+ # [4.5.0](https://github.com/vtex/faststore/compare/v4.5.0-dev.7...v4.5.0) (2026-08-03)
7
+
8
+ **Note:** Version bump only for package @faststore/core
9
+
10
+ # [4.5.0-dev.7](https://github.com/vtex/faststore/compare/v4.5.0-dev.6...v4.5.0-dev.7) (2026-08-03)
11
+
12
+ ### Features
13
+
14
+ - CMS-driven custom My Account pages ([#3411](https://github.com/vtex/faststore/issues/3411)) ([e38b278](https://github.com/vtex/faststore/commit/e38b2780bb7386a8ef7b83a9e3de5956bf6de50e))
15
+
6
16
  # [4.5.0-dev.6](https://github.com/vtex/faststore/compare/v4.5.0-dev.5...v4.5.0-dev.6) (2026-07-28)
7
17
 
8
18
  ### Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faststore/core",
3
- "version": "4.5.0-dev.6",
3
+ "version": "4.5.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -71,11 +71,11 @@
71
71
  "style-loader": "^3.3.1",
72
72
  "swr": "^2.2.5",
73
73
  "use-sync-external-store": "^1.6.0",
74
- "@faststore/diagnostics": "4.5.0-dev.6",
75
- "@faststore/api": "4.5.0-dev.6",
76
- "@faststore/lighthouse": "4.5.0-dev.6",
77
- "@faststore/sdk": "4.5.0-dev.6",
78
- "@faststore/ui": "4.5.0-dev.6"
74
+ "@faststore/api": "4.5.0",
75
+ "@faststore/lighthouse": "4.5.0",
76
+ "@faststore/sdk": "4.5.0",
77
+ "@faststore/ui": "4.5.0",
78
+ "@faststore/diagnostics": "4.5.0"
79
79
  },
80
80
  "devDependencies": {
81
81
  "@cypress/code-coverage": "^3.12.1",
@@ -0,0 +1,146 @@
1
+ import { gql } from '@generated/gql'
2
+ import type {
3
+ ServerAccountPageQueryQuery,
4
+ ServerAccountPageQueryQueryVariables,
5
+ } from '@generated/graphql'
6
+ import type { Locator } from '@vtex/client-cms'
7
+ import type { GetServerSideProps } from 'next'
8
+
9
+ import { getGlobalSectionsData } from 'src/components/cms/GlobalSections'
10
+ import { getIsRepresentative } from 'src/sdk/account/getIsRepresentative'
11
+ import type { AccountNavigationLabels } from 'src/sdk/account/getMyAccountRoutes'
12
+ import { validateUser } from 'src/sdk/account/validateUser'
13
+ import { execute } from 'src/server'
14
+ import { fetchMyAccountPageContent } from 'src/server/cms/fetchMyAccountPageContent'
15
+ import { injectGlobalSections } from 'src/server/cms/global'
16
+ import { extractAccountNavigationData } from 'src/server/cms/myAccountDefaultSections'
17
+ import { localizeRedirectDestination } from 'src/utils/localization/localizeRedirectDestination'
18
+ import { withLocaleValidationSSR } from 'src/utils/localization/withLocaleValidation'
19
+ import { getMyAccountRedirect } from 'src/utils/myAccountRedirect'
20
+ import storeConfig from '../../discovery.config'
21
+ import type { MyAccountProps } from './myAccountServerSideProps'
22
+
23
+ export type MyAccountCmsPageProps = MyAccountProps & {
24
+ pageSections: Array<{
25
+ name: string
26
+ data: Record<string, unknown>
27
+ $componentKey?: string
28
+ }>
29
+ navigationLabels: AccountNavigationLabels
30
+ }
31
+
32
+ const query = gql(`
33
+ query ServerAccountPageQuery {
34
+ accountProfile {
35
+ name
36
+ }
37
+ }
38
+ `)
39
+
40
+ /**
41
+ * Reusable SSR factory for CMS-driven custom My Account pages.
42
+ * Keeps the same auth/redirect guards as `myAccountServerSideProps`.
43
+ */
44
+ export function myAccountCmsServerSideProps(
45
+ contentType: string,
46
+ pagePath: string
47
+ ): GetServerSideProps<MyAccountCmsPageProps, Record<string, string>, Locator> {
48
+ const handler: GetServerSideProps<
49
+ MyAccountCmsPageProps,
50
+ Record<string, string>,
51
+ Locator
52
+ > = async (context) => {
53
+ const contentContext = {
54
+ previewData: context.previewData,
55
+ locale: context.locale,
56
+ }
57
+
58
+ const validationResult = await validateUser(context)
59
+
60
+ if (!validationResult.isValid && !validationResult.needsRefresh) {
61
+ return {
62
+ redirect: {
63
+ destination: localizeRedirectDestination('/login', context),
64
+ permanent: false,
65
+ },
66
+ }
67
+ }
68
+
69
+ if (!validationResult.isValid && validationResult.needsRefresh) {
70
+ const currentPath = context.req.url || '/pvt/account'
71
+ return {
72
+ redirect: {
73
+ destination: localizeRedirectDestination(
74
+ `/pvt/account/403?from=${encodeURIComponent(currentPath)}`,
75
+ context
76
+ ),
77
+ permanent: false,
78
+ },
79
+ }
80
+ }
81
+
82
+ const { isFaststoreMyAccountEnabled, redirect } = getMyAccountRedirect({
83
+ query: context.query,
84
+ })
85
+
86
+ if (!isFaststoreMyAccountEnabled) {
87
+ return { redirect }
88
+ }
89
+
90
+ const isRepresentative = getIsRepresentative({
91
+ headers: context.req.headers as Record<string, string>,
92
+ account: storeConfig.api.storeId,
93
+ })
94
+
95
+ const [
96
+ globalSectionsPromise,
97
+ globalSectionsHeaderPromise,
98
+ globalSectionsFooterPromise,
99
+ ] = getGlobalSectionsData(contentContext)
100
+
101
+ const [
102
+ pageContent,
103
+ account,
104
+ globalSections,
105
+ globalSectionsHeader,
106
+ globalSectionsFooter,
107
+ ] = await Promise.all([
108
+ fetchMyAccountPageContent(contentType, contentContext, pagePath),
109
+ execute<
110
+ ServerAccountPageQueryQueryVariables,
111
+ ServerAccountPageQueryQuery
112
+ >(
113
+ {
114
+ variables: {},
115
+ operation: query,
116
+ },
117
+ { headers: { ...context.req.headers } }
118
+ ),
119
+ globalSectionsPromise,
120
+ globalSectionsHeaderPromise,
121
+ globalSectionsFooterPromise,
122
+ ])
123
+
124
+ const { pageSections, navigationData } = extractAccountNavigationData(
125
+ pageContent.sections
126
+ )
127
+
128
+ const globalSectionsResult = injectGlobalSections({
129
+ globalSections,
130
+ globalSectionsHeader,
131
+ globalSectionsFooter,
132
+ })
133
+
134
+ return {
135
+ props: {
136
+ globalSections: globalSectionsResult,
137
+ accountName: account.data?.accountProfile?.name ?? '',
138
+ isRepresentative,
139
+ pageSections,
140
+ navigationLabels: navigationData as AccountNavigationLabels,
141
+ },
142
+ }
143
+ }
144
+
145
+ return withLocaleValidationSSR(handler)
146
+ }
@@ -3,6 +3,12 @@ export interface Route {
3
3
  title: string
4
4
  /* Accessible path within My Account */
5
5
  route: string
6
+ /**
7
+ * When set, the page body renders from this CMS content-type.
8
+ * Presence of this field is the single opt-in for a CMS-driven page.
9
+ * Pure CMS vs hybrid is derived by the CLI from whether a code body exists.
10
+ */
11
+ contentType?: string
6
12
  }
7
13
 
8
14
  export type AccountNavigationLabels = Partial<{
@@ -1,4 +1,4 @@
1
- export type MyAccountContentType =
1
+ export type NativeMyAccountContentType =
2
2
  | 'myAccountProfile'
3
3
  | 'myAccountOrders'
4
4
  | 'myAccountOrderDetails'
@@ -6,13 +6,16 @@ export type MyAccountContentType =
6
6
  | 'myAccountSecurity'
7
7
  | 'myAccountUnauthorized'
8
8
 
9
+ /** Store content-types are opaque strings; native literals keep autocompletion. */
10
+ export type MyAccountContentType = NativeMyAccountContentType | (string & {})
11
+
9
12
  export type DefaultMyAccountSection = {
10
13
  name: string
11
14
  $componentKey: string
12
15
  data: Record<string, unknown>
13
16
  }
14
17
 
15
- const DEFAULT_SECTION_KEYS: Record<MyAccountContentType, string[]> = {
18
+ const DEFAULT_SECTION_KEYS: Record<NativeMyAccountContentType, string[]> = {
16
19
  myAccountProfile: ['AccountNavigation', 'AccountProfile'],
17
20
  myAccountOrders: ['AccountNavigation', 'AccountOrdersList'],
18
21
  myAccountOrderDetails: [
@@ -34,7 +37,9 @@ const DEFAULT_SECTION_KEYS: Record<MyAccountContentType, string[]> = {
34
37
  export function getDefaultMyAccountSections(
35
38
  contentType: MyAccountContentType
36
39
  ): DefaultMyAccountSection[] {
37
- return (DEFAULT_SECTION_KEYS[contentType] ?? []).map((key) => ({
40
+ return (
41
+ DEFAULT_SECTION_KEYS[contentType as NativeMyAccountContentType] ?? []
42
+ ).map((key) => ({
38
43
  name: key,
39
44
  $componentKey: key,
40
45
  data: {},
@@ -1,6 +1,16 @@
1
1
  import storeConfig from 'discovery.config'
2
2
  import type { ParsedUrlQuery } from 'node:querystring'
3
3
 
4
+ type MyAccountRedirect = { destination: string; permanent: boolean }
5
+
6
+ /**
7
+ * Discriminated union so callers that guard on `isFaststoreMyAccountEnabled`
8
+ * get a non-nullable `redirect`.
9
+ */
10
+ export type MyAccountRedirectResult =
11
+ | { isFaststoreMyAccountEnabled: false; redirect: MyAccountRedirect }
12
+ | { isFaststoreMyAccountEnabled: true; redirect: null }
13
+
4
14
  /**
5
15
  * Check if the Faststore My Account feature flag is enabled.
6
16
  * If not, redirect to the legacy My Account URL with the query parameters.
@@ -8,10 +18,9 @@ import type { ParsedUrlQuery } from 'node:querystring'
8
18
  * @param {ParsedUrlQuery} query - The query parameters from the request.
9
19
  * @returns {Object} - An object containing the feature flag status and redirect information.
10
20
  */
11
- export function getMyAccountRedirect({ query }: { query: ParsedUrlQuery }): {
12
- isFaststoreMyAccountEnabled: boolean
13
- redirect: { destination: string; permanent: boolean } | null
14
- } {
21
+ export function getMyAccountRedirect({
22
+ query,
23
+ }: { query: ParsedUrlQuery }): MyAccountRedirectResult {
15
24
  const isFaststoreMyAccountEnabled =
16
25
  storeConfig.experimental?.enableFaststoreMyAccount
17
26
 
@@ -29,8 +38,8 @@ export function getMyAccountRedirect({ query }: { query: ParsedUrlQuery }): {
29
38
  permanent: false,
30
39
  }
31
40
 
32
- return { isFaststoreMyAccountEnabled, redirect }
41
+ return { isFaststoreMyAccountEnabled: false, redirect }
33
42
  }
34
43
 
35
- return { isFaststoreMyAccountEnabled, redirect: null }
44
+ return { isFaststoreMyAccountEnabled: true, redirect: null }
36
45
  }
@@ -0,0 +1,221 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
2
+
3
+ const mockValidateUser = vi.fn()
4
+ const mockGetMyAccountRedirect = vi.fn()
5
+ const mockGetIsRepresentative = vi.fn()
6
+ const mockFetchMyAccountPageContent = vi.fn()
7
+ const mockGetGlobalSectionsData = vi.fn()
8
+ const mockExecute = vi.fn()
9
+ const mockInjectGlobalSections = vi.fn()
10
+ const mockLocalizeRedirectDestination = vi.fn(
11
+ (destination: string) => destination
12
+ )
13
+
14
+ vi.mock('src/sdk/account/validateUser', () => ({
15
+ validateUser: (...args: unknown[]) => mockValidateUser(...args),
16
+ }))
17
+
18
+ vi.mock('src/utils/myAccountRedirect', () => ({
19
+ getMyAccountRedirect: (...args: unknown[]) =>
20
+ mockGetMyAccountRedirect(...args),
21
+ }))
22
+
23
+ vi.mock('src/sdk/account/getIsRepresentative', () => ({
24
+ getIsRepresentative: (...args: unknown[]) => mockGetIsRepresentative(...args),
25
+ }))
26
+
27
+ vi.mock('src/server/cms/fetchMyAccountPageContent', () => ({
28
+ fetchMyAccountPageContent: (...args: unknown[]) =>
29
+ mockFetchMyAccountPageContent(...args),
30
+ }))
31
+
32
+ vi.mock('src/components/cms/GlobalSections', () => ({
33
+ getGlobalSectionsData: (...args: unknown[]) =>
34
+ mockGetGlobalSectionsData(...args),
35
+ }))
36
+
37
+ vi.mock('src/server', () => ({
38
+ execute: (...args: unknown[]) => mockExecute(...args),
39
+ }))
40
+
41
+ vi.mock('src/server/cms/global', () => ({
42
+ injectGlobalSections: (...args: unknown[]) =>
43
+ mockInjectGlobalSections(...args),
44
+ }))
45
+
46
+ vi.mock('src/utils/localization/localizeRedirectDestination', () => ({
47
+ localizeRedirectDestination: (...args: unknown[]) =>
48
+ mockLocalizeRedirectDestination(...(args as [string])),
49
+ }))
50
+
51
+ vi.mock('src/utils/localization/withLocaleValidation', () => ({
52
+ withLocaleValidationSSR: <T>(fn: T) => fn,
53
+ }))
54
+
55
+ vi.mock('../../../discovery.config', () => ({
56
+ default: { api: { storeId: 'teststore' } },
57
+ }))
58
+
59
+ vi.mock('@generated/gql', () => ({
60
+ gql: (source: string) => source,
61
+ }))
62
+
63
+ import { myAccountCmsServerSideProps } from '../../../src/experimental/myAccountCmsServerSideProps'
64
+
65
+ function buildContext(overrides: Record<string, unknown> = {}) {
66
+ return {
67
+ previewData: undefined,
68
+ locale: 'en-US',
69
+ query: {},
70
+ req: {
71
+ url: '/pvt/account/wishlist',
72
+ headers: {},
73
+ },
74
+ ...overrides,
75
+ } as never
76
+ }
77
+
78
+ describe('myAccountCmsServerSideProps', () => {
79
+ beforeEach(() => {
80
+ vi.clearAllMocks()
81
+ mockGetIsRepresentative.mockReturnValue(false)
82
+ mockGetGlobalSectionsData.mockReturnValue([
83
+ Promise.resolve({ sections: [], settings: {} }),
84
+ Promise.resolve({ sections: [] }),
85
+ Promise.resolve({ sections: [] }),
86
+ ])
87
+ mockInjectGlobalSections.mockReturnValue({
88
+ sections: [{ name: 'Children', data: {} }],
89
+ settings: {},
90
+ })
91
+ mockExecute.mockResolvedValue({
92
+ data: { accountProfile: { name: 'Ada' } },
93
+ errors: undefined,
94
+ })
95
+ })
96
+
97
+ it('redirects to login when the user is invalid and does not need refresh', async () => {
98
+ mockValidateUser.mockResolvedValue({
99
+ isValid: false,
100
+ needsRefresh: false,
101
+ })
102
+
103
+ const result = await myAccountCmsServerSideProps(
104
+ 'myAccountWishlist',
105
+ '/pvt/account/wishlist'
106
+ )(buildContext())
107
+
108
+ expect(result).toEqual({
109
+ redirect: { destination: '/login', permanent: false },
110
+ })
111
+ })
112
+
113
+ it('redirects to 403 when the user is invalid and needs refresh', async () => {
114
+ mockValidateUser.mockResolvedValue({
115
+ isValid: false,
116
+ needsRefresh: true,
117
+ })
118
+
119
+ const result = await myAccountCmsServerSideProps(
120
+ 'myAccountWishlist',
121
+ '/pvt/account/wishlist'
122
+ )(buildContext())
123
+
124
+ expect(result).toEqual({
125
+ redirect: {
126
+ destination: `/pvt/account/403?from=${encodeURIComponent('/pvt/account/wishlist')}`,
127
+ permanent: false,
128
+ },
129
+ })
130
+ })
131
+
132
+ it('returns the My Account disabled redirect', async () => {
133
+ mockValidateUser.mockResolvedValue({ isValid: true })
134
+ mockGetMyAccountRedirect.mockReturnValue({
135
+ isFaststoreMyAccountEnabled: false,
136
+ redirect: { destination: '/login', permanent: false },
137
+ })
138
+
139
+ const result = await myAccountCmsServerSideProps(
140
+ 'myAccountWishlist',
141
+ '/pvt/account/wishlist'
142
+ )(buildContext())
143
+
144
+ expect(result).toEqual({
145
+ redirect: { destination: '/login', permanent: false },
146
+ })
147
+ })
148
+
149
+ it('returns pageSections, navigationLabels and accountName on success', async () => {
150
+ mockValidateUser.mockResolvedValue({ isValid: true })
151
+ mockGetMyAccountRedirect.mockReturnValue({
152
+ isFaststoreMyAccountEnabled: true,
153
+ redirect: { destination: '/login', permanent: false },
154
+ })
155
+ mockFetchMyAccountPageContent.mockResolvedValue({
156
+ sections: [
157
+ {
158
+ name: 'AccountNavigation',
159
+ $componentKey: 'AccountNavigation',
160
+ data: { profileLabel: 'Profile' },
161
+ },
162
+ {
163
+ name: 'HelloAccount',
164
+ $componentKey: 'HelloAccount',
165
+ data: { label: 'Hi' },
166
+ },
167
+ ],
168
+ settings: {},
169
+ })
170
+
171
+ const result = await myAccountCmsServerSideProps(
172
+ 'myAccountWishlist',
173
+ '/pvt/account/wishlist'
174
+ )(buildContext())
175
+
176
+ expect(result).toMatchObject({
177
+ props: {
178
+ accountName: 'Ada',
179
+ isRepresentative: false,
180
+ navigationLabels: { profileLabel: 'Profile' },
181
+ pageSections: [
182
+ {
183
+ name: 'HelloAccount',
184
+ $componentKey: 'HelloAccount',
185
+ data: { label: 'Hi' },
186
+ },
187
+ ],
188
+ },
189
+ })
190
+ expect(mockFetchMyAccountPageContent).toHaveBeenCalledWith(
191
+ 'myAccountWishlist',
192
+ { previewData: undefined, locale: 'en-US' },
193
+ '/pvt/account/wishlist'
194
+ )
195
+ })
196
+
197
+ it('returns empty pageSections when the content-type is unpublished', async () => {
198
+ mockValidateUser.mockResolvedValue({ isValid: true })
199
+ mockGetMyAccountRedirect.mockReturnValue({
200
+ isFaststoreMyAccountEnabled: true,
201
+ redirect: { destination: '/login', permanent: false },
202
+ })
203
+ mockFetchMyAccountPageContent.mockResolvedValue({
204
+ sections: [],
205
+ settings: {},
206
+ })
207
+
208
+ const result = await myAccountCmsServerSideProps(
209
+ 'myAccountWishlist',
210
+ '/pvt/account/wishlist'
211
+ )(buildContext())
212
+
213
+ expect(result).toMatchObject({
214
+ props: {
215
+ pageSections: [],
216
+ navigationLabels: {},
217
+ accountName: 'Ada',
218
+ },
219
+ })
220
+ })
221
+ })
@@ -73,6 +73,45 @@ describe('getMyAccountRoutes', () => {
73
73
  expect(result.find((r) => r.route === QUOTES_ROUTE)?.title).toBe('Quotes')
74
74
  })
75
75
 
76
+ it('preserves contentType through getMyAccountRoutes', () => {
77
+ const result = getMyAccountRoutes({
78
+ routes: [
79
+ {
80
+ title: 'Wishlist',
81
+ route: '/pvt/account/wishlist',
82
+ contentType: 'myAccountWishlist',
83
+ },
84
+ ],
85
+ })
86
+
87
+ const wishlist = result.find((r) => r.route === '/pvt/account/wishlist')
88
+ expect(wishlist?.contentType).toBe('myAccountWishlist')
89
+ expect(wishlist?.title).toBe('Wishlist')
90
+ })
91
+
92
+ it('keeps custom-route titles when CMS labels override native routes (FR-011)', () => {
93
+ const result = getMyAccountRoutes({
94
+ routes: [
95
+ {
96
+ title: 'Wishlist',
97
+ route: '/pvt/account/wishlist',
98
+ contentType: 'myAccountWishlist',
99
+ },
100
+ ],
101
+ labels: { profileLabel: 'Meu Perfil', ordersLabel: 'Meus Pedidos' },
102
+ })
103
+
104
+ expect(result.find((r) => r.route === PROFILE_ROUTE)?.title).toBe(
105
+ 'Meu Perfil'
106
+ )
107
+ expect(result.find((r) => r.route === ORDERS_ROUTE)?.title).toBe(
108
+ 'Meus Pedidos'
109
+ )
110
+ expect(result.find((r) => r.route === '/pvt/account/wishlist')?.title).toBe(
111
+ 'Wishlist'
112
+ )
113
+ })
114
+
76
115
  it('marks Quotes as a B2B-only route', () => {
77
116
  expect(ROUTES_ONLY_FOR_B2B_MEMBERS).toContain(QUOTES_ROUTE)
78
117
  })
@@ -0,0 +1,49 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import {
4
+ getDefaultMyAccountSections,
5
+ withDefaultMyAccountSections,
6
+ } from '../../../src/server/cms/myAccountDefaultSections'
7
+
8
+ describe('getDefaultMyAccountSections', () => {
9
+ it('returns the native section list for myAccountProfile', () => {
10
+ expect(getDefaultMyAccountSections('myAccountProfile')).toEqual([
11
+ {
12
+ name: 'AccountNavigation',
13
+ $componentKey: 'AccountNavigation',
14
+ data: {},
15
+ },
16
+ { name: 'AccountProfile', $componentKey: 'AccountProfile', data: {} },
17
+ ])
18
+ })
19
+
20
+ it('returns an empty list for store (unknown) content-types', () => {
21
+ expect(getDefaultMyAccountSections('myAccountWishlist')).toEqual([])
22
+ })
23
+ })
24
+
25
+ describe('withDefaultMyAccountSections', () => {
26
+ it('returns published sections when present', () => {
27
+ const sections = [
28
+ {
29
+ name: 'HelloAccount',
30
+ $componentKey: 'HelloAccount',
31
+ data: { label: 'Hi' },
32
+ },
33
+ ]
34
+
35
+ expect(withDefaultMyAccountSections('myAccountWishlist', sections)).toEqual(
36
+ sections
37
+ )
38
+ })
39
+
40
+ it('falls back to defaults for native types when sections are empty', () => {
41
+ expect(withDefaultMyAccountSections('myAccountSecurity', [])).toEqual(
42
+ getDefaultMyAccountSections('myAccountSecurity')
43
+ )
44
+ })
45
+
46
+ it('falls back to [] for store types when sections are empty', () => {
47
+ expect(withDefaultMyAccountSections('myAccountWishlist', null)).toEqual([])
48
+ })
49
+ })