From 89678542e010217074cfff6c6013f540c4affb61 Mon Sep 17 00:00:00 2001 From: Scott Kirkland Date: Tue, 24 Feb 2026 15:28:33 -0800 Subject: [PATCH 1/4] ability to do dev bypass auth logins --- web/src/app/auth/login/page.tsx | 41 +++++++++++++++++++ web/src/auth.ts | 34 ++++++++++++++++ web/src/lib/devAuthBypass.ts | 72 +++++++++++++++++++++++++++++++++ 3 files changed, 147 insertions(+) create mode 100644 web/src/lib/devAuthBypass.ts diff --git a/web/src/app/auth/login/page.tsx b/web/src/app/auth/login/page.tsx index c7065a4..d95eade 100644 --- a/web/src/app/auth/login/page.tsx +++ b/web/src/app/auth/login/page.tsx @@ -5,9 +5,17 @@ import Image from 'next/image'; import FooterLinks from '@/components/layout/footerLinks'; import { campuses } from '@/lib/constants'; +import { + DEV_AUTH_BYPASS_PROVIDER_ID, + getDevAuthUserId, + isDevAuthBypassEnabled, +} from '@/lib/devAuthBypass'; import { signIn } from '../../../auth'; +const devAuthBypassEnabled = isDevAuthBypassEnabled(); +const devAuthUserId = getDevAuthUserId(); + export const generateMetadata = async () => { return { title: 'Login', @@ -31,6 +39,22 @@ async function signInHandler(formData: FormData) { ); } +// Server action to handle dev-only sign in using credentials provider. +async function devBypassSignInHandler(formData: FormData) { + 'use server'; + if (!devAuthBypassEnabled) { + throw new Error('Dev auth bypass is disabled'); + } + + const callbackUrl = (formData.get('callbackUrl') as string) || '/'; + const userId = (formData.get('userId') as string) || devAuthUserId; + + await signIn( + DEV_AUTH_BYPASS_PROVIDER_ID, + { redirectTo: callbackUrl, userId } as any + ); +} + interface LoginProps { searchParams: { callbackUrl?: string }; } @@ -51,6 +75,23 @@ const Login: React.FC = async (props) => { question and voila PolicyWonk will do it’s best to answer it!

Available Campuses

+ {devAuthBypassEnabled && ( + <> +

Development

+
+ + +
+ +
+
+ + )}
diff --git a/web/src/auth.ts b/web/src/auth.ts index 28d63f2..2ba891b 100644 --- a/web/src/auth.ts +++ b/web/src/auth.ts @@ -2,10 +2,18 @@ import NextAuth, { Profile } from 'next-auth'; import BoxyHQSAMLProvider from 'next-auth/providers/boxyhq-saml'; import { setCurrentGroup } from './lib/cookies'; +import { + DEV_AUTH_BYPASS_PROVIDER_ID, + getDevAuthBypassProvider, + getDevAuthGroup, + isDevAuthBypassEnabled, +} from './lib/devAuthBypass'; import { getGroupNameFromAffiliation } from './lib/groups'; import { User } from './models/user'; import { ensureUserExists } from './services/userService'; +const devAuthBypassProvider = getDevAuthBypassProvider(); + const SamlClaims = { name: 'urn:oid:2.16.840.1.113730.3.1.241', upn: 'urn:oid:1.3.6.1.4.1.5923.1.1.1.6', // unique id - resembles email but isn't actually email @@ -26,6 +34,7 @@ export const { signOut, } = NextAuth({ providers: [ + ...(devAuthBypassProvider ? [devAuthBypassProvider] : []), BoxyHQSAMLProvider({ authorization: { params: { scope: '' } }, // This is needed for OAuth 2.0 flow, otherwise default to openid (from docs) issuer: process.env.SSO_ISSUER_URL, @@ -40,6 +49,31 @@ export const { // params.profile has user info -- params.profile.raw has the shibboleth claims // params.profile.requested.tenant has the tenant as defined by boxy -- might be nice for multi-tenant if (params.trigger === 'signIn') { + if (params.account?.provider === DEV_AUTH_BYPASS_PROVIDER_ID) { + if (!isDevAuthBypassEnabled()) { + throw new Error('Dev auth bypass is disabled'); + } + + const userId = Number(params.user?.id); + if (!Number.isInteger(userId) || userId <= 0) { + throw new Error('Invalid dev bypass user id'); + } + + await setCurrentGroup(getDevAuthGroup()); + + return { + ...params.token, + name: params.user?.name, + email: params.user?.email, + userId, + }; + } + + // Only BoxyHQ SAML sign-ins should reach the logic below. + if (params.account?.provider !== 'boxyhq-saml') { + return params.token; + } + // profile is only available on sign in const profileExtended = params.profile as ProfileWithRaw | undefined; diff --git a/web/src/lib/devAuthBypass.ts b/web/src/lib/devAuthBypass.ts new file mode 100644 index 0000000..11dfa30 --- /dev/null +++ b/web/src/lib/devAuthBypass.ts @@ -0,0 +1,72 @@ +import type { Provider } from 'next-auth/providers'; +import Credentials from 'next-auth/providers/credentials'; + +import prisma from './db'; + +export const DEV_AUTH_BYPASS_PROVIDER_ID = 'dev-bypass'; + +const truthyValues = new Set(['1', 'true', 'yes']); + +const isTruthyEnvValue = (value: string | undefined) => { + if (!value) { + return false; + } + + return truthyValues.has(value.toLowerCase()); +}; + +const parsePositiveInteger = (value: string) => { + const parsed = Number(value); + + if (!Number.isInteger(parsed) || parsed <= 0) { + return null; + } + + return parsed; +}; + +export const isDevAuthBypassEnabled = () => + process.env.NODE_ENV !== 'production' && + isTruthyEnvValue(process.env.DEV_AUTH_BYPASS); + +export const getDevAuthUserId = () => process.env.DEV_AUTH_USER_ID ?? '1'; + +export const getDevAuthGroup = () => process.env.DEV_AUTH_GROUP ?? 'ucdavis'; + +export const getDevAuthBypassProvider = (): Provider | null => { + if (!isDevAuthBypassEnabled()) { + return null; + } + + return Credentials({ + id: DEV_AUTH_BYPASS_PROVIDER_ID, + name: 'Dev Bypass', + credentials: { + userId: { label: 'User ID', type: 'text' }, + }, + async authorize(credentials) { + const userIdRaw = + (credentials?.userId as string | undefined) ?? getDevAuthUserId(); + const userId = parsePositiveInteger(userIdRaw); + + if (!userId) { + return null; + } + + const user = await prisma.users.findUnique({ + where: { id: userId }, + select: { id: true, name: true, email: true }, + }); + + if (!user) { + return null; + } + + return { + id: String(user.id), + name: user.name || `User ${user.id}`, + email: user.email || undefined, + }; + }, + }); +}; From ea700504be5a6c6ff1517b8340f8d4350ebf864e Mon Sep 17 00:00:00 2001 From: Scott Kirkland Date: Wed, 25 Feb 2026 09:28:55 -0800 Subject: [PATCH 2/4] upgrade to aisdk6 and remove RSC --- .gitignore | 7 + web/package-lock.json | 3502 ++++++++++++----- web/package.json | 12 +- .../app/(home)/[group]/chat/[chatid]/page.tsx | 9 +- .../(home)/[group]/share/[shareid]/page.tsx | 14 +- web/src/app/api/chat/route.ts | 122 + .../components/chat/answer/chatActions.tsx | 31 +- .../chat/answer/feedbackButtons.tsx | 32 +- web/src/components/chat/answer/shareModal.tsx | 51 +- .../components/chat/answer/wonkMessage.tsx | 24 +- web/src/components/chat/ask/chatInput.tsx | 66 +- .../components/chat/ask/defaultQuestions.tsx | 68 - web/src/components/chat/main.tsx | 201 +- web/src/lib/actions.tsx | 212 +- web/src/lib/aiProvider.tsx | 52 - web/src/lib/chat/citationsTransform.test.ts | 152 + web/src/lib/chat/citationsTransform.ts | 123 + web/src/lib/hooks/useStreamableText.ts | 66 - web/vitest.config.mts | 8 + 19 files changed, 3145 insertions(+), 1607 deletions(-) create mode 100644 web/src/app/api/chat/route.ts delete mode 100644 web/src/components/chat/ask/defaultQuestions.tsx delete mode 100644 web/src/lib/aiProvider.tsx create mode 100644 web/src/lib/chat/citationsTransform.test.ts create mode 100644 web/src/lib/chat/citationsTransform.ts delete mode 100644 web/src/lib/hooks/useStreamableText.ts create mode 100644 web/vitest.config.mts diff --git a/.gitignore b/.gitignore index 553194d..16ab04c 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,10 @@ __pycache__/ # dev container artifacts .devcontainer/pg_data/ web/package-lock.json + +# playwright-cli artifacts +.playwright-cli/ + +# Codex/Cursor agent artifacts +web/.agents/ +web/skills-lock.json diff --git a/web/package-lock.json b/web/package-lock.json index ff391d0..d8cf410 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -8,8 +8,8 @@ "name": "policywonk", "version": "0.1.0", "dependencies": { - "@ai-sdk/openai": "^2.0.88", - "@ai-sdk/rsc": "^1.0.117", + "@ai-sdk/openai": "^3.0.33", + "@ai-sdk/react": "^3.0.101", "@elastic/elasticsearch": "^8.12.2", "@fortawesome/free-brands-svg-icons": "^6.5.1", "@fortawesome/free-regular-svg-icons": "^6.5.1", @@ -18,7 +18,7 @@ "@next/third-parties": "16.1.5", "@prisma/client": "^6.5.0", "@ucdavis/gunrockin": "^1.1.3", - "ai": "5.0.114", + "ai": "^6.0.99", "bootstrap": "^5.3.3", "canvas-confetti": "^1.9.3", "framer-motion": "^12.29.2", @@ -48,18 +48,19 @@ "prettier": "^3.2.5", "prisma": "^6.5.0", "sass": "^1.72.0", - "typescript": "^5" + "typescript": "^5", + "vitest": "^4.0.18" } }, "node_modules/@ai-sdk/gateway": { - "version": "2.0.21", - "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-2.0.21.tgz", - "integrity": "sha512-BwV7DU/lAm3Xn6iyyvZdWgVxgLu3SNXzl5y57gMvkW4nGhAOV5269IrJzQwGt03bb107sa6H6uJwWxc77zXoGA==", + "version": "3.0.55", + "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-3.0.55.tgz", + "integrity": "sha512-7xMeTJnCjwRwXKVCiv4Ly4qzWvDuW3+W1WIV0X1EFu6W83d4mEhV9bFArto10MeTw40ewuDjrbrZd21mXKohkw==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "2.0.0", - "@ai-sdk/provider-utils": "3.0.19", - "@vercel/oidc": "3.0.5" + "@ai-sdk/provider": "3.0.8", + "@ai-sdk/provider-utils": "4.0.15", + "@vercel/oidc": "3.1.0" }, "engines": { "node": ">=18" @@ -69,9 +70,9 @@ } }, "node_modules/@ai-sdk/gateway/node_modules/@ai-sdk/provider": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz", - "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.8.tgz", + "integrity": "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ==", "license": "Apache-2.0", "dependencies": { "json-schema": "^0.4.0" @@ -81,13 +82,13 @@ } }, "node_modules/@ai-sdk/gateway/node_modules/@ai-sdk/provider-utils": { - "version": "3.0.19", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.19.tgz", - "integrity": "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA==", + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.15.tgz", + "integrity": "sha512-8XiKWbemmCbvNN0CLR9u3PQiet4gtEVIrX4zzLxnCj06AwsEDJwJVBbKrEI4t6qE8XRSIvU2irka0dcpziKW6w==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "2.0.0", - "@standard-schema/spec": "^1.0.0", + "@ai-sdk/provider": "3.0.8", + "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "engines": { @@ -98,13 +99,13 @@ } }, "node_modules/@ai-sdk/openai": { - "version": "2.0.89", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-2.0.89.tgz", - "integrity": "sha512-4+qWkBCbL9HPKbgrUO/F2uXZ8GqrYxHa8SWEYIzxEJ9zvWw3ISr3t1/27O1i8MGSym+PzEyHBT48EV4LAwWaEw==", + "version": "3.0.33", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-3.0.33.tgz", + "integrity": "sha512-O/8SVKAiwFHkGAUfBnrLb7L2IjbpP9ySWbmOktOfa0KtzutZkmKNrJ5CtB5dj+lwuENbOuZeRsnsZdOjar7hig==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "2.0.1", - "@ai-sdk/provider-utils": "3.0.20" + "@ai-sdk/provider": "3.0.8", + "@ai-sdk/provider-utils": "4.0.15" }, "engines": { "node": ">=18" @@ -113,10 +114,10 @@ "zod": "^3.25.76 || ^4.1.8" } }, - "node_modules/@ai-sdk/provider": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.1.tgz", - "integrity": "sha512-KCUwswvsC5VsW2PWFqF8eJgSCu5Ysj7m1TxiHTVA6g7k360bk0RNQENT8KTMAYEs+8fWPD3Uu4dEmzGHc+jGng==", + "node_modules/@ai-sdk/openai/node_modules/@ai-sdk/provider": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.8.tgz", + "integrity": "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ==", "license": "Apache-2.0", "dependencies": { "json-schema": "^0.4.0" @@ -125,14 +126,14 @@ "node": ">=18" } }, - "node_modules/@ai-sdk/provider-utils": { - "version": "3.0.20", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.20.tgz", - "integrity": "sha512-iXHVe0apM2zUEzauqJwqmpC37A5rihrStAih5Ks+JE32iTe4LZ58y17UGBjpQQTCRw9YxMeo2UFLxLpBluyvLQ==", + "node_modules/@ai-sdk/openai/node_modules/@ai-sdk/provider-utils": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.15.tgz", + "integrity": "sha512-8XiKWbemmCbvNN0CLR9u3PQiet4gtEVIrX4zzLxnCj06AwsEDJwJVBbKrEI4t6qE8XRSIvU2irka0dcpziKW6w==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "2.0.1", - "@standard-schema/spec": "^1.0.0", + "@ai-sdk/provider": "3.0.8", + "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "engines": { @@ -142,66 +143,45 @@ "zod": "^3.25.76 || ^4.1.8" } }, - "node_modules/@ai-sdk/rsc": { - "version": "1.0.125", - "resolved": "https://registry.npmjs.org/@ai-sdk/rsc/-/rsc-1.0.125.tgz", - "integrity": "sha512-c4KTPVdZNj4qB7e5Yen4hWmFr/GMKI4eru6+QtqQ2vdKS0OLSZJTnTMsVx4Q3iV1fpJarCy5PtBm955CV66cMg==", + "node_modules/@ai-sdk/react": { + "version": "3.0.101", + "resolved": "https://registry.npmjs.org/@ai-sdk/react/-/react-3.0.101.tgz", + "integrity": "sha512-ifQtOldMUbIncxQsaotP4UOM1yBk0EVwR211NFu51+El/DptHz9GhKlBbTVLiqoMeTeVhPlGianhc7MRiKFJnA==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "2.0.1", - "@ai-sdk/provider-utils": "3.0.20", - "ai": "5.0.123", - "jsondiffpatch": "0.7.3" + "@ai-sdk/provider-utils": "4.0.15", + "ai": "6.0.99", + "swr": "^2.2.5", + "throttleit": "2.1.0" }, "engines": { "node": ">=18" }, "peerDependencies": { - "react": "^18 || ~19.0.1 || ~19.1.2 || ^19.2.1", - "zod": "^3.25.76 || ^4.1.8" - }, - "peerDependenciesMeta": { - "zod": { - "optional": true - } + "react": "^18 || ~19.0.1 || ~19.1.2 || ^19.2.1" } }, - "node_modules/@ai-sdk/rsc/node_modules/@ai-sdk/gateway": { - "version": "2.0.29", - "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-2.0.29.tgz", - "integrity": "sha512-1b7E9F/B5gex/1uCkhs+sGIbH0KsZOItHnNz3iY5ir+nc4ZUA6WOU5Cu2w1USlc+3UVbhf+H+iNLlxVjLe4VvQ==", + "node_modules/@ai-sdk/react/node_modules/@ai-sdk/provider": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.8.tgz", + "integrity": "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "2.0.1", - "@ai-sdk/provider-utils": "3.0.20", - "@vercel/oidc": "3.1.0" + "json-schema": "^0.4.0" }, "engines": { "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.25.76 || ^4.1.8" - } - }, - "node_modules/@ai-sdk/rsc/node_modules/@vercel/oidc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@vercel/oidc/-/oidc-3.1.0.tgz", - "integrity": "sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w==", - "license": "Apache-2.0", - "engines": { - "node": ">= 20" } }, - "node_modules/@ai-sdk/rsc/node_modules/ai": { - "version": "5.0.123", - "resolved": "https://registry.npmjs.org/ai/-/ai-5.0.123.tgz", - "integrity": "sha512-V3Imb0tg0pHCa6a/VsoW/FZpT07mwUw/4Hj6nexJC1Nvf1eyKQJyaYVkl+YTLnA8cKQSUkoarKhXWbFy4CSgjw==", + "node_modules/@ai-sdk/react/node_modules/@ai-sdk/provider-utils": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.15.tgz", + "integrity": "sha512-8XiKWbemmCbvNN0CLR9u3PQiet4gtEVIrX4zzLxnCj06AwsEDJwJVBbKrEI4t6qE8XRSIvU2irka0dcpziKW6w==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/gateway": "2.0.29", - "@ai-sdk/provider": "2.0.1", - "@ai-sdk/provider-utils": "3.0.20", - "@opentelemetry/api": "1.9.0" + "@ai-sdk/provider": "3.0.8", + "@standard-schema/spec": "^1.1.0", + "eventsource-parser": "^3.0.6" }, "engines": { "node": ">=18" @@ -488,12 +468,6 @@ "node": ">=6.9.0" } }, - "node_modules/@dmsnell/diff-match-patch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@dmsnell/diff-match-patch/-/diff-match-patch-1.1.0.tgz", - "integrity": "sha512-yejLPmM5pjsGvxS9gXablUSbInW7H976c/FJ4iQxWIm7/38xBySRemTPDe34lhg1gVLbJntX0+sH0jYfU+PN9A==", - "license": "Apache-2.0" - }, "node_modules/@elastic/elasticsearch": { "version": "8.19.1", "resolved": "https://registry.npmjs.org/@elastic/elasticsearch/-/elasticsearch-8.19.1.tgz", @@ -560,1140 +534,1984 @@ "tslib": "^2.4.0" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "node": ">=18" } }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=18" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=18" } }, - "node_modules/@eslint/config-array": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", - "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=18" } }, - "node_modules/@eslint/js": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", - "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" + "node": ">=18" } }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@fortawesome/fontawesome-common-types": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-7.1.0.tgz", - "integrity": "sha512-l/BQM7fYntsCI//du+6sEnHOP6a74UixFyOYUyz2DLMXKx+6DEhfR3F2NYGE45XH1JJuIamacb4IZs9S0ZOWLA==", + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "dev": true, "license": "MIT", - "peer": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6" + "node": ">=18" } }, - "node_modules/@fortawesome/fontawesome-svg-core": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-7.1.0.tgz", - "integrity": "sha512-fNxRUk1KhjSbnbuBxlWSnBLKLBNun52ZBTcs22H/xEEzM6Ap81ZFTQ4bZBxVQGQgVY0xugKGoRcCbaKjLQ3XZA==", + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "dev": true, "license": "MIT", - "peer": true, - "dependencies": { - "@fortawesome/fontawesome-common-types": "7.1.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6" + "node": ">=18" } }, - "node_modules/@fortawesome/free-brands-svg-icons": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.7.2.tgz", - "integrity": "sha512-zu0evbcRTgjKfrr77/2XX+bU+kuGfjm0LbajJHVIgBWNIDzrhpRxiCPNT8DW5AdmSsq7Mcf9D1bH0aSeSUSM+Q==", - "license": "(CC-BY-4.0 AND MIT)", - "dependencies": { - "@fortawesome/fontawesome-common-types": "6.7.2" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/free-brands-svg-icons/node_modules/@fortawesome/fontawesome-common-types": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.2.tgz", - "integrity": "sha512-Zs+YeHUC5fkt7Mg1l6XTniei3k4bwG/yo3iFUtZWd/pMx9g3fdvkSK9E0FOC+++phXOka78uJcYb8JaFkW52Xg==", + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/free-regular-svg-icons": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.7.2.tgz", - "integrity": "sha512-7Z/ur0gvCMW8G93dXIQOkQqHo2M5HLhYrRVC0//fakJXxcF1VmMPsxnG6Ee8qEylA8b8Q3peQXWMNZ62lYF28g==", - "license": "(CC-BY-4.0 AND MIT)", - "dependencies": { - "@fortawesome/fontawesome-common-types": "6.7.2" - }, - "engines": { - "node": ">=6" + "node": ">=18" } }, - "node_modules/@fortawesome/free-regular-svg-icons/node_modules/@fortawesome/fontawesome-common-types": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.2.tgz", - "integrity": "sha512-Zs+YeHUC5fkt7Mg1l6XTniei3k4bwG/yo3iFUtZWd/pMx9g3fdvkSK9E0FOC+++phXOka78uJcYb8JaFkW52Xg==", + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/free-solid-svg-icons": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.7.2.tgz", - "integrity": "sha512-GsBrnOzU8uj0LECDfD5zomZJIjrPhIlWU82AHwa2s40FKH+kcxQaBvBo3Z4TxyZHIyX8XTDxsyA33/Vx9eFuQA==", - "license": "(CC-BY-4.0 AND MIT)", - "dependencies": { - "@fortawesome/fontawesome-common-types": "6.7.2" - }, - "engines": { - "node": ">=6" + "node": ">=18" } }, - "node_modules/@fortawesome/free-solid-svg-icons/node_modules/@fortawesome/fontawesome-common-types": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.2.tgz", - "integrity": "sha512-Zs+YeHUC5fkt7Mg1l6XTniei3k4bwG/yo3iFUtZWd/pMx9g3fdvkSK9E0FOC+++phXOka78uJcYb8JaFkW52Xg==", + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/react-fontawesome": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.6.tgz", - "integrity": "sha512-mtBFIi1UsYQo7rYonYFkjgYKGoL8T+fEH6NGUpvuqtY3ytMsAoDaPo5rk25KuMtKDipY4bGYM/CkmCHA1N3FUg==", - "deprecated": "v0.2.x is no longer supported. Unless you are still using FontAwesome 5, please update to v3.1.1 or greater.", - "license": "MIT", - "dependencies": { - "prop-types": "^15.8.1" - }, - "peerDependencies": { - "@fortawesome/fontawesome-svg-core": "~1 || ~6 || ~7", - "react": "^16.3 || ^17.0.0 || ^18.0.0 || ^19.0.0" + "node": ">=18" } }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.18.0" + "node": ">=18" } }, - "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.4.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.18.0" + "node": ">=18" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "node": ">=18" } }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@img/colour": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", - "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", "license": "MIT", "optional": true, + "os": [ + "netbsd" + ], "engines": { "node": ">=18" } }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", "cpu": [ "arm64" ], - "license": "Apache-2.0", + "dev": true, + "license": "MIT", "optional": true, "os": [ - "darwin" + "openbsd" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" + "node": ">=18" } }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", "cpu": [ "x64" ], - "license": "Apache-2.0", + "dev": true, + "license": "MIT", "optional": true, "os": [ - "darwin" + "openbsd" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" + "node": ">=18" } }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", "cpu": [ "arm64" ], - "license": "LGPL-3.0-or-later", + "dev": true, + "license": "MIT", "optional": true, "os": [ - "darwin" + "openharmony" ], - "funding": { - "url": "https://opencollective.com/libvips" + "engines": { + "node": ">=18" } }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", "cpu": [ "x64" ], - "license": "LGPL-3.0-or-later", + "dev": true, + "license": "MIT", "optional": true, "os": [ - "darwin" + "sunos" ], - "funding": { - "url": "https://opencollective.com/libvips" + "engines": { + "node": ">=18" } }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", "cpu": [ - "arm" + "arm64" ], - "license": "LGPL-3.0-or-later", + "dev": true, + "license": "MIT", "optional": true, "os": [ - "linux" + "win32" ], - "funding": { - "url": "https://opencollective.com/libvips" + "engines": { + "node": ">=18" } }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", "cpu": [ - "arm64" + "ia32" ], - "license": "LGPL-3.0-or-later", + "dev": true, + "license": "MIT", "optional": true, "os": [ - "linux" + "win32" ], - "funding": { - "url": "https://opencollective.com/libvips" + "engines": { + "node": ">=18" } }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", "cpu": [ - "ppc64" + "x64" ], - "license": "LGPL-3.0-or-later", + "dev": true, + "license": "MIT", "optional": true, "os": [ - "linux" + "win32" ], - "funding": { - "url": "https://opencollective.com/libvips" + "engines": { + "node": ">=18" } }, - "node_modules/@img/sharp-libvips-linux-riscv64": { + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", + "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", + "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-7.1.0.tgz", + "integrity": "sha512-l/BQM7fYntsCI//du+6sEnHOP6a74UixFyOYUyz2DLMXKx+6DEhfR3F2NYGE45XH1JJuIamacb4IZs9S0ZOWLA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-7.1.0.tgz", + "integrity": "sha512-fNxRUk1KhjSbnbuBxlWSnBLKLBNun52ZBTcs22H/xEEzM6Ap81ZFTQ4bZBxVQGQgVY0xugKGoRcCbaKjLQ3XZA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "7.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-brands-svg-icons": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.7.2.tgz", + "integrity": "sha512-zu0evbcRTgjKfrr77/2XX+bU+kuGfjm0LbajJHVIgBWNIDzrhpRxiCPNT8DW5AdmSsq7Mcf9D1bH0aSeSUSM+Q==", + "license": "(CC-BY-4.0 AND MIT)", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.7.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-brands-svg-icons/node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.2.tgz", + "integrity": "sha512-Zs+YeHUC5fkt7Mg1l6XTniei3k4bwG/yo3iFUtZWd/pMx9g3fdvkSK9E0FOC+++phXOka78uJcYb8JaFkW52Xg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-regular-svg-icons": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.7.2.tgz", + "integrity": "sha512-7Z/ur0gvCMW8G93dXIQOkQqHo2M5HLhYrRVC0//fakJXxcF1VmMPsxnG6Ee8qEylA8b8Q3peQXWMNZ62lYF28g==", + "license": "(CC-BY-4.0 AND MIT)", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.7.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-regular-svg-icons/node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.2.tgz", + "integrity": "sha512-Zs+YeHUC5fkt7Mg1l6XTniei3k4bwG/yo3iFUtZWd/pMx9g3fdvkSK9E0FOC+++phXOka78uJcYb8JaFkW52Xg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.7.2.tgz", + "integrity": "sha512-GsBrnOzU8uj0LECDfD5zomZJIjrPhIlWU82AHwa2s40FKH+kcxQaBvBo3Z4TxyZHIyX8XTDxsyA33/Vx9eFuQA==", + "license": "(CC-BY-4.0 AND MIT)", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.7.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons/node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.2.tgz", + "integrity": "sha512-Zs+YeHUC5fkt7Mg1l6XTniei3k4bwG/yo3iFUtZWd/pMx9g3fdvkSK9E0FOC+++phXOka78uJcYb8JaFkW52Xg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/react-fontawesome": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.6.tgz", + "integrity": "sha512-mtBFIi1UsYQo7rYonYFkjgYKGoL8T+fEH6NGUpvuqtY3ytMsAoDaPo5rk25KuMtKDipY4bGYM/CkmCHA1N3FUg==", + "deprecated": "v0.2.x is no longer supported. Unless you are still using FontAwesome 5, please update to v3.1.1 or greater.", + "license": "MIT", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~1 || ~6 || ~7", + "react": "^16.3 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", "cpu": [ "riscv64" ], - "license": "LGPL-3.0-or-later", + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", "optional": true, "os": [ "linux" ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, "funding": { "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" } }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", "cpu": [ "s390x" ], - "license": "LGPL-3.0-or-later", + "license": "Apache-2.0", "optional": true, "os": [ "linux" ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, "funding": { "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" } }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", "cpu": [ "x64" ], - "license": "LGPL-3.0-or-later", + "license": "Apache-2.0", "optional": true, "os": [ "linux" ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, "funding": { "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" } }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", "cpu": [ "arm64" ], - "license": "LGPL-3.0-or-later", + "license": "Apache-2.0", "optional": true, "os": [ "linux" ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, "funding": { "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" } }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", "cpu": [ "x64" ], - "license": "LGPL-3.0-or-later", + "license": "Apache-2.0", "optional": true, "os": [ "linux" ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, "funding": { "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" } }, - "node_modules/@img/sharp-linux-arm": { + "node_modules/@img/sharp-wasm32": { "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", "cpu": [ - "arm" + "wasm32" ], - "license": "Apache-2.0", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ - "linux" + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" ], "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, + "node_modules/@next/env": { + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.5.tgz", + "integrity": "sha512-CRSCPJiSZoi4Pn69RYBDI9R7YK2g59vLexPQFXY0eyw+ILevIenCywzg+DqmlBik9zszEnw2HLFOUlLAcJbL7g==", + "license": "MIT" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.1.5.tgz", + "integrity": "sha512-gUWcEsOl+1W7XakmouClcJ0TNFCkblvDUho31wulbDY9na0C6mGtBTSXGRU5GXJY65GjGj0zNaCD/GaBp888Mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "3.3.1" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.5.tgz", + "integrity": "sha512-eK7Wdm3Hjy/SCL7TevlH0C9chrpeOYWx2iR7guJDaz4zEQKWcS1IMVfMb9UKBFMg1XgzcPTYPIp1Vcpukkjg6Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" } }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "node_modules/@next/swc-darwin-x64": { + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.5.tgz", + "integrity": "sha512-foQscSHD1dCuxBmGkbIr6ScAUF6pRoDZP6czajyvmXPAOFNnQUJu2Os1SGELODjKp/ULa4fulnBWoHV3XdPLfA==", "cpu": [ - "arm64" + "x64" ], - "license": "Apache-2.0", + "license": "MIT", "optional": true, "os": [ - "linux" + "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" + "node": ">= 10" } }, - "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.5.tgz", + "integrity": "sha512-qNIb42o3C02ccIeSeKjacF3HXotGsxh/FMk/rSRmCzOVMtoWH88odn2uZqF8RLsSUWHcAqTgYmPD3pZ03L9ZAA==", "cpu": [ - "ppc64" + "arm64" ], - "license": "Apache-2.0", + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" + "node": ">= 10" } }, - "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "node_modules/@next/swc-linux-arm64-musl": { + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.5.tgz", + "integrity": "sha512-U+kBxGUY1xMAzDTXmuVMfhaWUZQAwzRaHJ/I6ihtR5SbTVUEaDRiEU9YMjy1obBWpdOBuk1bcm+tsmifYSygfw==", "cpu": [ - "riscv64" + "arm64" ], - "license": "Apache-2.0", + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" + "node": ">= 10" } }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "node_modules/@next/swc-linux-x64-gnu": { + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.5.tgz", + "integrity": "sha512-gq2UtoCpN7Ke/7tKaU7i/1L7eFLfhMbXjNghSv0MVGF1dmuoaPeEVDvkDuO/9LVa44h5gqpWeJ4mRRznjDv7LA==", "cpu": [ - "s390x" + "x64" ], - "license": "Apache-2.0", + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" + "node": ">= 10" } }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "node_modules/@next/swc-linux-x64-musl": { + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.5.tgz", + "integrity": "sha512-bQWSE729PbXT6mMklWLf8dotislPle2L70E9q6iwETYEOt092GDn0c+TTNj26AjmeceSsC4ndyGsK5nKqHYXjQ==", "cpu": [ "x64" ], - "license": "Apache-2.0", + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" + "node": ">= 10" } }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.5.tgz", + "integrity": "sha512-LZli0anutkIllMtTAWZlDqdfvjWX/ch8AFK5WgkNTvaqwlouiD1oHM+WW8RXMiL0+vAkAJyAGEzPPjO+hnrSNQ==", "cpu": [ "arm64" ], - "license": "Apache-2.0", + "license": "MIT", "optional": true, "os": [ - "linux" + "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + "node": ">= 10" } }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "node_modules/@next/swc-win32-x64-msvc": { + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.5.tgz", + "integrity": "sha512-7is37HJTNQGhjPpQbkKjKEboHYQnCgpVt/4rBrrln0D9nderNxZ8ZWs8w1fAtzUx7wEyYjQ+/13myFgFj6K2Ng==", "cpu": [ "x64" ], - "license": "Apache-2.0", + "license": "MIT", "optional": true, "os": [ - "linux" + "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">= 10" + } + }, + "node_modules/@next/third-parties": { + "version": "16.1.5", + "resolved": "https://registry.npmjs.org/@next/third-parties/-/third-parties-16.1.5.tgz", + "integrity": "sha512-p8BKg+Y6ovrh2AE4su6MjaEDBYIuk5CkeA1Gtld5V7L3+3nBN2D2N4In2YfsHxFVRVa6fPZXyd2keqEoCpIcnw==", + "license": "MIT", + "dependencies": { + "third-party-capital": "1.0.20" }, - "funding": { - "url": "https://opencollective.com/libvips" + "peerDependencies": { + "next": "^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0-beta.0", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + "engines": { + "node": ">= 8" } }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.4.0" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.5.0.tgz", + "integrity": "sha512-ka4H8OM6+DlUhSAZpONu0cPBtPPTQKxbxVzC4CzVx5+K4JnroJVBtDzLAMx4/3CDTJXRvVFhpFjtl4SaiTNoyQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.39.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.39.0.tgz", + "integrity": "sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@panva/hkdf": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.1.tgz", + "integrity": "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", + "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.7.0" + "detect-libc": "^2.0.3", + "is-glob": "^4.0.3", + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.3" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">= 10.0.0" }, "funding": { - "url": "https://opencollective.com/libvips" + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.6", + "@parcel/watcher-darwin-arm64": "2.5.6", + "@parcel/watcher-darwin-x64": "2.5.6", + "@parcel/watcher-freebsd-x64": "2.5.6", + "@parcel/watcher-linux-arm-glibc": "2.5.6", + "@parcel/watcher-linux-arm-musl": "2.5.6", + "@parcel/watcher-linux-arm64-glibc": "2.5.6", + "@parcel/watcher-linux-arm64-musl": "2.5.6", + "@parcel/watcher-linux-x64-glibc": "2.5.6", + "@parcel/watcher-linux-x64-musl": "2.5.6", + "@parcel/watcher-win32-arm64": "2.5.6", + "@parcel/watcher-win32-ia32": "2.5.6", + "@parcel/watcher-win32-x64": "2.5.6" } }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz", + "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==", "cpu": [ "arm64" ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", + "dev": true, + "license": "MIT", "optional": true, "os": [ - "win32" + "android" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">= 10.0.0" }, "funding": { - "url": "https://opencollective.com/libvips" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz", + "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==", "cpu": [ - "ia32" + "arm64" ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", + "dev": true, + "license": "MIT", "optional": true, "os": [ - "win32" + "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">= 10.0.0" }, "funding": { - "url": "https://opencollective.com/libvips" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz", + "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==", "cpu": [ "x64" ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", + "dev": true, + "license": "MIT", "optional": true, "os": [ - "win32" + "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">= 10.0.0" }, "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz", + "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", - "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz", + "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" - } - }, - "node_modules/@next/env": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.5.tgz", - "integrity": "sha512-CRSCPJiSZoi4Pn69RYBDI9R7YK2g59vLexPQFXY0eyw+ILevIenCywzg+DqmlBik9zszEnw2HLFOUlLAcJbL7g==", - "license": "MIT" - }, - "node_modules/@next/eslint-plugin-next": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.1.5.tgz", - "integrity": "sha512-gUWcEsOl+1W7XakmouClcJ0TNFCkblvDUho31wulbDY9na0C6mGtBTSXGRU5GXJY65GjGj0zNaCD/GaBp888Mg==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-glob": "3.3.1" + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@next/swc-darwin-arm64": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.5.tgz", - "integrity": "sha512-eK7Wdm3Hjy/SCL7TevlH0C9chrpeOYWx2iR7guJDaz4zEQKWcS1IMVfMb9UKBFMg1XgzcPTYPIp1Vcpukkjg6Q==", + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz", + "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==", "cpu": [ - "arm64" + "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ - "darwin" + "linux" ], "engines": { - "node": ">= 10" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@next/swc-darwin-x64": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.5.tgz", - "integrity": "sha512-foQscSHD1dCuxBmGkbIr6ScAUF6pRoDZP6czajyvmXPAOFNnQUJu2Os1SGELODjKp/ULa4fulnBWoHV3XdPLfA==", + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz", + "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==", "cpu": [ - "x64" + "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ - "darwin" + "linux" ], "engines": { - "node": ">= 10" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.5.tgz", - "integrity": "sha512-qNIb42o3C02ccIeSeKjacF3HXotGsxh/FMk/rSRmCzOVMtoWH88odn2uZqF8RLsSUWHcAqTgYmPD3pZ03L9ZAA==", + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz", + "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">= 10" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.5.tgz", - "integrity": "sha512-U+kBxGUY1xMAzDTXmuVMfhaWUZQAwzRaHJ/I6ihtR5SbTVUEaDRiEU9YMjy1obBWpdOBuk1bcm+tsmifYSygfw==", + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz", + "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==", "cpu": [ - "arm64" + "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.5.tgz", - "integrity": "sha512-gq2UtoCpN7Ke/7tKaU7i/1L7eFLfhMbXjNghSv0MVGF1dmuoaPeEVDvkDuO/9LVa44h5gqpWeJ4mRRznjDv7LA==", + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz", + "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">= 10" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.5.tgz", - "integrity": "sha512-bQWSE729PbXT6mMklWLf8dotislPle2L70E9q6iwETYEOt092GDn0c+TTNj26AjmeceSsC4ndyGsK5nKqHYXjQ==", + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz", + "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==", "cpu": [ - "x64" + "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" + "win32" ], "engines": { - "node": ">= 10" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.5.tgz", - "integrity": "sha512-LZli0anutkIllMtTAWZlDqdfvjWX/ch8AFK5WgkNTvaqwlouiD1oHM+WW8RXMiL0+vAkAJyAGEzPPjO+hnrSNQ==", + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz", + "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==", "cpu": [ - "arm64" + "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">= 10" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.5.tgz", - "integrity": "sha512-7is37HJTNQGhjPpQbkKjKEboHYQnCgpVt/4rBrrln0D9nderNxZ8ZWs8w1fAtzUx7wEyYjQ+/13myFgFj6K2Ng==", + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz", + "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/third-parties": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/@next/third-parties/-/third-parties-16.1.5.tgz", - "integrity": "sha512-p8BKg+Y6ovrh2AE4su6MjaEDBYIuk5CkeA1Gtld5V7L3+3nBN2D2N4In2YfsHxFVRVa6fPZXyd2keqEoCpIcnw==", - "license": "MIT", - "dependencies": { - "third-party-capital": "1.0.20" + "node": ">= 10.0.0" }, - "peerDependencies": { - "next": "^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0-beta.0", - "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@parcel/watcher/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, + "optional": true, "engines": { - "node": ">= 8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", "dev": true, "license": "MIT", "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" }, - "engines": { - "node": ">= 8" + "funding": { + "url": "https://opencollective.com/pkgr" } }, - "node_modules/@nolyfill/is-core-module": { - "version": "1.0.39", - "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", - "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", - "dev": true, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", "license": "MIT", - "engines": { - "node": ">=12.4.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" } }, - "node_modules/@opentelemetry/api": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "node_modules/@prisma/client": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.19.2.tgz", + "integrity": "sha512-gR2EMvfK/aTxsuooaDA32D8v+us/8AAet+C3J1cc04SW35FPdZYgLF+iN4NDLUgAaUGTKdAB0CYenu1TAgGdMg==", + "hasInstallScript": true, "license": "Apache-2.0", "engines": { - "node": ">=8.0.0" + "node": ">=18.18" + }, + "peerDependencies": { + "prisma": "*", + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + }, + "typescript": { + "optional": true + } } }, - "node_modules/@opentelemetry/core": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.5.0.tgz", - "integrity": "sha512-ka4H8OM6+DlUhSAZpONu0cPBtPPTQKxbxVzC4CzVx5+K4JnroJVBtDzLAMx4/3CDTJXRvVFhpFjtl4SaiTNoyQ==", + "node_modules/@prisma/config": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.19.2.tgz", + "integrity": "sha512-kadBGDl+aUswv/zZMk9Mx0C8UZs1kjao8H9/JpI4Wh4SHZaM7zkTwiKn/iFLfRg+XtOAo/Z/c6pAYhijKl0nzQ==", + "devOptional": true, "license": "Apache-2.0", "dependencies": { - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" + "c12": "3.1.0", + "deepmerge-ts": "7.1.5", + "effect": "3.18.4", + "empathic": "2.0.0" } }, - "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.39.0.tgz", - "integrity": "sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==", + "node_modules/@prisma/debug": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.19.2.tgz", + "integrity": "sha512-lFnEZsLdFLmEVCVNdskLDCL8Uup41GDfU0LUfquw+ercJC8ODTuL0WNKgOKmYxCJVvFwf0OuZBzW99DuWmoH2A==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.19.2.tgz", + "integrity": "sha512-TTkJ8r+uk/uqczX40wb+ODG0E0icVsMgwCTyTHXehaEfb0uo80M9g1aW1tEJrxmFHeOZFXdI2sTA1j1AgcHi4A==", + "devOptional": true, + "hasInstallScript": true, "license": "Apache-2.0", - "engines": { - "node": ">=14" + "dependencies": { + "@prisma/debug": "6.19.2", + "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", + "@prisma/fetch-engine": "6.19.2", + "@prisma/get-platform": "6.19.2" } }, - "node_modules/@panva/hkdf": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.1.tgz", - "integrity": "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/panva" - } + "node_modules/@prisma/engines-version": { + "version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7.tgz", + "integrity": "sha512-03bgb1VD5gvuumNf+7fVGBzfpJPjmqV423l/WxsWk2cNQ42JD0/SsFBPhN6z8iAvdHs07/7ei77SKu7aZfq8bA==", + "devOptional": true, + "license": "Apache-2.0" }, - "node_modules/@parcel/watcher": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", - "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, + "node_modules/@prisma/fetch-engine": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.19.2.tgz", + "integrity": "sha512-h4Ff4Pho+SR1S8XerMCC12X//oY2bG3Iug/fUnudfcXEUnIeRiBdXHFdGlGOgQ3HqKgosTEhkZMvGM9tWtYC+Q==", + "devOptional": true, + "license": "Apache-2.0", "dependencies": { - "detect-libc": "^2.0.3", - "is-glob": "^4.0.3", - "node-addon-api": "^7.0.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "@parcel/watcher-android-arm64": "2.5.6", - "@parcel/watcher-darwin-arm64": "2.5.6", - "@parcel/watcher-darwin-x64": "2.5.6", - "@parcel/watcher-freebsd-x64": "2.5.6", - "@parcel/watcher-linux-arm-glibc": "2.5.6", - "@parcel/watcher-linux-arm-musl": "2.5.6", - "@parcel/watcher-linux-arm64-glibc": "2.5.6", - "@parcel/watcher-linux-arm64-musl": "2.5.6", - "@parcel/watcher-linux-x64-glibc": "2.5.6", - "@parcel/watcher-linux-x64-musl": "2.5.6", - "@parcel/watcher-win32-arm64": "2.5.6", - "@parcel/watcher-win32-ia32": "2.5.6", - "@parcel/watcher-win32-x64": "2.5.6" + "@prisma/debug": "6.19.2", + "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", + "@prisma/get-platform": "6.19.2" } }, - "node_modules/@parcel/watcher-android-arm64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz", - "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==", + "node_modules/@prisma/get-platform": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.19.2.tgz", + "integrity": "sha512-PGLr06JUSTqIvztJtAzIxOwtWKtJm5WwOG6xpsgD37Rc84FpfUBGLKz65YpJBGtkRQGXTYEFie7pYALocC3MtA==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.19.2" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", "cpu": [ - "arm64" + "arm" ], "dev": true, "license": "MIT", "optional": true, "os": [ "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "cpu": [ + "arm64" ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] }, - "node_modules/@parcel/watcher-darwin-arm64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz", - "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==", + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", "cpu": [ "arm64" ], @@ -1702,19 +2520,12 @@ "optional": true, "os": [ "darwin" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } + ] }, - "node_modules/@parcel/watcher-darwin-x64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz", - "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==", + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", "cpu": [ "x64" ], @@ -1723,19 +2534,26 @@ "optional": true, "os": [ "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "cpu": [ + "arm64" ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] }, - "node_modules/@parcel/watcher-freebsd-x64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz", - "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==", + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", "cpu": [ "x64" ], @@ -1744,19 +2562,12 @@ "optional": true, "os": [ "freebsd" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } + ] }, - "node_modules/@parcel/watcher-linux-arm-glibc": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz", - "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==", + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", "cpu": [ "arm" ], @@ -1765,19 +2576,12 @@ "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } + ] }, - "node_modules/@parcel/watcher-linux-arm-musl": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz", - "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==", + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", "cpu": [ "arm" ], @@ -1786,19 +2590,12 @@ "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } + ] }, - "node_modules/@parcel/watcher-linux-arm64-glibc": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz", - "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==", + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", "cpu": [ "arm64" ], @@ -1807,19 +2604,12 @@ "optional": true, "os": [ "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } + ] }, - "node_modules/@parcel/watcher-linux-arm64-musl": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz", - "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==", + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", "cpu": [ "arm64" ], @@ -1828,241 +2618,217 @@ "optional": true, "os": [ "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "cpu": [ + "loong64" ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@parcel/watcher-linux-x64-glibc": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz", - "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==", + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", "cpu": [ - "x64" + "loong64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "cpu": [ + "ppc64" ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@parcel/watcher-linux-x64-musl": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz", - "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==", + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", "cpu": [ - "x64" + "ppc64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "cpu": [ + "riscv64" ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@parcel/watcher-win32-arm64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz", - "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==", + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", "cpu": [ - "arm64" + "riscv64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "win32" + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "cpu": [ + "s390x" ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@parcel/watcher-win32-ia32": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz", - "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==", + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", "cpu": [ - "ia32" + "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "win32" + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "cpu": [ + "x64" ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@parcel/watcher-win32-x64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz", - "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==", + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, "os": [ "win32" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } + ] }, - "node_modules/@parcel/watcher/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", "optional": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } + "os": [ + "win32" + ] }, - "node_modules/@pkgr/core": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", - "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/pkgr" - } + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/@popperjs/core": { - "version": "2.11.8", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", - "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } - }, - "node_modules/@prisma/client": { - "version": "6.19.2", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.19.2.tgz", - "integrity": "sha512-gR2EMvfK/aTxsuooaDA32D8v+us/8AAet+C3J1cc04SW35FPdZYgLF+iN4NDLUgAaUGTKdAB0CYenu1TAgGdMg==", - "hasInstallScript": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "peerDependencies": { - "prisma": "*", - "typescript": ">=5.1.0" - }, - "peerDependenciesMeta": { - "prisma": { - "optional": true - }, - "typescript": { - "optional": true - } - } - }, - "node_modules/@prisma/config": { - "version": "6.19.2", - "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.19.2.tgz", - "integrity": "sha512-kadBGDl+aUswv/zZMk9Mx0C8UZs1kjao8H9/JpI4Wh4SHZaM7zkTwiKn/iFLfRg+XtOAo/Z/c6pAYhijKl0nzQ==", - "devOptional": true, - "license": "Apache-2.0", - "dependencies": { - "c12": "3.1.0", - "deepmerge-ts": "7.1.5", - "effect": "3.18.4", - "empathic": "2.0.0" - } - }, - "node_modules/@prisma/debug": { - "version": "6.19.2", - "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.19.2.tgz", - "integrity": "sha512-lFnEZsLdFLmEVCVNdskLDCL8Uup41GDfU0LUfquw+ercJC8ODTuL0WNKgOKmYxCJVvFwf0OuZBzW99DuWmoH2A==", - "devOptional": true, - "license": "Apache-2.0" - }, - "node_modules/@prisma/engines": { - "version": "6.19.2", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.19.2.tgz", - "integrity": "sha512-TTkJ8r+uk/uqczX40wb+ODG0E0icVsMgwCTyTHXehaEfb0uo80M9g1aW1tEJrxmFHeOZFXdI2sTA1j1AgcHi4A==", - "devOptional": true, - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@prisma/debug": "6.19.2", - "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", - "@prisma/fetch-engine": "6.19.2", - "@prisma/get-platform": "6.19.2" - } - }, - "node_modules/@prisma/engines-version": { - "version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7.tgz", - "integrity": "sha512-03bgb1VD5gvuumNf+7fVGBzfpJPjmqV423l/WxsWk2cNQ42JD0/SsFBPhN6z8iAvdHs07/7ei77SKu7aZfq8bA==", - "devOptional": true, - "license": "Apache-2.0" - }, - "node_modules/@prisma/fetch-engine": { - "version": "6.19.2", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.19.2.tgz", - "integrity": "sha512-h4Ff4Pho+SR1S8XerMCC12X//oY2bG3Iug/fUnudfcXEUnIeRiBdXHFdGlGOgQ3HqKgosTEhkZMvGM9tWtYC+Q==", - "devOptional": true, - "license": "Apache-2.0", - "dependencies": { - "@prisma/debug": "6.19.2", - "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", - "@prisma/get-platform": "6.19.2" - } - }, - "node_modules/@prisma/get-platform": { - "version": "6.19.2", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.19.2.tgz", - "integrity": "sha512-PGLr06JUSTqIvztJtAzIxOwtWKtJm5WwOG6xpsgD37Rc84FpfUBGLKz65YpJBGtkRQGXTYEFie7pYALocC3MtA==", - "devOptional": true, - "license": "Apache-2.0", - "dependencies": { - "@prisma/debug": "6.19.2" - } + "optional": true, + "os": [ + "win32" + ] }, "node_modules/@rtsao/scc": { "version": "1.1.0", @@ -2104,6 +2870,17 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, "node_modules/@types/command-line-args": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/command-line-args/-/command-line-args-5.2.3.tgz", @@ -2125,6 +2902,13 @@ "@types/ms": "*" } }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -2764,14 +3548,125 @@ ] }, "node_modules/@vercel/oidc": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@vercel/oidc/-/oidc-3.0.5.tgz", - "integrity": "sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@vercel/oidc/-/oidc-3.1.0.tgz", + "integrity": "sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w==", "license": "Apache-2.0", "engines": { "node": ">= 20" } }, + "node_modules/@vitest/expect": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz", + "integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.0.18", + "@vitest/utils": "4.0.18", + "chai": "^6.2.1", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz", + "integrity": "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.0.18", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz", + "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz", + "integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.0.18", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz", + "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.18", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz", + "integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz", + "integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.18", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -2796,14 +3691,14 @@ } }, "node_modules/ai": { - "version": "5.0.114", - "resolved": "https://registry.npmjs.org/ai/-/ai-5.0.114.tgz", - "integrity": "sha512-q/lxcJA6avYn/TXTaE41VX6p9lN245mDU9bIGuPpfk6WxDMvmMoUKUIS0/aXAPYN3UmkUn/r9rvq/8C98RoCWw==", + "version": "6.0.99", + "resolved": "https://registry.npmjs.org/ai/-/ai-6.0.99.tgz", + "integrity": "sha512-Zg43DDJLppe22e7IWXNwpgtxR2VRFyVJSBUZNlCz2jmyzRgaHzBqINkoy6WIakyD75LOqLCQdGMOGAqTnfO3Aw==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/gateway": "2.0.21", - "@ai-sdk/provider": "2.0.0", - "@ai-sdk/provider-utils": "3.0.19", + "@ai-sdk/gateway": "3.0.55", + "@ai-sdk/provider": "3.0.8", + "@ai-sdk/provider-utils": "4.0.15", "@opentelemetry/api": "1.9.0" }, "engines": { @@ -2814,9 +3709,9 @@ } }, "node_modules/ai/node_modules/@ai-sdk/provider": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz", - "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.8.tgz", + "integrity": "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ==", "license": "Apache-2.0", "dependencies": { "json-schema": "^0.4.0" @@ -2826,13 +3721,13 @@ } }, "node_modules/ai/node_modules/@ai-sdk/provider-utils": { - "version": "3.0.19", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.19.tgz", - "integrity": "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA==", + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.15.tgz", + "integrity": "sha512-8XiKWbemmCbvNN0CLR9u3PQiet4gtEVIrX4zzLxnCj06AwsEDJwJVBbKrEI4t6qE8XRSIvU2irka0dcpziKW6w==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "2.0.0", - "@standard-schema/spec": "^1.0.0", + "@ai-sdk/provider": "3.0.8", + "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "engines": { @@ -3095,6 +3990,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/ast-types-flow": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", @@ -3380,6 +4285,16 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -4000,6 +4915,13 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", @@ -4060,6 +4982,48 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -4561,6 +5525,16 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -4577,7 +5551,17 @@ "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", "license": "MIT", "engines": { - "node": ">=18.0.0" + "node": ">=18.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" } }, "node_modules/exsolve": { @@ -4814,6 +5798,21 @@ } } }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -5855,21 +6854,6 @@ "node": ">=6" } }, - "node_modules/jsondiffpatch": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/jsondiffpatch/-/jsondiffpatch-0.7.3.tgz", - "integrity": "sha512-zd4dqFiXSYyant2WgSXAZ9+yYqilNVvragVNkNRn2IFZKgjyULNrKRznqN4Zon0MkLueCg+3QaPVCnDAVP20OQ==", - "license": "MIT", - "dependencies": { - "@dmsnell/diff-match-patch": "^1.1.0" - }, - "bin": { - "jsondiffpatch": "bin/jsondiffpatch.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -5991,6 +6975,16 @@ "yallist": "^3.0.2" } }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, "node_modules/markdown-table": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", @@ -7232,6 +8226,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, "node_modules/ohash": { "version": "2.0.11", "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", @@ -7922,6 +8927,51 @@ "node": ">=0.10.0" } }, + "node_modules/rollup": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", + "fsevents": "~2.3.2" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -8260,6 +9310,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -8286,6 +9343,20 @@ "dev": true, "license": "MIT" }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -8516,6 +9587,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swr": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.4.0.tgz", + "integrity": "sha512-sUlC20T8EOt1pHmDiqueUWMmRRX03W7w5YxovWX7VR2KHEPCTMly85x05vpkP5i6Bu4h44ePSMD9Tc+G2MItFw==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/synckit": { "version": "0.11.12", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", @@ -8551,6 +9635,25 @@ "integrity": "sha512-oB7yIimd8SuGptespDAZnNkzIz+NWaJCu2RMsbs4Wmp9zSDUM8Nhi3s2OOcqYuv3mN4hitXc8DVx+LyUmbUDiA==", "license": "ISC" }, + "node_modules/throttleit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-2.1.0.tgz", + "integrity": "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, "node_modules/tinyexec": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", @@ -8609,6 +9712,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tinyrainbow": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", + "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -9023,6 +10136,15 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/vfile": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", @@ -9051,6 +10173,251 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.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": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vite/node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/vitest": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.18.tgz", + "integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.0.18", + "@vitest/mocker": "4.0.18", + "@vitest/pretty-format": "4.0.18", + "@vitest/runner": "4.0.18", + "@vitest/snapshot": "4.0.18", + "@vitest/spy": "4.0.18", + "@vitest/utils": "4.0.18", + "es-module-lexer": "^1.7.0", + "expect-type": "^1.2.2", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^3.10.0", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.0.18", + "@vitest/browser-preview": "4.0.18", + "@vitest/browser-webdriverio": "4.0.18", + "@vitest/ui": "4.0.18", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/warning": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", @@ -9165,6 +10532,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", diff --git a/web/package.json b/web/package.json index 8e97306..9c78307 100644 --- a/web/package.json +++ b/web/package.json @@ -3,18 +3,19 @@ "version": "0.1.0", "private": true, "scripts": { - "predev": "echo \"Use http://localhost:3001\" to view the app", + "predev": "echo \"Use http://localhost:3000\" to view the app", "dev": "next dev", "build": "next build", "start": "next start", "production": "next build && next start", "serve": "node_modules/next/dist/bin/next start", "lint": "eslint .", + "test": "vitest run", "lint:fix": "eslint --fix . && prettier --write \"src/**/*.{js,jsx,ts,tsx}\"" }, "dependencies": { - "@ai-sdk/openai": "^2.0.88", - "@ai-sdk/rsc": "^1.0.117", + "@ai-sdk/openai": "^3.0.33", + "@ai-sdk/react": "^3.0.101", "@elastic/elasticsearch": "^8.12.2", "@fortawesome/free-brands-svg-icons": "^6.5.1", "@fortawesome/free-regular-svg-icons": "^6.5.1", @@ -23,7 +24,7 @@ "@next/third-parties": "16.1.5", "@prisma/client": "^6.5.0", "@ucdavis/gunrockin": "^1.1.3", - "ai": "5.0.114", + "ai": "^6.0.99", "bootstrap": "^5.3.3", "canvas-confetti": "^1.9.3", "framer-motion": "^12.29.2", @@ -53,7 +54,8 @@ "prettier": "^3.2.5", "prisma": "^6.5.0", "sass": "^1.72.0", - "typescript": "^5" + "typescript": "^5", + "vitest": "^4.0.18" }, "overrides": { "@types/react": "19.2.10", diff --git a/web/src/app/(home)/[group]/chat/[chatid]/page.tsx b/web/src/app/(home)/[group]/chat/[chatid]/page.tsx index 89f161c..6e6b0fb 100644 --- a/web/src/app/(home)/[group]/chat/[chatid]/page.tsx +++ b/web/src/app/(home)/[group]/chat/[chatid]/page.tsx @@ -5,7 +5,6 @@ import { Metadata, ResolvingMetadata } from 'next'; import { auth } from '@/auth'; import MainContent from '@/components/chat/main'; -import { AI } from '@/lib/aiProvider'; import { isWonkSuccess, WonkStatusCodes } from '@/lib/error/error'; import WonkyPageError from '@/lib/error/wonkyPageError'; import { isValidGroupName } from '@/lib/groups'; @@ -83,11 +82,7 @@ const ChatPage = async (props: HomePageProps) => { chat = newChatSession(session, group, focus, subFocus); } - return ( - - - - ); + return ; }; export default ChatPage; @@ -102,7 +97,7 @@ const newChatSession = ( const chat: ChatHistory = { ...blankAIState, - // id is '' in state until submitUserMessage() is called + // id is '' in state until the chat is saved group, meta: { focus: focus ?? focuses[0], diff --git a/web/src/app/(home)/[group]/share/[shareid]/page.tsx b/web/src/app/(home)/[group]/share/[shareid]/page.tsx index cb39174..6a24a74 100644 --- a/web/src/app/(home)/[group]/share/[shareid]/page.tsx +++ b/web/src/app/(home)/[group]/share/[shareid]/page.tsx @@ -3,12 +3,13 @@ import React from 'react'; import { Metadata, ResolvingMetadata } from 'next'; +import { auth } from '@/auth'; import MainContent from '@/components/chat/main'; -import { AI } from '@/lib/aiProvider'; import { WonkReturnObject, isWonkSuccess } from '@/lib/error/error'; import WonkyPageError from '@/lib/error/wonkyPageError'; import { cleanMetadataTitle } from '@/lib/util'; import { ChatHistory } from '@/models/chat'; +import type { WonkSession } from '@/models/session'; import { getSharedChat } from '@/services/historyService'; type SharedPageProps = { @@ -28,6 +29,11 @@ export async function generateMetadata( props: SharedPageProps, parent: ResolvingMetadata ): Promise { + const session = (await auth()) as WonkSession; + if (!session?.userId) { + return { title: 'Chat' }; + } + const params = await props.params; const { shareid } = params; const result = await getCachedSharedChat(shareid); @@ -53,11 +59,11 @@ const SharePage = async (props: SharedPageProps) => { } return ( - + <>
Shared Chat

- -
+ + ); }; diff --git a/web/src/app/api/chat/route.ts b/web/src/app/api/chat/route.ts new file mode 100644 index 0000000..c99ed5a --- /dev/null +++ b/web/src/app/api/chat/route.ts @@ -0,0 +1,122 @@ +import { nanoid } from 'nanoid'; +import { convertToModelMessages, streamText } from 'ai'; + +import { auth } from '@/auth'; +import { isWonkSuccess } from '@/lib/error/error'; +import { createCitationsTransform } from '@/lib/chat/citationsTransform'; +import { isValidGroupName } from '@/lib/groups'; +import { focuses, getFocusWithSubFocus, getFocusesForGroup } from '@/models/focus'; +import type { WonkSession } from '@/models/session'; +import { + expandedTransformSearchResults, + getEmbeddings, + getSearchResultsElastic, + getSystemMessage, + llmModel, + openai, +} from '@/services/chatService'; +import { saveChat } from '@/services/historyService'; + +export const runtime = 'nodejs'; +export const dynamic = 'force-dynamic'; + +const getTextFromParts = (parts: unknown): string => { + if (!Array.isArray(parts)) { + return ''; + } + + return parts + .filter((part) => part && typeof part === 'object' && part.type === 'text') + .map((part) => (part as any).text) + .filter((text): text is string => typeof text === 'string') + .join(''); +}; + +export async function POST(req: Request) { + const session = (await auth()) as WonkSession; + if (!session?.userId) { + return new Response('Unauthorized', { status: 401 }); + } + + let body: any; + try { + body = await req.json(); + } catch { + return new Response('Invalid JSON body', { status: 400 }); + } + + const messages = body?.messages; + const group = body?.group; + const focusParam = body?.focus as string | undefined; + const subFocusParam = body?.subFocus as string | undefined; + + if (typeof group !== 'string' || !isValidGroupName(group)) { + return new Response('Invalid group', { status: 400 }); + } + + const focusOptions = getFocusesForGroup(group); + const requestedFocus = getFocusWithSubFocus(focusParam, subFocusParam); + const focus = + requestedFocus && focusOptions.some((f) => f.name === requestedFocus.name) + ? requestedFocus + : focusOptions[0] ?? focuses[0]; + + const lastUserMessage = Array.isArray(messages) + ? [...messages].reverse().find((m) => m?.role === 'user') + : null; + const userInput = getTextFromParts(lastUserMessage?.parts).trim(); + + if (!userInput) { + return new Response('Missing user message', { status: 400 }); + } + + const chatId = nanoid(); + + let chatSaved = false; + + const embeddings = await getEmbeddings(userInput); + const searchResults = await getSearchResultsElastic(embeddings, focus); + + const transformedResults = expandedTransformSearchResults(searchResults); + const systemMessage = getSystemMessage(transformedResults); + + const modelMessages = await convertToModelMessages( + Array.isArray(messages) ? messages : [] + ); + + const result = streamText({ + model: openai(llmModel), + system: systemMessage.content, + messages: modelMessages, + providerOptions: { + openai: { reasoningEffort: 'low' }, + }, + experimental_transform: createCitationsTransform({ + policies: searchResults, + onAssistantTextComplete: async (assistantText) => { + const saveResult = await saveChat( + chatId, + [ + systemMessage, + { id: nanoid(), role: 'user', content: userInput }, + { id: nanoid(), role: 'assistant', content: assistantText }, + ], + group, + focus + ); + + chatSaved = isWonkSuccess(saveResult); + }, + }), + }); + + return result.toUIMessageStreamResponse({ + messageMetadata: ({ part }) => { + if (part.type === 'finish' && chatSaved) { + return { chatId }; + } + + return undefined; + }, + }); +} diff --git a/web/src/components/chat/answer/chatActions.tsx b/web/src/components/chat/answer/chatActions.tsx index 1aa0f31..79b2c39 100644 --- a/web/src/components/chat/answer/chatActions.tsx +++ b/web/src/components/chat/answer/chatActions.tsx @@ -1,14 +1,13 @@ 'use client'; import React from 'react'; -import { useAIState } from '@ai-sdk/rsc'; import { AnimatePresence } from 'framer-motion'; import { usePathname } from 'next/navigation'; -import { AI } from '../../../lib/aiProvider'; import WonkyErrorBoundary from '../../../lib/error/wonkyErrorBoundary'; import { useGtagEvent } from '../../../lib/hooks/useGtagEvent'; import { getFullQuestionAndAnswer } from '../../../lib/util'; +import type { ChatHistory } from '../../../models/chat'; import { GTagEvents } from '../../../models/gtag'; import CopyToClipboardButton from '../../ui/copyToClipboardButton'; @@ -16,14 +15,23 @@ import FeedbackBar from './feedbackBar'; import FeedbackButtons from './feedbackButtons'; import ShareModal from './shareModal'; -const ChatActions: React.FC = () => { +interface ChatActionsProps { + chat: ChatHistory; + onReactionUpdate: (reaction: ChatHistory['reaction']) => void; + onShareIdUpdate: (shareId: ChatHistory['shareId']) => void; +} + +const ChatActions: React.FC = ({ + chat, + onReactionUpdate, + onShareIdUpdate, +}) => { const gtagEvent = useGtagEvent(); const pathname = usePathname(); const onSharedPage = pathname.includes('/share/'); - const [aiState] = useAIState(); const fullQuestionAndAnswer = React.useMemo( - () => getFullQuestionAndAnswer(aiState.messages), - [aiState.messages] + () => getFullQuestionAndAnswer(chat.messages), + [chat.messages] ); return ( @@ -34,23 +42,26 @@ const ChatActions: React.FC = () => { id='gtag-copy-chat' value={fullQuestionAndAnswer} onClick={() => { - gtagEvent({ event: GTagEvents.COPY_CHAT, chat: aiState }); + gtagEvent({ event: GTagEvents.COPY_CHAT, chat }); }} /> {!onSharedPage && ( <> - + - + )}
- {!onSharedPage && !!aiState.reaction && ( + {!onSharedPage && !!chat.reaction && ( diff --git a/web/src/components/chat/answer/feedbackButtons.tsx b/web/src/components/chat/answer/feedbackButtons.tsx index cd252fe..e473878 100644 --- a/web/src/components/chat/answer/feedbackButtons.tsx +++ b/web/src/components/chat/answer/feedbackButtons.tsx @@ -1,7 +1,6 @@ 'use client'; import React from 'react'; -import { useAIState, useActions } from '@ai-sdk/rsc'; import { faThumbsUp, faThumbsDown } from '@fortawesome/free-regular-svg-icons'; import { faThumbsUp as faThumbsUpSolid, @@ -9,8 +8,8 @@ import { } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { AI } from '../../../lib/aiProvider'; import { throwConfettiAt } from '../../../lib/confetti'; +import { submitFeedback } from '../../../lib/actions'; import { useGtagEvent } from '../../../lib/hooks/useGtagEvent'; import { ChatHistory, Feedback } from '../../../models/chat'; import { GTagEvents } from '../../../models/gtag'; @@ -18,40 +17,41 @@ import AnimatedButton from '../../ui/animatedButton'; export type FeedbackLoadingStates = '' | Feedback; -interface FeedbackButtonsProps {} +interface FeedbackButtonsProps { + chat: ChatHistory; + onReactionUpdate: (reaction: Feedback | undefined) => void; +} -const FeedbackButtons: React.FC = ({}) => { - const [aiState, setAIState] = useAIState(); - const { id: chatId, reaction: feedback } = aiState; - const { submitFeedback } = useActions(); +const FeedbackButtons: React.FC = ({ + chat, + onReactionUpdate, +}) => { + const { id: chatId, reaction: feedback } = chat; const gtagEvent = useGtagEvent(); const thumbsUpRef = React.useRef(null); const onFeedback = async (newFeedback: Feedback) => { - const newAiState: ChatHistory = { - ...aiState, + const updatedChat: ChatHistory = { + ...chat, reaction: newFeedback, }; - // optimistically update the AI state - setAIState(newAiState); + // optimistically update state + onReactionUpdate(newFeedback); gtagEvent({ event: newFeedback === 'thumbs_up' ? GTagEvents.FEEDBACK_THUMBS_UP : GTagEvents.FEEDBACK_THUMBS_DOWN, - chat: newAiState, + chat: updatedChat, }); try { await submitFeedback(chatId, newFeedback); } catch (e) { // TODO: handle error - setAIState((currentAIState) => ({ - ...currentAIState, - reaction: undefined, // unset the reaction on error - })); + onReactionUpdate(undefined); } }; diff --git a/web/src/components/chat/answer/shareModal.tsx b/web/src/components/chat/answer/shareModal.tsx index baa0ddd..0c64bf1 100644 --- a/web/src/components/chat/answer/shareModal.tsx +++ b/web/src/components/chat/answer/shareModal.tsx @@ -1,16 +1,16 @@ 'use client'; import React from 'react'; -import { useActions, useAIState } from '@ai-sdk/rsc'; import { faPaperPlane } from '@fortawesome/free-regular-svg-icons'; import { faPaperPlane as faPaperPlaneSolid } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Modal, ModalHeader, ModalBody, ModalFooter, Button } from 'reactstrap'; -import { AI } from '../../../lib/aiProvider'; +import { shareChat, unshareChat } from '../../../lib/actions'; import WonkyClientError from '../../../lib/error/wonkyClientError'; import WonkyErrorBoundary from '../../../lib/error/wonkyErrorBoundary'; import { useGtagEvent } from '../../../lib/hooks/useGtagEvent'; +import type { ChatHistory } from '../../../models/chat'; import { GTagEvents } from '../../../models/gtag'; import AnimatedButton from '../../ui/animatedButton'; @@ -23,15 +23,18 @@ export type ShareModalLoadingStates = | GTagEvents.REGEN_SHARE | GTagEvents.UNSHARE; -const ShareModal: React.FC = () => { +interface ShareModalProps { + chat: ChatHistory; + onShareIdUpdate: (shareId: ChatHistory['shareId']) => void; +} + +const ShareModal: React.FC = ({ chat, onShareIdUpdate }) => { const gtagEvent = useGtagEvent(); - const { shareChat, unshareChat } = useActions(); - const [aiState] = useAIState(); - const { id: chatId, shareId, group } = aiState; + const { id: chatId, shareId, group } = chat; const [isOpen, setIsOpen] = React.useState(false); const [isLoading, setIsLoading] = React.useState(''); - const isShared = shareId !== undefined; + const isShared = !!shareId; const handleShare = async ( type: GTagEvents.SHARE | GTagEvents.REGEN_SHARE @@ -39,23 +42,29 @@ const ShareModal: React.FC = () => { setIsLoading(type); gtagEvent({ event: type, - chat: aiState, + chat, }); - // TODO: handle errors - await shareChat(chatId); - setIsLoading(''); + try { + const newShareId = await shareChat(chatId); + onShareIdUpdate(newShareId); + } finally { + setIsLoading(''); + } }; const handleUnshare = async () => { setIsLoading(GTagEvents.UNSHARE); - gtagEvent({ event: GTagEvents.UNSHARE, chat: aiState }); - // TODO: handle errors - await unshareChat(chatId); - setIsLoading(''); + gtagEvent({ event: GTagEvents.UNSHARE, chat }); + try { + await unshareChat(chatId); + onShareIdUpdate(undefined); + } finally { + setIsLoading(''); + } }; const handleCopyShareUrl = () => { - gtagEvent({ event: GTagEvents.COPY_SHARE, chat: aiState }); + gtagEvent({ event: GTagEvents.COPY_SHARE, chat }); }; const toggle = () => { @@ -76,7 +85,7 @@ const ShareModal: React.FC = () => { isOpen={isOpen} toggle={toggle} onOpened={() => { - gtagEvent({ event: GTagEvents.OPEN_SHARE_MODAL, chat: aiState }); + gtagEvent({ event: GTagEvents.OPEN_SHARE_MODAL, chat }); }} > Share Chat @@ -101,10 +110,10 @@ const ShareModal: React.FC = () => { isShared={isShared} handleRegenShare={() => handleShare(GTagEvents.REGEN_SHARE)} handleUnshare={handleUnshare} - handleCopyShareUrl={handleCopyShareUrl} - isLoading={isLoading} - /> - + handleCopyShareUrl={handleCopyShareUrl} + isLoading={isLoading} + /> + {!isShared && ( diff --git a/web/src/components/chat/answer/wonkMessage.tsx b/web/src/components/chat/answer/wonkMessage.tsx index 09e283c..cc6d774 100644 --- a/web/src/components/chat/answer/wonkMessage.tsx +++ b/web/src/components/chat/answer/wonkMessage.tsx @@ -1,31 +1,25 @@ 'use client'; -import { StreamableValue } from '@ai-sdk/rsc'; +import type { ReactNode } from 'react'; import WonkyClientError from '../../../lib/error/wonkyClientError'; import WonkyErrorBoundary from '../../../lib/error/wonkyErrorBoundary'; -import { useStreamableText } from '../../../lib/hooks/useStreamableText'; - -import ChatActions from './chatActions'; import WonkAnswer from './wonkAnswer'; export const WonkMessage = ({ content, isLoading, - wonkThoughts, + children, }: { - content: string | StreamableValue; + content: string; isLoading: boolean; - wonkThoughts: StreamableValue | string; + children?: ReactNode; }) => { - const text = useStreamableText(content); - const wonkText = useStreamableText(wonkThoughts, { shouldAppend: false }); - return (
- {text ? ( + {content ? ( } > - + ) : ( - wonkText +

+ {isLoading ? 'PolicyWonk is thinking…' : ''} +

)}
- {!isLoading && } + {!isLoading && children}
); }; diff --git a/web/src/components/chat/ask/chatInput.tsx b/web/src/components/chat/ask/chatInput.tsx index 0fc78d0..d5e0157 100644 --- a/web/src/components/chat/ask/chatInput.tsx +++ b/web/src/components/chat/ask/chatInput.tsx @@ -1,66 +1,32 @@ 'use client'; -import { useUIState, useActions, useAIState } from '@ai-sdk/rsc'; -import { nanoid } from 'nanoid'; -import { useSession } from 'next-auth/react'; +import type { FC } from 'react'; -import { AI } from '../../../lib/aiProvider'; -import { useGtagEvent } from '../../../lib/hooks/useGtagEvent'; import { Focus } from '../../../models/focus'; -import { GTagEvents } from '../../../models/gtag'; -import { UserMessage } from '../userMessage'; import ChatBoxForm from './chatBoxForm'; import FocusBar from './focusBar'; // Container for all of components that can be used to send messages to the chat // Will send the actual message to the chatAI system -const ChatInput = () => { - const gtagEvent = useGtagEvent(); - const session = useSession(); - const [aiState, setAIState] = useAIState(); - const [_, setMessagesUI] = useUIState(); - - // instead of passing in a submit function, we use a server action defined in actions.tsx when we create the AI - const { submitUserMessage } = useActions(); - - const onFocusSelection = (focus: Focus) => { - setAIState((a) => ({ ...a, meta: { ...a.meta, focus } })); - }; - - const onQuestionSubmit = async (question: string) => { - // Optimistically add user message UI - setMessagesUI((currentMessages) => [ - ...currentMessages, - { - id: nanoid(), - display: ( - <> - - - ), - }, - ]); - - // TODO: handle errors - const responseMessage = await submitUserMessage(question); - - gtagEvent({ - event: GTagEvents.NEW_CHAT, - chat: { ...aiState }, - }); - - setMessagesUI((currentMessages) => [...currentMessages, responseMessage]); - }; - +interface ChatInputProps { + group: string; + focus: Focus; + onFocusSelection: (focus: Focus) => void; + onQuestionSubmit: (question: string) => void | Promise; +} + +const ChatInput: FC = ({ + group, + focus, + onFocusSelection, + onQuestionSubmit, +}: ChatInputProps) => { return (
diff --git a/web/src/components/chat/ask/defaultQuestions.tsx b/web/src/components/chat/ask/defaultQuestions.tsx deleted file mode 100644 index a7b0ad0..0000000 --- a/web/src/components/chat/ask/defaultQuestions.tsx +++ /dev/null @@ -1,68 +0,0 @@ -'use client'; -import React from 'react'; - -import { useAIState } from '@ai-sdk/rsc'; - -import { Focus } from '../../../models/focus'; - -interface DefaultQuestionsProps { - onQuestionSubmit: (question: string) => void; -} - -const DefaultQuestions: React.FC = ({ - onQuestionSubmit, -}) => { - const [aiState] = useAIState(); - - const questions = getDefaultQuestions(aiState.meta.focus); - return ( -
- {questions.map((question, index) => ( - - ))} -
- ); -}; - -const getDefaultQuestions = (focus: Focus) => { - if (focus.name === 'core') { - return [ - 'Do I need approval to work from home?', - 'What is extended leave?', - 'What are the official campus holidays?', - 'When must security updates be installed?', - ]; - } else if (focus.name === 'apm') { - return [ - 'What is a “non-faculty academic appointee”?', - 'What does the Privilege and Tenure committee do?', - 'Who can appoint an advisor to address a grievance?', - 'Who can get an Academic Coordinator title?', - ]; - } else if (focus.name === 'unions') { - return [ - 'Describe the scheduling considerations for this union', - 'How is this union recognized?', - 'Tell me about compensation', - 'What is the sick leave policy?', - ]; - } else if (focus.name === 'knowledgebase') { - return [ - 'How do I schedule a Zoom meeting?', - 'Tips on keeping my laptop safe', - 'How do I backup my data?', - 'How do I setup email forwarding?', - ]; - } else { - return []; - } -}; - -export default DefaultQuestions; diff --git a/web/src/components/chat/main.tsx b/web/src/components/chat/main.tsx index 152bcbe..2f05581 100644 --- a/web/src/components/chat/main.tsx +++ b/web/src/components/chat/main.tsx @@ -1,52 +1,162 @@ 'use client'; import React from 'react'; -import { useAIState, useUIState } from '@ai-sdk/rsc'; +import { useChat } from '@ai-sdk/react'; import { faPenToSquare } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { DefaultChatTransport, type UIMessage } from 'ai'; import { usePathname, useRouter } from 'next/navigation'; -import { AI } from '../../lib/aiProvider'; +import type { Focus } from '../../models/focus'; +import type { ChatHistory } from '../../models/chat'; import WonkyClientError from '../../lib/error/wonkyClientError'; import WonkyErrorBoundary from '../../lib/error/wonkyErrorBoundary'; import WonkBottom from '../layout/wonkBottom'; import WonkTop from '../layout/wonkTop'; import FocusBanner from './answer/focusBanner'; +import ChatActions from './answer/chatActions'; import ChatInput from './ask/chatInput'; import ChatHeader from './chatHeader'; +import { UserMessage } from './userMessage'; +import { WonkMessage } from './answer/wonkMessage'; -const MainContent = () => { +type MainContentProps = { + initialChat: ChatHistory; +}; + +const getTextFromMessage = (message: UIMessage): string => { + return message.parts + .filter((part) => part.type === 'text') + .map((part) => part.text) + .join(''); +}; + +const chatMessagesToUIMessages = (messages: ChatHistory['messages']) => { + const uiMessages: UIMessage[] = []; + + for (const message of messages ?? []) { + if (message.role === 'system') { + continue; + } + + uiMessages.push({ + id: message.id, + role: message.role, + parts: [{ type: 'text', text: message.content }], + }); + } + + return uiMessages; +}; + +const MainContent: React.FC = ({ initialChat }) => { const router = useRouter(); const pathname = usePathname(); - const [aiState] = useAIState(); - const [messagesUI, _] = useUIState(); + const [chat, setChat] = React.useState(initialChat); + const lastRedirectedChatIdRef = React.useRef(null); + + const initialMessages = React.useMemo( + () => chatMessagesToUIMessages(initialChat.messages), + [initialChat.messages] + ); + + const transport = React.useMemo( + () => new DefaultChatTransport({ api: '/api/chat' }), + [] + ); + + const { messages, sendMessage, status, error } = useChat({ + transport, + messages: initialMessages, + }); + + const chatIdFromMetadata = React.useMemo(() => { + const lastAssistantMessage = [...messages] + .reverse() + .find((message) => message.role === 'assistant'); + + if ( + !lastAssistantMessage?.metadata || + typeof lastAssistantMessage.metadata !== 'object' + ) { + return null; + } + + const chatId = (lastAssistantMessage.metadata as any).chatId; + return typeof chatId === 'string' ? chatId : null; + }, [messages]); React.useEffect(() => { if ( // on first response from AI - pathname === `/${aiState.group}/chat/new` && - aiState.id !== '' // id is set only once the chat has been saved to the db + pathname === `/${chat.group}/chat/new` && + chatIdFromMetadata && // id is only sent once the chat has been saved to the db + lastRedirectedChatIdRef.current !== chatIdFromMetadata ) { - // reloads the sidebar, which repulls from the db now that the chat has been saved - router.replace(`/${aiState.group}/chat/${aiState.id}`); - router.refresh(); + lastRedirectedChatIdRef.current = chatIdFromMetadata; + router.replace(`/${chat.group}/chat/${chatIdFromMetadata}`); + // The sidebar uses `React.cache`, so force a refresh after navigation to + // ensure history reflects the newly saved chat. + setTimeout(() => { + router.refresh(); + }, 0); } - }, [aiState.messages, router, aiState.id, pathname]); + + if (!chat.id && chatIdFromMetadata) { + setChat((current) => ({ + ...current, + id: chatIdFromMetadata, + })); + } + }, [router, pathname, chat.group, chat.id, chatIdFromMetadata]); const onNewMessage = () => { - let newRoute = `/?focus=${aiState.meta.focus.name}`; + let newRoute = `/?focus=${chat.meta.focus.name}`; - if (aiState.meta.focus.subFocus) { - newRoute += `&subFocus=${aiState.meta.focus.subFocus}`; + if (chat.meta.focus.subFocus) { + newRoute += `&subFocus=${chat.meta.focus.subFocus}`; } router.push(newRoute); }; + const onFocusSelection = (focus: Focus) => { + setChat((currentChat) => ({ + ...currentChat, + meta: { ...currentChat.meta, focus }, + })); + }; + + const onQuestionSubmit = async (question: string) => { + await sendMessage( + { text: question }, + { + body: { + group: chat.group, + focus: chat.meta.focus.name, + subFocus: chat.meta.focus.subFocus, + }, + } + ); + }; + + const onFeedbackUpdate = (reaction: ChatHistory['reaction']) => { + setChat((currentChat) => ({ ...currentChat, reaction })); + }; + + const onShareIdUpdate = (shareId: ChatHistory['shareId']) => { + setChat((currentChat) => ({ ...currentChat, shareId })); + }; + return ( <> - {!messagesUI.length ? ( + {!!error && ( +
+ +
+ )} + {!messages.length ? ( <> @@ -63,7 +173,12 @@ const MainContent = () => { /> } > - + {/* */} @@ -71,26 +186,40 @@ const MainContent = () => { ) : ( <> - - {messagesUI // this is a list of actual React Nodes - // as generated by our server action submitUserMessage in actions.tsx - // the first UI node is added on submit in chatInput.tsx - .map((m) => { - return ( -
- - } - > - {m.display} - -
- ); - })} + + {messages.map((message, index) => { + const text = getTextFromMessage(message); + const isLastMessage = index === messages.length - 1; + const isLoadingAnswer = + isLastMessage && message.role === 'assistant' && status !== 'ready'; + + return ( +
+ + } + > + {message.role === 'user' ? ( + + ) : ( + + {chat.id && status === 'ready' && ( + + )} + + )} + +
+ ); + })}
diff --git a/web/src/lib/actions.tsx b/web/src/lib/actions.tsx index 64408ea..708a2eb 100644 --- a/web/src/lib/actions.tsx +++ b/web/src/lib/actions.tsx @@ -1,231 +1,44 @@ 'use server'; -import { - createStreamableUI, - createStreamableValue, - getMutableAIState, - streamUI, -} from '@ai-sdk/rsc'; -import { nanoid } from 'nanoid'; + import { redirect } from 'next/navigation'; -import { WonkMessage } from '../components/chat/answer/wonkMessage'; -import type { ChatMessage, Feedback, UIStateNode } from '../models/chat'; -import { Focus } from '../models/focus'; -import { - getEmbeddings, - getSystemMessage, - expandedTransformSearchResults, - openai, - llmModel, - transformContentWithCitations, - getSearchResultsElastic, -} from '../services/chatService'; +import type { Feedback } from '@/models/chat'; +import { WonkServerError, isWonkSuccess } from '@/lib/error/error'; import { removeChat, removeShareChat, - saveChat, saveReaction, saveShareChat, -} from '../services/historyService'; - -import { AI } from './aiProvider'; -import { WonkServerError, isWonkSuccess } from './error/error'; -import { isValidGroupName } from './groups'; - -// to add an action, add it to this type and also in the aiProvider -export type WonkActions = { - submitUserMessage: (userInput: string) => Promise; - shareChat: (chatId: string) => Promise; - unshareChat: (chatId: string) => Promise; - submitFeedback: (chatId: string, feedback: Feedback) => Promise; -}; - -export const submitUserMessage = async (userInput: string) => { - // provided by in the page.tsx - const aiState = getMutableAIState(); - const focus = aiState.get().meta.focus as Focus; - const group = aiState.get().group; - - if (!isValidGroupName(group)) { - return WonkServerError(); - } - - // before we actually do anything, stream loading UI (for the chat window) - // user message is added on client - const chatWindowUI = createStreamableUI(); - - // and create the text stream for the response - let textStream = createStreamableValue(); - // wonk thoughts for fun, but also to show that we can update at any point - let wonkThoughts = createStreamableValue( - 'Great question! Let me look that up for you.' - ); - - let textNode: React.ReactNode = ( - - ); - chatWindowUI.update(textNode); - - // TODO: move into separate function - // then start our async process - // this is an immediately invoked function expression (IIFE) so that the above code is not blocked - (async () => { - wonkThoughts.update('Getting embeddings...'); - const embeddings = await getEmbeddings(userInput); - - // TODO: PWv2 will change to using search results from pgsql - wonkThoughts.update('Searching for relevant documents...'); - const searchResults = await getSearchResultsElastic(embeddings, focus); - - const transformedResults = expandedTransformSearchResults(searchResults); - - const systemMessage = getSystemMessage(transformedResults); +} from '@/services/historyService'; - const initialMessages: ChatMessage[] = [ - systemMessage, // system message with full document info - { - id: nanoid(), // new id for the user message - role: 'user', - content: userInput, - }, - ]; - - // Update the AI state - aiState.update({ - ...aiState.get(), // blank state - // id is set on save to db - meta: { focus }, // focus from the user - messages: [...aiState.get().messages, ...initialMessages], - }); - - wonkThoughts.done('Search complete, getting your answer...'); // chatMessage component controls when to stop showing this message - - // start streaming the assistant response, this returns the ReactNode to render - streamUI({ - model: openai(llmModel), - initial: textNode, - messages: [ - ...aiState.get().messages.map((m) => ({ - role: m.role, - content: m.content, - })), - ], - providerOptions: { - openai: { reasoningEffort: 'low' }, - }, - // `text` is called when an AI returns a text response (as opposed to a tool call). - // Its content is streamed from the LLM, so this function will be called - // multiple times with `content` being incremental. `delta` is the new text to append. - text: ({ content, done, delta }) => { - if (done) { - textStream.done(); - - // once we are finished, we need to modify the content to transform the citations - const finalContent = transformContentWithCitations( - content, - searchResults - ); - - const finalNode = ( - - ); - - // finally, close out the initial UI stream with the final node - chatWindowUI.done(finalNode); - - const finalMessages: ChatMessage[] = [ - ...aiState.get().messages, - { - id: nanoid(), // new id for the message - role: 'assistant', - content: finalContent, - }, - ]; - (async () => { - // where the new chat id is generated - const chatId = nanoid(); - // save the chat to the db - // TODO: handle errors - const result = await saveChat(chatId, finalMessages, group, focus); - if (!isWonkSuccess(result)) { - return WonkServerError(); - } - // and update the AI state with the final message - aiState.done({ - ...aiState.get(), - id: chatId, // only once the chat has been saved to the db does the aiState.id get set - meta: { focus }, - messages: finalMessages, - }); - })(); - } else { - textStream.update(delta); - } - - // return the node to render - return textNode; - }, - }); - })(); - - const uiNode: UIStateNode = { - id: nanoid(), - display: chatWindowUI.value, - }; - return uiNode; -}; - -export const shareChat = async (chatId: string) => { +export const shareChat = async (chatId: string): Promise => { const result = await saveShareChat(chatId); if (!isWonkSuccess(result)) { return WonkServerError(); } - const aiState = getMutableAIState(); - aiState.done({ - ...aiState.get(), - shareId: result.data, - }); + return result.data; }; -export const unshareChat = async (chatId: string) => { - const aiState = getMutableAIState(); - +export const unshareChat = async (chatId: string): Promise => { const result = await removeShareChat(chatId); if (!isWonkSuccess(result)) { return WonkServerError(); } - - aiState.done({ - ...aiState.get(), - shareId: undefined, - }); }; -export const submitFeedback = async (chatId: string, feedback: Feedback) => { +export const submitFeedback = async ( + chatId: string, + feedback: Feedback +): Promise => { const result = await saveReaction(chatId, feedback); if (!isWonkSuccess(result)) { return WonkServerError(); } - - const aiState = getMutableAIState(); - - aiState.done({ - ...aiState.get(), - reaction: feedback, - }); }; /** - * this happens outside of the AI Provider, so it does not mutate the AI state + * this happens outside of the chat UI, so it does not update local state */ export const deleteChatFromSidebar = async ( chatId: string, @@ -239,3 +52,4 @@ export const deleteChatFromSidebar = async ( redirect('/'); } }; + diff --git a/web/src/lib/aiProvider.tsx b/web/src/lib/aiProvider.tsx deleted file mode 100644 index cee54d8..0000000 --- a/web/src/lib/aiProvider.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { createAI, getAIState } from '@ai-sdk/rsc'; - -import FocusBanner from '../components/chat/answer/focusBanner'; -import { WonkMessage } from '../components/chat/answer/wonkMessage'; -import { UserMessage } from '../components/chat/userMessage'; -import type { ChatMessage } from '../models/chat'; -import { ChatHistory, UIState, blankAIState } from '../models/chat'; - -import { - shareChat, - submitFeedback, - submitUserMessage, - unshareChat, -} from './actions'; -import type { WonkActions } from './actions'; - -// AI is a provider you wrap your application with so you can access AI and UI state in your components. -export const AI = createAI({ - actions: { - submitUserMessage, - shareChat, - unshareChat, - submitFeedback, - }, - initialUIState: [], - initialAIState: blankAIState, - onGetUIState: async () => { - 'use server'; - - const aiState = getAIState() as ChatHistory; - const messages: ChatMessage[] = aiState.messages; - return messages - .filter((message) => message.role !== 'system') - .map((message: ChatMessage, index) => ({ - id: message.id, - display: - message.role === 'user' ? ( - <> - - - - ) : ( - - ), - })); - }, - // not using onSetAIState, instead we are manually saving to the db with historyService -}); diff --git a/web/src/lib/chat/citationsTransform.test.ts b/web/src/lib/chat/citationsTransform.test.ts new file mode 100644 index 0000000..9be0570 --- /dev/null +++ b/web/src/lib/chat/citationsTransform.test.ts @@ -0,0 +1,152 @@ +import { describe, expect, it, vi } from 'vitest'; +import type { TextStreamPart } from 'ai'; + +import { createCitationsTransform, type CitationPolicy } from './citationsTransform'; + +const runTransform = async ({ + chunks, + policies, +}: { + chunks: TextStreamPart[]; + policies: CitationPolicy[]; +}) => { + const onAssistantTextComplete = vi.fn(async () => {}); + + const transform = createCitationsTransform({ + policies, + onAssistantTextComplete, + })({ tools: {}, stopStream: () => {} }); + + const input = new ReadableStream>({ + start(controller) { + for (const chunk of chunks) { + controller.enqueue(chunk); + } + controller.close(); + }, + }); + + const reader = input.pipeThrough(transform).getReader(); + const output: TextStreamPart[] = []; + + while (true) { + const { done, value } = await reader.read(); + if (done) { + break; + } + output.push(value); + } + + const outputText = output + .filter((chunk) => chunk.type === 'text-delta') + .map((chunk) => chunk.text) + .join(''); + + return { output, outputText, onAssistantTextComplete }; +}; + +describe('createCitationsTransform', () => { + const basePolicies: CitationPolicy[] = [ + { docNumber: 0, metadata: { title: 'Doc 0', url: 'https://example.com/0' } }, + { docNumber: 2, metadata: { title: 'Doc 2', url: 'https://example.com/2' } }, + { docNumber: 7, metadata: { title: 'Doc 7', url: 'https://example.com/7' } }, + ]; + + it('replaces markers and appends citation footnotes', async () => { + const { outputText, onAssistantTextComplete } = await runTransform({ + policies: basePolicies, + chunks: [ + { type: 'text-start', id: 't1' }, + { + type: 'text-delta', + id: 't1', + text: 'Hello world .', + }, + { type: 'text-end', id: 't1' }, + { + type: 'finish-step', + response: {} as any, + usage: {} as any, + finishReason: 'stop' as any, + rawFinishReason: undefined, + providerMetadata: undefined, + }, + { + type: 'finish', + finishReason: 'stop' as any, + rawFinishReason: undefined, + totalUsage: {} as any, + }, + ], + }); + + expect(outputText).toContain('Hello [^0] world [^2].'); + expect(outputText).toContain('## Citations'); + expect(outputText).toContain('[^0]: [Doc 0](https://example.com/0)'); + expect(outputText).toContain('[^2]: [Doc 2](https://example.com/2)'); + expect(outputText).not.toContain('[^7]: [Doc 7](https://example.com/7)'); + + expect(onAssistantTextComplete).toHaveBeenCalledTimes(1); + expect(onAssistantTextComplete.mock.calls[0]?.[0]).toContain( + '[^0]: [Doc 0](https://example.com/0)' + ); + }); + + it('handles citation markers split across chunks', async () => { + const { outputText } = await runTransform({ + policies: basePolicies, + chunks: [ + { type: 'text-start', id: 't1' }, + { type: 'text-delta', id: 't1', text: 'Start end.' }, + { type: 'text-end', id: 't1' }, + { + type: 'finish-step', + response: {} as any, + usage: {} as any, + finishReason: 'stop' as any, + rawFinishReason: undefined, + providerMetadata: undefined, + }, + { + type: 'finish', + finishReason: 'stop' as any, + rawFinishReason: undefined, + totalUsage: {} as any, + }, + ], + }); + + expect(outputText).toContain('Start [^0] end.'); + expect(outputText).not.toContain(' { + const { outputText } = await runTransform({ + policies: basePolicies, + chunks: [ + { type: 'text-start', id: 't1' }, + { type: 'text-delta', id: 't1', text: 'No citations here.' }, + { type: 'text-end', id: 't1' }, + { + type: 'finish-step', + response: {} as any, + usage: {} as any, + finishReason: 'stop' as any, + rawFinishReason: undefined, + providerMetadata: undefined, + }, + { + type: 'finish', + finishReason: 'stop' as any, + rawFinishReason: undefined, + totalUsage: {} as any, + }, + ], + }); + + expect(outputText).toBe('No citations here.'); + expect(outputText).not.toContain('## Citations'); + }); +}); + diff --git a/web/src/lib/chat/citationsTransform.ts b/web/src/lib/chat/citationsTransform.ts new file mode 100644 index 0000000..4c61e6d --- /dev/null +++ b/web/src/lib/chat/citationsTransform.ts @@ -0,0 +1,123 @@ +import { nanoid } from 'nanoid'; +import type { StreamTextTransform, TextStreamPart } from 'ai'; + +export type CitationPolicy = { + docNumber: number; + metadata: { + title: string; + url: string; + }; +}; + +export const createCitationsTransform = ({ + policies, + onAssistantTextComplete, +}: { + policies: CitationPolicy[]; + onAssistantTextComplete: (assistantText: string) => Promise; +}): StreamTextTransform => { + const usedCitationDocNums = new Set(); + let buffer = ''; + let assistantText = ''; + let didPersist = false; + + return () => + new TransformStream, TextStreamPart>({ + async transform(chunk, controller) { + if (chunk.type === 'text-delta') { + const combined = buffer + chunk.text; + + let cutoffIndex = combined.length; + buffer = ''; + + let searchFrom = combined.length; + while (true) { + if (searchFrom <= 0) { + break; + } + + const idx = combined.lastIndexOf('<', searchFrom - 1); + if (idx === -1) { + break; + } + + const tail = combined.slice(idx); + if ( + /^<$/.test(tail) || + /^/g, + (_match, docNum) => { + const parsed = Number(docNum); + if (Number.isInteger(parsed)) { + usedCitationDocNums.add(parsed); + } + return `[^${docNum}]`; + } + ); + + assistantText += transformed; + + if (transformed) { + controller.enqueue({ ...chunk, text: transformed }); + } + + return; + } + + if (chunk.type === 'finish-step') { + buffer = ''; + + const usedPolicies = policies.filter((p) => + usedCitationDocNums.has(p.docNumber) + ); + + if (usedPolicies.length > 0) { + const citationFootnoteMarkdown = usedPolicies + .map( + (p) => `[^${p.docNumber}]: [${p.metadata.title}](${p.metadata.url})` + ) + .join('\n'); + + const citations = `\n\n## Citations\n${citationFootnoteMarkdown}\n`; + assistantText += citations; + + const citationsTextId = nanoid(); + controller.enqueue({ type: 'text-start', id: citationsTextId }); + controller.enqueue({ + type: 'text-delta', + id: citationsTextId, + text: citations, + }); + controller.enqueue({ type: 'text-end', id: citationsTextId }); + } + + if (!didPersist) { + didPersist = true; + await onAssistantTextComplete(assistantText); + } + + controller.enqueue(chunk); + return; + } + + controller.enqueue(chunk); + }, + }); +}; diff --git a/web/src/lib/hooks/useStreamableText.ts b/web/src/lib/hooks/useStreamableText.ts deleted file mode 100644 index 1f9a2c3..0000000 --- a/web/src/lib/hooks/useStreamableText.ts +++ /dev/null @@ -1,66 +0,0 @@ -'use client'; - -import { useEffect, useState } from 'react'; - -import { StreamableValue, readStreamableValue } from '@ai-sdk/rsc'; - -interface StreamableTextOptions { - shouldAppend?: boolean; -} - -/** - * This hook reads a streamable value and returns its final updated value. - * - * @param content Either a StreamableValue, or a string once the stream has completed - * @param options \{ shouldAppend: true }, controls whether any updates to the stream append to or replace previous content - * @returns if the stream is complete and content is a string, it returns the content. otherwise, it returns the most recent value of the stream - */ -export const useStreamableText = ( - content: string | StreamableValue, - options: StreamableTextOptions = { - shouldAppend: true, - } -) => { - const streamedContent = useStreamingText(content, options); - // if the content is a string, we are done streaming - if (typeof content === 'string') { - return content; - } - return streamedContent; -}; - -/** - * This hook reads a stream and ultimately returns the final value on the **stream object**. - * - * The state, `rawContent`, will not accurately reflect any changes made after `textStream.done()`. - * **If `content` is a string, it will only be used to initialize the value.** - * There is no way to selectively append, e.g. you cannot have the stream append up until `textStream.done(finalContent)`, - * and then have the finalContent override the previously appended content (like we want to do when updating citations). - * This is because the IIFE updates the state outside of the react lifecycle, and `readStreamableValue` only returns text, so we cannot - * see if it is the final chunk, or if there is a `next` - */ -const useStreamingText = ( - content: string | StreamableValue, - options: StreamableTextOptions -) => { - const { shouldAppend } = options; - - const [rawContent, setRawContent] = useState( - typeof content === 'string' ? content : '' - ); - - useEffect(() => { - (async () => { - if (typeof content === 'object') { - let value = ''; - for await (const delta of readStreamableValue(content)) { - if (typeof delta === 'string') { - setRawContent(shouldAppend ? (value = value + delta) : delta); - } - } - } - })(); - }, [content, shouldAppend]); - - return rawContent; -}; diff --git a/web/vitest.config.mts b/web/vitest.config.mts new file mode 100644 index 0000000..76b228d --- /dev/null +++ b/web/vitest.config.mts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + environment: 'node', + }, +}); + From a841f18b3cd1cb6aca0dc0a984ef7e5d5adcf7cd Mon Sep 17 00:00:00 2001 From: Scott Kirkland Date: Wed, 25 Feb 2026 09:46:15 -0800 Subject: [PATCH 3/4] better redirect/refresh of page --- .../(home)/[group]/chat/[chatid]/loading.tsx | 16 ---- web/src/app/api/history/route.ts | 28 +++++++ web/src/components/chat/main.tsx | 8 +- .../components/chatHistory/chatHistory.tsx | 2 +- .../chatHistory/chatHistoryWrapper.tsx | 74 ++++++++++++++++++- 5 files changed, 104 insertions(+), 24 deletions(-) delete mode 100644 web/src/app/(home)/[group]/chat/[chatid]/loading.tsx create mode 100644 web/src/app/api/history/route.ts diff --git a/web/src/app/(home)/[group]/chat/[chatid]/loading.tsx b/web/src/app/(home)/[group]/chat/[chatid]/loading.tsx deleted file mode 100644 index 0b6bcc5..0000000 --- a/web/src/app/(home)/[group]/chat/[chatid]/loading.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; - -import ChatHeader from '@/components/chat/chatHeader'; -import WonkTop from '@/components/layout/wonkTop'; - -const Loading: React.FC = () => { - return ( - <> - - Loading, please wait... - - - ); -}; - -export default Loading; diff --git a/web/src/app/api/history/route.ts b/web/src/app/api/history/route.ts new file mode 100644 index 0000000..ab27d77 --- /dev/null +++ b/web/src/app/api/history/route.ts @@ -0,0 +1,28 @@ +import { NextRequest, NextResponse } from 'next/server'; + +import { isWonkSuccess } from '@/lib/error/error'; +import { isValidGroupName } from '@/lib/groups'; +import { getChatHistoryForGroup } from '@/services/historyService'; + +export const runtime = 'nodejs'; +export const dynamic = 'force-dynamic'; + +export async function GET(request: NextRequest) { + const { searchParams } = new URL(request.url); + const group = searchParams.get('group'); + + if (!group || !isValidGroupName(group)) { + return NextResponse.json({ error: 'Invalid group' }, { status: 400 }); + } + + const result = await getChatHistoryForGroup(group); + + if (!isWonkSuccess(result)) { + return NextResponse.json( + { error: 'Unable to load history' }, + { status: Number(result.status) } + ); + } + + return NextResponse.json({ chats: result.data }); +} diff --git a/web/src/components/chat/main.tsx b/web/src/components/chat/main.tsx index 2f05581..7ac595a 100644 --- a/web/src/components/chat/main.tsx +++ b/web/src/components/chat/main.tsx @@ -91,16 +91,12 @@ const MainContent: React.FC = ({ initialChat }) => { if ( // on first response from AI pathname === `/${chat.group}/chat/new` && + status === 'ready' && chatIdFromMetadata && // id is only sent once the chat has been saved to the db lastRedirectedChatIdRef.current !== chatIdFromMetadata ) { lastRedirectedChatIdRef.current = chatIdFromMetadata; router.replace(`/${chat.group}/chat/${chatIdFromMetadata}`); - // The sidebar uses `React.cache`, so force a refresh after navigation to - // ensure history reflects the newly saved chat. - setTimeout(() => { - router.refresh(); - }, 0); } if (!chat.id && chatIdFromMetadata) { @@ -109,7 +105,7 @@ const MainContent: React.FC = ({ initialChat }) => { id: chatIdFromMetadata, })); } - }, [router, pathname, chat.group, chat.id, chatIdFromMetadata]); + }, [router, pathname, chat.group, chat.id, chatIdFromMetadata, status]); const onNewMessage = () => { let newRoute = `/?focus=${chat.meta.focus.name}`; diff --git a/web/src/components/chatHistory/chatHistory.tsx b/web/src/components/chatHistory/chatHistory.tsx index 80f1a7e..8fc5716 100644 --- a/web/src/components/chatHistory/chatHistory.tsx +++ b/web/src/components/chatHistory/chatHistory.tsx @@ -59,7 +59,7 @@ const ChatHistory: React.FC = async ({ group }) => {

Chat History

- {!!chats && } + {!!chats && }
diff --git a/web/src/components/chatHistory/chatHistoryWrapper.tsx b/web/src/components/chatHistory/chatHistoryWrapper.tsx index a9ac2d8..f8dd9bf 100644 --- a/web/src/components/chatHistory/chatHistoryWrapper.tsx +++ b/web/src/components/chatHistory/chatHistoryWrapper.tsx @@ -1,4 +1,9 @@ 'use client'; + +import React from 'react'; + +import { usePathname } from 'next/navigation'; + import { ChatHistoryTitleEntry } from '@/services/historyService'; import WonkyClientError from '../../lib/error/wonkyClientError'; @@ -7,11 +12,78 @@ import WonkyErrorBoundary from '../../lib/error/wonkyErrorBoundary'; import ChatHistoryList from './chatHistoryList'; interface ChatHistoryWrapperProps { + group: string; chats: ChatHistoryTitleEntry[]; } // Wrapper for the chat history to handle errors -const ChatHistoryWrapper: React.FC = ({ chats }) => { +const ChatHistoryWrapper: React.FC = ({ + group, + chats: initialChats, +}) => { + const pathname = usePathname(); + const [chats, setChats] = React.useState( + initialChats + ); + + React.useEffect(() => { + setChats(initialChats); + }, [initialChats]); + + React.useEffect(() => { + const prefix = `/${group}/chat/`; + if (!pathname.startsWith(prefix)) { + return; + } + + const chatId = pathname.slice(prefix.length).split('/')[0]; + if (!chatId || chatId === 'new') { + return; + } + + let cancelled = false; + + const refreshChatHistory = async () => { + try { + const response = await fetch( + `/api/history?group=${encodeURIComponent(group)}`, + { + method: 'GET', + cache: 'no-store', + credentials: 'same-origin', + } + ); + + if (!response.ok) { + return; + } + + const data = (await response.json()) as { + chats?: (Omit & { + timestamp: string; + })[]; + }; + + const nextChats = (data.chats ?? []).map((chat) => ({ + ...chat, + timestamp: new Date(chat.timestamp), + })) as ChatHistoryTitleEntry[]; + + if (!cancelled) { + setChats(nextChats); + } + } catch { + // ignore + } + }; + + refreshChatHistory(); + + return () => { + cancelled = true; + }; + }, [pathname, group]); + return ( Date: Wed, 25 Feb 2026 10:45:21 -0800 Subject: [PATCH 4/4] wonk messages are back --- web/eslint.config.mjs | 122 +++++++++--------- web/src/app/api/chat/route.ts | 122 +++++++++++------- web/src/app/api/documents/route.ts | 2 - web/src/app/auth/login/page.tsx | 8 +- web/src/app/global-error.tsx | 2 +- .../chat/answer/feedbackButtons.tsx | 2 +- web/src/components/chat/answer/shareModal.tsx | 8 +- .../components/chat/answer/wonkMessage.tsx | 14 +- web/src/components/chat/ask/chatInput.tsx | 6 +- web/src/components/chat/main.tsx | 57 +++++++- .../components/chatHistory/chatHistory.tsx | 14 +- .../chatHistory/chatHistoryWrapper.tsx | 5 +- .../chatHistory/deleteChatButton.tsx | 10 +- .../layout/collapsibleSidebarProvider.tsx | 1 - web/src/lib/actions.tsx | 3 +- web/src/lib/chat/citationsTransform.test.ts | 23 +++- web/src/lib/chat/citationsTransform.ts | 20 ++- web/src/lib/gtagProvider.tsx | 1 - web/vitest.config.mts | 1 - 19 files changed, 258 insertions(+), 163 deletions(-) diff --git a/web/eslint.config.mjs b/web/eslint.config.mjs index 9c160b2..1c43a7d 100644 --- a/web/eslint.config.mjs +++ b/web/eslint.config.mjs @@ -1,71 +1,77 @@ -import { defineConfig, globalIgnores } from "eslint/config"; -import nextCoreWebVitals from "eslint-config-next/core-web-vitals"; -import react from "eslint-plugin-react"; -import reactHooks from "eslint-plugin-react-hooks"; -import reactRefresh from "eslint-plugin-react-refresh"; -import _import from "eslint-plugin-import"; -import prettier from "eslint-plugin-prettier"; -import { fixupPluginRules } from "@eslint/compat"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; -import js from "@eslint/js"; -import { FlatCompat } from "@eslint/eslintrc"; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { FlatCompat } from '@eslint/eslintrc'; +import js from '@eslint/js'; +import { defineConfig, globalIgnores } from 'eslint/config'; +import nextCoreWebVitals from 'eslint-config-next/core-web-vitals'; +import reactRefresh from 'eslint-plugin-react-refresh'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const compat = new FlatCompat({ - baseDirectory: __dirname, - recommendedConfig: js.configs.recommended, - allConfig: js.configs.all + baseDirectory: __dirname, + recommendedConfig: js.configs.recommended, + allConfig: js.configs.all, }); export default defineConfig([ - globalIgnores(["**/node_modules", "**/build", "**/dist", "**/public", "**/.next"]), - { - extends: [ - ...compat.extends("plugin:react/recommended"), - ...nextCoreWebVitals, - ...compat.extends("plugin:prettier/recommended") - ], - - plugins: { - react, - "react-hooks": fixupPluginRules(reactHooks), - "react-refresh": reactRefresh, - import: fixupPluginRules(_import), - prettier, - }, - - rules: { - eqeqeq: "error", - "no-console": "warn", - "no-restricted-globals": ["warn", "setTimeout"], - "prettier/prettier": "error", - "react/display-name": "off", - "react/react-in-jsx-scope": "off", - "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": "warn", - "react/prop-types": "off", - "react/no-unescaped-entities": "off", - curly: ["error", "all"], + globalIgnores([ + '**/node_modules', + '**/build', + '**/dist', + '**/public', + '**/.next', + ]), + ...nextCoreWebVitals, + ...compat.extends('plugin:prettier/recommended'), + { + files: ['**/*.{js,jsx,mjs,ts,tsx,mts,cts}'], + plugins: { + 'react-refresh': reactRefresh, + }, + rules: { + eqeqeq: 'error', + 'no-console': 'warn', + 'no-restricted-globals': ['warn', 'setTimeout'], + 'prettier/prettier': 'error', + 'react/display-name': 'off', + 'react/react-in-jsx-scope': 'off', + 'react-hooks/rules-of-hooks': 'error', + 'react-hooks/exhaustive-deps': 'warn', + 'react/prop-types': 'off', + 'react/no-unescaped-entities': 'off', + curly: ['error', 'all'], - "import/order": ["error", { - groups: ["builtin", "external", "internal", "parent", "sibling", "index"], + 'import/order': [ + 'error', + { + groups: [ + 'builtin', + 'external', + 'internal', + 'parent', + 'sibling', + 'index', + ], - pathGroups: [{ - pattern: "react", - group: "external", - position: "before", - }], + pathGroups: [ + { + pattern: 'react', + group: 'external', + position: 'before', + }, + ], - pathGroupsExcludedImportTypes: ["builtin"], - "newlines-between": "always", + pathGroupsExcludedImportTypes: ['builtin'], + 'newlines-between': 'always', - alphabetize: { - order: "asc", - caseInsensitive: true, - }, - }], + alphabetize: { + order: 'asc', + caseInsensitive: true, + }, }, + ], }, -]); \ No newline at end of file + }, +]); diff --git a/web/src/app/api/chat/route.ts b/web/src/app/api/chat/route.ts index c99ed5a..94a9eeb 100644 --- a/web/src/app/api/chat/route.ts +++ b/web/src/app/api/chat/route.ts @@ -1,11 +1,20 @@ +import { + convertToModelMessages, + createUIMessageStream, + createUIMessageStreamResponse, + streamText, +} from 'ai'; import { nanoid } from 'nanoid'; -import { convertToModelMessages, streamText } from 'ai'; import { auth } from '@/auth'; -import { isWonkSuccess } from '@/lib/error/error'; import { createCitationsTransform } from '@/lib/chat/citationsTransform'; +import { isWonkSuccess } from '@/lib/error/error'; import { isValidGroupName } from '@/lib/groups'; -import { focuses, getFocusWithSubFocus, getFocusesForGroup } from '@/models/focus'; +import { + focuses, + getFocusWithSubFocus, + getFocusesForGroup, +} from '@/models/focus'; import type { WonkSession } from '@/models/session'; import { expandedTransformSearchResults, @@ -59,7 +68,7 @@ export async function POST(req: Request) { const focus = requestedFocus && focusOptions.some((f) => f.name === requestedFocus.name) ? requestedFocus - : focusOptions[0] ?? focuses[0]; + : (focusOptions[0] ?? focuses[0]); const lastUserMessage = Array.isArray(messages) ? [...messages].reverse().find((m) => m?.role === 'user') @@ -74,49 +83,70 @@ export async function POST(req: Request) { let chatSaved = false; - const embeddings = await getEmbeddings(userInput); - const searchResults = await getSearchResultsElastic(embeddings, focus); - - const transformedResults = expandedTransformSearchResults(searchResults); - const systemMessage = getSystemMessage(transformedResults); - - const modelMessages = await convertToModelMessages( - Array.isArray(messages) ? messages : [] - ); - - const result = streamText({ - model: openai(llmModel), - system: systemMessage.content, - messages: modelMessages, - providerOptions: { - openai: { reasoningEffort: 'low' }, + const stream = createUIMessageStream({ + execute: async ({ writer }) => { + const writeThought = (thought: string) => { + writer.write({ + type: 'data-aggiethought', + data: thought, + transient: true, + }); + }; + + writeThought('Getting embeddings...'); + const embeddings = await getEmbeddings(userInput); + + writeThought('Searching for relevant documents...'); + const searchResults = await getSearchResultsElastic(embeddings, focus); + + const transformedResults = expandedTransformSearchResults(searchResults); + const systemMessage = getSystemMessage(transformedResults); + + const modelMessages = await convertToModelMessages( + Array.isArray(messages) ? messages : [] + ); + + writeThought('Search complete, getting your answer...'); + + const result = streamText({ + model: openai(llmModel), + system: systemMessage.content, + messages: modelMessages, + providerOptions: { + openai: { reasoningEffort: 'medium' }, + }, + experimental_transform: createCitationsTransform({ + policies: searchResults, + onAssistantTextComplete: async (assistantText) => { + const saveResult = await saveChat( + chatId, + [ + systemMessage, + { id: nanoid(), role: 'user', content: userInput }, + { id: nanoid(), role: 'assistant', content: assistantText }, + ], + group, + focus + ); + + chatSaved = isWonkSuccess(saveResult); + }, + }), + }); + + writer.merge( + result.toUIMessageStream({ + messageMetadata: ({ part }) => { + if (part.type === 'finish' && chatSaved) { + return { chatId }; + } + + return undefined; + }, + }) + ); }, - experimental_transform: createCitationsTransform({ - policies: searchResults, - onAssistantTextComplete: async (assistantText) => { - const saveResult = await saveChat( - chatId, - [ - systemMessage, - { id: nanoid(), role: 'user', content: userInput }, - { id: nanoid(), role: 'assistant', content: assistantText }, - ], - group, - focus - ); - - chatSaved = isWonkSuccess(saveResult); - }, - }), }); - return result.toUIMessageStreamResponse({ - messageMetadata: ({ part }) => { - if (part.type === 'finish' && chatSaved) { - return { chatId }; - } - - return undefined; - }, - }); + return createUIMessageStreamResponse({ stream }); } diff --git a/web/src/app/api/documents/route.ts b/web/src/app/api/documents/route.ts index cdc15e7..0bac317 100644 --- a/web/src/app/api/documents/route.ts +++ b/web/src/app/api/documents/route.ts @@ -82,8 +82,6 @@ export async function GET(request: NextRequest) { where: prismaFilter, }); - console.log('total:', total); - // If no documents found, return an empty result set. if (docs.length === 0) { return NextResponse.json({ diff --git a/web/src/app/auth/login/page.tsx b/web/src/app/auth/login/page.tsx index d95eade..62f72b5 100644 --- a/web/src/app/auth/login/page.tsx +++ b/web/src/app/auth/login/page.tsx @@ -49,10 +49,10 @@ async function devBypassSignInHandler(formData: FormData) { const callbackUrl = (formData.get('callbackUrl') as string) || '/'; const userId = (formData.get('userId') as string) || devAuthUserId; - await signIn( - DEV_AUTH_BYPASS_PROVIDER_ID, - { redirectTo: callbackUrl, userId } as any - ); + await signIn(DEV_AUTH_BYPASS_PROVIDER_ID, { + redirectTo: callbackUrl, + userId, + } as any); } interface LoginProps { diff --git a/web/src/app/global-error.tsx b/web/src/app/global-error.tsx index cdf8581..e80975b 100644 --- a/web/src/app/global-error.tsx +++ b/web/src/app/global-error.tsx @@ -1,8 +1,8 @@ 'use client'; import React, { useEffect, useState } from 'react'; -import ChatSidebar from '@/components/layout/chatSidebar'; import ChatHeader from '@/components/chat/chatHeader'; +import ChatSidebar from '@/components/layout/chatSidebar'; import WonkyClientError from '@/lib/error/wonkyClientError'; import WonkyErrorBoundary from '@/lib/error/wonkyErrorBoundary'; diff --git a/web/src/components/chat/answer/feedbackButtons.tsx b/web/src/components/chat/answer/feedbackButtons.tsx index e473878..eb8063c 100644 --- a/web/src/components/chat/answer/feedbackButtons.tsx +++ b/web/src/components/chat/answer/feedbackButtons.tsx @@ -8,8 +8,8 @@ import { } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { throwConfettiAt } from '../../../lib/confetti'; import { submitFeedback } from '../../../lib/actions'; +import { throwConfettiAt } from '../../../lib/confetti'; import { useGtagEvent } from '../../../lib/hooks/useGtagEvent'; import { ChatHistory, Feedback } from '../../../models/chat'; import { GTagEvents } from '../../../models/gtag'; diff --git a/web/src/components/chat/answer/shareModal.tsx b/web/src/components/chat/answer/shareModal.tsx index 0c64bf1..78234c6 100644 --- a/web/src/components/chat/answer/shareModal.tsx +++ b/web/src/components/chat/answer/shareModal.tsx @@ -110,10 +110,10 @@ const ShareModal: React.FC = ({ chat, onShareIdUpdate }) => { isShared={isShared} handleRegenShare={() => handleShare(GTagEvents.REGEN_SHARE)} handleUnshare={handleUnshare} - handleCopyShareUrl={handleCopyShareUrl} - isLoading={isLoading} - /> - + handleCopyShareUrl={handleCopyShareUrl} + isLoading={isLoading} + /> + {!isShared && ( diff --git a/web/src/components/chat/answer/wonkMessage.tsx b/web/src/components/chat/answer/wonkMessage.tsx index cc6d774..a0a146a 100644 --- a/web/src/components/chat/answer/wonkMessage.tsx +++ b/web/src/components/chat/answer/wonkMessage.tsx @@ -4,21 +4,29 @@ import type { ReactNode } from 'react'; import WonkyClientError from '../../../lib/error/wonkyClientError'; import WonkyErrorBoundary from '../../../lib/error/wonkyErrorBoundary'; + import WonkAnswer from './wonkAnswer'; export const WonkMessage = ({ content, isLoading, + thought, children, }: { content: string; isLoading: boolean; + thought?: string | null; children?: ReactNode; }) => { return (
+ {isLoading && !content && ( +

+ {thought || 'PolicyWonk is thinking…'} +

+ )} {content ? ( - ) : ( -

- {isLoading ? 'PolicyWonk is thinking…' : ''} -

- )} + ) : null}
{!isLoading && children} diff --git a/web/src/components/chat/ask/chatInput.tsx b/web/src/components/chat/ask/chatInput.tsx index d5e0157..19d19ca 100644 --- a/web/src/components/chat/ask/chatInput.tsx +++ b/web/src/components/chat/ask/chatInput.tsx @@ -24,11 +24,7 @@ const ChatInput: FC = ({ }: ChatInputProps) => { return (
- +
); diff --git a/web/src/components/chat/main.tsx b/web/src/components/chat/main.tsx index 7ac595a..58637cc 100644 --- a/web/src/components/chat/main.tsx +++ b/web/src/components/chat/main.tsx @@ -7,19 +7,21 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { DefaultChatTransport, type UIMessage } from 'ai'; import { usePathname, useRouter } from 'next/navigation'; -import type { Focus } from '../../models/focus'; -import type { ChatHistory } from '../../models/chat'; import WonkyClientError from '../../lib/error/wonkyClientError'; import WonkyErrorBoundary from '../../lib/error/wonkyErrorBoundary'; +import { useGtagEvent } from '../../lib/hooks/useGtagEvent'; +import type { ChatHistory } from '../../models/chat'; +import type { Focus } from '../../models/focus'; +import { GTagEvents } from '../../models/gtag'; import WonkBottom from '../layout/wonkBottom'; import WonkTop from '../layout/wonkTop'; -import FocusBanner from './answer/focusBanner'; import ChatActions from './answer/chatActions'; +import FocusBanner from './answer/focusBanner'; +import { WonkMessage } from './answer/wonkMessage'; import ChatInput from './ask/chatInput'; import ChatHeader from './chatHeader'; import { UserMessage } from './userMessage'; -import { WonkMessage } from './answer/wonkMessage'; type MainContentProps = { initialChat: ChatHistory; @@ -53,8 +55,12 @@ const chatMessagesToUIMessages = (messages: ChatHistory['messages']) => { const MainContent: React.FC = ({ initialChat }) => { const router = useRouter(); const pathname = usePathname(); + const gtagEvent = useGtagEvent(); const [chat, setChat] = React.useState(initialChat); + const [currentStatusMessage, setCurrentStatusMessage] = + React.useState(''); const lastRedirectedChatIdRef = React.useRef(null); + const didLogNewChatEventRef = React.useRef(false); const initialMessages = React.useMemo( () => chatMessagesToUIMessages(initialChat.messages), @@ -69,6 +75,16 @@ const MainContent: React.FC = ({ initialChat }) => { const { messages, sendMessage, status, error } = useChat({ transport, messages: initialMessages, + onData: (dataPart) => { + if (dataPart.type !== 'data-aggiethought') { + return; + } + + const thought = dataPart.data; + if (typeof thought === 'string') { + setCurrentStatusMessage(thought); + } + }, }); const chatIdFromMetadata = React.useMemo(() => { @@ -87,6 +103,12 @@ const MainContent: React.FC = ({ initialChat }) => { return typeof chatId === 'string' ? chatId : null; }, [messages]); + React.useEffect(() => { + if (status === 'ready') { + setCurrentStatusMessage(''); + } + }, [status]); + React.useEffect(() => { if ( // on first response from AI @@ -125,6 +147,15 @@ const MainContent: React.FC = ({ initialChat }) => { }; const onQuestionSubmit = async (question: string) => { + if ( + !didLogNewChatEventRef.current && + pathname === `/${chat.group}/chat/new` + ) { + didLogNewChatEventRef.current = true; + gtagEvent({ event: GTagEvents.NEW_CHAT, chat }); + } + + setCurrentStatusMessage('Starting...'); await sendMessage( { text: question }, { @@ -187,7 +218,9 @@ const MainContent: React.FC = ({ initialChat }) => { const text = getTextFromMessage(message); const isLastMessage = index === messages.length - 1; const isLoadingAnswer = - isLastMessage && message.role === 'assistant' && status !== 'ready'; + isLastMessage && + message.role === 'assistant' && + status !== 'ready'; return (
@@ -202,7 +235,11 @@ const MainContent: React.FC = ({ initialChat }) => { {message.role === 'user' ? ( ) : ( - + {chat.id && status === 'ready' && ( = ({ initialChat }) => {
); })} + {status !== 'ready' && + messages[messages.length - 1]?.role !== 'assistant' && ( +
+

+ {currentStatusMessage || 'PolicyWonk is thinking…'} +

+
+ )}
diff --git a/web/src/components/chatHistory/chatHistory.tsx b/web/src/components/chatHistory/chatHistory.tsx index 8fc5716..2829add 100644 --- a/web/src/components/chatHistory/chatHistory.tsx +++ b/web/src/components/chatHistory/chatHistory.tsx @@ -31,14 +31,24 @@ const ChatHistory: React.FC = async ({ group }) => { // we have a valid group, so just show the chat history for that group let chats: ChatHistoryTitleEntry[] = []; + let status: 'ok' | 'skip' | 'error' = 'ok'; try { const result = await loadChatHistoryForGroup(group); if (!isWonkSuccess(result)) { - return <>; // don't show anything on known errors + status = 'skip'; // don't show anything on known errors + } else { + chats = result.data; } - chats = result.data; } catch (error) { + status = 'error'; + } + + if (status === 'skip') { + return null; + } + + if (status === 'error') { // same return as the fallback in the error boundary // but we have to do it here too, since the error boundary doesn't catch server errors return ( diff --git a/web/src/components/chatHistory/chatHistoryWrapper.tsx b/web/src/components/chatHistory/chatHistoryWrapper.tsx index f8dd9bf..19bb89c 100644 --- a/web/src/components/chatHistory/chatHistoryWrapper.tsx +++ b/web/src/components/chatHistory/chatHistoryWrapper.tsx @@ -22,9 +22,8 @@ const ChatHistoryWrapper: React.FC = ({ chats: initialChats, }) => { const pathname = usePathname(); - const [chats, setChats] = React.useState( - initialChats - ); + const [chats, setChats] = + React.useState(initialChats); React.useEffect(() => { setChats(initialChats); diff --git a/web/src/components/chatHistory/deleteChatButton.tsx b/web/src/components/chatHistory/deleteChatButton.tsx index 57140c1..63c8a04 100644 --- a/web/src/components/chatHistory/deleteChatButton.tsx +++ b/web/src/components/chatHistory/deleteChatButton.tsx @@ -41,10 +41,12 @@ const DeleteChatButton: React.FC = ({ } }; - const adjustedVariant = IconVariants; - adjustedVariant.bounce = { - ...adjustedVariant.bounce, - y: [0, -1, 0], + const adjustedVariant = { + ...IconVariants, + bounce: { + ...IconVariants.bounce, + y: [0, -1, 0], + }, }; return (
diff --git a/web/src/components/layout/collapsibleSidebarProvider.tsx b/web/src/components/layout/collapsibleSidebarProvider.tsx index 1af4f74..26b337a 100644 --- a/web/src/components/layout/collapsibleSidebarProvider.tsx +++ b/web/src/components/layout/collapsibleSidebarProvider.tsx @@ -66,7 +66,6 @@ export const CollapsibleSidebarProvider: React.FC<{ window.removeEventListener('resize', handleResize); document.removeEventListener('mousedown', handleClickOutside); }; - // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( diff --git a/web/src/lib/actions.tsx b/web/src/lib/actions.tsx index 708a2eb..fb8a6e3 100644 --- a/web/src/lib/actions.tsx +++ b/web/src/lib/actions.tsx @@ -2,8 +2,8 @@ import { redirect } from 'next/navigation'; -import type { Feedback } from '@/models/chat'; import { WonkServerError, isWonkSuccess } from '@/lib/error/error'; +import type { Feedback } from '@/models/chat'; import { removeChat, removeShareChat, @@ -52,4 +52,3 @@ export const deleteChatFromSidebar = async ( redirect('/'); } }; - diff --git a/web/src/lib/chat/citationsTransform.test.ts b/web/src/lib/chat/citationsTransform.test.ts index 9be0570..a7aff05 100644 --- a/web/src/lib/chat/citationsTransform.test.ts +++ b/web/src/lib/chat/citationsTransform.test.ts @@ -1,7 +1,10 @@ -import { describe, expect, it, vi } from 'vitest'; import type { TextStreamPart } from 'ai'; +import { describe, expect, it, vi } from 'vitest'; -import { createCitationsTransform, type CitationPolicy } from './citationsTransform'; +import { + createCitationsTransform, + type CitationPolicy, +} from './citationsTransform'; const runTransform = async ({ chunks, @@ -47,9 +50,18 @@ const runTransform = async ({ describe('createCitationsTransform', () => { const basePolicies: CitationPolicy[] = [ - { docNumber: 0, metadata: { title: 'Doc 0', url: 'https://example.com/0' } }, - { docNumber: 2, metadata: { title: 'Doc 2', url: 'https://example.com/2' } }, - { docNumber: 7, metadata: { title: 'Doc 7', url: 'https://example.com/7' } }, + { + docNumber: 0, + metadata: { title: 'Doc 0', url: 'https://example.com/0' }, + }, + { + docNumber: 2, + metadata: { title: 'Doc 2', url: 'https://example.com/2' }, + }, + { + docNumber: 7, + metadata: { title: 'Doc 7', url: 'https://example.com/7' }, + }, ]; it('replaces markers and appends citation footnotes', async () => { @@ -149,4 +161,3 @@ describe('createCitationsTransform', () => { expect(outputText).not.toContain('## Citations'); }); }); - diff --git a/web/src/lib/chat/citationsTransform.ts b/web/src/lib/chat/citationsTransform.ts index 4c61e6d..26d69e3 100644 --- a/web/src/lib/chat/citationsTransform.ts +++ b/web/src/lib/chat/citationsTransform.ts @@ -1,5 +1,5 @@ -import { nanoid } from 'nanoid'; import type { StreamTextTransform, TextStreamPart } from 'ai'; +import { nanoid } from 'nanoid'; export type CitationPolicy = { docNumber: number; @@ -61,16 +61,13 @@ export const createCitationsTransform = ({ } const toEmit = combined.slice(0, cutoffIndex); - const transformed = toEmit.replace( - //g, - (_match, docNum) => { - const parsed = Number(docNum); - if (Number.isInteger(parsed)) { - usedCitationDocNums.add(parsed); - } - return `[^${docNum}]`; + const transformed = toEmit.replace(//g, (_match, docNum) => { + const parsed = Number(docNum); + if (Number.isInteger(parsed)) { + usedCitationDocNums.add(parsed); } - ); + return `[^${docNum}]`; + }); assistantText += transformed; @@ -91,7 +88,8 @@ export const createCitationsTransform = ({ if (usedPolicies.length > 0) { const citationFootnoteMarkdown = usedPolicies .map( - (p) => `[^${p.docNumber}]: [${p.metadata.title}](${p.metadata.url})` + (p) => + `[^${p.docNumber}]: [${p.metadata.title}](${p.metadata.url})` ) .join('\n'); diff --git a/web/src/lib/gtagProvider.tsx b/web/src/lib/gtagProvider.tsx index 7a289ee..4197afd 100644 --- a/web/src/lib/gtagProvider.tsx +++ b/web/src/lib/gtagProvider.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-console */ import { GoogleTagManager } from '@next/third-parties/google'; const GtagProvider: React.FC = ({}) => { diff --git a/web/vitest.config.mts b/web/vitest.config.mts index 76b228d..4ac6027 100644 --- a/web/vitest.config.mts +++ b/web/vitest.config.mts @@ -5,4 +5,3 @@ export default defineConfig({ environment: 'node', }, }); -