From 19065c1d82400c057281a4f081eccfdc07672415 Mon Sep 17 00:00:00 2001 From: tedraykov Date: Fri, 21 Jun 2024 11:50:58 +0300 Subject: [PATCH] add useAuth context --- .nvmrc | 2 +- app/(auth)/authorize/page.tsx | 2 + app/(auth)/login/page.tsx | 3 +- app/layout.tsx | 19 +++-- components/auth/actions.ts | 37 ++++----- components/auth/login.tsx | 3 - components/profile/popover.tsx | 33 ++++++--- contexts/auth-context.tsx | 38 ++++++++++ hooks/use-auth.ts | 11 +++ lib/shopify/auth.ts | 4 +- package.json | 6 +- pnpm-lock.yaml | 132 ++++++++++++++++++--------------- 12 files changed, 181 insertions(+), 109 deletions(-) create mode 100644 contexts/auth-context.tsx create mode 100644 hooks/use-auth.ts diff --git a/.nvmrc b/.nvmrc index 3c032078a..209e3ef4b 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18 +20 diff --git a/app/(auth)/authorize/page.tsx b/app/(auth)/authorize/page.tsx index 723f8befb..806e5c47e 100644 --- a/app/(auth)/authorize/page.tsx +++ b/app/(auth)/authorize/page.tsx @@ -1,5 +1,7 @@ import { headers } from 'next/headers'; + export const runtime = 'edge'; + export default async function AuthorizationPage() { const headersList = headers(); const access = headersList.get('x-shop-access'); diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx index dce7c33dd..d8748e347 100644 --- a/app/(auth)/login/page.tsx +++ b/app/(auth)/login/page.tsx @@ -1,5 +1,6 @@ import { LoginMessage } from 'components/auth/login-message'; -export const runtime = 'edge'; //this needs to be here on thie page. I don't know why + +export const runtime = 'edge'; export default async function LoginPage() { return ( diff --git a/app/layout.tsx b/app/layout.tsx index df9c59c6f..468672c3d 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -4,6 +4,7 @@ import { GeistSans } from 'geist/font/sans'; import { ensureStartsWith } from 'lib/utils'; import { ReactNode, Suspense } from 'react'; import './globals.css'; +import { AuthProvider } from 'contexts/auth-context'; const { TWITTER_CREATOR, TWITTER_SITE, SITE_NAME } = process.env; const baseUrl = process.env.NEXT_PUBLIC_VERCEL_URL @@ -36,13 +37,17 @@ export default async function RootLayout({ children }: { children: ReactNode }) return ( -
- - -
- -
{children}
-
+ +
+
+ + +
+ +
{children}
+
+
+
); diff --git a/components/auth/actions.ts b/components/auth/actions.ts index 0692e039e..9f5542d4d 100644 --- a/components/auth/actions.ts +++ b/components/auth/actions.ts @@ -1,5 +1,3 @@ -//See https://react.dev/reference/react-dom/hooks/useFormState -//https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations#forms 'use server'; import { redirect } from 'next/navigation'; @@ -18,10 +16,8 @@ export async function doLogin(_: any) { const clientId = CUSTOMER_API_CLIENT_ID; const origin = ORIGIN_URL; const loginUrl = new URL(`${customerAccountApiUrl}/auth/oauth/authorize`); - //console.log ("previous", prevState) try { - //await addToCart(cartId, [{ merchandiseId: selectedVariantId, quantity: 1 }]); loginUrl.searchParams.set('client_id', clientId); loginUrl.searchParams.append('response_type', 'code'); loginUrl.searchParams.append('redirect_uri', `${origin}/authorize`); @@ -30,34 +26,33 @@ export async function doLogin(_: any) { 'openid email https://api.customers.com/auth/customer.graphql' ); const verifier = await generateCodeVerifier(); - //const newVerifier = verifier.replace("+", '_').replace("-",'_').replace("/",'_').trim() + const challenge = await generateCodeChallenge(verifier); - cookies().set('shop_verifier', verifier as string, { - // @ts-ignore - //expires: auth?.expires, //not necessary here - }); + cookies().set('shop_verifier', verifier as string, {}); const state = await generateRandomString(); const nonce = await generateRandomString(); - cookies().set('shop_state', state as string, { - // @ts-ignore - //expires: auth?.expires, //not necessary here - }); - cookies().set('shop_nonce', nonce as string, { - // @ts-ignore - //expires: auth?.expires, //not necessary here - }); + cookies().set('shop_state', state as string, {}); + cookies().set('shop_nonce', nonce as string, {}); + loginUrl.searchParams.append('state', state); loginUrl.searchParams.append('nonce', nonce); loginUrl.searchParams.append('code_challenge', challenge); loginUrl.searchParams.append('code_challenge_method', 'S256'); - //console.log ("loginURL", loginUrl) - //throw new Error ("Error") //this is how you throw an error, if you want to. Then the catch will execute } catch (e) { console.log('Error', e); - //you can throw error here or return - return goes back to form b/c of state, throw will throw the error boundary - //throw new Error ("Error") return 'Error logging in. Please try again'; } redirect(`${loginUrl}`); // Navigate to the new post page } + +export async function isLoggedIn() { + const customerToken = cookies().get('shop_customer_token')?.value; + const refreshToken = cookies().get('shop_refresh_token')?.value; + + if (!customerToken && !refreshToken) { + return false; + } else { + return true; + } +} diff --git a/components/auth/login.tsx b/components/auth/login.tsx index 8346013e1..f63123e45 100644 --- a/components/auth/login.tsx +++ b/components/auth/login.tsx @@ -6,9 +6,6 @@ export default async function Login() { const customerToken = cookies().get('shop_customer_token')?.value; const refreshToken = cookies().get('shop_refresh_token')?.value; let isLoggedIn; - //obviously just checking for the cookies without verifying the cookie itself is not ideal. However, the cookie is validated on the - //account page, so a "fake" cookie does nothing, except show the UI and then it would be deleted when clicking on account - //so for now, just checking the cookie for the UI is sufficient. Alternatively, we can do a query here, or a custom JWT if (!customerToken && !refreshToken) { isLoggedIn = false; } else { diff --git a/components/profile/popover.tsx b/components/profile/popover.tsx index 2af579db1..7f969a433 100644 --- a/components/profile/popover.tsx +++ b/components/profile/popover.tsx @@ -1,6 +1,5 @@ 'use client'; - -import { Popover, PopoverButton, PopoverPanel, Transition } from '@headlessui/react'; +import { CloseButton, Popover, PopoverButton, PopoverPanel, Transition } from '@headlessui/react'; import { ArrowRightIcon } from '@heroicons/react/16/solid'; import { Menu } from 'lib/shopify/types'; import { Fragment } from 'react'; @@ -8,6 +7,8 @@ import OpenProfile from './open-profile'; import { useFormState, useFormStatus } from 'react-dom'; import { doLogin } from 'components/auth/actions'; import { Button } from 'components/button'; +import useAuth from 'hooks/use-auth'; +import Link from 'next/link'; type ProfilePopoverProps = { menu: Menu[]; @@ -34,7 +35,8 @@ function SubmitButton(props: any) { ); } const ProfilePopover = ({ menu }: ProfilePopoverProps) => { - const [message, formAction] = useFormState(doLogin, null); + const [message] = useFormState(doLogin, null); + const { isAuthenticated, loading } = useAuth(); return ( @@ -53,22 +55,29 @@ const ProfilePopover = ({ menu }: ProfilePopoverProps) => {
My Account -
- -

