diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 99d7340..b9c9165 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -24,3 +24,5 @@ jobs: run: npm install - name: Run build run: npm run build + env: + NEXT_PUBLIC_BASE_URL: https://cosmic.csui.dev diff --git a/app/menfess/page.tsx b/app/menfess/page.tsx index 62b85cf..595c2be 100644 --- a/app/menfess/page.tsx +++ b/app/menfess/page.tsx @@ -40,7 +40,6 @@ export const metadata: Metadata = { }, }; - const MenfessPage = async () => { const res = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/menfess`, { next: { revalidate: 10 }, // Revalidate every 10 seconds diff --git a/components/MenfessPage/send.tsx b/components/MenfessPage/send.tsx index 320149e..efa3c0b 100644 --- a/components/MenfessPage/send.tsx +++ b/components/MenfessPage/send.tsx @@ -8,6 +8,7 @@ import { briefFamsData } from "@/modules/fams-data"; import { useMemo } from "react"; import Image from "next/image"; import { toast } from "sonner"; +import { detectHate } from "@/lib/detectHate"; const SendMenfess = () => { const [to, setTo] = useState(""); @@ -27,6 +28,7 @@ const SendMenfess = () => { toast.error("Please fill all fields"); return; } + const menfess = { to: to, from: from, @@ -39,6 +41,23 @@ const SendMenfess = () => { } console.log(to, from, message); const loader = toast.loading("Sending menfess..."); + + const status = await detectHate(message); + console.log(status); + + if (status === "ERROR") { + toast.error("LLM Error", { + id: loader, + }); + } + + if (status === "HATEFUL") { + toast.error("Message is indicated to be hateful speech", { + id: loader, + }); + return; + } + const res = await fetch("/api/menfess", { method: "POST", headers: { @@ -48,9 +67,12 @@ const SendMenfess = () => { }); const data = await res.json(); if (data.success) { - toast.success("Menfess sent successfully", { - id: loader, - }); + if (status === "ERROR") { + } else { + toast.success("Menfess sent successfully", { + id: loader, + }); + } setTo(""); setFrom(""); setMessage(""); diff --git a/lib/detectHate.ts b/lib/detectHate.ts new file mode 100644 index 0000000..eeca490 --- /dev/null +++ b/lib/detectHate.ts @@ -0,0 +1,41 @@ +"use server"; + +import { generateText } from "ai"; +// import { google } from "@ai-sdk/google"; +import { createGoogleGenerativeAI } from "@ai-sdk/google"; + +const google = createGoogleGenerativeAI({ + apiKey: process.env.GOOGLE_API_KEY, +}); + +export async function detectHate( + input: string +): Promise<"HATEFUL" | "NOT_HATEFUL" | "ERROR"> { + try { + const prompt = ` +You are a content moderation assistant. Classify the input below as either: + +- "HATEFUL" — if it forms any kind of hateful speech or abusive behavior, especially toward an individual whether it's explicitly or implicitly +- "NOT_HATEFUL" — if it does not. + +Only respond with "HATEFUL" or "NOT_HATEFUL", nothing else. Detect it either in bahasa indonesia or english + +Input: +"""${input}""" + `.trim(); + + const result = await generateText({ + model: google("gemini-2.0-flash"), + prompt, + }); + + const output = result.text.trim(); + const status = + output === "HATEFUL" || output === "NOT_HATEFUL" ? output : "ERROR"; + + return status; + } catch (e) { + console.error("Moderation error:", e); + return "ERROR"; + } +} diff --git a/lib/generated/prisma/edge.js b/lib/generated/prisma/edge.js index e484039..e4990c2 100644 --- a/lib/generated/prisma/edge.js +++ b/lib/generated/prisma/edge.js @@ -35,12 +35,12 @@ exports.Prisma = Prisma exports.$Enums = {} /** - * Prisma Client JS version: 6.8.2 - * Query Engine version: 2060c79ba17c6bb9f5823312b6f6b7f4a845738e + * Prisma Client JS version: 6.10.1 + * Query Engine version: 9b628578b3b7cae625e8c927178f15a170e74a9c */ Prisma.prismaVersion = { - client: "6.8.2", - engine: "2060c79ba17c6bb9f5823312b6f6b7f4a845738e" + client: "6.10.1", + engine: "9b628578b3b7cae625e8c927178f15a170e74a9c" } Prisma.PrismaClientKnownRequestError = PrismaClientKnownRequestError; @@ -143,7 +143,7 @@ const config = { "value": "prisma-client-js" }, "output": { - "value": "D:\\Projects\\WebAngkatanCSUI24\\csui24-web\\lib\\generated\\prisma", + "value": "/home/kims/Projects/csui24-web/lib/generated/prisma", "fromEnvVar": null }, "config": { @@ -152,7 +152,7 @@ const config = { "binaryTargets": [ { "fromEnvVar": null, - "value": "windows", + "value": "debian-openssl-3.0.x", "native": true }, { @@ -161,7 +161,7 @@ const config = { } ], "previewFeatures": [], - "sourceFilePath": "D:\\Projects\\WebAngkatanCSUI24\\csui24-web\\prisma\\schema.prisma", + "sourceFilePath": "/home/kims/Projects/csui24-web/prisma/schema.prisma", "isCustomOutput": true }, "relativeEnvPaths": { @@ -169,13 +169,12 @@ const config = { "schemaEnvPath": "../../../.env" }, "relativePath": "../../../prisma", - "clientVersion": "6.8.2", - "engineVersion": "2060c79ba17c6bb9f5823312b6f6b7f4a845738e", + "clientVersion": "6.10.1", + "engineVersion": "9b628578b3b7cae625e8c927178f15a170e74a9c", "datasourceNames": [ "db" ], "activeProvider": "postgresql", - "postinstall": false, "inlineDatasources": { "db": { "url": { diff --git a/lib/generated/prisma/index-browser.js b/lib/generated/prisma/index-browser.js index bef022f..61fc7f1 100644 --- a/lib/generated/prisma/index-browser.js +++ b/lib/generated/prisma/index-browser.js @@ -20,12 +20,12 @@ exports.Prisma = Prisma exports.$Enums = {} /** - * Prisma Client JS version: 6.8.2 - * Query Engine version: 2060c79ba17c6bb9f5823312b6f6b7f4a845738e + * Prisma Client JS version: 6.10.1 + * Query Engine version: 9b628578b3b7cae625e8c927178f15a170e74a9c */ Prisma.prismaVersion = { - client: "6.8.2", - engine: "2060c79ba17c6bb9f5823312b6f6b7f4a845738e" + client: "6.10.1", + engine: "9b628578b3b7cae625e8c927178f15a170e74a9c" } Prisma.PrismaClientKnownRequestError = () => { diff --git a/lib/generated/prisma/index.d.ts b/lib/generated/prisma/index.d.ts index 49eeaef..6a20eea 100644 --- a/lib/generated/prisma/index.d.ts +++ b/lib/generated/prisma/index.d.ts @@ -241,8 +241,8 @@ export namespace Prisma { export import Exact = $Public.Exact /** - * Prisma Client JS version: 6.8.2 - * Query Engine version: 2060c79ba17c6bb9f5823312b6f6b7f4a845738e + * Prisma Client JS version: 6.10.1 + * Query Engine version: 9b628578b3b7cae625e8c927178f15a170e74a9c */ export type PrismaVersion = { client: string diff --git a/lib/generated/prisma/index.js b/lib/generated/prisma/index.js index bd77527..5876a94 100644 --- a/lib/generated/prisma/index.js +++ b/lib/generated/prisma/index.js @@ -35,12 +35,12 @@ exports.Prisma = Prisma exports.$Enums = {} /** - * Prisma Client JS version: 6.8.2 - * Query Engine version: 2060c79ba17c6bb9f5823312b6f6b7f4a845738e + * Prisma Client JS version: 6.10.1 + * Query Engine version: 9b628578b3b7cae625e8c927178f15a170e74a9c */ Prisma.prismaVersion = { - client: "6.8.2", - engine: "2060c79ba17c6bb9f5823312b6f6b7f4a845738e" + client: "6.10.1", + engine: "9b628578b3b7cae625e8c927178f15a170e74a9c" } Prisma.PrismaClientKnownRequestError = PrismaClientKnownRequestError; @@ -144,7 +144,7 @@ const config = { "value": "prisma-client-js" }, "output": { - "value": "D:\\Projects\\WebAngkatanCSUI24\\csui24-web\\lib\\generated\\prisma", + "value": "/home/kims/Projects/csui24-web/lib/generated/prisma", "fromEnvVar": null }, "config": { @@ -153,7 +153,7 @@ const config = { "binaryTargets": [ { "fromEnvVar": null, - "value": "windows", + "value": "debian-openssl-3.0.x", "native": true }, { @@ -162,7 +162,7 @@ const config = { } ], "previewFeatures": [], - "sourceFilePath": "D:\\Projects\\WebAngkatanCSUI24\\csui24-web\\prisma\\schema.prisma", + "sourceFilePath": "/home/kims/Projects/csui24-web/prisma/schema.prisma", "isCustomOutput": true }, "relativeEnvPaths": { @@ -170,13 +170,12 @@ const config = { "schemaEnvPath": "../../../.env" }, "relativePath": "../../../prisma", - "clientVersion": "6.8.2", - "engineVersion": "2060c79ba17c6bb9f5823312b6f6b7f4a845738e", + "clientVersion": "6.10.1", + "engineVersion": "9b628578b3b7cae625e8c927178f15a170e74a9c", "datasourceNames": [ "db" ], "activeProvider": "postgresql", - "postinstall": false, "inlineDatasources": { "db": { "url": { @@ -225,8 +224,8 @@ exports.PrismaClient = PrismaClient Object.assign(exports, Prisma) // file annotations for bundling tools to include these files -path.join(__dirname, "query_engine-windows.dll.node"); -path.join(process.cwd(), "lib/generated/prisma/query_engine-windows.dll.node") +path.join(__dirname, "libquery_engine-debian-openssl-3.0.x.so.node"); +path.join(process.cwd(), "lib/generated/prisma/libquery_engine-debian-openssl-3.0.x.so.node") // file annotations for bundling tools to include these files path.join(__dirname, "libquery_engine-rhel-openssl-3.0.x.so.node"); diff --git a/lib/generated/prisma/libquery_engine-debian-openssl-3.0.x.so.node b/lib/generated/prisma/libquery_engine-debian-openssl-3.0.x.so.node new file mode 100755 index 0000000..85fed5d Binary files /dev/null and b/lib/generated/prisma/libquery_engine-debian-openssl-3.0.x.so.node differ diff --git a/lib/generated/prisma/libquery_engine-rhel-openssl-3.0.x.so.node b/lib/generated/prisma/libquery_engine-rhel-openssl-3.0.x.so.node old mode 100644 new mode 100755 diff --git a/lib/generated/prisma/package.json b/lib/generated/prisma/package.json index b88975a..f03a1d7 100644 --- a/lib/generated/prisma/package.json +++ b/lib/generated/prisma/package.json @@ -97,11 +97,17 @@ "import": "./runtime/binary.mjs", "default": "./runtime/binary.mjs" }, - "./runtime/wasm": { - "types": "./runtime/wasm.d.ts", - "require": "./runtime/wasm.js", - "import": "./runtime/wasm.mjs", - "default": "./runtime/wasm.mjs" + "./runtime/wasm-engine-edge": { + "types": "./runtime/wasm-engine-edge.d.ts", + "require": "./runtime/wasm-engine-edge.js", + "import": "./runtime/wasm-engine-edge.mjs", + "default": "./runtime/wasm-engine-edge.mjs" + }, + "./runtime/wasm-compiler-edge": { + "types": "./runtime/wasm-compiler-edge.d.ts", + "require": "./runtime/wasm-compiler-edge.js", + "import": "./runtime/wasm-compiler-edge.mjs", + "default": "./runtime/wasm-compiler-edge.mjs" }, "./runtime/edge": { "types": "./runtime/edge.d.ts", @@ -135,6 +141,6 @@ }, "./*": "./*" }, - "version": "6.8.2", + "version": "6.10.1", "sideEffects": false } \ No newline at end of file diff --git a/lib/generated/prisma/wasm.js b/lib/generated/prisma/wasm.js index bef022f..61fc7f1 100644 --- a/lib/generated/prisma/wasm.js +++ b/lib/generated/prisma/wasm.js @@ -20,12 +20,12 @@ exports.Prisma = Prisma exports.$Enums = {} /** - * Prisma Client JS version: 6.8.2 - * Query Engine version: 2060c79ba17c6bb9f5823312b6f6b7f4a845738e + * Prisma Client JS version: 6.10.1 + * Query Engine version: 9b628578b3b7cae625e8c927178f15a170e74a9c */ Prisma.prismaVersion = { - client: "6.8.2", - engine: "2060c79ba17c6bb9f5823312b6f6b7f4a845738e" + client: "6.10.1", + engine: "9b628578b3b7cae625e8c927178f15a170e74a9c" } Prisma.PrismaClientKnownRequestError = () => { diff --git a/package.json b/package.json index 8c23a0d..80bbeb9 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "postbuild": "next-sitemap --config next-sitemap.config.mjs" }, "dependencies": { + "@ai-sdk/google": "^1.2.19", "@prisma/client": "^6.8.1", "@radix-ui/react-alert-dialog": "^1.1.2", "@radix-ui/react-dialog": "^1.1.14", @@ -17,6 +18,7 @@ "@radix-ui/react-slot": "^1.2.2", "@vercel/analytics": "^1.5.0", "@vercel/speed-insights": "^1.2.0", + "ai": "^4.3.16", "blurhash-base64": "^0.0.3", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", diff --git a/pages/api/menfess.ts b/pages/api/menfess.ts index 1faddf1..5997432 100644 --- a/pages/api/menfess.ts +++ b/pages/api/menfess.ts @@ -3,6 +3,7 @@ import { PrismaClient } from "@/lib/generated/prisma"; import { TwitterApi } from "twitter-api-v2"; import { briefFamsData } from "@/modules/fams-data"; import { globalRateLimit } from "@/lib/rateLimiter"; +import { detectHate } from "@/lib/detectHate"; const prisma = new PrismaClient(); @@ -30,7 +31,9 @@ export default async function handler( orderBy: { createdAt: "desc", }, - take: process.env.LIMIT_MENFESS ? parseInt(process.env.LIMIT_MENFESS) : undefined, + take: process.env.LIMIT_MENFESS + ? parseInt(process.env.LIMIT_MENFESS) + : undefined, }); return res.status(200).json({ success: true, @@ -132,6 +135,25 @@ export default async function handler( data: null, }); } + + const status = await detectHate(message); + + if (status === "ERROR") { + return res.status(200).json({ + success: true, + message: "LLM Error", + data: null, + }); + } + + if (status === "HATEFUL") { + return res.status(403).json({ + success: false, + message: "Message is indicated to be hateful speech", + data: null, + }); + } + try { const newMenfess = await prisma.menfess.create({ data: { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9307024..eb67676 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@ai-sdk/google': + specifier: ^1.2.19 + version: 1.2.19(zod@3.25.67) '@prisma/client': specifier: ^6.8.1 version: 6.8.2(prisma@6.8.2(typescript@5.6.3))(typescript@5.6.3) @@ -25,10 +28,13 @@ importers: version: 1.2.2(@types/react@18.3.12)(react@18.3.1) '@vercel/analytics': specifier: ^1.5.0 - version: 1.5.0(next@14.2.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + version: 1.5.0(next@14.2.28(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) '@vercel/speed-insights': specifier: ^1.2.0 - version: 1.2.0(next@14.2.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + version: 1.2.0(next@14.2.28(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + ai: + specifier: ^4.3.16 + version: 4.3.16(react@18.3.1)(zod@3.25.67) blurhash-base64: specifier: ^0.0.3 version: 0.0.3 @@ -55,10 +61,10 @@ importers: version: 0.447.0(react@18.3.1) next: specifier: ^14.2.24 - version: 14.2.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 14.2.28(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) next-sitemap: specifier: ^4.2.3 - version: 4.2.3(next@14.2.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + version: 4.2.3(next@14.2.28(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) next-themes: specifier: ^0.4.6 version: 0.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -123,6 +129,38 @@ importers: packages: + '@ai-sdk/google@1.2.19': + resolution: {integrity: sha512-Xgl6eftIRQ4srUdCzxM112JuewVMij5q4JLcNmHcB68Bxn9dpr3MVUSPlJwmameuiQuISIA8lMB+iRiRbFsaqA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/provider-utils@2.2.8': + resolution: {integrity: sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.23.8 + + '@ai-sdk/provider@1.1.3': + resolution: {integrity: sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==} + engines: {node: '>=18'} + + '@ai-sdk/react@1.2.12': + resolution: {integrity: sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g==} + engines: {node: '>=18'} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + zod: ^3.23.8 + peerDependenciesMeta: + zod: + optional: true + + '@ai-sdk/ui-utils@1.2.11': + resolution: {integrity: sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.23.8 + '@alloc/quick-lru@5.2.0': resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} @@ -390,6 +428,10 @@ packages: resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} engines: {node: '>=12.4.0'} + '@opentelemetry/api@1.9.0': + resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} + engines: {node: '>=8.0.0'} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -831,6 +873,9 @@ packages: '@tybys/wasm-util@0.9.0': resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==} + '@types/diff-match-patch@1.0.36': + resolution: {integrity: sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==} + '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} @@ -1044,6 +1089,16 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + ai@4.3.16: + resolution: {integrity: sha512-KUDwlThJ5tr2Vw0A1ZkbDKNME3wzWhuVfAOwIvFUzl1TPVDFAXDFTXio3p+jaKneB+dKNCvFFlolYmmgHttG1g==} + engines: {node: '>=18'} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + zod: ^3.23.8 + peerDependenciesMeta: + react: + optional: true + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} @@ -1192,6 +1247,10 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + chalk@5.4.1: + resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -1289,6 +1348,10 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + detect-libc@2.0.4: resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} engines: {node: '>=8'} @@ -1299,6 +1362,9 @@ packages: didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + diff-match-patch@1.0.5: + resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} + diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} @@ -1842,6 +1908,9 @@ packages: json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema@0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -1849,6 +1918,11 @@ packages: resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} hasBin: true + jsondiffpatch@0.6.0: + resolution: {integrity: sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} @@ -2304,6 +2378,9 @@ packages: scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + secure-json-parse@2.7.0: + resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -2450,6 +2527,11 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + swr@2.3.3: + resolution: {integrity: sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A==} + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + tailwind-merge@2.5.4: resolution: {integrity: sha512-0q8cfZHMu9nuYP/b5Shb7Y7Sh1B7Nnl5GqNr1U+n2p6+mybvRtayrQ+0042Z5byvTA8ihjlP8Odo8/VnHbZu4Q==} @@ -2473,6 +2555,10 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + throttleit@2.1.0: + resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==} + engines: {node: '>=18'} + tinyglobby@0.2.13: resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} engines: {node: '>=12.0.0'} @@ -2595,6 +2681,11 @@ packages: '@types/react': optional: true + use-sync-external-store@1.5.0: + resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -2650,8 +2741,50 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + zod-to-json-schema@3.24.5: + resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==} + peerDependencies: + zod: ^3.24.1 + + zod@3.25.67: + resolution: {integrity: sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==} + snapshots: + '@ai-sdk/google@1.2.19(zod@3.25.67)': + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.67) + zod: 3.25.67 + + '@ai-sdk/provider-utils@2.2.8(zod@3.25.67)': + dependencies: + '@ai-sdk/provider': 1.1.3 + nanoid: 3.3.11 + secure-json-parse: 2.7.0 + zod: 3.25.67 + + '@ai-sdk/provider@1.1.3': + dependencies: + json-schema: 0.4.0 + + '@ai-sdk/react@1.2.12(react@18.3.1)(zod@3.25.67)': + dependencies: + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.67) + '@ai-sdk/ui-utils': 1.2.11(zod@3.25.67) + react: 18.3.1 + swr: 2.3.3(react@18.3.1) + throttleit: 2.1.0 + optionalDependencies: + zod: 3.25.67 + + '@ai-sdk/ui-utils@1.2.11(zod@3.25.67)': + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.67) + zod: 3.25.67 + zod-to-json-schema: 3.24.5(zod@3.25.67) + '@alloc/quick-lru@5.2.0': {} '@babel/runtime@7.27.1': {} @@ -2877,6 +3010,8 @@ snapshots: '@nolyfill/is-core-module@1.0.39': {} + '@opentelemetry/api@1.9.0': {} + '@pkgjs/parseargs@0.11.0': optional: true @@ -3262,6 +3397,8 @@ snapshots: tslib: 2.8.1 optional: true + '@types/diff-match-patch@1.0.36': {} + '@types/json5@0.0.29': {} '@types/node@20.17.5': @@ -3411,14 +3548,14 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.7.2': optional: true - '@vercel/analytics@1.5.0(next@14.2.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + '@vercel/analytics@1.5.0(next@14.2.28(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': optionalDependencies: - next: 14.2.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 14.2.28(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 - '@vercel/speed-insights@1.2.0(next@14.2.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + '@vercel/speed-insights@1.2.0(next@14.2.28(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': optionalDependencies: - next: 14.2.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 14.2.28(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 acorn-jsx@5.3.2(acorn@8.14.1): @@ -3432,6 +3569,18 @@ snapshots: acorn@8.14.1: {} + ai@4.3.16(react@18.3.1)(zod@3.25.67): + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.67) + '@ai-sdk/react': 1.2.12(react@18.3.1)(zod@3.25.67) + '@ai-sdk/ui-utils': 1.2.11(zod@3.25.67) + '@opentelemetry/api': 1.9.0 + jsondiffpatch: 0.6.0 + zod: 3.25.67 + optionalDependencies: + react: 18.3.1 + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -3601,6 +3750,8 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + chalk@5.4.1: {} + chokidar@3.6.0: dependencies: anymatch: 3.1.3 @@ -3698,12 +3849,16 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + dequal@2.0.3: {} + detect-libc@2.0.4: {} detect-node-es@1.1.0: {} didyoumean@1.2.2: {} + diff-match-patch@1.0.5: {} + diff@4.0.2: optional: true @@ -3851,8 +4006,8 @@ snapshots: '@typescript-eslint/parser': 8.32.1(eslint@8.57.1)(typescript@5.6.3) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@8.57.1) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.32.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.32.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1) eslint-plugin-react: 7.37.5(eslint@8.57.1) eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.57.1) @@ -3871,7 +4026,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0)(eslint@8.57.1): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.1 @@ -3882,22 +4037,22 @@ snapshots: tinyglobby: 0.2.13 unrs-resolver: 1.7.2 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.32.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.32.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.32.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.32.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.32.1(eslint@8.57.1)(typescript@5.6.3) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@8.57.1) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -3908,7 +4063,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.32.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.32.1(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.1(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -4405,12 +4560,20 @@ snapshots: json-schema-traverse@0.4.1: {} + json-schema@0.4.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} json5@1.0.2: dependencies: minimist: 1.2.8 + jsondiffpatch@0.6.0: + dependencies: + '@types/diff-match-patch': 1.0.36 + chalk: 5.4.1 + diff-match-patch: 1.0.5 + jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.8 @@ -4495,20 +4658,20 @@ snapshots: natural-compare@1.4.0: {} - next-sitemap@4.2.3(next@14.2.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1)): + next-sitemap@4.2.3(next@14.2.28(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)): dependencies: '@corex/deepmerge': 4.0.43 '@next/env': 13.5.11 fast-glob: 3.3.3 minimist: 1.2.8 - next: 14.2.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 14.2.28(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) next-themes@0.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - next@14.2.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + next@14.2.28(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@next/env': 14.2.28 '@swc/helpers': 0.5.5 @@ -4529,6 +4692,7 @@ snapshots: '@next/swc-win32-arm64-msvc': 14.2.28 '@next/swc-win32-ia32-msvc': 14.2.28 '@next/swc-win32-x64-msvc': 14.2.28 + '@opentelemetry/api': 1.9.0 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -4856,6 +5020,8 @@ snapshots: dependencies: loose-envify: 1.4.0 + secure-json-parse@2.7.0: {} + semver@6.3.1: {} semver@7.7.2: {} @@ -5054,6 +5220,12 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + swr@2.3.3(react@18.3.1): + dependencies: + dequal: 2.0.3 + react: 18.3.1 + use-sync-external-store: 1.5.0(react@18.3.1) + tailwind-merge@2.5.4: {} tailwindcss-animate@1.0.7(tailwindcss@3.4.14(ts-node@10.9.2(@types/node@20.17.5)(typescript@5.6.3))): @@ -5097,6 +5269,8 @@ snapshots: dependencies: any-promise: 1.3.0 + throttleit@2.1.0: {} + tinyglobby@0.2.13: dependencies: fdir: 6.4.4(picomatch@4.0.2) @@ -5248,6 +5422,10 @@ snapshots: optionalDependencies: '@types/react': 18.3.12 + use-sync-external-store@1.5.0(react@18.3.1): + dependencies: + react: 18.3.1 + util-deprecate@1.0.2: {} v8-compile-cache-lib@3.0.1: @@ -5320,3 +5498,9 @@ snapshots: optional: true yocto-queue@0.1.0: {} + + zod-to-json-schema@3.24.5(zod@3.25.67): + dependencies: + zod: 3.25.67 + + zod@3.25.67: {}