@faststore/core 3.99.3 → 3.99.6

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 (52) 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/edge-server-production/0.pack +0 -0
  8. package/.next/cache/webpack/edge-server-production/index.pack +0 -0
  9. package/.next/cache/webpack/server-production/0.pack +0 -0
  10. package/.next/cache/webpack/server-production/index.pack +0 -0
  11. package/.next/prerender-manifest.js +1 -1
  12. package/.next/prerender-manifest.json +1 -1
  13. package/.next/react-loadable-manifest.json +6 -6
  14. package/.next/routes-manifest.json +1 -1
  15. package/.next/server/chunks/3890.js +1 -1
  16. package/.next/server/chunks/948.js +1 -1
  17. package/.next/server/chunks/9563.js +1 -1
  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 +1 -1
  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/{WPYJvsqBdHs3zpay3w2iE → UmIFhJcdMOfy3xrHNNZS2}/_buildManifest.js +1 -1
  31. package/.next/static/chunks/{2927.23bae2c79f0ac0f3.js → 2927.5a79877943a6bf7c.js} +1 -1
  32. package/.next/static/chunks/9173-515913f3f10caf5e.js +2 -0
  33. package/.next/static/chunks/{UIToast.de15325248043ce5.js → UIToast.19a8664c01a00d3a.js} +1 -1
  34. package/.next/static/chunks/pages/{_app-84aaff8a9d4de60e.js → _app-35e8dda638ee2672.js} +1 -1
  35. package/.next/static/chunks/pages/pvt/account/403-ccddb4364ef27179.js +1 -0
  36. package/.next/static/chunks/{webpack-61b283a96fbb5ee2.js → webpack-65b1de5f075b8367.js} +1 -1
  37. package/.next/trace +143 -143
  38. package/.turbo/turbo-build.log +11 -11
  39. package/.turbo/turbo-test.log +11 -9
  40. package/CHANGELOG.md +13 -0
  41. package/package.json +7 -7
  42. package/src/components/cms/RenderSections.tsx +38 -3
  43. package/src/pages/api/graphql.ts +2 -2
  44. package/src/pages/pvt/account/403.tsx +9 -0
  45. package/src/sdk/account/useRefreshToken.ts +12 -0
  46. package/src/sdk/session/index.ts +3 -7
  47. package/src/utils/isLocalHost.ts +33 -0
  48. package/test/utils/isLocalHost.browser.test.ts +40 -0
  49. package/test/utils/isLocalHost.test.ts +38 -0
  50. package/.next/static/chunks/9173-e89da04079c35360.js +0 -2
  51. package/.next/static/chunks/pages/pvt/account/403-491e6b062487b30f.js +0 -1
  52. /package/.next/static/{WPYJvsqBdHs3zpay3w2iE → UmIFhJcdMOfy3xrHNNZS2}/_ssgManifest.js +0 -0
@@ -1,23 +1,23 @@
1
1
 
2
- > @faststore/core@3.99.2 prebuild /home/runner/work/faststore/faststore/packages/core
2
+ > @faststore/core@3.99.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.2 partytown /home/runner/work/faststore/faststore/packages/core
6
+ > @faststore/core@3.99.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.2 generate /home/runner/work/faststore/faststore/packages/core
11
+ > @faststore/core@3.99.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.2 generate:schema /home/runner/work/faststore/faststore/packages/core
15
+ > @faststore/core@3.99.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.2 generate:codegen /home/runner/work/faststore/faststore/packages/core
20
+ > @faststore/core@3.99.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.2 format:generated /home/runner/work/faststore/faststore/packages/core
40
+ > @faststore/core@3.99.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.2 build /home/runner/work/faststore/faststore/packages/core
44
+ > @faststore/core@3.99.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
@@ -86,8 +86,8 @@ Search not found. Add a new component for this section or remove it from the CMS
86
86
 
87
87
  Generating static pages (2/6)
88
88
  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.
89
89
  Search not found. Add a new component for this section or remove it from the CMS
90
-
91
90
  Generating static pages (4/6)
92
91
  Search not found. Add a new component for this section or remove it from the CMS
92
+
93
93
  Generating static pages (4/6)
94
94
  Search not found. Add a new component for this section or remove it from the CMS
95
95
 
96
96
  ✓ Generating static pages (6/6)
97
97
  Finalizing page optimization ...
@@ -112,7 +112,7 @@ Route (pages) Size First Load JS
112
112
  ├ ● /login 1.7 kB 160 kB
113
113
  ├ λ /pvt/account 247 B 109 kB
114
114
  ├ ● /pvt/account/[...unknown] 287 B 109 kB