- {message} -

- + {!isAuthenticated && !loading && } {menu.length ? ( -
    +
      + {isAuthenticated && ( +
    • + + My Orders + +
    • + )} {menu.map((menuItem) => (
    • - {menuItem.title} - +
    • ))}
    diff --git a/contexts/auth-context.tsx b/contexts/auth-context.tsx new file mode 100644 index 000000000..0034a0b21 --- /dev/null +++ b/contexts/auth-context.tsx @@ -0,0 +1,38 @@ +'use client'; +import { isLoggedIn } from 'components/auth/actions'; +import { createContext, useState, useEffect } from 'react'; + +type AuthContextType = { + isAuthenticated: boolean; + loading: boolean; +}; + +const AuthContext = createContext({ + isAuthenticated: false, + loading: true +}); + +type AuthProviderProps = { + children: React.ReactNode; +}; + +export function AuthProvider({ children }: AuthProviderProps) { + const [isAuthenticated, setIsAuthenticated] = useState(false); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function checkAuth() { + const isLogged = await isLoggedIn(); + setIsAuthenticated(isLogged); + setLoading(false); + } + + checkAuth(); + }, []); + + return ( + {children} + ); +} + +export default AuthContext; diff --git a/hooks/use-auth.ts b/hooks/use-auth.ts new file mode 100644 index 000000000..75fb4d7f0 --- /dev/null +++ b/hooks/use-auth.ts @@ -0,0 +1,11 @@ +import AuthContext from 'contexts/auth-context'; +import { useContext } from 'react'; + +export default function useAuth() { + const context = useContext(AuthContext); + + if (context === undefined) { + throw new Error('useAuth must be used within an AuthProvider'); + } + return context; +} diff --git a/lib/shopify/auth.ts b/lib/shopify/auth.ts index e115b4b20..b29024857 100644 --- a/lib/shopify/auth.ts +++ b/lib/shopify/auth.ts @@ -105,14 +105,13 @@ export async function initialAccessToken( headersNew.append('User-Agent', userAgent); headersNew.append('Origin', newOrigin || ''); const tokenRequestUrl = `${customerAccountApiUrl}/auth/oauth/token`; - console.log('sending request to', tokenRequestUrl); const response = await fetch(tokenRequestUrl, { method: 'POST', headers: headersNew, body }); - console.log('ok', response.ok); + if (!response.ok) { const error = await response.text(); console.log('data response error auth', error); @@ -336,7 +335,6 @@ export async function isLoggedIn(request: NextRequest, origin: string) { const refreshToken = request.cookies.get('shop_refresh_token'); const refreshTokenValue = refreshToken?.value; - console.log('customer token', customerTokenValue); const newHeaders = new Headers(request.headers); if (!customerTokenValue && !refreshTokenValue) { const redirectUrl = new URL(`${origin}`); diff --git a/package.json b/package.json index f7c29d18a..976bad353 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "private": true, "packageManager": "pnpm@9.1.2", "engines": { - "node": ">=18", + "node": ">=20", "pnpm": ">=7" }, "scripts": { @@ -32,7 +32,7 @@ "lodash.get": "^4.4.2", "lodash.kebabcase": "^4.1.1", "lodash.startcase": "^4.4.0", - "next": "14.1.4", + "next": "14.2.4", "react": "18.2.0", "react-dom": "18.2.0", "react-hook-form": "^7.51.5", @@ -55,7 +55,7 @@ "@vercel/git-hooks": "^1.0.0", "autoprefixer": "^10.4.19", "eslint": "^8.57.0", - "eslint-config-next": "^14.1.4", + "eslint-config-next": "^14.2.4", "eslint-config-prettier": "^9.1.0", "eslint-plugin-unicorn": "^51.0.1", "lint-staged": "^15.2.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3125ab071..a8d1f95f5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -28,7 +28,7 @@ importers: version: 2.1.0 geist: specifier: ^1.3.0 - version: 1.3.0(next@14.1.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0)) + version: 1.3.0(next@14.2.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0)) lodash.get: specifier: ^4.4.2 version: 4.4.2 @@ -39,8 +39,8 @@ importers: specifier: ^4.4.0 version: 4.4.0 next: - specifier: 14.1.4 - version: 14.1.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + specifier: 14.2.4 + version: 14.2.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react: specifier: 18.2.0 version: 18.2.0 @@ -103,8 +103,8 @@ importers: specifier: ^8.57.0 version: 8.57.0 eslint-config-next: - specifier: ^14.1.4 - version: 14.1.4(eslint@8.57.0)(typescript@5.4.3) + specifier: ^14.2.4 + version: 14.2.4(eslint@8.57.0)(typescript@5.4.3) eslint-config-prettier: specifier: ^9.1.0 version: 9.1.0(eslint@8.57.0) @@ -245,62 +245,62 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - '@next/env@14.1.4': - resolution: {integrity: sha512-e7X7bbn3Z6DWnDi75UWn+REgAbLEqxI8Tq2pkFOFAMpWAWApz/YCUhtWMWn410h8Q2fYiYL7Yg5OlxMOCfFjJQ==} + '@next/env@14.2.4': + resolution: {integrity: sha512-3EtkY5VDkuV2+lNmKlbkibIJxcO4oIHEhBWne6PaAp+76J9KoSsGvNikp6ivzAT8dhhBMYrm6op2pS1ApG0Hzg==} - '@next/eslint-plugin-next@14.1.4': - resolution: {integrity: sha512-n4zYNLSyCo0Ln5b7qxqQeQ34OZKXwgbdcx6kmkQbywr+0k6M3Vinft0T72R6CDAcDrne2IAgSud4uWCzFgc5HA==} + '@next/eslint-plugin-next@14.2.4': + resolution: {integrity: sha512-svSFxW9f3xDaZA3idQmlFw7SusOuWTpDTAeBlO3AEPDltrraV+lqs7mAc6A27YdnpQVVIA3sODqUAAHdWhVWsA==} - '@next/swc-darwin-arm64@14.1.4': - resolution: {integrity: sha512-ubmUkbmW65nIAOmoxT1IROZdmmJMmdYvXIe8211send9ZYJu+SqxSnJM4TrPj9wmL6g9Atvj0S/2cFmMSS99jg==} + '@next/swc-darwin-arm64@14.2.4': + resolution: {integrity: sha512-AH3mO4JlFUqsYcwFUHb1wAKlebHU/Hv2u2kb1pAuRanDZ7pD/A/KPD98RHZmwsJpdHQwfEc/06mgpSzwrJYnNg==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@14.1.4': - resolution: {integrity: sha512-b0Xo1ELj3u7IkZWAKcJPJEhBop117U78l70nfoQGo4xUSvv0PJSTaV4U9xQBLvZlnjsYkc8RwQN1HoH/oQmLlQ==} + '@next/swc-darwin-x64@14.2.4': + resolution: {integrity: sha512-QVadW73sWIO6E2VroyUjuAxhWLZWEpiFqHdZdoQ/AMpN9YWGuHV8t2rChr0ahy+irKX5mlDU7OY68k3n4tAZTg==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@14.1.4': - resolution: {integrity: sha512-457G0hcLrdYA/u1O2XkRMsDKId5VKe3uKPvrKVOyuARa6nXrdhJOOYU9hkKKyQTMru1B8qEP78IAhf/1XnVqKA==} + '@next/swc-linux-arm64-gnu@14.2.4': + resolution: {integrity: sha512-KT6GUrb3oyCfcfJ+WliXuJnD6pCpZiosx2X3k66HLR+DMoilRb76LpWPGb4tZprawTtcnyrv75ElD6VncVamUQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@14.1.4': - resolution: {integrity: sha512-l/kMG+z6MB+fKA9KdtyprkTQ1ihlJcBh66cf0HvqGP+rXBbOXX0dpJatjZbHeunvEHoBBS69GYQG5ry78JMy3g==} + '@next/swc-linux-arm64-musl@14.2.4': + resolution: {integrity: sha512-Alv8/XGSs/ytwQcbCHwze1HmiIkIVhDHYLjczSVrf0Wi2MvKn/blt7+S6FJitj3yTlMwMxII1gIJ9WepI4aZ/A==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@14.1.4': - resolution: {integrity: sha512-BapIFZ3ZRnvQ1uWbmqEGJuPT9cgLwvKtxhK/L2t4QYO7l+/DxXuIGjvp1x8rvfa/x1FFSsipERZK70pewbtJtw==} + '@next/swc-linux-x64-gnu@14.2.4': + resolution: {integrity: sha512-ze0ShQDBPCqxLImzw4sCdfnB3lRmN3qGMB2GWDRlq5Wqy4G36pxtNOo2usu/Nm9+V2Rh/QQnrRc2l94kYFXO6Q==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@14.1.4': - resolution: {integrity: sha512-mqVxTwk4XuBl49qn2A5UmzFImoL1iLm0KQQwtdRJRKl21ylQwwGCxJtIYo2rbfkZHoSKlh/YgztY0qH3wG1xIg==} + '@next/swc-linux-x64-musl@14.2.4': + resolution: {integrity: sha512-8dwC0UJoc6fC7PX70csdaznVMNr16hQrTDAMPvLPloazlcaWfdPogq+UpZX6Drqb1OBlwowz8iG7WR0Tzk/diQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@14.1.4': - resolution: {integrity: sha512-xzxF4ErcumXjO2Pvg/wVGrtr9QQJLk3IyQX1ddAC/fi6/5jZCZ9xpuL9Tzc4KPWMFq8GGWFVDMshZOdHGdkvag==} + '@next/swc-win32-arm64-msvc@14.2.4': + resolution: {integrity: sha512-jxyg67NbEWkDyvM+O8UDbPAyYRZqGLQDTPwvrBBeOSyVWW/jFQkQKQ70JDqDSYg1ZDdl+E3nkbFbq8xM8E9x8A==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-ia32-msvc@14.1.4': - resolution: {integrity: sha512-WZiz8OdbkpRw6/IU/lredZWKKZopUMhcI2F+XiMAcPja0uZYdMTZQRoQ0WZcvinn9xZAidimE7tN9W5v9Yyfyw==} + '@next/swc-win32-ia32-msvc@14.2.4': + resolution: {integrity: sha512-twrmN753hjXRdcrZmZttb/m5xaCBFa48Dt3FbeEItpJArxriYDunWxJn+QFXdJ3hPkm4u7CKxncVvnmgQMY1ag==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] - '@next/swc-win32-x64-msvc@14.1.4': - resolution: {integrity: sha512-4Rto21sPfw555sZ/XNLqfxDUNeLhNYGO2dlPqsnuCg8N8a2a9u1ltqBOPQ4vj1Gf7eJC0W2hHG2eYUHuiXgY2w==} + '@next/swc-win32-x64-msvc@14.2.4': + resolution: {integrity: sha512-tkLrjBzqFTP8DVrAAQmZelEahfR9OxWpFR++vAI9FBhCiIxtwHwBHC23SBHCTURBtwB4kc/x44imVOnkKGNVGg==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -469,9 +469,15 @@ packages: '@rushstack/eslint-patch@1.8.0': resolution: {integrity: sha512-0HejFckBN2W+ucM6cUOlwsByTKt9/+0tWhqUffNIcHqCXkthY/mZ7AuYPK/2IIaGWhdl0h+tICDO0ssLMd6XMQ==} + '@swc/counter@0.1.3': + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + '@swc/helpers@0.5.2': resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==} + '@swc/helpers@0.5.5': + resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==} + '@tailwindcss/aspect-ratio@0.4.2': resolution: {integrity: sha512-8QPrypskfBa7QIMuKHg2TA7BqES6vhBrDLOv8Unb6FcFyd3TjKbc6lcmb9UPQHxfl24sXoJ41ux/H7qQQvfaSQ==} peerDependencies: @@ -939,8 +945,8 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - eslint-config-next@14.1.4: - resolution: {integrity: sha512-cihIahbhYAWwXJwZkAaRPpUi5t9aOi/HdfWXOjZeUOqNWXHD8X22kd1KG58Dc3MVaRx3HoR/oMGk2ltcrqDn8g==} + eslint-config-next@14.2.4: + resolution: {integrity: sha512-Qr0wMgG9m6m4uYy2jrYJmyuNlYZzPRQq5Kvb9IDlYwn+7yq6W6sfMNFgb+9guM1KYwuIo6TIaiFhZJ6SnQ/Efw==} peerDependencies: eslint: ^7.23.0 || ^8.0.0 typescript: '>=3.3.1' @@ -1568,18 +1574,21 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - next@14.1.4: - resolution: {integrity: sha512-1WTaXeSrUwlz/XcnhGTY7+8eiaFvdet5z9u3V2jb+Ek1vFo0VhHKSAIJvDWfQpttWjnyw14kBeq28TPq7bTeEQ==} + next@14.2.4: + resolution: {integrity: sha512-R8/V7vugY+822rsQGQCjoLhMuC9oFj9SOi4Cl4b2wjDrseD0LRZ10W7R6Czo4w9ZznVSshKjuIomsRjvm9EKJQ==} engines: {node: '>=18.17.0'} hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.41.2 react: ^18.2.0 react-dom: ^18.2.0 sass: ^1.3.0 peerDependenciesMeta: '@opentelemetry/api': optional: true + '@playwright/test': + optional: true sass: optional: true @@ -2406,37 +2415,37 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 - '@next/env@14.1.4': {} + '@next/env@14.2.4': {} - '@next/eslint-plugin-next@14.1.4': + '@next/eslint-plugin-next@14.2.4': dependencies: glob: 10.3.10 - '@next/swc-darwin-arm64@14.1.4': + '@next/swc-darwin-arm64@14.2.4': optional: true - '@next/swc-darwin-x64@14.1.4': + '@next/swc-darwin-x64@14.2.4': optional: true - '@next/swc-linux-arm64-gnu@14.1.4': + '@next/swc-linux-arm64-gnu@14.2.4': optional: true - '@next/swc-linux-arm64-musl@14.1.4': + '@next/swc-linux-arm64-musl@14.2.4': optional: true - '@next/swc-linux-x64-gnu@14.1.4': + '@next/swc-linux-x64-gnu@14.2.4': optional: true - '@next/swc-linux-x64-musl@14.1.4': + '@next/swc-linux-x64-musl@14.2.4': optional: true - '@next/swc-win32-arm64-msvc@14.1.4': + '@next/swc-win32-arm64-msvc@14.2.4': optional: true - '@next/swc-win32-ia32-msvc@14.1.4': + '@next/swc-win32-ia32-msvc@14.2.4': optional: true - '@next/swc-win32-x64-msvc@14.1.4': + '@next/swc-win32-x64-msvc@14.2.4': optional: true '@nodelib/fs.scandir@2.1.5': @@ -2597,10 +2606,17 @@ snapshots: '@rushstack/eslint-patch@1.8.0': {} + '@swc/counter@0.1.3': {} + '@swc/helpers@0.5.2': dependencies: tslib: 2.6.2 + '@swc/helpers@0.5.5': + dependencies: + '@swc/counter': 0.1.3 + tslib: 2.6.2 + '@tailwindcss/aspect-ratio@0.4.2(tailwindcss@3.4.1)': dependencies: tailwindcss: 3.4.1 @@ -3148,9 +3164,9 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-next@14.1.4(eslint@8.57.0)(typescript@5.4.3): + eslint-config-next@14.2.4(eslint@8.57.0)(typescript@5.4.3): dependencies: - '@next/eslint-plugin-next': 14.1.4 + '@next/eslint-plugin-next': 14.2.4 '@rushstack/eslint-patch': 1.8.0 '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.4.3) eslint: 8.57.0 @@ -3454,9 +3470,9 @@ snapshots: functions-have-names@1.2.3: {} - geist@1.3.0(next@14.1.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0)): + geist@1.3.0(next@14.2.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0)): dependencies: - next: 14.1.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + next: 14.2.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) get-east-asian-width@1.2.0: {} @@ -3869,10 +3885,10 @@ snapshots: natural-compare@1.4.0: {} - next@14.1.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + next@14.2.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: - '@next/env': 14.1.4 - '@swc/helpers': 0.5.2 + '@next/env': 14.2.4 + '@swc/helpers': 0.5.5 busboy: 1.6.0 caniuse-lite: 1.0.30001600 graceful-fs: 4.2.11 @@ -3881,15 +3897,15 @@ snapshots: react-dom: 18.2.0(react@18.2.0) styled-jsx: 5.1.1(react@18.2.0) optionalDependencies: - '@next/swc-darwin-arm64': 14.1.4 - '@next/swc-darwin-x64': 14.1.4 - '@next/swc-linux-arm64-gnu': 14.1.4 - '@next/swc-linux-arm64-musl': 14.1.4 - '@next/swc-linux-x64-gnu': 14.1.4 - '@next/swc-linux-x64-musl': 14.1.4 - '@next/swc-win32-arm64-msvc': 14.1.4 - '@next/swc-win32-ia32-msvc': 14.1.4 - '@next/swc-win32-x64-msvc': 14.1.4 + '@next/swc-darwin-arm64': 14.2.4 + '@next/swc-darwin-x64': 14.2.4 + '@next/swc-linux-arm64-gnu': 14.2.4 + '@next/swc-linux-arm64-musl': 14.2.4 + '@next/swc-linux-x64-gnu': 14.2.4 + '@next/swc-linux-x64-musl': 14.2.4 + '@next/swc-win32-arm64-msvc': 14.2.4 + '@next/swc-win32-ia32-msvc': 14.2.4 + '@next/swc-win32-x64-msvc': 14.2.4 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros