@faststore/core 4.10.1 → 4.10.2-dev.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 (79) hide show
  1. package/.turbo/turbo-generate.log +3 -3
  2. package/.turbo/turbo-test.log +304 -252
  3. package/@generated/graphql.ts +1 -0
  4. package/@generated/schema.graphql +1 -1
  5. package/CHANGELOG.md +82 -0
  6. package/package.json +6 -6
  7. package/src/components/account/Drawer/OrganizationDrawer/OrganizationDrawer.tsx +3 -3
  8. package/src/components/account/Drawer/OrganizationDrawer/OrganizationDrawerBody.tsx +5 -7
  9. package/src/components/account/Drawer/OrganizationSignInButton/OrganizationSignInButton.tsx +4 -1
  10. package/src/components/account/Layout/Layout.tsx +5 -7
  11. package/src/components/account/Menu/Menu.tsx +1 -1
  12. package/src/components/navigation/Navbar/Navbar.tsx +3 -3
  13. package/src/components/navigation/Navbar/NavbarAccountArea.tsx +5 -5
  14. package/src/components/navigation/NavbarSlider/NavbarSlider.tsx +3 -3
  15. package/src/experimental/myAccountCmsServerSideProps.ts +3 -3
  16. package/src/experimental/myAccountServerSideProps.ts +4 -4
  17. package/src/pages/[slug]/p.tsx +18 -4
  18. package/src/pages/api/fs/logout.ts +18 -10
  19. package/src/pages/api/fs/switch-contract.ts +170 -21
  20. package/src/pages/api/graphql.ts +101 -60
  21. package/src/pages/pvt/account/403.tsx +5 -4
  22. package/src/pages/pvt/account/cards/index.tsx +5 -5
  23. package/src/pages/pvt/account/orders/[id].tsx +5 -5
  24. package/src/pages/pvt/account/orders/index.tsx +5 -5
  25. package/src/pages/pvt/account/profile.tsx +5 -5
  26. package/src/pages/pvt/account/quotes/index.tsx +5 -5
  27. package/src/pages/pvt/account/security.tsx +5 -5
  28. package/src/pages/pvt/account/user-details.tsx +6 -6
  29. package/src/sdk/account/getB2BSessionClaims.ts +1 -1
  30. package/src/sdk/account/getIsB2BBuyer.ts +28 -0
  31. package/src/sdk/account/refreshToken.ts +89 -11
  32. package/src/sdk/account/refreshTokenPaths.ts +25 -0
  33. package/src/sdk/account/useRefreshToken.ts +15 -11
  34. package/src/sdk/product/isProductNotFound.ts +13 -0
  35. package/src/sdk/product/usePageProductsQuery.ts +80 -21
  36. package/src/sdk/product/useProductNotFoundRedirect.ts +43 -0
  37. package/src/sdk/product/useProductQuery.ts +29 -0
  38. package/src/sdk/session/index.ts +46 -34
  39. package/src/server/cms/index.ts +59 -7
  40. package/src/server/cms/pdp.ts +10 -5
  41. package/src/server/cms/plp.ts +10 -5
  42. package/src/server/content/promiseCache.ts +68 -0
  43. package/src/server/content/service.ts +71 -51
  44. package/src/utils/getCheckoutParentDomain.ts +53 -0
  45. package/src/utils/isLocalHost.ts +2 -16
  46. package/src/utils/shouldBypassRefreshToken.ts +63 -0
  47. package/src/utils/trustedForwardedHost.ts +18 -0
  48. package/test/components/account/Layout.test.tsx +1 -1
  49. package/test/components/account/Menu.browser.test.tsx +7 -5
  50. package/test/components/account/OrganizationDrawer.browser.test.tsx +13 -13
  51. package/test/components/account/OrganizationDrawerBody.test.tsx +3 -3
  52. package/test/components/account/OrganizationSignInButton.browser.test.tsx +19 -4
  53. package/test/components/navigation/Navbar.browser.test.tsx +3 -3
  54. package/test/components/navigation/NavbarAccountArea.browser.test.tsx +5 -5
  55. package/test/components/navigation/NavbarSlider.browser.test.tsx +3 -3
  56. package/test/experimental/myAccountCmsServerSideProps.test.ts +5 -5
  57. package/test/pages/api/graphql.test.ts +175 -0
  58. package/test/pages/api/logout.test.ts +106 -0
  59. package/test/pages/api/switch-contract.test.ts +443 -15
  60. package/test/pages/pvt/account/403.test.ts +67 -0
  61. package/test/pages/pvt/account/cards/index.test.ts +3 -3
  62. package/test/sdk/account/getIsB2BBuyer.test.ts +52 -0
  63. package/test/sdk/account/refreshToken.browser.test.ts +10 -1
  64. package/test/sdk/account/refreshToken.test.ts +150 -18
  65. package/test/sdk/account/useRefreshToken.browser.test.ts +72 -0
  66. package/test/sdk/product/usePageProductsQuery.browser.test.ts +260 -0
  67. package/test/sdk/product/usePageProductsQuery.deliveryPromise.browser.test.ts +130 -0
  68. package/test/sdk/product/useProductNotFoundRedirect.browser.test.ts +83 -0
  69. package/test/sdk/product/useProductQuery.browser.test.ts +154 -0
  70. package/test/sdk/session/refreshTokenRoute.browser.test.ts +106 -0
  71. package/test/sdk/session/validateSession.browser.test.ts +111 -5
  72. package/test/server/cms/index.test.ts +102 -9
  73. package/test/server/content/promiseCache.test.ts +150 -0
  74. package/test/server/content/service.test.ts +277 -11
  75. package/test/utils/shouldBypassRefreshToken.browser.test.ts +38 -0
  76. package/test/utils/shouldBypassRefreshToken.test.ts +98 -0
  77. package/test/utils/trustedForwardedHost.test.ts +21 -0
  78. package/src/sdk/account/getIsRepresentative.ts +0 -20
  79. package/test/utils/isLocalHost.browser.test.ts +0 -37
