diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 934dc4d0..e51296e2 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -13,12 +13,12 @@ jobs: concurrency: ${{ github.workflow }}-${{ github.ref }} steps: - name: Checkout code - uses: actions/checkout@v5 + uses: actions/checkout@v6 # v6.0.2 - name: Setup pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@v5 # v5.0.0 - name: Setup Node.js - uses: actions/setup-node@v5 + uses: actions/setup-node@v6 # v6.3.0 with: node-version-file: '.nvmrc' cache: 'pnpm' diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 172cac9d..281681e7 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,11 +35,11 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 # v6.0.2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v4 + uses: github/codeql-action/init@v4 # v4.34.1 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -50,7 +50,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v4 + uses: github/codeql-action/autobuild@v4 # v4.34.1 # โ„น๏ธ Command-line programs to run using the OS shell. # ๐Ÿ“š https://git.io/JvXDl @@ -64,4 +64,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v4 + uses: github/codeql-action/analyze@v4 # v4.34.1 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index f9c2814a..dcd53f1a 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -7,19 +7,24 @@ on: concurrency: ${{ github.workflow }}-${{ github.ref }} +permissions: + id-token: write # Required for OIDC + contents: write # Required to push tags and releases + pull-requests: write # Required for changesets to create release PRs + jobs: release: name: Release runs-on: ubuntu-latest steps: - name: Checkout Repo - uses: actions/checkout@v5 + uses: actions/checkout@v6 # v6.0.2 - name: Setup pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@v5 # v5.0.0 - name: Setup Node - uses: actions/setup-node@v5 + uses: actions/setup-node@v6 # v6.3.0 with: node-version-file: ".nvmrc" cache: "pnpm" @@ -29,10 +34,9 @@ jobs: - name: Create Release Pull Request or Publish to npm id: changesets - uses: changesets/action@v1 + uses: changesets/action@v1 # v1.7.0 with: version: pnpm run version publish: pnpm publish -r env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_SECRET }} diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 82bcca65..8a92d62c 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Close Stale Issues - uses: actions/stale@v10 + uses: actions/stale@v10 # v10.2.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-pr-message: | diff --git a/examples/nextjs/CHANGELOG.md b/examples/nextjs/CHANGELOG.md index d693193e..957ca160 100644 --- a/examples/nextjs/CHANGELOG.md +++ b/examples/nextjs/CHANGELOG.md @@ -1,5 +1,48 @@ # nextjs +## 0.1.33 + +### Patch Changes + +- Updated dependencies [8d3f2c2] + - @godaddy/react@1.0.35 + +## 0.1.32 + +### Patch Changes + +- Updated dependencies [57d6115] + - @godaddy/react@1.0.34 + +## 0.1.31 + +### Patch Changes + +- Updated dependencies [353dc8b] + - @godaddy/localizations@1.0.9 + - @godaddy/react@1.0.33 + +## 0.1.30 + +### Patch Changes + +- Updated dependencies [ed3cbef] + - @godaddy/react@1.0.32 + +## 0.1.29 + +### Patch Changes + +- Updated dependencies [b3c0dad] + - @godaddy/react@1.0.31 + +## 0.1.28 + +### Patch Changes + +- Updated dependencies [c1ebb52] + - @godaddy/react@1.0.30 + ## 0.1.27 ### Patch Changes diff --git a/examples/nextjs/app/page.tsx b/examples/nextjs/app/page.tsx index 28e0c01c..ba3079a8 100644 --- a/examples/nextjs/app/page.tsx +++ b/examples/nextjs/app/page.tsx @@ -68,7 +68,7 @@ export default async function Home() { }, paypal: { processor: 'paypal', - checkoutTypes: ['express', 'standard'], + checkoutTypes: ['standard'], }, }, operatingHours: { diff --git a/examples/nextjs/app/store/actions.ts b/examples/nextjs/app/store/actions.ts index 6dbe2207..49c523ec 100644 --- a/examples/nextjs/app/store/actions.ts +++ b/examples/nextjs/app/store/actions.ts @@ -3,6 +3,41 @@ import { createCheckoutSession } from '@godaddy/react/server'; import { redirect } from 'next/navigation'; +/** + * Fetches selling plans for given SKU IDs from the external (or local) selling-plans API. + * Only runs when SELLING_PLANS_API_URL is set (e.g. in .env.local). If unset, returns + * empty so no request is made and no 404 is logged. + */ +export async function getSellingPlans(storeId: string, options: any) { + const base = process.env.SELLING_PLANS_API_URL; + if (!base) { + return null; + } + // API shape: GET /api/v1/selling-plans/{storeId}/groups?skuIds=... + const path = `/api/v1/selling-plans/${encodeURIComponent(storeId)}/groups` + const url = new URL(path, base); + for (const id of options.skuIds ?? []) { + url.searchParams.append('skuIds', id); + } + for (const id of options.skuGroupIds ?? []) { + url.searchParams.append('skuGroupIds', id); + } + url.searchParams.append('includes', 'catalogPrices'); + url.searchParams.append('includes', 'allocations'); + // url.searchParams.append('planStatus', 'ACTIVE'); // uncomment it when push to repo + try { + const res = await fetch(url.toString(), { cache: 'no-store' }); + if (!res.ok) { + return null; + } + const data = await res.json(); + return data.groups; + } catch { + // Network/socket errors (e.g. selling-plans API not running): fail silently so product page still works + return null; + } +} + export async function checkoutWithOrder(orderId: string) { const session = await createCheckoutSession( { diff --git a/examples/nextjs/app/store/product/[productId]/product.tsx b/examples/nextjs/app/store/product/[productId]/product.tsx index 9b2453e4..23cb2845 100644 --- a/examples/nextjs/app/store/product/[productId]/product.tsx +++ b/examples/nextjs/app/store/product/[productId]/product.tsx @@ -3,10 +3,20 @@ import { ProductDetails } from '@godaddy/react'; import { ArrowLeft } from 'lucide-react'; import Link from 'next/link'; +import { useState } from 'react'; +import { SellingPlanDropdown } from '../selling-plan-dropdown'; import { useCart } from '../../layout'; +/** Route param productId is the SKU Group id (product id from catalog skuGroups). */ export default function Product({ productId }: { productId: string }) { const { openCart } = useCart(); + const [selectedSellingPlanId, setSelectedSellingPlanId] = useState(null); + const [selectedSellingPlan, setSelectedSellingPlan] = useState(null); + + const handleSellingPlanChange = (planId: string | null, plan: any) => { + setSelectedSellingPlanId(planId); + setSelectedSellingPlan(plan); + }; return (
@@ -17,7 +27,23 @@ export default function Product({ productId }: { productId: string }) { Back to Store - + ( + + ), + }} + />
); } diff --git a/examples/nextjs/app/store/product/selling-plan-dropdown.tsx b/examples/nextjs/app/store/product/selling-plan-dropdown.tsx new file mode 100644 index 00000000..63bd1c56 --- /dev/null +++ b/examples/nextjs/app/store/product/selling-plan-dropdown.tsx @@ -0,0 +1,150 @@ +'use client'; + +import { useCallback, useEffect, useState } from 'react'; +import { getSellingPlans } from '../actions'; + +/** + * Resolve checkout price for the current SKU from the plan's catalogPrices. + * API shape: plan.catalogPrices[] has { skuId, checkoutPrices: [{ value, currency }] }. + * Picks the entry matching skuId and returns checkoutPrices[0] as { value, currencyCode }. + * Value is in minor units (cents). + */ +function normalizeCheckoutPrice( + plan: any, + skuId: string | null +): { value: number; currencyCode?: string } | undefined { + if (plan.checkoutPrice?.value != null) { + return { + value: Number(plan.checkoutPrice.value), + currencyCode: plan.checkoutPrice.currencyCode ?? plan.checkoutPrice.currency, + }; + } + if (skuId && Array.isArray(plan.catalogPrices)) { + const forSku = plan.catalogPrices.find((c: any) => c.skuId === skuId); + const checkout = forSku?.checkoutPrices?.[0]; + if (checkout?.value != null) { + return { + value: Number(checkout.value), + currencyCode: checkout.currencyCode ?? checkout.currency, + }; + } + } + const first = plan.catalogPrices?.[0]; + const checkoutFirst = first?.checkoutPrices?.[0]; + if (checkoutFirst?.value != null) { + return { + value: Number(checkoutFirst.value), + currencyCode: checkoutFirst.currencyCode ?? checkoutFirst.currency, + }; + } + if (plan.priceAtCheckout?.value != null) { + return { + value: Number(plan.priceAtCheckout.value), + currencyCode: plan.priceAtCheckout.currencyCode ?? plan.priceAtCheckout.currency, + }; + } + return undefined; +} + +export function SellingPlanDropdown({ + storeId, + skuId, + skuGroupId, + selectedPlanId, + onSelectionChange, +}: { + storeId: string; + skuId: string | null; + skuGroupId: string | null; + selectedPlanId: string | null; + onSelectionChange: (planId: string | null, plan: any) => void; +}) { + const [plans, setPlans] = useState([]); + const [loading, setLoading] = useState(false); + + /** + * Loads selling plans from the selling-plans API for the current skuId/skuGroupId. + * + * Production note: This triggers one API call per product (or per PDP visit). To reduce + * requests, consider a strategy to preload selling groups on the store list page: fetch + * groups for all visible skuIds and skuGroupIds once, pass the result (e.g. via context + * or cache) into this component. + */ + const loadPlans = useCallback(async () => { + if (!storeId || (!skuId && !skuGroupId)) { + setPlans([]); + return; + } + setLoading(true); + try { + const groups: any = await getSellingPlans(storeId, { + skuIds: skuId ? [skuId] : [], + skuGroupIds: skuGroupId ? [skuGroupId] : [], + }) ?? []; + // 1. Max 2 selling groups: one for skuId, one for skuGroupId + // 2. Each group has sellingPlans and allocations + // 3. Each allocation has resourceType (SKU | SKU_GROUP) and resourceId + // 4. Prefer group that matches skuId; else group that matches skuGroupId; else [] + const forSku = groups?.find((g: any) => + (g.allocations ?? []).some( + (a: any) => a.resourceType === 'SKU' && a.resourceId === skuId + ) + ); + const forSkuGroup = groups?.find((g: any) => + (g.allocations ?? []).some( + (a: any) => + a.resourceType === 'SKU_GROUP' && a.resourceId === skuGroupId + ) + ); + const sellingPlans = (forSku ?? forSkuGroup)?.sellingPlans ?? []; + setPlans(sellingPlans); + if (sellingPlans.length === 0) { + onSelectionChange(null, null); + } + } finally { + setLoading(false); + } + }, [storeId, skuId, skuGroupId, onSelectionChange]); + + useEffect(() => { + loadPlans(); + }, [loadPlans]); + + if (loading || plans.length === 0) { + return null; + } + + const value = selectedPlanId ?? ''; + + return ( +
+ + +
+ ); +} diff --git a/examples/nextjs/env.sample b/examples/nextjs/env.sample index abd13fa6..87ed4b2a 100644 --- a/examples/nextjs/env.sample +++ b/examples/nextjs/env.sample @@ -25,3 +25,7 @@ NEXT_PUBLIC_PAYPAL_CLIENT_ID= # MercadoPago Credentials NEXT_PUBLIC_MERCADOPAGO_PUBLIC_KEY= NEXT_PUBLIC_MERCADOPAGO_COUNTRY=AR + +# Selling plans API (external or local in dev) +# In local development set to your local API base URL (e.g. http://localhost:8443) +SELLING_PLANS_API_URL= diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json index d7311655..6b0c7c7e 100644 --- a/examples/nextjs/package.json +++ b/examples/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "nextjs", - "version": "0.1.27", + "version": "0.1.33", "private": true, "scripts": { "dev": "next dev", @@ -15,7 +15,7 @@ "@tanstack/react-query": "^5.66.0", "@tanstack/react-query-devtools": "^5.76.1", "lucide-react": "^0.475.0", - "next": "16.0.10", + "next": "16.2.1", "react": "19.2.0", "react-dom": "19.2.0", "zod": "^3.24.1" diff --git a/package.json b/package.json index 2a361aeb..880e86ae 100644 --- a/package.json +++ b/package.json @@ -42,11 +42,11 @@ }, "devDependencies": { "@changesets/cli": "^2.29.5", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "^9.24.0", - "@typescript-eslint/eslint-plugin": "^8.35.0", - "@typescript-eslint/parser": "^8.30.1", - "eslint": "^9.24.0", + "@eslint/eslintrc": "^3.3.4", + "@eslint/js": "^10.0.0", + "@typescript-eslint/eslint-plugin": "^8.54.0", + "@typescript-eslint/parser": "^8.54.0", + "eslint": "^10.0.2", "eslint-plugin-json": "^4.0.1", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-mocha": "^10.5.0", diff --git a/packages/app-connect/package.json b/packages/app-connect/package.json index f53673c4..03d4bcfe 100644 --- a/packages/app-connect/package.json +++ b/packages/app-connect/package.json @@ -67,7 +67,7 @@ "pino-pretty": "^13.0.0", "tsdown": "^0.15.6", "typescript": "~5.7.3", - "vitest": "^1.2.2" + "vitest": "^4.1.2" }, "publishConfig": { "registry": "https://registry.npmjs.org/" diff --git a/packages/eslint-config-godaddy-react-typescript/CHANGELOG.md b/packages/eslint-config-godaddy-react-typescript/CHANGELOG.md index b4ffa85d..7ced0a14 100644 --- a/packages/eslint-config-godaddy-react-typescript/CHANGELOG.md +++ b/packages/eslint-config-godaddy-react-typescript/CHANGELOG.md @@ -1,5 +1,16 @@ # eslint-config-godaddy-react-typescript +## 6.0.0 + +### Major Changes + +- 0fabf67: Upgrade to ESLint 10 + +### Patch Changes + +- Updated dependencies [0fabf67] + - eslint-config-godaddy-react@11.0.0 + ## 5.0.0 ### Major Changes diff --git a/packages/eslint-config-godaddy-react-typescript/package.json b/packages/eslint-config-godaddy-react-typescript/package.json index e32d6359..a27a7073 100644 --- a/packages/eslint-config-godaddy-react-typescript/package.json +++ b/packages/eslint-config-godaddy-react-typescript/package.json @@ -1,6 +1,6 @@ { "name": "eslint-config-godaddy-react-typescript", - "version": "5.0.0", + "version": "6.0.0", "description": "ESLint config for consistent style in ES6 React projects using TypeScript at GoDaddy.", "type": "module", "main": "index.js", @@ -27,17 +27,17 @@ ], "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "^8.35.0", - "@typescript-eslint/parser": "^8.30.1", + "@typescript-eslint/eslint-plugin": "^8.54.0", + "@typescript-eslint/parser": "^8.54.0", "eslint-config-godaddy-react": "workspace:^", "eslint-plugin-react-hooks": "^5.2.0" }, "peerDependencies": { - "eslint": "^9", + "eslint": "^10", "typescript": ">=5" }, "devDependencies": { - "eslint": "^9.24.0" + "eslint": "^10.0.2" }, "exports": { ".": "./index.js" diff --git a/packages/eslint-config-godaddy-react/CHANGELOG.md b/packages/eslint-config-godaddy-react/CHANGELOG.md index 0b09faa0..d85a413e 100644 --- a/packages/eslint-config-godaddy-react/CHANGELOG.md +++ b/packages/eslint-config-godaddy-react/CHANGELOG.md @@ -1,5 +1,16 @@ # CHANGELOG +## 11.0.0 + +### Major Changes + +- 0fabf67: Upgrade to ESLint 10 + +### Patch Changes + +- Updated dependencies [0fabf67] + - eslint-config-godaddy@9.0.0 + ## 10.0.0 ### Major Changes diff --git a/packages/eslint-config-godaddy-react/index.js b/packages/eslint-config-godaddy-react/index.js index 44c78eb9..488405ac 100644 --- a/packages/eslint-config-godaddy-react/index.js +++ b/packages/eslint-config-godaddy-react/index.js @@ -2,6 +2,7 @@ import gdConfig from 'eslint-config-godaddy'; import react from 'eslint-plugin-react'; import jsxA11y from 'eslint-plugin-jsx-a11y'; +import { fixupConfigRules, fixupPluginRules } from '@eslint/compat'; import { FlatCompat } from '@eslint/eslintrc'; import path from 'path'; import { fileURLToPath } from 'url'; @@ -14,9 +15,12 @@ const compat = new FlatCompat({ baseDirectory: __dirname }); +// Wrap react plugin for ESLint 10 compatibility (context.getFilename etc.) +const reactCompat = fixupPluginRules(react); + const config = [ ...gdConfig, - react.configs.flat.recommended, + ...fixupConfigRules([react.configs.flat.recommended]), // This is needed due to react-hooks not being Flat Config compatible there is an open // issue for this https://github.com/facebook/react/issues/28313 and PR for this change // https://github.com/facebook/react/pull/30774 @@ -33,7 +37,7 @@ const config = [ } }, plugins: { - react, + react: reactCompat, jsxA11y }, rules: { diff --git a/packages/eslint-config-godaddy-react/package.json b/packages/eslint-config-godaddy-react/package.json index 398ec279..7f5c3a51 100644 --- a/packages/eslint-config-godaddy-react/package.json +++ b/packages/eslint-config-godaddy-react/package.json @@ -1,6 +1,6 @@ { "name": "eslint-config-godaddy-react", - "version": "10.0.0", + "version": "11.0.0", "description": "ESLint config for consistent style in ES6 React projects at GoDaddy.", "license": "MIT", "type": "module", @@ -28,17 +28,18 @@ ], "peerDependencies": { "@babel/core": ">=7", - "eslint": "^9" + "eslint": "^10" }, "dependencies": { - "@babel/eslint-parser": "^7.27.0", + "@babel/eslint-parser": "^7.28.6", + "@eslint/compat": "^2.0.2", "eslint-config-godaddy": "workspace:^", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^5.2.0" }, "devDependencies": { - "eslint": "^9.24.0" + "eslint": "^10.0.2" }, "exports": { ".": "./index.js" diff --git a/packages/eslint-config-godaddy-typescript/CHANGELOG.md b/packages/eslint-config-godaddy-typescript/CHANGELOG.md index 2fce354d..fc255d57 100644 --- a/packages/eslint-config-godaddy-typescript/CHANGELOG.md +++ b/packages/eslint-config-godaddy-typescript/CHANGELOG.md @@ -1,5 +1,16 @@ # eslint-config-godaddy-typescript +## 6.0.0 + +### Major Changes + +- 0fabf67: Upgrade to ESLint 10 + +### Patch Changes + +- Updated dependencies [0fabf67] + - eslint-config-godaddy@9.0.0 + ## 5.0.0 ### Major Changes diff --git a/packages/eslint-config-godaddy-typescript/package.json b/packages/eslint-config-godaddy-typescript/package.json index c8ae3683..c6f8360b 100644 --- a/packages/eslint-config-godaddy-typescript/package.json +++ b/packages/eslint-config-godaddy-typescript/package.json @@ -1,6 +1,6 @@ { "name": "eslint-config-godaddy-typescript", - "version": "5.0.0", + "version": "6.0.0", "description": "ESLint config for consistent style in node projects using Typescript at GoDaddy.", "type": "module", "main": "index.js", @@ -26,16 +26,16 @@ ], "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "^8.35.0", - "@typescript-eslint/parser": "^8.30.1", + "@typescript-eslint/eslint-plugin": "^8.54.0", + "@typescript-eslint/parser": "^8.54.0", "eslint-config-godaddy": "workspace:^" }, "peerDependencies": { - "eslint": "^9", + "eslint": "^10", "typescript": ">=5" }, "devDependencies": { - "eslint": "^9.24.0" + "eslint": "^10.0.2" }, "exports": { ".": "./index.js" diff --git a/packages/eslint-config-godaddy/CHANGELOG.md b/packages/eslint-config-godaddy/CHANGELOG.md index 954fcc6f..0ac17997 100644 --- a/packages/eslint-config-godaddy/CHANGELOG.md +++ b/packages/eslint-config-godaddy/CHANGELOG.md @@ -1,5 +1,11 @@ # eslint-config-godaddy +## 9.0.0 + +### Major Changes + +- 0fabf67: Upgrade to ESLint 10 + ## 8.0.2 ### Patch Changes diff --git a/packages/eslint-config-godaddy/package.json b/packages/eslint-config-godaddy/package.json index 1dc8e185..1946a34d 100644 --- a/packages/eslint-config-godaddy/package.json +++ b/packages/eslint-config-godaddy/package.json @@ -1,6 +1,6 @@ { "name": "eslint-config-godaddy", - "version": "8.0.2", + "version": "9.0.0", "description": "Base ESLint config for consistent style in ES6 projects at GoDaddy.", "license": "MIT", "type": "module", @@ -24,17 +24,17 @@ "eslint" ], "peerDependencies": { - "eslint": "^9" + "eslint": "^10" }, "dependencies": { + "@eslint/js": "^10.0.0", "eslint-plugin-jsdoc": "^50.6.9", "eslint-plugin-json": "^4.0.1", "eslint-plugin-mocha": "^10.5.0", "globals": "^16.0.0" }, "devDependencies": { - "@eslint/js": "^9.24.0", - "eslint": "^9.24.0" + "eslint": "^10.0.0" }, "exports": { ".": "./index.js" diff --git a/packages/localizations/CHANGELOG.md b/packages/localizations/CHANGELOG.md index af12ced3..140fa10e 100644 --- a/packages/localizations/CHANGELOG.md +++ b/packages/localizations/CHANGELOG.md @@ -1,5 +1,11 @@ # @godaddy/localizations +## 1.0.9 + +### Patch Changes + +- 353dc8b: Add support for enAu localizations + ## 1.0.8 ### Patch Changes diff --git a/packages/localizations/package.json b/packages/localizations/package.json index 5fcd2b89..589ed9db 100644 --- a/packages/localizations/package.json +++ b/packages/localizations/package.json @@ -1,9 +1,13 @@ { "name": "@godaddy/localizations", - "version": "1.0.8", + "version": "1.0.9", "description": "Internationalization localizations for GoDaddy checkout components", "type": "module", "types": "./dist/index.d.ts", + "repository": { + "type": "git", + "url": "git+https://github.com/godaddy/javascript.git" + }, "files": [ "dist" ], diff --git a/packages/localizations/src/enAu.ts b/packages/localizations/src/enAu.ts new file mode 100644 index 00000000..d7610972 --- /dev/null +++ b/packages/localizations/src/enAu.ts @@ -0,0 +1,377 @@ +export const enAu = { + general: { + optional: 'optional', + notes: 'Notes', + quantity: 'Quantity', + free: 'FREE', + closed: 'Closed', + apply: 'Apply', + checkoutDisabled: + 'Checkout is currently disabled. We apologise for the inconvenience.', + godaddyCheckout: 'GoDaddy Checkout', + poweredBy: 'Powered by', + }, + contact: { + title: 'Contact', + description: '', + email: 'Email', + }, + pickup: { + title: 'Local Pickup', + description: + 'All efforts will be made to meet your expected pickup time. Actual pickup times may vary.', + location: 'Pickup Location', + date: 'Pickup Date', + time: 'Preferred Pickup Time', + selectStore: 'Select a store location', + selectDate: 'Select a date', + selectTime: 'Select a pickup time', + storeHours: 'Store Hours', + seeDetails: 'See details', + noTimeSlots: + 'No available time slots for the selected date. Please select another date.', + hoursDisplayed: 'Hours displayed in your local timezone.', + storeOperatesIn: 'Store operates in {timezone} timezone.', + asap: 'ASAP', + hour: 'hour', + hours: 'hours', + minutes: 'minutes', + searchCountry: 'Search country...', + noCountryFound: 'No country found.', + }, + days: { + sunday: 'Sunday', + monday: 'Monday', + tuesday: 'Tuesday', + wednesday: 'Wednesday', + thursday: 'Thursday', + friday: 'Friday', + saturday: 'Saturday', + }, + delivery: { + title: 'Delivery', + method: 'Delivery method', + shipping: 'Shipping', + shipToAddress: 'Ship to your address', + localPickup: 'Local Pickup', + pickupFromStore: 'Pick up from store location', + }, + tips: { + title: 'Add a tip', + noTip: 'No Tip', + customAmount: 'Custom Amount', + customTipAmount: 'Custom Tip Amount', + placeholder: '$0.00', + }, + shipping: { + title: 'Shipping', + description: 'Shipping address', + method: 'Shipping method', + noShippingMethodAddress: + 'Enter your address to see available shipping methods.', + noShippingMethods: 'No shipping methods found.', + phone: 'Phone Number', + country: 'Country', + selectCountry: 'Select country', + searchCountry: 'Search country...', + noCountryFound: 'No country found', + firstName: 'First Name', + lastName: 'Last Name', + address1: 'Address', + address2: 'Apartment, suite, etc. (optional)', + city: 'City', + region: 'State/Territory', + postalCode: 'Postcode', + notesPlaceholder: 'Notes or special instructions', + addressSuggestion: 'Did you mean', + addressSuggestionEnd: '?', + noShippingOriginAddress: 'No shipping origin address provided.', + }, + billing: { + title: 'Billing', + description: 'Billing description', + }, + payment: { + title: 'Payment', + description: 'All payments are secure and encrypted.', + billingAddress: { + title: 'Billing Address', + description: 'Enter your billing address.', + useShippingAddress: 'Use shipping address as billing address', + }, + methods: { + creditCard: 'Credit or Debit Card', + paypal: 'PayPal', + applePay: 'Apple Pay', + googlePay: 'Google Pay', + paze: 'Paze', + offline: 'Offline payments', + mercadopago: 'Mercado Pago', + ccavenue: 'Pay with CCAvenue', + }, + descriptions: { + creditCard: '', + paypal: '', + applePay: '', + googlePay: '', + paze: '', + offline: '', + mercadopago: + 'Use the MercadoPago form below to complete your purchase securely.', + ccavenue: '', + }, + noMethodsAvailable: 'No payment methods available', + cardNumber: 'Card number', + cardNumberPlaceholder: '1234 1234 1234 1234', + expirationDate: 'Expiration date (MM / YY)', + securityCode: 'Security code', + cvvPlaceholder: 'CVV', + nameOnCard: 'Name on card', + payNow: 'Pay now', + standardShipping: 'Standard Shipping', + cardDetails: 'Card details', + payWithApplePay: 'Pay with Apple Pay', + expirationDatePlaceholder: 'MM / YY', + failedToInitializePayment: 'Failed to initialise payment.', + orderTotal: 'Order Total', + subtotal: 'Subtotal', + tax: 'GST', + shipping: 'Shipping', + discount: 'Coupon', + processingPayment: 'Processing payment...', + completingOrder: 'Completing order...', + completeOrder: 'Complete your order', + freePayment: 'Complete your free order', + }, + phone: { + placeholder: '+61 2 1234 5678', + searchCountry: 'Search country...', + noCountryFound: 'No country found.', + }, + discounts: { + placeholder: 'Coupon code', + enterCode: 'Enter coupon code', + apply: 'Apply', + alreadyApplied: 'This coupon code has already been applied', + failedToApply: 'Failed to apply coupon code', + enterCodeValidation: 'Please enter a coupon code', + }, + totals: { + subtotal: 'Subtotal', + discount: 'Discount', + shipping: 'Shipping', + tip: 'Tip', + estimatedTaxes: 'Estimated GST', + totalDue: 'Total Due', + orderSummary: 'Order Summary', + itemCount: 'items', + noItems: 'No items', + }, + lineItems: { + note: 'Note:', + }, + ui: { + autocomplete: { + addressPlaceholder: 'Address', + suggestions: 'Suggestions', + }, + form: { + useFormFieldError: 'useFormField should be used within ', + }, + pagination: { + ariaLabel: 'pagination', + previousPageAriaLabel: 'Go to previous page', + previous: 'Previous', + nextPageAriaLabel: 'Go to next page', + next: 'Next', + morePages: 'More pages', + }, + sidebar: { + useSidebarError: 'useSidebar must be used within a SidebarProvider.', + toggleSidebar: 'Toggle Sidebar', + }, + accessibility: { + payWithApplePay: 'Pay with Apple Pay', + applePayLogo: 'Apple Pay Logo', + close: 'Close', + additionalTipOptions: 'Additional tip options', + breadcrumb: 'breadcrumb', + more: 'More', + previousSlide: 'Previous slide', + nextSlide: 'Next slide', + }, + }, + errors: { + failedToGetAccessToken: 'Failed to get access token', + invalidEnvironment: 'Invalid environment', + noPublicAccessToken: 'No public access token provided', + noSessionToken: 'No session token or ID provided', + errorCreatingSession: 'Error creating checkout session:', + orderIdRequired: 'Order ID is required', + errorTrackingEvent: 'Error tracking event', + errorInTrackingSubscriber: 'Error in tracking subscriber', + useTrackingProviderError: + 'useTracking must be used within a TrackingProvider', + usePoyntCollectProviderError: + 'usePoyntCollect must be used within a PoyntCollectProvider', + useSquareProviderError: 'useSquare must be used within a SquareProvider', + errorProcessingPayment: 'Error processing payment', + stripeConfigMissing: + 'Stripe configuration is missing. Please check your setup.', + paymentSuccessful: 'Payment successful', + paymentFailed: 'Payment failed', + paypalConfigMissing: + 'PayPal configuration is missing. Please check your setup.', + stripeNotReady: 'Stripe not ready', + cardElementNotFound: 'CardElement not found', + errorProcessingCardPayment: 'Error processing card payment.', + errorProcessingExpressPayment: 'Error processing express payment.', + failedToInitializePayment: 'Failed to initialise payment.', + invalidPhoneNumber: 'invalid phone number', + }, + validation: { + phoneRequired: 'Phone number is required', + emailRequired: 'Enter an email', + emailInvalid: 'Enter a valid email', + phoneTooLong: 'Phone number too long', + invalidShippingAddress: 'Invalid shipping address', + invalidBillingAddress: 'Invalid billing address', + selectPaymentMethod: 'Select a payment method', + enterValidBillingPhone: 'Enter a valid billing phone number', + enterValidShippingPhone: 'Enter a valid shipping phone number', + enterFirstName: 'Enter a first name', + enterLastName: 'Enter a last name', + enterAddress: 'Enter an address', + enterCity: 'Enter a city', + enterZipCode: 'Enter a postcode', + enterZipPostalCode: 'Enter a postcode', + selectState: 'Select a state/territory', + selectCountry: 'Select a country', + enterCountry: 'Enter a country', + invalidCardNumber: 'Invalid card number', + invalidExpiry: 'Invalid expiration date', + invalidCvv: 'Invalid security code', + paymentSubmissionFailed: 'Payment submission failed. Please try again.', + }, + apiErrors: { + INTERNAL_SERVER_ERROR: 'Server error', + SESSION_NOT_FOUND: 'Session not found', + CHECKOUT_SESSION_RETRIEVAL_FAILED: 'Failed to retrieve checkout session', + CHECKOUT_SESSION_NOT_FOUND: 'Checkout session not found', + CHECKOUT_SESSION_FETCH_ERROR: 'General checkout session fetch error', + DRAFT_ORDER_NOT_FOUND: 'Order not found', + CHECKOUT_SESSION_CREATION_FAILED: 'Failed to create checkout session', + CREATED_SESSION_DATA_NOT_FOUND: 'Created session data not found', + CHECKOUT_SESSION_CREATION_ERROR: 'General checkout session creation error', + CHECKOUT_SESSION_UPDATE_FAILED: 'Failed to update checkout session', + UPDATED_SESSION_NOT_FOUND: 'Updated session not found', + CHECKOUT_SESSION_UPDATE_ERROR: 'General checkout session update error', + ORDER_API_NOT_FOUND: 'Order API data source not found', + DRAFT_ORDER_RETRIEVAL_FAILED: 'Failed to retrieve order', + DATA_SOURCES_NOT_FOUND: 'Data sources not found', + TRANSACTION_API_NOT_FOUND: 'Transaction API data source not found', + TRANSACTION_PROCESSING_FAILED: 'Failed to process transaction', + TRANSACTION_CREATION_FAILED: 'Failed to find or create transaction', + ORDER_OPENING_FAILED: 'Failed to open order', + INVALID_FULFILLMENT_LOCATION: 'Invalid fulfillment location', + NO_VALID_PICKUP_LOCATION: 'No valid pickup location available', + DRAFT_ORDER_FETCH_FAILED: 'Failed to fetch order', + LINE_ITEMS_FETCH_FAILED: 'Failed to fetch line items', + CURRENT_ORDER_FETCH_FAILED: 'Failed to fetch current order', + DRAFT_ORDER_UPDATE_FAILED: 'Failed to update order', + DISCOUNT_APPLICATION_FAILED: 'Failed to apply coupon', + CONTEXT_UNAVAILABLE: 'Session or data sources not available', + SHIPPING_LINES_UPDATE_FAILED: 'Failed to update shipping lines', + ORDER_TOTALS_UPDATE_FAILED: 'Failed to update order totals', + ORDER_TOTAL_CALCULATION_FAILED: 'Failed to calculate order totals', + SHIPPING_METHOD_APPLICATION_FAILED: 'Failed to apply shipping method', + SKU_FETCH_FAILED: 'Failed to fetch SKUs', + SHIPPING_RATES_CALCULATION_FAILED: 'Failed to calculate shipping rates', + SHIPPING_METHOD_NOT_FOUND: 'Shipping method not found', + SHIPPING_METHOD_REMOVAL_FAILED: 'Failed to remove shipping method', + SHIPPING_ADDRESS_REQUIRED: 'Shipping address required', + INVENTORY_VALIDATION_ERROR: 'Inventory validation error', + INSUFFICIENT_INVENTORY: 'Insufficient inventory', + INVALID_ORDER_STATUS: 'Invalid order status', + TRANSACTION_CREATION_ERROR: 'Transaction creation error', + TRANSACTION_PROCESSING_ERROR: 'Transaction processing error', + UNKNOWN_ERROR: 'Unknown error', + FULFILLMENT_CREATION_ERROR: 'Fulfillment creation error', + TAX_CALCULATION_ERROR: 'GST calculation error', + ADDRESS_VERIFICATION_ERROR: 'Address verification error', + ADDRESS_MATCHES_ERROR: 'Address matches error', + SHIPPING_CONFLICT_ERROR: 'Shipping conflict error', + SHIPPING_ERROR: 'General shipping error', + PRICE_ADJUSTMENT_CALCULATION_ERROR: 'Price adjustment calculation error', + ORDER_FETCH_ERROR: 'Order fetch error', + DRAFT_ORDER_UPDATE_ERROR: 'Order update error', + DISCOUNT_APPLICATION_ERROR: 'Coupon application error', + DRAFT_ORDER_OPEN_ERROR: 'Order open error', + ORDER_UPDATE_ERROR: 'Order update error', + CATALOG_FETCH_ERROR: 'Catalogue fetch error', + STORE_ID_NOT_FOUND: 'Store ID not found', + ORDER_NOT_DRAFT: 'Order is not in valid status', + NO_LINE_ITEMS: 'No line items found', + BAD_USER_INPUT: 'Invalid user input', + MISSING_SHIPPING_ADDRESS: 'Shipping address is required', + MISSING_REQUIRED_FIELD: 'Required field is missing', + CATALOG_API_ERROR: 'Catalogue service error', + TAX_SERVICE_UNAVAILABLE: 'GST service is unavailable', + SHIPPING_SERVICE_UNAVAILABLE: 'Shipping service is unavailable', + PRICE_ADJUSTMENTS_SERVICE_UNAVAILABLE: + 'Price adjustments service is unavailable', + PRICE_ADJUSTMENTS_CALCULATION_FAILED: + 'Failed to calculate price adjustments', + SERVICE_UNAVAILABLE: 'Service is temporarily unavailable', + RATE_LIMITED: 'Too many requests, please try again later', + TIMEOUT: 'Request timed out', + INTERNAL_ERROR: 'Internal server error', + SHIPPING_POSTAL_CODE_REQUIRED: 'Shipping postcode is required', + SHIPPING_COUNTRY_CODE_REQUIRED: 'Shipping country code is required', + SHIPPING_METHOD_REQUIRED: 'Shipping method is required', + SHIPPING_ADDRESS_VERIFICATION_FAILED: + 'Shipping address verification failed', + BILLING_ADDRESS_VERIFICATION_FAILED: 'Billing address verification failed', + MISSING_SHIPPING_INFO: 'Shipping address or method failed to apply', + DEPENDENCY_ERROR: + "We're unable to process your order right now. Please wait a moment and try again", + }, + storefront: { + product: 'Product', + sale: 'SALE', + noImage: 'No image', + noImageAvailable: 'No image available', + selectOptions: 'Select Options', + adding: 'Adding...', + addToCart: 'Add to Cart', + shoppingCart: 'Shopping Cart', + failedToLoadCart: 'Failed to load cart:', + retry: 'Retry', + yourCartIsEmpty: 'Your cart is empty', + addItemsToGetStarted: 'Add items to get started', + errorLoadingProducts: 'Error loading products:', + errorLoadingProduct: 'Error loading product:', + productNotFound: 'Product not found', + loadingVariantDetails: 'Loading variant details...', + combinationNotAvailable: + 'This combination is not available. Please select different options.', + variantsMatch: + 'variants match your selection. Select more attributes to narrow down.', + quantity: 'Quantity', + addingToCart: 'Adding to Cart...', + outOfStock: 'Out of Stock', + viewDetails: 'View Details', + productType: 'Product Type:', + productId: 'Product ID:', + selectedSku: 'Selected SKU:', + stockStatus: 'Stock Status:', + lowStock: 'Low Stock', + inStock: 'In Stock', + remove: 'Remove', + removing: 'Removing...', + checkout: 'Checkout', + itemsPerPage: 'Items per page:', + search: 'Search', + searchPlaceholder: 'Search products...', + }, +}; diff --git a/packages/localizations/src/index.ts b/packages/localizations/src/index.ts index 99de1801..c240e36b 100644 --- a/packages/localizations/src/index.ts +++ b/packages/localizations/src/index.ts @@ -1,4 +1,5 @@ export { deDe } from './deDe'; +export { enAu } from './enAu'; export { enIe } from './enIe'; export { enUs } from './enUs'; export { esAr } from './esAr'; diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md index e44aab18..328609d8 100644 --- a/packages/react/CHANGELOG.md +++ b/packages/react/CHANGELOG.md @@ -1,5 +1,43 @@ # @godaddy/react +## 1.0.35 + +### Patch Changes + +- 8d3f2c2: Update commerce api skill to utilize godaddy cli as api source + +## 1.0.34 + +### Patch Changes + +- 57d6115: Add commerce api skill + +## 1.0.33 + +### Patch Changes + +- 353dc8b: Add support for enAu localizations +- Updated dependencies [353dc8b] + - @godaddy/localizations@1.0.9 + +## 1.0.32 + +### Patch Changes + +- ed3cbef: Fix bug with large leadTimes and add pickupSlotInterval to uncouple leadTime from time slot generation + +## 1.0.31 + +### Patch Changes + +- b3c0dad: Fix MercadoPago amount conversion from minor units to major units. The SDK expects amounts in major units (e.g., 90.00 BRL) but we were sending minor units (e.g., 9000 cents). + +## 1.0.30 + +### Patch Changes + +- c1ebb52: Format local pickup times to store timezone + ## 1.0.29 ### Patch Changes diff --git a/packages/react/README.md b/packages/react/README.md index bba2aa04..2047b067 100644 --- a/packages/react/README.md +++ b/packages/react/README.md @@ -18,34 +18,41 @@ The first parameter accepts all checkout session configuration options from the #### Required Parameters -- **`channelId`** (string): The ID of the sales channel that originated this session -- **`storeId`** (string): The ID of the store this checkout session belongs to -- **`draftOrderId`** (string): The ID of the draft order +- **`storeId`** (string): The ID of the store this checkout session belongs to - **`returnUrl`** (string): URL to redirect to when user cancels checkout - **`successUrl`** (string): URL to redirect to after successful checkout +- **`draftOrderId`** (string): ID of an existing draft order (required if `lineItems` not provided) +- **`lineItems`** ([CheckoutSessionLineItemInput!]): Line items to create a draft order from (required if `draftOrderId` not provided) #### Optional Parameters +- **`appearance`** (GoDaddyAppearanceInput): Appearance configuration for the checkout (see [Appearance](#appearance)) +- **`channelId`** (string): The ID of the sales channel that originated this session - **`customerId`** (string): Customer ID for the checkout session -- **`storeName`** (string): The name of the store this checkout session belongs to -- **`url`** (string): Custom URL for the checkout session -- **`environment`** (enum): Environment - `ote`, `prod` -- **`expiresAt`** (DateTime): When the session expires +- **`enableAddressAutocomplete`** (boolean): Enable address autocomplete - **`enableBillingAddressCollection`** (boolean): Enable billing address collection - **`enableLocalPickup`** (boolean): Enable local pickup option - **`enableNotesCollection`** (boolean): Enable order notes collection - **`enablePaymentMethodCollection`** (boolean): Enable payment method collection - **`enablePhoneCollection`** (boolean): Enable phone number collection - **`enablePromotionCodes`** (boolean): Enable promotion/discount codes +- **`enableShipping`** (boolean): Enable shipping - **`enableShippingAddressCollection`** (boolean): Enable shipping address collection - **`enableSurcharge`** (boolean): Enable surcharge fees - **`enableTaxCollection`** (boolean): Enable tax collection - **`enableTips`** (boolean): Enable tip/gratuity options - **`enabledLocales`** ([String!]): List of enabled locales - **`enabledPaymentProviders`** ([String!]): List of enabled payment providers +- **`environment`** (enum): Environment - `ote`, `prod` +- **`expiresAt`** (DateTime): When the session expires - **`locations`** ([CheckoutSessionLocationInput!]): Available pickup locations -- **`operatingHours`** (CheckoutSessionOperatingHoursMapInput): Store operating hours configuration +- **`operatingHours`** (CheckoutSessionOperatingHoursMapInput): Store operating hours configuration (see [Operating Hours](#operating-hours)) - **`paymentMethods`** (CheckoutSessionPaymentMethodsInput): Payment method configurations +- **`shipping`** (CheckoutSessionShippingOptionsInput): Shipping configuration โ€” primarily used to set an `originAddress` for shipping rate calculations and an optional `fulfillmentLocationId` +- **`sourceApp`** (string): The source application that created this checkout session +- **`storeName`** (string): The name of the store this checkout session belongs to +- **`taxes`** (CheckoutSessionTaxesOptionsInput): Tax configuration โ€” used to set an `originAddress` for tax calculations (e.g. the store or warehouse address that taxes are calculated from) +- **`url`** (string): Custom URL for the checkout session ### Checkout Session Options @@ -76,12 +83,134 @@ The checkout session supports multiple environments through the input parameter: ### API Scopes -The checkout session automatically requests the following OAuth2 scopes: +The checkout session automatically requests the following OAuth2 scope: - `commerce.product:read` -- `commerce.order:read` -- `commerce.order:update` -- `location.address-verification:execute` + +### Operating Hours + +The `operatingHours` field configures local pickup scheduling โ€” time zones, lead times, pickup windows, and slot intervals. + +```typescript +operatingHours: { + default: { + timeZone: 'America/New_York', + leadTime: 60, + pickupWindowInDays: 7, + pickupSlotInterval: 30, + hours: { + monday: { enabled: true, openTime: '09:00', closeTime: '17:00' }, + tuesday: { enabled: true, openTime: '09:00', closeTime: '17:00' }, + wednesday: { enabled: true, openTime: '09:00', closeTime: '17:00' }, + thursday: { enabled: true, openTime: '09:00', closeTime: '17:00' }, + friday: { enabled: true, openTime: '09:00', closeTime: '18:00' }, + saturday: { enabled: true, openTime: '10:00', closeTime: '16:00' }, + sunday: { enabled: false, openTime: null, closeTime: null }, + }, + }, +} +``` + +#### Store Hours Fields + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `timeZone` | string | Yes | IANA timezone for the store (e.g. `America/New_York`). All slot times are displayed in this timezone. | +| `leadTime` | number | Yes | Minimum advance notice in minutes before a pickup can be scheduled. Controls the earliest available slot (now + leadTime). | +| `pickupWindowInDays` | number | Yes | Number of days ahead customers can schedule pickup. Set to `0` for ASAP-only mode (no date/time picker). | +| `pickupSlotInterval` | number | No | Minutes between selectable time slots (e.g. `30` โ†’ 10:00, 10:30, 11:00โ€ฆ). Defaults to 30 if omitted. Separate from `leadTime` โ€” the interval controls slot spacing, while leadTime controls advance notice. | +| `hours` | object | Yes | Per-day operating hours. Each day has `enabled` (boolean), `openTime` (HH:mm or null), and `closeTime` (HH:mm or null). | + +#### Behavior Notes + +- **ASAP option** โ€” Shown for today only, when the store can fulfill an order (now + leadTime) before closing time. +- **Lead time vs slot interval** โ€” A store with `leadTime: 1440` (24 hours) and `pickupSlotInterval: 15` shows 15-minute slots starting tomorrow, not 24-hour gaps. +- **Timezone handling** โ€” All date/time logic uses the store's `timeZone`, not the customer's browser timezone. A store in Phoenix shows Phoenix hours regardless of where the customer is browsing from. +- **No available slots** โ€” When leadTime exceeds the entire pickup window, or no days are enabled, a "No available time slots" banner is shown. + +### Appearance + +The `appearance` field customizes the checkout's look and feel. + +```typescript +appearance: { + theme: 'base', + variables: { + primary: '#4f46e5', + background: '#ffffff', + foreground: '#111827', + radius: '0.5rem', + }, +} +``` + +#### Theme + +| Value | Description | +|-------|-------------| +| `base` | Default theme | +| `orange` | Orange accent theme | +| `purple` | Purple accent theme | + +#### CSS Variables + +All fields are optional strings. Pass any subset to override the defaults. + +| Variable | Description | +|----------|-------------| +| `accent` | Accent color | +| `accentForeground` | Text on accent backgrounds | +| `background` | Page background | +| `border` | Border color | +| `card` | Card background | +| `cardForeground` | Text on cards | +| `defaultFontFamily` | Default font family | +| `destructive` | Destructive action color (errors, delete) | +| `destructiveForeground` | Text on destructive backgrounds | +| `fontMono` | Monospace font family | +| `fontSans` | Sans-serif font family | +| `fontSerif` | Serif font family | +| `foreground` | Primary text color | +| `input` | Input field background | +| `muted` | Muted/subtle background | +| `mutedForeground` | Text on muted backgrounds | +| `popover` | Popover background | +| `popoverForeground` | Text in popovers | +| `primary` | Primary brand color | +| `primaryForeground` | Text on primary backgrounds | +| `radius` | Border radius (e.g. `0.5rem`) | +| `ring` | Focus ring color | +| `secondary` | Secondary color | +| `secondaryBackground` | Secondary background | +| `secondaryForeground` | Text on secondary backgrounds | + +## AI Agent Skills + +This package ships a [TanStack Intent](https://tanstack.com/intent/latest) skill that teaches AI coding agents how to authenticate with the GoDaddy Commerce Platform using OAuth2 client credentials and create checkout sessions. For API discovery and testing, the skill directs agents to use [`@godaddy/cli`](https://www.npmjs.com/package/@godaddy/cli). + +### Loading the skill + +Tell your agent: + +``` +Read node_modules/@godaddy/react/skills/commerce-api/SKILL.md and use it to authenticate with the GoDaddy Commerce APIs. +``` + +### Automatic discovery + +From your project directory, run: + +``` +npx @tanstack/intent@latest list +``` + +This will show the `commerce-api` skill and its path. To set up persistent skill-to-task mappings in your `AGENTS.md`, run: + +``` +npx @tanstack/intent@latest install +``` + +Then ask your agent to follow the instructions it outputs. ## Codegen diff --git a/packages/react/package.json b/packages/react/package.json index d65e91e2..8c601f78 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,11 +1,12 @@ { "name": "@godaddy/react", "private": false, - "version": "1.0.29", + "version": "1.0.35", "type": "module", "types": "./dist/index.d.ts", "files": [ - "dist" + "dist", + "skills" ], "exports": { "./package.json": "./package.json", @@ -73,8 +74,8 @@ "@radix-ui/react-toggle": "^1.1.2", "@radix-ui/react-toggle-group": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.8", - "@stripe/react-stripe-js": "^3.7.0", - "@stripe/stripe-js": "^7.3.1", + "@stripe/react-stripe-js": "^5.6.1", + "@stripe/stripe-js": "^8.11.0", "@tailwindcss/cli": "^4.1.10", "@tailwindcss/vite": "^4.1.4", "@tanstack/react-pacer": "^0.2.0", @@ -104,7 +105,7 @@ "@types/node": "^22.13.1", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", - "@vitejs/plugin-react": "^4.2.1", + "@vitejs/plugin-react": "^4.7.0", "biome-config-godaddy": "workspace:*", "globals": "^15.14.0", "jsdom": "^26.0.0", @@ -113,10 +114,17 @@ "react-hook-form": "^7.54.2", "tsdown": "^0.15.6", "typescript": "~5.7.3", - "vite": "^5.1.6", - "vitest": "^3.0.6" + "vite": "^6.4.1", + "vitest": "^4.1.2" }, "publishConfig": { "registry": "https://registry.npmjs.org/" - } + }, + "repository": { + "type": "git", + "url": "git+https://github.com/godaddy/javascript.git" + }, + "keywords": [ + "tanstack-intent" + ] } diff --git a/packages/react/skills/commerce-api/SKILL.md b/packages/react/skills/commerce-api/SKILL.md new file mode 100644 index 00000000..c6e5e524 --- /dev/null +++ b/packages/react/skills/commerce-api/SKILL.md @@ -0,0 +1,299 @@ +--- +name: commerce-api +description: > + Authenticate with the GoDaddy Commerce Platform using OAuth2 client + credentials or JWT grants. Covers the /v2/oauth2/token endpoint, + environments (ote, prod), required headers, and scopes. For API + discovery, schema introspection, and testing use @godaddy/cli + (godaddy api list, godaddy api describe, godaddy api call). For + checkout session creation use the Checkout API. Activate when an + agent needs to obtain an OAuth token, configure commerce API auth, + create checkout sessions, or discover available commerce endpoints. +type: core +library: "@godaddy/react" +sources: + - "godaddy/javascript:packages/react/skills/commerce-api/SKILL.md" +--- + +# GoDaddy Commerce API โ€” Authentication & Discovery + +## Setup + +Connecting to the GoDaddy Commerce Platform requires an OAuth client ID, +client secret, and a store ID (UUID). + +**Environments:** + +| Environment | API Host | Token Endpoint | +|-------------|-----------------------|-----------------------------------------------| +| ote | `api.ote-godaddy.com` | `https://api.ote-godaddy.com/v2/oauth2/token` | +| prod | `api.godaddy.com` | `https://api.godaddy.com/v2/oauth2/token` | + +**Obtain an OAuth token** using the client credentials grant with form +parameters: + +```typescript +async function getAccessToken(env: 'ote' | 'prod' = 'ote'): Promise { + const host = env === 'prod' ? 'api.godaddy.com' : 'api.ote-godaddy.com'; + const clientId = process.env.OAUTH_CLIENT_ID!; + const clientSecret = process.env.OAUTH_CLIENT_SECRET!; + + const response = await fetch(`https://${host}/v2/oauth2/token`, { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: new URLSearchParams({ + client_id: clientId, + client_secret: clientSecret, + grant_type: 'client_credentials', + scope: 'commerce.product:read commerce.product:write commerce.order:read', + }), + }); + + if (!response.ok) { + const text = await response.text(); + throw new Error(`OAuth token request failed: ${response.status} โ€” ${text}`); + } + + const data = await response.json(); + return data.access_token; // also: data.expires_in (seconds) +} +``` + +Tokens are short-lived (~1 hour). Cache and refresh before expiry. + +**Required headers** for all API requests: + +```typescript +const headers = { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json', + 'x-store-id': storeId, + 'user-agent': 'your-app/1.0.0 (GoDaddy Commerce Platform)', +}; +``` + +**OAuth scopes** โ€” request only the scopes your application needs. If a +scope is not provisioned for your OAuth app, the token request returns +`invalid_scope`. + +| Scope | Purpose | +|--------------------------|----------------------------------| +| `commerce.product:read` | Read catalog/SKU data | +| `commerce.product:write` | Create and update catalog data | +| `commerce.order:read` | Read order data | + +## Core Patterns + +### Discover APIs with @godaddy/cli + +Use the `@godaddy/cli` package to discover available endpoints, inspect +schemas, and test API calls. Install globally: + +```bash +npm install -g @godaddy/cli +``` + +Discover available API domains and endpoints: + +```bash +# List all API domains +godaddy api list + +# List endpoints in a specific domain +godaddy api list --domain catalog-products +godaddy api list --domain orders + +# Search for endpoints by keyword +godaddy api search checkout +godaddy api search tax + +# Describe an endpoint's schema, parameters, and scopes +godaddy api describe /location/addresses + +# Make an authenticated API call +godaddy api call /v1/commerce/catalog/products +godaddy api call /v1/commerce/orders -s commerce.order:read +``` + +All CLI commands return structured JSON with `next_actions` that suggest +what to run next. Use `godaddy api describe` to inspect request/response +schemas and required scopes before implementing API calls in code. + +### Create and Update Checkout Sessions + +The Checkout API uses a dedicated host: `checkout.commerce.api.{host}` + +```typescript +import { GraphQLClient } from 'graphql-request'; + +const checkoutClient = new GraphQLClient( + `https://checkout.commerce.api.ote-godaddy.com/`, + { + headers: { + Authorization: `Bearer ${token}`, + 'x-store-id': storeId, + 'user-agent': 'my-app/1.0.0 (GoDaddy Commerce Platform)', + }, + } +); + +const session = await checkoutClient.request(` + mutation CreateCheckoutSession($input: MutationCreateCheckoutSessionInput!) { + createCheckoutSession(input: $input) { + id url status expiresAt + draftOrder { + id number + totals { total { value currencyCode } } + lineItems { edges { node { id name quantity unitPrice { value currencyCode } } } } + } + } + } +`, { + input: { + storeId, + returnUrl: 'https://example.com/cart', + successUrl: 'https://example.com/thank-you', + lineItems: [ + { skuId: 'sku-123', quantity: 1 }, + ], + }, +}); + +const updated = await checkoutClient.request(` + mutation UpdateCheckoutSession($id: String!, $input: MutationUpdateCheckoutSessionInput!) { + updateCheckoutSession(id: $id, input: $input) { + id status + } + } +`, { + id: session.createCheckoutSession.id, + input: { + enablePromotionCodes: true, + enableShipping: true, + enableTaxCollection: true, + }, +}); +``` + +### Token Caching + +```typescript +let cached = { token: '', expiresAt: 0 }; + +async function getValidToken(env: 'ote' | 'prod' = 'ote'): Promise { + if (Date.now() < cached.expiresAt - 60_000) return cached.token; + const host = env === 'prod' ? 'api.godaddy.com' : 'api.ote-godaddy.com'; + + const response = await fetch(`https://${host}/v2/oauth2/token`, { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: new URLSearchParams({ + client_id: process.env.OAUTH_CLIENT_ID!, + client_secret: process.env.OAUTH_CLIENT_SECRET!, + grant_type: 'client_credentials', + scope: 'commerce.product:read commerce.order:read', + }), + }); + + const data = await response.json(); + cached = { + token: data.access_token, + expiresAt: Date.now() + data.expires_in * 1000, + }; + return cached.token; +} +``` + +## Common Mistakes + +### CRITICAL Missing Bearer prefix in Authorization header + +Wrong: + +```typescript +headers: { 'Authorization': token } +``` + +Correct: + +```typescript +headers: { 'Authorization': `Bearer ${token}` } +``` + +The API returns 401 if the `Bearer ` prefix is missing. This is a silent +failure when error handling swallows the status code. + +### CRITICAL Using wrong token endpoint URL + +Wrong: + +```typescript +const tokenUrl = 'https://sso.godaddy.com/v1/token'; +``` + +Correct: + +```typescript +const tokenUrl = 'https://api.godaddy.com/v2/oauth2/token'; +``` + +The OAuth token endpoint is `/v2/oauth2/token` on the **API host** +(`api.godaddy.com` or `api.ote-godaddy.com`). Using any other host or +path returns 404 or 405. + +### HIGH Omitting scope in token request + +Wrong: + +```typescript +body: new URLSearchParams({ client_id: id, client_secret: secret, grant_type: 'client_credentials' }) +``` + +Correct: + +```typescript +body: new URLSearchParams({ + client_id: id, client_secret: secret, + grant_type: 'client_credentials', + scope: 'commerce.product:read commerce.order:read', +}) +``` + +Omitting `scope` may return a token without commerce permissions, causing +403 Forbidden on API calls despite having a valid token. Requesting a scope +not provisioned for your OAuth app returns an `invalid_scope` error. + +### HIGH Checkout API uses a different host than subgraph APIs + +Wrong: + +```typescript +const url = `https://api.ote-godaddy.com/checkout`; +``` + +Correct: + +```typescript +const url = `https://checkout.commerce.api.ote-godaddy.com/`; +``` + +The Checkout API uses a dedicated subdomain (`checkout.commerce.api.{host}`), +not a path on the standard API host. Using the wrong host returns 404. + +### MEDIUM Using expired token without refresh + +Wrong: + +```typescript +const token = await getAccessToken(); +// reuse for hours without checking expiry +``` + +Correct: + +```typescript +const token = await getValidToken(); // see Token Caching pattern above +``` + +Tokens expire in ~1 hour. Cache the token and refresh with a 1-minute +buffer before `expires_in` elapses. After expiry, requests fail with 401. diff --git a/packages/react/src/components/checkout/line-items/line-items.tsx b/packages/react/src/components/checkout/line-items/line-items.tsx index 196e9315..224fc2fc 100644 --- a/packages/react/src/components/checkout/line-items/line-items.tsx +++ b/packages/react/src/components/checkout/line-items/line-items.tsx @@ -52,6 +52,8 @@ export type Product = Partial & { addons?: SelectedAddon[]; selectedOptions?: SelectedOption[]; discounts?: ProductDiscount[]; + /** Selling plan (subscription) from PDP selection; shown in cart. */ + sellingPlan?: { name?: string; category?: string }; }; export interface DraftOrderLineItemsProps { @@ -109,6 +111,12 @@ export function DraftOrderLineItems({ ) : null} + {item.sellingPlan?.name ? ( + + {item.sellingPlan.name} + {item.sellingPlan.category ? ` ยท ${item.sellingPlan.category}` : ''} + + ) : null} {item?.addons?.map((addon: SelectedAddon, index: number) => ( diff --git a/packages/react/src/components/checkout/payment/checkout-buttons/express/godaddy.tsx b/packages/react/src/components/checkout/payment/checkout-buttons/express/godaddy.tsx index a5c823f1..1f0745b8 100644 --- a/packages/react/src/components/checkout/payment/checkout-buttons/express/godaddy.tsx +++ b/packages/react/src/components/checkout/payment/checkout-buttons/express/godaddy.tsx @@ -1,11 +1,4 @@ -import { - useCallback, - useEffect, - useLayoutEffect, - useMemo, - useRef, - useState, -} from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useFormContext } from 'react-hook-form'; import { useCheckoutContext } from '@/components/checkout/checkout'; import { useGetPriceAdjustments } from '@/components/checkout/discount/utils/use-get-price-adjustments'; @@ -16,7 +9,6 @@ import { import { useUpdateTaxes } from '@/components/checkout/order/use-update-taxes'; import type { Address, - ShippingMethod, ShippingMethods, TokenizeJs, WalletError, @@ -96,6 +88,9 @@ export function ExpressCheckoutButton() { const confirmCheckout = useConfirmCheckout(); const collect = useRef(null); const hasMounted = useRef(false); + const handleExpressPayClickRef = useRef< + (args: { source?: 'apple_pay' | 'google_pay' | 'paze' }) => Promise + >(async () => undefined); // Use refs to store current coupon state to avoid stale closures in event handlers const appliedCouponCodeRef = useRef(null); @@ -305,6 +300,9 @@ export function ExpressCheckoutButton() { ] ); + // Keep ref in sync so the SDK's stale onClick closure always calls the latest handler + handleExpressPayClickRef.current = handleExpressPayClick; + // Track the status of coupon code fetching with a state variable const [couponFetchStatus, setCouponFetchStatus] = useState< 'idle' | 'fetching' | 'done' @@ -401,20 +399,21 @@ export function ExpressCheckoutButton() { // Initialize the TokenizeJs instance when the component mounts // But only after price adjustments have been fetched - useLayoutEffect(() => { - const shouldInitialize = - godaddyPaymentsConfig && - (godaddyPaymentsConfig?.businessId || session?.businessId) && - isPoyntLoaded && - isCollectLoading && - draftOrder && - couponFetchStatus === 'done'; - - if (!shouldInitialize) return; - - if (!collect.current && !hasMounted.current) { + // Initialize TokenizeJs and mount wallet buttons in a single effect + useEffect(() => { + if ( + !isPoyntLoaded || + !godaddyPaymentsConfig || + !isCollectLoading || + !draftOrder || + hasMounted.current || + couponFetchStatus !== 'done' || + (!godaddyPaymentsConfig?.businessId && !session?.businessId) + ) + return; + + if (!collect.current) { // Create coupon config if there's a price adjustment from existing coupon - // Read from refs to get current values const currentAdjustments = calculatedAdjustmentsRef.current; const currentCouponCode = appliedCouponCodeRef.current; @@ -452,8 +451,58 @@ export function ExpressCheckoutButton() { } ); } + + if (collect.current) { + collect.current.supportWalletPayments().then(supports => { + const paymentMethods: string[] = []; + if (supports.applePay) { + track({ + eventId: eventIds.expressApplePayImpression, + type: TrackingEventType.IMPRESSION, + properties: { + provider: 'poynt', + }, + }); + paymentMethods.push('apple_pay'); + } + if (supports.googlePay) { + paymentMethods.push('google_pay'); + track({ + eventId: eventIds.expressGooglePayImpression, + type: TrackingEventType.IMPRESSION, + properties: { + provider: 'poynt', + }, + }); + } + + if (paymentMethods.length > 0 && !hasMounted.current) { + hasMounted.current = true; + collect.current?.mount('gdpay-express-pay-element', document, { + paymentMethods: paymentMethods, + buttonsContainerOptions: { + className: 'gap-1 !flex-col sm:!flex-row place-items-center', + }, + buttonOptions: { + type: 'plain', + margin: '0', + height: '50px', + width: '100%', + justifyContent: 'flex-start', + onClick: (args: { + source?: 'apple_pay' | 'google_pay' | 'paze'; + }) => handleExpressPayClickRef.current(args), + }, + }); + } + }); + } }, [ + isPoyntLoaded, godaddyPaymentsConfig, + isCollectLoading, + draftOrder, + couponFetchStatus, countryCode, currencyCode, session?.businessId, @@ -462,78 +511,10 @@ export function ExpressCheckoutButton() { session?.enablePromotionCodes, session?.enableShippingAddressCollection, session?.storeName, - isPoyntLoaded, - isCollectLoading, - draftOrder, - couponFetchStatus, t, - handleExpressPayClick, formatCurrency, ]); - // Mount the TokenizeJs instance - useEffect(() => { - if ( - !isPoyntLoaded || - !godaddyPaymentsConfig || - !isCollectLoading || - !collect.current || - hasMounted.current || - (!godaddyPaymentsConfig?.businessId && !session?.businessId) - ) - return; - - collect.current?.supportWalletPayments().then(supports => { - const paymentMethods: string[] = []; - if (supports.applePay) { - track({ - eventId: eventIds.expressApplePayImpression, - type: TrackingEventType.IMPRESSION, - properties: { - provider: 'poynt', - }, - }); - paymentMethods.push('apple_pay'); - } - if (supports.googlePay) { - paymentMethods.push('google_pay'); - track({ - eventId: eventIds.expressGooglePayImpression, - type: TrackingEventType.IMPRESSION, - properties: { - provider: 'poynt', - }, - }); - } - // if (supports.paze) paymentMethods.push("paze"); // paze is not an "express" payment and needs to be implemented as a standard flow - - if (paymentMethods.length > 0 && !hasMounted.current) { - hasMounted.current = true; - // console.log("[poynt collect] Mounting"); - collect?.current?.mount('gdpay-express-pay-element', document, { - paymentMethods: paymentMethods, - buttonsContainerOptions: { - className: 'gap-1 !flex-col sm:!flex-row place-items-center', - }, - buttonOptions: { - type: 'plain', - margin: '0', - height: '50px', - width: '100%', - justifyContent: 'flex-start', - onClick: handleExpressPayClick, - }, - }); - } - }); - }, [ - isPoyntLoaded, - godaddyPaymentsConfig, - isCollectLoading, - handleExpressPayClick, - session?.businessId, - ]); - // Function to convert shipping address to shippingLines format for price adjustments const convertAddressToShippingLines = useCallback( ( diff --git a/packages/react/src/components/checkout/payment/checkout-buttons/mercadopago/mercadopago.tsx b/packages/react/src/components/checkout/payment/checkout-buttons/mercadopago/mercadopago.tsx index 29ea7781..d43b11df 100644 --- a/packages/react/src/components/checkout/payment/checkout-buttons/mercadopago/mercadopago.tsx +++ b/packages/react/src/components/checkout/payment/checkout-buttons/mercadopago/mercadopago.tsx @@ -9,6 +9,7 @@ import { } from '@/components/checkout/payment/utils/use-confirm-checkout'; import { useIsPaymentDisabled } from '@/components/checkout/payment/utils/use-is-payment-disabled'; import { useLoadMercadoPago } from '@/components/checkout/payment/utils/use-load-mercadopago'; +import { formatCurrency } from '@/components/checkout/utils/format-currency'; import { Button } from '@/components/ui/button'; import { useGoDaddyContext } from '@/godaddy-provider'; import { GraphQLErrorWithCodes } from '@/lib/graphql-with-errors'; @@ -96,7 +97,15 @@ export function MercadoPagoCheckoutButton() { } else { // Create new brick const renderBrick = async () => { - const total = totals?.total?.value || 0; + // Convert from minor units (cents) to major units + const total = parseFloat( + formatCurrency({ + amount: totals?.total?.value || 0, + currencyCode: totals?.total?.currencyCode || 'USD', + inputInMinorUnits: true, + returnRaw: true, + }) + ); try { const container = document.getElementById(elementId); diff --git a/packages/react/src/components/checkout/pickup/local-pickup.tsx b/packages/react/src/components/checkout/pickup/local-pickup.tsx index 6b79944a..a46f2e75 100644 --- a/packages/react/src/components/checkout/pickup/local-pickup.tsx +++ b/packages/react/src/components/checkout/pickup/local-pickup.tsx @@ -1,7 +1,8 @@ -import { addDays, format, set } from 'date-fns'; +import { addDays, format } from 'date-fns'; import { format as formatTz, toZonedTime } from 'date-fns-tz'; import { CalendarIcon, ChevronDown, Clock, MapPin, Store } from 'lucide-react'; import React, { useCallback, useEffect, useState } from 'react'; +import { useFormContext } from 'react-hook-form'; import { useCheckoutContext } from '@/components/checkout/checkout'; import { useApplyFulfillmentLocation } from '@/components/checkout/delivery/utils/use-apply-fulfillment-location'; import { NotesForm } from '@/components/checkout/notes/notes-form'; @@ -36,10 +37,13 @@ import { cn } from '@/lib/utils'; import { eventIds } from '@/tracking/events'; import { TrackingEventType, track } from '@/tracking/track'; import type { CheckoutSessionLocation, StoreHours } from '@/types'; - -const FALLBACK_LEAD_TIME = 30; // Default lead time in minutes - -import { useFormContext } from 'react-hook-form'; +import { + FALLBACK_LEAD_TIME, + findFirstAvailablePickupDate, + formatLeadTimeDisplay, + generatePickupTimeSlots, + isAsapAvailable, +} from './utils/generate-pickup-time-slots'; // Map day of week to the corresponding property in hours const dayToProperty = { @@ -136,21 +140,12 @@ export function LocalPickupForm({ return; } - let dateToCheck = new Date(); - const maxDays = locationHours.pickupWindowInDays; - for (let i = 0; i < maxDays; i++) { - const dayOfWeek = dateToCheck.getDay(); - const dayProperty = - dayToProperty[dayOfWeek as keyof typeof dayToProperty]; - if (locationHours.hours[dayProperty]?.enabled) { - const zonedDate = toZonedTime(dateToCheck, locationHours.timeZone); - setSelectedDate(zonedDate); - if (form.getValues('pickupDate') === '') { - form.setValue('pickupDate', format(zonedDate, 'yyyy-MM-dd')); - } - break; + const date = findFirstAvailablePickupDate(locationHours); + if (date) { + setSelectedDate(date); + if (form.getValues('pickupDate') === '') { + form.setValue('pickupDate', format(date, 'yyyy-MM-dd')); } - dateToCheck = addDays(dateToCheck, 1); } }, [session?.locations, getStoreHours, form] @@ -244,192 +239,71 @@ export function LocalPickupForm({ const dayOfWeek = selectedDate.getDay(); const dayProperty = dayToProperty[dayOfWeek as keyof typeof dayToProperty]; const hoursForDay = locationHours.hours[dayProperty]; - if (!hoursForDay?.enabled) { + if ( + !hoursForDay?.enabled || + !hoursForDay.openTime || + !hoursForDay.closeTime + ) { setAvailableTimeSlots([]); return; } + const leadTimeMinutes = locationHours.leadTime || FALLBACK_LEAD_TIME; - // We'll get the raw open time values directly for consistency - if (!hoursForDay.openTime) { - setAvailableTimeSlots([]); - return; - } - const openTimeHours = Number.parseInt( - hoursForDay?.openTime?.split(':')?.[0] ?? '00', - 10 - ); - const openTimeMins = Number.parseInt( - hoursForDay?.openTime?.split(':')?.[1] ?? '00', - 10 - ); - // Create a base date object for the selected date - const baseDate = new Date(selectedDate); - // Set hours and minutes directly to match opening time - const openTime = set(baseDate, { - hours: openTimeHours, - minutes: openTimeMins, - seconds: 0, - milliseconds: 0, + // Delegate timed-slot generation to the pure utility + const timedSlots = generatePickupTimeSlots({ + selectedDate, + storeHours: locationHours, }); - // We'll get the raw close time values directly from the hours object const slots: TimeSlot[] = []; + const isToday = formatTz(selectedDate, 'yyyy-MM-dd', { timeZone: locationTimeZone }) === formatTz(new Date(), 'yyyy-MM-dd', { timeZone: locationTimeZone }); - // Extract hours and minutes for direct string comparison to avoid timezone issues - if (!hoursForDay.closeTime) { - setAvailableTimeSlots([]); - return; - } - const closeTimeHours = Number.parseInt( - hoursForDay?.closeTime?.split(':')?.[0] ?? '23', - 10 - ); - const closeTimeMins = Number.parseInt( - hoursForDay?.closeTime?.split(':')?.[1] ?? '59', - 10 - ); - - // Get the current time in the location's timezone only - const now = toZonedTime(new Date(), locationTimeZone); - const earliestPickup = isToday - ? new Date(now.getTime() + leadTimeMinutes * 60000) - : openTime; - // Initialize currentTime to openTime (exactly matching the hours/minutes) - let currentTime = set(new Date(selectedDate), { - hours: openTimeHours, - minutes: openTimeMins, - seconds: 0, - milliseconds: 0, - }); - - // Only add ASAP option if there will be at least one other time slot available today - // Calculate if any time slots would be available after earliest pickup time - let hasAvailableTimeSlots = false; if (isToday) { + const now = toZonedTime(new Date(), locationTimeZone); const nowInMinutes = now.getHours() * 60 + now.getMinutes(); + const closeTimeHours = Number.parseInt( + hoursForDay.closeTime.split(':')[0] ?? '23', + 10 + ); + const closeTimeMins = Number.parseInt( + hoursForDay.closeTime.split(':')[1] ?? '59', + 10 + ); const closeTimeInMinutes = closeTimeHours * 60 + closeTimeMins; - const minimumBufferMinutes = 30; // Same buffer as in our main logic - - // Check if there's enough time remaining in the day for at least one time slot - if (nowInMinutes + minimumBufferMinutes < closeTimeInMinutes) { - const leadTimeDisplay = - leadTimeMinutes >= 60 - ? `${leadTimeMinutes / 60} ${leadTimeMinutes === 60 ? t.pickup.hour : t.pickup.hours}` - : `${leadTimeMinutes} ${t.pickup.minutes}`; + + let hasAsap = false; + if (isAsapAvailable(nowInMinutes, leadTimeMinutes, closeTimeInMinutes)) { + const leadTimeDisplay = formatLeadTimeDisplay(leadTimeMinutes, { + hour: t.pickup.hour, + hours: t.pickup.hours, + minutes: t.pickup.minutes, + }); slots.push({ label: `${t.pickup.asap} (${leadTimeDisplay})`, value: 'ASAP', }); - hasAvailableTimeSlots = true; - } - if (earliestPickup > openTime) { - const minutesSinceMidnight = - earliestPickup.getHours() * 60 + earliestPickup.getMinutes(); - const roundedMinutes = - Math.ceil(minutesSinceMidnight / leadTimeMinutes) * leadTimeMinutes; - currentTime = set(openTime, { - hours: Math.floor(roundedMinutes / 60), - minutes: roundedMinutes % 60, - seconds: 0, - }); + hasAsap = true; } - // If no time slots will be available today, find the next available day - if (!hasAvailableTimeSlots) { - // Reset the slots array since we'll be finding a new day - slots.length = 0; - - let nextAvailableDate = addDays(selectedDate, 1); - const maxDays = storeHours?.pickupWindowInDays; - let foundNextDay = false; - - for (let i = 1; i < maxDays; i++) { - const nextDayOfWeek = nextAvailableDate.getDay(); - const nextDayProperty = - dayToProperty[nextDayOfWeek as keyof typeof dayToProperty]; - - if (storeHours?.hours[nextDayProperty]?.enabled) { - // We found the next available day - const nextDayZoned = toZonedTime( - nextAvailableDate, - locationTimeZone - ); - setSelectedDate(nextDayZoned); - form.setValue('pickupDate', format(nextDayZoned, 'yyyy-MM-dd')); - foundNextDay = true; - break; - } - - nextAvailableDate = addDays(nextAvailableDate, 1); - } - - // Early return as we'll rerun this effect with the new selected date - if (foundNextDay) { - return; + // If today has no ASAP and no timed slots, jump to the next bookable day + if (!hasAsap && timedSlots.length === 0) { + const nextDate = findFirstAvailablePickupDate(locationHours); + if ( + nextDate && + format(nextDate, 'yyyy-MM-dd') !== format(selectedDate, 'yyyy-MM-dd') + ) { + setSelectedDate(nextDate); + form.setValue('pickupDate', format(nextDate, 'yyyy-MM-dd')); + return; // re-run effect with the new date } } } - while (true) { - // Get the current slot's hour and minute for comparison - const currentSlotHours = currentTime.getHours(); - const currentSlotMins = currentTime.getMinutes(); - - // Make sure current time is at or after opening time - const isAfterOrAtOpeningTime = - currentSlotHours > openTimeHours || - (currentSlotHours === openTimeHours && currentSlotMins >= openTimeMins); - - // If current time is at or after closing time, break - if ( - !isAfterOrAtOpeningTime || - currentSlotHours > closeTimeHours || - (currentSlotHours === closeTimeHours && - currentSlotMins >= closeTimeMins) - ) { - break; - } - if (isToday) { - // If current slot time is before the earliest pickup time (now + lead time), skip to next slot - if (currentTime < earliestPickup) { - currentTime = set(currentTime, { - minutes: currentTime.getMinutes() + leadTimeMinutes, - }); - continue; - } - - const currentTimeInMinutes = - currentTime.getHours() * 60 + currentTime.getMinutes(); - const nowInMinutes = now.getHours() * 60 + now.getMinutes(); - - const minimumBufferMinutes = - locationHours.leadTime || FALLBACK_LEAD_TIME; // Use locationHours.leadTime with fallback - if (currentTimeInMinutes < nowInMinutes + minimumBufferMinutes) { - currentTime = set(currentTime, { - minutes: currentTime.getMinutes() + leadTimeMinutes, - }); - continue; - } - } - const timeString = formatTz(currentTime, 'HH:mm', { - timeZone: locationTimeZone, - }); - // Format the time in the location's timezone, not the user's timezone - const slotLabel = formatTz(currentTime, 'h:mm a', { - timeZone: locationTimeZone, - }); - slots.push({ - label: slotLabel, - value: timeString, // <-- Only HH:MM - }); - currentTime = set(currentTime, { - minutes: currentTime.getMinutes() + leadTimeMinutes, - }); - } + slots.push(...timedSlots); slots.sort((a, b) => { if (a.value === 'ASAP') return -1; if (b.value === 'ASAP') return 1; diff --git a/packages/react/src/components/checkout/pickup/utils/build-pickup-payload.test.ts b/packages/react/src/components/checkout/pickup/utils/build-pickup-payload.test.ts new file mode 100644 index 00000000..002ab5bd --- /dev/null +++ b/packages/react/src/components/checkout/pickup/utils/build-pickup-payload.test.ts @@ -0,0 +1,170 @@ +import { describe, expect, it, vi } from 'vitest'; +import { buildPickupPayload } from './build-pickup-payload'; + +describe('buildPickupPayload', () => { + describe('date + time (scheduled pickup)', () => { + it('should produce the correct ISO string in the store timezone', () => { + const result = buildPickupPayload({ + pickupDate: '2026-03-26', + pickupTime: '13:00', + pickupLocationId: 'loc-1', + timezone: 'America/New_York', + }); + + // March 26 1:00 PM EDT = 2026-03-26T13:00:00-04:00 + expect(result.fulfillmentStartAt).toBe('2026-03-26T13:00:00-04:00'); + expect(result.fulfillmentEndAt).toBe('2026-03-26T13:00:00-04:00'); + expect(result.fulfillmentLocationId).toBe('loc-1'); + }); + + it('should handle a timezone far from the browser timezone', () => { + const result = buildPickupPayload({ + pickupDate: '2026-03-26', + pickupTime: '09:30', + pickupLocationId: 'loc-2', + timezone: 'Asia/Kolkata', + }); + + // March 26 9:30 AM IST = 2026-03-26T09:30:00+05:30 + expect(result.fulfillmentStartAt).toBe('2026-03-26T09:30:00+05:30'); + expect(result.fulfillmentEndAt).toBe('2026-03-26T09:30:00+05:30'); + }); + + it('should handle UTC timezone', () => { + const result = buildPickupPayload({ + pickupDate: '2026-07-15', + pickupTime: '18:45', + pickupLocationId: 'loc-3', + timezone: 'UTC', + }); + + // XXX token outputs "Z" for UTC + expect(result.fulfillmentStartAt).toBe('2026-07-15T18:45:00Z'); + expect(result.fulfillmentEndAt).toBe('2026-07-15T18:45:00Z'); + }); + + it('should handle a Date object for pickupDate', () => { + // Date object for March 26, 2026 (local fields are what matter) + const dateObj = new Date(2026, 2, 26); + + const result = buildPickupPayload({ + pickupDate: dateObj, + pickupTime: '14:00', + pickupLocationId: 'loc-4', + timezone: 'America/Chicago', + }); + + // March 26 2:00 PM CDT = 2026-03-26T14:00:00-05:00 + expect(result.fulfillmentStartAt).toBe('2026-03-26T14:00:00-05:00'); + }); + + it('should default missing hours/minutes to 0', () => { + const result = buildPickupPayload({ + pickupDate: '2026-06-01', + pickupTime: ':', + pickupLocationId: 'loc-5', + timezone: 'America/Los_Angeles', + }); + + // Midnight PDT + expect(result.fulfillmentStartAt).toBe('2026-06-01T00:00:00-07:00'); + }); + + it('should handle DST boundary correctly', () => { + // Nov 1 2026 โ€” US falls back from EDT to EST + const result = buildPickupPayload({ + pickupDate: '2026-11-02', + pickupTime: '10:00', + pickupLocationId: 'loc-6', + timezone: 'America/New_York', + }); + + // After fall-back, EST = UTC-5 + expect(result.fulfillmentStartAt).toBe('2026-11-02T10:00:00-05:00'); + }); + }); + + describe('date only (no time)', () => { + it('should default to midnight in the store timezone', () => { + const result = buildPickupPayload({ + pickupDate: '2026-04-10', + pickupTime: null, + pickupLocationId: 'loc-7', + timezone: 'America/New_York', + }); + + expect(result.fulfillmentStartAt).toBe('2026-04-10T00:00:00-04:00'); + }); + }); + + describe('ASAP', () => { + it('should add lead time and use the store timezone', () => { + const fakeNow = new Date('2026-03-26T17:00:00.000Z'); // 1 PM EDT + vi.useFakeTimers({ now: fakeNow }); + + const result = buildPickupPayload({ + pickupTime: 'ASAP', + pickupLocationId: 'loc-8', + leadTime: 30, + timezone: 'America/New_York', + }); + + // 1:00 PM + 30 min = 1:30 PM EDT + expect(result.fulfillmentStartAt).toBe('2026-03-26T13:30:00-04:00'); + + vi.useRealTimers(); + }); + }); + + describe('no date and no time (fallback)', () => { + it('should use current time in the store timezone', () => { + const fakeNow = new Date('2026-03-26T20:00:00.000Z'); // 4 PM EDT + vi.useFakeTimers({ now: fakeNow }); + + const result = buildPickupPayload({ + pickupLocationId: 'loc-9', + timezone: 'America/New_York', + }); + + expect(result.fulfillmentStartAt).toBe('2026-03-26T16:00:00-04:00'); + + vi.useRealTimers(); + }); + }); + + describe('timezone defaults', () => { + it('should fall back to UTC when timezone is null', () => { + const result = buildPickupPayload({ + pickupDate: '2026-03-26', + pickupTime: '13:00', + pickupLocationId: 'loc-10', + timezone: null, + }); + + expect(result.fulfillmentStartAt).toBe('2026-03-26T13:00:00Z'); + }); + }); + + describe('fulfillmentLocationId', () => { + it('should pass through the location id', () => { + const result = buildPickupPayload({ + pickupDate: '2026-03-26', + pickupTime: '13:00', + pickupLocationId: 'my-store', + timezone: 'UTC', + }); + + expect(result.fulfillmentLocationId).toBe('my-store'); + }); + + it('should default to null when location id is not provided', () => { + const result = buildPickupPayload({ + pickupDate: '2026-03-26', + pickupTime: '13:00', + timezone: 'UTC', + }); + + expect(result.fulfillmentLocationId).toBeNull(); + }); + }); +}); diff --git a/packages/react/src/components/checkout/pickup/utils/build-pickup-payload.ts b/packages/react/src/components/checkout/pickup/utils/build-pickup-payload.ts index 410b291b..16787be0 100644 --- a/packages/react/src/components/checkout/pickup/utils/build-pickup-payload.ts +++ b/packages/react/src/components/checkout/pickup/utils/build-pickup-payload.ts @@ -1,4 +1,4 @@ -import { format as formatTz, toZonedTime } from 'date-fns-tz'; +import { format as formatTz, fromZonedTime, toZonedTime } from 'date-fns-tz'; type FormFields = { pickupDate?: string | Date | null; @@ -14,9 +14,18 @@ type PickupPayload = { fulfillmentLocationId: string | null; }; -function parseDate(dateStr: string): Date { - const [year, month, day] = dateStr.split('-').map(Number); - return new Date(year, month - 1, day); +/** + * Extract a yyyy-MM-dd date string from either a string or Date. + * When given a Date, reads its local (runtime) fields โ€” this is safe + * because the calendar UI stores dates as yyyy-MM-dd strings or as + * midnight-local Date objects whose year/month/day are always correct. + */ +function toDateString(pickupDate: string | Date): string { + if (typeof pickupDate === 'string') return pickupDate; + const y = pickupDate.getFullYear(); + const m = String(pickupDate.getMonth() + 1).padStart(2, '0'); + const d = String(pickupDate.getDate()).padStart(2, '0'); + return `${y}-${m}-${d}`; } export function buildPickupPayload({ @@ -26,27 +35,34 @@ export function buildPickupPayload({ leadTime = 0, timezone = 'UTC', }: FormFields): PickupPayload { + const tz = timezone ?? 'UTC'; let date: Date; if (pickupTime === 'ASAP') { const now = new Date(); now.setMinutes(now.getMinutes() + leadTime); - date = toZonedTime(now, timezone ?? 'UTC'); + date = toZonedTime(now, tz); } else if (pickupDate && pickupTime) { - const baseDate = - typeof pickupDate === 'string' ? parseDate(pickupDate) : pickupDate; + const dateStr = toDateString(pickupDate); const [hours, minutes] = pickupTime.split(':').map(Number); - const zonedDate = toZonedTime(baseDate, timezone ?? 'UTC'); - zonedDate.setHours(hours || 0, minutes || 0, 0, 0); - date = zonedDate; + const h = String(hours || 0).padStart(2, '0'); + const m = String(minutes || 0).padStart(2, '0'); + + // Build the wall-clock datetime in the store timezone, then convert to + // a correct UTC instant via fromZonedTime before creating the zoned + // representation that formatTz expects. + const utcDate = fromZonedTime(`${dateStr}T${h}:${m}:00`, tz); + date = toZonedTime(utcDate, tz); } else if (pickupDate) { - date = typeof pickupDate === 'string' ? parseDate(pickupDate) : pickupDate; + const dateStr = toDateString(pickupDate); + const utcDate = fromZonedTime(`${dateStr}T00:00:00`, tz); + date = toZonedTime(utcDate, tz); } else { - date = new Date(); + date = toZonedTime(new Date(), tz); } const isoString = formatTz(date, "yyyy-MM-dd'T'HH:mm:ssXXX", { - timeZone: timezone ?? 'UTC', + timeZone: tz, }); return { diff --git a/packages/react/src/components/checkout/pickup/utils/generate-pickup-time-slots.test.ts b/packages/react/src/components/checkout/pickup/utils/generate-pickup-time-slots.test.ts new file mode 100644 index 00000000..1a5e436d --- /dev/null +++ b/packages/react/src/components/checkout/pickup/utils/generate-pickup-time-slots.test.ts @@ -0,0 +1,865 @@ +import { addDays } from 'date-fns'; +import { describe, expect, it } from 'vitest'; +import { + DEFAULT_SLOT_INTERVAL, + findFirstAvailablePickupDate, + formatLeadTimeDisplay, + generatePickupTimeSlots, + isAsapAvailable, + type OperatingHours, +} from './generate-pickup-time-slots'; + +// โ”€โ”€ helpers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +const enabledDay = { enabled: true, openTime: '10:00', closeTime: '15:00' }; +const disabledDay = { enabled: false, openTime: null, closeTime: null }; + +const standardWeek = { + monday: enabledDay, + tuesday: enabledDay, + wednesday: enabledDay, + thursday: enabledDay, + friday: enabledDay, + saturday: enabledDay, + sunday: disabledDay, +}; + +function makeHours(overrides: Partial = {}): OperatingHours { + return { + leadTime: 30, + pickupWindowInDays: 7, + timeZone: 'UTC', + hours: standardWeek, + ...overrides, + }; +} + +// Monday 2024-03-25 at 10:00 UTC +const MON_10AM = new Date('2024-03-25T10:00:00Z'); +const TUE = addDays(MON_10AM, 1); // 2024-03-26 +const WED = addDays(MON_10AM, 2); // 2024-03-27 +const _THU = addDays(MON_10AM, 3); // 2024-03-28 + +function slotValues(slots: { value: string }[]) { + return slots.map(s => s.value); +} + +// โ”€โ”€ findFirstAvailablePickupDate โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +describe('findFirstAvailablePickupDate', () => { + it('returns today when leadTime is small and today is enabled', () => { + const date = findFirstAvailablePickupDate( + makeHours({ leadTime: 30, pickupWindowInDays: 3 }), + MON_10AM + ); + expect(date).toBeDefined(); + expect(date!.getUTCDate()).toBe(25); // Monday + }); + + it('skips today if close time is before earliestPickup', () => { + // now = Mon 10am, leadTime = 360 (6 h), earliestPickup = Mon 4pm + // Mon close = 3pm < 4pm โ†’ skip; Tue close = 3pm Tue > Mon 4pm โ†’ pick Tue + const date = findFirstAvailablePickupDate( + makeHours({ leadTime: 360, pickupWindowInDays: 3 }), + MON_10AM + ); + expect(date).toBeDefined(); + expect(date!.getUTCDate()).toBe(26); // Tuesday + }); + + it('skips disabled days', () => { + // Sunday is disabled; start on Sunday + const sunday = new Date('2024-03-24T10:00:00Z'); + const date = findFirstAvailablePickupDate( + makeHours({ leadTime: 30, pickupWindowInDays: 3 }), + sunday + ); + expect(date).toBeDefined(); + expect(date!.getUTCDay()).toBe(1); // Monday + }); + + it('returns undefined when leadTime exceeds entire window', () => { + // 5500 min โ‰ˆ 3.82 days, window = 3 days + const date = findFirstAvailablePickupDate( + makeHours({ leadTime: 5500, pickupWindowInDays: 3 }), + MON_10AM + ); + expect(date).toBeUndefined(); + }); + + it('returns undefined when leadTime is 14400 with 3-day window', () => { + const date = findFirstAvailablePickupDate( + makeHours({ leadTime: 14400, pickupWindowInDays: 3 }), + MON_10AM + ); + expect(date).toBeUndefined(); + }); + + it('finds day 3 with 2-day leadTime and 3-day window', () => { + // leadTime 2880 min = 2 days; earliestPickup = Wed 10am + // Mon close 3pm < Wed 10am โ†’ skip + // Tue close 3pm Tue < Wed 10am โ†’ skip + // Wed close 3pm Wed > Wed 10am โ†’ pick + const date = findFirstAvailablePickupDate( + makeHours({ leadTime: 2880, pickupWindowInDays: 3 }), + MON_10AM + ); + expect(date).toBeDefined(); + expect(date!.getUTCDate()).toBe(27); // Wednesday + }); + + it('returns today for pickupWindowInDays 0 (ASAP-only)', () => { + const date = findFirstAvailablePickupDate( + makeHours({ leadTime: 99999, pickupWindowInDays: 0 }), + MON_10AM + ); + expect(date).toBeDefined(); + expect(date!.getUTCDate()).toBe(25); + }); + + it('returns undefined when no days are enabled', () => { + const allDisabled = { + monday: disabledDay, + tuesday: disabledDay, + wednesday: disabledDay, + thursday: disabledDay, + friday: disabledDay, + saturday: disabledDay, + sunday: disabledDay, + }; + const date = findFirstAvailablePickupDate( + makeHours({ hours: allDisabled, pickupWindowInDays: 7 }), + MON_10AM + ); + expect(date).toBeUndefined(); + }); + + describe('pickupWindowInDays: 1 (single day)', () => { + it('returns today when today is enabled and has slots after leadTime', () => { + const date = findFirstAvailablePickupDate( + makeHours({ leadTime: 30, pickupWindowInDays: 1 }), + MON_10AM + ); + expect(date).toBeDefined(); + expect(date!.getUTCDate()).toBe(25); + }); + + it('returns undefined when today is disabled (cannot fall back to tomorrow)', () => { + const hours = { + ...standardWeek, + monday: disabledDay, + }; + const date = findFirstAvailablePickupDate( + makeHours({ hours, pickupWindowInDays: 1 }), + MON_10AM + ); + expect(date).toBeUndefined(); + }); + + it('returns undefined when leadTime pushes past today close', () => { + const date = findFirstAvailablePickupDate( + makeHours({ leadTime: 360, pickupWindowInDays: 1 }), + // Mon 10am + 360min lead = 4pm, close is 3pm + MON_10AM + ); + expect(date).toBeUndefined(); + }); + + it('returns today when leadTime still fits before close', () => { + const date = findFirstAvailablePickupDate( + makeHours({ leadTime: 240, pickupWindowInDays: 1 }), + // Mon 10am + 240min lead = 2pm, close is 3pm โ€” fits + MON_10AM + ); + expect(date).toBeDefined(); + expect(date!.getUTCDate()).toBe(25); + }); + }); + + it('uses FALLBACK_LEAD_TIME when leadTime is 0', () => { + // leadTime 0 โ†’ falls back to 30 min + const date = findFirstAvailablePickupDate( + makeHours({ leadTime: 0, pickupWindowInDays: 3 }), + MON_10AM + ); + expect(date).toBeDefined(); + expect(date!.getUTCDate()).toBe(25); // today still works with 30 min lead + }); +}); + +// โ”€โ”€ generatePickupTimeSlots โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +describe('generatePickupTimeSlots', () => { + // โ”€โ”€ basic / edge cases โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + it('returns empty when pickupWindowInDays is 0', () => { + const slots = generatePickupTimeSlots({ + selectedDate: TUE, + storeHours: makeHours({ pickupWindowInDays: 0 }), + now: MON_10AM, + }); + expect(slots).toEqual([]); + }); + + it('returns empty when the selected day is disabled', () => { + const sunday = new Date('2024-03-24T10:00:00Z'); + const slots = generatePickupTimeSlots({ + selectedDate: sunday, + storeHours: makeHours(), + now: MON_10AM, + }); + expect(slots).toEqual([]); + }); + + it('returns empty when the day has no openTime', () => { + const slots = generatePickupTimeSlots({ + selectedDate: TUE, + storeHours: makeHours({ + hours: { + ...standardWeek, + tuesday: { enabled: true, openTime: null, closeTime: '15:00' }, + }, + }), + now: MON_10AM, + }); + expect(slots).toEqual([]); + }); + + // โ”€โ”€ leadTime 0 (falls back to FALLBACK_LEAD_TIME = 30) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe('leadTime: 0 (uses fallback of 30 min)', () => { + const hours = makeHours({ leadTime: 0 }); + + it('generates 30-min slots on a future date', () => { + const slots = generatePickupTimeSlots({ + selectedDate: TUE, + storeHours: hours, + now: MON_10AM, + }); + // 10:00โ€“14:30 in 30-min steps = 10 slots + expect(slots).toHaveLength(10); + expect(slotValues(slots)[0]).toBe('10:00'); + expect(slotValues(slots)[9]).toBe('14:30'); + }); + }); + + // โ”€โ”€ leadTime: 15 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe('leadTime: 15', () => { + const hours = makeHours({ leadTime: 15 }); + + it('generates slots at default 30-min interval (pickupSlotInterval not set)', () => { + // leadTime=15 but pickupSlotInterval defaults to 30 + const slots = generatePickupTimeSlots({ + selectedDate: TUE, + storeHours: hours, + now: MON_10AM, + }); + expect(slots).toHaveLength(10); + expect(slotValues(slots)[0]).toBe('10:00'); + expect(slotValues(slots)[1]).toBe('10:30'); + }); + + it('generates slots at 15-min interval when pickupSlotInterval is 15', () => { + const slots = generatePickupTimeSlots({ + selectedDate: TUE, + storeHours: { ...hours, pickupSlotInterval: 15 }, + now: MON_10AM, + }); + // 10:00โ€“14:45 in 15-min steps = 20 slots + expect(slots).toHaveLength(20); + expect(slotValues(slots)[0]).toBe('10:00'); + expect(slotValues(slots)[1]).toBe('10:15'); + expect(slotValues(slots)[19]).toBe('14:45'); + }); + }); + + // โ”€โ”€ leadTime: 30 (standard) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe('leadTime: 30', () => { + const hours = makeHours({ leadTime: 30 }); + + it('generates 10 slots for a future date (10:00โ€“15:00, 30-min gap)', () => { + const slots = generatePickupTimeSlots({ + selectedDate: TUE, + storeHours: hours, + now: MON_10AM, + }); + expect(slots).toHaveLength(10); + expect(slotValues(slots)).toEqual([ + '10:00', + '10:30', + '11:00', + '11:30', + '12:00', + '12:30', + '13:00', + '13:30', + '14:00', + '14:30', + ]); + }); + + it('skips slots before earliestPickup for today', () => { + // now = Mon 10am, leadTime = 30, earliest = 10:30 + // First timed slot should be 10:30 (10:00 skipped) + const slots = generatePickupTimeSlots({ + selectedDate: MON_10AM, + storeHours: hours, + now: MON_10AM, + }); + expect(slotValues(slots)[0]).toBe('10:30'); + expect(slots).toHaveLength(9); + }); + }); + + // โ”€โ”€ leadTime: 60 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe('leadTime: 60', () => { + it('generates slots at 30-min default interval, skipping the first hour for today', () => { + // now = 10am, leadTime 60, earliest = 11am + // Rounding: ceil(660/30)*30 = 660 โ†’ 11:00 + // Slots: 11:00, 11:30, 12:00, 12:30, 13:00, 13:30, 14:00, 14:30 = 8 + const slots = generatePickupTimeSlots({ + selectedDate: MON_10AM, + storeHours: makeHours({ leadTime: 60 }), + now: MON_10AM, + }); + expect(slotValues(slots)[0]).toBe('11:00'); + expect(slots).toHaveLength(8); + }); + + it('generates all slots for a future date (lead time already satisfied)', () => { + const slots = generatePickupTimeSlots({ + selectedDate: TUE, + storeHours: makeHours({ leadTime: 60 }), + now: MON_10AM, + }); + expect(slots).toHaveLength(10); + expect(slotValues(slots)[0]).toBe('10:00'); + }); + + it('with pickupSlotInterval: 60, generates hourly slots on a future date', () => { + const slots = generatePickupTimeSlots({ + selectedDate: TUE, + storeHours: makeHours({ leadTime: 60, pickupSlotInterval: 60 }), + now: MON_10AM, + }); + expect(slotValues(slots)).toEqual([ + '10:00', + '11:00', + '12:00', + '13:00', + '14:00', + ]); + }); + }); + + // โ”€โ”€ leadTime: 120 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe('leadTime: 120', () => { + it('with pickupSlotInterval: 120, generates 3 slots on a future date', () => { + const slots = generatePickupTimeSlots({ + selectedDate: TUE, + storeHours: makeHours({ leadTime: 120, pickupSlotInterval: 120 }), + now: MON_10AM, + }); + expect(slotValues(slots)).toEqual(['10:00', '12:00', '14:00']); + }); + + it('with default pickupSlotInterval (30), generates all 30-min slots on a future date', () => { + const slots = generatePickupTimeSlots({ + selectedDate: TUE, + storeHours: makeHours({ leadTime: 120 }), + now: MON_10AM, + }); + // All 10 slots available because Tue 10am is well past Mon 10am + 2h + expect(slots).toHaveLength(10); + }); + }); + + // โ”€โ”€ leadTime: 300 (5 h = full daily window) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe('leadTime: 300 (equals the 5-hour daily window)', () => { + it('still shows all 30-min slots on a future date (leadTime satisfied by date distance)', () => { + // Tue 10am is 24h after Mon 10am โ€” well past the 5h lead + const slots = generatePickupTimeSlots({ + selectedDate: TUE, + storeHours: makeHours({ leadTime: 300 }), + now: MON_10AM, + }); + expect(slots).toHaveLength(10); + }); + + it('generates zero timed slots for today', () => { + // earliestPickup = Mon 3pm, close = 3pm โ†’ no room + const slots = generatePickupTimeSlots({ + selectedDate: MON_10AM, + storeHours: makeHours({ leadTime: 300 }), + now: MON_10AM, + }); + expect(slots).toHaveLength(0); + }); + }); + + // โ”€โ”€ leadTime: 1440 (exactly 1 day) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe('leadTime: 1440 (1 day)', () => { + it('generates all 30-min slots on Tue when now is Mon 10am (lead satisfied)', () => { + // earliestPickup = Tue 10am. Tue 10:00 >= Tue 10:00 โ†’ all slots valid + const slots = generatePickupTimeSlots({ + selectedDate: TUE, + storeHours: makeHours({ leadTime: 1440 }), + now: MON_10AM, + }); + expect(slots).toHaveLength(10); + expect(slotValues(slots)[0]).toBe('10:00'); + }); + + it('generates zero slots on Tue when now is Mon noon (lead pushes past Tue open)', () => { + // now = Mon 12pm, earliestPickup = Tue 12pm + // Tue slots 10:00โ€“11:30 all < Tue 12pm โ†’ skipped + // Tue slot 12:00 โ‰ฅ Tue 12pm โ†’ generated! + const monNoon = new Date('2024-03-25T12:00:00Z'); + const slots = generatePickupTimeSlots({ + selectedDate: TUE, + storeHours: makeHours({ leadTime: 1440 }), + now: monNoon, + }); + expect(slotValues(slots)[0]).toBe('12:00'); + expect(slots).toHaveLength(6); // 12:00, 12:30, 13:00, 13:30, 14:00, 14:30 + }); + + it('generates zero timed slots for today', () => { + const slots = generatePickupTimeSlots({ + selectedDate: MON_10AM, + storeHours: makeHours({ leadTime: 1440 }), + now: MON_10AM, + }); + expect(slots).toHaveLength(0); + }); + + it('does not infinite loop (completes in reasonable time)', () => { + const start = performance.now(); + generatePickupTimeSlots({ + selectedDate: TUE, + storeHours: makeHours({ leadTime: 1440 }), + now: MON_10AM, + }); + expect(performance.now() - start).toBeLessThan(100); // < 100ms + }); + }); + + // โ”€โ”€ leadTime: 2880 (2 days) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe('leadTime: 2880 (2 days)', () => { + it('generates zero slots on Tue (within lead window)', () => { + // earliestPickup = Wed 10am. Tue 10amโ€“14:30 all < Wed 10am โ†’ 0 slots + const slots = generatePickupTimeSlots({ + selectedDate: TUE, + storeHours: makeHours({ leadTime: 2880 }), + now: MON_10AM, + }); + expect(slots).toHaveLength(0); + }); + + it('generates all slots on Wed (first day past lead window)', () => { + // Wed 10am โ‰ฅ Wed 10am โ†’ all slots available + const slots = generatePickupTimeSlots({ + selectedDate: WED, + storeHours: makeHours({ leadTime: 2880 }), + now: MON_10AM, + }); + expect(slots).toHaveLength(10); + expect(slotValues(slots)[0]).toBe('10:00'); + }); + + it('does not infinite loop (completes in reasonable time)', () => { + const start = performance.now(); + generatePickupTimeSlots({ + selectedDate: TUE, + storeHours: makeHours({ leadTime: 2880 }), + now: MON_10AM, + }); + expect(performance.now() - start).toBeLessThan(100); + }); + }); + + // โ”€โ”€ leadTime: 5500 (~3.82 days) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe('leadTime: 5500 (~3.82 days)', () => { + const hours = makeHours({ leadTime: 5500, pickupWindowInDays: 3 }); + + it('generates zero slots for Mon, Tue, and Wed', () => { + for (const date of [MON_10AM, TUE, WED]) { + const slots = generatePickupTimeSlots({ + selectedDate: date, + storeHours: hours, + now: MON_10AM, + }); + expect(slots).toHaveLength(0); + } + }); + + it('does not infinite loop even for every day in the window', () => { + const start = performance.now(); + for (const date of [MON_10AM, TUE, WED]) { + generatePickupTimeSlots({ + selectedDate: date, + storeHours: hours, + now: MON_10AM, + }); + } + expect(performance.now() - start).toBeLessThan(100); + }); + }); + + // โ”€โ”€ leadTime: 14400 (10 days) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe('leadTime: 14400 (10 days)', () => { + it('generates zero slots for every day in a 7-day window', () => { + const hours = makeHours({ leadTime: 14400, pickupWindowInDays: 7 }); + for (let i = 0; i < 7; i++) { + const date = addDays(MON_10AM, i); + const slots = generatePickupTimeSlots({ + selectedDate: date, + storeHours: hours, + now: MON_10AM, + }); + expect(slots).toHaveLength(0); + } + }); + + it('does not infinite loop', () => { + const hours = makeHours({ leadTime: 14400, pickupWindowInDays: 7 }); + const start = performance.now(); + for (let i = 0; i < 7; i++) { + generatePickupTimeSlots({ + selectedDate: addDays(MON_10AM, i), + storeHours: hours, + now: MON_10AM, + }); + } + expect(performance.now() - start).toBeLessThan(100); + }); + }); + + // โ”€โ”€ pickupWindowInDays: 1 (single day) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe('pickupWindowInDays: 1 (single day)', () => { + it('generates slots for today when leadTime is small', () => { + const slots = generatePickupTimeSlots({ + selectedDate: MON_10AM, + storeHours: makeHours({ leadTime: 30, pickupWindowInDays: 1 }), + now: MON_10AM, + }); + // 10:00 + 30min lead = 10:30 earliest; slots: 10:30โ€“14:30 = 9 slots + expect(slots.length).toBeGreaterThan(0); + expect(slotValues(slots)[0]).toBe('10:30'); + }); + + it('generates all slots when selected date is today and leadTime already passed open', () => { + // now is 10am, leadTime 0 โ†’ fallback 30min, earliest 10:30 + const slots = generatePickupTimeSlots({ + selectedDate: MON_10AM, + storeHours: makeHours({ leadTime: 0, pickupWindowInDays: 1 }), + now: MON_10AM, + }); + expect(slots.length).toBeGreaterThan(0); + expect(slotValues(slots)[0]).toBe('10:30'); + }); + + it('generates zero slots when leadTime exceeds remaining hours', () => { + const slots = generatePickupTimeSlots({ + selectedDate: MON_10AM, + storeHours: makeHours({ leadTime: 360, pickupWindowInDays: 1 }), + now: MON_10AM, + }); + // 10am + 360min = 4pm, close is 3pm โ†’ no slots + expect(slots).toEqual([]); + }); + + it('generates fewer slots as leadTime eats into the window', () => { + const slotsShortLead = generatePickupTimeSlots({ + selectedDate: MON_10AM, + storeHours: makeHours({ leadTime: 30, pickupWindowInDays: 1 }), + now: MON_10AM, + }); + const slotsLongLead = generatePickupTimeSlots({ + selectedDate: MON_10AM, + storeHours: makeHours({ leadTime: 180, pickupWindowInDays: 1 }), + now: MON_10AM, + }); + // 30min lead โ†’ first slot 10:30; 180min lead โ†’ first slot 13:00 + expect(slotsShortLead.length).toBeGreaterThan(slotsLongLead.length); + expect(slotValues(slotsLongLead)[0]).toBe('13:00'); + }); + + it('respects pickupSlotInterval with single day window', () => { + const slots = generatePickupTimeSlots({ + selectedDate: TUE, + storeHours: makeHours({ + leadTime: 1440, + pickupWindowInDays: 1, + pickupSlotInterval: 60, + }), + now: MON_10AM, + }); + // Tomorrow (Tue) with 1-day lead satisfied, 60-min interval: 10:00โ€“14:00 = 5 slots + expect(slotValues(slots)).toEqual([ + '10:00', + '11:00', + '12:00', + '13:00', + '14:00', + ]); + }); + }); + + // โ”€โ”€ pickupSlotInterval (decoupled from leadTime) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe('pickupSlotInterval (decoupled from leadTime)', () => { + it('uses DEFAULT_SLOT_INTERVAL (30) when pickupSlotInterval is not set', () => { + const slots = generatePickupTimeSlots({ + selectedDate: TUE, + storeHours: makeHours({ leadTime: 1440 }), + now: MON_10AM, + }); + // 30-min interval: 10:00โ€“14:30 = 10 slots + expect(slots).toHaveLength(10); + expect(DEFAULT_SLOT_INTERVAL).toBe(30); + }); + + it('uses pickupSlotInterval: 15 for 15-min gaps with a 1-day leadTime', () => { + const slots = generatePickupTimeSlots({ + selectedDate: TUE, + storeHours: makeHours({ leadTime: 1440, pickupSlotInterval: 15 }), + now: MON_10AM, + }); + // 15-min interval: 10:00โ€“14:45 = 20 slots + expect(slots).toHaveLength(20); + expect(slotValues(slots)[1]).toBe('10:15'); + }); + + it('uses pickupSlotInterval: 60 for hourly gaps with a 2-day leadTime', () => { + const slots = generatePickupTimeSlots({ + selectedDate: WED, + storeHours: makeHours({ leadTime: 2880, pickupSlotInterval: 60 }), + now: MON_10AM, + }); + expect(slotValues(slots)).toEqual([ + '10:00', + '11:00', + '12:00', + '13:00', + '14:00', + ]); + }); + + it('ignores pickupSlotInterval: 0 and uses default', () => { + const slots = generatePickupTimeSlots({ + selectedDate: TUE, + storeHours: makeHours({ leadTime: 30, pickupSlotInterval: 0 }), + now: MON_10AM, + }); + // Falls back to 30-min interval + expect(slots).toHaveLength(10); + }); + + it('ignores negative pickupSlotInterval and uses default', () => { + const slots = generatePickupTimeSlots({ + selectedDate: TUE, + storeHours: makeHours({ leadTime: 30, pickupSlotInterval: -15 }), + now: MON_10AM, + }); + expect(slots).toHaveLength(10); + }); + }); + + // โ”€โ”€ timezone-aware tests โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + + describe('timezone handling', () => { + // 2024-03-25T15:00:00Z = Mon 10:00 AM in America/New_York (EDT, UTC-4) + // but still Mon 15:00 UTC + const MON_10AM_NYC = new Date('2024-03-25T14:00:00Z'); + + function makeNYCHours( + overrides: Partial = {} + ): OperatingHours { + return { + leadTime: 30, + pickupWindowInDays: 7, + timeZone: 'America/New_York', + hours: standardWeek, + ...overrides, + }; + } + + it('findFirstAvailablePickupDate respects store timezone', () => { + // UTC 14:00 = NYC 10:00 AM, leadTime 30 โ†’ earliest 10:30, close 15:00 โ†’ today works + const date = findFirstAvailablePickupDate( + makeNYCHours({ leadTime: 30, pickupWindowInDays: 1 }), + MON_10AM_NYC + ); + expect(date).toBeDefined(); + }); + + it('findFirstAvailablePickupDate returns undefined when leadTime exceeds remaining store hours', () => { + // UTC 14:00 = NYC 10:00 AM, leadTime 360 โ†’ earliest 4pm NYC, close 3pm โ†’ no slots + const date = findFirstAvailablePickupDate( + makeNYCHours({ leadTime: 360, pickupWindowInDays: 1 }), + MON_10AM_NYC + ); + expect(date).toBeUndefined(); + }); + + it('generatePickupTimeSlots produces correct slot times in store timezone', () => { + const nycDate = new Date('2024-03-26T14:00:00Z'); // Tue 10am NYC + const slots = generatePickupTimeSlots({ + selectedDate: nycDate, + storeHours: makeNYCHours({ leadTime: 1440 }), + now: MON_10AM_NYC, + }); + // Lead time satisfied (>1 day ahead), slots should start at store open (10:00 NYC) + expect(slots.length).toBeGreaterThan(0); + expect(slotValues(slots)[0]).toBe('10:00'); + }); + + it('generates zero slots when current time is past close in store timezone', () => { + // UTC 20:00 Mon = Mon 4pm NYC (EDT) โ€” past 3pm close, same day in both timezones + const pastCloseNYC = new Date('2024-03-25T20:00:00Z'); + const slots = generatePickupTimeSlots({ + selectedDate: pastCloseNYC, + storeHours: makeNYCHours({ leadTime: 30, pickupWindowInDays: 7 }), + now: pastCloseNYC, + }); + // In NYC it's Mon 4pm, store closes at 3pm โ€” zero slots + expect(slots).toEqual([]); + }); + + it('uses store timezone day-of-week, not system timezone', () => { + // UTC 03:00 Tue = Mon 11pm NYC โ€” getDay() in UTC would say Tuesday, + // but in the store timezone it's still Monday + const lateNightUTC = new Date('2024-03-26T03:00:00Z'); + const slots = generatePickupTimeSlots({ + selectedDate: lateNightUTC, + storeHours: makeNYCHours({ leadTime: 30, pickupWindowInDays: 7 }), + now: lateNightUTC, + }); + // In NYC it's Mon 11pm, store closes at 3pm โ€” zero slots + expect(slots).toEqual([]); + }); + + it('next-day rollover: UTC date is tomorrow but store timezone is still today', () => { + // UTC 01:00 Tue Mar 26 = Mon 9pm EDT Mar 25 โ€” still Monday in NYC + const utcTueMorning = new Date('2024-03-26T01:00:00Z'); + const date = findFirstAvailablePickupDate( + makeNYCHours({ leadTime: 30, pickupWindowInDays: 1 }), + utcTueMorning + ); + // In NYC it's Mon 9pm, 30min lead = 9:30pm, close is 3pm โ†’ no slots today + expect(date).toBeUndefined(); + }); + + // US West Coast: UTC-7 (PDT in March 2024) + it('works with America/Los_Angeles timezone', () => { + // UTC 17:00 Mon = 10:00 AM PDT + const monMorningLA = new Date('2024-03-25T17:00:00Z'); + const laHours: OperatingHours = { + leadTime: 60, + pickupWindowInDays: 3, + timeZone: 'America/Los_Angeles', + hours: standardWeek, + }; + const date = findFirstAvailablePickupDate(laHours, monMorningLA); + expect(date).toBeDefined(); + + const slots = generatePickupTimeSlots({ + selectedDate: date!, + storeHours: laHours, + now: monMorningLA, + }); + // 10am + 60min lead = 11am, 30-min slots: 11:00โ€“14:30 = 8 slots + expect(slots.length).toBeGreaterThan(0); + expect(slotValues(slots)[0]).toBe('11:00'); + }); + }); +}); + +// โ”€โ”€ formatLeadTimeDisplay โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +const labels = { hour: 'hour', hours: 'hours', minutes: 'minutes' }; + +describe('formatLeadTimeDisplay', () => { + it('shows only minutes when under an hour', () => { + expect(formatLeadTimeDisplay(45, labels)).toBe('45 minutes'); + }); + + it('shows only minutes for small values', () => { + expect(formatLeadTimeDisplay(5, labels)).toBe('5 minutes'); + }); + + it('shows singular hour for exactly 60 minutes', () => { + expect(formatLeadTimeDisplay(60, labels)).toBe('1 hour'); + }); + + it('shows plural hours for exact multiples', () => { + expect(formatLeadTimeDisplay(120, labels)).toBe('2 hours'); + expect(formatLeadTimeDisplay(1440, labels)).toBe('24 hours'); + }); + + it('shows hours and minutes for non-exact values', () => { + expect(formatLeadTimeDisplay(90, labels)).toBe('1 hour 30 minutes'); + expect(formatLeadTimeDisplay(1400, labels)).toBe('23 hours 20 minutes'); + }); + + it('handles large lead times', () => { + expect(formatLeadTimeDisplay(5500, labels)).toBe('91 hours 40 minutes'); + }); + + it('handles zero', () => { + expect(formatLeadTimeDisplay(0, labels)).toBe('0 minutes'); + }); +}); + +// โ”€โ”€ isAsapAvailable โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +describe('isAsapAvailable', () => { + // Store closes at 3pm (900 minutes since midnight) + const closeTime = 15 * 60; // 900 + + it('returns true when now + leadTime is before close', () => { + // 10am (600) + 30min lead = 10:30am < 3pm + expect(isAsapAvailable(600, 30, closeTime)).toBe(true); + }); + + it('returns false when now + leadTime is after close', () => { + // 10am (600) + 1400min lead = ~9:20pm next day > 3pm + expect(isAsapAvailable(600, 1400, closeTime)).toBe(false); + }); + + it('returns false when now + leadTime equals close exactly', () => { + // 2pm (840) + 60min lead = 3pm = close โ†’ not strictly before + expect(isAsapAvailable(840, 60, closeTime)).toBe(false); + }); + + it('returns true when just barely fits before close', () => { + // 2pm (840) + 59min lead = 2:59pm < 3pm + expect(isAsapAvailable(840, 59, closeTime)).toBe(true); + }); + + it('returns false when already past close', () => { + // 4pm (960) + 30min lead = 4:30pm > 3pm + expect(isAsapAvailable(960, 30, closeTime)).toBe(false); + }); + + it('returns false with large leadTime and early now', () => { + // 9am (540) + 360min (6hr) lead = 3pm = close โ†’ not strictly before + expect(isAsapAvailable(540, 360, closeTime)).toBe(false); + }); + + it('returns true with zero leadTime', () => { + // 2pm (840) + 0 = 2pm < 3pm + expect(isAsapAvailable(840, 0, closeTime)).toBe(true); + }); +}); diff --git a/packages/react/src/components/checkout/pickup/utils/generate-pickup-time-slots.ts b/packages/react/src/components/checkout/pickup/utils/generate-pickup-time-slots.ts new file mode 100644 index 00000000..8ddb199f --- /dev/null +++ b/packages/react/src/components/checkout/pickup/utils/generate-pickup-time-slots.ts @@ -0,0 +1,260 @@ +import { addDays, format, set } from 'date-fns'; +import { toZonedTime } from 'date-fns-tz'; + +export const DEFAULT_SLOT_INTERVAL = 30; +export const FALLBACK_LEAD_TIME = 30; + +export const dayToProperty = { + 0: 'sunday', + 1: 'monday', + 2: 'tuesday', + 3: 'wednesday', + 4: 'thursday', + 5: 'friday', + 6: 'saturday', +} as const; + +export type DayHours = { + enabled: boolean; + openTime: string | null; + closeTime: string | null; +}; + +export type WeekHours = { + monday: DayHours | null; + tuesday: DayHours | null; + wednesday: DayHours | null; + thursday: DayHours | null; + friday: DayHours | null; + saturday: DayHours | null; + sunday: DayHours | null; +}; + +export type OperatingHours = { + leadTime: number; + pickupWindowInDays: number; + timeZone: string; + hours: WeekHours; + /** + * Interval in minutes between selectable pickup time slots. + * Defaults to DEFAULT_SLOT_INTERVAL (30) when not provided. + * + * Intentionally separate from `leadTime`: + * โ€“ leadTime = how much advance notice the store needs + * โ€“ pickupSlotInterval = the gap between choosable times (e.g. 30 โ†’ 10:00, 10:30, 11:00โ€ฆ) + */ + pickupSlotInterval?: number | null; +}; + +export type TimeSlot = { + label: string; + value: string; +}; + +/** + * Format a lead time in minutes for display. + * e.g. 90 โ†’ "1 hour 30 minutes", 60 โ†’ "1 hour", 45 โ†’ "45 minutes" + */ +export function formatLeadTimeDisplay( + leadTimeMinutes: number, + labels: { hour: string; hours: string; minutes: string } +): string { + const hours = Math.floor(leadTimeMinutes / 60); + const mins = leadTimeMinutes % 60; + if (hours === 0) return `${mins} ${labels.minutes}`; + if (mins === 0) return `${hours} ${hours === 1 ? labels.hour : labels.hours}`; + return `${hours} ${hours === 1 ? labels.hour : labels.hours} ${mins} ${labels.minutes}`; +} + +/** + * Determine whether an ASAP pickup option should be shown for today. + * True when the store can fulfill the order (now + leadTime) before closing. + */ +export function isAsapAvailable( + nowMinutesSinceMidnight: number, + leadTimeMinutes: number, + closeTimeMinutes: number +): boolean { + return nowMinutesSinceMidnight + leadTimeMinutes < closeTimeMinutes; +} + +/** Resolve the effective slot interval, with fallback. */ +function getSlotInterval(hours: OperatingHours): number { + return hours.pickupSlotInterval && hours.pickupSlotInterval > 0 + ? hours.pickupSlotInterval + : DEFAULT_SLOT_INTERVAL; +} + +/** + * Find the first date within the pickup window that has at least one + * bookable time slot, accounting for lead time. + * + * Returns `undefined` when no date in the window qualifies (e.g. when + * leadTime exceeds the entire pickup window). + */ +export function findFirstAvailablePickupDate( + storeHours: OperatingHours, + now?: Date +): Date | undefined { + if (storeHours.pickupWindowInDays === 0) { + // ASAP-only mode โ€” always today + return toZonedTime(now ?? new Date(), storeHours.timeZone); + } + + const leadTimeMinutes = storeHours.leadTime || FALLBACK_LEAD_TIME; + const zonedNow = toZonedTime(now ?? new Date(), storeHours.timeZone); + const earliestPickup = new Date(zonedNow.getTime() + leadTimeMinutes * 60000); + + let dateToCheck = new Date(now ?? Date.now()); + const maxDays = storeHours.pickupWindowInDays; + + for (let i = 0; i < maxDays; i++) { + const zonedDate = toZonedTime(dateToCheck, storeHours.timeZone); + const dayOfWeek = zonedDate.getDay(); + const dayProperty = dayToProperty[dayOfWeek as keyof typeof dayToProperty]; + const dayHours = storeHours.hours[dayProperty]; + + if (dayHours?.enabled && dayHours.closeTime) { + const [closeH, closeM] = dayHours.closeTime.split(':').map(Number); + const dayCloseTime = set(zonedDate, { + hours: closeH, + minutes: closeM, + seconds: 0, + milliseconds: 0, + }); + + if (dayCloseTime > earliestPickup) { + return zonedDate; + } + } + + dateToCheck = addDays(dateToCheck, 1); + } + + return undefined; +} + +/** + * Generate available timed pickup slots for a specific date. + * + * Does NOT include the ASAP option โ€” that is a presentation concern + * handled by the component for "today" only. + * + * Uses `pickupSlotInterval` (default 30 min) for the gap between slots, and + * `leadTime` only for the earliest-pickup constraint (now + leadTime). + */ +export function generatePickupTimeSlots({ + selectedDate, + storeHours, + now: nowInput, +}: { + selectedDate: Date; + storeHours: OperatingHours; + now?: Date; +}): TimeSlot[] { + const tz = storeHours.timeZone; + const leadTimeMinutes = storeHours.leadTime || FALLBACK_LEAD_TIME; + const pickupSlotInterval = getSlotInterval(storeHours); + + if (storeHours.pickupWindowInDays === 0) return []; + + const zonedSelected = toZonedTime(selectedDate, tz); + const dayOfWeek = zonedSelected.getDay(); + const dayProperty = dayToProperty[dayOfWeek as keyof typeof dayToProperty]; + const hoursForDay = storeHours.hours[dayProperty]; + + if ( + !hoursForDay?.enabled || + !hoursForDay.openTime || + !hoursForDay.closeTime + ) { + return []; + } + + const [openTimeHours, openTimeMins] = hoursForDay.openTime + .split(':') + .map(Number); + const [closeTimeHours, closeTimeMins] = hoursForDay.closeTime + .split(':') + .map(Number); + + // All date math uses zoned dates so comparisons are consistent + // when the UTC date and store-local date differ (e.g. UTC Tue 3am = NYC Mon 11pm). + const now = toZonedTime(nowInput ?? new Date(), tz); + const earliestPickup = new Date(now.getTime() + leadTimeMinutes * 60000); + + const openTime = set(new Date(zonedSelected), { + hours: openTimeHours, + minutes: openTimeMins, + seconds: 0, + milliseconds: 0, + }); + + const closeDateTime = set(new Date(zonedSelected), { + hours: closeTimeHours, + minutes: closeTimeMins, + seconds: 0, + milliseconds: 0, + }); + + const isToday = + format(zonedSelected, 'yyyy-MM-dd') === format(now, 'yyyy-MM-dd'); + + let currentTime = set(new Date(zonedSelected), { + hours: openTimeHours, + minutes: openTimeMins, + seconds: 0, + milliseconds: 0, + }); + + // For today, round currentTime forward past earliestPickup + if (isToday && earliestPickup > openTime) { + const minutesSinceMidnight = + earliestPickup.getHours() * 60 + earliestPickup.getMinutes(); + const roundedMinutes = + Math.ceil(minutesSinceMidnight / pickupSlotInterval) * pickupSlotInterval; + currentTime = set(openTime, { + hours: Math.floor(roundedMinutes / 60), + minutes: roundedMinutes % 60, + seconds: 0, + }); + } + + const slots: TimeSlot[] = []; + + while (true) { + const currentSlotHours = currentTime.getHours(); + const currentSlotMins = currentTime.getMinutes(); + + const isAfterOrAtOpeningTime = + currentSlotHours > openTimeHours || + (currentSlotHours === openTimeHours && currentSlotMins >= openTimeMins); + + // Full-Date comparison prevents infinite loops when pickupSlotInterval + // or leadTime causes the date to overflow into the next day. + if (!isAfterOrAtOpeningTime || currentTime >= closeDateTime) { + break; + } + + // Skip slots before the earliest possible pickup (now + leadTime). + // Applied to ALL dates so that large lead times correctly produce + // zero slots on future dates still within the lead-time window. + if (currentTime < earliestPickup) { + currentTime = set(currentTime, { + minutes: currentTime.getMinutes() + pickupSlotInterval, + }); + continue; + } + + const timeString = format(currentTime, 'HH:mm'); + const slotLabel = format(currentTime, 'h:mm a'); + + slots.push({ label: slotLabel, value: timeString }); + + currentTime = set(currentTime, { + minutes: currentTime.getMinutes() + pickupSlotInterval, + }); + } + + return slots; +} diff --git a/packages/react/src/components/storefront/cart.tsx b/packages/react/src/components/storefront/cart.tsx index a5021d53..ed81367b 100644 --- a/packages/react/src/components/storefront/cart.tsx +++ b/packages/react/src/components/storefront/cart.tsx @@ -75,43 +75,91 @@ export function Cart({ const { t } = useGoDaddyContext(); - // Transform cart line items to Product format for CartLineItems component + // Transform cart line items to Product format for CartLineItems component. + // Selling plan JSON lives on line metafields (SELLING_PLAN); use its checkout price for display + // when present so the cart matches PDP, which may differ from draft line totals.subTotal. const items: Product[] = - order?.lineItems?.map(item => ({ - id: item.id, - name: item.name || t.storefront.product, - image: item.details?.productAssetUrl || '', - quantity: item.quantity || 0, - originalPrice: (item.totals?.subTotal?.value || 0) / (item.quantity || 1), - price: (item.totals?.subTotal?.value || 0) / (item.quantity || 1), - selectedOptions: - item?.details?.selectedOptions?.map(option => ({ - attribute: option.attribute || '', - values: option.values || [], - })) || [], - addons: item.details?.selectedAddons?.map(addon => ({ - attribute: addon.attribute || '', - sku: addon.sku || '', - values: addon.values?.map(value => ({ - costAdjustment: value.costAdjustment - ? { - currencyCode: value.costAdjustment.currencyCode ?? undefined, - value: value.costAdjustment.value ?? undefined, - } - : undefined, - name: value.name ?? undefined, + order?.lineItems?.map(item => { + const rawPlan = item.metafields?.find(m => m?.key === 'SELLING_PLAN')?.value; + let sellingPlan: { name?: string; category?: string } | null = null; + let checkoutPriceMinor: number | undefined; + if (rawPlan) { + try { + const parsed = JSON.parse(rawPlan) as { + name?: string; + category?: string; + catalogPrices?: Array<{ + skuId?: string; + checkoutPrices?: Array<{ value?: number }>; + }>; + checkoutPrice?: { value?: number }; + }; + sellingPlan = { name: parsed.name, category: parsed.category }; + const skuId = item.skuId; + const forSku = skuId + ? parsed.catalogPrices?.find(c => c.skuId === skuId) + : undefined; + const cpFromCatalog = forSku?.checkoutPrices?.[0]?.value; + checkoutPriceMinor = + cpFromCatalog != null + ? Number(cpFromCatalog) + : parsed.checkoutPrice?.value != null + ? Number(parsed.checkoutPrice.value) + : undefined; + } catch { + sellingPlan = null; + } + } + const qty = item.quantity || 1; + const apiUnit = + qty > 0 ? (item.totals?.subTotal?.value || 0) / qty : 0; + const unitPrice = checkoutPriceMinor != null ? checkoutPriceMinor : apiUnit; + return { + id: item.id, + name: item.name || t.storefront.product, + image: item.details?.productAssetUrl || '', + quantity: item.quantity || 0, + originalPrice: unitPrice, + price: unitPrice, + selectedOptions: + item?.details?.selectedOptions?.map(option => ({ + attribute: option.attribute || '', + values: option.values || [], + })) || [], + addons: item.details?.selectedAddons?.map(addon => ({ + attribute: addon.attribute || '', + sku: addon.sku || '', + values: addon.values?.map(value => ({ + costAdjustment: value.costAdjustment + ? { + currencyCode: value.costAdjustment.currencyCode ?? undefined, + value: value.costAdjustment.value ?? undefined, + } + : undefined, + name: value.name ?? undefined, + })), })), - })), - })) || []; + sellingPlan: + sellingPlan?.name != null || sellingPlan?.category != null + ? { name: sellingPlan.name, category: sellingPlan.category } + : undefined, + }; + }) || []; - // Calculate totals + // Calculate totals: subtotal matches sum of displayed line amounts (selling-plan metafield prices when set). const itemCount = items.reduce((sum, item) => sum + item.quantity, 0); const currencyCode = order?.totals?.total?.currencyCode || 'USD'; - const subtotal = order?.totals?.subTotal?.value || 0; + const apiSubtotal = order?.totals?.subTotal?.value || 0; + const lineSubtotalSum = items.reduce( + (sum, item) => sum + item.originalPrice * (item.quantity || 0), + 0 + ); + const subtotal = lineSubtotalSum; const shipping = order?.totals?.shippingTotal?.value || 0; const taxes = order?.totals?.taxTotal?.value || 0; const discount = order?.totals?.discountTotal?.value || 0; - const total = order?.totals?.total?.value || 0; + const apiTotal = order?.totals?.total?.value || 0; + const total = apiTotal + (lineSubtotalSum - apiSubtotal); const totals = { subtotal, diff --git a/packages/react/src/components/storefront/contexts/product-details-context.tsx b/packages/react/src/components/storefront/contexts/product-details-context.tsx new file mode 100644 index 00000000..f08161df --- /dev/null +++ b/packages/react/src/components/storefront/contexts/product-details-context.tsx @@ -0,0 +1,31 @@ +'use client'; + +import { createContext, useContext } from 'react'; +import type { ReactNode } from 'react'; +import type { ProductDetailsTarget } from '../targets/product-details-target'; + +export interface ProductDetailsContextValue { + targets?: Partial< + Record< + ProductDetailsTarget, + (props: { skuId: string | null; storeId: string | undefined }) => ReactNode + > + >; + skuId: string | null; +} + +const productDetailsContext = createContext( + null +); + +export const ProductDetailsProvider = productDetailsContext.Provider; + +export function useProductDetailsContext(): ProductDetailsContextValue { + const ctx = useContext(productDetailsContext); + if (!ctx) { + throw new Error( + 'useProductDetailsContext must be used within a component' + ); + } + return ctx; +} diff --git a/packages/react/src/components/storefront/hooks/use-add-to-cart.ts b/packages/react/src/components/storefront/hooks/use-add-to-cart.ts index 43cd8b8d..614f6149 100644 --- a/packages/react/src/components/storefront/hooks/use-add-to-cart.ts +++ b/packages/react/src/components/storefront/hooks/use-add-to-cart.ts @@ -1,13 +1,27 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useGoDaddyContext } from '@/godaddy-provider'; import { getCartOrderId, setCartOrderId } from '@/lib/cart-storage'; -import { addCartLineItem, createCartOrder } from '@/lib/godaddy/godaddy'; +import { + addCartLineItem, + createCartOrder, + getCartOrder, + updateCartLineItem, +} from '@/lib/godaddy/godaddy'; + +export type AddToCartSellingPlan = { + planId: string; + name?: string; + category?: string; + [key: string]: unknown; +}; export interface AddToCartInput { skuId: string; name: string; quantity: number; productAssetUrl?: string; + sellingPlanId?: string | null; + sellingPlan?: AddToCartSellingPlan | null; } export interface UseAddToCartOptions { @@ -48,7 +62,33 @@ export function useAddToCart(options?: UseAddToCartOptions) { }, }); - // Add line item mutation + // Update line item quantity (merge when same sku + same selling plan) + const updateLineItemMutation = useMutation({ + mutationFn: ({ + orderId, + lineItemId, + newQuantity, + }: { + orderId: string; + lineItemId: string; + newQuantity: number; + }) => + updateCartLineItem( + { id: lineItemId, orderId, quantity: newQuantity }, + context.storeId!, + context.clientId!, + context?.apiHost + ), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['cart-order'] }); + options?.onSuccess?.(); + }, + onError: error => { + options?.onError?.(error as Error); + }, + }); + + // Add line item: optional top-level metafields (SELLING_PLAN JSON) โ€” API-supported way to attach the plan. const addLineItemMutation = useMutation({ mutationFn: ({ orderId, @@ -68,16 +108,24 @@ export function useAddToCart(options?: UseAddToCartOptions) { details: { productAssetUrl: input.productAssetUrl || undefined, }, + ...(input.sellingPlan + ? { + metafields: [ + { + key: 'SELLING_PLAN', + type: 'JSON', + value: JSON.stringify(input.sellingPlan), + }, + ], + } + : {}), }, context.storeId!, context.clientId!, context?.apiHost ), onSuccess: () => { - // Invalidate all cart queries to refresh (queryKey prefix match) queryClient.invalidateQueries({ queryKey: ['cart-order'] }); - - // Call success callback options?.onSuccess?.(); }, onError: error => { @@ -106,14 +154,51 @@ export function useAddToCart(options?: UseAddToCartOptions) { } } - // Add line item to cart - await addLineItemMutation.mutateAsync({ orderId: cartOrderId, input }); + // Fetch current cart to check for matching line (same sku + same selling plan = merge quantity) + const order = await getCartOrder( + cartOrderId, + context.storeId!, + context.clientId!, + context?.apiHost + ).then(data => data?.orderById); + + const inputPlanId = input.sellingPlan?.planId ?? null; + const matchingItem = order?.lineItems?.find(item => { + if (item.skuId !== input.skuId) return false; + const metaValue = item.metafields?.find(m => m?.key === 'SELLING_PLAN')?.value; + let existingPlanId: string | null = null; + if (metaValue) { + try { + const parsed = JSON.parse(metaValue) as { planId?: string }; + existingPlanId = parsed?.planId ?? null; + } catch { + existingPlanId = null; + } + } + return existingPlanId === inputPlanId; + }); + + if (matchingItem?.id && matchingItem.quantity != null) { + await updateLineItemMutation.mutateAsync({ + orderId: cartOrderId, + lineItemId: matchingItem.id, + newQuantity: matchingItem.quantity + input.quantity, + }); + } else { + await addLineItemMutation.mutateAsync({ + orderId: cartOrderId, + input, + }); + } }; return { addToCart, - isLoading: createCartMutation.isPending || addLineItemMutation.isPending, + isLoading: + createCartMutation.isPending || + addLineItemMutation.isPending || + updateLineItemMutation.isPending, isCreatingCart: createCartMutation.isPending, - isAddingItem: addLineItemMutation.isPending, + isAddingItem: addLineItemMutation.isPending || updateLineItemMutation.isPending, }; } diff --git a/packages/react/src/components/storefront/index.ts b/packages/react/src/components/storefront/index.ts index 02bebd62..656f3669 100644 --- a/packages/react/src/components/storefront/index.ts +++ b/packages/react/src/components/storefront/index.ts @@ -5,3 +5,4 @@ export * from './product-card'; export * from './product-details.tsx'; export * from './product-grid'; export * from './product-search'; +export type { ProductDetailsTarget } from './targets/product-details-target'; diff --git a/packages/react/src/components/storefront/product-details.tsx b/packages/react/src/components/storefront/product-details.tsx index 7e072ca8..da52e36c 100644 --- a/packages/react/src/components/storefront/product-details.tsx +++ b/packages/react/src/components/storefront/product-details.tsx @@ -2,6 +2,7 @@ import { useQuery } from '@tanstack/react-query'; import { Loader2, Minus, Plus, ShoppingCart } from 'lucide-react'; +import type { ReactNode } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useFormatCurrency } from '@/components/checkout/utils/format-currency'; import { useAddToCart } from '@/components/storefront/hooks/use-add-to-cart'; @@ -20,6 +21,25 @@ import { Skeleton } from '@/components/ui/skeleton'; import { useGoDaddyContext } from '@/godaddy-provider'; import { getSku, getSkuGroup } from '@/lib/godaddy/godaddy'; import type { SKUGroupAttribute, SKUGroupAttributeValue } from '@/types'; +import { ProductDetailsProvider } from './contexts/product-details-context'; +import type { ProductDetailsTarget } from './targets/product-details-target'; +import { ProductDetailsTargetSlot } from './targets/product-details-target'; + +/** Price at checkout (e.g. subscription price). Value in minor units (cents). */ +export type SellingPlanCheckoutPrice = { + value: number; + currencyCode?: string; +}; + +/** Selling plan option for add-to-cart (from PDP selector). */ +export type SellingPlanSelection = { + planId: string; + name?: string; + category?: string; + /** When set, product details shows this as the main price (checkout/subscription price). Value in minor units. */ + checkoutPrice?: SellingPlanCheckoutPrice; + [key: string]: unknown; +}; interface ProductDetailsProps { productId: string; @@ -27,6 +47,17 @@ interface ProductDetailsProps { clientId?: string; onAddToCartSuccess?: () => void; onAddToCartError?: (error: Error) => void; + /** Selected selling plan id (from dropdown). */ + selectedSellingPlanId?: string | null; + /** Full selected selling plan for display and cart metafield. */ + selectedSellingPlan?: SellingPlanSelection | null; + /** Extensibility slots โ€” keyed render functions injected at predefined locations. */ + targets?: Partial< + Record< + ProductDetailsTarget, + (props: { skuId: string | null; storeId: string | undefined }) => ReactNode + > + >; } // Flattened attribute structure for UI (transforms edges/node to flat array) @@ -124,6 +155,9 @@ export function ProductDetails({ clientId: clientIdProp, onAddToCartSuccess, onAddToCartError, + selectedSellingPlanId, + selectedSellingPlan, + targets, }: ProductDetailsProps) { const context = useGoDaddyContext(); const { t } = context; @@ -282,6 +316,9 @@ export function ProductDetails({ // Use individual SKU data if available, otherwise use SKU Group data const selectedSku = individualSkuData?.sku; + const resolvedSkuId = + selectedSku?.id ?? data?.skuGroup?.skus?.edges?.[0]?.node?.id ?? null; + // Track main carousel selection and sync thumbnail carousel useEffect(() => { if (!carouselApi) return; @@ -340,16 +377,42 @@ export function ProductDetails({ // Use SKU-specific pricing if available, otherwise fall back to SKU Group pricing const skuPrice = selectedSku?.prices?.edges?.[0]?.node; - const priceMin = skuPrice?.value?.value ?? product?.priceRange?.min ?? 0; - const priceMax = selectedSku - ? priceMin - : (product?.priceRange?.max ?? priceMin); + const catalogPriceMin = skuPrice?.value?.value ?? product?.priceRange?.min ?? 0; + const catalogPriceMax = selectedSku + ? catalogPriceMin + : (product?.priceRange?.max ?? catalogPriceMin); const compareAtMin = skuPrice?.compareAtValue?.value ?? product?.compareAtPriceRange?.min; const compareAtMax = selectedSku ? compareAtMin : product?.compareAtPriceRange?.max; - const isOnSale = compareAtMin && compareAtMin > priceMin; + + // When a selling plan is selected, use checkout price for the current SKU (from plan.checkoutPrice or plan.catalogPrices[skuId].checkoutPrices[0]) + const resolvedCheckoutPrice = (() => { + if (!selectedSellingPlan) return undefined; + const currentSkuId = selectedSku?.id; + const catalogPrices = selectedSellingPlan.catalogPrices as Array<{ skuId?: string; checkoutPrices?: Array<{ value?: number; currency?: string; currencyCode?: string }> }> | undefined; + if (currentSkuId && Array.isArray(catalogPrices)) { + const forSku = catalogPrices.find(c => c.skuId === currentSkuId); + const checkout = forSku?.checkoutPrices?.[0]; + if (checkout?.value != null) { + return { value: Number(checkout.value), currencyCode: checkout.currencyCode ?? checkout.currency }; + } + } + const cp = selectedSellingPlan.checkoutPrice; + if (cp?.value != null) return { value: Number(cp.value), currencyCode: cp.currencyCode }; + return undefined; + })(); + const sellingPlanCheckoutPrice = resolvedCheckoutPrice?.value; + const hasCheckoutPrice = sellingPlanCheckoutPrice != null; + const priceMin = hasCheckoutPrice ? sellingPlanCheckoutPrice : catalogPriceMin; + const priceMax = hasCheckoutPrice ? sellingPlanCheckoutPrice : catalogPriceMax; + const priceCurrency = (resolvedCheckoutPrice?.currencyCode as string) || 'USD'; + const compareAtWhenPlan = + hasCheckoutPrice ? catalogPriceMin : undefined; + const isOnSale = + (compareAtMin && compareAtMin > priceMin) || + (compareAtWhenPlan != null && compareAtWhenPlan > priceMin); const isPriceRange = priceMin !== priceMax; const isCompareAtPriceRange = compareAtMin && compareAtMax && compareAtMin !== compareAtMax; @@ -402,338 +465,364 @@ export function ProductDetails({ return; } + const skuId = selectedSku?.id || product?.skus?.edges?.[0]?.node?.id || ''; await addToCart({ - skuId: selectedSku?.id || product?.skus?.edges?.[0]?.node?.id || '', + skuId, name: title, quantity, productAssetUrl: images[0] || undefined, + ...(selectedSellingPlan && { + sellingPlanId: selectedSellingPlanId ?? selectedSellingPlan.planId, + sellingPlan: selectedSellingPlan, + }), }); }; return ( -
- {/* Product Images */} -
- {/* Main Image Carousel */} -
- {isOnSale && ( - + +
+ {/* Product Images */} +
+ + + {/* Main Image Carousel */} +
+ {isOnSale && ( + + {t.storefront.sale} + + )} + - {t.storefront.sale} - - )} - - - {images.length > 0 ? ( - images.map((image: string, index: number) => ( - - + + {images.length > 0 ? ( + images.map((image: string, index: number) => ( + + + {`${title} + + + )) + ) : ( + + +
+ {t.storefront.noImageAvailable} +
+
+
+ )} +
+ {images.length > 1 && ( + <> + + + + )} +
+
+ + {/* Thumbnail Grid or Carousel */} + {images.length > 1 && ( + <> + {images.length <= 4 ? ( + // Simple grid for 4 or fewer images +
+ {images.map((image: string, index: number) => ( + + ))} +
) : ( - - -
- {t.storefront.noImageAvailable} -
-
-
+ // Carousel for more than 4 images + + + {images.map((image: string, index: number) => ( + +
+ +
+
+ ))} +
+ + +
)} - - {images.length > 1 && ( - <> - - - - )} - + + )} + +
- {/* Thumbnail Grid or Carousel */} - {images.length > 1 && ( - <> - {images.length <= 4 ? ( - // Simple grid for 4 or fewer images -
- {images.map((image: string, index: number) => ( - - ))} -
- ) : ( - // Carousel for more than 4 images - - - {images.map((image: string, index: number) => ( - -
- -
-
- ))} -
- - -
- )} - - )} -
+ {/* Product Information */} +
+ +
+

{title}

- {/* Product Information */} -
-
-

{title}

- - {/* Price */} -
- - {isPriceRange - ? `${formatCurrency({ amount: priceMin, currencyCode: 'USD', inputInMinorUnits: true })} - ${formatCurrency({ amount: priceMax, currencyCode: 'USD', inputInMinorUnits: true })}` - : formatCurrency({ - amount: priceMin, - currencyCode: 'USD', - inputInMinorUnits: true, - })} - - {isOnSale && compareAtMin && ( - - {isCompareAtPriceRange - ? `${formatCurrency({ amount: compareAtMin, currencyCode: 'USD', inputInMinorUnits: true })} - ${formatCurrency({ amount: compareAtMax!, currencyCode: 'USD', inputInMinorUnits: true })}` + {/* Price */} +
+ + {isPriceRange + ? `${formatCurrency({ amount: priceMin, currencyCode: priceCurrency, inputInMinorUnits: true })} - ${formatCurrency({ amount: priceMax, currencyCode: priceCurrency, inputInMinorUnits: true })}` : formatCurrency({ - amount: compareAtMin, - currencyCode: 'USD', + amount: priceMin, + currencyCode: priceCurrency, inputInMinorUnits: true, })} - )} + {isOnSale && (compareAtMin || compareAtWhenPlan != null) && ( + + {isCompareAtPriceRange + ? `${formatCurrency({ amount: compareAtMin!, currencyCode: priceCurrency, inputInMinorUnits: true })} - ${formatCurrency({ amount: compareAtMax!, currencyCode: priceCurrency, inputInMinorUnits: true })}` + : formatCurrency({ + amount: compareAtWhenPlan ?? compareAtMin!, + currencyCode: priceCurrency, + inputInMinorUnits: true, + })} + + )} +
-
+ + + + {/* Description */} + {htmlDescription || description ? ( +
+ {htmlDescription ? ( +
+ ) : ( +

{description}

+ )} +
+ ) : null} + + + + {/* Product Attributes (Size, Color, etc.) */} + {attributes.length > 0 && ( +
+ {attributes.map(attribute => ( +
+ +
+ {attribute.values.map(value => ( + + ))} +
+
+ ))} + + {/* SKU Match Status */} + {selectedAttributeValues.length > 0 && ( +
+ {isSkuLoading && ( +
+
+ {t.storefront.loadingVariantDetails} +
+ )} + {!isSkuLoading && matchedSkus.length === 0 && ( +
+ {t.storefront.combinationNotAvailable} +
+ )} + {!isSkuLoading && matchedSkus.length > 1 && ( +
+ {matchedSkus.length} {t.storefront.variantsMatch} +
+ )} +
+ )} +
+ )} + - {/* Description */} - {htmlDescription || description ? ( + + {/* Quantity Selector */}
- {htmlDescription ? ( -
- ) : ( -

{description}

- )} -
- ) : null} - - {/* Product Attributes (Size, Color, etc.) */} - {attributes.length > 0 && ( -
- {attributes.map(attribute => ( -
- -
- {attribute.values.map(value => ( - - ))} -
-
- ))} - - {/* SKU Match Status */} - {selectedAttributeValues.length > 0 && ( -
- {isSkuLoading && ( -
-
- {t.storefront.loadingVariantDetails} -
- )} - {!isSkuLoading && matchedSkus.length === 0 && ( -
- {t.storefront.combinationNotAvailable} -
- )} - {!isSkuLoading && matchedSkus.length > 1 && ( -
- {matchedSkus.length} {t.storefront.variantsMatch} -
- )} -
- )} + +
+ + + {quantity} + + +
- )} + - {/* Quantity Selector */} -
- -
- - - {quantity} - - -
-
+ - {/* Add to Cart Button */} - + {/* Add to Cart Button */} + + - {/* Additional Product Information */} -
- {product?.type && ( -
- - {t.storefront.productType} - - - {product.type} - -
- )} - {product?.id && ( -
- - {t.storefront.productId} - - - {product.id} - -
- )} - {selectedSku && ( - <> + + {/* Additional Product Information */} +
+ {product?.type && (
- {t.storefront.selectedSku} + {t.storefront.productType} + + + {product.type} + +
+ )} + {product?.id && ( +
+ + {t.storefront.productId} - {selectedSku.code} + {product.id}
- {selectedSku.inventoryCounts?.edges && - selectedSku.inventoryCounts.edges.length > 0 && ( -
- - {t.storefront.stockStatus} - - - {(() => { - const availableCount = - selectedSku.inventoryCounts.edges.find( - edge => edge?.node?.type === 'AVAILABLE' - )?.node?.quantity ?? 0; - if (availableCount === 0) - return t.storefront.outOfStock; - if (availableCount < 10) - return `${t.storefront.lowStock} (${availableCount})`; - return t.storefront.inStock; - })()} - -
- )} - - )} + )} + {selectedSku && ( + <> +
+ + {t.storefront.selectedSku} + + + {selectedSku.code} + +
+ {selectedSku.inventoryCounts?.edges && + selectedSku.inventoryCounts.edges.length > 0 && ( +
+ + {t.storefront.stockStatus} + + + {(() => { + const availableCount = + selectedSku.inventoryCounts.edges.find( + edge => edge?.node?.type === 'AVAILABLE' + )?.node?.quantity ?? 0; + if (availableCount === 0) + return t.storefront.outOfStock; + if (availableCount < 10) + return `${t.storefront.lowStock} (${availableCount})`; + return t.storefront.inStock; + })()} + +
+ )} + + )} +
+
-
+ + ); } diff --git a/packages/react/src/components/storefront/targets/product-details-target.tsx b/packages/react/src/components/storefront/targets/product-details-target.tsx new file mode 100644 index 00000000..0ca6eefd --- /dev/null +++ b/packages/react/src/components/storefront/targets/product-details-target.tsx @@ -0,0 +1,53 @@ +'use client'; + +import { useGoDaddyContext } from '@/godaddy-provider'; +import { cn } from '@/lib/utils'; +import { useProductDetailsContext } from '../contexts/product-details-context'; + +export type ProductDetailsTarget = + | 'product-details.before' + | 'product-details.after' + | 'product-details.media.before' + | 'product-details.media.after' + | 'product-details.title.before' + | 'product-details.title.after' + | 'product-details.description.before' + | 'product-details.description.after' + | 'product-details.attributes.before' + | 'product-details.attributes.after' + | 'product-details.quantity.before' + | 'product-details.quantity.after' + | 'product-details.add-to-cart.before' + | 'product-details.add-to-cart.after' + | 'product-details.metadata.before' + | 'product-details.metadata.after'; + +export function ProductDetailsTargetSlot({ + id, +}: { + id: ProductDetailsTarget; +}) { + const { debug, storeId } = useGoDaddyContext(); + const { targets, skuId } = useProductDetailsContext(); + + const target = targets?.[id]; + + let content: React.ReactNode = null; + if (target) { + content = target({ skuId, storeId }); + } else if (debug) { + content = {id}; + } + + return ( +
+ {content} +
+ ); +} diff --git a/packages/react/src/lib/godaddy/checkout-env.ts b/packages/react/src/lib/godaddy/checkout-env.ts index c0c4afc2..13d8a695 100644 --- a/packages/react/src/lib/godaddy/checkout-env.ts +++ b/packages/react/src/lib/godaddy/checkout-env.ts @@ -3108,28 +3108,28 @@ const introspection = { }, }, { - name: 'offline', + name: 'mercadopago', type: { kind: 'INPUT_OBJECT', name: 'CheckoutSessionPaymentMethodConfigInput', }, }, { - name: 'paypal', + name: 'offline', type: { kind: 'INPUT_OBJECT', name: 'CheckoutSessionPaymentMethodConfigInput', }, }, { - name: 'paze', + name: 'paypal', type: { kind: 'INPUT_OBJECT', name: 'CheckoutSessionPaymentMethodConfigInput', }, }, { - name: 'mercadopago', + name: 'paze', type: { kind: 'INPUT_OBJECT', name: 'CheckoutSessionPaymentMethodConfigInput', @@ -3297,6 +3297,15 @@ const introspection = { args: [], isDeprecated: false, }, + { + name: 'pickupSlotInterval', + type: { + kind: 'SCALAR', + name: 'Int', + }, + args: [], + isDeprecated: false, + }, { name: 'pickupWindowInDays', type: { @@ -3349,12 +3358,22 @@ const introspection = { }, }, { - name: 'pickupWindowInDays', + name: 'pickupSlotInterval', type: { kind: 'SCALAR', name: 'Int', }, }, + { + name: 'pickupWindowInDays', + type: { + kind: 'NON_NULL', + ofType: { + kind: 'SCALAR', + name: 'Int', + }, + }, + }, { name: 'timeZone', type: { diff --git a/packages/react/src/lib/godaddy/checkout-mutations.ts b/packages/react/src/lib/godaddy/checkout-mutations.ts index e3e77f19..6ff26d1b 100644 --- a/packages/react/src/lib/godaddy/checkout-mutations.ts +++ b/packages/react/src/lib/godaddy/checkout-mutations.ts @@ -143,6 +143,7 @@ export const CreateCheckoutSessionMutation = graphql(` operatingHours { pickupWindowInDays leadTime + pickupSlotInterval timeZone hours { monday { @@ -186,6 +187,7 @@ export const CreateCheckoutSessionMutation = graphql(` defaultOperatingHours { pickupWindowInDays leadTime + pickupSlotInterval timeZone hours { monday { diff --git a/packages/react/src/lib/godaddy/checkout-queries.ts b/packages/react/src/lib/godaddy/checkout-queries.ts index e463a8c3..1cceb93a 100644 --- a/packages/react/src/lib/godaddy/checkout-queries.ts +++ b/packages/react/src/lib/godaddy/checkout-queries.ts @@ -131,6 +131,7 @@ export const GetCheckoutSessionQuery = graphql(` operatingHours { pickupWindowInDays leadTime + pickupSlotInterval timeZone hours { monday { @@ -174,6 +175,7 @@ export const GetCheckoutSessionQuery = graphql(` defaultOperatingHours { pickupWindowInDays leadTime + pickupSlotInterval timeZone hours { monday { diff --git a/packages/react/src/lib/godaddy/orders-storefront-mutations.ts b/packages/react/src/lib/godaddy/orders-storefront-mutations.ts index f485ce0f..9c10377d 100644 --- a/packages/react/src/lib/godaddy/orders-storefront-mutations.ts +++ b/packages/react/src/lib/godaddy/orders-storefront-mutations.ts @@ -134,6 +134,11 @@ export const AddLineItemBySkuIdMutation = graphql(` } createdAt updatedAt + metafields { + key + type + value + } } } `); diff --git a/packages/react/src/lib/godaddy/orders-storefront-queries.ts b/packages/react/src/lib/godaddy/orders-storefront-queries.ts index 4a4dcf4c..2f482021 100644 --- a/packages/react/src/lib/godaddy/orders-storefront-queries.ts +++ b/packages/react/src/lib/godaddy/orders-storefront-queries.ts @@ -18,6 +18,11 @@ export const GetCartOrderQuery = graphql(` skuId type fulfillmentMode + metafields { + key + type + value + } details { productAssetUrl sku diff --git a/packages/react/vitest.config.ts b/packages/react/vitest.config.ts index d2fa8286..da9e9c45 100644 --- a/packages/react/vitest.config.ts +++ b/packages/react/vitest.config.ts @@ -1,7 +1,6 @@ import path from 'node:path'; import react from '@vitejs/plugin-react'; -/// -import { defineConfig } from 'vite'; +import { defineConfig } from 'vitest/config'; // https://vitejs.dev/config/ export default defineConfig({ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d9b9d625..1b583867 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,32 +12,32 @@ importers: specifier: ^2.29.5 version: 2.29.6(@types/node@22.18.12) '@eslint/eslintrc': - specifier: ^3.3.1 - version: 3.3.1 + specifier: ^3.3.4 + version: 3.3.4 '@eslint/js': - specifier: ^9.24.0 - version: 9.34.0 + specifier: ^10.0.0 + version: 10.0.0 '@typescript-eslint/eslint-plugin': - specifier: ^8.35.0 - version: 8.41.0(@typescript-eslint/parser@8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2))(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2) + specifier: ^8.54.0 + version: 8.56.1(@typescript-eslint/parser@8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.2))(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.2) '@typescript-eslint/parser': - specifier: ^8.30.1 - version: 8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2) + specifier: ^8.54.0 + version: 8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.2) eslint: - specifier: ^9.24.0 - version: 9.34.0(jiti@2.6.1) + specifier: ^10.0.2 + version: 10.0.2(jiti@2.6.1) eslint-plugin-json: specifier: ^4.0.1 version: 4.0.1 eslint-plugin-jsx-a11y: specifier: ^6.10.2 - version: 6.10.2(eslint@9.34.0(jiti@2.6.1)) + version: 6.10.2(eslint@10.0.2(jiti@2.6.1)) eslint-plugin-mocha: specifier: ^10.5.0 - version: 10.5.0(eslint@9.34.0(jiti@2.6.1)) + version: 10.5.0(eslint@10.0.2(jiti@2.6.1)) eslint-plugin-react: specifier: ^7.37.5 - version: 7.37.5(eslint@9.34.0(jiti@2.6.1)) + version: 7.37.5(eslint@10.0.2(jiti@2.6.1)) react: specifier: ^19.2.0 version: 19.2.0 @@ -66,8 +66,8 @@ importers: specifier: ^0.475.0 version: 0.475.0(react@19.2.0) next: - specifier: 16.0.10 - version: 16.0.10(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + specifier: 16.2.1 + version: 16.2.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react: specifier: 19.2.0 version: 19.2.0 @@ -110,7 +110,7 @@ importers: version: 1.133.22(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@tanstack/react-start': specifier: ^1.127.9 - version: 1.133.22(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@5.4.21(@types/node@22.18.12)(lightningcss@1.30.2)) + version: 1.133.22(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)) pino: specifier: ^9.7.0 version: 9.14.0 @@ -140,8 +140,8 @@ importers: specifier: ~5.7.3 version: 5.7.3 vitest: - specifier: ^1.2.2 - version: 1.6.1(@types/node@22.18.12)(jsdom@26.1.0)(lightningcss@1.30.2) + specifier: ^4.1.2 + version: 4.1.2(@types/node@22.18.12)(jsdom@26.1.0)(vite@6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)) packages/biome-config-godaddy: devDependencies: @@ -151,25 +151,25 @@ importers: packages/eslint-config-godaddy: dependencies: + '@eslint/js': + specifier: ^10.0.0 + version: 10.0.0 eslint-plugin-jsdoc: specifier: ^50.6.9 - version: 50.8.0(eslint@9.34.0(jiti@2.6.1)) + version: 50.8.0(eslint@10.0.2(jiti@2.6.1)) eslint-plugin-json: specifier: ^4.0.1 version: 4.0.1 eslint-plugin-mocha: specifier: ^10.5.0 - version: 10.5.0(eslint@9.34.0(jiti@2.6.1)) + version: 10.5.0(eslint@10.0.2(jiti@2.6.1)) globals: specifier: ^16.0.0 version: 16.3.0 devDependencies: - '@eslint/js': - specifier: ^9.24.0 - version: 9.34.0 eslint: - specifier: ^9.24.0 - version: 9.34.0(jiti@2.6.1) + specifier: ^10.0.0 + version: 10.0.2(jiti@2.6.1) packages/eslint-config-godaddy-react: dependencies: @@ -177,55 +177,58 @@ importers: specifier: '>=7' version: 7.28.3 '@babel/eslint-parser': - specifier: ^7.27.0 - version: 7.28.0(@babel/core@7.28.3)(eslint@9.34.0(jiti@2.6.1)) + specifier: ^7.28.6 + version: 7.28.6(@babel/core@7.28.3)(eslint@10.0.2(jiti@2.6.1)) + '@eslint/compat': + specifier: ^2.0.2 + version: 2.0.2(eslint@10.0.2(jiti@2.6.1)) eslint-config-godaddy: specifier: workspace:^ version: link:../eslint-config-godaddy eslint-plugin-jsx-a11y: specifier: ^6.10.2 - version: 6.10.2(eslint@9.34.0(jiti@2.6.1)) + version: 6.10.2(eslint@10.0.2(jiti@2.6.1)) eslint-plugin-react: specifier: ^7.37.5 - version: 7.37.5(eslint@9.34.0(jiti@2.6.1)) + version: 7.37.5(eslint@10.0.2(jiti@2.6.1)) eslint-plugin-react-hooks: specifier: ^5.2.0 - version: 5.2.0(eslint@9.34.0(jiti@2.6.1)) + version: 5.2.0(eslint@10.0.2(jiti@2.6.1)) devDependencies: eslint: - specifier: ^9.24.0 - version: 9.34.0(jiti@2.6.1) + specifier: ^10.0.2 + version: 10.0.2(jiti@2.6.1) packages/eslint-config-godaddy-react-typescript: dependencies: '@typescript-eslint/eslint-plugin': - specifier: ^8.35.0 - version: 8.41.0(@typescript-eslint/parser@8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2))(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2) + specifier: ^8.54.0 + version: 8.56.1(@typescript-eslint/parser@8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.2))(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.2) '@typescript-eslint/parser': - specifier: ^8.30.1 - version: 8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2) + specifier: ^8.54.0 + version: 8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.2) eslint-config-godaddy-react: specifier: workspace:^ version: link:../eslint-config-godaddy-react eslint-plugin-react-hooks: specifier: ^5.2.0 - version: 5.2.0(eslint@9.34.0(jiti@2.6.1)) + version: 5.2.0(eslint@10.0.2(jiti@2.6.1)) typescript: specifier: '>=5' version: 5.9.2 devDependencies: eslint: - specifier: ^9.24.0 - version: 9.34.0(jiti@2.6.1) + specifier: ^10.0.2 + version: 10.0.2(jiti@2.6.1) packages/eslint-config-godaddy-typescript: dependencies: '@typescript-eslint/eslint-plugin': - specifier: ^8.35.0 - version: 8.41.0(@typescript-eslint/parser@8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2))(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2) + specifier: ^8.54.0 + version: 8.56.1(@typescript-eslint/parser@8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.2))(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.2) '@typescript-eslint/parser': - specifier: ^8.30.1 - version: 8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2) + specifier: ^8.54.0 + version: 8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.2) eslint-config-godaddy: specifier: workspace:^ version: link:../eslint-config-godaddy @@ -234,8 +237,8 @@ importers: version: 5.9.2 devDependencies: eslint: - specifier: ^9.24.0 - version: 9.34.0(jiti@2.6.1) + specifier: ^10.0.2 + version: 10.0.2(jiti@2.6.1) packages/localizations: devDependencies: @@ -327,17 +330,17 @@ importers: specifier: ^1.1.8 version: 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@stripe/react-stripe-js': - specifier: ^3.7.0 - version: 3.10.0(@stripe/stripe-js@7.9.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + specifier: ^5.6.1 + version: 5.6.1(@stripe/stripe-js@8.11.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@stripe/stripe-js': - specifier: ^7.3.1 - version: 7.9.0 + specifier: ^8.11.0 + version: 8.11.0 '@tailwindcss/cli': specifier: ^4.1.10 version: 4.1.15 '@tailwindcss/vite': specifier: ^4.1.4 - version: 4.1.15(vite@5.4.21(@types/node@22.18.12)(lightningcss@1.30.2)) + version: 4.1.15(vite@6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)) '@tanstack/react-pacer': specifier: ^0.2.0 version: 0.2.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -415,8 +418,8 @@ importers: specifier: ^19.0.3 version: 19.2.2(@types/react@19.2.2) '@vitejs/plugin-react': - specifier: ^4.2.1 - version: 4.7.0(vite@5.4.21(@types/node@22.18.12)(lightningcss@1.30.2)) + specifier: ^4.7.0 + version: 4.7.0(vite@6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)) biome-config-godaddy: specifier: workspace:* version: link:../biome-config-godaddy @@ -442,11 +445,11 @@ importers: specifier: ~5.7.3 version: 5.7.3 vite: - specifier: ^5.1.6 - version: 5.4.21(@types/node@22.18.12)(lightningcss@1.30.2) + specifier: ^6.4.1 + version: 6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6) vitest: - specifier: ^3.0.6 - version: 3.2.4(@types/node@22.18.12)(jsdom@26.1.0)(lightningcss@1.30.2) + specifier: ^4.1.2 + version: 4.1.2(@types/node@22.18.12)(jsdom@26.1.0)(vite@6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)) packages: @@ -491,8 +494,8 @@ packages: resolution: {integrity: sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==} engines: {node: '>=6.9.0'} - '@babel/eslint-parser@7.28.0': - resolution: {integrity: sha512-N4ntErOlKvcbTt01rr5wj3y55xnIdx1ymrfIr8C2WnM1Y9glFgWaGDEULJIazOX3XM9NRzhfJ6zZnQ1sBNWU+w==} + '@babel/eslint-parser@7.28.6': + resolution: {integrity: sha512-QGmsKi2PBO/MHSQk+AAgA9R6OHQr+VqnniFE0eMWZcVcfBZoA2dKn2hUsl3Csg/Plt9opRUWdY7//VXsrIlEiA==} engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0} peerDependencies: '@babel/core': ^7.11.0 @@ -888,6 +891,9 @@ packages: '@emnapi/runtime@1.6.0': resolution: {integrity: sha512-obtUmAHTMjll499P+D9A3axeJFlhdjOWdKUNs/U6QIGT7V5RjcUW1xToAzjvmgTSQhDbYn/NwfTRoJcQ2rNBxA==} + '@emnapi/runtime@1.9.1': + resolution: {integrity: sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==} + '@emnapi/wasi-threads@1.1.0': resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} @@ -895,204 +901,102 @@ packages: resolution: {integrity: sha512-YAdE/IJSpwbOTiaURNCKECdAwqrJuFiZhylmesBcIRawtYKnBR2wxPhoIewMg+Yu+QuYvHfJNReWpoxGBKOChA==} engines: {node: '>=18'} - '@esbuild/aix-ppc64@0.21.5': - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [aix] - '@esbuild/aix-ppc64@0.25.11': resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.21.5': - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - '@esbuild/android-arm64@0.25.11': resolution: {integrity: sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.21.5': - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - '@esbuild/android-arm@0.25.11': resolution: {integrity: sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.21.5': - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - '@esbuild/android-x64@0.25.11': resolution: {integrity: sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.21.5': - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - '@esbuild/darwin-arm64@0.25.11': resolution: {integrity: sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.21.5': - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - '@esbuild/darwin-x64@0.25.11': resolution: {integrity: sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.21.5': - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - '@esbuild/freebsd-arm64@0.25.11': resolution: {integrity: sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.21.5': - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - '@esbuild/freebsd-x64@0.25.11': resolution: {integrity: sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.21.5': - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - '@esbuild/linux-arm64@0.25.11': resolution: {integrity: sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.21.5': - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - '@esbuild/linux-arm@0.25.11': resolution: {integrity: sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.21.5': - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - '@esbuild/linux-ia32@0.25.11': resolution: {integrity: sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.21.5': - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - '@esbuild/linux-loong64@0.25.11': resolution: {integrity: sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.21.5': - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - '@esbuild/linux-mips64el@0.25.11': resolution: {integrity: sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.21.5': - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - '@esbuild/linux-ppc64@0.25.11': resolution: {integrity: sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.21.5': - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - '@esbuild/linux-riscv64@0.25.11': resolution: {integrity: sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.21.5': - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - '@esbuild/linux-s390x@0.25.11': resolution: {integrity: sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.21.5': - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - '@esbuild/linux-x64@0.25.11': resolution: {integrity: sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==} engines: {node: '>=18'} @@ -1105,12 +1009,6 @@ packages: cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.21.5': - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - '@esbuild/netbsd-x64@0.25.11': resolution: {integrity: sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==} engines: {node: '>=18'} @@ -1123,12 +1021,6 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.21.5': - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - '@esbuild/openbsd-x64@0.25.11': resolution: {integrity: sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==} engines: {node: '>=18'} @@ -1141,91 +1033,77 @@ packages: cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.21.5': - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - '@esbuild/sunos-x64@0.25.11': resolution: {integrity: sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.21.5': - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - '@esbuild/win32-arm64@0.25.11': resolution: {integrity: sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.21.5': - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - '@esbuild/win32-ia32@0.25.11': resolution: {integrity: sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.21.5': - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - '@esbuild/win32-x64@0.25.11': resolution: {integrity: sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==} engines: {node: '>=18'} cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.7.0': - resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/regexpp@4.12.1': - resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/config-array@0.21.0': - resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/compat@2.0.2': + resolution: {integrity: sha512-pR1DoD0h3HfF675QZx0xsyrsU8q70Z/plx7880NOhS02NuWLgBCOMDL787nUeQ7EWLkxv3bPQJaarjcPQb2Dwg==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + peerDependencies: + eslint: ^8.40 || 9 || 10 + peerDependenciesMeta: + eslint: + optional: true - '@eslint/config-helpers@0.3.1': - resolution: {integrity: sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/config-array@0.23.2': + resolution: {integrity: sha512-YF+fE6LV4v5MGWRGj7G404/OZzGNepVF8fxk7jqmqo3lrza7a0uUcDnROGRBG1WFC1omYUS/Wp1f42i0M+3Q3A==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/core@0.15.2': - resolution: {integrity: sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/config-helpers@0.5.2': + resolution: {integrity: sha512-a5MxrdDXEvqnIq+LisyCX6tQMPF/dSJpCfBgBauY+pNZ28yCtSsTvyTYrMhaI+LK26bVyCJfJkT0u8KIj2i1dQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/eslintrc@3.3.1': - resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/core@1.1.0': + resolution: {integrity: sha512-/nr9K9wkr3P1EzFTdFdMoLuo1PmIxjmwvPozwoSodjNBdefGujXQUF93u1DDZpEaTuDvMsIQddsd35BwtrW9Xw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/js@9.34.0': - resolution: {integrity: sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==} + '@eslint/eslintrc@3.3.4': + resolution: {integrity: sha512-4h4MVF8pmBsncB60r0wSJiIeUKTSD4m7FmTFThG8RHlsg9ajqckLm9OraguFGZE4vVdpiI1Q4+hFnisopmG6gQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/object-schema@2.1.6': - resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + '@eslint/js@10.0.0': + resolution: {integrity: sha512-J2VKrn6YUBegZFzRQVOVA8jk7VViV/MXhQUvXuCUK1I2RGGT2E+bmNGOsgtdO9Wo25Kd3hfZFuCm8LcfsUFV7g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + deprecated: This version should not be used. - '@eslint/plugin-kit@0.3.5': - resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/object-schema@3.0.2': + resolution: {integrity: sha512-HOy56KJt48Bx8KmJ+XGQNSUMT/6dZee/M54XyUyuvTvPXJmsERRvBchsUVx1UMe1WwIH49XLAczNC7V2INsuUw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/plugin-kit@0.6.0': + resolution: {integrity: sha512-bIZEUzOI1jkhviX2cp5vNyXQc6olzb2ohewQubuYlMXZ2Q/XjBO0x0XhGPvc9fjSIiUN0vw+0hq53BJ4eQSJKQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} '@floating-ui/core@1.7.3': resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} @@ -1302,124 +1180,135 @@ packages: resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} engines: {node: '>=18'} - '@img/sharp-darwin-arm64@0.34.4': - resolution: {integrity: sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA==} + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [darwin] - '@img/sharp-darwin-x64@0.34.4': - resolution: {integrity: sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg==} + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [darwin] - '@img/sharp-libvips-darwin-arm64@1.2.3': - resolution: {integrity: sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw==} + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.2.3': - resolution: {integrity: sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA==} + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} cpu: [x64] os: [darwin] - '@img/sharp-libvips-linux-arm64@1.2.3': - resolution: {integrity: sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==} + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] - '@img/sharp-libvips-linux-arm@1.2.3': - resolution: {integrity: sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==} + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] - '@img/sharp-libvips-linux-ppc64@1.2.3': - resolution: {integrity: sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==} + '@img/sharp-libvips-linux-ppc64@1.2.4': + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] - '@img/sharp-libvips-linux-s390x@1.2.3': - resolution: {integrity: sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==} + '@img/sharp-libvips-linux-riscv64@1.2.4': + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + cpu: [riscv64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.2.4': + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] - '@img/sharp-libvips-linux-x64@1.2.3': - resolution: {integrity: sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg==} + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] - '@img/sharp-libvips-linuxmusl-arm64@1.2.3': - resolution: {integrity: sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==} + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] - '@img/sharp-libvips-linuxmusl-x64@1.2.3': - resolution: {integrity: sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==} + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] - '@img/sharp-linux-arm64@0.34.4': - resolution: {integrity: sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==} + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - '@img/sharp-linux-arm@0.34.4': - resolution: {integrity: sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==} + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] - '@img/sharp-linux-ppc64@0.34.4': - resolution: {integrity: sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ==} + '@img/sharp-linux-ppc64@0.34.5': + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] - '@img/sharp-linux-s390x@0.34.4': - resolution: {integrity: sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==} + '@img/sharp-linux-riscv64@0.34.5': + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [riscv64] + os: [linux] + + '@img/sharp-linux-s390x@0.34.5': + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] - '@img/sharp-linux-x64@0.34.4': - resolution: {integrity: sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A==} + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - '@img/sharp-linuxmusl-arm64@0.34.4': - resolution: {integrity: sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==} + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - '@img/sharp-linuxmusl-x64@0.34.4': - resolution: {integrity: sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==} + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - '@img/sharp-wasm32@0.34.4': - resolution: {integrity: sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==} + '@img/sharp-wasm32@0.34.5': + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] - '@img/sharp-win32-arm64@0.34.4': - resolution: {integrity: sha512-2Q250do/5WXTwxW3zjsEuMSv5sUU4Tq9VThWKlU2EYLm4MB7ZeMwF+SFJutldYODXF6jzc6YEOC+VfX0SZQPqA==} + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [win32] - '@img/sharp-win32-ia32@0.34.4': - resolution: {integrity: sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw==} + '@img/sharp-win32-ia32@0.34.5': + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ia32] os: [win32] - '@img/sharp-win32-x64@0.34.4': - resolution: {integrity: sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig==} + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [win32] @@ -1433,22 +1322,10 @@ packages: '@types/node': optional: true - '@isaacs/balanced-match@4.0.1': - resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} - engines: {node: 20 || >=22} - - '@isaacs/brace-expansion@5.0.0': - resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} - engines: {node: 20 || >=22} - '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} - '@jest/schemas@29.6.3': - resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -1474,53 +1351,53 @@ packages: '@napi-rs/wasm-runtime@1.0.7': resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} - '@next/env@16.0.10': - resolution: {integrity: sha512-8tuaQkyDVgeONQ1MeT9Mkk8pQmZapMKFh5B+OrFUlG3rVmYTXcXlBetBgTurKXGaIZvkoqRT9JL5K3phXcgang==} + '@next/env@16.2.1': + resolution: {integrity: sha512-n8P/HCkIWW+gVal2Z8XqXJ6aB3J0tuM29OcHpCsobWlChH/SITBs1DFBk/HajgrwDkqqBXPbuUuzgDvUekREPg==} - '@next/swc-darwin-arm64@16.0.10': - resolution: {integrity: sha512-4XgdKtdVsaflErz+B5XeG0T5PeXKDdruDf3CRpnhN+8UebNa5N2H58+3GDgpn/9GBurrQ1uWW768FfscwYkJRg==} + '@next/swc-darwin-arm64@16.2.1': + resolution: {integrity: sha512-BwZ8w8YTaSEr2HIuXLMLxIdElNMPvY9fLqb20LX9A9OMGtJilhHLbCL3ggyd0TwjmMcTxi0XXt+ur1vWUoxj2Q==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@16.0.10': - resolution: {integrity: sha512-spbEObMvRKkQ3CkYVOME+ocPDFo5UqHb8EMTS78/0mQ+O1nqE8toHJVioZo4TvebATxgA8XMTHHrScPrn68OGw==} + '@next/swc-darwin-x64@16.2.1': + resolution: {integrity: sha512-/vrcE6iQSJq3uL3VGVHiXeaKbn8Es10DGTGRJnRZlkNQQk3kaNtAJg8Y6xuAlrx/6INKVjkfi5rY0iEXorZ6uA==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@16.0.10': - resolution: {integrity: sha512-uQtWE3X0iGB8apTIskOMi2w/MKONrPOUCi5yLO+v3O8Mb5c7K4Q5KD1jvTpTF5gJKa3VH/ijKjKUq9O9UhwOYw==} + '@next/swc-linux-arm64-gnu@16.2.1': + resolution: {integrity: sha512-uLn+0BK+C31LTVbQ/QU+UaVrV0rRSJQ8RfniQAHPghDdgE+SlroYqcmFnO5iNjNfVWCyKZHYrs3Nl0mUzWxbBw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@16.0.10': - resolution: {integrity: sha512-llA+hiDTrYvyWI21Z0L1GiXwjQaanPVQQwru5peOgtooeJ8qx3tlqRV2P7uH2pKQaUfHxI/WVarvI5oYgGxaTw==} + '@next/swc-linux-arm64-musl@16.2.1': + resolution: {integrity: sha512-ssKq6iMRnHdnycGp9hCuGnXJZ0YPr4/wNwrfE5DbmvEcgl9+yv97/Kq3TPVDfYome1SW5geciLB9aiEqKXQjlQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@16.0.10': - resolution: {integrity: sha512-AK2q5H0+a9nsXbeZ3FZdMtbtu9jxW4R/NgzZ6+lrTm3d6Zb7jYrWcgjcpM1k8uuqlSy4xIyPR2YiuUr+wXsavA==} + '@next/swc-linux-x64-gnu@16.2.1': + resolution: {integrity: sha512-HQm7SrHRELJ30T1TSmT706IWovFFSRGxfgUkyWJZF/RKBMdbdRWJuFrcpDdE5vy9UXjFOx6L3mRdqH04Mmx0hg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@16.0.10': - resolution: {integrity: sha512-1TDG9PDKivNw5550S111gsO4RGennLVl9cipPhtkXIFVwo31YZ73nEbLjNC8qG3SgTz/QZyYyaFYMeY4BKZR/g==} + '@next/swc-linux-x64-musl@16.2.1': + resolution: {integrity: sha512-aV2iUaC/5HGEpbBkE+4B8aHIudoOy5DYekAKOMSHoIYQ66y/wIVeaRx8MS2ZMdxe/HIXlMho4ubdZs/J8441Tg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@16.0.10': - resolution: {integrity: sha512-aEZIS4Hh32xdJQbHz121pyuVZniSNoqDVx1yIr2hy+ZwJGipeqnMZBJHyMxv2tiuAXGx6/xpTcQJ6btIiBjgmg==} + '@next/swc-win32-arm64-msvc@16.2.1': + resolution: {integrity: sha512-IXdNgiDHaSk0ZUJ+xp0OQTdTgnpx1RCfRTalhn3cjOP+IddTMINwA7DXZrwTmGDO8SUr5q2hdP/du4DcrB1GxA==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@16.0.10': - resolution: {integrity: sha512-E+njfCoFLb01RAFEnGZn6ERoOqhK1Gl3Lfz1Kjnj0Ulfu7oJbuMyvBKNj/bw8XZnenHDASlygTjZICQW+rYW1Q==} + '@next/swc-win32-x64-msvc@16.2.1': + resolution: {integrity: sha512-qvU+3a39Hay+ieIztkGSbF7+mccbbg1Tk25hc4JDylf8IHjYmY/Zm64Qq1602yPyQqvie+vf5T/uPwNxDNIoeg==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -2531,21 +2408,21 @@ packages: cpu: [x64] os: [win32] - '@sinclair/typebox@0.27.8': - resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} '@standard-schema/utils@0.3.0': resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} - '@stripe/react-stripe-js@3.10.0': - resolution: {integrity: sha512-UPqHZwMwDzGSax0ZI7XlxR3tZSpgIiZdk3CiwjbTK978phwR/fFXeAXQcN/h8wTAjR4ZIAzdlI9DbOqJhuJdeg==} + '@stripe/react-stripe-js@5.6.1': + resolution: {integrity: sha512-5xBrjkGmFvKvpMod6VvpOaFaa67eRbmieKeFTePZyOr/sUXzm7A3YY91l330pS0usUst5PxTZDUZHWfOc0v1GA==} peerDependencies: - '@stripe/stripe-js': '>=1.44.1 <8.0.0' + '@stripe/stripe-js': '>=8.0.0 <9.0.0' react: '>=16.8.0 <20.0.0' react-dom: '>=16.8.0 <20.0.0' - '@stripe/stripe-js@7.9.0': - resolution: {integrity: sha512-ggs5k+/0FUJcIgNY08aZTqpBTtbExkJMYMLSMwyucrhtWexVOEY1KJmhBsxf+E/Q15f5rbwBpj+t0t2AW2oCsQ==} + '@stripe/stripe-js@8.11.0': + resolution: {integrity: sha512-3fVF4z3efsgwgyj64nFK+6F4/vMw0mUXD2TBbOfftJtKVNx4JNv3CSfe1fY4DCtCk0JFp8/YPNcRkzgV0HJ8cg==} engines: {node: '>=12.16'} '@swc/helpers@0.5.15': @@ -2895,6 +2772,9 @@ packages: '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/esrecurse@4.3.1': + resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -2945,63 +2825,67 @@ packages: '@types/serve-static@1.15.9': resolution: {integrity: sha512-dOTIuqpWLyl3BBXU3maNQsS4A3zuuoYRNIvYSxxhebPfXg2mzWQEPne/nlJ37yOse6uGgR386uTpdsx4D0QZWA==} - '@typescript-eslint/eslint-plugin@8.41.0': - resolution: {integrity: sha512-8fz6oa6wEKZrhXWro/S3n2eRJqlRcIa6SlDh59FXJ5Wp5XRZ8B9ixpJDcjadHq47hMx0u+HW6SNa6LjJQ6NLtw==} + '@typescript-eslint/eslint-plugin@8.56.1': + resolution: {integrity: sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.41.0 - eslint: ^8.57.0 || ^9.0.0 + '@typescript-eslint/parser': ^8.56.1 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.41.0': - resolution: {integrity: sha512-gTtSdWX9xiMPA/7MV9STjJOOYtWwIJIYxkQxnSV1U3xcE+mnJSH3f6zI0RYP+ew66WSlZ5ed+h0VCxsvdC1jJg==} + '@typescript-eslint/parser@8.56.1': + resolution: {integrity: sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.41.0': - resolution: {integrity: sha512-b8V9SdGBQzQdjJ/IO3eDifGpDBJfvrNTp2QD9P2BeqWTGrRibgfgIlBSw6z3b6R7dPzg752tOs4u/7yCLxksSQ==} + '@typescript-eslint/project-service@8.56.1': + resolution: {integrity: sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.41.0': - resolution: {integrity: sha512-n6m05bXn/Cd6DZDGyrpXrELCPVaTnLdPToyhBoFkLIMznRUQUEQdSp96s/pcWSQdqOhrgR1mzJ+yItK7T+WPMQ==} + '@typescript-eslint/scope-manager@8.56.1': + resolution: {integrity: sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.41.0': - resolution: {integrity: sha512-TDhxYFPUYRFxFhuU5hTIJk+auzM/wKvWgoNYOPcOf6i4ReYlOoYN8q1dV5kOTjNQNJgzWN3TUUQMtlLOcUgdUw==} + '@typescript-eslint/tsconfig-utils@8.56.1': + resolution: {integrity: sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.41.0': - resolution: {integrity: sha512-63qt1h91vg3KsjVVonFJWjgSK7pZHSQFKH6uwqxAH9bBrsyRhO6ONoKyXxyVBzG1lJnFAJcKAcxLS54N1ee1OQ==} + '@typescript-eslint/type-utils@8.56.1': + resolution: {integrity: sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.0.0' '@typescript-eslint/types@8.41.0': resolution: {integrity: sha512-9EwxsWdVqh42afLbHP90n2VdHaWU/oWgbH2P0CfcNfdKL7CuKpwMQGjwev56vWu9cSKU7FWSu6r9zck6CVfnag==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.41.0': - resolution: {integrity: sha512-D43UwUYJmGhuwHfY7MtNKRZMmfd8+p/eNSfFe6tH5mbVDto+VQCayeAt35rOx3Cs6wxD16DQtIKw/YXxt5E0UQ==} + '@typescript-eslint/types@8.56.1': + resolution: {integrity: sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.56.1': + resolution: {integrity: sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.41.0': - resolution: {integrity: sha512-udbCVstxZ5jiPIXrdH+BZWnPatjlYwJuJkDA4Tbo3WyYLh8NvB+h/bKeSZHDOFKfphsZYJQqaFtLeXEqurQn1A==} + '@typescript-eslint/utils@8.56.1': + resolution: {integrity: sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.41.0': - resolution: {integrity: sha512-+GeGMebMCy0elMNg67LRNoVnUFPIm37iu5CmHESVx56/9Jsfdpsvbv605DQ81Pi/x11IdKUsS5nzgTYbCQU9fg==} + '@typescript-eslint/visitor-keys@8.56.1': + resolution: {integrity: sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@vitejs/plugin-react@4.7.0': @@ -3010,61 +2894,42 @@ packages: peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 - '@vitest/expect@1.6.1': - resolution: {integrity: sha512-jXL+9+ZNIJKruofqXuuTClf44eSpcHlgj3CiuNihUF3Ioujtmc0zIa3UJOW5RjDK1YLBJZnWBlPuqhYycLioog==} - - '@vitest/expect@3.2.4': - resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + '@vitest/expect@4.1.2': + resolution: {integrity: sha512-gbu+7B0YgUJ2nkdsRJrFFW6X7NTP44WlhiclHniUhxADQJH5Szt9mZ9hWnJPJ8YwOK5zUOSSlSvyzRf0u1DSBQ==} - '@vitest/mocker@3.2.4': - resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} + '@vitest/mocker@4.1.2': + resolution: {integrity: sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q==} peerDependencies: msw: ^2.4.9 - vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: msw: optional: true vite: optional: true - '@vitest/pretty-format@3.2.4': - resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} - - '@vitest/runner@1.6.1': - resolution: {integrity: sha512-3nSnYXkVkf3mXFfE7vVyPmi3Sazhb/2cfZGGs0JRzFsPFvAMBEcrweV1V1GsrstdXeKCTXlJbvnQwGWgEIHmOA==} - - '@vitest/runner@3.2.4': - resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} + '@vitest/pretty-format@4.1.2': + resolution: {integrity: sha512-dwQga8aejqeuB+TvXCMzSQemvV9hNEtDDpgUKDzOmNQayl2OG241PSWeJwKRH3CiC+sESrmoFd49rfnq7T4RnA==} - '@vitest/snapshot@1.6.1': - resolution: {integrity: sha512-WvidQuWAzU2p95u8GAKlRMqMyN1yOJkGHnx3M1PL9Raf7AQ1kwLKg04ADlCa3+OXUZE7BceOhVZiuWAbzCKcUQ==} + '@vitest/runner@4.1.2': + resolution: {integrity: sha512-Gr+FQan34CdiYAwpGJmQG8PgkyFVmARK8/xSijia3eTFgVfpcpztWLuP6FttGNfPLJhaZVP/euvujeNYar36OQ==} - '@vitest/snapshot@3.2.4': - resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} + '@vitest/snapshot@4.1.2': + resolution: {integrity: sha512-g7yfUmxYS4mNxk31qbOYsSt2F4m1E02LFqO53Xpzg3zKMhLAPZAjjfyl9e6z7HrW6LvUdTwAQR3HHfLjpko16A==} - '@vitest/spy@1.6.1': - resolution: {integrity: sha512-MGcMmpGkZebsMZhbQKkAf9CX5zGvjkBTqf8Zx3ApYWXr3wG+QvEu2eXWfnIIWYSJExIp4V9FCKDEeygzkYrXMw==} + '@vitest/spy@4.1.2': + resolution: {integrity: sha512-DU4fBnbVCJGNBwVA6xSToNXrkZNSiw59H8tcuUspVMsBDBST4nfvsPsEHDHGtWRRnqBERBQu7TrTKskmjqTXKA==} - '@vitest/spy@3.2.4': - resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} - - '@vitest/utils@1.6.1': - resolution: {integrity: sha512-jOrrUvXM4Av9ZWiG1EajNto0u96kWAhJ1LmPmJhXXQx/32MecEKd10pOLYgS2BQx1TgkGhloPU1ArDW2vvaY6g==} - - '@vitest/utils@3.2.4': - resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + '@vitest/utils@4.1.2': + resolution: {integrity: sha512-xw2/TiX82lQHA06cgbqRKFb5lCAy3axQ4H4SoUFhUsg+wztiet+co86IAMDtF6Vm1hc7J6j09oh/rgDn+JdKIQ==} acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn-walk@8.3.4: - resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} - engines: {node: '>=0.4.0'} - - acorn@8.15.0: - resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} engines: {node: '>=0.4.0'} hasBin: true @@ -3072,8 +2937,8 @@ packages: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv@6.14.0: + resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} @@ -3091,10 +2956,6 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - ansi-styles@6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} @@ -3157,9 +3018,6 @@ packages: resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} - assertion-error@1.1.0: - resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} - assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -3204,6 +3062,15 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + baseline-browser-mapping@2.10.11: + resolution: {integrity: sha512-DAKrHphkJyiGuau/cFieRYhcTFeK/lBuD++C7cZ6KZHbMhBrisoi+EvhQ5RZrIfV5qwsW8kgQ07JIC+MDJRAhg==} + engines: {node: '>=6.0.0'} + hasBin: true + better-path-resolve@1.0.0: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} @@ -3221,8 +3088,9 @@ packages: brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - brace-expansion@2.0.2: - resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + brace-expansion@5.0.3: + resolution: {integrity: sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==} + engines: {node: 18 || 20 || >=22} braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} @@ -3256,28 +3124,13 @@ packages: caniuse-lite@1.0.30001737: resolution: {integrity: sha512-BiloLiXtQNrY5UyF0+1nSJLXUENuhka2pzy2Fx5pGxqavdrxSCW4U6Pn/PoG3Efspi2frRbHpBV2XsrPE6EDlw==} - chai@4.5.0: - resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==} - engines: {node: '>=4'} - - chai@5.3.3: - resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - chardet@2.1.0: resolution: {integrity: sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==} - check-error@1.0.3: - resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} - - check-error@2.1.1: - resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} - engines: {node: '>= 16'} - cheerio-select@2.1.0: resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} @@ -3337,9 +3190,6 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - confbox@0.1.8: - resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} - convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -3421,14 +3271,6 @@ packages: decimal.js@10.6.0: resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} - deep-eql@4.1.4: - resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} - engines: {node: '>=6'} - - deep-eql@5.0.2: - resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} - engines: {node: '>=6'} - deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -3463,10 +3305,6 @@ packages: detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} - diff-sequences@29.6.3: - resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - diff@8.0.2: resolution: {integrity: sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==} engines: {node: '>=0.3.1'} @@ -3572,8 +3410,8 @@ packages: resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} engines: {node: '>= 0.4'} - es-module-lexer@1.7.0: - resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-module-lexer@2.0.0: + resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} @@ -3591,11 +3429,6 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} - esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} - hasBin: true - esbuild@0.25.11: resolution: {integrity: sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==} engines: {node: '>=18'} @@ -3647,9 +3480,9 @@ packages: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} - eslint-scope@8.4.0: - resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint-scope@9.1.1: + resolution: {integrity: sha512-GaUN0sWim5qc8KVErfPBWmc31LEsOkrUJbvJZV+xuL3u2phMUK4HIvXlWAakfC8W4nzlK+chPEAkYOYb5ZScIw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} eslint-utils@3.0.0: resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} @@ -3669,9 +3502,13 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.34.0: - resolution: {integrity: sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@10.0.2: + resolution: {integrity: sha512-uYixubwmqJZH+KLVYIVKY1JQt7tysXhtj21WSvjcSmU5SVNzMus1bgLe+pAt816yQ8opKfheVVoPLqvVMGejYw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} hasBin: true peerDependencies: jiti: '*' @@ -3683,6 +3520,10 @@ packages: resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + espree@11.1.1: + resolution: {integrity: sha512-AVHPqQoZYc+RUM4/3Ly5udlZY/U4LS8pIG05jEjWM2lQMU/oaZ7qshzAl2YP1tfNmXfftH3ohurfwNAug+MnsQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} @@ -3692,6 +3533,10 @@ packages: resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} @@ -3711,12 +3556,8 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} - execa@8.0.1: - resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} - engines: {node: '>=16.17'} - - expect-type@1.2.2: - resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} exsolve@1.0.7: @@ -3825,9 +3666,6 @@ packages: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} - get-func-name@2.0.2: - resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} - get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -3840,10 +3678,6 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} - get-stream@8.0.1: - resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} - engines: {node: '>=16'} - get-symbol-description@1.1.0: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} @@ -3901,9 +3735,6 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - graphql-request@5.2.0: resolution: {integrity: sha512-pLhKIvnMyBERL0dtFI3medKqWOz/RhHdcgbZ+hMMIb32mEPa5MJSzS4AuXxfI4sRAu6JVVk5tvXuGfCWl9JYWQ==} peerDependencies: @@ -3926,10 +3757,6 @@ packages: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} - has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - has-property-descriptors@1.0.2: resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} @@ -3977,10 +3804,6 @@ packages: resolution: {integrity: sha512-3gKm/gCSUipeLsRYZbbdA1BD83lBoWUkZ7G9VFrhWPAU76KwYo5KR8V28bpoPm/ygy0x5/GCbpRQdY7VLYCoIg==} hasBin: true - human-signals@5.0.0: - resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} - engines: {node: '>=16.17.0'} - iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} @@ -4103,10 +3926,6 @@ packages: resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} engines: {node: '>= 0.4'} - is-stream@3.0.0: - resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - is-string@1.1.1: resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} engines: {node: '>= 0.4'} @@ -4168,15 +3987,12 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-tokens@9.0.1: - resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} - js-yaml@3.14.1: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} hasBin: true - js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true jsdoc-type-pratt-parser@4.1.0: @@ -4308,10 +4124,6 @@ packages: resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} engines: {node: '>= 12.0.0'} - local-pkg@0.5.1: - resolution: {integrity: sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==} - engines: {node: '>=14'} - locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -4320,9 +4132,6 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - lodash.startcase@4.4.0: resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} @@ -4333,12 +4142,6 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true - loupe@2.3.7: - resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} - - loupe@3.2.1: - resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} - lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -4357,13 +4160,13 @@ packages: magic-string@0.30.19: resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -4380,20 +4183,15 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} - engines: {node: '>=12'} - - minimatch@10.0.3: - resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} - engines: {node: 20 || >=22} + minimatch@10.2.2: + resolution: {integrity: sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==} + engines: {node: 18 || 20 || >=22} minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} + minimatch@3.1.3: + resolution: {integrity: sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==} minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} @@ -4402,9 +4200,6 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} - mlly@1.8.0: - resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} - mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -4420,8 +4215,8 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - next@16.0.10: - resolution: {integrity: sha512-RtWh5PUgI+vxlV3HdR+IfWA1UUHu0+Ram/JBO4vWB54cVPentCD0e+lxyAYEsDTqGGMg7qpjhKh6dc6aW7W/sA==} + next@16.2.1: + resolution: {integrity: sha512-VaChzNL7o9rbfdt60HUj8tev4m6d7iC1igAy157526+cJlXOQu5LzsBXNT+xaJnTP/k+utSX5vMv7m0G+zKH+Q==} engines: {node: '>=20.9.0'} hasBin: true peerDependencies: @@ -4460,10 +4255,6 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - npm-run-path@5.3.0: - resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} @@ -4498,6 +4289,9 @@ packages: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + on-exit-leak-free@2.1.2: resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} engines: {node: '>=14.0.0'} @@ -4505,10 +4299,6 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - onetime@6.0.0: - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} - engines: {node: '>=12'} - optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -4532,10 +4322,6 @@ packages: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} - p-limit@5.0.0: - resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} - engines: {node: '>=18'} - p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} @@ -4585,10 +4371,6 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} @@ -4600,19 +4382,9 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - pathval@1.1.1: - resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} - - pathval@2.0.1: - resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} - engines: {node: '>= 14.16'} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -4642,9 +4414,6 @@ packages: resolution: {integrity: sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==} hasBin: true - pkg-types@1.3.1: - resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} - possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -4671,10 +4440,6 @@ packages: engines: {node: '>=14'} hasBin: true - pretty-format@29.7.0: - resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - process-warning@5.0.0: resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} @@ -4723,9 +4488,6 @@ packages: react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - react-phone-number-input@3.4.12: resolution: {integrity: sha512-Raob77KdtLGm49iC6nuOX9qy6Mg16idkgC7Y1mHmvG2WBYoauHpzxYNlfmFskQKeiztrJIwPhPzBhjFwjenNCA==} peerDependencies: @@ -4941,8 +4703,8 @@ packages: resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} engines: {node: '>= 0.4'} - sharp@0.34.4: - resolution: {integrity: sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA==} + sharp@0.34.5: + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} shebang-command@2.0.0: @@ -5022,8 +4784,8 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - std-env@3.10.0: - resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + std-env@4.0.0: + resolution: {integrity: sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==} stop-iteration-iterator@1.1.0: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} @@ -5072,10 +4834,6 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} - strip-final-newline@3.0.0: - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} - engines: {node: '>=12'} - strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -5084,12 +4842,6 @@ packages: resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} engines: {node: '>=14.16'} - strip-literal@2.1.1: - resolution: {integrity: sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==} - - strip-literal@3.1.0: - resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} - styled-jsx@5.1.6: resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} engines: {node: '>= 12.0.0'} @@ -5103,10 +4855,6 @@ packages: babel-plugin-macros: optional: true - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -5146,34 +4894,19 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@0.3.2: - resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - tinyexec@1.0.1: resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} + tinyexec@1.0.4: + resolution: {integrity: sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==} + engines: {node: '>=18'} + tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} - tinypool@0.8.4: - resolution: {integrity: sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==} - engines: {node: '>=14.0.0'} - - tinypool@1.1.1: - resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} - engines: {node: ^18.0.0 || >=20.0.0} - - tinyrainbow@2.0.0: - resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} - engines: {node: '>=14.0.0'} - - tinyspy@2.2.1: - resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} - engines: {node: '>=14.0.0'} - - tinyspy@4.0.4: - resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} + tinyrainbow@3.1.0: + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} tldts-core@6.1.86: @@ -5202,8 +4935,8 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true - ts-api-utils@2.1.0: - resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + ts-api-utils@2.4.0: + resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} engines: {node: '>=18.12'} peerDependencies: typescript: '>=4.8.4' @@ -5242,10 +4975,6 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - type-detect@4.1.0: - resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} - engines: {node: '>=4'} - type-fest@0.20.2: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} @@ -5345,32 +5074,27 @@ packages: react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc - vite-node@1.6.1: - resolution: {integrity: sha512-YAXkfvGtuTzwWbDSACdJSg4A4DZiAqckWe90Zapc/sEX3XvHcw1NdurM/6od8J207tSDqNbSsgdCacBgvJKFuA==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - - vite-node@3.2.4: - resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} + vite@6.4.1: + resolution: {integrity: sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true - - vite@5.4.21: - resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' less: '*' lightningcss: ^1.21.0 sass: '*' sass-embedded: '*' stylus: '*' sugarss: '*' - terser: ^5.4.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 peerDependenciesMeta: '@types/node': optional: true + jiti: + optional: true less: optional: true lightningcss: @@ -5385,6 +5109,10 @@ packages: optional: true terser: optional: true + tsx: + optional: true + yaml: + optional: true vitefu@1.1.1: resolution: {integrity: sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==} @@ -5394,51 +5122,33 @@ packages: vite: optional: true - vitest@1.6.1: - resolution: {integrity: sha512-Ljb1cnSJSivGN0LqXd/zmDbWEM0RNNg2t1QW/XUhYl/qPqyu7CsqeWtqQXHVaJsecLPuDoak2oJcZN2QoRIOag==} - engines: {node: ^18.0.0 || >=20.0.0} + vitest@4.1.2: + resolution: {integrity: sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' - '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 1.6.1 - '@vitest/ui': 1.6.1 + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.2 + '@vitest/browser-preview': 4.1.2 + '@vitest/browser-webdriverio': 4.1.2 + '@vitest/ui': 4.1.2 happy-dom: '*' jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: '@edge-runtime/vm': optional: true - '@types/node': - optional: true - '@vitest/browser': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: + '@opentelemetry/api': optional: true - - vitest@3.2.4: - resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@types/debug': ^4.1.12 - '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - '@vitest/browser': 3.2.4 - '@vitest/ui': 3.2.4 - happy-dom: '*' - jsdom: '*' - peerDependenciesMeta: - '@edge-runtime/vm': + '@types/node': optional: true - '@types/debug': + '@vitest/browser-playwright': optional: true - '@types/node': + '@vitest/browser-preview': optional: true - '@vitest/browser': + '@vitest/browser-webdriverio': optional: true '@vitest/ui': optional: true @@ -5562,10 +5272,6 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - yocto-queue@1.2.1: - resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==} - engines: {node: '>=12.20'} - zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -5630,11 +5336,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/eslint-parser@7.28.0(@babel/core@7.28.3)(eslint@9.34.0(jiti@2.6.1))': + '@babel/eslint-parser@7.28.6(@babel/core@7.28.3)(eslint@10.0.2(jiti@2.6.1))': dependencies: '@babel/core': 7.28.3 '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1 - eslint: 9.34.0(jiti@2.6.1) + eslint: 10.0.2(jiti@2.6.1) eslint-visitor-keys: 2.1.0 semver: 6.3.1 @@ -5803,7 +5509,7 @@ snapshots: '@babel/parser': 7.28.3 '@babel/template': 7.27.2 '@babel/types': 7.28.2 - debug: 4.4.1 + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -6097,6 +5803,11 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/runtime@1.9.1': + dependencies: + tslib: 2.8.1 + optional: true + '@emnapi/wasi-threads@1.1.0': dependencies: tslib: 2.8.1 @@ -6110,195 +5821,134 @@ snapshots: esquery: 1.6.0 jsdoc-type-pratt-parser: 4.1.0 - '@esbuild/aix-ppc64@0.21.5': - optional: true - '@esbuild/aix-ppc64@0.25.11': optional: true - '@esbuild/android-arm64@0.21.5': - optional: true - '@esbuild/android-arm64@0.25.11': optional: true - '@esbuild/android-arm@0.21.5': - optional: true - '@esbuild/android-arm@0.25.11': optional: true - '@esbuild/android-x64@0.21.5': - optional: true - '@esbuild/android-x64@0.25.11': optional: true - '@esbuild/darwin-arm64@0.21.5': - optional: true - '@esbuild/darwin-arm64@0.25.11': optional: true - '@esbuild/darwin-x64@0.21.5': - optional: true - '@esbuild/darwin-x64@0.25.11': optional: true - '@esbuild/freebsd-arm64@0.21.5': - optional: true - '@esbuild/freebsd-arm64@0.25.11': optional: true - '@esbuild/freebsd-x64@0.21.5': - optional: true - '@esbuild/freebsd-x64@0.25.11': optional: true - '@esbuild/linux-arm64@0.21.5': - optional: true - '@esbuild/linux-arm64@0.25.11': optional: true - '@esbuild/linux-arm@0.21.5': - optional: true - '@esbuild/linux-arm@0.25.11': optional: true - '@esbuild/linux-ia32@0.21.5': - optional: true - '@esbuild/linux-ia32@0.25.11': optional: true - '@esbuild/linux-loong64@0.21.5': - optional: true - '@esbuild/linux-loong64@0.25.11': optional: true - '@esbuild/linux-mips64el@0.21.5': - optional: true - '@esbuild/linux-mips64el@0.25.11': optional: true - '@esbuild/linux-ppc64@0.21.5': - optional: true - '@esbuild/linux-ppc64@0.25.11': optional: true - '@esbuild/linux-riscv64@0.21.5': - optional: true - '@esbuild/linux-riscv64@0.25.11': optional: true - '@esbuild/linux-s390x@0.21.5': - optional: true - '@esbuild/linux-s390x@0.25.11': optional: true - '@esbuild/linux-x64@0.21.5': - optional: true - '@esbuild/linux-x64@0.25.11': optional: true '@esbuild/netbsd-arm64@0.25.11': optional: true - '@esbuild/netbsd-x64@0.21.5': - optional: true - '@esbuild/netbsd-x64@0.25.11': optional: true '@esbuild/openbsd-arm64@0.25.11': optional: true - '@esbuild/openbsd-x64@0.21.5': - optional: true - '@esbuild/openbsd-x64@0.25.11': optional: true '@esbuild/openharmony-arm64@0.25.11': optional: true - '@esbuild/sunos-x64@0.21.5': - optional: true - '@esbuild/sunos-x64@0.25.11': optional: true - '@esbuild/win32-arm64@0.21.5': - optional: true - '@esbuild/win32-arm64@0.25.11': optional: true - '@esbuild/win32-ia32@0.21.5': - optional: true - '@esbuild/win32-ia32@0.25.11': optional: true - '@esbuild/win32-x64@0.21.5': - optional: true - '@esbuild/win32-x64@0.25.11': optional: true - '@eslint-community/eslint-utils@4.7.0(eslint@9.34.0(jiti@2.6.1))': + '@eslint-community/eslint-utils@4.9.1(eslint@10.0.2(jiti@2.6.1))': dependencies: - eslint: 9.34.0(jiti@2.6.1) + eslint: 10.0.2(jiti@2.6.1) eslint-visitor-keys: 3.4.3 - '@eslint-community/regexpp@4.12.1': {} + '@eslint-community/regexpp@4.12.2': {} - '@eslint/config-array@0.21.0': + '@eslint/compat@2.0.2(eslint@10.0.2(jiti@2.6.1))': dependencies: - '@eslint/object-schema': 2.1.6 - debug: 4.4.1 - minimatch: 3.1.2 + '@eslint/core': 1.1.0 + optionalDependencies: + eslint: 10.0.2(jiti@2.6.1) + + '@eslint/config-array@0.23.2': + dependencies: + '@eslint/object-schema': 3.0.2 + debug: 4.4.3 + minimatch: 10.2.2 transitivePeerDependencies: - supports-color - '@eslint/config-helpers@0.3.1': {} + '@eslint/config-helpers@0.5.2': + dependencies: + '@eslint/core': 1.1.0 - '@eslint/core@0.15.2': + '@eslint/core@1.1.0': dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.1': + '@eslint/eslintrc@3.3.4': dependencies: - ajv: 6.12.6 - debug: 4.4.1 + ajv: 6.14.0 + debug: 4.4.3 espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 import-fresh: 3.3.1 - js-yaml: 4.1.0 - minimatch: 3.1.2 + js-yaml: 4.1.1 + minimatch: 3.1.3 strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color - '@eslint/js@9.34.0': {} + '@eslint/js@10.0.0': {} - '@eslint/object-schema@2.1.6': {} + '@eslint/object-schema@3.0.2': {} - '@eslint/plugin-kit@0.3.5': + '@eslint/plugin-kit@0.6.0': dependencies: - '@eslint/core': 0.15.2 + '@eslint/core': 1.1.0 levn: 0.4.1 '@floating-ui/core@1.7.3': @@ -6364,90 +6014,98 @@ snapshots: '@img/colour@1.0.0': optional: true - '@img/sharp-darwin-arm64@0.34.4': + '@img/sharp-darwin-arm64@0.34.5': optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.2.3 + '@img/sharp-libvips-darwin-arm64': 1.2.4 optional: true - '@img/sharp-darwin-x64@0.34.4': + '@img/sharp-darwin-x64@0.34.5': optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.2.3 + '@img/sharp-libvips-darwin-x64': 1.2.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.4': optional: true - '@img/sharp-libvips-darwin-arm64@1.2.3': + '@img/sharp-libvips-darwin-x64@1.2.4': optional: true - '@img/sharp-libvips-darwin-x64@1.2.3': + '@img/sharp-libvips-linux-arm64@1.2.4': optional: true - '@img/sharp-libvips-linux-arm64@1.2.3': + '@img/sharp-libvips-linux-arm@1.2.4': optional: true - '@img/sharp-libvips-linux-arm@1.2.3': + '@img/sharp-libvips-linux-ppc64@1.2.4': optional: true - '@img/sharp-libvips-linux-ppc64@1.2.3': + '@img/sharp-libvips-linux-riscv64@1.2.4': optional: true - '@img/sharp-libvips-linux-s390x@1.2.3': + '@img/sharp-libvips-linux-s390x@1.2.4': optional: true - '@img/sharp-libvips-linux-x64@1.2.3': + '@img/sharp-libvips-linux-x64@1.2.4': optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.2.3': + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': optional: true - '@img/sharp-libvips-linuxmusl-x64@1.2.3': + '@img/sharp-libvips-linuxmusl-x64@1.2.4': optional: true - '@img/sharp-linux-arm64@0.34.4': + '@img/sharp-linux-arm64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.2.3 + '@img/sharp-libvips-linux-arm64': 1.2.4 optional: true - '@img/sharp-linux-arm@0.34.4': + '@img/sharp-linux-arm@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.2.3 + '@img/sharp-libvips-linux-arm': 1.2.4 optional: true - '@img/sharp-linux-ppc64@0.34.4': + '@img/sharp-linux-ppc64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.2.3 + '@img/sharp-libvips-linux-ppc64': 1.2.4 optional: true - '@img/sharp-linux-s390x@0.34.4': + '@img/sharp-linux-riscv64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.2.3 + '@img/sharp-libvips-linux-riscv64': 1.2.4 optional: true - '@img/sharp-linux-x64@0.34.4': + '@img/sharp-linux-s390x@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.2.3 + '@img/sharp-libvips-linux-s390x': 1.2.4 optional: true - '@img/sharp-linuxmusl-arm64@0.34.4': + '@img/sharp-linux-x64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.2.3 + '@img/sharp-libvips-linux-x64': 1.2.4 optional: true - '@img/sharp-linuxmusl-x64@0.34.4': + '@img/sharp-linuxmusl-arm64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.2.3 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 optional: true - '@img/sharp-wasm32@0.34.4': + '@img/sharp-linuxmusl-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + optional: true + + '@img/sharp-wasm32@0.34.5': dependencies: - '@emnapi/runtime': 1.6.0 + '@emnapi/runtime': 1.9.1 optional: true - '@img/sharp-win32-arm64@0.34.4': + '@img/sharp-win32-arm64@0.34.5': optional: true - '@img/sharp-win32-ia32@0.34.4': + '@img/sharp-win32-ia32@0.34.5': optional: true - '@img/sharp-win32-x64@0.34.4': + '@img/sharp-win32-x64@0.34.5': optional: true '@inquirer/external-editor@1.0.1(@types/node@22.18.12)': @@ -6457,12 +6115,6 @@ snapshots: optionalDependencies: '@types/node': 22.18.12 - '@isaacs/balanced-match@4.0.1': {} - - '@isaacs/brace-expansion@5.0.0': - dependencies: - '@isaacs/balanced-match': 4.0.1 - '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -6472,10 +6124,6 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 - '@jest/schemas@29.6.3': - dependencies: - '@sinclair/typebox': 0.27.8 - '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -6518,30 +6166,30 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true - '@next/env@16.0.10': {} + '@next/env@16.2.1': {} - '@next/swc-darwin-arm64@16.0.10': + '@next/swc-darwin-arm64@16.2.1': optional: true - '@next/swc-darwin-x64@16.0.10': + '@next/swc-darwin-x64@16.2.1': optional: true - '@next/swc-linux-arm64-gnu@16.0.10': + '@next/swc-linux-arm64-gnu@16.2.1': optional: true - '@next/swc-linux-arm64-musl@16.0.10': + '@next/swc-linux-arm64-musl@16.2.1': optional: true - '@next/swc-linux-x64-gnu@16.0.10': + '@next/swc-linux-x64-gnu@16.2.1': optional: true - '@next/swc-linux-x64-musl@16.0.10': + '@next/swc-linux-x64-musl@16.2.1': optional: true - '@next/swc-win32-arm64-msvc@16.0.10': + '@next/swc-win32-arm64-msvc@16.2.1': optional: true - '@next/swc-win32-x64-msvc@16.0.10': + '@next/swc-win32-x64-msvc@16.2.1': optional: true '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1': @@ -7443,18 +7091,18 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.52.5': optional: true - '@sinclair/typebox@0.27.8': {} + '@standard-schema/spec@1.1.0': {} '@standard-schema/utils@0.3.0': {} - '@stripe/react-stripe-js@3.10.0(@stripe/stripe-js@7.9.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@stripe/react-stripe-js@5.6.1(@stripe/stripe-js@8.11.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@stripe/stripe-js': 7.9.0 + '@stripe/stripe-js': 8.11.0 prop-types: 15.8.1 react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - '@stripe/stripe-js@7.9.0': {} + '@stripe/stripe-js@8.11.0': {} '@swc/helpers@0.5.15': dependencies: @@ -7600,14 +7248,14 @@ snapshots: postcss: 8.5.6 tailwindcss: 4.1.16 - '@tailwindcss/vite@4.1.15(vite@5.4.21(@types/node@22.18.12)(lightningcss@1.30.2))': + '@tailwindcss/vite@4.1.15(vite@6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6))': dependencies: '@tailwindcss/node': 4.1.15 '@tailwindcss/oxide': 4.1.15 tailwindcss: 4.1.15 - vite: 5.4.21(@types/node@22.18.12)(lightningcss@1.30.2) + vite: 6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6) - '@tanstack/directive-functions-plugin@1.133.19(vite@5.4.21(@types/node@22.18.12)(lightningcss@1.30.2))': + '@tanstack/directive-functions-plugin@1.133.19(vite@6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6))': dependencies: '@babel/code-frame': 7.27.1 '@babel/core': 7.28.3 @@ -7617,7 +7265,7 @@ snapshots: babel-dead-code-elimination: 1.0.10 pathe: 2.0.3 tiny-invariant: 1.3.3 - vite: 5.4.21(@types/node@22.18.12)(lightningcss@1.30.2) + vite: 6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6) transitivePeerDependencies: - supports-color @@ -7679,19 +7327,19 @@ snapshots: transitivePeerDependencies: - crossws - '@tanstack/react-start@1.133.22(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@5.4.21(@types/node@22.18.12)(lightningcss@1.30.2))': + '@tanstack/react-start@1.133.22(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6))': dependencies: '@tanstack/react-router': 1.133.22(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@tanstack/react-start-client': 1.133.22(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@tanstack/react-start-server': 1.133.22(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@tanstack/router-utils': 1.133.19 '@tanstack/start-client-core': 1.133.20 - '@tanstack/start-plugin-core': 1.133.22(@tanstack/react-router@1.133.22(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(vite@5.4.21(@types/node@22.18.12)(lightningcss@1.30.2)) + '@tanstack/start-plugin-core': 1.133.22(@tanstack/react-router@1.133.22(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(vite@6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)) '@tanstack/start-server-core': 1.133.20 pathe: 2.0.3 react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - vite: 5.4.21(@types/node@22.18.12)(lightningcss@1.30.2) + vite: 6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6) transitivePeerDependencies: - '@rsbuild/core' - crossws @@ -7729,7 +7377,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/router-plugin@1.133.22(@tanstack/react-router@1.133.22(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(vite@5.4.21(@types/node@22.18.12)(lightningcss@1.30.2))': + '@tanstack/router-plugin@1.133.22(@tanstack/react-router@1.133.22(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(vite@6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6))': dependencies: '@babel/core': 7.28.3 '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.3) @@ -7747,7 +7395,7 @@ snapshots: zod: 3.25.76 optionalDependencies: '@tanstack/react-router': 1.133.22(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - vite: 5.4.21(@types/node@22.18.12)(lightningcss@1.30.2) + vite: 6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6) transitivePeerDependencies: - supports-color @@ -7764,7 +7412,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/server-functions-plugin@1.133.19(vite@5.4.21(@types/node@22.18.12)(lightningcss@1.30.2))': + '@tanstack/server-functions-plugin@1.133.19(vite@6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6))': dependencies: '@babel/code-frame': 7.27.1 '@babel/core': 7.28.3 @@ -7773,7 +7421,7 @@ snapshots: '@babel/template': 7.27.2 '@babel/traverse': 7.28.3 '@babel/types': 7.28.2 - '@tanstack/directive-functions-plugin': 1.133.19(vite@5.4.21(@types/node@22.18.12)(lightningcss@1.30.2)) + '@tanstack/directive-functions-plugin': 1.133.19(vite@6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)) babel-dead-code-elimination: 1.0.10 tiny-invariant: 1.3.3 transitivePeerDependencies: @@ -7788,7 +7436,7 @@ snapshots: tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - '@tanstack/start-plugin-core@1.133.22(@tanstack/react-router@1.133.22(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(vite@5.4.21(@types/node@22.18.12)(lightningcss@1.30.2))': + '@tanstack/start-plugin-core@1.133.22(@tanstack/react-router@1.133.22(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(vite@6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6))': dependencies: '@babel/code-frame': 7.26.2 '@babel/core': 7.28.3 @@ -7796,9 +7444,9 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.40 '@tanstack/router-core': 1.133.20 '@tanstack/router-generator': 1.133.20 - '@tanstack/router-plugin': 1.133.22(@tanstack/react-router@1.133.22(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(vite@5.4.21(@types/node@22.18.12)(lightningcss@1.30.2)) + '@tanstack/router-plugin': 1.133.22(@tanstack/react-router@1.133.22(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(vite@6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)) '@tanstack/router-utils': 1.133.19 - '@tanstack/server-functions-plugin': 1.133.19(vite@5.4.21(@types/node@22.18.12)(lightningcss@1.30.2)) + '@tanstack/server-functions-plugin': 1.133.19(vite@6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)) '@tanstack/start-client-core': 1.133.20 '@tanstack/start-server-core': 1.133.20 babel-dead-code-elimination: 1.0.10 @@ -7808,8 +7456,8 @@ snapshots: srvx: 0.8.16 tinyglobby: 0.2.15 ufo: 1.6.1 - vite: 5.4.21(@types/node@22.18.12)(lightningcss@1.30.2) - vitefu: 1.1.1(vite@5.4.21(@types/node@22.18.12)(lightningcss@1.30.2)) + vite: 6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6) + vitefu: 1.1.1(vite@6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)) xmlbuilder2: 3.1.1 zod: 3.25.76 transitivePeerDependencies: @@ -7847,24 +7495,24 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.28.3 - '@babel/types': 7.28.2 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.28.0 '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.28.2 + '@babel/types': 7.28.4 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.28.3 - '@babel/types': 7.28.2 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 '@types/babel__traverse@7.28.0': dependencies: - '@babel/types': 7.28.2 + '@babel/types': 7.28.4 '@types/body-parser@1.19.6': dependencies: @@ -7882,6 +7530,8 @@ snapshots: '@types/deep-eql@4.0.2': {} + '@types/esrecurse@4.3.1': {} + '@types/estree@1.0.8': {} '@types/express-serve-static-core@5.1.0': @@ -7940,100 +7590,100 @@ snapshots: '@types/node': 22.18.12 '@types/send': 0.17.5 - '@typescript-eslint/eslint-plugin@8.41.0(@typescript-eslint/parser@8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2))(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2)': + '@typescript-eslint/eslint-plugin@8.56.1(@typescript-eslint/parser@8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.2))(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.2)': dependencies: - '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2) - '@typescript-eslint/scope-manager': 8.41.0 - '@typescript-eslint/type-utils': 8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2) - '@typescript-eslint/utils': 8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2) - '@typescript-eslint/visitor-keys': 8.41.0 - eslint: 9.34.0(jiti@2.6.1) - graphemer: 1.4.0 + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.2) + '@typescript-eslint/scope-manager': 8.56.1 + '@typescript-eslint/type-utils': 8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.2) + '@typescript-eslint/utils': 8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.2) + '@typescript-eslint/visitor-keys': 8.56.1 + eslint: 10.0.2(jiti@2.6.1) ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.9.2) + ts-api-utils: 2.4.0(typescript@5.9.2) typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2)': + '@typescript-eslint/parser@8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.2)': dependencies: - '@typescript-eslint/scope-manager': 8.41.0 - '@typescript-eslint/types': 8.41.0 - '@typescript-eslint/typescript-estree': 8.41.0(typescript@5.9.2) - '@typescript-eslint/visitor-keys': 8.41.0 - debug: 4.4.1 - eslint: 9.34.0(jiti@2.6.1) + '@typescript-eslint/scope-manager': 8.56.1 + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.2) + '@typescript-eslint/visitor-keys': 8.56.1 + debug: 4.4.3 + eslint: 10.0.2(jiti@2.6.1) typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.41.0(typescript@5.9.2)': + '@typescript-eslint/project-service@8.56.1(typescript@5.9.2)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.41.0(typescript@5.9.2) - '@typescript-eslint/types': 8.41.0 - debug: 4.4.1 + '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@5.9.2) + '@typescript-eslint/types': 8.56.1 + debug: 4.4.3 typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.41.0': + '@typescript-eslint/scope-manager@8.56.1': dependencies: - '@typescript-eslint/types': 8.41.0 - '@typescript-eslint/visitor-keys': 8.41.0 + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/visitor-keys': 8.56.1 - '@typescript-eslint/tsconfig-utils@8.41.0(typescript@5.9.2)': + '@typescript-eslint/tsconfig-utils@8.56.1(typescript@5.9.2)': dependencies: typescript: 5.9.2 - '@typescript-eslint/type-utils@8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2)': + '@typescript-eslint/type-utils@8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.2)': dependencies: - '@typescript-eslint/types': 8.41.0 - '@typescript-eslint/typescript-estree': 8.41.0(typescript@5.9.2) - '@typescript-eslint/utils': 8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2) - debug: 4.4.1 - eslint: 9.34.0(jiti@2.6.1) - ts-api-utils: 2.1.0(typescript@5.9.2) + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.2) + '@typescript-eslint/utils': 8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.2) + debug: 4.4.3 + eslint: 10.0.2(jiti@2.6.1) + ts-api-utils: 2.4.0(typescript@5.9.2) typescript: 5.9.2 transitivePeerDependencies: - supports-color '@typescript-eslint/types@8.41.0': {} - '@typescript-eslint/typescript-estree@8.41.0(typescript@5.9.2)': + '@typescript-eslint/types@8.56.1': {} + + '@typescript-eslint/typescript-estree@8.56.1(typescript@5.9.2)': dependencies: - '@typescript-eslint/project-service': 8.41.0(typescript@5.9.2) - '@typescript-eslint/tsconfig-utils': 8.41.0(typescript@5.9.2) - '@typescript-eslint/types': 8.41.0 - '@typescript-eslint/visitor-keys': 8.41.0 - debug: 4.4.1 - fast-glob: 3.3.3 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.7.2 - ts-api-utils: 2.1.0(typescript@5.9.2) + '@typescript-eslint/project-service': 8.56.1(typescript@5.9.2) + '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@5.9.2) + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/visitor-keys': 8.56.1 + debug: 4.4.3 + minimatch: 10.2.2 + semver: 7.7.3 + tinyglobby: 0.2.15 + ts-api-utils: 2.4.0(typescript@5.9.2) typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.41.0(eslint@9.34.0(jiti@2.6.1))(typescript@5.9.2)': + '@typescript-eslint/utils@8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.2)': dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.34.0(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.41.0 - '@typescript-eslint/types': 8.41.0 - '@typescript-eslint/typescript-estree': 8.41.0(typescript@5.9.2) - eslint: 9.34.0(jiti@2.6.1) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.2(jiti@2.6.1)) + '@typescript-eslint/scope-manager': 8.56.1 + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.2) + eslint: 10.0.2(jiti@2.6.1) typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.41.0': + '@typescript-eslint/visitor-keys@8.56.1': dependencies: - '@typescript-eslint/types': 8.41.0 - eslint-visitor-keys: 4.2.1 + '@typescript-eslint/types': 8.56.1 + eslint-visitor-keys: 5.0.1 - '@vitejs/plugin-react@4.7.0(vite@5.4.21(@types/node@22.18.12)(lightningcss@1.30.2))': + '@vitejs/plugin-react@4.7.0(vite@6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6))': dependencies: '@babel/core': 7.28.3 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.3) @@ -8041,94 +7691,60 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.27 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 5.4.21(@types/node@22.18.12)(lightningcss@1.30.2) + vite: 6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6) transitivePeerDependencies: - supports-color - '@vitest/expect@1.6.1': - dependencies: - '@vitest/spy': 1.6.1 - '@vitest/utils': 1.6.1 - chai: 4.5.0 - - '@vitest/expect@3.2.4': + '@vitest/expect@4.1.2': dependencies: + '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 3.2.4 - '@vitest/utils': 3.2.4 - chai: 5.3.3 - tinyrainbow: 2.0.0 + '@vitest/spy': 4.1.2 + '@vitest/utils': 4.1.2 + chai: 6.2.2 + tinyrainbow: 3.1.0 - '@vitest/mocker@3.2.4(vite@5.4.21(@types/node@22.18.12)(lightningcss@1.30.2))': + '@vitest/mocker@4.1.2(vite@6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6))': dependencies: - '@vitest/spy': 3.2.4 + '@vitest/spy': 4.1.2 estree-walker: 3.0.3 - magic-string: 0.30.19 + magic-string: 0.30.21 optionalDependencies: - vite: 5.4.21(@types/node@22.18.12)(lightningcss@1.30.2) - - '@vitest/pretty-format@3.2.4': - dependencies: - tinyrainbow: 2.0.0 + vite: 6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6) - '@vitest/runner@1.6.1': + '@vitest/pretty-format@4.1.2': dependencies: - '@vitest/utils': 1.6.1 - p-limit: 5.0.0 - pathe: 1.1.2 + tinyrainbow: 3.1.0 - '@vitest/runner@3.2.4': + '@vitest/runner@4.1.2': dependencies: - '@vitest/utils': 3.2.4 + '@vitest/utils': 4.1.2 pathe: 2.0.3 - strip-literal: 3.1.0 - - '@vitest/snapshot@1.6.1': - dependencies: - magic-string: 0.30.19 - pathe: 1.1.2 - pretty-format: 29.7.0 - '@vitest/snapshot@3.2.4': + '@vitest/snapshot@4.1.2': dependencies: - '@vitest/pretty-format': 3.2.4 - magic-string: 0.30.19 + '@vitest/pretty-format': 4.1.2 + '@vitest/utils': 4.1.2 + magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@1.6.1': - dependencies: - tinyspy: 2.2.1 - - '@vitest/spy@3.2.4': - dependencies: - tinyspy: 4.0.4 - - '@vitest/utils@1.6.1': - dependencies: - diff-sequences: 29.6.3 - estree-walker: 3.0.3 - loupe: 2.3.7 - pretty-format: 29.7.0 + '@vitest/spy@4.1.2': {} - '@vitest/utils@3.2.4': + '@vitest/utils@4.1.2': dependencies: - '@vitest/pretty-format': 3.2.4 - loupe: 3.2.1 - tinyrainbow: 2.0.0 - - acorn-jsx@5.3.2(acorn@8.15.0): - dependencies: - acorn: 8.15.0 + '@vitest/pretty-format': 4.1.2 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.0 - acorn-walk@8.3.4: + acorn-jsx@5.3.2(acorn@8.16.0): dependencies: - acorn: 8.15.0 + acorn: 8.16.0 - acorn@8.15.0: {} + acorn@8.16.0: {} agent-base@7.1.4: {} - ajv@6.12.6: + ajv@6.14.0: dependencies: fast-deep-equal: 3.1.3 fast-json-stable-stringify: 2.1.0 @@ -8145,8 +7761,6 @@ snapshots: dependencies: color-convert: 2.0.1 - ansi-styles@5.2.0: {} - ansi-styles@6.2.1: {} ansis@4.2.0: {} @@ -8229,8 +7843,6 @@ snapshots: get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 - assertion-error@1.1.0: {} - assertion-error@2.0.1: {} ast-kit@2.1.3: @@ -8269,6 +7881,10 @@ snapshots: balanced-match@1.0.2: {} + balanced-match@4.0.4: {} + + baseline-browser-mapping@2.10.11: {} + better-path-resolve@1.0.0: dependencies: is-windows: 1.0.2 @@ -8284,9 +7900,9 @@ snapshots: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.0.2: + brace-expansion@5.0.3: dependencies: - balanced-match: 1.0.2 + balanced-match: 4.0.4 braces@3.0.3: dependencies: @@ -8322,37 +7938,10 @@ snapshots: caniuse-lite@1.0.30001737: {} - chai@4.5.0: - dependencies: - assertion-error: 1.1.0 - check-error: 1.0.3 - deep-eql: 4.1.4 - get-func-name: 2.0.2 - loupe: 2.3.7 - pathval: 1.1.1 - type-detect: 4.1.0 - - chai@5.3.3: - dependencies: - assertion-error: 2.0.1 - check-error: 2.1.1 - deep-eql: 5.0.2 - loupe: 3.2.1 - pathval: 2.0.1 - - chalk@4.1.2: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 + chai@6.2.2: {} chardet@2.1.0: {} - check-error@1.0.3: - dependencies: - get-func-name: 2.0.2 - - check-error@2.1.1: {} - cheerio-select@2.1.0: dependencies: boolbase: 1.0.0 @@ -8430,8 +8019,6 @@ snapshots: concat-map@0.0.1: {} - confbox@0.1.8: {} - convert-source-map@2.0.0: {} cookie-es@2.0.0: {} @@ -8510,12 +8097,6 @@ snapshots: decimal.js@10.6.0: {} - deep-eql@4.1.4: - dependencies: - type-detect: 4.1.0 - - deep-eql@5.0.2: {} - deep-is@0.1.4: {} define-data-property@1.1.4: @@ -8542,8 +8123,6 @@ snapshots: detect-node-es@1.1.0: {} - diff-sequences@29.6.3: {} - diff@8.0.2: {} dir-glob@3.0.1: @@ -8705,7 +8284,7 @@ snapshots: iterator.prototype: 1.1.5 safe-array-concat: 1.1.3 - es-module-lexer@1.7.0: {} + es-module-lexer@2.0.0: {} es-object-atoms@1.1.1: dependencies: @@ -8728,32 +8307,6 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 - esbuild@0.21.5: - optionalDependencies: - '@esbuild/aix-ppc64': 0.21.5 - '@esbuild/android-arm': 0.21.5 - '@esbuild/android-arm64': 0.21.5 - '@esbuild/android-x64': 0.21.5 - '@esbuild/darwin-arm64': 0.21.5 - '@esbuild/darwin-x64': 0.21.5 - '@esbuild/freebsd-arm64': 0.21.5 - '@esbuild/freebsd-x64': 0.21.5 - '@esbuild/linux-arm': 0.21.5 - '@esbuild/linux-arm64': 0.21.5 - '@esbuild/linux-ia32': 0.21.5 - '@esbuild/linux-loong64': 0.21.5 - '@esbuild/linux-mips64el': 0.21.5 - '@esbuild/linux-ppc64': 0.21.5 - '@esbuild/linux-riscv64': 0.21.5 - '@esbuild/linux-s390x': 0.21.5 - '@esbuild/linux-x64': 0.21.5 - '@esbuild/netbsd-x64': 0.21.5 - '@esbuild/openbsd-x64': 0.21.5 - '@esbuild/sunos-x64': 0.21.5 - '@esbuild/win32-arm64': 0.21.5 - '@esbuild/win32-ia32': 0.21.5 - '@esbuild/win32-x64': 0.21.5 - esbuild@0.25.11: optionalDependencies: '@esbuild/aix-ppc64': 0.25.11 @@ -8787,14 +8340,14 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-plugin-jsdoc@50.8.0(eslint@9.34.0(jiti@2.6.1)): + eslint-plugin-jsdoc@50.8.0(eslint@10.0.2(jiti@2.6.1)): dependencies: '@es-joy/jsdoccomment': 0.50.2 are-docs-informative: 0.0.2 comment-parser: 1.4.1 debug: 4.4.1 escape-string-regexp: 4.0.0 - eslint: 9.34.0(jiti@2.6.1) + eslint: 10.0.2(jiti@2.6.1) espree: 10.4.0 esquery: 1.6.0 parse-imports-exports: 0.2.4 @@ -8808,7 +8361,7 @@ snapshots: lodash: 4.17.21 vscode-json-languageservice: 4.2.1 - eslint-plugin-jsx-a11y@6.10.2(eslint@9.34.0(jiti@2.6.1)): + eslint-plugin-jsx-a11y@6.10.2(eslint@10.0.2(jiti@2.6.1)): dependencies: aria-query: 5.3.2 array-includes: 3.1.9 @@ -8818,7 +8371,7 @@ snapshots: axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 - eslint: 9.34.0(jiti@2.6.1) + eslint: 10.0.2(jiti@2.6.1) hasown: 2.0.2 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 @@ -8827,18 +8380,18 @@ snapshots: safe-regex-test: 1.1.0 string.prototype.includes: 2.0.1 - eslint-plugin-mocha@10.5.0(eslint@9.34.0(jiti@2.6.1)): + eslint-plugin-mocha@10.5.0(eslint@10.0.2(jiti@2.6.1)): dependencies: - eslint: 9.34.0(jiti@2.6.1) - eslint-utils: 3.0.0(eslint@9.34.0(jiti@2.6.1)) + eslint: 10.0.2(jiti@2.6.1) + eslint-utils: 3.0.0(eslint@10.0.2(jiti@2.6.1)) globals: 13.24.0 rambda: 7.5.0 - eslint-plugin-react-hooks@5.2.0(eslint@9.34.0(jiti@2.6.1)): + eslint-plugin-react-hooks@5.2.0(eslint@10.0.2(jiti@2.6.1)): dependencies: - eslint: 9.34.0(jiti@2.6.1) + eslint: 10.0.2(jiti@2.6.1) - eslint-plugin-react@7.37.5(eslint@9.34.0(jiti@2.6.1)): + eslint-plugin-react@7.37.5(eslint@10.0.2(jiti@2.6.1)): dependencies: array-includes: 3.1.9 array.prototype.findlast: 1.2.5 @@ -8846,7 +8399,7 @@ snapshots: array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 es-iterator-helpers: 1.2.1 - eslint: 9.34.0(jiti@2.6.1) + eslint: 10.0.2(jiti@2.6.1) estraverse: 5.3.0 hasown: 2.0.2 jsx-ast-utils: 3.3.5 @@ -8865,14 +8418,16 @@ snapshots: esrecurse: 4.3.0 estraverse: 4.3.0 - eslint-scope@8.4.0: + eslint-scope@9.1.1: dependencies: + '@types/esrecurse': 4.3.1 + '@types/estree': 1.0.8 esrecurse: 4.3.0 estraverse: 5.3.0 - eslint-utils@3.0.0(eslint@9.34.0(jiti@2.6.1)): + eslint-utils@3.0.0(eslint@10.0.2(jiti@2.6.1)): dependencies: - eslint: 9.34.0(jiti@2.6.1) + eslint: 10.0.2(jiti@2.6.1) eslint-visitor-keys: 2.1.0 eslint-visitor-keys@2.1.0: {} @@ -8881,30 +8436,28 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.34.0(jiti@2.6.1): + eslint-visitor-keys@5.0.1: {} + + eslint@10.0.2(jiti@2.6.1): dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.34.0(jiti@2.6.1)) - '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.21.0 - '@eslint/config-helpers': 0.3.1 - '@eslint/core': 0.15.2 - '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.34.0 - '@eslint/plugin-kit': 0.3.5 + '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.2(jiti@2.6.1)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.23.2 + '@eslint/config-helpers': 0.5.2 + '@eslint/core': 1.1.0 + '@eslint/plugin-kit': 0.6.0 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 '@types/estree': 1.0.8 - '@types/json-schema': 7.0.15 - ajv: 6.12.6 - chalk: 4.1.2 + ajv: 6.14.0 cross-spawn: 7.0.6 - debug: 4.4.1 + debug: 4.4.3 escape-string-regexp: 4.0.0 - eslint-scope: 8.4.0 - eslint-visitor-keys: 4.2.1 - espree: 10.4.0 - esquery: 1.6.0 + eslint-scope: 9.1.1 + eslint-visitor-keys: 5.0.1 + espree: 11.1.1 + esquery: 1.7.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 file-entry-cache: 8.0.0 @@ -8914,8 +8467,7 @@ snapshots: imurmurhash: 0.1.4 is-glob: 4.0.3 json-stable-stringify-without-jsonify: 1.0.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 + minimatch: 10.2.2 natural-compare: 1.4.0 optionator: 0.9.4 optionalDependencies: @@ -8925,16 +8477,26 @@ snapshots: espree@10.4.0: dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) eslint-visitor-keys: 4.2.1 + espree@11.1.1: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 5.0.1 + esprima@4.0.1: {} esquery@1.6.0: dependencies: estraverse: 5.3.0 + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + esrecurse@4.3.0: dependencies: estraverse: 5.3.0 @@ -8949,19 +8511,7 @@ snapshots: esutils@2.0.3: {} - execa@8.0.1: - dependencies: - cross-spawn: 7.0.6 - get-stream: 8.0.1 - human-signals: 5.0.0 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.3.0 - onetime: 6.0.0 - signal-exit: 4.1.0 - strip-final-newline: 3.0.0 - - expect-type@1.2.2: {} + expect-type@1.3.0: {} exsolve@1.0.7: {} @@ -9069,8 +8619,6 @@ snapshots: gensync@1.0.0-beta.2: {} - get-func-name@2.0.2: {} - get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -9091,8 +8639,6 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 - get-stream@8.0.1: {} - get-symbol-description@1.1.0: dependencies: call-bound: 1.0.4 @@ -9115,7 +8661,7 @@ snapshots: dependencies: foreground-child: 3.3.1 jackspeak: 4.1.1 - minimatch: 10.0.3 + minimatch: 10.2.2 minipass: 7.1.2 package-json-from-dist: 1.0.1 path-scurry: 2.0.0 @@ -9160,8 +8706,6 @@ snapshots: graceful-fs@4.2.11: {} - graphemer@1.4.0: {} - graphql-request@5.2.0(graphql@16.11.0): dependencies: '@graphql-typed-document-node/core': 3.2.0(graphql@16.11.0) @@ -9183,8 +8727,6 @@ snapshots: has-bigints@1.1.0: {} - has-flag@4.0.0: {} - has-property-descriptors@1.0.2: dependencies: es-define-property: 1.0.1 @@ -9223,21 +8765,19 @@ snapshots: http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 - debug: 4.4.1 + debug: 4.4.3 transitivePeerDependencies: - supports-color https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 - debug: 4.4.1 + debug: 4.4.3 transitivePeerDependencies: - supports-color human-id@4.1.1: {} - human-signals@5.0.0: {} - iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 @@ -9355,8 +8895,6 @@ snapshots: dependencies: call-bound: 1.0.4 - is-stream@3.0.0: {} - is-string@1.1.1: dependencies: call-bound: 1.0.4 @@ -9414,14 +8952,12 @@ snapshots: js-tokens@4.0.0: {} - js-tokens@9.0.1: {} - js-yaml@3.14.1: dependencies: argparse: 1.0.10 esprima: 4.0.1 - js-yaml@4.1.0: + js-yaml@4.1.1: dependencies: argparse: 2.0.1 @@ -9543,11 +9079,6 @@ snapshots: lightningcss-win32-arm64-msvc: 1.30.2 lightningcss-win32-x64-msvc: 1.30.2 - local-pkg@0.5.1: - dependencies: - mlly: 1.8.0 - pkg-types: 1.3.1 - locate-path@5.0.0: dependencies: p-locate: 4.1.0 @@ -9556,8 +9087,6 @@ snapshots: dependencies: p-locate: 5.0.0 - lodash.merge@4.6.2: {} - lodash.startcase@4.4.0: {} lodash@4.17.21: {} @@ -9566,12 +9095,6 @@ snapshots: dependencies: js-tokens: 4.0.0 - loupe@2.3.7: - dependencies: - get-func-name: 2.0.2 - - loupe@3.2.1: {} - lru-cache@10.4.3: {} lru-cache@11.1.0: {} @@ -9588,9 +9111,11 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - math-intrinsics@1.1.0: {} + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 - merge-stream@2.0.0: {} + math-intrinsics@1.1.0: {} merge2@1.4.1: {} @@ -9605,31 +9130,22 @@ snapshots: dependencies: mime-db: 1.52.0 - mimic-fn@4.0.0: {} - - minimatch@10.0.3: + minimatch@10.2.2: dependencies: - '@isaacs/brace-expansion': 5.0.0 + brace-expansion: 5.0.3 minimatch@3.1.2: dependencies: brace-expansion: 1.1.12 - minimatch@9.0.5: + minimatch@3.1.3: dependencies: - brace-expansion: 2.0.2 + brace-expansion: 1.1.12 minimist@1.2.8: {} minipass@7.1.2: {} - mlly@1.8.0: - dependencies: - acorn: 8.15.0 - pathe: 2.0.3 - pkg-types: 1.3.1 - ufo: 1.6.1 - mri@1.2.0: {} ms@2.1.3: {} @@ -9638,25 +9154,26 @@ snapshots: natural-compare@1.4.0: {} - next@16.0.10(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + next@16.2.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: - '@next/env': 16.0.10 + '@next/env': 16.2.1 '@swc/helpers': 0.5.15 + baseline-browser-mapping: 2.10.11 caniuse-lite: 1.0.30001737 postcss: 8.4.31 react: 19.2.0 react-dom: 19.2.0(react@19.2.0) styled-jsx: 5.1.6(react@19.2.0) optionalDependencies: - '@next/swc-darwin-arm64': 16.0.10 - '@next/swc-darwin-x64': 16.0.10 - '@next/swc-linux-arm64-gnu': 16.0.10 - '@next/swc-linux-arm64-musl': 16.0.10 - '@next/swc-linux-x64-gnu': 16.0.10 - '@next/swc-linux-x64-musl': 16.0.10 - '@next/swc-win32-arm64-msvc': 16.0.10 - '@next/swc-win32-x64-msvc': 16.0.10 - sharp: 0.34.4 + '@next/swc-darwin-arm64': 16.2.1 + '@next/swc-darwin-x64': 16.2.1 + '@next/swc-linux-arm64-gnu': 16.2.1 + '@next/swc-linux-arm64-musl': 16.2.1 + '@next/swc-linux-x64-gnu': 16.2.1 + '@next/swc-linux-x64-musl': 16.2.1 + '@next/swc-win32-arm64-msvc': 16.2.1 + '@next/swc-win32-x64-msvc': 16.2.1 + sharp: 0.34.5 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -9671,10 +9188,6 @@ snapshots: normalize-path@3.0.0: {} - npm-run-path@5.3.0: - dependencies: - path-key: 4.0.0 - nth-check@2.1.1: dependencies: boolbase: 1.0.0 @@ -9717,16 +9230,14 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + obug@2.1.1: {} + on-exit-leak-free@2.1.2: {} once@1.4.0: dependencies: wrappy: 1.0.2 - onetime@6.0.0: - dependencies: - mimic-fn: 4.0.0 - optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -9756,10 +9267,6 @@ snapshots: dependencies: yocto-queue: 0.1.0 - p-limit@5.0.0: - dependencies: - yocto-queue: 1.2.1 - p-locate@4.1.0: dependencies: p-limit: 2.3.0 @@ -9805,8 +9312,6 @@ snapshots: path-key@3.1.1: {} - path-key@4.0.0: {} - path-parse@1.0.7: {} path-scurry@2.0.0: @@ -9816,14 +9321,8 @@ snapshots: path-type@4.0.0: {} - pathe@1.1.2: {} - pathe@2.0.3: {} - pathval@1.1.1: {} - - pathval@2.0.1: {} - picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -9868,12 +9367,6 @@ snapshots: sonic-boom: 4.2.0 thread-stream: 3.1.0 - pkg-types@1.3.1: - dependencies: - confbox: 0.1.8 - mlly: 1.8.0 - pathe: 2.0.3 - possible-typed-array-names@1.1.0: {} postcss@8.4.31: @@ -9894,12 +9387,6 @@ snapshots: prettier@3.6.2: {} - pretty-format@29.7.0: - dependencies: - '@jest/schemas': 29.6.3 - ansi-styles: 5.2.0 - react-is: 18.3.1 - process-warning@5.0.0: {} promise-polyfill@8.3.0: {} @@ -9941,8 +9428,6 @@ snapshots: react-is@16.13.1: {} - react-is@18.3.1: {} - react-phone-number-input@3.4.12(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: classnames: 2.5.1 @@ -10201,34 +9686,36 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.1 - sharp@0.34.4: + sharp@0.34.5: dependencies: '@img/colour': 1.0.0 detect-libc: 2.1.2 semver: 7.7.3 optionalDependencies: - '@img/sharp-darwin-arm64': 0.34.4 - '@img/sharp-darwin-x64': 0.34.4 - '@img/sharp-libvips-darwin-arm64': 1.2.3 - '@img/sharp-libvips-darwin-x64': 1.2.3 - '@img/sharp-libvips-linux-arm': 1.2.3 - '@img/sharp-libvips-linux-arm64': 1.2.3 - '@img/sharp-libvips-linux-ppc64': 1.2.3 - '@img/sharp-libvips-linux-s390x': 1.2.3 - '@img/sharp-libvips-linux-x64': 1.2.3 - '@img/sharp-libvips-linuxmusl-arm64': 1.2.3 - '@img/sharp-libvips-linuxmusl-x64': 1.2.3 - '@img/sharp-linux-arm': 0.34.4 - '@img/sharp-linux-arm64': 0.34.4 - '@img/sharp-linux-ppc64': 0.34.4 - '@img/sharp-linux-s390x': 0.34.4 - '@img/sharp-linux-x64': 0.34.4 - '@img/sharp-linuxmusl-arm64': 0.34.4 - '@img/sharp-linuxmusl-x64': 0.34.4 - '@img/sharp-wasm32': 0.34.4 - '@img/sharp-win32-arm64': 0.34.4 - '@img/sharp-win32-ia32': 0.34.4 - '@img/sharp-win32-x64': 0.34.4 + '@img/sharp-darwin-arm64': 0.34.5 + '@img/sharp-darwin-x64': 0.34.5 + '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-linux-arm': 0.34.5 + '@img/sharp-linux-arm64': 0.34.5 + '@img/sharp-linux-ppc64': 0.34.5 + '@img/sharp-linux-riscv64': 0.34.5 + '@img/sharp-linux-s390x': 0.34.5 + '@img/sharp-linux-x64': 0.34.5 + '@img/sharp-linuxmusl-arm64': 0.34.5 + '@img/sharp-linuxmusl-x64': 0.34.5 + '@img/sharp-wasm32': 0.34.5 + '@img/sharp-win32-arm64': 0.34.5 + '@img/sharp-win32-ia32': 0.34.5 + '@img/sharp-win32-x64': 0.34.5 optional: true shebang-command@2.0.0: @@ -10303,7 +9790,7 @@ snapshots: stackback@0.0.2: {} - std-env@3.10.0: {} + std-env@4.0.0: {} stop-iteration-iterator@1.1.0: dependencies: @@ -10382,29 +9869,15 @@ snapshots: strip-bom@3.0.0: {} - strip-final-newline@3.0.0: {} - strip-json-comments@3.1.1: {} strip-json-comments@5.0.3: {} - strip-literal@2.1.1: - dependencies: - js-tokens: 9.0.1 - - strip-literal@3.1.0: - dependencies: - js-tokens: 9.0.1 - styled-jsx@5.1.6(react@19.2.0): dependencies: client-only: 0.0.1 react: 19.2.0 - supports-color@7.2.0: - dependencies: - has-flag: 4.0.0 - supports-preserve-symlinks-flag@1.0.0: {} symbol-tree@3.2.4: {} @@ -10431,24 +9904,16 @@ snapshots: tinybench@2.9.0: {} - tinyexec@0.3.2: {} - tinyexec@1.0.1: {} + tinyexec@1.0.4: {} + tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 - tinypool@0.8.4: {} - - tinypool@1.1.1: {} - - tinyrainbow@2.0.0: {} - - tinyspy@2.2.1: {} - - tinyspy@4.0.4: {} + tinyrainbow@3.1.0: {} tldts-core@6.1.86: {} @@ -10472,7 +9937,7 @@ snapshots: tree-kill@1.2.2: {} - ts-api-utils@2.1.0(typescript@5.9.2): + ts-api-utils@2.4.0(typescript@5.9.2): dependencies: typescript: 5.9.2 @@ -10514,8 +9979,6 @@ snapshots: dependencies: prelude-ls: 1.2.1 - type-detect@4.1.0: {} - type-fest@0.20.2: {} typed-array-buffer@1.0.3: @@ -10582,7 +10045,7 @@ snapshots: unplugin@2.3.10: dependencies: '@jridgewell/remapping': 2.3.5 - acorn: 8.15.0 + acorn: 8.16.0 picomatch: 4.0.3 webpack-virtual-modules: 0.6.2 @@ -10624,129 +10087,52 @@ snapshots: - '@types/react' - '@types/react-dom' - vite-node@1.6.1(@types/node@22.18.12)(lightningcss@1.30.2): - dependencies: - cac: 6.7.14 - debug: 4.4.1 - pathe: 1.1.2 - picocolors: 1.1.1 - vite: 5.4.21(@types/node@22.18.12)(lightningcss@1.30.2) - transitivePeerDependencies: - - '@types/node' - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - vite-node@3.2.4(@types/node@22.18.12)(lightningcss@1.30.2): + vite@6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6): dependencies: - cac: 6.7.14 - debug: 4.4.1 - es-module-lexer: 1.7.0 - pathe: 2.0.3 - vite: 5.4.21(@types/node@22.18.12)(lightningcss@1.30.2) - transitivePeerDependencies: - - '@types/node' - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - vite@5.4.21(@types/node@22.18.12)(lightningcss@1.30.2): - dependencies: - esbuild: 0.21.5 + esbuild: 0.25.11 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 postcss: 8.5.6 rollup: 4.52.5 + tinyglobby: 0.2.15 optionalDependencies: '@types/node': 22.18.12 fsevents: 2.3.3 + jiti: 2.6.1 lightningcss: 1.30.2 + tsx: 4.20.6 - vitefu@1.1.1(vite@5.4.21(@types/node@22.18.12)(lightningcss@1.30.2)): + vitefu@1.1.1(vite@6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)): optionalDependencies: - vite: 5.4.21(@types/node@22.18.12)(lightningcss@1.30.2) - - vitest@1.6.1(@types/node@22.18.12)(jsdom@26.1.0)(lightningcss@1.30.2): - dependencies: - '@vitest/expect': 1.6.1 - '@vitest/runner': 1.6.1 - '@vitest/snapshot': 1.6.1 - '@vitest/spy': 1.6.1 - '@vitest/utils': 1.6.1 - acorn-walk: 8.3.4 - chai: 4.5.0 - debug: 4.4.1 - execa: 8.0.1 - local-pkg: 0.5.1 - magic-string: 0.30.19 - pathe: 1.1.2 - picocolors: 1.1.1 - std-env: 3.10.0 - strip-literal: 2.1.1 - tinybench: 2.9.0 - tinypool: 0.8.4 - vite: 5.4.21(@types/node@22.18.12)(lightningcss@1.30.2) - vite-node: 1.6.1(@types/node@22.18.12)(lightningcss@1.30.2) - why-is-node-running: 2.3.0 - optionalDependencies: - '@types/node': 22.18.12 - jsdom: 26.1.0 - transitivePeerDependencies: - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - vitest@3.2.4(@types/node@22.18.12)(jsdom@26.1.0)(lightningcss@1.30.2): - dependencies: - '@types/chai': 5.2.3 - '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@5.4.21(@types/node@22.18.12)(lightningcss@1.30.2)) - '@vitest/pretty-format': 3.2.4 - '@vitest/runner': 3.2.4 - '@vitest/snapshot': 3.2.4 - '@vitest/spy': 3.2.4 - '@vitest/utils': 3.2.4 - chai: 5.3.3 - debug: 4.4.1 - expect-type: 1.2.2 - magic-string: 0.30.19 + vite: 6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6) + + vitest@4.1.2(@types/node@22.18.12)(jsdom@26.1.0)(vite@6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)): + dependencies: + '@vitest/expect': 4.1.2 + '@vitest/mocker': 4.1.2(vite@6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6)) + '@vitest/pretty-format': 4.1.2 + '@vitest/runner': 4.1.2 + '@vitest/snapshot': 4.1.2 + '@vitest/spy': 4.1.2 + '@vitest/utils': 4.1.2 + es-module-lexer: 2.0.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 pathe: 2.0.3 picomatch: 4.0.3 - std-env: 3.10.0 + std-env: 4.0.0 tinybench: 2.9.0 - tinyexec: 0.3.2 + tinyexec: 1.0.4 tinyglobby: 0.2.15 - tinypool: 1.1.1 - tinyrainbow: 2.0.0 - vite: 5.4.21(@types/node@22.18.12)(lightningcss@1.30.2) - vite-node: 3.2.4(@types/node@22.18.12)(lightningcss@1.30.2) + tinyrainbow: 3.1.0 + vite: 6.4.1(@types/node@22.18.12)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.6) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.18.12 jsdom: 26.1.0 transitivePeerDependencies: - - less - - lightningcss - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser vscode-json-languageservice@4.2.1: dependencies: @@ -10873,6 +10259,4 @@ snapshots: yocto-queue@0.1.0: {} - yocto-queue@1.2.1: {} - zod@3.25.76: {}