diff --git a/.github/workflows/eval-e2e.yml b/.github/workflows/eval-e2e.yml new file mode 100644 index 00000000..823f4a72 --- /dev/null +++ b/.github/workflows/eval-e2e.yml @@ -0,0 +1,33 @@ +on: + workflow_call: + inputs: + os: + required: false + description: "runs-on property, ex: ubuntu-latest, windows-latest" + type: string + default: "ubuntu-latest" + +jobs: + eval: + name: 'yarn:eval' + runs-on: ${{ input.os }} + steps: + - name: Configure git longpaths if on Windows + if: ${{ runner.os == 'Windows' }} + run: git config --system core.longpaths true + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: lts/* + cache: yarn + - uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '11' + - run: yarn + - run: yarn build + - name: Eval-based E2E tests + shell: bash + run: | + cd packages/mcp + yarn:eval diff --git a/packages/mcp/package.json b/packages/mcp/package.json index 3138c83e..ca2490fb 100644 --- a/packages/mcp/package.json +++ b/packages/mcp/package.json @@ -18,6 +18,7 @@ "lint": "wireit", "start": "yarn build && npm link && mcp-inspector sf-mcp-server", "test": "wireit", + "test:eval": "wireit", "test:only": "wireit" }, "repository": "salesforcecli/mcp", @@ -57,25 +58,30 @@ "zod": "^3.25.76" }, "devDependencies": { + "@ai-sdk/google": "^1.2.22", + "@ai-sdk/openai": "^1.3.23", "@salesforce/cli-plugins-testkit": "^5.3.39", "@salesforce/dev-config": "^4.3.2", - "prettier": "^2.8.8", - "@types/node": "^22.16.5", - "wireit": "^0.14.12", - "eslint": "^8.57.1", - "mocha": "11.7.2", - "chai": "^4.3.10", - "@types/mocha": "^10.0.10", + "@salesforce/prettier-config": "^0.0.3", "@types/chai": "^4.3.14", + "@types/mocha": "^10.0.10", + "@types/node": "^22.16.5", "@types/sinon": "^10.0.20", - "sinon": "10.0.0", - "nyc": "^17.0.0", + "ai": "^4.3.17", + "chai": "^4.3.10", + "eslint": "^8.57.1", "eslint-config-salesforce-license": "^1.0.1", "eslint-config-salesforce-typescript": "4.0.1", - "@salesforce/prettier-config": "^0.0.3", + "mocha": "11.7.2", + "nyc": "^17.0.0", "oclif": "^4.21.0", + "prettier": "^2.8.8", + "sinon": "10.0.0", "ts-node": "^10.9.2", - "typescript": "^5.8.3" + "typescript": "^5.8.3", + "vitest": "^3.2.4", + "vitest-evals": "^0.5.0", + "wireit": "^0.14.12" }, "publishConfig": { "access": "public" @@ -128,6 +134,16 @@ ], "output": [] }, + "test:eval": { + "command": "vitest run", + "files": [ + "test/**/*.eval.ts" + ], + "dependencies": [ + "test:compile" + ], + "output": [] + }, "test": { "command": "nyc mocha \"test/**/*.test.ts\"", "env": { diff --git a/packages/mcp/test/evals/describe_code_analyzer_rule.eval.ts b/packages/mcp/test/evals/describe_code_analyzer_rule.eval.ts new file mode 100644 index 00000000..e7f9f33f --- /dev/null +++ b/packages/mcp/test/evals/describe_code_analyzer_rule.eval.ts @@ -0,0 +1,29 @@ +/* + * Copyright 2025, Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { describeEval } from 'vitest-evals'; +import { TaskRunner, outputIncludesExpectationArray } from './utils.js'; + +describeEval('describe_code_analyzer_rule', { + data: async () => [{ + input: 'tell me the tags that are associated with the Code Analysis Rule named VFUnescapeEl, which is a rule for the pmd engine', + expected: ['Recommended', 'Security', 'Visualforce'] + }], + task: TaskRunner(), + scorers: [outputIncludesExpectationArray], + threshold: 0.9, + timeout: 60_000 +}); \ No newline at end of file diff --git a/packages/mcp/test/evals/run_code_analyzer.eval.ts b/packages/mcp/test/evals/run_code_analyzer.eval.ts new file mode 100644 index 00000000..bb04c375 --- /dev/null +++ b/packages/mcp/test/evals/run_code_analyzer.eval.ts @@ -0,0 +1,39 @@ +/* + * Copyright 2025, Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import path from 'node:path'; +import {fileURLToPath} from 'node:url'; +import { describeEval } from 'vitest-evals'; +import { TaskRunner } from './utils.js'; + +const fileName = fileURLToPath(import.meta.url); +const dirName = path.dirname(fileName); + +const pathToTarget: string = path.join(dirName, '..', 'fixtures', 'sample-targets', 'SampleTarget1.cls'); + +describeEval('run_code_analyzer', { + data: async () => [{ + input: `Run code analysis against ${pathToTarget}, and tell me the number of violations in that file using the response format "There are X violations".`, + expected: [6] + }], + task: TaskRunner(), + scorers: [(opts: {output: string; expected: number}) => { + const score: number = opts.output === `There are ${opts.expected} violations.` ? 1 : 0; + return {score}; + }], + threshold: 0.9, + timeout: 60_000 +}); \ No newline at end of file diff --git a/packages/mcp/test/evals/sf-query-org.eval.ts b/packages/mcp/test/evals/sf-query-org.eval.ts new file mode 100644 index 00000000..ffc8e263 --- /dev/null +++ b/packages/mcp/test/evals/sf-query-org.eval.ts @@ -0,0 +1,52 @@ +/* + * Copyright 2025, Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { describeEval } from 'vitest-evals'; +import { Factuality, TaskRunner } from './utils.js'; + +describeEval('SOQL queries', { + data: async () => [ + { + input: 'List the name of the Property__c records in my org, ordered in ascending order by their name.', + expected: `The response should include these records: +Architectural Details +City Living +Contemporary City Living +Contemporary Luxury +Heart of Harvard Square +Modern City Living +Quiet Retreat +Seaport District Retreat +Stunning Colonial +Stunning Victorian +Ultimate Sophistication +Waterfront in the City +`, + // expected: `The response should include these records: + // Sophisticated Urban Escape + // Metropolitan Elegance + // Vibrant City Sanctuary + // Downtown Dreamscape + // Sleek Urban Oasis + // Modern Metropole + // Luxe in the Loop + // `, + }, + ], + task: TaskRunner(), + scorers: [Factuality()], + threshold: 0.6, + timeout: 30_000, +}); \ No newline at end of file diff --git a/packages/mcp/test/evals/utils.ts b/packages/mcp/test/evals/utils.ts new file mode 100644 index 00000000..ea8ee97a --- /dev/null +++ b/packages/mcp/test/evals/utils.ts @@ -0,0 +1,155 @@ +/* + * Copyright 2025, Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import * as path from 'node:path'; +import { google } from '@ai-sdk/google'; +import { experimental_createMCPClient, generateObject, streamText, type LanguageModel } from 'ai'; +import { Experimental_StdioMCPTransport } from 'ai/mcp-stdio'; +import { z } from 'zod'; + +// This prompt intends to represent what an IDE context window could look like, some specifics: +// +// * Current open project directory +// * Current open file +const SYSTEM_PROMPT = `You are an assistant responsible for evaluating the results of calling various tools. +You a general purpose LLM-based Agent. Your purpose is to answer the user's query using the tools provided. +- You should ONLY use the tools available to answer the user's query. +- Use as few tool calls as possible to get to the answer. +- Using multiple tool calls to get to the answer is allowed when needed. +The current open project dir is "${process.env.SF_EVAL_PROMPT_PROJECT_DIR}" +`; + +// Supported models: https://ai.google.dev/gemini-api/docs/models +const defaultModel = google('gemini-2.5-flash'); + +export function TaskRunner(model: LanguageModel = defaultModel) { + return async function TaskRun(input: string) { + const mcpClient = await experimental_createMCPClient({ + transport: new Experimental_StdioMCPTransport({ + command: 'node', + args: [path.join(import.meta.dirname, '../../bin/run.js'), '--toolsets', 'all', '-o', 'DEFAULT_TARGET_ORG', '--no-telemetry', '--allow-non-ga-tools'] + }), + }); + + const tools = await mcpClient.tools(); + + try { + const result = streamText({ + model, + tools, + system: SYSTEM_PROMPT, + prompt: input, + maxRetries: 1, + maxSteps: 10, + experimental_telemetry: { + isEnabled: false, + }, + onError: (error) => { + // eslint-disable-next-line no-console + console.error(error); + }, + }); + + // TODO: we don't need text streaming here, maybe switch to `generateText`? + // eslint-disable-next-line + for await (const _ of result.fullStream) { + } + + return await result.text; + } catch (error) { + // eslint-disable-next-line no-console + console.error(error); + throw error; + } finally { + await mcpClient.close(); + } + }; +} + +export function outputIncludesExpectationArray(opts: {input: string; output: string; expected: string[]}) { + let score: number = 0; + const increment: number = 1/opts.expected.length; + for (const expected of opts.expected) { + if (opts.output.toLowerCase().includes(expected.toLowerCase())) { + score += increment; + } + } + return { + score + } +} + +/** + * A Factuality checker utilizing the `ai` SDK based on the implementation in `autoevals`. + * + * ``` + * import { openai } from "@ai-sdk/openai"; + * + * scorers: [Factuality(openai("gpt-4o"))] + * ``` + */ +export function Factuality(model: LanguageModel = defaultModel) { + // TODO: remove function wrapper + // eslint-disable-next-line @typescript-eslint/no-shadow + return async function Factuality(opts: { input: string; output: string; expected?: string }) { + const { object } = await generateObject({ + model, + /** + * Prompt implementation from `autoevals`: + * + * {@link https://github.com/braintrustdata/autoevals/blob/5aa20a0a9eb8fc9e07e9e5722ebf71c68d082f32/templates/factuality.yaml} + */ + prompt: ` + You are comparing a submitted answer to an expert answer on a given question. Here is the data: + [BEGIN DATA] + ************ + [Question]: ${opts.input} + ************ + [Expert]: ${opts.expected} + ************ + [Submission]: ${opts.output} + ************ + [END DATA] + Compare the factual content of the submitted answer with the expert answer. Ignore any differences in style, grammar, or punctuation, or overall structure. + The submitted answer may either be a subset or superset of the expert answer, or it may conflict with it. Determine which case applies. Answer the question by selecting one of the following options: + + (A) The submitted answer is a subset of the expert answer and is fully consistent with it. + (B) The submitted answer is a superset of the expert answer and is fully consistent with it. + (C) The submitted answer contains all the same details as the expert answer. + (D) There is a disagreement between the submitted answer and the expert answer. + (E) The answers differ, but these differences don't matter from the perspective of factuality. + `, + schema: z.object({ + answer: z.enum(['A', 'B', 'C', 'D', 'E']).describe('Your selection.'), + rationale: z.string().describe('Why you chose this answer. Be very detailed.'), + }), + }); + + const scores = { + A: 0.4, + B: 0.6, + C: 1, + D: 0, + E: 1, + }; + + return { + score: scores[object.answer], + metadata: { + rationale: object.rationale, + }, + }; + }; +} \ No newline at end of file diff --git a/packages/mcp/test/fixtures/sample-targets/SampleTarget1.cls b/packages/mcp/test/fixtures/sample-targets/SampleTarget1.cls new file mode 100644 index 00000000..30cec28b --- /dev/null +++ b/packages/mcp/test/fixtures/sample-targets/SampleTarget1.cls @@ -0,0 +1,9 @@ +public class SampleTarget1 { + public static boolean doSomething() { + Integer i = 0; + for (i = 0; i < 10; i++) { + Account[] accs = [SELECT Name FROM Account WITH SYSTEM_MODE]; + } + return false; + } +} \ No newline at end of file diff --git a/packages/mcp/vitest.config.ts b/packages/mcp/vitest.config.ts new file mode 100644 index 00000000..d2a22168 --- /dev/null +++ b/packages/mcp/vitest.config.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2025, Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + include: ['**/*.eval.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + reporters: ['vitest-evals/reporter'], + }, +}); \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 62c71c8a..c435649e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,57 @@ # yarn lockfile v1 +"@ai-sdk/google@^1.2.22": + version "1.2.22" + resolved "https://registry.yarnpkg.com/@ai-sdk/google/-/google-1.2.22.tgz#9993e4781c9a773cd17d47490b9efdc90895abd2" + integrity sha512-Ppxu3DIieF1G9pyQ5O1Z646GYR0gkC57YdBqXJ82qvCdhEhZHu0TWhmnOoeIWe2olSbuDeoOY+MfJrW8dzS3Hw== + dependencies: + "@ai-sdk/provider" "1.1.3" + "@ai-sdk/provider-utils" "2.2.8" + +"@ai-sdk/openai@^1.3.23": + version "1.3.24" + resolved "https://registry.yarnpkg.com/@ai-sdk/openai/-/openai-1.3.24.tgz#169b78a1ccf338e5dbd8696a55f57d3ca2e3d6bc" + integrity sha512-GYXnGJTHRTZc4gJMSmFRgEQudjqd4PUN0ZjQhPwOAYH1yOAvQoG/Ikqs+HyISRbLPCrhbZnPKCNHuRU4OfpW0Q== + dependencies: + "@ai-sdk/provider" "1.1.3" + "@ai-sdk/provider-utils" "2.2.8" + +"@ai-sdk/provider-utils@2.2.8": + version "2.2.8" + resolved "https://registry.yarnpkg.com/@ai-sdk/provider-utils/-/provider-utils-2.2.8.tgz#ad11b92d5a1763ab34ba7b5fc42494bfe08b76d1" + integrity sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA== + dependencies: + "@ai-sdk/provider" "1.1.3" + nanoid "^3.3.8" + secure-json-parse "^2.7.0" + +"@ai-sdk/provider@1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@ai-sdk/provider/-/provider-1.1.3.tgz#ebdda8077b8d2b3f290dcba32c45ad19b2704681" + integrity sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg== + dependencies: + json-schema "^0.4.0" + +"@ai-sdk/react@1.2.12": + version "1.2.12" + resolved "https://registry.yarnpkg.com/@ai-sdk/react/-/react-1.2.12.tgz#f4250b6df566b170af98a71d5708b52108dd0ce1" + integrity sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g== + dependencies: + "@ai-sdk/provider-utils" "2.2.8" + "@ai-sdk/ui-utils" "1.2.11" + swr "^2.2.5" + throttleit "2.1.0" + +"@ai-sdk/ui-utils@1.2.11": + version "1.2.11" + resolved "https://registry.yarnpkg.com/@ai-sdk/ui-utils/-/ui-utils-1.2.11.tgz#4f815589d08d8fef7292ade54ee5db5d09652603" + integrity sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w== + dependencies: + "@ai-sdk/provider" "1.1.3" + "@ai-sdk/provider-utils" "2.2.8" + zod-to-json-schema "^3.24.1" + "@ampproject/remapping@^2.2.0": version "2.3.0" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" @@ -2196,7 +2247,7 @@ dependencies: "@opentelemetry/api" "^1.3.0" -"@opentelemetry/api@^1.3.0", "@opentelemetry/api@^1.7.0", "@opentelemetry/api@^1.9.0": +"@opentelemetry/api@1.9.0", "@opentelemetry/api@^1.3.0", "@opentelemetry/api@^1.7.0", "@opentelemetry/api@^1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe" integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg== @@ -3854,6 +3905,11 @@ resolved "https://registry.yarnpkg.com/@types/deep-eql/-/deep-eql-4.0.2.tgz#334311971d3a07121e7eb91b684a605e7eea9cbd" integrity sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw== +"@types/diff-match-patch@^1.0.36": + version "1.0.36" + resolved "https://registry.yarnpkg.com/@types/diff-match-patch/-/diff-match-patch-1.0.36.tgz#dcef10a69d357fe9d43ac4ff2eca6b85dbf466af" + integrity sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg== + "@types/eslint@^9.6.1": version "9.6.1" resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-9.6.1.tgz#d5795ad732ce81715f27f75da913004a56751584" @@ -4432,6 +4488,18 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" +ai@^4.3.17: + version "4.3.19" + resolved "https://registry.yarnpkg.com/ai/-/ai-4.3.19.tgz#e94f5b37f3885bc9c9637f892e13bddd0a1857e5" + integrity sha512-dIE2bfNpqHN3r6IINp9znguYdhIOheKW2LDigAMrgt/upT3B8eBGPSCblENvaZGoq+hxaN9fSMzjWpbqloP+7Q== + dependencies: + "@ai-sdk/provider" "1.1.3" + "@ai-sdk/provider-utils" "2.2.8" + "@ai-sdk/react" "1.2.12" + "@ai-sdk/ui-utils" "1.2.11" + "@opentelemetry/api" "1.9.0" + jsondiffpatch "0.6.0" + ajv@^6.12.4, ajv@^6.12.6: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -5709,6 +5777,11 @@ depd@2.0.0, depd@^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.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + destroy@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" @@ -5741,6 +5814,11 @@ diagnostic-channel@1.1.1: dependencies: semver "^7.5.3" +diff-match-patch@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.5.tgz#abb584d5f10cd1196dfc55aa03701592ae3f7b37" + integrity sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw== + diff3@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/diff3/-/diff3-0.0.3.tgz#d4e5c3a4cdf4e5fe1211ab42e693fcb4321580fc" @@ -8045,6 +8123,11 @@ json-schema-traverse@^1.0.0: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== +json-schema@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== + json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" @@ -8077,6 +8160,15 @@ jsonc-parser@^3.0.0: resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.3.1.tgz#f2a524b4f7fd11e3d791e559977ad60b98b798b4" integrity sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ== +jsondiffpatch@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/jsondiffpatch/-/jsondiffpatch-0.6.0.tgz#daa6a25bedf0830974c81545568d5f671c82551f" + integrity sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ== + dependencies: + "@types/diff-match-patch" "^1.0.36" + chalk "^5.3.0" + diff-match-patch "^1.0.5" + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -8697,7 +8789,7 @@ mute-stream@^2.0.0: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-2.0.0.tgz#a5446fc0c512b71c83c44d908d5c7b7b4c493b2b" integrity sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA== -nanoid@^3.3.11, nanoid@^3.3.7: +nanoid@^3.3.11, nanoid@^3.3.7, nanoid@^3.3.8: version "3.3.11" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== @@ -10106,7 +10198,7 @@ scheduler@^0.23.2: dependencies: loose-envify "^1.1.0" -secure-json-parse@^2.4.0: +secure-json-parse@^2.4.0, secure-json-parse@^2.7.0: version "2.7.0" resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz#5a5f9cd6ae47df23dba3151edd06855d47e09862" integrity sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw== @@ -10793,6 +10885,14 @@ svg-tags@^1.0.0: resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" integrity sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA== +swr@^2.2.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/swr/-/swr-2.3.6.tgz#5fee0ee8a0762a16871ee371075cb09422b64f50" + integrity sha512-wfHRmHWk/isGNMwlLGlZX5Gzz/uTgo0o2IRuTMcf4CPuPFJZlq0rDaKUx+ozB5nBOReNV1kiOyzMfj+MBMikLw== + dependencies: + dequal "^2.0.3" + use-sync-external-store "^1.4.0" + table@^6.9.0: version "6.9.0" resolved "https://registry.yarnpkg.com/table/-/table-6.9.0.tgz#50040afa6264141c7566b3b81d4d82c47a8668f5" @@ -10872,6 +10972,11 @@ thread-stream@^3.0.0: dependencies: real-require "^0.2.0" +throttleit@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-2.1.0.tgz#a7e4aa0bf4845a5bd10daa39ea0c783f631a07b4" + integrity sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw== + "through@>=2.2.7 <3", through@^2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -11277,6 +11382,11 @@ use-sidecar@^1.1.3: detect-node-es "^1.1.0" tslib "^2.0.0" +use-sync-external-store@^1.4.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz#55122e2a3edd2a6c106174c27485e0fd59bcfca0" + integrity sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A== + util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -11345,6 +11455,11 @@ vite-node@3.2.4: optionalDependencies: fsevents "~2.3.3" +vitest-evals@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/vitest-evals/-/vitest-evals-0.5.0.tgz#543f3c59ad7ffe7f05437d85666836ffa1512a9f" + integrity sha512-kg8r6NKNBD6jkmyjdO64qduATW6IUG+62atCJj3dLzaRZEG4TIdfFVy64iEh0dSSo1CCpAJi+dJqSN2Y2qv2Sw== + vitest@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/vitest/-/vitest-3.2.4.tgz#0637b903ad79d1539a25bc34c0ed54b5c67702ea"