diff --git a/.eslintignore b/.eslintignore
index 08975255475..8109e6bec48 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1 +1,2 @@
-public/serviceWorker.js
\ No newline at end of file
+public/serviceWorker.js
+app/mcp/mcp_config.json
\ No newline at end of file
diff --git a/README_CN.md b/README_CN.md
index 8173b9c4d1c..31b596f0bbd 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -6,7 +6,7 @@
NextChat
-一键免费部署你的私人 ChatGPT 网页应用,支持 GPT3, GPT4 & Gemini Pro 模型。
+一键免费部署你的私人 ChatGPT 网页应用,支持 Claude, GPT4 & Gemini Pro 模型。
[NextChatAI](https://nextchat.dev/chat?utm_source=readme) / [企业版](#%E4%BC%81%E4%B8%9A%E7%89%88) / [演示 Demo](https://chat-gpt-next-web.vercel.app/) / [反馈 Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [加入 Discord](https://discord.gg/zrhvHCr79N)
diff --git a/app/mcp/actions.ts b/app/mcp/actions.ts
new file mode 100644
index 00000000000..5fe611b3a84
--- /dev/null
+++ b/app/mcp/actions.ts
@@ -0,0 +1,82 @@
+"use server";
+
+import { createClient, executeRequest } from "./client";
+import { MCPClientLogger } from "./logger";
+import conf from "./mcp_config.json";
+import { McpRequestMessage } from "./types";
+
+const logger = new MCPClientLogger("MCP Actions");
+
+// Use Map to store all clients
+const clientsMap = new Map();
+
+// Whether initialized
+let initialized = false;
+
+// Store failed clients
+let errorClients: string[] = [];
+
+// Initialize all configured clients
+export async function initializeMcpClients() {
+ // If already initialized, return
+ if (initialized) {
+ return;
+ }
+
+ logger.info("Starting to initialize MCP clients...");
+
+ // Initialize all clients, key is clientId, value is client config
+ for (const [clientId, config] of Object.entries(conf.mcpServers)) {
+ try {
+ logger.info(`Initializing MCP client: ${clientId}`);
+ const client = await createClient(config, clientId);
+ clientsMap.set(clientId, client);
+ logger.success(`Client ${clientId} initialized`);
+ } catch (error) {
+ errorClients.push(clientId);
+ logger.error(`Failed to initialize client ${clientId}: ${error}`);
+ }
+ }
+
+ initialized = true;
+
+ if (errorClients.length > 0) {
+ logger.warn(`Failed to initialize clients: ${errorClients.join(", ")}`);
+ } else {
+ logger.success("All MCP clients initialized");
+ }
+
+ const availableClients = await getAvailableClients();
+
+ logger.info(`Available clients: ${availableClients.join(",")}`);
+}
+
+// Execute MCP request
+export async function executeMcpAction(
+ clientId: string,
+ request: McpRequestMessage,
+) {
+ try {
+ // Find the corresponding client
+ const client = clientsMap.get(clientId);
+ if (!client) {
+ logger.error(`Client ${clientId} not found`);
+ return;
+ }
+
+ logger.info(`Executing MCP request for ${clientId}`);
+
+ // Execute request and return result
+ return await executeRequest(client, request);
+ } catch (error) {
+ logger.error(`MCP execution error: ${error}`);
+ throw error;
+ }
+}
+
+// Get all available client IDs
+export async function getAvailableClients() {
+ return Array.from(clientsMap.keys()).filter(
+ (clientId) => !errorClients.includes(clientId),
+ );
+}
diff --git a/app/mcp/client.ts b/app/mcp/client.ts
new file mode 100644
index 00000000000..0600f00be92
--- /dev/null
+++ b/app/mcp/client.ts
@@ -0,0 +1,88 @@
+import { Client } from "@modelcontextprotocol/sdk/client/index.js";
+import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
+import { MCPClientLogger } from "./logger";
+import { McpRequestMessage } from "./types";
+import { z } from "zod";
+
+export interface ServerConfig {
+ command: string;
+ args?: string[];
+ env?: Record;
+}
+
+const logger = new MCPClientLogger();
+
+export async function createClient(
+ serverConfig: ServerConfig,
+ name: string,
+): Promise {
+ logger.info(`Creating client for server ${name}`);
+
+ const transport = new StdioClientTransport({
+ command: serverConfig.command,
+ args: serverConfig.args,
+ env: serverConfig.env,
+ });
+ const client = new Client(
+ {
+ name: `nextchat-mcp-client-${name}`,
+ version: "1.0.0",
+ },
+ {
+ capabilities: {
+ // roots: {
+ // listChanged: true,
+ // },
+ },
+ },
+ );
+ await client.connect(transport);
+ return client;
+}
+
+interface Primitive {
+ type: "resource" | "tool" | "prompt";
+ value: any;
+}
+
+/** List all resources, tools, and prompts */
+export async function listPrimitives(client: Client) {
+ const capabilities = client.getServerCapabilities();
+ const primitives: Primitive[] = [];
+ const promises = [];
+ if (capabilities?.resources) {
+ promises.push(
+ client.listResources().then(({ resources }) => {
+ resources.forEach((item) =>
+ primitives.push({ type: "resource", value: item }),
+ );
+ }),
+ );
+ }
+ if (capabilities?.tools) {
+ promises.push(
+ client.listTools().then(({ tools }) => {
+ tools.forEach((item) => primitives.push({ type: "tool", value: item }));
+ }),
+ );
+ }
+ if (capabilities?.prompts) {
+ promises.push(
+ client.listPrompts().then(({ prompts }) => {
+ prompts.forEach((item) =>
+ primitives.push({ type: "prompt", value: item }),
+ );
+ }),
+ );
+ }
+ await Promise.all(promises);
+ return primitives;
+}
+
+/** Execute a request */
+export async function executeRequest(
+ client: Client,
+ request: McpRequestMessage,
+) {
+ return client.request(request, z.any());
+}
diff --git a/app/mcp/example.ts b/app/mcp/example.ts
new file mode 100644
index 00000000000..83fc8784cf6
--- /dev/null
+++ b/app/mcp/example.ts
@@ -0,0 +1,31 @@
+import { createClient, listPrimitives } from "@/app/mcp/client";
+import { MCPClientLogger } from "@/app/mcp/logger";
+import conf from "./mcp_config.json";
+
+const logger = new MCPClientLogger("MCP Server Example", true);
+
+async function main() {
+ logger.info("Connecting to server...");
+
+ const client = await createClient(conf.mcpServers.everything, "everything");
+ const primitives = await listPrimitives(client);
+
+ logger.success(`Connected to server everything`);
+
+ logger.info(
+ `server capabilities: ${Object.keys(
+ client.getServerCapabilities() ?? [],
+ ).join(", ")}`,
+ );
+
+ logger.info("Server supports the following primitives:");
+
+ primitives.forEach((primitive) => {
+ logger.info("\n" + JSON.stringify(primitive, null, 2));
+ });
+}
+
+main().catch((error) => {
+ logger.error(error);
+ process.exit(1);
+});
diff --git a/app/mcp/logger.ts b/app/mcp/logger.ts
new file mode 100644
index 00000000000..25129c592c3
--- /dev/null
+++ b/app/mcp/logger.ts
@@ -0,0 +1,65 @@
+// ANSI color codes for terminal output
+const colors = {
+ reset: "\x1b[0m",
+ bright: "\x1b[1m",
+ dim: "\x1b[2m",
+ green: "\x1b[32m",
+ yellow: "\x1b[33m",
+ red: "\x1b[31m",
+ blue: "\x1b[34m",
+};
+
+export class MCPClientLogger {
+ private readonly prefix: string;
+ private readonly debugMode: boolean;
+
+ constructor(
+ prefix: string = "NextChat MCP Client",
+ debugMode: boolean = false,
+ ) {
+ this.prefix = prefix;
+ this.debugMode = debugMode;
+ }
+
+ info(message: any) {
+ this.print(colors.blue, message);
+ }
+
+ success(message: any) {
+ this.print(colors.green, message);
+ }
+
+ error(message: any) {
+ this.print(colors.red, message);
+ }
+
+ warn(message: any) {
+ this.print(colors.yellow, message);
+ }
+
+ debug(message: any) {
+ if (this.debugMode) {
+ this.print(colors.dim, message);
+ }
+ }
+
+ /**
+ * Format message to string, if message is object, convert to JSON string
+ */
+ private formatMessage(message: any): string {
+ return typeof message === "object"
+ ? JSON.stringify(message, null, 2)
+ : message;
+ }
+
+ /**
+ * Print formatted message to console
+ */
+ private print(color: string, message: any) {
+ const formattedMessage = this.formatMessage(message);
+ const logMessage = `${color}${colors.bright}[${this.prefix}]${colors.reset} ${formattedMessage}`;
+
+ // 只使用 console.log,这样日志会显示在 Tauri 的终端中
+ console.log(logMessage);
+ }
+}
diff --git a/app/mcp/mcp_config.json b/app/mcp/mcp_config.json
new file mode 100644
index 00000000000..6ad18236b52
--- /dev/null
+++ b/app/mcp/mcp_config.json
@@ -0,0 +1,16 @@
+{
+ "mcpServers": {
+ "filesystem": {
+ "command": "npx",
+ "args": [
+ "-y",
+ "@modelcontextprotocol/server-filesystem",
+ "/Users/kadxy/Desktop"
+ ]
+ },
+ "everything": {
+ "command": "npx",
+ "args": ["-y", "@modelcontextprotocol/server-everything"]
+ }
+ }
+}
diff --git a/app/mcp/types.ts b/app/mcp/types.ts
new file mode 100644
index 00000000000..763121bad88
--- /dev/null
+++ b/app/mcp/types.ts
@@ -0,0 +1,61 @@
+// ref: https://spec.modelcontextprotocol.io/specification/basic/messages/
+
+import { z } from "zod";
+
+export interface McpRequestMessage {
+ jsonrpc?: "2.0";
+ id?: string | number;
+ method: "tools/call" | string;
+ params?: {
+ [key: string]: unknown;
+ };
+}
+
+export const McpRequestMessageSchema: z.ZodType = z.object({
+ jsonrpc: z.literal("2.0").optional(),
+ id: z.union([z.string(), z.number()]).optional(),
+ method: z.string(),
+ params: z.record(z.unknown()).optional(),
+});
+
+export interface McpResponseMessage {
+ jsonrpc?: "2.0";
+ id?: string | number;
+ result?: {
+ [key: string]: unknown;
+ };
+ error?: {
+ code: number;
+ message: string;
+ data?: unknown;
+ };
+}
+
+export const McpResponseMessageSchema: z.ZodType = z.object(
+ {
+ jsonrpc: z.literal("2.0").optional(),
+ id: z.union([z.string(), z.number()]).optional(),
+ result: z.record(z.unknown()).optional(),
+ error: z
+ .object({
+ code: z.number(),
+ message: z.string(),
+ data: z.unknown().optional(),
+ })
+ .optional(),
+ },
+);
+
+export interface McpNotifications {
+ jsonrpc?: "2.0";
+ method: string;
+ params?: {
+ [key: string]: unknown;
+ };
+}
+
+export const McpNotificationsSchema: z.ZodType = z.object({
+ jsonrpc: z.literal("2.0").optional(),
+ method: z.string(),
+ params: z.record(z.unknown()).optional(),
+});
diff --git a/app/mcp/utils.ts b/app/mcp/utils.ts
new file mode 100644
index 00000000000..5b6dcbf027f
--- /dev/null
+++ b/app/mcp/utils.ts
@@ -0,0 +1,11 @@
+export function isMcpJson(content: string) {
+ return content.match(/```json:mcp:(\w+)([\s\S]*?)```/);
+}
+
+export function extractMcpJson(content: string) {
+ const match = content.match(/```json:mcp:(\w+)([\s\S]*?)```/);
+ if (match) {
+ return { clientId: match[1], mcp: JSON.parse(match[2]) };
+ }
+ return null;
+}
diff --git a/app/page.tsx b/app/page.tsx
index b3f169a9b74..d4ba2a27613 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -1,12 +1,13 @@
import { Analytics } from "@vercel/analytics/react";
-
import { Home } from "./components/home";
-
import { getServerSideConfig } from "./config/server";
+import { initializeMcpClients } from "./mcp/actions";
const serverConfig = getServerSideConfig();
export default async function App() {
+ await initializeMcpClients();
+
return (
<>
diff --git a/app/store/chat.ts b/app/store/chat.ts
index 63d7394ece6..e0ee956219c 100644
--- a/app/store/chat.ts
+++ b/app/store/chat.ts
@@ -1,4 +1,9 @@
-import { getMessageTextContent, trimTopic } from "../utils";
+import {
+ getMessageTextContent,
+ isDalle3,
+ safeLocalStorage,
+ trimTopic,
+} from "../utils";
import { indexedDBStorage } from "@/app/utils/indexedDB-storage";
import { nanoid } from "nanoid";
@@ -14,14 +19,13 @@ import {
DEFAULT_INPUT_TEMPLATE,
DEFAULT_MODELS,
DEFAULT_SYSTEM_TEMPLATE,
+ GEMINI_SUMMARIZE_MODEL,
KnowledgeCutOffDate,
+ ServiceProvider,
StoreKey,
SUMMARIZE_MODEL,
- GEMINI_SUMMARIZE_MODEL,
- ServiceProvider,
} from "../constant";
import Locale, { getLang } from "../locales";
-import { isDalle3, safeLocalStorage } from "../utils";
import { prettyObject } from "../utils/format";
import { createPersistStore } from "../utils/store";
import { estimateTokenLength } from "../utils/token";
@@ -29,6 +33,8 @@ import { ModelConfig, ModelType, useAppConfig } from "./config";
import { useAccessStore } from "./access";
import { collectModelsWithDefaultModel } from "../utils/model";
import { createEmptyMask, Mask } from "./mask";
+import { executeMcpAction } from "../mcp/actions";
+import { extractMcpJson, isMcpJson } from "../mcp/utils";
const localStorage = safeLocalStorage();
@@ -53,6 +59,7 @@ export type ChatMessage = RequestMessage & {
model?: ModelType;
tools?: ChatMessageTool[];
audio_url?: string;
+ isMcpResponse?: boolean;
};
export function createMessage(override: Partial): ChatMessage {
@@ -358,24 +365,30 @@ export const useChatStore = createPersistStore(
session.messages = session.messages.concat();
session.lastUpdate = Date.now();
});
+
get().updateStat(message, targetSession);
+
+ get().checkMcpJson(message);
+
get().summarizeSession(false, targetSession);
},
- async onUserInput(content: string, attachImages?: string[]) {
+ async onUserInput(
+ content: string,
+ attachImages?: string[],
+ isMcpResponse?: boolean,
+ ) {
const session = get().currentSession();
const modelConfig = session.mask.modelConfig;
- const userContent = fillTemplateWith(content, modelConfig);
- console.log("[User Input] after template: ", userContent);
-
- let mContent: string | MultimodalContent[] = userContent;
+ // MCP Response no need to fill template
+ let mContent: string | MultimodalContent[] = isMcpResponse
+ ? content
+ : fillTemplateWith(content, modelConfig);
- if (attachImages && attachImages.length > 0) {
+ if (!isMcpResponse && attachImages && attachImages.length > 0) {
mContent = [
- ...(userContent
- ? [{ type: "text" as const, text: userContent }]
- : []),
+ ...(content ? [{ type: "text" as const, text: content }] : []),
...attachImages.map((url) => ({
type: "image_url" as const,
image_url: { url },
@@ -386,6 +399,7 @@ export const useChatStore = createPersistStore(
let userMessage: ChatMessage = createMessage({
role: "user",
content: mContent,
+ isMcpResponse,
});
const botMessage: ChatMessage = createMessage({
@@ -425,7 +439,7 @@ export const useChatStore = createPersistStore(
session.messages = session.messages.concat();
});
},
- onFinish(message) {
+ async onFinish(message) {
botMessage.streaming = false;
if (message) {
botMessage.content = message;
@@ -764,6 +778,36 @@ export const useChatStore = createPersistStore(
lastInput,
});
},
+
+ /** check if the message contains MCP JSON and execute the MCP action */
+ checkMcpJson(message: ChatMessage) {
+ const content = getMessageTextContent(message);
+ if (isMcpJson(content)) {
+ try {
+ const mcpRequest = extractMcpJson(content);
+ if (mcpRequest) {
+ console.debug("[MCP Request]", mcpRequest);
+
+ executeMcpAction(mcpRequest.clientId, mcpRequest.mcp)
+ .then((result) => {
+ console.log("[MCP Response]", result);
+ const mcpResponse =
+ typeof result === "object"
+ ? JSON.stringify(result)
+ : String(result);
+ get().onUserInput(
+ `\`\`\`json:mcp:${mcpRequest.clientId}\n${mcpResponse}\n\`\`\``,
+ [],
+ true,
+ );
+ })
+ .catch((error) => showToast(String(error)));
+ }
+ } catch (error) {
+ console.error("[MCP Error]", error);
+ }
+ }
+ },
};
return methods;
diff --git a/next.config.mjs b/next.config.mjs
index 2bb6bc4f4b2..80241913929 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -32,6 +32,7 @@ const nextConfig = {
},
experimental: {
forceSwcTransforms: true,
+ serverActions: true,
},
};
@@ -71,8 +72,10 @@ if (mode !== "export") {
// },
{
// https://{resource_name}.openai.azure.com/openai/deployments/{deploy_name}/chat/completions
- source: "/api/proxy/azure/:resource_name/deployments/:deploy_name/:path*",
- destination: "https://:resource_name.openai.azure.com/openai/deployments/:deploy_name/:path*",
+ source:
+ "/api/proxy/azure/:resource_name/deployments/:deploy_name/:path*",
+ destination:
+ "https://:resource_name.openai.azure.com/openai/deployments/:deploy_name/:path*",
},
{
source: "/api/proxy/google/:path*",
@@ -99,7 +102,7 @@ if (mode !== "export") {
destination: "https://dashscope.aliyuncs.com/api/:path*",
},
];
-
+
return {
beforeFiles: ret,
};
diff --git a/package.json b/package.json
index e081567a4b1..0efe27b391a 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
"export:dev": "concurrently -r \"yarn mask:watch\" \"cross-env BUILD_MODE=export BUILD_APP=1 next dev\"",
"app:dev": "concurrently -r \"yarn mask:watch\" \"yarn tauri dev\"",
"app:build": "yarn mask && yarn tauri build",
+ "app:clear": "yarn tauri dev",
"prompts": "node ./scripts/fetch-prompts.mjs",
"prepare": "husky install",
"proxy-dev": "sh ./scripts/init-proxy.sh && proxychains -f ./scripts/proxychains.conf yarn dev",
@@ -22,6 +23,7 @@
"dependencies": {
"@fortaine/fetch-event-source": "^3.0.6",
"@hello-pangea/dnd": "^16.5.0",
+ "@modelcontextprotocol/sdk": "^1.0.4",
"@next/third-parties": "^14.1.0",
"@svgr/webpack": "^6.5.1",
"@vercel/analytics": "^0.1.11",
@@ -49,14 +51,15 @@
"remark-breaks": "^3.0.2",
"remark-gfm": "^3.0.1",
"remark-math": "^5.1.1",
+ "rt-client": "https://github.com/Azure-Samples/aoai-realtime-audio-sdk/releases/download/js/v0.5.0/rt-client-0.5.0.tgz",
"sass": "^1.59.2",
"spark-md5": "^3.0.2",
"use-debounce": "^9.0.4",
- "zustand": "^4.3.8",
- "rt-client": "https://github.com/Azure-Samples/aoai-realtime-audio-sdk/releases/download/js/v0.5.0/rt-client-0.5.0.tgz"
+ "zod": "^3.24.1",
+ "zustand": "^4.3.8"
},
"devDependencies": {
- "@tauri-apps/api": "^1.6.0",
+ "@tauri-apps/api": "^2.1.1",
"@tauri-apps/cli": "1.5.11",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
diff --git a/tsconfig.json b/tsconfig.json
index c73eef3e876..6d24b42f1de 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,6 +1,6 @@
{
"compilerOptions": {
- "target": "ES2015",
+ "target": "ES2022",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
@@ -23,6 +23,6 @@
"@/*": ["./*"]
}
},
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "app/calcTextareaHeight.ts"],
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
diff --git a/yarn.lock b/yarn.lock
index dffc35e9cb7..5b9741b2b4c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1797,6 +1797,15 @@
"@jridgewell/resolve-uri" "3.1.0"
"@jridgewell/sourcemap-codec" "1.4.14"
+"@modelcontextprotocol/sdk@^1.0.4":
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/@modelcontextprotocol/sdk/-/sdk-1.0.4.tgz#34ad1edd3db7dd7154e782312dfb29d2d0c11d21"
+ integrity sha512-C+jw1lF6HSGzs7EZpzHbXfzz9rj9him4BaoumlTciW/IDDgIpweF/qiCWKlP02QKg5PPcgY6xY2WCt5y2tpYow==
+ dependencies:
+ content-type "^1.0.5"
+ raw-body "^3.0.0"
+ zod "^3.23.8"
+
"@next/env@14.1.1":
version "14.1.1"
resolved "https://registry.yarnpkg.com/@next/env/-/env-14.1.1.tgz#80150a8440eb0022a73ba353c6088d419b908bac"
@@ -2029,10 +2038,10 @@
dependencies:
tslib "^2.4.0"
-"@tauri-apps/api@^1.6.0":
- version "1.6.0"
- resolved "https://registry.npmjs.org/@tauri-apps/api/-/api-1.6.0.tgz#745b7e4e26782c3b2ad9510d558fa5bb2cf29186"
- integrity sha512-rqI++FWClU5I2UBp4HXFvl+sBWkdigBkxnpJDQUWttNyG7IZP4FwQGhTNL5EOw0vI8i6eSAJ5frLqO7n7jbJdg==
+"@tauri-apps/api@^2.1.1":
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/@tauri-apps/api/-/api-2.1.1.tgz#77d4ddb683d31072de4e6a47c8613d9db011652b"
+ integrity sha512-fzUfFFKo4lknXGJq8qrCidkUcKcH2UHhfaaCNt4GzgzGaW2iS26uFOg4tS3H4P8D6ZEeUxtiD5z0nwFF0UN30A==
"@tauri-apps/cli-darwin-arm64@1.5.11":
version "1.5.11"
@@ -3039,6 +3048,11 @@ busboy@1.6.0:
dependencies:
streamsearch "^1.1.0"
+bytes@3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
+ integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
+
call-bind@^1.0.0, call-bind@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
@@ -3285,6 +3299,11 @@ concurrently@^8.2.2:
tree-kill "^1.2.2"
yargs "^17.7.2"
+content-type@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
+ integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
+
convert-source-map@^1.7.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f"
@@ -3849,6 +3868,11 @@ delayed-stream@~1.0.0:
resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
+depd@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
+ integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
+
dequal@^2.0.0, dequal@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be"
@@ -5007,6 +5031,17 @@ html-to-image@^1.11.11:
resolved "https://registry.npmmirror.com/html-to-image/-/html-to-image-1.11.11.tgz#c0f8a34dc9e4b97b93ff7ea286eb8562642ebbea"
integrity sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA==
+http-errors@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
+ integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
+ dependencies:
+ depd "2.0.0"
+ inherits "2.0.4"
+ setprototypeof "1.2.0"
+ statuses "2.0.1"
+ toidentifier "1.0.1"
+
http-proxy-agent@^5.0.0:
version "5.0.0"
resolved "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43"
@@ -5095,7 +5130,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
-inherits@2:
+inherits@2, inherits@2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -7138,6 +7173,16 @@ randombytes@^2.1.0:
dependencies:
safe-buffer "^5.1.0"
+raw-body@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-3.0.0.tgz#25b3476f07a51600619dae3fe82ddc28a36e5e0f"
+ integrity sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==
+ dependencies:
+ bytes "3.1.2"
+ http-errors "2.0.0"
+ iconv-lite "0.6.3"
+ unpipe "1.0.0"
+
react-dom@^18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
@@ -7569,6 +7614,11 @@ serialize-javascript@^6.0.1:
dependencies:
randombytes "^2.1.0"
+setprototypeof@1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
+ integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
+
shebang-command@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
@@ -7699,6 +7749,11 @@ stack-utils@^2.0.3:
dependencies:
escape-string-regexp "^2.0.0"
+statuses@2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
+ integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
+
stop-iteration-iterator@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4"
@@ -7977,6 +8032,11 @@ to-regex-range@^5.0.1:
dependencies:
is-number "^7.0.0"
+toidentifier@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
+ integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
+
tough-cookie@^4.1.2:
version "4.1.4"
resolved "https://registry.npmmirror.com/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36"
@@ -8219,6 +8279,11 @@ universalify@^0.2.0:
resolved "https://registry.npmmirror.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0"
integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==
+unpipe@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
+ integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
+
update-browserslist-db@^1.0.10:
version "1.0.10"
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3"
@@ -8572,6 +8637,11 @@ yocto-queue@^0.1.0:
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
+zod@^3.23.8, zod@^3.24.1:
+ version "3.24.1"
+ resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.1.tgz#27445c912738c8ad1e9de1bea0359fa44d9d35ee"
+ integrity sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==
+
zustand@^4.3.8:
version "4.3.8"
resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.3.8.tgz#37113df8e9e1421b0be1b2dca02b49b76210e7c4"