@@ -0,0 +1,98 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import {
3
+ shouldBypassRefreshToken,
4
+ shouldBypassRefreshTokenBrowser,
5
+ shouldBypassRefreshTokenForRequest,
6
+ } from '../../src/utils/shouldBypassRefreshToken'
7
+
8
+ describe('shouldBypassRefreshToken', () => {
9
+ it.each(['localhost', '127.0.0.1'])('returns true for "%s"', (hostname) => {
10
+ expect(shouldBypassRefreshToken(hostname)).toBe(true)
11
+ })
12
+
13
+ it('returns true for a WebOps preview host', () => {
14
+ expect(shouldBypassRefreshToken('brandless-abc123.preview.vtex.app')).toBe(
15
+ true
16
+ )
17
+ })
18
+
19
+ it('is case-insensitive for preview hosts', () => {
20
+ expect(shouldBypassRefreshToken('Brandless-ABC123.Preview.VTEX.app')).toBe(
21
+ true
22
+ )
23
+ })
24
+
25
+ it('ignores the trailing dot of a fully qualified preview host', () => {
26
+ expect(shouldBypassRefreshToken('brandless-abc123.preview.vtex.app.')).toBe(
27
+ true
28
+ )
29
+ })
30
+
31
+ it('returns false for the production "{account}.vtex.app" domain', () => {
32
+ // `{account}.vtex.app` is a production default domain, not a preview.
33
+ expect(shouldBypassRefreshToken('brandless.vtex.app')).toBe(false)
34
+ })
35
+
36
+ it('returns false for a custom store domain', () => {
37
+ expect(shouldBypassRefreshToken('brandless.fast.store')).toBe(false)
38
+ })
39
+
40
+ it('returns false for hosts that only contain the preview suffix', () => {
41
+ expect(shouldBypassRefreshToken('preview.vtex.app')).toBe(false)
42
+ expect(shouldBypassRefreshToken('x.preview.vtex.app.evil.com')).toBe(false)
43
+ })
44
+
45
+ it('returns false for nullish or empty input', () => {
46
+ expect(shouldBypassRefreshToken(null)).toBe(false)
47
+ expect(shouldBypassRefreshToken(undefined)).toBe(false)
48
+ expect(shouldBypassRefreshToken('')).toBe(false)
49
+ })
50
+ })
51
+
52
+ describe('shouldBypassRefreshTokenForRequest', () => {
53
+ it('returns true when the Host header is a local or preview host', () => {
54
+ expect(shouldBypassRefreshTokenForRequest({ host: 'localhost:3000' })).toBe(
55
+ true
56
+ )
57
+ expect(
58
+ shouldBypassRefreshTokenForRequest({
59
+ host: 'brandless-abc123.preview.vtex.app',
60
+ })
61
+ ).toBe(true)
62
+ })
63
+
64
+ it('returns true when the preview host arrives in x-forwarded-host', () => {
65
+ // Behind the preview ingress, Host is an internal name.
66
+ expect(
67
+ shouldBypassRefreshTokenForRequest({
68
+ host: 'faststore-internal:3000',
69
+ 'x-forwarded-host': 'brandless-abc123.preview.vtex.app',
70
+ })
71
+ ).toBe(true)
72
+ })
73
+
74
+ it('ignores a multi-value x-forwarded-host', () => {
75
+ expect(
76
+ shouldBypassRefreshTokenForRequest({
77
+ host: 'brandless.fast.store',
78
+ 'x-forwarded-host': 'brandless-abc123.preview.vtex.app, proxy',
79
+ })
80
+ ).toBe(false)
81
+ })
82
+
83
+ it('returns false on production hosts', () => {
84
+ expect(
85
+ shouldBypassRefreshTokenForRequest({
86
+ host: 'brandless.fast.store',
87
+ 'x-forwarded-host': 'brandless.fast.store',
88
+ })
89
+ ).toBe(false)
90
+ expect(shouldBypassRefreshTokenForRequest({})).toBe(false)
91
+ })
92
+ })
93
+
94
+ describe('shouldBypassRefreshTokenBrowser', () => {
95
+ it('returns false when window is not available (SSR/Node)', () => {
96
+ expect(shouldBypassRefreshTokenBrowser()).toBe(false)
97
+ })
98
+ })
@@ -1,6 +1,7 @@
1
1
  import { describe, expect, it } from 'vitest'
2
2
 
3
3
  import {
4
+ isCookieSafeHostname,
4
5
  singleForwardedHost,
5
6
  isHostAllowed,
6
7
  removeCookieDomain,
@@ -103,3 +104,23 @@ describe('removeCookieDomain', () => {
103
104
  expect(removeCookieDomain('a=1; path=/')).toBe('a=1; path=/')
104
105
  })
105
106
  })
107
+
108
+ describe('isCookieSafeHostname', () => {
109
+ it.each(['store.example.com', 'b2b-store.vtexfaststore.com', 'Store.COM'])(
110
+ 'accepts %s',
111
+ (host) => {
112
+ expect(isCookieSafeHostname(host)).toBe(true)
113
+ }
114
+ )
115
+
116
+ it.each([
117
+ 'store.com;evil=1',
118
+ 'store.com path=/',
119
+ 'store.com,other.com',
120
+ 'store.com\r\nSet-Cookie: evil=1',
121
+ 'store.com%0d%0a',
122
+ '',
123
+ ])('rejects %j', (host) => {
124
+ expect(isCookieSafeHostname(host)).toBe(false)
125
+ })
126
+ })
@@ -1,20 +0,0 @@
1
- import { parse } from 'cookie'
2
- import { parseJwt } from 'src/utils/getCookie'
3
-
4
- type GetIsRepresentativeParams = {
5
- headers?: Record<string, string>
6
- account: string
7
- }
8
-
9
- export function getIsRepresentative({
10
- headers,
11
- account,
12
- }: GetIsRepresentativeParams): boolean {
13
- const authCookie = parse(headers?.cookie ?? '')?.[
14
- 'VtexIdclientAutCookie_' + account
15
- ]
16
- const jwt = parseJwt(authCookie)
17
-
18
- const isRepresentative = jwt?.isRepresentative
19
- return !!isRepresentative
20
- }
@@ -1,37 +0,0 @@
1
- import { afterEach, describe, expect, it } from 'vitest'
2
- import { isLocalHostBrowser } from '../../src/utils/isLocalHost'
3
-
4
- const originalLocation = window.location
5
-
6
- function stubHostname(hostname: string) {
7
- // jsdom forbids assigning to `window.location` directly, so we redefine the
8
- // descriptor with a minimal stub that exposes the hostname we want to test.
9
- Object.defineProperty(window, 'location', {
10
- configurable: true,
11
- value: { ...originalLocation, hostname },
12
- })
13
- }
14
-
15
- describe('isLocalHostBrowser', () => {
16
- afterEach(() => {
17
- Object.defineProperty(window, 'location', {
18
- configurable: true,
19
- value: originalLocation,
20
- })
21
- })
22
-
23
- it('returns true when window.location.hostname is "localhost"', () => {
24
- stubHostname('localhost')
25
- expect(isLocalHostBrowser()).toBe(true)
26
- })
27
-
28
- it('returns true when window.location.hostname is "127.0.0.1"', () => {
29
- stubHostname('127.0.0.1')
30
- expect(isLocalHostBrowser()).toBe(true)
31
- })
32
-
33
- it('returns false for a production hostname', () => {
34
- stubHostname('brandless.fast.store')
35
- expect(isLocalHostBrowser()).toBe(false)
36
- })
37
- })