diff --git a/core/app/[locale]/(default)/cart/page-data.ts b/core/app/[locale]/(default)/cart/page-data.ts index 93b9d963de..d5f884e610 100644 --- a/core/app/[locale]/(default)/cart/page-data.ts +++ b/core/app/[locale]/(default)/cart/page-data.ts @@ -1,8 +1,11 @@ +import { removeEdgesAndNodes } from '@bigcommerce/catalyst-client'; + import { getSessionCustomerAccessToken } from '~/auth'; import { client } from '~/client'; import { FragmentOf, graphql, VariablesOf } from '~/client/graphql'; import { getShippingZones } from '~/client/management/get-shipping-zones'; import { TAGS } from '~/client/tags'; +import { getPreferredCurrencyCode } from '~/lib/currency'; export const PhysicalItemFragment = graphql(` fragment PhysicalItemFragment on CartPhysicalItem { @@ -262,6 +265,98 @@ export const getCart = async (variables: Variables) => { return data; }; +const PaymentWalletsQuery = graphql(` + query PaymentWalletsQuery($filters: PaymentWalletsFilterInput) { + site { + paymentWallets(filter: $filters) { + edges { + node { + entityId + } + } + } + } + } +`); + +type PaymentWalletsVariables = VariablesOf; + +export const getPaymentWallets = async (variables: PaymentWalletsVariables) => { + const customerAccessToken = await getSessionCustomerAccessToken(); + + const { data } = await client.fetch({ + document: PaymentWalletsQuery, + customerAccessToken, + fetchOptions: { cache: 'no-store' }, + variables, + }); + + return removeEdgesAndNodes(data.site.paymentWallets).map(({ entityId }) => entityId); +}; + +const PaymentWalletWithInitializationDataQuery = graphql(` + query PaymentWalletWithInitializationDataQuery($entityId: String!, $cartId: String!) { + site { + paymentWalletWithInitializationData( + filter: { paymentWalletEntityId: $entityId, cartEntityId: $cartId } + ) { + clientToken + initializationData + } + } + } +`); + +export const getPaymentWalletWithInitializationData = async (entityId: string, cartId: string) => { + const { data } = await client.fetch({ + document: PaymentWalletWithInitializationDataQuery, + variables: { + entityId, + cartId, + }, + customerAccessToken: await getSessionCustomerAccessToken(), + fetchOptions: { cache: 'no-store' }, + }); + + return data.site.paymentWalletWithInitializationData; +}; + +const CurrencyQuery = graphql(` + query Currency($currencyCode: currencyCode!) { + site { + currency(currencyCode: $currencyCode) { + display { + decimalPlaces + symbol + } + name + code + } + } + } +`); + +export const getCurrencyData = async (currencyCode?: string) => { + const code = await getPreferredCurrencyCode(currencyCode); + + if (!code) { + throw new Error('Could not get currency code'); + } + + const customerAccessToken = await getSessionCustomerAccessToken(); + + const { data } = await client.fetch({ + document: CurrencyQuery, + fetchOptions: { cache: 'no-store' }, + variables: { + currencyCode: code, + }, + customerAccessToken, + }); + + return data.site.currency; +}; + export const getShippingCountries = async (geography: FragmentOf) => { const hasAccessToken = Boolean(process.env.BIGCOMMERCE_ACCESS_TOKEN); const shippingZones = hasAccessToken ? await getShippingZones() : []; diff --git a/core/app/[locale]/(default)/cart/page.tsx b/core/app/[locale]/(default)/cart/page.tsx index 19c2dc9449..1efc338cd1 100644 --- a/core/app/[locale]/(default)/cart/page.tsx +++ b/core/app/[locale]/(default)/cart/page.tsx @@ -12,7 +12,13 @@ import { updateCouponCode } from './_actions/update-coupon-code'; import { updateLineItem } from './_actions/update-line-item'; import { updateShippingInfo } from './_actions/update-shipping-info'; import { CartViewed } from './_components/cart-viewed'; -import { getCart, getShippingCountries } from './page-data'; +import { + getCart, + getCurrencyData, + getPaymentWallets, + getPaymentWalletWithInitializationData, + getShippingCountries, +} from './page-data'; interface Props { params: Promise<{ locale: string }>; @@ -28,6 +34,36 @@ export async function generateMetadata({ params }: Props): Promise { }; } +const createWalletButtonsInitOptions = async ( + walletButtons: string[], + cart: { + entityId: string; + currencyCode: string; + }, +) => { + const currencyData = await getCurrencyData(cart.currencyCode); + + return Streamable.all( + walletButtons.map(async (entityId) => { + const initData = await getPaymentWalletWithInitializationData(entityId, cart.entityId); + const methodId = entityId.split('.').join(''); + + return { + methodId, + containerId: `${methodId}-button`, + [methodId]: { + cartId: cart.entityId, + currency: { + code: currencyData?.code, + decimalPlaces: currencyData?.display.decimalPlaces, + }, + ...initData, + }, + }; + }), + ); +}; + const getAnalyticsData = async (cartId: string) => { const data = await getCart({ cartId }); @@ -88,6 +124,16 @@ export default async function Cart({ params }: Props) { ); } + const walletButtons = await getPaymentWallets({ + filters: { + cartEntityId: cartId, + }, + }); + + const walletButtonsInitOptions = Streamable.from(() => + createWalletButtonsInitOptions(walletButtons, cart), + ); + const lineItems = [...cart.lineItems.physicalItems, ...cart.lineItems.digitalItems]; const formattedLineItems = lineItems.map((item) => ({ @@ -277,6 +323,7 @@ export default async function Cart({ params }: Props) { }} summaryTitle={t('CheckoutSummary.title')} title={t('title')} + walletButtonsInitOptions={walletButtonsInitOptions} /> { + server.use( + gql.query('PaymentWalletWithInitializationDataQuery', () => { + return HttpResponse.json({ data: { site: { paymentWalletWithInitializationData: null } } }); + }), + gql.mutation('CreatePaymentWalletIntentMutation', () => { + return HttpResponse.json({ + data: { + payment: { + paymentWallet: { + createPaymentWalletIntent: { + paymentWalletIntentData: { + __typename: 'PayPalCommercePaymentWalletIntentData', + orderId: '123', + approvalUrl: 'https://example.com/approval', + initializationEntityId: '456', + }, + errors: [], + }, + }, + }, + }, + }); + }), + ); +}); + +describe('query validation', () => { + test('route handler with valid query', async () => { + const query = graphql(` + query PaymentWalletWithInitializationDataQuery( + $paymentWalletEntityId: String! + $cartEntityId: String + ) { + site { + paymentWalletWithInitializationData( + filter: { paymentWalletEntityId: $paymentWalletEntityId, cartEntityId: $cartEntityId } + ) { + initializationData + } + } + } + `); + + const request = new NextRequest('http://localhost:3000/api/wallets/graphql', { + method: 'POST', + body: JSON.stringify({ + query: print(query), + variables: JSON.stringify({ + paymentWalletEntityId: '123', + cartEntityId: '456', + }), + }), + }); + + const response = await POST(request); + const data: unknown = await response.json(); + + expect(data).toMatchObject({ site: { paymentWalletWithInitializationData: null } }); + }); + + test('route handler with invalid query name', async () => { + const query = graphql(` + query FooBar($paymentWalletEntityId: String!, $cartEntityId: String) { + site { + paymentWalletWithInitializationData( + filter: { paymentWalletEntityId: $paymentWalletEntityId, cartEntityId: $cartEntityId } + ) { + initializationData + } + } + } + `); + + const request = new NextRequest('http://localhost:3000/api/wallets/graphql', { + method: 'POST', + body: JSON.stringify({ + query: print(query), + variables: JSON.stringify({ + paymentWalletEntityId: '123', + cartEntityId: '456', + }), + }), + }); + + const response = await POST(request); + const text = await response.text(); + + expect(response.status).toBe(403); + expect(text).toBe('Operation not allowed'); + }); + + test('route handler with invalid query data', async () => { + const query = graphql(` + query PaymentWalletWithInitializationDataQuery { + site { + settings { + storeName + } + } + } + `); + + const request = new NextRequest('http://localhost:3000/api/wallets/graphql', { + method: 'POST', + body: JSON.stringify({ + query: print(query), + }), + }); + + const response = await POST(request); + const text = await response.text(); + + expect(response.status).toBe(400); + expect(text).toBe('Query is invalid'); + }); +}); + +describe('mutation validation', () => { + test('route handler with valid mutation', async () => { + const mutation = graphql(` + mutation CreatePaymentWalletIntentMutation( + $paymentWalletEntityId: String! + $cartEntityId: String! + ) { + payment { + paymentWallet { + createPaymentWalletIntent( + input: { paymentWalletEntityId: $paymentWalletEntityId, cartEntityId: $cartEntityId } + ) { + paymentWalletIntentData { + __typename + ... on PayPalCommercePaymentWalletIntentData { + orderId + approvalUrl + initializationEntityId + } + } + errors { + __typename + ... on CreatePaymentWalletIntentGenericError { + message + } + ... on Error { + message + } + } + } + } + } + } + `); + + const request = new NextRequest('http://localhost:3000/api/wallets/graphql', { + method: 'POST', + body: JSON.stringify({ + query: print(mutation), + variables: JSON.stringify({ + paymentWalletEntityId: '123', + cartEntityId: '456', + }), + }), + }); + + const response = await POST(request); + const data: unknown = await response.json(); + + expect(data).toMatchObject({ + payment: { + paymentWallet: { + createPaymentWalletIntent: { + errors: [], + paymentWalletIntentData: { + __typename: 'PayPalCommercePaymentWalletIntentData', + approvalUrl: 'https://example.com/approval', + initializationEntityId: '456', + orderId: '123', + }, + }, + }, + }, + }); + }); + + test('route handler with invalid mutation name', async () => { + const mutation = graphql(` + mutation FooBar($paymentWalletEntityId: String!, $cartEntityId: String!) { + payment { + paymentWallet { + createPaymentWalletIntent( + input: { paymentWalletEntityId: $paymentWalletEntityId, cartEntityId: $cartEntityId } + ) { + paymentWalletIntentData { + __typename + ... on PayPalCommercePaymentWalletIntentData { + orderId + approvalUrl + initializationEntityId + } + } + errors { + __typename + ... on CreatePaymentWalletIntentGenericError { + message + } + ... on Error { + message + } + } + } + } + } + } + `); + + const request = new NextRequest('http://localhost:3000/api/wallets/graphql', { + method: 'POST', + body: JSON.stringify({ + query: print(mutation), + variables: JSON.stringify({ + paymentWalletEntityId: '123', + cartEntityId: '456', + }), + }), + }); + + const response = await POST(request); + const text = await response.text(); + + expect(response.status).toBe(403); + expect(text).toBe('Operation not allowed'); + }); + + test('route handler with invalid mutation data', async () => { + const query = graphql(` + mutation CreatePaymentWalletIntentMutation { + logout { + result + } + } + `); + + const request = new NextRequest('http://localhost:3000/api/wallets/graphql', { + method: 'POST', + body: JSON.stringify({ + query: print(query), + }), + }); + + const response = await POST(request); + const text = await response.text(); + + expect(response.status).toBe(400); + expect(text).toBe('Query is invalid'); + }); +}); + +test('route handler with invalid operation', async () => { + const request = new NextRequest('http://localhost:3000/api/wallets/graphql', { + method: 'POST', + body: JSON.stringify({ + query: 'fragment Foo on Bar { baz }', + }), + }); + + const response = await POST(request); + const text = await response.text(); + + expect(response.status).toBe(400); + expect(text).toBe('No operation found'); +}); diff --git a/core/app/api/wallets/graphql/route.ts b/core/app/api/wallets/graphql/route.ts new file mode 100644 index 0000000000..66e793bfa4 --- /dev/null +++ b/core/app/api/wallets/graphql/route.ts @@ -0,0 +1,143 @@ +import { OperationDefinitionNode, parse, SelectionNode } from 'graphql'; +import { NextRequest } from 'next/server'; +import { z } from 'zod'; + +import { client } from '~/client'; +import { graphql } from '~/client/graphql'; + +const ENABLE_LIST = [ + { + name: 'PaymentWalletWithInitializationDataQuery', + type: 'query', + allowedFields: { + site: { + paymentWalletWithInitializationData: true, // allow any subfields of paymentWallets + }, + }, + }, + { + name: 'CreatePaymentWalletIntentMutation', + type: 'mutation', + allowedFields: { + payment: { + paymentWallet: { + createPaymentWalletIntent: { + paymentWalletIntentData: true, + errors: true, + }, + }, + }, + }, + }, + { + name: 'CheckoutRedirectMutation', + type: 'mutation', + allowedFields: { + cart: { + createCartRedirectUrls: true, + }, + }, + }, + { + name: 'AddCheckoutBillingAddressMutation', + type: 'mutation', + allowedFields: { + checkout: { + billingAddress: true, + }, + }, + }, + { + name: 'UpdateCheckoutBillingAddressMutation', + type: 'mutation', + allowedFields: { + checkout: { + billingAddress: true, + }, + }, + }, +]; + +export const POST = async (request: NextRequest) => { + const body: unknown = await request.json(); + const { document, variables } = z + .object({ document: z.string(), variables: z.string().optional() }) + .parse(body); + + const ast = parse(document, { noLocation: true }); + + // Validate operation structure + const operation = ast.definitions.find( + (definition): definition is OperationDefinitionNode => + definition.kind === 'OperationDefinition', + ); + + if (!operation) return new Response('No operation found', { status: 400 }); + + const config = ENABLE_LIST.find( + (entry) => entry.name === operation.name?.value && entry.type === operation.operation, + ); + + if (!config) return new Response('Operation not allowed', { status: 403 }); + + try { + assertFieldsAllowed(operation.selectionSet.selections, config.allowedFields); + } catch { + return new Response('Query is invalid', { status: 400 }); + } + + const response = await client.fetch({ + document: graphql(document), + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + variables: variables ? JSON.parse(variables) : undefined, + errorPolicy: 'ignore', + }); + + return new Response(JSON.stringify(response), { + status: 200, + headers: { 'Content-Type': 'application/json' }, + }); +}; + +interface Allowed { + [key: string]: Allowed | boolean | null | undefined; +} + +function assertFieldsAllowed( + selections: readonly SelectionNode[], + allowed: Allowed, + path: string[] = ['site'], +): asserts selections is readonly SelectionNode[] { + selections.forEach((selection) => { + if (selection.kind !== 'Field') return; + + const fieldName = selection.name.value; + const currentPath = [...path, fieldName]; + + if (!(fieldName in allowed)) { + throw new Error(`Disallowed field: ${currentPath.join('.')}`); + } + + const allowedValue = allowed[fieldName]; + + if (allowedValue === true) { + // All descendants allowed + return; + } + + if (typeof allowedValue === 'object' && allowedValue !== null) { + if (!selection.selectionSet) { + throw new Error(`Expected subfields for: ${currentPath.join('.')}`); + } + + assertFieldsAllowed(selection.selectionSet.selections, allowedValue, currentPath); + + return; + } + + if (typeof allowedValue !== 'object' && selection.selectionSet) { + throw new Error(`Field ${currentPath.join('.')} does not allow subfields`); + } + // If allowedValue is falsy and no selectionSet, it's fine (already checked above) + }); +} diff --git a/core/components/wallet-buttons/_components/client-wallet-buttons/index.tsx b/core/components/wallet-buttons/_components/client-wallet-buttons/index.tsx new file mode 100644 index 0000000000..b9f5107f8e --- /dev/null +++ b/core/components/wallet-buttons/_components/client-wallet-buttons/index.tsx @@ -0,0 +1,42 @@ +'use client'; + +import { useEffect, useRef } from 'react'; + +import { Stream, Streamable, useStreamable } from '@/vibes/soul/lib/streamable'; +import { WalletButtonsInitializer } from '~/lib/wallet-buttons'; +import { InitializeButtonProps } from '~/lib/wallet-buttons/types'; + +export const ClientWalletButtons = ({ + walletButtonsInitOptions, + cartId, +}: { + walletButtonsInitOptions: Streamable; + cartId: string; +}) => { + const isMountedRef = useRef(false); + const initButtonProps = useStreamable(walletButtonsInitOptions); + + useEffect(() => { + if (!isMountedRef.current && initButtonProps.length) { + isMountedRef.current = true; + + const initWalletButtons = async () => { + await new WalletButtonsInitializer().initialize(initButtonProps); + }; + + void initWalletButtons(); + } + }, [cartId, initButtonProps]); + + return ( + + {(buttonOptions) => ( +
+ {buttonOptions.map((button) => + button.containerId ?
: null, + )} +
+ )} + + ); +}; diff --git a/core/lib/currency.ts b/core/lib/currency.ts index 9a7ff6d593..7f63df6cde 100644 --- a/core/lib/currency.ts +++ b/core/lib/currency.ts @@ -5,9 +5,9 @@ import { cookies } from 'next/headers'; import type { CurrencyCode } from '~/components/header/fragment'; import { CurrencyCodeSchema } from '~/components/header/schema'; -export async function getPreferredCurrencyCode(): Promise { +export async function getPreferredCurrencyCode(code?: string): Promise { const cookieStore = await cookies(); - const currencyCode = cookieStore.get('currencyCode')?.value; + const currencyCode = cookieStore.get('currencyCode')?.value || code; if (!currencyCode) { return undefined; diff --git a/core/lib/wallet-buttons/error.ts b/core/lib/wallet-buttons/error.ts new file mode 100644 index 0000000000..9d66a245c5 --- /dev/null +++ b/core/lib/wallet-buttons/error.ts @@ -0,0 +1,9 @@ +export class InitializationError extends Error { + constructor() { + super( + 'Unable to initialize the checkout button because the required script has not been loaded yet.', + ); + + this.name = 'InitializationError'; + } +} diff --git a/core/lib/wallet-buttons/index.ts b/core/lib/wallet-buttons/index.ts new file mode 100644 index 0000000000..b2ecbe6d86 --- /dev/null +++ b/core/lib/wallet-buttons/index.ts @@ -0,0 +1,53 @@ +import { InitializationError } from './error'; +import { InitializeButtonProps } from './types'; + +export class WalletButtonsInitializer { + private origin = window.location.origin; + private checkoutSdkUrl = `${this.origin}/v1/loader.js`; + + async initialize( + walletButtonsInitOptions: InitializeButtonProps[], + ): Promise { + await this.initializeCheckoutKitLoader(); + + const checkoutButtonInitializer = await this.initCheckoutButtonInitializer(); + + return walletButtonsInitOptions.map((buttonOption) => { + checkoutButtonInitializer.initializeWalletButton(buttonOption); + + return buttonOption; + }); + } + + private async initializeCheckoutKitLoader(): Promise { + if (window.checkoutKitLoader) { + return; + } + + await new Promise((resolve, reject) => { + const script = document.createElement('script'); + + script.type = 'text/javascript'; + script.defer = true; + script.src = this.checkoutSdkUrl; + + script.onload = resolve; + script.onerror = reject; + script.onabort = reject; + + document.body.append(script); + }); + } + + private async initCheckoutButtonInitializer() { + if (!window.checkoutKitLoader) { + throw new InitializationError(); + } + + const checkoutButtonModule = await window.checkoutKitLoader.load('wallet-button'); + + return checkoutButtonModule.createWalletButtonInitializer({ + graphQLEndpoint: 'api/wallets/graphql', + }); + } +} diff --git a/core/lib/wallet-buttons/types.ts b/core/lib/wallet-buttons/types.ts new file mode 100644 index 0000000000..41a87ddade --- /dev/null +++ b/core/lib/wallet-buttons/types.ts @@ -0,0 +1,25 @@ +declare global { + interface Window { + checkoutKitLoader?: CheckoutKitLoader; + } +} + +interface CheckoutKitLoader { + load(moduleName: string): Promise; +} + +interface CheckoutKitModule { + createWalletButtonInitializer(options: { + graphQLEndpoint: string; + }): CheckoutHeadlessButtonInitializer; +} + +interface CheckoutHeadlessButtonInitializer { + initializeWalletButton(option: InitializeButtonProps): void; +} + +export interface InitializeButtonProps { + [key: string]: unknown; + containerId: string; + methodId: string; +} diff --git a/core/msw.server.ts b/core/msw.server.ts new file mode 100644 index 0000000000..618e02b774 --- /dev/null +++ b/core/msw.server.ts @@ -0,0 +1,3 @@ +import { setupServer } from 'msw/node'; + +export const server = setupServer(); diff --git a/core/package.json b/core/package.json index 68dd094261..b6b2622a23 100644 --- a/core/package.json +++ b/core/package.json @@ -10,7 +10,8 @@ "build:analyze": "ANALYZE=true npm run build", "start": "next start", "lint": "next lint", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "test": "vitest" }, "dependencies": { "@bigcommerce/catalyst-client": "workspace:^", @@ -90,11 +91,15 @@ "dotenv-cli": "^8.0.0", "eslint": "^8.57.1", "eslint-config-next": "15.2.3", + "msw": "^2.7.4", "postcss": "^8.5.3", "prettier": "^3.5.3", "prettier-plugin-tailwindcss": "^0.6.11", "tailwindcss": "^4.1.4", "tailwindcss-animate": "1.0.7", - "typescript": "^5.8.3" + "typescript": "^5.8.3", + "vite": "^6.3.5", + "vite-tsconfig-paths": "^5.1.4", + "vitest": "^3.1.1" } } diff --git a/core/vibes/soul/sections/cart/client.tsx b/core/vibes/soul/sections/cart/client.tsx index 084f27509f..b8f87ce2cd 100644 --- a/core/vibes/soul/sections/cart/client.tsx +++ b/core/vibes/soul/sections/cart/client.tsx @@ -13,11 +13,14 @@ import { } from 'react'; import { useFormStatus } from 'react-dom'; +import { Streamable } from '@/vibes/soul/lib/streamable'; import { Button } from '@/vibes/soul/primitives/button'; import { toast } from '@/vibes/soul/primitives/toaster'; import { StickySidebarLayout } from '@/vibes/soul/sections/sticky-sidebar-layout'; +import { ClientWalletButtons } from 'components/wallet-buttons/_components/client-wallet-buttons'; import { useEvents } from '~/components/analytics/events'; import { Image } from '~/components/image'; +import { InitializeButtonProps } from '~/lib/wallet-buttons/types'; import { CouponCodeForm, CouponCodeFormState } from './coupon-code-form'; import { cartLineItemActionFormDataSchema } from './schema'; @@ -125,6 +128,8 @@ export interface CartProps { decrementLineItemLabel?: string; incrementLineItemLabel?: string; cart: Cart; + walletButtonsInitOptions?: Streamable; + cartId: string; couponCode?: CouponCode; shipping?: Shipping; } @@ -169,6 +174,8 @@ export function CartClient({ deleteLineItemLabel, lineItemAction, checkoutAction, + walletButtonsInitOptions, + cartId, checkoutLabel = 'Checkout', emptyState = defaultEmptyState, summaryTitle, @@ -271,6 +278,14 @@ export function CartClient({ {checkoutLabel} + {walletButtonsInitOptions && ( +
+ +
+ )}
} sidebarPosition="after" diff --git a/core/vitest.config.ts b/core/vitest.config.ts new file mode 100644 index 0000000000..30c5f0597a --- /dev/null +++ b/core/vitest.config.ts @@ -0,0 +1,13 @@ +import { loadEnv } from 'vite'; +import tsconfigPaths from 'vite-tsconfig-paths'; +import { defineConfig } from 'vitest/config'; + +export default defineConfig(({ mode }) => ({ + plugins: [tsconfigPaths()], + test: { + environment: 'node', + env: loadEnv(mode, process.cwd(), ''), + exclude: ['**/node_modules/**', '**/dist/**', '**/tests/**'], + setupFiles: ['./vitest.setup.ts'], + }, +})); diff --git a/core/vitest.setup.ts b/core/vitest.setup.ts new file mode 100644 index 0000000000..bda991d796 --- /dev/null +++ b/core/vitest.setup.ts @@ -0,0 +1,7 @@ +import { afterAll, afterEach, beforeAll } from 'vitest'; + +import { server } from './msw.server'; + +beforeAll(() => server.listen()); +afterEach(() => server.resetHandlers()); +afterAll(() => server.close()); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8747880a89..d31488a29d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,7 +25,7 @@ importers: version: 5.8.3 unlighthouse: specifier: ^0.16.3 - version: 0.16.3(encoding@0.1.13)(magicast@0.3.5)(puppeteer@24.9.0(typescript@5.8.3))(rollup@4.37.0)(typescript@5.8.3)(vue@3.5.14(typescript@5.8.3)) + version: 0.16.3(encoding@0.1.13)(magicast@0.3.5)(puppeteer@24.10.0(typescript@5.8.3))(rollup@4.37.0)(typescript@5.8.3)(vue@3.5.16(typescript@5.8.3)) core: dependencies: @@ -37,7 +37,7 @@ importers: version: 1.3.0(react@19.1.0) '@conform-to/zod': specifier: ^1.3.0 - version: 1.3.0(zod@3.25.48) + version: 1.3.0(zod@3.25.49) '@icons-pack/react-simple-icons': specifier: ^11.2.0 version: 11.2.0(react@19.1.0) @@ -85,19 +85,19 @@ importers: version: 1.2.0(@types/react-dom@19.0.4(@types/react@19.1.1))(@types/react@19.1.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@t3-oss/env-core': specifier: ^0.13.6 - version: 0.13.6(typescript@5.8.3)(zod@3.25.48) + version: 0.13.6(typescript@5.8.3)(zod@3.25.49) '@upstash/redis': specifier: ^1.34.7 version: 1.34.7 '@vercel/analytics': specifier: ^1.5.0 - version: 1.5.0(next@15.4.0-canary.0(@babel/core@7.27.1)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(svelte@5.1.15)(vue@3.5.14(typescript@5.8.3)) + version: 1.5.0(next@15.4.0-canary.0(@babel/core@7.27.4)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(svelte@5.1.15)(vue@3.5.16(typescript@5.8.3)) '@vercel/kv': specifier: ^3.0.0 version: 3.0.0 '@vercel/speed-insights': specifier: ^1.2.0 - version: 1.2.0(next@15.4.0-canary.0(@babel/core@7.27.1)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(svelte@5.1.15)(vue@3.5.14(typescript@5.8.3)) + version: 1.2.0(next@15.4.0-canary.0(@babel/core@7.27.4)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(svelte@5.1.15)(vue@3.5.16(typescript@5.8.3)) clsx: specifier: ^2.1.1 version: 2.1.1 @@ -139,16 +139,16 @@ importers: version: 0.474.0(react@19.1.0) next: specifier: 15.4.0-canary.0 - version: 15.4.0-canary.0(@babel/core@7.27.1)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 15.4.0-canary.0(@babel/core@7.27.4)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) next-auth: specifier: 5.0.0-beta.25 - version: 5.0.0-beta.25(next@15.4.0-canary.0(@babel/core@7.27.1)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(nodemailer@6.9.16)(react@19.1.0) + version: 5.0.0-beta.25(next@15.4.0-canary.0(@babel/core@7.27.4)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(nodemailer@6.9.16)(react@19.1.0) next-intl: specifier: ^4.0.2 - version: 4.0.2(next@15.4.0-canary.0(@babel/core@7.27.1)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(typescript@5.8.3) + version: 4.0.2(next@15.4.0-canary.0(@babel/core@7.27.4)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(typescript@5.8.3) nuqs: specifier: ^2.4.2 - version: 2.4.2(next@15.4.0-canary.0(@babel/core@7.27.1)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) + version: 2.4.2(next@15.4.0-canary.0(@babel/core@7.27.4)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) p-lazy: specifier: ^5.0.0 version: 5.0.0 @@ -187,7 +187,7 @@ importers: version: 11.1.0 zod: specifier: ^3.25.48 - version: 3.25.48 + version: 3.25.49 devDependencies: '@0no-co/graphqlsp': specifier: ^1.12.16 @@ -255,6 +255,9 @@ importers: eslint-config-next: specifier: 15.2.3 version: 15.2.3(eslint@8.57.1)(typescript@5.8.3) + msw: + specifier: ^2.7.4 + version: 2.7.4(@types/node@22.14.1)(typescript@5.8.3) postcss: specifier: ^8.5.3 version: 8.5.3 @@ -273,6 +276,15 @@ importers: typescript: specifier: ^5.8.3 version: 5.8.3 + vite: + specifier: ^6.3.5 + version: 6.3.5(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0) + vite-tsconfig-paths: + specifier: ^5.1.4 + version: 5.1.4(typescript@5.8.3)(vite@6.3.5(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0)) + vitest: + specifier: ^3.1.1 + version: 3.1.1(@types/node@22.14.1)(@vitest/ui@3.1.1)(jiti@2.4.2)(lightningcss@1.29.2)(msw@2.7.4(@types/node@22.14.1)(typescript@5.8.3))(yaml@2.6.0) packages/cli: dependencies: @@ -558,24 +570,24 @@ packages: resolution: {integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.27.2': - resolution: {integrity: sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==} + '@babel/compat-data@7.27.5': + resolution: {integrity: sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==} engines: {node: '>=6.9.0'} '@babel/core@7.24.7': resolution: {integrity: sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==} engines: {node: '>=6.9.0'} - '@babel/core@7.27.1': - resolution: {integrity: sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==} + '@babel/core@7.27.4': + resolution: {integrity: sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==} engines: {node: '>=6.9.0'} '@babel/generator@7.24.7': resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==} engines: {node: '>=6.9.0'} - '@babel/generator@7.27.1': - resolution: {integrity: sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==} + '@babel/generator@7.27.5': + resolution: {integrity: sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==} engines: {node: '>=6.9.0'} '@babel/helper-compilation-targets@7.24.7': @@ -612,8 +624,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-module-transforms@7.27.1': - resolution: {integrity: sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==} + '@babel/helper-module-transforms@7.27.3': + resolution: {integrity: sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -666,8 +678,8 @@ packages: resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.27.1': - resolution: {integrity: sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==} + '@babel/helpers@7.27.4': + resolution: {integrity: sha512-Y+bO6U+I7ZKaM5G5rDUZiYfUvQPUibYmAFe7EnKdnKBbVXDZxvp+MWOH5gYciY0EPk4EScsuFMQBbEfpdRKSCQ==} engines: {node: '>=6.9.0'} '@babel/highlight@7.24.7': @@ -684,8 +696,8 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - '@babel/parser@7.27.2': - resolution: {integrity: sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==} + '@babel/parser@7.27.5': + resolution: {integrity: sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==} engines: {node: '>=6.0.0'} hasBin: true @@ -766,8 +778,8 @@ packages: resolution: {integrity: sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==} engines: {node: '>=6.9.0'} - '@babel/standalone@7.27.3': - resolution: {integrity: sha512-F0UU3ifTQshuIgTMH1LqLwNNsLbnP62lkU+QpFpPEZ5cpWrVixYScQ7fCkx470pB2zYkKtHvRuq29Iam9wi29A==} + '@babel/standalone@7.27.5': + resolution: {integrity: sha512-AvoXdFyKqvcxS0pMMs4dNl3hOYUkhTCF4P7aEwLXN1ED5ekxxjVWeFtr4ZVUGO0Y7u5d5H3nOyO8Y0CS/EBXPA==} engines: {node: '>=6.9.0'} '@babel/template@7.24.7': @@ -782,8 +794,8 @@ packages: resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.27.1': - resolution: {integrity: sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==} + '@babel/traverse@7.27.4': + resolution: {integrity: sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==} engines: {node: '>=6.9.0'} '@babel/types@7.24.7': @@ -794,8 +806,8 @@ packages: resolution: {integrity: sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==} engines: {node: '>=6.9.0'} - '@babel/types@7.27.1': - resolution: {integrity: sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==} + '@babel/types@7.27.3': + resolution: {integrity: sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==} engines: {node: '>=6.9.0'} '@bcoe/v8-coverage@0.2.3': @@ -1843,8 +1855,8 @@ packages: '@paulirish/trace_engine@0.0.23': resolution: {integrity: sha512-2ym/q7HhC5K+akXkNV6Gip3oaHpbI6TsGjmcAsl7bcJ528MVbacPQeoauLFEeLXH4ulJvsxQwNDIg/kAEhFZxw==} - '@paulirish/trace_engine@0.0.52': - resolution: {integrity: sha512-RSIDdpvYRJIaXUSiJfTYxVRtjq3FPjU8FPT5BkpYXS4H7ofExEb4tZBXcqlRoriA8ykVTClgbqabmoI32n5zRQ==} + '@paulirish/trace_engine@0.0.53': + resolution: {integrity: sha512-PUl/vlfo08Oj804VI5nDPeSk9vyslnBlVzDDwFt8SUVxY8+KdGMkra/vrXjEEHe8gb7+RqVTfOIlGw0nyrEelA==} '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} @@ -3148,34 +3160,34 @@ packages: '@vitest/utils@3.1.1': resolution: {integrity: sha512-1XIjflyaU2k3HMArJ50bwSh3wKWPD6Q47wz/NUSmRV0zNywPc4w79ARjg/i/aNINHwA+mIALhUVqD9/aUvZNgg==} - '@vue/compiler-core@3.5.14': - resolution: {integrity: sha512-k7qMHMbKvoCXIxPhquKQVw3Twid3Kg4s7+oYURxLGRd56LiuHJVrvFKI4fm2AM3c8apqODPfVJGoh8nePbXMRA==} + '@vue/compiler-core@3.5.16': + resolution: {integrity: sha512-AOQS2eaQOaaZQoL1u+2rCJIKDruNXVBZSiUD3chnUrsoX5ZTQMaCvXlWNIfxBJuU15r1o7+mpo5223KVtIhAgQ==} - '@vue/compiler-dom@3.5.14': - resolution: {integrity: sha512-1aOCSqxGOea5I80U2hQJvXYpPm/aXo95xL/m/mMhgyPUsKe9jhjwWpziNAw7tYRnbz1I61rd9Mld4W9KmmRoug==} + '@vue/compiler-dom@3.5.16': + resolution: {integrity: sha512-SSJIhBr/teipXiXjmWOVWLnxjNGo65Oj/8wTEQz0nqwQeP75jWZ0n4sF24Zxoht1cuJoWopwj0J0exYwCJ0dCQ==} - '@vue/compiler-sfc@3.5.14': - resolution: {integrity: sha512-9T6m/9mMr81Lj58JpzsiSIjBgv2LiVoWjIVa7kuXHICUi8LiDSIotMpPRXYJsXKqyARrzjT24NAwttrMnMaCXA==} + '@vue/compiler-sfc@3.5.16': + resolution: {integrity: sha512-rQR6VSFNpiinDy/DVUE0vHoIDUF++6p910cgcZoaAUm3POxgNOOdS/xgoll3rNdKYTYPnnbARDCZOyZ+QSe6Pw==} - '@vue/compiler-ssr@3.5.14': - resolution: {integrity: sha512-Y0G7PcBxr1yllnHuS/NxNCSPWnRGH4Ogrp0tsLA5QemDZuJLs99YjAKQ7KqkHE0vCg4QTKlQzXLKCMF7WPSl7Q==} + '@vue/compiler-ssr@3.5.16': + resolution: {integrity: sha512-d2V7kfxbdsjrDSGlJE7my1ZzCXViEcqN6w14DOsDrUCHEA6vbnVCpRFfrc4ryCP/lCKzX2eS1YtnLE/BuC9f/A==} - '@vue/reactivity@3.5.14': - resolution: {integrity: sha512-7cK1Hp343Fu/SUCCO52vCabjvsYu7ZkOqyYu7bXV9P2yyfjUMUXHZafEbq244sP7gf+EZEz+77QixBTuEqkQQw==} + '@vue/reactivity@3.5.16': + resolution: {integrity: sha512-FG5Q5ee/kxhIm1p2bykPpPwqiUBV3kFySsHEQha5BJvjXdZTUfmya7wP7zC39dFuZAcf/PD5S4Lni55vGLMhvA==} - '@vue/runtime-core@3.5.14': - resolution: {integrity: sha512-w9JWEANwHXNgieAhxPpEpJa+0V5G0hz3NmjAZwlOebtfKyp2hKxKF0+qSh0Xs6/PhfGihuSdqMprMVcQU/E6ag==} + '@vue/runtime-core@3.5.16': + resolution: {integrity: sha512-bw5Ykq6+JFHYxrQa7Tjr+VSzw7Dj4ldR/udyBZbq73fCdJmyy5MPIFR9IX/M5Qs+TtTjuyUTCnmK3lWWwpAcFQ==} - '@vue/runtime-dom@3.5.14': - resolution: {integrity: sha512-lCfR++IakeI35TVR80QgOelsUIdcKjd65rWAMfdSlCYnaEY5t3hYwru7vvcWaqmrK+LpI7ZDDYiGU5V3xjMacw==} + '@vue/runtime-dom@3.5.16': + resolution: {integrity: sha512-T1qqYJsG2xMGhImRUV9y/RseB9d0eCYZQ4CWca9ztCuiPj/XWNNN+lkNBuzVbia5z4/cgxdL28NoQCvC0Xcfww==} - '@vue/server-renderer@3.5.14': - resolution: {integrity: sha512-Rf/ISLqokIvcySIYnv3tNWq40PLpNLDLSJwwVWzG6MNtyIhfbcrAxo5ZL9nARJhqjZyWWa40oRb2IDuejeuv6w==} + '@vue/server-renderer@3.5.16': + resolution: {integrity: sha512-BrX0qLiv/WugguGsnQUJiYOE0Fe5mZTwi6b7X/ybGB0vfrPH9z0gD/Y6WOR1sGCgX4gc25L1RYS5eYQKDMoNIg==} peerDependencies: - vue: 3.5.14 + vue: 3.5.16 - '@vue/shared@3.5.14': - resolution: {integrity: sha512-oXTwNxVfc9EtP1zzXAlSlgARLXNC84frFYkS0HHz0h3E4WZSP9sywqjqzGCP9Y34M8ipNmd380pVgmMuwELDyQ==} + '@vue/shared@3.5.16': + resolution: {integrity: sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg==} acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} @@ -3965,11 +3977,11 @@ packages: devtools-protocol@0.0.1312386: resolution: {integrity: sha512-DPnhUXvmvKT2dFA/j7B+riVLUt9Q6RKJlcppojL5CoRywJJKLDYnRlw0gTFKfgDPHP5E04UoB71SxoJlVZy8FA==} - devtools-protocol@0.0.1439962: - resolution: {integrity: sha512-jJF48UdryzKiWhJ1bLKr7BFWUQCEIT5uCNbDLqkQJBtkFxYzILJH44WN0PDKMIlGDN7Utb8vyUY85C3w4R/t2g==} + devtools-protocol@0.0.1452169: + resolution: {integrity: sha512-FOFDVMGrAUNp0dDKsAU1TorWJUx2JOU1k9xdgBKKJF3IBh/Uhl2yswG5r3TEAOrCiGY2QRp1e6LVDQrCsTKO4g==} - devtools-protocol@0.0.1445099: - resolution: {integrity: sha512-GEuIbCLU2Iu6Sg05GeWS7ksijhOUZIDJD2YBUNRanK7SLKjeci1uxUUomu2VNvygQRuoq/vtnTYrgPZBEiYNMA==} + devtools-protocol@0.0.1467305: + resolution: {integrity: sha512-LxwMLqBoPPGpMdRL4NkLFRNy3QLp6Uqa7GNp1v6JaBheop2QrB9Q7q0A/q/CYYP9sBfZdHOyszVx4gc9zyk7ow==} diff-sequences@29.6.3: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} @@ -4510,6 +4522,14 @@ packages: picomatch: optional: true + fdir@6.4.5: + resolution: {integrity: sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + fflate@0.8.2: resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} @@ -4716,6 +4736,9 @@ packages: resolution: {integrity: sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==} engines: {node: '>=18'} + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -4851,8 +4874,8 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - ignore@7.0.4: - resolution: {integrity: sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==} + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} image-ssim@0.2.0: @@ -5406,8 +5429,8 @@ packages: engines: {node: '>=18.16'} hasBin: true - lighthouse@12.6.0: - resolution: {integrity: sha512-ufYw6dBR0PDEpO4pj45zRStatdTvBSi/LTXgs6ULmadSLTNXklP3XGGGuL7SA9pE/NltGbs5zQOA/ICQao1ywA==} + lighthouse@12.6.1: + resolution: {integrity: sha512-85WDkjcXAVdlFem9Y6SSxqoKiz/89UsDZhLpeLJIsJ4LlHxw047XTZhlFJmjYCB7K5S1erSBAf5cYLcfyNbH3A==} engines: {node: '>=18.20'} hasBin: true @@ -6310,12 +6333,12 @@ packages: resolution: {integrity: sha512-cHArnywCiAAVXa3t4GGL2vttNxh7GqXtIYGym99egkNJ3oG//wL9LkvO4WE8W1TJe95t1F1ocu9X4xWaGsOKOA==} engines: {node: '>=18'} - puppeteer-core@24.9.0: - resolution: {integrity: sha512-HFdCeH/wx6QPz8EncafbCqJBqaCG1ENW75xg3cLFMRUoqZDgByT6HSueiumetT2uClZxwqj0qS4qMVZwLHRHHw==} + puppeteer-core@24.10.0: + resolution: {integrity: sha512-xX0QJRc8t19iAwRDsAOR38Q/Zx/W6WVzJCEhKCAwp2XMsaWqfNtQ+rBfQW9PlF+Op24d7c8Zlgq9YNmbnA7hdQ==} engines: {node: '>=18'} - puppeteer@24.9.0: - resolution: {integrity: sha512-L0pOtALIx8rgDt24Y+COm8X52v78gNtBOW6EmUcEPci0TYD72SAuaXKqasRIx4JXxmg2Tkw5ySKcpPOwN8xXnQ==} + puppeteer@24.10.0: + resolution: {integrity: sha512-Oua9VkGpj0S2psYu5e6mCer6W9AU9POEQh22wRgSXnLXASGH+MwLUVWgLCLeP9QPHHcJ7tySUlg4Sa9OJmaLpw==} engines: {node: '>=18'} hasBin: true @@ -6605,8 +6628,8 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shell-quote@1.8.2: - resolution: {integrity: sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==} + shell-quote@1.8.3: + resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} engines: {node: '>= 0.4'} side-channel-list@1.0.0: @@ -6963,6 +6986,10 @@ packages: resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==} engines: {node: '>=12.0.0'} + tinyglobby@0.2.14: + resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + engines: {node: '>=12.0.0'} + tinypool@1.0.2: resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -7032,6 +7059,16 @@ packages: ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + tsconfck@3.1.6: + resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} @@ -7298,8 +7335,16 @@ packages: engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true - vite@6.2.0: - resolution: {integrity: sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ==} + vite-tsconfig-paths@5.1.4: + resolution: {integrity: sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==} + peerDependencies: + vite: '*' + peerDependenciesMeta: + vite: + optional: true + + vite@6.3.5: + resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: @@ -7366,8 +7411,8 @@ packages: jsdom: optional: true - vue@3.5.14: - resolution: {integrity: sha512-LbOm50/vZFG6Mhy6KscQYXZMQ0LMCC/y40HDJPPvGFQ+i/lUH+PJHR6C3assgOQiXdl6tAfsXHbXYVBZZu65ew==} + vue@3.5.16: + resolution: {integrity: sha512-rjOV2ecxMd5SiAmof2xzh2WxntRcigkX/He4YFJ6WdRvVUrbt6DxC1Iujh10XLl8xCDRDtGKMeO3D+pRQ1PP9w==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -7548,8 +7593,8 @@ packages: zod@3.24.2: resolution: {integrity: sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==} - zod@3.25.48: - resolution: {integrity: sha512-0X1mz8FtgEIvaxGjdIImYpZEaZMrund9pGXm3M6vM7Reba0e2eI71KPjSCGXBfwKDPwPoywf6waUKc3/tFvX2Q==} + zod@3.25.49: + resolution: {integrity: sha512-JMMPMy9ZBk3XFEdbM3iL1brx4NUSejd6xr3ELrrGEfGb355gjhiAWtG3K5o+AViV/3ZfkIrCzXsZn6SbLwTR8Q==} snapshots: @@ -7599,7 +7644,7 @@ snapshots: '@babel/compat-data@7.24.7': {} - '@babel/compat-data@7.27.2': {} + '@babel/compat-data@7.27.5': {} '@babel/core@7.24.7': dependencies: @@ -7614,27 +7659,27 @@ snapshots: '@babel/traverse': 7.24.7 '@babel/types': 7.24.7 convert-source-map: 2.0.0 - debug: 4.4.1 + debug: 4.4.0 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/core@7.27.1': + '@babel/core@7.27.4': dependencies: '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.27.1 - '@babel/generator': 7.27.1 + '@babel/generator': 7.27.5 '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-transforms': 7.27.1(@babel/core@7.27.1) - '@babel/helpers': 7.27.1 - '@babel/parser': 7.27.2 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.4) + '@babel/helpers': 7.27.4 + '@babel/parser': 7.27.5 '@babel/template': 7.27.2 - '@babel/traverse': 7.27.1 - '@babel/types': 7.27.1 + '@babel/traverse': 7.27.4 + '@babel/types': 7.27.3 convert-source-map: 2.0.0 - debug: 4.4.1 + debug: 4.4.0 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -7648,10 +7693,10 @@ snapshots: '@jridgewell/trace-mapping': 0.3.25 jsesc: 2.5.2 - '@babel/generator@7.27.1': + '@babel/generator@7.27.5': dependencies: - '@babel/parser': 7.27.2 - '@babel/types': 7.27.1 + '@babel/parser': 7.27.5 + '@babel/types': 7.27.3 '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 jsesc: 3.1.0 @@ -7666,7 +7711,7 @@ snapshots: '@babel/helper-compilation-targets@7.27.2': dependencies: - '@babel/compat-data': 7.27.2 + '@babel/compat-data': 7.27.5 '@babel/helper-validator-option': 7.27.1 browserslist: 4.24.4 lru-cache: 5.1.1 @@ -7674,28 +7719,28 @@ snapshots: '@babel/helper-environment-visitor@7.24.7': dependencies: - '@babel/types': 7.27.1 + '@babel/types': 7.24.7 '@babel/helper-function-name@7.24.7': dependencies: '@babel/template': 7.24.7 - '@babel/types': 7.27.1 + '@babel/types': 7.24.7 '@babel/helper-hoist-variables@7.24.7': dependencies: - '@babel/types': 7.27.1 + '@babel/types': 7.24.7 '@babel/helper-module-imports@7.24.7': dependencies: '@babel/traverse': 7.24.7 - '@babel/types': 7.27.1 + '@babel/types': 7.24.7 transitivePeerDependencies: - supports-color '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.27.1 - '@babel/types': 7.27.1 + '@babel/traverse': 7.27.4 + '@babel/types': 7.27.3 transitivePeerDependencies: - supports-color @@ -7710,12 +7755,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.27.1(@babel/core@7.27.1)': + '@babel/helper-module-transforms@7.27.3(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.27.1 + '@babel/core': 7.27.4 '@babel/helper-module-imports': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.27.1 + '@babel/traverse': 7.27.4 transitivePeerDependencies: - supports-color @@ -7724,13 +7769,13 @@ snapshots: '@babel/helper-simple-access@7.24.7': dependencies: '@babel/traverse': 7.24.7 - '@babel/types': 7.27.1 + '@babel/types': 7.24.7 transitivePeerDependencies: - supports-color '@babel/helper-split-export-declaration@7.24.7': dependencies: - '@babel/types': 7.27.1 + '@babel/types': 7.24.7 '@babel/helper-string-parser@7.24.7': {} @@ -7753,10 +7798,10 @@ snapshots: '@babel/template': 7.24.7 '@babel/types': 7.24.7 - '@babel/helpers@7.27.1': + '@babel/helpers@7.27.4': dependencies: '@babel/template': 7.27.2 - '@babel/types': 7.27.1 + '@babel/types': 7.27.3 '@babel/highlight@7.24.7': dependencies: @@ -7773,9 +7818,9 @@ snapshots: dependencies: '@babel/types': 7.26.9 - '@babel/parser@7.27.2': + '@babel/parser@7.27.5': dependencies: - '@babel/types': 7.27.1 + '@babel/types': 7.27.3 '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.7)': dependencies: @@ -7851,19 +7896,19 @@ snapshots: dependencies: regenerator-runtime: 0.14.1 - '@babel/standalone@7.27.3': {} + '@babel/standalone@7.27.5': {} '@babel/template@7.24.7': dependencies: '@babel/code-frame': 7.24.7 - '@babel/parser': 7.27.2 + '@babel/parser': 7.24.7 '@babel/types': 7.24.7 '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 - '@babel/parser': 7.27.2 - '@babel/types': 7.27.1 + '@babel/parser': 7.27.5 + '@babel/types': 7.27.3 '@babel/traverse@7.24.7': dependencies: @@ -7873,21 +7918,21 @@ snapshots: '@babel/helper-function-name': 7.24.7 '@babel/helper-hoist-variables': 7.24.7 '@babel/helper-split-export-declaration': 7.24.7 - '@babel/parser': 7.27.2 + '@babel/parser': 7.24.7 '@babel/types': 7.24.7 - debug: 4.4.1 + debug: 4.4.0 globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/traverse@7.27.1': + '@babel/traverse@7.27.4': dependencies: '@babel/code-frame': 7.27.1 - '@babel/generator': 7.27.1 - '@babel/parser': 7.27.2 + '@babel/generator': 7.27.5 + '@babel/parser': 7.27.5 '@babel/template': 7.27.2 - '@babel/types': 7.27.1 - debug: 4.4.1 + '@babel/types': 7.27.3 + debug: 4.4.0 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -7903,7 +7948,7 @@ snapshots: '@babel/helper-string-parser': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 - '@babel/types@7.27.1': + '@babel/types@7.27.3': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 @@ -8170,10 +8215,10 @@ snapshots: '@conform-to/dom': 1.3.0 react: 19.1.0 - '@conform-to/zod@1.3.0(zod@3.25.48)': + '@conform-to/zod@1.3.0(zod@3.25.49)': dependencies: '@conform-to/dom': 1.3.0 - zod: 3.25.48 + zod: 3.25.49 '@date-fns/tz@1.2.0': {} @@ -8376,10 +8421,10 @@ snapshots: graphql: 16.10.0 typescript: 5.8.3 - '@headlessui/vue@1.7.23(vue@3.5.14(typescript@5.8.3))': + '@headlessui/vue@1.7.23(vue@3.5.16(typescript@5.8.3))': dependencies: - '@tanstack/vue-virtual': 3.13.9(vue@3.5.14(typescript@5.8.3)) - vue: 3.5.14(typescript@5.8.3) + '@tanstack/vue-virtual': 3.13.9(vue@3.5.16(typescript@5.8.3)) + vue: 3.5.16(typescript@5.8.3) '@humanwhocodes/config-array@0.13.0': dependencies: @@ -8878,7 +8923,7 @@ snapshots: '@lhci/utils@0.14.0(encoding@0.1.13)': dependencies: - debug: 4.4.1 + debug: 4.4.0 isomorphic-fetch: 3.0.0(encoding@0.1.13) js-yaml: 3.14.1 lighthouse: 12.1.0 @@ -9061,7 +9106,7 @@ snapshots: '@paulirish/trace_engine@0.0.23': {} - '@paulirish/trace_engine@0.0.52': + '@paulirish/trace_engine@0.0.53': dependencies: legacy-javascript: 0.0.1 third-party-web: 0.26.6 @@ -9096,7 +9141,7 @@ snapshots: '@puppeteer/browsers@2.3.0': dependencies: - debug: 4.4.1 + debug: 4.4.0 extract-zip: 2.0.1 progress: 2.0.3 proxy-agent: 6.5.0 @@ -9863,10 +9908,10 @@ snapshots: dependencies: defer-to-connect: 2.0.1 - '@t3-oss/env-core@0.13.6(typescript@5.8.3)(zod@3.25.48)': + '@t3-oss/env-core@0.13.6(typescript@5.8.3)(zod@3.25.49)': optionalDependencies: typescript: 5.8.3 - zod: 3.25.48 + zod: 3.25.49 '@tailwindcss/container-queries@0.1.1(tailwindcss@4.1.4)': dependencies: @@ -9948,10 +9993,10 @@ snapshots: '@tanstack/virtual-core@3.13.9': {} - '@tanstack/vue-virtual@3.13.9(vue@3.5.14(typescript@5.8.3))': + '@tanstack/vue-virtual@3.13.9(vue@3.5.16(typescript@5.8.3))': dependencies: '@tanstack/virtual-core': 3.13.9 - vue: 3.5.14(typescript@5.8.3) + vue: 3.5.16(typescript@5.8.3) '@tootallnate/quickjs-emscripten@0.23.0': {} @@ -9962,7 +10007,7 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.27.2 + '@babel/parser': 7.24.7 '@babel/types': 7.24.7 '@types/babel__generator': 7.6.8 '@types/babel__template': 7.4.4 @@ -9970,16 +10015,16 @@ snapshots: '@types/babel__generator@7.6.8': dependencies: - '@babel/types': 7.27.1 + '@babel/types': 7.24.7 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.27.2 - '@babel/types': 7.27.1 + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 '@types/babel__traverse@7.20.6': dependencies: - '@babel/types': 7.27.1 + '@babel/types': 7.24.7 '@types/cacheable-request@6.0.3': dependencies: @@ -10163,7 +10208,7 @@ snapshots: dependencies: '@typescript-eslint/types': 8.21.0 '@typescript-eslint/visitor-keys': 8.21.0 - debug: 4.4.1 + debug: 4.4.0 fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 @@ -10221,12 +10266,12 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@unlighthouse/cli@0.16.3(encoding@0.1.13)(magicast@0.3.5)(puppeteer@24.9.0(typescript@5.8.3))(rollup@4.37.0)(typescript@5.8.3)': + '@unlighthouse/cli@0.16.3(encoding@0.1.13)(magicast@0.3.5)(puppeteer@24.10.0(typescript@5.8.3))(rollup@4.37.0)(typescript@5.8.3)': dependencies: '@lhci/utils': 0.14.0(encoding@0.1.13) '@unlighthouse/client': 0.16.3(typescript@5.8.3) - '@unlighthouse/core': 0.16.3(magicast@0.3.5)(puppeteer@24.9.0(typescript@5.8.3))(rollup@4.37.0) - '@unlighthouse/server': 0.16.3(magicast@0.3.5)(puppeteer@24.9.0(typescript@5.8.3))(rollup@4.37.0) + '@unlighthouse/core': 0.16.3(magicast@0.3.5)(puppeteer@24.10.0(typescript@5.8.3))(rollup@4.37.0) + '@unlighthouse/server': 0.16.3(magicast@0.3.5)(puppeteer@24.10.0(typescript@5.8.3))(rollup@4.37.0) better-opn: 3.0.2 cac: 6.7.14 consola: 3.4.2 @@ -10250,18 +10295,18 @@ snapshots: '@unlighthouse/client@0.16.3(typescript@5.8.3)': dependencies: - '@headlessui/vue': 1.7.23(vue@3.5.14(typescript@5.8.3)) + '@headlessui/vue': 1.7.23(vue@3.5.16(typescript@5.8.3)) dayjs: 1.11.13 defu: 6.1.4 fuse.js: 7.1.0 lightweight-charts: 5.0.7 lodash-es: 4.17.21 ufo: 1.5.4 - vue: 3.5.14(typescript@5.8.3) + vue: 3.5.16(typescript@5.8.3) transitivePeerDependencies: - typescript - '@unlighthouse/core@0.16.3(magicast@0.3.5)(puppeteer@24.9.0(typescript@5.8.3))(rollup@4.37.0)': + '@unlighthouse/core@0.16.3(magicast@0.3.5)(puppeteer@24.10.0(typescript@5.8.3))(rollup@4.37.0)': dependencies: '@puppeteer/browsers': 2.10.5 '@unrouted/core': 0.6.0 @@ -10281,15 +10326,15 @@ snapshots: h3: 1.15.3 hookable: 5.5.3 launch-editor: 2.10.0 - lighthouse: 12.6.0 + lighthouse: 12.6.1 lodash-es: 4.17.21 minimist: 1.2.8 mlly: 1.7.4 object-hash: 3.0.0 ofetch: 1.4.1 pathe: 2.0.3 - puppeteer-cluster: 0.24.0(puppeteer@24.9.0(typescript@5.8.3)) - puppeteer-core: 24.9.0 + puppeteer-cluster: 0.24.0(puppeteer@24.10.0(typescript@5.8.3)) + puppeteer-core: 24.10.0 radix3: 1.1.2 regexparam: 3.0.0 sanitize-filename: 1.6.3 @@ -10301,7 +10346,7 @@ snapshots: wrap-ansi: 9.0.0 ws: 8.18.2 optionalDependencies: - puppeteer: 24.9.0(typescript@5.8.3) + puppeteer: 24.10.0(typescript@5.8.3) transitivePeerDependencies: - bare-buffer - bufferutil @@ -10311,9 +10356,9 @@ snapshots: - supports-color - utf-8-validate - '@unlighthouse/server@0.16.3(magicast@0.3.5)(puppeteer@24.9.0(typescript@5.8.3))(rollup@4.37.0)': + '@unlighthouse/server@0.16.3(magicast@0.3.5)(puppeteer@24.10.0(typescript@5.8.3))(rollup@4.37.0)': dependencies: - '@unlighthouse/core': 0.16.3(magicast@0.3.5)(puppeteer@24.9.0(typescript@5.8.3))(rollup@4.37.0) + '@unlighthouse/core': 0.16.3(magicast@0.3.5)(puppeteer@24.10.0(typescript@5.8.3))(rollup@4.37.0) h3: 1.15.3 listhen: 1.9.0 transitivePeerDependencies: @@ -10415,23 +10460,23 @@ snapshots: dependencies: crypto-js: 4.2.0 - '@vercel/analytics@1.5.0(next@15.4.0-canary.0(@babel/core@7.27.1)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(svelte@5.1.15)(vue@3.5.14(typescript@5.8.3))': + '@vercel/analytics@1.5.0(next@15.4.0-canary.0(@babel/core@7.27.4)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(svelte@5.1.15)(vue@3.5.16(typescript@5.8.3))': optionalDependencies: - next: 15.4.0-canary.0(@babel/core@7.27.1)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.4.0-canary.0(@babel/core@7.27.4)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 svelte: 5.1.15 - vue: 3.5.14(typescript@5.8.3) + vue: 3.5.16(typescript@5.8.3) '@vercel/kv@3.0.0': dependencies: '@upstash/redis': 1.34.7 - '@vercel/speed-insights@1.2.0(next@15.4.0-canary.0(@babel/core@7.27.1)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(svelte@5.1.15)(vue@3.5.14(typescript@5.8.3))': + '@vercel/speed-insights@1.2.0(next@15.4.0-canary.0(@babel/core@7.27.4)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(svelte@5.1.15)(vue@3.5.16(typescript@5.8.3))': optionalDependencies: - next: 15.4.0-canary.0(@babel/core@7.27.1)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.4.0-canary.0(@babel/core@7.27.4)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 svelte: 5.1.15 - vue: 3.5.14(typescript@5.8.3) + vue: 3.5.16(typescript@5.8.3) '@vitest/coverage-v8@3.1.1(vitest@3.1.1)': dependencies: @@ -10458,14 +10503,14 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.1.1(msw@2.7.4(@types/node@22.14.1)(typescript@5.8.3))(vite@6.2.0(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0))': + '@vitest/mocker@3.1.1(msw@2.7.4(@types/node@22.14.1)(typescript@5.8.3))(vite@6.3.5(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0))': dependencies: '@vitest/spy': 3.1.1 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: msw: 2.7.4(@types/node@22.14.1)(typescript@5.8.3) - vite: 6.2.0(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0) + vite: 6.3.5(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0) '@vitest/pretty-format@3.1.1': dependencies: @@ -10503,59 +10548,59 @@ snapshots: loupe: 3.1.3 tinyrainbow: 2.0.0 - '@vue/compiler-core@3.5.14': + '@vue/compiler-core@3.5.16': dependencies: - '@babel/parser': 7.27.2 - '@vue/shared': 3.5.14 + '@babel/parser': 7.27.5 + '@vue/shared': 3.5.16 entities: 4.5.0 estree-walker: 2.0.2 source-map-js: 1.2.1 - '@vue/compiler-dom@3.5.14': + '@vue/compiler-dom@3.5.16': dependencies: - '@vue/compiler-core': 3.5.14 - '@vue/shared': 3.5.14 + '@vue/compiler-core': 3.5.16 + '@vue/shared': 3.5.16 - '@vue/compiler-sfc@3.5.14': + '@vue/compiler-sfc@3.5.16': dependencies: - '@babel/parser': 7.27.2 - '@vue/compiler-core': 3.5.14 - '@vue/compiler-dom': 3.5.14 - '@vue/compiler-ssr': 3.5.14 - '@vue/shared': 3.5.14 + '@babel/parser': 7.27.5 + '@vue/compiler-core': 3.5.16 + '@vue/compiler-dom': 3.5.16 + '@vue/compiler-ssr': 3.5.16 + '@vue/shared': 3.5.16 estree-walker: 2.0.2 magic-string: 0.30.17 postcss: 8.5.3 source-map-js: 1.2.1 - '@vue/compiler-ssr@3.5.14': + '@vue/compiler-ssr@3.5.16': dependencies: - '@vue/compiler-dom': 3.5.14 - '@vue/shared': 3.5.14 + '@vue/compiler-dom': 3.5.16 + '@vue/shared': 3.5.16 - '@vue/reactivity@3.5.14': + '@vue/reactivity@3.5.16': dependencies: - '@vue/shared': 3.5.14 + '@vue/shared': 3.5.16 - '@vue/runtime-core@3.5.14': + '@vue/runtime-core@3.5.16': dependencies: - '@vue/reactivity': 3.5.14 - '@vue/shared': 3.5.14 + '@vue/reactivity': 3.5.16 + '@vue/shared': 3.5.16 - '@vue/runtime-dom@3.5.14': + '@vue/runtime-dom@3.5.16': dependencies: - '@vue/reactivity': 3.5.14 - '@vue/runtime-core': 3.5.14 - '@vue/shared': 3.5.14 + '@vue/reactivity': 3.5.16 + '@vue/runtime-core': 3.5.16 + '@vue/shared': 3.5.16 csstype: 3.1.3 - '@vue/server-renderer@3.5.14(vue@3.5.14(typescript@5.8.3))': + '@vue/server-renderer@3.5.16(vue@3.5.16(typescript@5.8.3))': dependencies: - '@vue/compiler-ssr': 3.5.14 - '@vue/shared': 3.5.14 - vue: 3.5.14(typescript@5.8.3) + '@vue/compiler-ssr': 3.5.16 + '@vue/shared': 3.5.16 + vue: 3.5.16(typescript@5.8.3) - '@vue/shared@3.5.14': {} + '@vue/shared@3.5.16': {} acorn-jsx@5.3.2(acorn@8.14.0): dependencies: @@ -10580,7 +10625,7 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.4.1 + debug: 4.4.0 transitivePeerDependencies: - supports-color @@ -10781,7 +10826,7 @@ snapshots: babel-plugin-jest-hoist@29.6.3: dependencies: '@babel/template': 7.24.7 - '@babel/types': 7.27.1 + '@babel/types': 7.24.7 '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.20.6 @@ -11039,11 +11084,11 @@ snapshots: urlpattern-polyfill: 10.0.0 zod: 3.23.8 - chromium-bidi@5.1.0(devtools-protocol@0.0.1439962): + chromium-bidi@5.1.0(devtools-protocol@0.0.1452169): dependencies: - devtools-protocol: 0.0.1439962 + devtools-protocol: 0.0.1452169 mitt: 3.0.1 - zod: 3.24.2 + zod: 3.25.49 ci-info@3.9.0: {} @@ -11358,9 +11403,9 @@ snapshots: devtools-protocol@0.0.1312386: {} - devtools-protocol@0.0.1439962: {} + devtools-protocol@0.0.1452169: {} - devtools-protocol@0.0.1445099: {} + devtools-protocol@0.0.1467305: {} diff-sequences@29.6.3: {} @@ -12138,6 +12183,10 @@ snapshots: optionalDependencies: picomatch: 4.0.2 + fdir@6.4.5(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + fflate@0.8.2: {} figures@6.1.0: @@ -12385,11 +12434,13 @@ snapshots: dependencies: '@sindresorhus/merge-streams': 2.3.0 fast-glob: 3.3.3 - ignore: 7.0.4 + ignore: 7.0.5 path-type: 6.0.0 slash: 5.1.0 unicorn-magic: 0.3.0 + globrex@0.1.2: {} + gopd@1.2.0: {} got@11.8.6: @@ -12502,7 +12553,7 @@ snapshots: https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.4.1 + debug: 4.4.0 transitivePeerDependencies: - supports-color @@ -12533,7 +12584,7 @@ snapshots: ignore@5.3.2: {} - ignore@7.0.4: {} + ignore@7.0.5: {} image-ssim@0.2.0: {} @@ -12768,7 +12819,7 @@ snapshots: istanbul-lib-instrument@5.2.1: dependencies: '@babel/core': 7.24.7 - '@babel/parser': 7.27.2 + '@babel/parser': 7.24.7 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 6.3.1 @@ -12793,7 +12844,7 @@ snapshots: istanbul-lib-source-maps@4.0.1: dependencies: - debug: 4.4.1 + debug: 4.4.0 istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: @@ -13220,7 +13271,7 @@ snapshots: launch-editor@2.10.0: dependencies: picocolors: 1.1.1 - shell-quote: 1.8.2 + shell-quote: 1.8.3 legacy-javascript@0.0.1: {} @@ -13282,15 +13333,15 @@ snapshots: - supports-color - utf-8-validate - lighthouse@12.6.0: + lighthouse@12.6.1: dependencies: - '@paulirish/trace_engine': 0.0.52 + '@paulirish/trace_engine': 0.0.53 '@sentry/node': 7.120.3 axe-core: 4.10.3 chrome-launcher: 1.2.0 configstore: 5.0.1 csp_evaluator: 1.1.5 - devtools-protocol: 0.0.1445099 + devtools-protocol: 0.0.1467305 enquirer: 2.4.1 http-link-header: 1.1.3 intl-messageformat: 10.7.16 @@ -13303,7 +13354,7 @@ snapshots: metaviewport-parser: 0.3.0 open: 8.4.2 parse-cache-control: 1.0.1 - puppeteer-core: 24.9.0 + puppeteer-core: 24.10.0 robots-parser: 3.0.1 semver: 5.7.2 speedline-core: 1.4.3 @@ -13552,7 +13603,7 @@ snapshots: mlly@1.7.4: dependencies: - acorn: 8.14.1 + acorn: 8.14.0 pathe: 2.0.3 pkg-types: 1.3.1 ufo: 1.5.4 @@ -13610,25 +13661,25 @@ snapshots: netmask@2.0.2: {} - next-auth@5.0.0-beta.25(next@15.4.0-canary.0(@babel/core@7.27.1)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(nodemailer@6.9.16)(react@19.1.0): + next-auth@5.0.0-beta.25(next@15.4.0-canary.0(@babel/core@7.27.4)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(nodemailer@6.9.16)(react@19.1.0): dependencies: '@auth/core': 0.37.2(nodemailer@6.9.16) - next: 15.4.0-canary.0(@babel/core@7.27.1)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.4.0-canary.0(@babel/core@7.27.4)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 optionalDependencies: nodemailer: 6.9.16 - next-intl@4.0.2(next@15.4.0-canary.0(@babel/core@7.27.1)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(typescript@5.8.3): + next-intl@4.0.2(next@15.4.0-canary.0(@babel/core@7.27.4)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(typescript@5.8.3): dependencies: '@formatjs/intl-localematcher': 0.5.10 negotiator: 1.0.0 - next: 15.4.0-canary.0(@babel/core@7.27.1)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.4.0-canary.0(@babel/core@7.27.4)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 use-intl: 4.0.2(react@19.1.0) optionalDependencies: typescript: 5.8.3 - next@15.4.0-canary.0(@babel/core@7.27.1)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + next@15.4.0-canary.0(@babel/core@7.27.4)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@next/env': 15.4.0-canary.0 '@swc/counter': 0.1.3 @@ -13638,7 +13689,7 @@ snapshots: postcss: 8.4.31 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - styled-jsx: 5.1.6(@babel/core@7.27.1)(react@19.1.0) + styled-jsx: 5.1.6(@babel/core@7.27.4)(react@19.1.0) optionalDependencies: '@next/swc-darwin-arm64': 15.4.0-canary.0 '@next/swc-darwin-x64': 15.4.0-canary.0 @@ -13696,12 +13747,12 @@ snapshots: dependencies: boolbase: 1.0.0 - nuqs@2.4.2(next@15.4.0-canary.0(@babel/core@7.27.1)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0): + nuqs@2.4.2(next@15.4.0-canary.0(@babel/core@7.27.4)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0): dependencies: mitt: 3.0.1 react: 19.1.0 optionalDependencies: - next: 15.4.0-canary.0(@babel/core@7.27.1)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.4.0-canary.0(@babel/core@7.27.4)(@playwright/test@1.51.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) nypm@0.5.4: dependencies: @@ -14091,10 +14142,10 @@ snapshots: punycode@2.3.1: {} - puppeteer-cluster@0.24.0(puppeteer@24.9.0(typescript@5.8.3)): + puppeteer-cluster@0.24.0(puppeteer@24.10.0(typescript@5.8.3)): dependencies: - debug: 4.4.1 - puppeteer: 24.9.0(typescript@5.8.3) + debug: 4.4.0 + puppeteer: 24.10.0(typescript@5.8.3) transitivePeerDependencies: - supports-color @@ -14102,7 +14153,7 @@ snapshots: dependencies: '@puppeteer/browsers': 2.3.0 chromium-bidi: 0.6.3(devtools-protocol@0.0.1312386) - debug: 4.4.1 + debug: 4.4.0 devtools-protocol: 0.0.1312386 ws: 8.18.2 transitivePeerDependencies: @@ -14111,12 +14162,12 @@ snapshots: - supports-color - utf-8-validate - puppeteer-core@24.9.0: + puppeteer-core@24.10.0: dependencies: '@puppeteer/browsers': 2.10.5 - chromium-bidi: 5.1.0(devtools-protocol@0.0.1439962) + chromium-bidi: 5.1.0(devtools-protocol@0.0.1452169) debug: 4.4.1 - devtools-protocol: 0.0.1439962 + devtools-protocol: 0.0.1452169 typed-query-selector: 2.12.0 ws: 8.18.2 transitivePeerDependencies: @@ -14125,13 +14176,13 @@ snapshots: - supports-color - utf-8-validate - puppeteer@24.9.0(typescript@5.8.3): + puppeteer@24.10.0(typescript@5.8.3): dependencies: '@puppeteer/browsers': 2.10.5 - chromium-bidi: 5.1.0(devtools-protocol@0.0.1439962) + chromium-bidi: 5.1.0(devtools-protocol@0.0.1452169) cosmiconfig: 9.0.0(typescript@5.8.3) - devtools-protocol: 0.0.1439962 - puppeteer-core: 24.9.0 + devtools-protocol: 0.0.1452169 + puppeteer-core: 24.10.0 typed-query-selector: 2.12.0 transitivePeerDependencies: - bare-buffer @@ -14490,7 +14541,7 @@ snapshots: shebang-regex@3.0.0: {} - shell-quote@1.8.2: {} + shell-quote@1.8.3: {} side-channel-list@1.0.0: dependencies: @@ -14746,12 +14797,12 @@ snapshots: stubborn-fs@1.2.5: {} - styled-jsx@5.1.6(@babel/core@7.27.1)(react@19.1.0): + styled-jsx@5.1.6(@babel/core@7.27.4)(react@19.1.0): dependencies: client-only: 0.0.1 react: 19.1.0 optionalDependencies: - '@babel/core': 7.27.1 + '@babel/core': 7.27.4 sucrase@3.35.0: dependencies: @@ -14891,6 +14942,11 @@ snapshots: fdir: 6.4.3(picomatch@4.0.2) picomatch: 4.0.2 + tinyglobby@0.2.14: + dependencies: + fdir: 6.4.5(picomatch@4.0.2) + picomatch: 4.0.2 + tinypool@1.0.2: {} tinyrainbow@2.0.0: {} @@ -14946,6 +15002,10 @@ snapshots: ts-interface-checker@0.1.13: {} + tsconfck@3.1.6(typescript@5.8.3): + optionalDependencies: + typescript: 5.8.3 + tsconfig-paths@3.15.0: dependencies: '@types/json5': 0.0.29 @@ -15139,14 +15199,14 @@ snapshots: universalify@2.0.1: {} - unlighthouse@0.16.3(encoding@0.1.13)(magicast@0.3.5)(puppeteer@24.9.0(typescript@5.8.3))(rollup@4.37.0)(typescript@5.8.3)(vue@3.5.14(typescript@5.8.3)): + unlighthouse@0.16.3(encoding@0.1.13)(magicast@0.3.5)(puppeteer@24.10.0(typescript@5.8.3))(rollup@4.37.0)(typescript@5.8.3)(vue@3.5.16(typescript@5.8.3)): dependencies: - '@unlighthouse/cli': 0.16.3(encoding@0.1.13)(magicast@0.3.5)(puppeteer@24.9.0(typescript@5.8.3))(rollup@4.37.0)(typescript@5.8.3) + '@unlighthouse/cli': 0.16.3(encoding@0.1.13)(magicast@0.3.5)(puppeteer@24.10.0(typescript@5.8.3))(rollup@4.37.0)(typescript@5.8.3) '@unlighthouse/client': 0.16.3(typescript@5.8.3) - '@unlighthouse/core': 0.16.3(magicast@0.3.5)(puppeteer@24.9.0(typescript@5.8.3))(rollup@4.37.0) + '@unlighthouse/core': 0.16.3(magicast@0.3.5)(puppeteer@24.10.0(typescript@5.8.3))(rollup@4.37.0) optionalDependencies: - puppeteer: 24.9.0(typescript@5.8.3) - vue: 3.5.14(typescript@5.8.3) + puppeteer: 24.10.0(typescript@5.8.3) + vue: 3.5.16(typescript@5.8.3) transitivePeerDependencies: - bare-buffer - bufferutil @@ -15177,9 +15237,9 @@ snapshots: untyped@1.5.2: dependencies: - '@babel/core': 7.27.1 - '@babel/standalone': 7.27.3 - '@babel/types': 7.27.1 + '@babel/core': 7.27.4 + '@babel/standalone': 7.27.5 + '@babel/types': 7.26.9 citty: 0.1.6 defu: 6.1.4 jiti: 2.4.2 @@ -15249,7 +15309,7 @@ snapshots: debug: 4.4.0 es-module-lexer: 1.6.0 pathe: 2.0.3 - vite: 6.2.0(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0) + vite: 6.3.5(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0) transitivePeerDependencies: - '@types/node' - jiti @@ -15264,11 +15324,25 @@ snapshots: - tsx - yaml - vite@6.2.0(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0): + vite-tsconfig-paths@5.1.4(typescript@5.8.3)(vite@6.3.5(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0)): + dependencies: + debug: 4.4.0 + globrex: 0.1.2 + tsconfck: 3.1.6(typescript@5.8.3) + optionalDependencies: + vite: 6.3.5(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0) + transitivePeerDependencies: + - supports-color + - typescript + + vite@6.3.5(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0): dependencies: esbuild: 0.25.1 + fdir: 6.4.5(picomatch@4.0.2) + picomatch: 4.0.2 postcss: 8.5.3 rollup: 4.37.0 + tinyglobby: 0.2.14 optionalDependencies: '@types/node': 22.14.1 fsevents: 2.3.3 @@ -15279,7 +15353,7 @@ snapshots: vitest@3.1.1(@types/node@22.14.1)(@vitest/ui@3.1.1)(jiti@2.4.2)(lightningcss@1.29.2)(msw@2.7.4(@types/node@22.14.1)(typescript@5.8.3))(yaml@2.6.0): dependencies: '@vitest/expect': 3.1.1 - '@vitest/mocker': 3.1.1(msw@2.7.4(@types/node@22.14.1)(typescript@5.8.3))(vite@6.2.0(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0)) + '@vitest/mocker': 3.1.1(msw@2.7.4(@types/node@22.14.1)(typescript@5.8.3))(vite@6.3.5(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0)) '@vitest/pretty-format': 3.1.1 '@vitest/runner': 3.1.1 '@vitest/snapshot': 3.1.1 @@ -15295,7 +15369,7 @@ snapshots: tinyexec: 0.3.2 tinypool: 1.0.2 tinyrainbow: 2.0.0 - vite: 6.2.0(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0) + vite: 6.3.5(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0) vite-node: 3.1.1(@types/node@22.14.1)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.6.0) why-is-node-running: 2.3.0 optionalDependencies: @@ -15315,13 +15389,13 @@ snapshots: - tsx - yaml - vue@3.5.14(typescript@5.8.3): + vue@3.5.16(typescript@5.8.3): dependencies: - '@vue/compiler-dom': 3.5.14 - '@vue/compiler-sfc': 3.5.14 - '@vue/runtime-dom': 3.5.14 - '@vue/server-renderer': 3.5.14(vue@3.5.14(typescript@5.8.3)) - '@vue/shared': 3.5.14 + '@vue/compiler-dom': 3.5.16 + '@vue/compiler-sfc': 3.5.16 + '@vue/runtime-dom': 3.5.16 + '@vue/server-renderer': 3.5.16(vue@3.5.16(typescript@5.8.3)) + '@vue/shared': 3.5.16 optionalDependencies: typescript: 5.8.3 @@ -15513,4 +15587,4 @@ snapshots: zod@3.24.2: {} - zod@3.25.48: {} + zod@3.25.49: {}