115
- ├ λ /pvt/account/403 2.97 kB 161 kB
115
+ ├ λ /pvt/account/403 3 kB 161 kB
116
116
  ├ └ css/6819dddf2b1b5ea7.css 4.84 kB
117
117
  ├ λ /pvt/account/404 2.18 kB 160 kB
118
118
  ├ └ css/ea11d74a2848b093.css 4.89 kB
@@ -130,8 +130,8 @@ Route (pages) Size First Load JS
130
130
  + First Load JS shared by all 118 kB
131
131
  ├ chunks/framework-d514426edf885c68.js 45.4 kB
132
132
  ├ chunks/main-ec03882c4375091d.js 33.2 kB
133
- ├ chunks/pages/_app-84aaff8a9d4de60e.js 26.5 kB
134
- ├ chunks/webpack-61b283a96fbb5ee2.js 3.89 kB
133
+ ├ chunks/pages/_app-35e8dda638ee2672.js 26.6 kB
134
+ ├ chunks/webpack-65b1de5f075b8367.js 3.89 kB
135
135
  └ css/146a8678babc61bf.css 9.2 kB
136
136
 
137
137
  λ (Server) server-side renders at runtime (uses getInitialProps or getServerSideProps)
@@ -1,17 +1,19 @@
1
1
 
2
- > @faststore/core@3.99.2 test /home/runner/work/faststore/faststore/packages/core
2
+ > @faststore/core@3.99.4 test /home/runner/work/faststore/faststore/packages/core
3
3
  > jest
4
4
 
5
- PASS test/utils/clearCookies.test.ts (11.209 s)
6
- PASS test/utils/multipleTemplates.test.ts (12.971 s)
5
+ PASS test/utils/multipleTemplates.test.ts (11.618 s)
6
+ PASS test/utils/clearCookies.test.ts (12.194 s)
7
+ PASS test/utils/cookieCacheBusting.test.ts (13.633 s)
7
8
  PASS test/server/cms/global.test.ts
8
9
  PASS test/utils/validateSessionRefreshToken.test.ts
9
- PASS test/utils/cookieCacheBusting.test.ts (14.113 s)
10
- PASS test/server/cms/index.test.ts (5.539 s)
11
- PASS test/server/index.test.ts (12.817 s)
10
+ PASS test/utils/isLocalHost.test.ts
11
+ PASS test/utils/isLocalHost.browser.test.ts
12
+ PASS test/server/cms/index.test.ts (6.005 s)
13
+ PASS test/server/index.test.ts (13.099 s)
12
14
 
13
- Test Suites: 7 passed, 7 total
14
- Tests: 58 passed, 58 total
15
+ Test Suites: 9 passed, 9 total
16
+ Tests: 68 passed, 68 total
15
17
  Snapshots: 0 total
16
- Time: 26.435 s
18
+ Time: 25.905 s
17
19
  Ran all test suites.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,19 @@
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.6](https://github.com/vtex/faststore/compare/v3.99.5...v3.99.6) (2026-05-19)
7
+
8
+ ### Bug Fixes
9
+
10
+ - bypass refresh-token flow on localhost in 403 page and useRefreshToken hook ([#3326](https://github.com/vtex/faststore/issues/3326)) ([5943129](https://github.com/vtex/faststore/commit/5943129ed4aa51823e90b7826a88a864f75ddd11)), closes [#3325](https://github.com/vtex/faststore/issues/3325) [#3325](https://github.com/vtex/faststore/issues/3325)
11
+
12
+ ## 3.99.4 (2026-05-14)
13
+
14
+ ### Bug Fixes
15
+
16
+ - region slider section render 3.x ([#3321](https://github.com/vtex/faststore/issues/3321)) ([8e9fa4f](https://github.com/vtex/faststore/commit/8e9fa4fae7d6c306e66164e3229d2f74b28982d3))
17
+ - use starter.store 3.x branch in CI ([#3324](https://github.com/vtex/faststore/issues/3324)) ([baaf13c](https://github.com/vtex/faststore/commit/baaf13cc791b7c54948f31fe2891ea3351706053)), closes [#3321](https://github.com/vtex/faststore/issues/3321) [#3321](https://github.com/vtex/faststore/issues/3321)
18
+
6
19
  ## [3.99.3](https://github.com/vtex/faststore/compare/v3.99.2...v3.99.3) (2026-05-12)
7
20
 
8
21
  ### Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faststore/core",
3
- "version": "3.99.3",
3
+ "version": "3.99.6",
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.99.1",
55
- "@faststore/graphql-utils": "^3.99.1",
56
- "@faststore/lighthouse": "3.99.1",
57
- "@faststore/sdk": "3.99.2",
58
- "@faststore/ui": "3.99.2",
54
+ "@faststore/api": "3.99.4",
55
+ "@faststore/graphql-utils": "^3.99.4",
56
+ "@faststore/lighthouse": "3.99.4",
57
+ "@faststore/sdk": "3.99.4",
58
+ "@faststore/ui": "3.99.4",
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",
@@ -126,5 +126,5 @@
126
126
  "ts-jest": "29.1.1",
127
127
  "typescript": "5.3.2"
128
128
  },
129
- "gitHead": "9246edbaf5fc09d779d11b17d357a07860f67d26"
129
+ "gitHead": "5636f4f2af5ef09cc522c45364d85d445ca5c716"
130
130
  }
@@ -23,7 +23,37 @@ interface Props {
23
23
  isInteractive?: boolean
24
24
  }
25
25
 
26
- const SECTIONS_OUT_OF_VIEWPORT = ['CartSidebar', 'RegionModal', 'RegionSlider']
26
+ const REGION_SLIDER_SECTION_NAME = 'RegionSlider'
27
+
28
+ const SECTIONS_OUT_OF_VIEWPORT = [
29
+ 'CartSidebar',
30
+ 'RegionModal',
31
+ REGION_SLIDER_SECTION_NAME,
32
+ ]
33
+
34
+ /**
35
+ * Ensure a RegionSlider section exists in the iteration. After PR #2961 the
36
+ * section was removed from `sections.json`, so CP-based stores (whose CP
37
+ * schemas were generated in PR #3071) and fresh hCMS stores no longer have it
38
+ * in their CMS content. The runtime still expects the section to flow through
39
+ * `RenderSections` so that `LazyLoadingSection` can gate the lazy import on
40
+ * `regionSlider.isOpen`. Inject the entry only if it is not already present in
41
+ * `sections` or `globalSections` to avoid double-rendering for legacy hCMS
42
+ * stores that have it persisted in their CMS.
43
+ */
44
+ function withRegionSliderSection(
45
+ sections: Section[] | undefined,
46
+ globalSections: Section[] | undefined
47
+ ): Section[] {
48
+ const base = sections ?? []
49
+ const isPresent =
50
+ base.some(({ name }) => name === REGION_SLIDER_SECTION_NAME) ||
51
+ globalSections?.some(({ name }) => name === REGION_SLIDER_SECTION_NAME)
52
+
53
+ if (isPresent) return base
54
+
55
+ return [...base, { name: REGION_SLIDER_SECTION_NAME, data: {} }]
56
+ }
27
57
 
28
58
  const Toast = dynamic(
29
59
  () => import(/* webpackChunkName: "Toast" */ '../common/Toast'),
@@ -148,6 +178,11 @@ function RenderSections({
148
178
  globalSections ?? sections
149
179
  )
150
180
 
181
+ const augmentedSections = useMemo(
182
+ () => withRegionSliderSection(sections, globalSections),
183
+ [sections, globalSections]
184
+ )
185
+
151
186
  const { isInteractive } = useTTI()
152
187
  const router = useRouter()
153
188
 
@@ -170,9 +205,9 @@ function RenderSections({
170
205
  isInteractive={isInteractive}
171
206
  />
172
207
  )}
173
- {sections && sections.length > 0 && (
208
+ {augmentedSections.length > 0 && (
174
209
  <RenderSectionsBase
175
- sections={sections}
210
+ sections={augmentedSections}
176
211
  components={components}
177
212
  isInteractive={isInteractive}
178
213
  />
@@ -9,6 +9,7 @@ import type { NextApiHandler, NextApiRequest } from 'next'
9
9
 
10
10
  import discoveryConfig from 'discovery.config'
11
11
  import { getJWTAutCookie } from 'src/utils/getCookie'
12
+ import { isLocalHost } from 'src/utils/isLocalHost'
12
13
  import { shouldForceRefreshTokenForValidateSession } from 'src/utils/validateSessionRefreshToken'
13
14
  import { execute } from '../../server'
14
15
 
@@ -161,8 +162,7 @@ const handler: NextApiHandler = async (request, response) => {
161
162
  // value is used to cache bust the request if there is a VtexIdclientAutCookie
162
163
  const { operation, variables, query, v: value } = parseRequest(request)
163
164
 
164
- const hostname = request.headers.host?.split(':')[0]
165
- const isLocal = hostname === 'localhost' || hostname === '127.0.0.1'
165
+ const isLocal = isLocalHost(request.headers.host?.split(':')[0])
166
166
 
167
167
  if (
168
168
  !isLocal &&
@@ -23,6 +23,7 @@ import { useRefreshToken } from 'src/sdk/account/useRefreshToken'
23
23
  import PageProvider from 'src/sdk/overrides/PageProvider'
24
24
  import { execute } from 'src/server'
25
25
  import { injectGlobalSections } from 'src/server/cms/global'
26
+ import { isLocalHost } from 'src/utils/isLocalHost'
26
27
  import { getMyAccountRedirect } from 'src/utils/myAccountRedirect'
27
28
 
28
29
  import storeConfig from 'discovery.config'
@@ -132,10 +133,18 @@ export const getServerSideProps: GetServerSideProps<
132
133
  const fromPage =
133
134
  typeof context.query.from === 'string' ? context.query.from : ''
134
135
 
136
+ // The refresh-token round-trip is unreachable from localhost (cross-origin
137
+ // POST that drops the `vid_rt` cookie), and forcing it would clear the
138
+ // manually injected `VtexIdclientAutCookie_<account>`. Render the static
139
+ // 403 view instead so developers can keep testing logged-in scenarios
140
+ // regardless of the `experimental.refreshToken` flag.
141
+ const isLocal = isLocalHost(context.req.headers.host?.split(':')[0])
142
+
135
143
  return {
136
144
  props: {
137
145
  globalSections: globalSectionsResult,
138
146
  needsRefreshToken:
147
+ !isLocal &&
139
148
  (statusCode === 401 || statusCode === 403) &&
140
149
  storeConfig.experimental?.refreshToken,
141
150
  fromPage,
@@ -1,4 +1,5 @@
1
1
  import { useEffect, useState } from 'react'
2
+ import { isLocalHostBrowser } from 'src/utils/isLocalHost'
2
3
  import { logoutAndClearSession, sessionStore } from '../session'
3
4
  import { isRefreshTokenSuccessful, refreshTokenRequest } from './refreshToken'
4
5
 
@@ -12,6 +13,17 @@ export const useRefreshToken = (
12
13
  const handleRefreshTokenAndUpdateSession = async () => {
13
14
  if (!needsRefreshToken) return
14
15
 
16
+ // The refresh-token endpoint lives on the production origin and relies on
17
+ // the `vid_rt` cookie scoped to that origin. From localhost the request is
18
+ // cross-origin, so the cookie is not sent and the refresh always fails —
19
+ // which would also wipe the manually injected `VtexIdclientAutCookie_<account>`
20
+ // via `logoutAndClearSession`. Bail out and fall back to the static 403
21
+ // view so developers can keep testing logged-in flows.
22
+ if (isLocalHostBrowser()) {
23
+ setShouldShow403(true)
24
+ return
25
+ }
26
+
15
27
  const currentSession = sessionStore.read() ?? sessionStore.readInitial()
16
28
 
17
29
  try {
@@ -8,6 +8,7 @@ import type {
8
8
  ValidateSessionMutationVariables,
9
9
  } from '@generated/graphql'
10
10
  import deepEqual from 'fast-deep-equal'
11
+ import { isLocalHostBrowser } from 'src/utils/isLocalHost'
11
12
  import storeConfig from '../../../discovery.config'
12
13
  import {
13
14
  isRefreshTokenSuccessful,
@@ -19,11 +20,6 @@ import { createValidationStore, useStore } from '../useStore'
19
20
  import { getPostalCode } from '../userLocation/index'
20
21
  import { RELOAD_AFTER_LOGOUT_KEY, SESSION_READY_KEY } from './storageKeys'
21
22
 
22
- const isLocalEnvironment = (): boolean =>
23
- typeof window !== 'undefined' &&
24
- (window.location.hostname === 'localhost' ||
25
- window.location.hostname === '127.0.0.1')
26
-
27
23
  const isReloadAfterLogoutPending = (): boolean => {
28
24
  try {
29
25
  return (
@@ -127,7 +123,7 @@ export const validateSession = async (session: Session) => {
127
123
  // On failure (logoutAndClearSession already triggered), bail out.
128
124
  // Skipped in local environments where the refresh token infrastructure is unavailable.
129
125
  if (
130
- !isLocalEnvironment() &&
126
+ !isLocalHostBrowser() &&
131
127
  storeConfig.experimental?.refreshToken &&
132
128
  isRefreshAfterExpired(session)
133
129
  ) {
@@ -180,7 +176,7 @@ export const validateSession = async (session: Session) => {
180
176
  return data.validateSession
181
177
  } catch (error) {
182
178
  const shouldRefreshToken =
183
- !isLocalEnvironment() &&
179
+ !isLocalHostBrowser() &&
184
180
  error?.status === 401 &&
185
181
  storeConfig.experimental?.refreshToken
186
182
 
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Hostnames that represent a local development environment.
3
+ *
4
+ * These are the only hosts where the FastStore app skips the refresh-token
5
+ * flow so developers can drive the app with a manually injected
6
+ * `VtexIdclientAutCookie_<account>` cookie regardless of feature flags.
7
+ */
8
+ const LOCAL_HOSTNAMES = new Set(['localhost', '127.0.0.1'])
9
+
10
+ /**
11
+ * Returns `true` when the given hostname corresponds to a local development
12
+ * environment (`localhost` / `127.0.0.1`).
13
+ *
14
+ * Refresh-token and other production-only flows MUST be bypassed on these
15
+ * hostnames so that developers can test logged-in scenarios with manually
16
+ * injected cookies, independent of any experimental flag.
17
+ *
18
+ * The input MUST be a bare hostname (no port). Callers should strip the port
19
+ * (e.g. `host.split(':')[0]`) before invoking this helper.
20
+ */
21
+ export function isLocalHost(hostname: string | null | undefined): boolean {
22
+ if (!hostname) return false
23
+ return LOCAL_HOSTNAMES.has(hostname.toLowerCase())
24
+ }
25
+
26
+ /**
27
+ * Browser-side wrapper that reads `window.location.hostname`. Returns `false`
28
+ * when `window` is not available (SSR/Node).
29
+ */
30
+ export function isLocalHostBrowser(): boolean {
31
+ if (typeof window === 'undefined') return false
32
+ return isLocalHost(window.location.hostname)
33
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ */
4
+
5
+ import { isLocalHostBrowser } from '../../src/utils/isLocalHost'
6
+
7
+ const originalLocation = window.location
8
+
9
+ function stubHostname(hostname: string) {
10
+ // jsdom forbids assigning to `window.location` directly, so we redefine the
11
+ // descriptor with a minimal stub that exposes the hostname we want to test.
12
+ Object.defineProperty(window, 'location', {
13
+ configurable: true,
14
+ value: { ...originalLocation, hostname },
15
+ })
16
+ }
17
+
18
+ describe('isLocalHostBrowser', () => {
19
+ afterEach(() => {
20
+ Object.defineProperty(window, 'location', {
21
+ configurable: true,
22
+ value: originalLocation,
23
+ })
24
+ })
25
+
26
+ it('returns true when window.location.hostname is "localhost"', () => {
27
+ stubHostname('localhost')
28
+ expect(isLocalHostBrowser()).toBe(true)
29
+ })
30
+
31
+ it('returns true when window.location.hostname is "127.0.0.1"', () => {
32
+ stubHostname('127.0.0.1')
33
+ expect(isLocalHostBrowser()).toBe(true)
34
+ })
35
+
36
+ it('returns false for a production hostname', () => {
37
+ stubHostname('brandless.fast.store')
38
+ expect(isLocalHostBrowser()).toBe(false)
39
+ })
40
+ })
@@ -0,0 +1,38 @@
1
+ import { isLocalHost } from '../../src/utils/isLocalHost'
2
+
3
+ describe('isLocalHost', () => {
4
+ it('returns true for "localhost"', () => {
5
+ expect(isLocalHost('localhost')).toBe(true)
6
+ })
7
+
8
+ it('returns true for "127.0.0.1"', () => {
9
+ expect(isLocalHost('127.0.0.1')).toBe(true)
10
+ })
11
+
12
+ it('is case-insensitive', () => {
13
+ expect(isLocalHost('LOCALHOST')).toBe(true)
14
+ expect(isLocalHost('Localhost')).toBe(true)
15
+ })
16
+
17
+ it('returns false for a production hostname', () => {
18
+ expect(isLocalHost('brandless.fast.store')).toBe(false)
19
+ })
20
+
21
+ it('returns false for IPv6 loopback (not in the bypass list)', () => {
22
+ // We intentionally bypass only the two canonical local hosts. IPv6
23
+ // loopback (`::1`) and other loopback variants are out of scope.
24
+ expect(isLocalHost('::1')).toBe(false)
25
+ })
26
+
27
+ it('returns false for inputs that still carry a port', () => {
28
+ // Callers MUST normalize the hostname (strip the port) before invoking
29
+ // this helper.
30
+ expect(isLocalHost('localhost:3000')).toBe(false)
31
+ })
32
+
33
+ it('returns false for null/undefined/empty input', () => {
34
+ expect(isLocalHost(null)).toBe(false)
35
+ expect(isLocalHost(undefined)).toBe(false)
36
+ expect(isLocalHost('')).toBe(false)
37
+ })
38
+ })