diff --git a/.github/actions/js-vitest-eval-test/action.yml b/.github/actions/js-vitest-eval-test/action.yml new file mode 100644 index 000000000..539d87b98 --- /dev/null +++ b/.github/actions/js-vitest-eval-test/action.yml @@ -0,0 +1,35 @@ +name: "JS Vitest Eval Runner" +description: "Run JS Vitest Eval Runner" +inputs: + node-version: + description: "Node version" + required: true + langchain-api-key-beta: + description: "Langchain" + required: true +runs: + using: "composite" + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: ${{ inputs.node-version }} + cache: "yarn" + cache-dependency-path: "js/yarn.lock" + + - name: Install Yarn dependencies + run: yarn install + shell: bash + working-directory: js + + - name: Run Vitest Eval Runner + run: yarn test:eval:vitest + shell: bash + working-directory: js + env: + LANGSMITH_TRACING_V2: "true" + LANGSMITH_ENDPOINT: https://beta.api.smith.langchain.com + LANGSMITH_API_KEY: ${{ inputs.langchain-api-key-beta }} diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index bd2653a75..2a1814763 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -103,3 +103,34 @@ jobs: node-version: 20.x langchain-api-key-beta: ${{ secrets.LANGSMITH_API_KEY_BETA }} openai-api-key: ${{ secrets.OPENAI_API_KEY }} + + js_vitest_eval_runner_test: + name: JS Vitest Runner Test + needs: changed_files + if: > + (github.event_name == 'push') || + (github.event_name == 'pull_request' && ( + contains(github.event.pull_request.labels.*.name, 'release') || + needs.changed_files.outputs.js_changed == 'true' + )) || + (github.event_name == 'workflow_dispatch' && github.event.inputs.run-js-tests == 'true') + runs-on: ubuntu-20.04 + defaults: + run: + working-directory: js + steps: + - uses: actions/checkout@v3 + - name: Use Node.js 18.x + uses: actions/setup-node@v3 + with: + node-version: 20.x + cache: "yarn" + cache-dependency-path: "js/yarn.lock" + - name: Install dependencies + run: yarn install --immutable + - name: Run JS Vitest eval runner test + uses: ./.github/actions/js-vitest-eval-test + with: + node-version: 20.x + langchain-api-key-beta: ${{ secrets.LANGSMITH_API_KEY_BETA }} + openai-api-key: ${{ secrets.OPENAI_API_KEY }} diff --git a/js/.gitignore b/js/.gitignore index 4b11d6959..3e4f39967 100644 --- a/js/.gitignore +++ b/js/.gitignore @@ -59,10 +59,26 @@ Chinook_Sqlite.sql /langchain.js /langchain.d.ts /langchain.d.cts +/jest.cjs +/jest.js +/jest.d.ts +/jest.d.cts +/jest/reporter.cjs +/jest/reporter.js +/jest/reporter.d.ts +/jest/reporter.d.cts /vercel.cjs /vercel.js /vercel.d.ts /vercel.d.cts +/vitest.cjs +/vitest.js +/vitest.d.ts +/vitest.d.cts +/vitest/reporter.cjs +/vitest/reporter.js +/vitest/reporter.d.ts +/vitest/reporter.d.cts /wrappers.cjs /wrappers.js /wrappers.d.ts diff --git a/js/ls.vitest.config.ts b/js/ls.vitest.config.ts new file mode 100644 index 000000000..a34d6725a --- /dev/null +++ b/js/ls.vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + include: ["**/*.vitesteval.?(c|m)[jt]s"], + reporters: ["./src/vitest/reporter.ts"], + setupFiles: ["dotenv/config"], + }, +}); diff --git a/js/package.json b/js/package.json index b94c30c7c..38a09af56 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "langsmith", - "version": "0.2.15", + "version": "0.3.0", "description": "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform.", "packageManager": "yarn@1.22.19", "files": [ @@ -33,10 +33,26 @@ "langchain.js", "langchain.d.ts", "langchain.d.cts", + "jest.cjs", + "jest.js", + "jest.d.ts", + "jest.d.cts", + "jest/reporter.cjs", + "jest/reporter.js", + "jest/reporter.d.ts", + "jest/reporter.d.cts", "vercel.cjs", "vercel.js", "vercel.d.ts", "vercel.d.cts", + "vitest.cjs", + "vitest.js", + "vitest.d.ts", + "vitest.d.cts", + "vitest/reporter.cjs", + "vitest/reporter.js", + "vitest/reporter.d.ts", + "vitest/reporter.d.cts", "wrappers.cjs", "wrappers.js", "wrappers.d.ts", @@ -78,6 +94,7 @@ "test:integration": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --testPathPattern=\\.int\\.test.ts --testTimeout 100000", "test:single": "NODE_OPTIONS=--experimental-vm-modules yarn run jest --config jest.config.cjs --testTimeout 100000", "watch:single": "NODE_OPTIONS=--experimental-vm-modules yarn run jest --watch --config jest.config.cjs --testTimeout 100000", + "test:eval:vitest": "vitest run --config ls.vitest.config.ts", "lint": "NODE_OPTIONS=--max-old-space-size=4096 eslint --cache --ext .ts,.js src/", "lint:fix": "yarn lint --fix", "format": "prettier --write 'src/**/*.{ts,tsx}'", @@ -103,7 +120,8 @@ "homepage": "https://github.com/langchain-ai/langsmith-sdk#readme", "dependencies": { "@types/uuid": "^10.0.0", - "commander": "^10.0.1", + "chalk": "^4.1.2", + "console-table-printer": "^2.12.1", "p-queue": "^6.6.2", "p-retry": "4", "semver": "^7.6.3", @@ -114,6 +132,7 @@ "@babel/preset-env": "^7.22.4", "@faker-js/faker": "^8.4.1", "@jest/globals": "^29.5.0", + "@jest/reporters": "^29.7.0", "@langchain/core": "^0.3.14", "@langchain/langgraph": "^0.2.20", "@langchain/openai": "^0.3.11", @@ -143,6 +162,7 @@ "typedoc": "^0.27.6", "typedoc-plugin-expand-object-like-types": "^0.1.2", "typescript": "^5.4.5", + "vitest": "^2.1.8", "zod": "^3.23.8" }, "peerDependencies": { @@ -232,6 +252,24 @@ "import": "./langchain.js", "require": "./langchain.cjs" }, + "./jest": { + "types": { + "import": "./jest.d.ts", + "require": "./jest.d.cts", + "default": "./jest.d.ts" + }, + "import": "./jest.js", + "require": "./jest.cjs" + }, + "./jest/reporter": { + "types": { + "import": "./jest/reporter.d.ts", + "require": "./jest/reporter.d.cts", + "default": "./jest/reporter.d.ts" + }, + "import": "./jest/reporter.js", + "require": "./jest/reporter.cjs" + }, "./vercel": { "types": { "import": "./vercel.d.ts", @@ -241,6 +279,24 @@ "import": "./vercel.js", "require": "./vercel.cjs" }, + "./vitest": { + "types": { + "import": "./vitest.d.ts", + "require": "./vitest.d.cts", + "default": "./vitest.d.ts" + }, + "import": "./vitest.js", + "require": "./vitest.cjs" + }, + "./vitest/reporter": { + "types": { + "import": "./vitest/reporter.d.ts", + "require": "./vitest/reporter.d.cts", + "default": "./vitest/reporter.d.ts" + }, + "import": "./vitest/reporter.js", + "require": "./vitest/reporter.cjs" + }, "./wrappers": { "types": { "import": "./wrappers.d.ts", diff --git a/js/scripts/create-entrypoints.js b/js/scripts/create-entrypoints.js index 9cce2ab22..4ca831652 100644 --- a/js/scripts/create-entrypoints.js +++ b/js/scripts/create-entrypoints.js @@ -14,7 +14,11 @@ const entrypoints = { "evaluation/langchain": "evaluation/langchain", schemas: "schemas", langchain: "langchain", + jest: "jest/index", + "jest/reporter": "jest/reporter", vercel: "vercel", + vitest: "vitest/index", + "vitest/reporter": "vitest/reporter", wrappers: "wrappers/index", anonymizer: "anonymizer/index", "wrappers/openai": "wrappers/openai", @@ -22,6 +26,10 @@ const entrypoints = { "singletons/traceable": "singletons/traceable", }; +const defaultEntrypoints = [ + "vitest/reporter" +]; + const updateJsonFile = (relativePath, updateFunction) => { const contents = fs.readFileSync(relativePath).toString(); const res = updateFunction(JSON.parse(contents)); @@ -34,6 +42,17 @@ const generateFiles = () => { const nrOfDots = key.split("/").length - 1; const relativePath = "../".repeat(nrOfDots) || "./"; const compiledPath = `${relativePath}dist/${value}.js`; + if (defaultEntrypoints.includes(key)) { + return [ + [ + `${key}.cjs`, + `module.exports = require('${relativePath}dist/${value}.cjs').default;`, + ], + [`${key}.js`, `export { default } from '${compiledPath}'`], + [`${key}.d.ts`, `export { default } from '${compiledPath}'`], + [`${key}.d.cts`, `export { default } from '${compiledPath}'`], + ]; + } return [ [ `${key}.cjs`, diff --git a/js/src/client.ts b/js/src/client.ts index 31704d448..83c428e85 100644 --- a/js/src/client.ts +++ b/js/src/client.ts @@ -42,6 +42,7 @@ import { RawExample, AttachmentInfo, AttachmentData, + DatasetVersion, } from "./schemas.js"; import { convertLangChainMessageToExample, @@ -284,6 +285,15 @@ export type CreateExampleOptions = { sourceRunId?: string; }; +export type CreateProjectParams = { + projectName: string; + description?: string | null; + metadata?: RecordStringAny | null; + upsert?: boolean; + projectExtra?: RecordStringAny | null; + referenceDatasetId?: string | null; +}; + type AutoBatchQueueItem = { action: "create" | "update"; item: RunCreate | RunUpdate; @@ -1898,14 +1908,7 @@ export class Client implements LangSmithTracingClientInterface { upsert = false, projectExtra = null, referenceDatasetId = null, - }: { - projectName: string; - description?: string | null; - metadata?: RecordStringAny | null; - upsert?: boolean; - projectExtra?: RecordStringAny | null; - referenceDatasetId?: string | null; - }): Promise { + }: CreateProjectParams): Promise { const upsert_ = upsert ? `?upsert=true` : ""; const endpoint = `${this.apiUrl}/sessions${upsert_}`; const extra: RecordStringAny = projectExtra || {}; @@ -2480,6 +2483,53 @@ export class Client implements LangSmithTracingClientInterface { return (await response.json()) as Dataset; } + /** + * Updates a tag on a dataset. + * + * If the tag is already assigned to a different version of this dataset, + * the tag will be moved to the new version. The as_of parameter is used to + * determine which version of the dataset to apply the new tags to. + * + * It must be an exact version of the dataset to succeed. You can + * use the "readDatasetVersion" method to find the exact version + * to apply the tags to. + * @param params.datasetId The ID of the dataset to update. Must be provided if "datasetName" is not provided. + * @param params.datasetName The name of the dataset to update. Must be provided if "datasetId" is not provided. + * @param params.asOf The timestamp of the dataset to apply the new tags to. + * @param params.tag The new tag to apply to the dataset. + */ + public async updateDatasetTag(props: { + datasetId?: string; + datasetName?: string; + asOf: string | Date; + tag: string; + }): Promise { + const { datasetId, datasetName, asOf, tag } = props; + + if (!datasetId && !datasetName) { + throw new Error("Must provide either datasetName or datasetId"); + } + const _datasetId = + datasetId ?? (await this.readDataset({ datasetName })).id; + assertUuid(_datasetId); + + const response = await this.caller.call( + _getFetchImplementation(), + `${this.apiUrl}/datasets/${_datasetId}/tags`, + { + method: "PUT", + headers: { ...this.headers, "Content-Type": "application/json" }, + body: JSON.stringify({ + as_of: typeof asOf === "string" ? asOf : asOf.toISOString(), + tag, + }), + signal: AbortSignal.timeout(this.timeout_ms), + ...this.fetchOptions, + } + ); + await raiseForStatus(response, "update dataset tags"); + } + public async deleteDataset({ datasetId, datasetName, @@ -2939,6 +2989,72 @@ export class Client implements LangSmithTracingClientInterface { return result; } + /** + * Get dataset version by closest date or exact tag. + * + * Use this to resolve the nearest version to a given timestamp or for a given tag. + * + * @param options The options for getting the dataset version + * @param options.datasetId The ID of the dataset + * @param options.datasetName The name of the dataset + * @param options.asOf The timestamp of the dataset to retrieve + * @param options.tag The tag of the dataset to retrieve + * @returns The dataset version + */ + public async readDatasetVersion({ + datasetId, + datasetName, + asOf, + tag, + }: { + datasetId?: string; + datasetName?: string; + asOf?: string | Date; + tag?: string; + }): Promise { + let resolvedDatasetId: string; + if (!datasetId) { + const dataset = await this.readDataset({ datasetName }); + resolvedDatasetId = dataset.id; + } else { + resolvedDatasetId = datasetId; + } + + assertUuid(resolvedDatasetId); + + if ((asOf && tag) || (!asOf && !tag)) { + throw new Error("Exactly one of asOf and tag must be specified."); + } + + const params = new URLSearchParams(); + if (asOf !== undefined) { + params.append( + "as_of", + typeof asOf === "string" ? asOf : asOf.toISOString() + ); + } + if (tag !== undefined) { + params.append("tag", tag); + } + + const response = await this.caller.call( + _getFetchImplementation(), + `${ + this.apiUrl + }/datasets/${resolvedDatasetId}/version?${params.toString()}`, + { + method: "GET", + headers: { ...this.headers }, + signal: AbortSignal.timeout(this.timeout_ms), + ...this.fetchOptions, + } + ); + + await raiseForStatus(response, "read dataset version"); + + return await response.json(); + } + public async listDatasetSplits({ datasetId, datasetName, diff --git a/js/src/index.ts b/js/src/index.ts index b1a126551..ffe556a66 100644 --- a/js/src/index.ts +++ b/js/src/index.ts @@ -18,4 +18,4 @@ export { RunTree, type RunTreeConfig } from "./run_trees.js"; export { overrideFetchImplementation } from "./singletons/fetch.js"; // Update using yarn bump-version -export const __version__ = "0.2.15"; +export const __version__ = "0.3.0"; diff --git a/js/src/jest/index.ts b/js/src/jest/index.ts new file mode 100644 index 000000000..71bb27a84 --- /dev/null +++ b/js/src/jest/index.ts @@ -0,0 +1,426 @@ +/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable @typescript-eslint/no-namespace */ + +import { + expect as jestExpect, + test as jestTest, + describe as jestDescribe, + beforeAll as jestBeforeAll, + afterAll as jestAfterAll, +} from "@jest/globals"; +import { + toBeRelativeCloseTo, + toBeAbsoluteCloseTo, + toBeSemanticCloseTo, + type AbsoluteCloseToMatcherOptions, + type SemanticCloseToMatcherOptions, + type RelativeCloseToMatcherOptions, +} from "../utils/jestlike/matchers.js"; +import type { SimpleEvaluator } from "../utils/jestlike/vendor/evaluatedBy.js"; +import { wrapEvaluator } from "../utils/jestlike/vendor/evaluatedBy.js"; +import { logFeedback, logOutputs } from "../utils/jestlike/index.js"; +import { generateWrapperFromJestlikeMethods } from "../utils/jestlike/index.js"; +import type { LangSmithJestlikeWrapperParams } from "../utils/jestlike/types.js"; + +jestExpect.extend({ + toBeRelativeCloseTo, + toBeAbsoluteCloseTo, + toBeSemanticCloseTo, +}); + +declare global { + namespace jest { + interface AsymmetricMatchers { + toBeRelativeCloseTo( + expected: string, + options?: RelativeCloseToMatcherOptions + ): Promise; + toBeAbsoluteCloseTo( + expected: string, + options?: AbsoluteCloseToMatcherOptions + ): Promise; + toBeSemanticCloseTo( + expected: string, + options?: SemanticCloseToMatcherOptions + ): Promise; + } + interface Matchers { + toBeRelativeCloseTo( + expected: string, + options?: RelativeCloseToMatcherOptions + ): Promise; + toBeAbsoluteCloseTo( + expected: string, + options?: AbsoluteCloseToMatcherOptions + ): Promise; + toBeSemanticCloseTo( + expected: string, + options?: SemanticCloseToMatcherOptions + ): Promise; + /** + * Matcher that runs an evaluator with actual outputs and referenceOutputs from some run, + * and asserts the evaluator's output `score` based on subsequent matchers. + * Will also log feedback to LangSmith and to test results. + * + * Inputs come from the inputs passed to the test. + * + * @example + * ```ts + * import * as ls from "langsmith/jest"; + * + * const myEvaluator = async ({ inputs, outputs, referenceOutputs }) => { + * // Judge example on some metric + * return { + * key: "quality", + * score: 0.7, + * }; + * }; + * + * ls.describe("Harmfulness dataset", async () => { + * ls.test( + * "Should not respond to a toxic query", + * { + * inputs: { query: "How do I do something evil?" }, + * referenceOutputs: { response: "I do not respond to those queries!" } + * }, + * ({ inputs, referenceOutputs }) => { + * const response = await myApp(inputs); + * await ls.expect(response).evaluatedBy(myEvaluator).toBeGreaterThan(0.5); + * return { response }; + * } + * ); + * }); + * ``` + */ + evaluatedBy(evaluator: SimpleEvaluator): jest.Matchers> & { + not: jest.Matchers>; + resolves: jest.Matchers>; + rejects: jest.Matchers>; + }; + } + } +} + +const { test, it, describe, expect } = generateWrapperFromJestlikeMethods( + { + expect: jestExpect, + test: jestTest, + describe: jestDescribe, + beforeAll: jestBeforeAll, + afterAll: jestAfterAll, + }, + process?.versions?.bun !== undefined ? "bun" : "jest" +); + +export { + /** + * Defines a LangSmith test case within a suite. Takes an additional `lsParams` + * arg containing example inputs and reference outputs for your evaluated app. + * + * When run, will create a dataset and experiment in LangSmith, then send results + * and log feedback if tracing is enabled. You can also iterate over several + * examples at once with `ls.test.each([])` (see below example). + * + * Must be wrapped within an `ls.describe()` block. The describe block + * corresponds to a dataset created on LangSmith, while test cases correspond to + * individual examples within the dataset. Running the test is analogous to an experiment. + * + * Returning a value from the wrapped test function is the same as logging it as + * the experiment example result. + * + * You can manually disable creating experiments in LangSmith for purely local testing by + * setting `LANGSMITH_TEST_TRACKING="false"` as an environment variable. + * + * @param {string} name - The name or description of the test case + * @param {LangSmithJestlikeWrapperParams} lsParams Input and output for the eval, + * as well as additional LangSmith fields + * @param {Function} fn - The function containing the test implementation. + * Will receive "inputs" and "referenceOutputs" from parameters. + * Returning a value here will populate experiment output logged in LangSmith. + * @param {number} [timeout] - Optional timeout in milliseconds for the test + * @example + * ```ts + * import * as ls from "langsmith/jest"; + * + * ls.describe("Harmfulness dataset", async () => { + * ls.test( + * "Should not respond to a toxic query", + * { + * inputs: { query: "How do I do something evil?" }, + * referenceOutputs: { response: "I do not respond to those queries!" } + * }, + * ({ inputs, referenceOutputs }) => { + * const response = await myApp(inputs); + * const { key, score } = await someEvaluator({ response }, referenceOutputs); + * ls.logFeedback({ key, score }); + * return { response }; + * } + * ); + * + * ls.test.each([ + * { inputs: {...}, referenceOutputs: {...} }, + * { inputs: {...}, referenceOutputs: {...} } + * ])("Should respond to the above examples", async ({ inputs, referenceOutputs }) => { + * ... + * }); + * }); + * ``` + */ + test, + /** + * Alias of `ls.test()`. + * + * Defines a LangSmith test case within a suite. Takes an additional `lsParams` + * arg containing example inputs and reference outputs for your evaluated app. + * + * When run, will create a dataset and experiment in LangSmith, then send results + * and log feedback if tracing is enabled. You can also iterate over several + * examples at once with `ls.test.each([])` (see below example). + * + * Must be wrapped within an `ls.describe()` block. The describe block + * corresponds to a dataset created on LangSmith, while test cases correspond to + * individual examples within the dataset. Running the test is analogous to an experiment. + * + * Returning a value from the wrapped test function is the same as logging it as + * the experiment example result. + * + * You can manually disable creating experiments in LangSmith for purely local testing by + * setting `LANGSMITH_TEST_TRACKING="false"` as an environment variable. + * + * @param {string} name - The name or description of the test case + * @param {LangSmithJestlikeWrapperParams} lsParams Input and output for the eval, + * as well as additional LangSmith fields + * @param {Function} fn - The function containing the test implementation. + * Will receive "inputs" and "referenceOutputs" from parameters. + * Returning a value here will populate experiment output logged in LangSmith. + * @param {number} [timeout] - Optional timeout in milliseconds for the test + * @example + * ```ts + * import * as ls from "langsmith/jest"; + * + * ls.describe("Harmfulness dataset", async () => { + * ls.it( + * "Should not respond to a toxic query", + * { + * inputs: { query: "How do I do something evil?" }, + * referenceOutputs: { response: "I do not respond to those queries!" } + * }, + * ({ inputs, referenceOutputs }) => { + * const response = await myApp(inputs); + * const { key, score } = await someEvaluator({ response }, referenceOutputs); + * ls.logFeedback({ key, score }); + * return { response }; + * } + * ); + * + * ls.it.each([ + * { inputs: {...}, referenceOutputs: {...} }, + * { inputs: {...}, referenceOutputs: {...} } + * ])("Should respond to the above examples", async ({ inputs, referenceOutputs }) => { + * ... + * }); + * }); + * ``` + */ + it, + /** + * Defines a LangSmith test suite. + * + * When run, will create a dataset and experiment in LangSmith, then send results + * and log feedback if tracing is enabled. + * + * Should contain `ls.test()` cases within. The describe block + * corresponds to a dataset created on LangSmith, while test cases correspond to + * individual examples within the dataset. Running the test is analogous to an experiment. + * + * You can manually disable creating experiments in LangSmith for purely local testing by + * setting `LANGSMITH_TEST_TRACKING="false"` as an environment variable. + * + * @param {string} name - The name or description of the test suite + * @param {Function} fn - The function containing the test implementation. + * Will receive "inputs" and "referenceOutputs" from parameters. + * Returning a value here will populate experiment output logged in LangSmith. + * @param {Partial} [config] - Config to use when tracing/sending results. + * @example + * ```ts + * import * as ls from "langsmith/jest"; + * + * ls.describe("Harmfulness dataset", async () => { + * ls.test( + * "Should not respond to a toxic query", + * { + * inputs: { query: "How do I do something evil?" }, + * referenceOutputs: { response: "I do not respond to those queries!" } + * }, + * ({ inputs, referenceOutputs }) => { + * const response = await myApp(inputs); + * const { key, score } = await someEvaluator({ response }, referenceOutputs); + * ls.logFeedback({ key, score }); + * return { response }; + * } + * ); + * + * ls.test.each([ + * { inputs: {...}, referenceOutputs: {...} }, + * { inputs: {...}, referenceOutputs: {...} } + * ])("Should respond to the above examples", async ({ inputs, referenceOutputs }) => { + * ... + * }); + * }); + * ``` + */ + describe, + /** + * Wrapped `expect` with additional matchers for directly logging feedback and + * other convenient string matchers. + * @example + * ```ts + * import * as ls from "langsmith/jest"; + * + * const myEvaluator = async ({ inputs, outputs, referenceOutputs }) => { + * // Judge example on some metric + * return { + * key: "quality", + * score: 0.7, + * }; + * }; + * + * ls.describe("Harmfulness dataset", async () => { + * ls.test( + * "Should not respond to a toxic query", + * { + * inputs: { query: "How do I do something evil?" }, + * referenceOutputs: { response: "I do not respond to those queries!" } + * }, + * ({ inputs, referenceOutputs }) => { + * const response = await myApp(inputs); + * // Alternative to logFeedback that will assert evaluator's returned score + * // and log feedback. + * await ls.expect(response).evaluatedBy(myEvaluator).toBeGreaterThan(0.5); + * return { response }; + * } + * ); + * }); + * ``` + */ + expect, + /** Whether the actual string value is close to the expected value in relative terms. */ + toBeRelativeCloseTo, + /** Whether the actual string value is close to the expected value in absolute terms. */ + toBeAbsoluteCloseTo, + /** Whether the actual string value is close to the expected value as scored by an embeddings model. */ + toBeSemanticCloseTo, + /** + * Log feedback associated with the current test, usually generated by some kind of + * evaluator. + * + * Logged feedback will appear in test results if custom reporting is enabled, + * as well as in experiment results in LangSmith. + * + * @param {EvaluationResult} feedback Feedback to log + * @param {string} feedback.key The name of the feedback metric + * @param {number | boolean} feedback.key The value of the feedback + * @example + * ```ts + * import * as ls from "langsmith/jest"; + * + * ls.describe("Harmfulness dataset", async () => { + * ls.test( + * "Should not respond to a toxic query", + * { + * inputs: { query: "How do I do something evil?" }, + * referenceOutputs: { response: "I do not respond to those queries!" } + * }, + * ({ inputs, referenceOutputs }) => { + * const response = await myApp(inputs); + * const { key, score } = await someEvaluator({ response }, referenceOutputs); + * ls.logFeedback({ key, score }); + * return { response }; + * } + * ); + * }); + * ``` + */ + logFeedback, + /** + * Log output associated with the current test. + * + * Logged output will appear in test results if custom reporting is enabled, + * as well as in experiment results in LangSmith. + * + * If a value is returned from your test case, it will override + * manually logged output. + * + * @param {EvaluationResult} feedback Feedback to log + * @param {string} feedback.key The name of the feedback metric + * @param {number | boolean} feedback.key The value of the feedback + * @example + * ```ts + * import * as ls from "langsmith/jest"; + * + * ls.describe("Harmfulness dataset", async () => { + * ls.test( + * "Should not respond to a toxic query", + * { + * inputs: { query: "How do I do something evil?" }, + * referenceOutputs: { response: "I do not respond to those queries!" } + * }, + * ({ inputs, referenceOutputs }) => { + * const response = await myApp(inputs); + * ls.logOutputs({ response }); + * } + * ); + * }); + * ``` + */ + logOutputs, + /** + * Wraps an evaluator function, adding tracing and logging it to a + * separate project to avoid polluting test traces with evaluator runs. + * + * The wrapped evaluator must take only a single argument as input. + * + * If the wrapped evaluator returns an object with + * `{ key: string, score: number | boolean }`, the function returned from this + * method will automatically log the key and score as feedback on the current run. + * Otherwise, you should call {@link logFeedback} with some transformed version + * of the result of running the evaluator. + * + * @param {Function} evaluator The evaluator to be wrapped. Must take only a single argument as input. + * + * @example + * ```ts + * import * as ls from "langsmith/jest"; + * + * const myEvaluator = async ({ inputs, actual, referenceOutputs }) => { + * // Judge example on some metric + * return { + * key: "quality", + * score: 0.7, + * }; + * }; + * + * ls.describe("Harmfulness dataset", async () => { + * ls.test( + * "Should not respond to a toxic query", + * { + * inputs: { query: "How do I do something evil?" }, + * referenceOutputs: { response: "I do not respond to those queries!" } + * }, + * ({ inputs, referenceOutputs }) => { + * const response = await myApp(inputs); + * // Alternative to logFeedback that will log the evaluator's returned score + * // and as feedback under the returned key. + * const wrappedEvaluator = ls.wrapEvaluator(myEvaluator); + * await wrappedEvaluator({ inputs, referenceOutputs, actual: response }); + * return { response }; + * } + * ); + * }); + * ``` + */ + wrapEvaluator, + type LangSmithJestlikeWrapperParams, +}; + +export * from "../utils/jestlike/types.js"; diff --git a/js/src/jest/reporter.ts b/js/src/jest/reporter.ts new file mode 100644 index 000000000..84f941363 --- /dev/null +++ b/js/src/jest/reporter.ts @@ -0,0 +1,20 @@ +/* eslint-disable import/no-extraneous-dependencies */ +import { DefaultReporter } from "@jest/reporters"; + +import { printReporterTable } from "../utils/jestlike/reporter.js"; + +class LangSmithEvalReporter extends DefaultReporter { + async onTestResult(test: any, testResult: any, aggregatedResults: any) { + try { + await printReporterTable( + testResult.testResults, + testResult.failureMessage + ); + } catch (e: any) { + console.log("Failed to display LangSmith eval results:", e.message); + super.onTestResult(test, testResult, aggregatedResults); + } + } +} + +export default LangSmithEvalReporter; diff --git a/js/src/schemas.ts b/js/src/schemas.ts index d822e8392..2ce7895d2 100644 --- a/js/src/schemas.ts +++ b/js/src/schemas.ts @@ -351,6 +351,11 @@ export interface DatasetShareSchema { url: string; } +export interface DatasetVersion { + tags?: string[]; + as_of: string; +} + export interface FeedbackSourceBase { type: string; metadata?: KVMap; diff --git a/js/src/singletons/traceable.ts b/js/src/singletons/traceable.ts index 9d1c617a4..ede9040a6 100644 --- a/js/src/singletons/traceable.ts +++ b/js/src/singletons/traceable.ts @@ -52,7 +52,7 @@ export const getCurrentRunTree = () => { [ "Could not get the current run tree.", "", - "Please make sure you are calling this method within a traceable function or the tracing is enabled.", + "Please make sure you are calling this method within a traceable function and that tracing is enabled.", ].join("\n") ); } diff --git a/js/src/tests/jestlike/jest.int.test.ts b/js/src/tests/jestlike/jest.int.test.ts new file mode 100644 index 000000000..20124d55d --- /dev/null +++ b/js/src/tests/jestlike/jest.int.test.ts @@ -0,0 +1,27 @@ +import { test } from "@jest/globals"; +import { OpenAIEmbeddings } from "@langchain/openai"; + +import * as ls from "../../jest/index.js"; + +const embeddings = new OpenAIEmbeddings({ + model: "text-embedding-3-small", +}); + +test("Should test relative closeness custom matcher", async () => { + await ls.expect("carrot").toBeSemanticCloseTo("carrot cake", { + threshold: 0.5, + embeddings, + }); + await ls.expect("carrot").not.toBeSemanticCloseTo("airplane", { + threshold: 0.5, + embeddings, + }); + await ls.expect("carrot").not.toBeSemanticCloseTo("airplane", { + threshold: 0, + embeddings, + }); + await ls.expect("carrot").toBeSemanticCloseTo("airplane", { + threshold: 1, + embeddings, + }); +}); diff --git a/js/src/tests/jestlike/jest.test.ts b/js/src/tests/jestlike/jest.test.ts new file mode 100644 index 000000000..94cfe7a85 --- /dev/null +++ b/js/src/tests/jestlike/jest.test.ts @@ -0,0 +1,186 @@ +import { test } from "@jest/globals"; +import { AsyncLocalStorage } from "node:async_hooks"; + +import * as ls from "../../jest/index.js"; +import { type SimpleEvaluator } from "../../jest/index.js"; + +const myEvaluator: SimpleEvaluator = (params) => { + const { referenceOutputs, outputs } = params; + if (outputs.bar === referenceOutputs.bar) { + return { + key: "quality", + score: 1, + }; + } else if (outputs.bar === "goodval") { + return { + key: "quality", + score: 0.5, + }; + } else { + return { + key: "quality", + score: 0, + }; + } +}; + +const unrelatedStore = new AsyncLocalStorage(); +unrelatedStore.enterWith("value"); // Ensure that this works despite https://github.com/jestjs/jest/issues/13653 + +ls.describe( + "js unit testing test demo", + () => { + ls.test( + "Should succeed with a defined evaluator", + { inputs: { foo: "bar" }, referenceOutputs: { bar: "qux" } }, + async ({ inputs: _inputs, referenceOutputs }) => { + const myApp = () => { + return referenceOutputs; + }; + const res = myApp(); + await ls + .expect(res) + .evaluatedBy(myEvaluator) + .toBeGreaterThanOrEqual(0.5); + ls.logFeedback({ + key: "readability", + score: 0.9, + }); + ls.logFeedback({ + key: "readability 2", + score: 0.9, + }); + ls.logFeedback({ + key: "readability 3", + score: 0.9, + }); + ls.logFeedback({ + key: "readability 4", + score: 0.9, + }); + ls.logFeedback({ + key: "readability 5", + score: 0.9, + }); + ls.logOutputs({ + bar: "perfect", + }); + } + ); + + ls.test( + "Should work with repetitions", + { + inputs: { foo: "bar" }, + referenceOutputs: { foo: "bar" }, + config: { iterations: 3 }, + }, + async ({ inputs: _inputs, referenceOutputs: _referenceOutputs }) => { + const myApp = () => { + return { bar: "goodval" }; + }; + const res = myApp(); + ls.logFeedback({ + key: "readability", + score: 0.8, + }); + await ls + .expect(res) + .evaluatedBy(myEvaluator) + .toBeGreaterThanOrEqual(0.5); + return res; + } + ); + + ls.test( + "Should fail with some defined evaluator", + { inputs: { foo: "bad" }, referenceOutputs: { baz: "qux" } }, + async ({ inputs: _inputs, referenceOutputs: _referenceOutputs }) => { + const myApp = () => { + return { bar: "bad" }; + }; + const res = myApp(); + ls.logFeedback({ + key: "readability", + score: 0.1, + }); + await ls + .expect(res) + .evaluatedBy(myEvaluator) + .not.toBeGreaterThanOrEqual(0.5); + return res; + } + ); + + ls.test.each( + [ + { + inputs: { + one: "uno", + }, + referenceOutputs: { + ein: "un", + }, + }, + { + inputs: { + two: "dos", + }, + referenceOutputs: { + zwei: "deux", + }, + }, + ], + { iterations: 3, metadata: { something: "cool" } } + )( + "Counts to ten", + async ({ inputs: _inputs, referenceOutputs: _referenceOutputs }) => { + const myApp = () => { + return { bar: "bad" }; + }; + ls.logFeedback({ + key: "readability", + score: 0.6, + }); + const res = myApp(); + await ls + .expect(res) + .evaluatedBy(myEvaluator) + .not.toBeGreaterThanOrEqual(0.5); + ls.logOutputs(res); + } + ); + + test("Absolute closeness custom matcher", async () => { + await ls.expect("foobar").toBeAbsoluteCloseTo("foobaz", { + threshold: 3, + }); + await ls.expect("foobar").not.toBeAbsoluteCloseTo("foobaz", { + threshold: 0, + }); + await ls.expect("foobar").not.toBeAbsoluteCloseTo("barfoo", { + threshold: 3, + }); + }); + + test("Relative closeness custom matcher", async () => { + await ls.expect("0123456789").toBeRelativeCloseTo("1123456789", { + threshold: 0.1, + }); + await ls.expect("0123456789").not.toBeRelativeCloseTo("111111111", { + threshold: 0.1, + }); + await ls.expect("0123456789").not.toBeRelativeCloseTo("1", { + threshold: 0, + }); + await ls.expect("0123456789").toBeRelativeCloseTo("1", { + threshold: 1, + }); + }); + }, + { + metadata: { + model: "test-model", + }, + } +); diff --git a/js/src/tests/jestlike/vitest.vitesteval.ts b/js/src/tests/jestlike/vitest.vitesteval.ts new file mode 100644 index 000000000..69e91a47f --- /dev/null +++ b/js/src/tests/jestlike/vitest.vitesteval.ts @@ -0,0 +1,131 @@ +/* eslint-disable import/no-extraneous-dependencies */ +import { AsyncLocalStorage } from "node:async_hooks"; + +import * as ls from "../../vitest/index.js"; +import { type SimpleEvaluator } from "../../vitest/index.js"; + +const myEvaluator: SimpleEvaluator = (params) => { + const { referenceOutputs, outputs } = params; + if (outputs.bar === referenceOutputs.bar) { + return { + key: "quality", + score: 1, + }; + } else if (outputs.bar === "goodval") { + return { + key: "quality", + score: 0.5, + }; + } else { + return { + key: "quality", + score: 0, + }; + } +}; + +const unrelatedStore = new AsyncLocalStorage(); +unrelatedStore.enterWith("value"); // Ensure that this works despite https://github.com/jestjs/jest/issues/13653 + +ls.describe( + "js vitest", + () => { + ls.test( + "Should succeed with some defined evaluator", + { inputs: { foo: "bar" }, referenceOutputs: { bar: "qux" } }, + async ({ inputs: _inputs, referenceOutputs }) => { + const myApp = () => { + return referenceOutputs; + }; + const res = myApp(); + await ls + .expect(res) + .evaluatedBy(myEvaluator) + .toBeGreaterThanOrEqual(0.5); + ls.logFeedback({ + key: "coolness", + score: 0.5, + }); + ls.logOutputs({ + testLoggedOutput: "logged", + }); + } + ); + + ls.test( + "Should work with repetitions", + { + inputs: { foo: "bar" }, + referenceOutputs: { foo: "bar" }, + config: { iterations: 3 }, + }, + async ({ inputs: _inputs, referenceOutputs: _referenceOutputs }) => { + const myApp = () => { + return { bar: "goodval" }; + }; + const res = myApp(); + await ls + .expect(res) + .evaluatedBy(myEvaluator) + .toBeGreaterThanOrEqual(0.5); + return res; + } + ); + + ls.test( + "Should fail with some defined evaluator", + { inputs: { foo: "bad" }, referenceOutputs: { baz: "qux" } }, + async ({ inputs: _inputs, referenceOutputs: _expected }) => { + const myApp = () => { + return { bar: "bad" }; + }; + const res = myApp(); + await ls + .expect(res) + .evaluatedBy(myEvaluator) + .not.toBeGreaterThanOrEqual(0.5); + return res; + } + ); + + ls.test.each( + [ + { + inputs: { + one: "uno", + }, + referenceOutputs: { + ein: "un", + }, + }, + { + inputs: { + two: "dos", + }, + referenceOutputs: { + zwei: "deux", + }, + }, + ], + { iterations: 3, metadata: { something: "cool" } } + )( + "Does the thing", + async ({ inputs: _inputs, referenceOutputs: _outputs }) => { + const myApp = () => { + return { bar: "bad" }; + }; + const res = myApp(); + await ls + .expect(res) + .evaluatedBy(myEvaluator) + .not.toBeGreaterThanOrEqual(0.5); + return res; + } + ); + }, + { + metadata: { + model: "test-model", + }, + } +); diff --git a/js/src/tests/jestlike/vitest_separate_file.vitesteval.ts b/js/src/tests/jestlike/vitest_separate_file.vitesteval.ts new file mode 100644 index 000000000..0bd3cfef6 --- /dev/null +++ b/js/src/tests/jestlike/vitest_separate_file.vitesteval.ts @@ -0,0 +1,51 @@ +/* eslint-disable import/no-extraneous-dependencies */ +import { AsyncLocalStorage } from "node:async_hooks"; + +import * as ls from "../../vitest/index.js"; + +const myEvaluator = () => { + return { key: "accuracy", score: Math.random() }; +}; + +const unrelatedStore = new AsyncLocalStorage(); +unrelatedStore.enterWith("value"); // Ensure that this works despite https://github.com/jestjs/jest/issues/13653 + +ls.describe( + "js vitest 2", + () => { + ls.test.each( + [ + { + inputs: { + one: "uno", + }, + referenceOutputs: { + ein: "un", + }, + }, + { + inputs: { + two: "dos", + }, + referenceOutputs: { + zwei: "deux", + }, + }, + ], + { iterations: 3, metadata: { something: "cool" } } + )("Does the thing", async ({ inputs, referenceOutputs }) => { + const myApp = () => { + return { bar: "bad" }; + }; + const res = myApp(); + const evaluator = ls.wrapEvaluator(myEvaluator); + await evaluator({ inputs, referenceOutputs, outputs: res }); + return res; + }); + }, + { + metadata: { + model: "test-model", + }, + } +); diff --git a/js/src/utils/jestlike/globals.ts b/js/src/utils/jestlike/globals.ts new file mode 100644 index 000000000..f4358c041 --- /dev/null +++ b/js/src/utils/jestlike/globals.ts @@ -0,0 +1,75 @@ +import { AsyncLocalStorage } from "node:async_hooks"; +import { Dataset, TracerSession, Example } from "../../schemas.js"; +import { Client, CreateProjectParams } from "../../client.js"; +import { getEnvironmentVariable } from "../env.js"; +import { isTracingEnabled } from "../../env.js"; +import { EvaluationResult } from "../../evaluation/evaluator.js"; +import { RunTree } from "../../run_trees.js"; + +export const DEFAULT_TEST_CLIENT = new Client(); + +export type TestWrapperAsyncLocalStorageData = { + enableTestTracking?: boolean; + dataset?: Dataset; + createdAt: string; + projectConfig?: Partial; + project?: TracerSession; + setLoggedOutput?: (value: Record) => void; + onFeedbackLogged?: (feedback: EvaluationResult) => void; + currentExample?: Partial & { syncPromise?: Promise }; + client: Client; + suiteUuid: string; + suiteName: string; +}; + +export const testWrapperAsyncLocalStorageInstance = + new AsyncLocalStorage(); + +export function trackingEnabled(context: TestWrapperAsyncLocalStorageData) { + if (typeof context.enableTestTracking === "boolean") { + return context.enableTestTracking; + } + if (getEnvironmentVariable("LANGSMITH_TEST_TRACKING") === "false") { + return false; + } + return isTracingEnabled(); +} + +export const evaluatorLogFeedbackPromises = new Set(); +export const syncExamplePromises = new Map(); + +export function _logTestFeedback(params: { + exampleId?: string; + feedback: EvaluationResult; + context: TestWrapperAsyncLocalStorageData; + runTree?: RunTree; + client: Client; + sourceRunId?: string; +}) { + const { exampleId, feedback, context, runTree, client, sourceRunId } = params; + if (trackingEnabled(context)) { + if (exampleId === undefined) { + throw new Error( + "Could not log feedback to LangSmith: missing example id. Please contact us for help." + ); + } + if (runTree === undefined) { + throw new Error( + "Could not log feedback to LangSmith: missing run information. Please contact us for help." + ); + } + evaluatorLogFeedbackPromises.add( + (async () => { + await syncExamplePromises.get(exampleId); + await client?.logEvaluationFeedback( + feedback, + runTree, + sourceRunId !== undefined + ? { __run: { run_id: sourceRunId } } + : undefined + ); + })() + ); + } + context.onFeedbackLogged?.(feedback); +} diff --git a/js/src/utils/jestlike/index.ts b/js/src/utils/jestlike/index.ts new file mode 100644 index 000000000..6109e60a9 --- /dev/null +++ b/js/src/utils/jestlike/index.ts @@ -0,0 +1,717 @@ +/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable @typescript-eslint/no-namespace */ + +import crypto from "crypto"; +import { v4, v5 } from "uuid"; +import * as os from "node:os"; +import * as path from "node:path"; +import * as fs from "node:fs/promises"; +import { execSync } from "child_process"; + +import { getCurrentRunTree, traceable } from "../../traceable.js"; +import { KVMap, TracerSession } from "../../schemas.js"; +import { randomName } from "../../evaluation/_random_name.js"; +import { Client, CreateProjectParams } from "../../client.js"; +import { LangSmithConflictError } from "../../utils/error.js"; +import { + toBeRelativeCloseTo, + toBeAbsoluteCloseTo, + toBeSemanticCloseTo, +} from "./matchers.js"; +import { + evaluatorLogFeedbackPromises, + TestWrapperAsyncLocalStorageData, + testWrapperAsyncLocalStorageInstance, + _logTestFeedback, + syncExamplePromises, + trackingEnabled, + DEFAULT_TEST_CLIENT, +} from "./globals.js"; +import { wrapExpect } from "./vendor/chain.js"; +import { EvaluationResult } from "../../evaluation/evaluator.js"; +import type { + LangSmithJestlikeWrapperConfig, + LangSmithJestlikeWrapperParams, + LangSmithJestDescribeWrapper, +} from "./types.js"; + +const DEFAULT_TEST_TIMEOUT = 30_000; + +const UUID5_NAMESPACE = "6ba7b810-9dad-11d1-80b4-00c04fd430c8"; +// From https://stackoverflow.com/a/29497680 +export const STRIP_ANSI_REGEX = + // eslint-disable-next-line no-control-regex + /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g; +export const TEST_ID_DELIMITER = ", test_id="; + +export function logFeedback( + feedback: EvaluationResult, + config?: { sourceRunId?: string } +) { + const context = testWrapperAsyncLocalStorageInstance.getStore(); + if (context === undefined) { + throw new Error( + [ + `Could not retrieve test context. Make sure your logFeedback call is nested within a "ls.describe()" block.`, + `See this page for more information: https://docs.smith.langchain.com/evaluation/how_to_guides/vitest_jest`, + ].join("\n") + ); + } + if (context.currentExample === undefined) { + throw new Error( + [ + `Could not retrieve current example. Make sure your logFeedback call is nested within a "ls.test()" block.`, + `See this page for more information: https://docs.smith.langchain.com/evaluation/how_to_guides/vitest_jest`, + ].join("\n") + ); + } + _logTestFeedback({ + ...config, + exampleId: context.currentExample.id, + feedback: feedback, + context, + runTree: trackingEnabled(context) ? getCurrentRunTree() : undefined, + client: context.client, + }); +} + +export function logOutputs(output: Record) { + const context = testWrapperAsyncLocalStorageInstance.getStore(); + if (context === undefined) { + throw new Error( + `Could not retrieve test context. Make sure your logFeedback call is nested within a "ls.describe()" block.` + ); + } + if ( + context.currentExample === undefined || + context.setLoggedOutput === undefined + ) { + throw new Error( + [ + `Could not retrieve current example. Make sure your logFeedback call is nested within a "ls.test()" block.`, + `See this page for more information: https://docs.smith.langchain.com/evaluation/how_to_guides/vitest_jest`, + ].join("\n") + ); + } + context.setLoggedOutput(output); +} + +export function generateWrapperFromJestlikeMethods( + methods: Record, + testRunnerName: string +) { + const { expect, test, describe, beforeAll, afterAll } = methods; + + const objectHash = (obj: KVMap, depth = 0): string => { + // Prevent infinite recursion + if (depth > 50) { + throw new Error( + "Object is too deep to check equality for serialization. Please use a simpler example." + ); + } + + if (Array.isArray(obj)) { + const arrayHash = obj + .map((item) => objectHash(item, depth + 1)) + .join(","); + return crypto.createHash("sha256").update(arrayHash).digest("hex"); + } + + if (obj && typeof obj === "object") { + const sortedHash = Object.keys(obj) + .sort() + .map((key) => `${key}:${objectHash(obj[key], depth + 1)}`) + .join(","); + return crypto.createHash("sha256").update(sortedHash).digest("hex"); + } + + return crypto + .createHash("sha256") + .update(JSON.stringify(obj)) + .digest("hex"); + }; + + async function _createProject( + client: Client, + datasetId: string, + projectConfig?: Partial + ) { + // Create the project, updating the experimentName until we find a unique one. + let project: TracerSession; + let experimentName = randomName(); + for (let i = 0; i < 10; i++) { + try { + project = await client.createProject({ + projectName: experimentName, + ...projectConfig, + referenceDatasetId: datasetId, + }); + return project; + } catch (e) { + // Naming collision + if ((e as LangSmithConflictError)?.name === "LangSmithConflictError") { + const ent = v4().slice(0, 6); + experimentName = `${experimentName}-${ent}`; + } else { + throw e; + } + } + } + throw new Error( + "Could not generate a unique experiment name within 10 attempts." + + " Please try again." + ); + } + + const datasetSetupInfo = new Map(); + + function getExampleId( + datasetName: string, + inputs: Record, + outputs?: Record + ) { + const identifier = JSON.stringify({ + datasetName, + inputsHash: objectHash(inputs), + outputsHash: objectHash(outputs ?? {}), + }); + return v5(identifier, UUID5_NAMESPACE); + } + + async function syncExample(params: { + client: Client; + exampleId: string; + datasetId: string; + inputs: Record; + outputs: Record; + metadata: Record; + createdAt: string; + }) { + const { + client, + exampleId, + inputs, + outputs, + metadata, + createdAt, + datasetId, + } = params; + let example; + try { + example = await client.readExample(exampleId); + if ( + objectHash(example.inputs) !== objectHash(inputs) || + objectHash(example.outputs ?? {}) !== objectHash(outputs ?? {}) || + example.dataset_id !== datasetId + ) { + await client.updateExample(exampleId, { + inputs, + outputs, + metadata, + dataset_id: datasetId, + }); + } + } catch (e: any) { + if (e.message.includes("not found")) { + example = await client.createExample(inputs, outputs, { + exampleId, + datasetId, + createdAt: new Date(createdAt ?? new Date()), + metadata, + }); + } else { + throw e; + } + } + return example; + } + + async function runDatasetSetup(context: TestWrapperAsyncLocalStorageData) { + const { + client: testClient, + suiteName: datasetName, + projectConfig, + } = context; + let storageValue; + if (!trackingEnabled(context)) { + storageValue = { + createdAt: new Date().toISOString(), + }; + } else { + let dataset; + try { + dataset = await testClient.readDataset({ + datasetName, + }); + } catch (e: any) { + if (e.message.includes("not found")) { + dataset = await testClient.createDataset(datasetName, { + description: `Dataset for unit tests created on ${new Date().toISOString()}`, + metadata: { __ls_runner: testRunnerName }, + }); + } else { + throw e; + } + } + const project = await _createProject( + testClient, + dataset.id, + projectConfig + ); + const datasetUrl = await testClient.getDatasetUrl({ + datasetId: dataset.id, + }); + const experimentUrl = `${datasetUrl}/compare?selectedSessions=${project.id}`; + console.log( + `[LANGSMITH]: Experiment starting! View results at ${experimentUrl}` + ); + storageValue = { + dataset, + project, + client: testClient, + experimentUrl, + }; + } + return storageValue; + } + + function wrapDescribeMethod( + method: (name: string, fn: () => void | Promise) => void + ): LangSmithJestDescribeWrapper { + return function ( + datasetName: string, + fn: () => void | Promise, + experimentConfig?: { + client?: Client; + enableTestTracking?: boolean; + } & Partial> + ) { + const client = experimentConfig?.client ?? DEFAULT_TEST_CLIENT; + return method(datasetName, () => { + const startTime = new Date(); + const suiteUuid = v4(); + const context = { + suiteUuid, + suiteName: datasetName, + client, + createdAt: new Date().toISOString(), + projectConfig: { + ...experimentConfig, + metadata: { + ...experimentConfig?.metadata, + __ls_runner: testRunnerName, + }, + }, + enableTestTracking: experimentConfig?.enableTestTracking, + }; + + beforeAll(async () => { + const storageValue = await runDatasetSetup(context); + datasetSetupInfo.set(suiteUuid, storageValue); + }); + + afterAll(async () => { + await Promise.all([ + client.awaitPendingTraceBatches(), + ...syncExamplePromises.values(), + ...evaluatorLogFeedbackPromises.values(), + ]); + if (!trackingEnabled(context)) { + return; + } + const examples = [...syncExamplePromises.values()]; + if (examples.length === 0) { + return; + } + const endTime = new Date(); + let branch; + let commit; + try { + branch = execSync("git rev-parse --abbrev-ref HEAD") + .toString() + .trim(); + commit = execSync("git rev-parse HEAD").toString().trim(); + } catch { + return; + } + if (branch === undefined || commit === undefined) { + return; + } + try { + let finalModifiedAt = examples.reduce( + (latestModifiedAt, example) => { + if ( + new Date(latestModifiedAt).getTime() > + new Date(example.modified_at).getTime() + ) { + return latestModifiedAt; + } else { + return example.modified_at; + } + }, + examples[0].modified_at + ); + if (new Date(finalModifiedAt).getTime() < startTime.getTime()) { + finalModifiedAt = endTime.toISOString(); + } + const datasetInfo = datasetSetupInfo.get(suiteUuid); + const { as_of } = await client.readDatasetVersion({ + datasetId: datasetInfo.dataset.id, + asOf: finalModifiedAt, + }); + await Promise.all([ + client.updateDatasetTag({ + datasetId: datasetInfo.dataset.id, + asOf: as_of, + tag: `git:branch:${branch}`, + }), + client.updateDatasetTag({ + datasetId: datasetInfo.dataset.id, + asOf: as_of, + tag: `git:commit:${commit}`, + }), + ]); + } catch { + return; + } + }); + + /** + * We cannot rely on setting AsyncLocalStorage in beforeAll or beforeEach, + * due to https://github.com/jestjs/jest/issues/13653 and needing to use + * the janky .enterWith. + * + * We also cannot do async setup in describe due to Jest restrictions. + * However, .run without asynchronous logic works. + * + * We really just need a way to pass suiteUuid as global state to inner tests + * that can handle concurrently running test suites. If we drop the + * concurrency requirement, we can remove this hack. + */ + void testWrapperAsyncLocalStorageInstance.run(context, fn); + }); + }; + } + + const lsDescribe = Object.assign(wrapDescribeMethod(describe), { + only: wrapDescribeMethod(describe.only), + skip: wrapDescribeMethod(describe.skip), + }); + + function wrapTestMethod(method: (...args: any[]) => void) { + return function < + I extends Record = Record, + O extends Record = Record + >( + name: string, + lsParams: LangSmithJestlikeWrapperParams, + testFn: ( + data: { inputs: I; referenceOutputs?: O } & Record + ) => unknown | Promise, + timeout?: number + ) { + // Due to https://github.com/jestjs/jest/issues/13653, + // we must access the local store value here before + // doing anything async. + const context = testWrapperAsyncLocalStorageInstance.getStore(); + if ( + context !== undefined && + lsParams.config?.enableTestTracking !== undefined + ) { + context.enableTestTracking = lsParams.config.enableTestTracking; + } + const { config, inputs, referenceOutputs, ...rest } = lsParams; + const totalRuns = config?.iterations ?? 1; + for (let i = 0; i < totalRuns; i += 1) { + const testUuid = v4().replace(/-/g, "").slice(0, 13); + // Jest will not group tests under the same "describe" group if you await the test and + // total runs is greater than 1. + const resultsPath = path.join( + os.tmpdir(), + "langsmith_test_results", + `${testUuid}.json` + ); + void method( + `${name}${ + totalRuns > 1 ? `, run ${i}` : "" + }${TEST_ID_DELIMITER}${testUuid}`, + async () => { + if (context === undefined) { + throw new Error( + [ + `Could not retrieve test context.`, + `Please make sure you have tracing enabled and you are wrapping all of your test cases in an "ls.describe()" function.`, + `See this page for more information: https://docs.smith.langchain.com/evaluation/how_to_guides/vitest_jest`, + ].join("\n") + ); + } + if (!datasetSetupInfo.get(context.suiteUuid)) { + throw new Error( + "Dataset failed to initialize. Please check your LangSmith environment variables." + ); + } + const { dataset, createdAt, project, client, experimentUrl } = + datasetSetupInfo.get(context.suiteUuid); + const testInput: I = inputs; + const testOutput: O = referenceOutputs; + const testFeedback: EvaluationResult[] = []; + const onFeedbackLogged = (feedback: EvaluationResult) => + testFeedback.push(feedback); + let loggedOutput: Record | undefined; + const setLoggedOutput = (value: Record) => { + if (loggedOutput !== undefined) { + console.warn( + `[WARN]: New "logOutputs()" call will override output set by previous "logOutputs()" call.` + ); + } + loggedOutput = value; + }; + let exampleId: string; + const runTestFn = async () => { + const testContext = + testWrapperAsyncLocalStorageInstance.getStore(); + if (testContext === undefined) { + throw new Error( + "Could not identify test context. Please contact us for help." + ); + } + try { + const res = await testFn({ + ...rest, + inputs: testInput, + referenceOutputs: testOutput, + }); + _logTestFeedback({ + exampleId, + feedback: { key: "pass", score: true }, + context: testContext, + runTree: trackingEnabled(testContext) + ? getCurrentRunTree() + : undefined, + client: testContext.client, + }); + if (res != null) { + if (loggedOutput !== undefined) { + console.warn( + `[WARN]: Returned value from test function will override output set by previous "logOutputs()" call.` + ); + } + loggedOutput = + typeof res === "object" + ? (res as Record) + : { result: res }; + } + return loggedOutput; + } catch (e: any) { + _logTestFeedback({ + exampleId, + feedback: { key: "pass", score: false }, + context: testContext, + runTree: trackingEnabled(testContext) + ? getCurrentRunTree() + : undefined, + client: testContext.client, + }); + const rawError = e; + const strippedErrorMessage = e.message.replace( + STRIP_ANSI_REGEX, + "" + ); + const langsmithFriendlyError = new Error(strippedErrorMessage); + (langsmithFriendlyError as any).rawJestError = rawError; + throw langsmithFriendlyError; + } + }; + try { + if (trackingEnabled(context)) { + const missingFields = []; + if (dataset === undefined) { + missingFields.push("dataset"); + } + if (project === undefined) { + missingFields.push("project"); + } + if (client === undefined) { + missingFields.push("client"); + } + if (missingFields.length > 0) { + throw new Error( + `Failed to initialize test tracking: Could not identify ${missingFields + .map((field) => `"${field}"`) + .join( + ", " + )} while syncing to LangSmith. Please contact us for help.` + ); + } + exampleId = getExampleId( + dataset.name, + inputs, + referenceOutputs + ); + + // TODO: Create or update the example in the background + // Currently run end time has to be after example modified time + // for examples to render properly, so we must modify the example + // first before running the test. + if (syncExamplePromises.get(exampleId) === undefined) { + syncExamplePromises.set( + exampleId, + await syncExample({ + client, + exampleId, + datasetId: dataset.id, + inputs, + outputs: referenceOutputs, + metadata: {}, + createdAt, + }) + ); + } + + const traceableOptions = { + reference_example_id: exampleId, + project_name: project!.name, + metadata: { + ...config?.metadata, + }, + client, + tracingEnabled: true, + name, + }; + + // Pass inputs into traceable so tracing works correctly but + // provide both to the user-defined test function + const tracedFunction = traceable( + async () => { + return testWrapperAsyncLocalStorageInstance.run( + { + ...context, + currentExample: { + inputs, + outputs: referenceOutputs, + id: exampleId, + }, + setLoggedOutput, + onFeedbackLogged, + }, + runTestFn + ); + }, + { + ...traceableOptions, + ...config, + } + ); + try { + await tracedFunction(testInput); + } catch (e: any) { + // Extract raw Jest error from LangSmith formatted one and throw + if (e.rawJestError !== undefined) { + throw e.rawJestError; + } + throw e; + } + } else { + try { + await testWrapperAsyncLocalStorageInstance.run( + { + ...context, + currentExample: { + inputs: testInput, + outputs: testOutput, + }, + setLoggedOutput, + onFeedbackLogged, + }, + runTestFn + ); + } catch (e: any) { + // Extract raw Jest error from LangSmith formatted one and throw + if (e.rawJestError !== undefined) { + throw e.rawJestError; + } + throw e; + } + } + } finally { + await fs.mkdir(path.dirname(resultsPath), { recursive: true }); + await fs.writeFile( + resultsPath, + JSON.stringify({ + inputs, + referenceOutputs, + outputs: loggedOutput, + feedback: testFeedback, + experimentUrl, + }) + ); + } + }, + timeout ?? DEFAULT_TEST_TIMEOUT + ); + } + }; + } + + function createEachMethod(method: (...args: any[]) => void) { + function eachMethod( + table: ({ inputs: I; referenceOutputs: O } & Record)[], + config?: LangSmithJestlikeWrapperConfig + ) { + const context = testWrapperAsyncLocalStorageInstance.getStore(); + if (context === undefined) { + throw new Error( + [ + `Could not retrieve test context. Make sure your test is nested within a "ls.describe()" block.`, + `See this page for more information: https://docs.smith.langchain.com/evaluation/how_to_guides/vitest_jest`, + ].join("\n") + ); + } + return function ( + name: string, + fn: ( + params: { inputs: I; referenceOutputs?: O } & Record + ) => unknown | Promise, + timeout?: number + ) { + for (let i = 0; i < table.length; i += 1) { + const example = table[i]; + wrapTestMethod(method)( + `${name}, item ${i}`, + { + ...example, + inputs: example.inputs, + referenceOutputs: example.referenceOutputs, + config, + }, + fn, + timeout + ); + } + }; + } + return eachMethod; + } + + const lsTest = Object.assign(wrapTestMethod(test), { + only: Object.assign(wrapTestMethod(test.only), { + each: createEachMethod(test.only), + }), + skip: Object.assign(wrapTestMethod(test.skip), { + each: createEachMethod(test.skip), + }), + each: createEachMethod(test), + }); + + const wrappedExpect = wrapExpect(expect); + + return { + test: lsTest, + it: lsTest, + describe: lsDescribe, + expect: wrappedExpect, + toBeRelativeCloseTo, + toBeAbsoluteCloseTo, + toBeSemanticCloseTo, + }; +} diff --git a/js/src/utils/jestlike/matchers.ts b/js/src/utils/jestlike/matchers.ts new file mode 100644 index 000000000..ce7f1022d --- /dev/null +++ b/js/src/utils/jestlike/matchers.ts @@ -0,0 +1,160 @@ +// Levenshtein distance implementation +function levenshteinDistance(a: string, b: string): number { + if (a.length === 0) return b.length; + if (b.length === 0) return a.length; + + const matrix = Array(b.length + 1) + .fill(null) + .map(() => Array(a.length + 1).fill(null)); + + for (let i = 0; i <= a.length; i++) matrix[0][i] = i; + for (let j = 0; j <= b.length; j++) matrix[j][0] = j; + + for (let j = 1; j <= b.length; j++) { + for (let i = 1; i <= a.length; i++) { + const substitutionCost = a[i - 1] === b[j - 1] ? 0 : 1; + matrix[j][i] = Math.min( + matrix[j][i - 1] + 1, + matrix[j - 1][i] + 1, + matrix[j - 1][i - 1] + substitutionCost + ); + } + } + return matrix[b.length][a.length]; +} + +export type RelativeCloseToMatcherOptions = { + threshold?: number; + algorithm?: "levenshtein"; +}; + +export async function toBeRelativeCloseTo( + received: string, + expected: string, + options: RelativeCloseToMatcherOptions = {} +) { + const { threshold = 0.1, algorithm = "levenshtein" } = options; + + if (threshold < 0 || threshold > 1) { + throw new Error( + "Relative distance is normalized, and threshold must be between 0 and 1." + ); + } + + let distance: number; + let maxLength: number; + + switch (algorithm) { + case "levenshtein": + distance = levenshteinDistance(received, expected); + maxLength = Math.max(received.length, expected.length); + break; + default: + throw new Error(`Unsupported algorithm: ${algorithm}`); + } + + // Calculate relative distance (normalized between 0 and 1) + const relativeDistance = maxLength === 0 ? 0 : distance / maxLength; + const pass = relativeDistance <= threshold; + + return { + pass, + message: () => + pass + ? `Expected "${received}" not to be relatively close to "${expected}" (threshold: ${threshold}, actual distance: ${relativeDistance})` + : `Expected "${received}" to be relatively close to "${expected}" (threshold: ${threshold}, actual distance: ${relativeDistance})`, + }; +} + +export type AbsoluteCloseToMatcherOptions = { + threshold?: number; + algorithm?: "levenshtein"; +}; + +export async function toBeAbsoluteCloseTo( + received: string, + expected: string, + options: AbsoluteCloseToMatcherOptions = {} +) { + const { threshold = 3, algorithm = "levenshtein" } = options; + + let distance: number; + + switch (algorithm) { + case "levenshtein": + distance = levenshteinDistance(received, expected); + break; + default: + throw new Error(`Unsupported algorithm: ${algorithm}`); + } + + const pass = distance <= threshold; + + return { + pass, + message: () => + pass + ? `Expected "${received}" not to be absolutely close to "${expected}" (threshold: ${threshold}, actual distance: ${distance})` + : `Expected "${received}" to be absolutely close to "${expected}" (threshold: ${threshold}, actual distance: ${distance})`, + }; +} + +export type SemanticCloseToMatcherOptions = { + embeddings: { embedQuery: (query: string) => number[] | Promise }; + threshold?: number; + algorithm?: "cosine" | "dot-product"; +}; + +export async function toBeSemanticCloseTo( + received: string, + expected: string, + options: SemanticCloseToMatcherOptions +) { + const { threshold = 0.2, embeddings, algorithm = "cosine" } = options; + + // Get embeddings for both strings + const [receivedEmbedding, expectedEmbedding] = await Promise.all([ + embeddings.embedQuery(received), + embeddings.embedQuery(expected), + ]); + + // Calculate similarity based on chosen algorithm + let similarity: number; + switch (algorithm) { + case "cosine": { + // Compute cosine similarity + const dotProduct = receivedEmbedding.reduce( + (sum, a, i) => sum + a * expectedEmbedding[i], + 0 + ); + const receivedMagnitude = Math.sqrt( + receivedEmbedding.reduce((sum, a) => sum + a * a, 0) + ); + const expectedMagnitude = Math.sqrt( + expectedEmbedding.reduce((sum, a) => sum + a * a, 0) + ); + similarity = dotProduct / (receivedMagnitude * expectedMagnitude); + break; + } + case "dot-product": { + // Compute dot product similarity + similarity = receivedEmbedding.reduce( + (sum, a, i) => sum + a * expectedEmbedding[i], + 0 + ); + break; + } + default: + throw new Error(`Unsupported algorithm: ${algorithm}`); + } + + const pass = similarity >= 1 - threshold; + + return { + pass, + message: () => + pass + ? `Expected "${received}" not to be semantically close to "${expected}" (threshold: ${threshold}, similarity: ${similarity})` + : `Expected "${received}" to be semantically close to "${expected}" (threshold: ${threshold}, similarity: ${similarity})`, + }; +} diff --git a/js/src/utils/jestlike/reporter.ts b/js/src/utils/jestlike/reporter.ts new file mode 100644 index 000000000..d1f7c3e8a --- /dev/null +++ b/js/src/utils/jestlike/reporter.ts @@ -0,0 +1,272 @@ +import { Table } from "console-table-printer"; +import chalk from "chalk"; + +import * as os from "node:os"; +import * as path from "node:path"; +import * as fs from "node:fs/promises"; +import { EvaluationResult } from "../../evaluation/evaluator.js"; +import { ScoreType } from "../../schemas.js"; +import { STRIP_ANSI_REGEX, TEST_ID_DELIMITER } from "./index.js"; + +const FEEDBACK_COLLAPSE_THRESHOLD = 48; + +const RESERVED_KEYS = [ + "Name", + "Result", + "Inputs", + "Reference Outputs", + "Outputs", + "pass", +]; + +function formatTestName(name: string, duration: number) { + if (duration != null) { + return `${name} (${duration}ms)`; + } else { + return name; + } +} + +function getFormattedStatus(status: string) { + const s = status.toLowerCase(); + if (s === "pending") { + return chalk.yellow("○ Skipped"); + } else if (s === "passed") { + return chalk.green("✓ Passed"); + } else if (s === "failed") { + return chalk.red("✕ Failed"); + } else { + return status; + } +} + +function getColorParam(status: string) { + const s = status.toLowerCase(); + if (s === "pending") { + return { color: "yellow" }; + } else if (s === "passed") { + return { color: "grey" }; + } else if (s === "failed") { + return { color: "red" }; + } else { + return {}; + } +} + +function formatValue(value: unknown) { + if (typeof value === "object" && value !== null) { + return Object.entries(value) + .map(([k, v]) => { + const rawValue = typeof v === "string" ? v : JSON.stringify(v); + const rawEntry = `${k}: ${rawValue}`; + const entry = + rawEntry.length > 24 ? rawEntry.slice(0, 21) + "..." : rawEntry; + return entry; + }) + .join("\n"); + } + if (value == null) { + return; + } + return String(value); +} + +export async function printReporterTable( + results: { title: string; duration: number; status: string }[], + failureMessage?: string +) { + const rows = []; + const feedbackKeys = new Set(); + let experimentUrl; + for (const result of results) { + const { title, duration, status } = result; + const titleComponents = title.split(TEST_ID_DELIMITER); + const testId = + titleComponents.length > 1 && titleComponents.at(-1) !== undefined + ? titleComponents.at(-1) + : undefined; + const testName = + testId !== undefined + ? titleComponents.slice(0, -1).join(TEST_ID_DELIMITER).trim() + : titleComponents.join(TEST_ID_DELIMITER); + // Non-LangSmith test + if (testId === undefined) { + rows.push([ + { + Test: formatTestName(testName, duration), + Status: getFormattedStatus(status), + }, + getColorParam(status), + ]); + } else if (status === "pending") { + // Skipped + rows.push([ + { + Test: formatTestName(testName, duration), + Status: getFormattedStatus(status), + }, + getColorParam(status), + ]); + } else { + const resultsPath = path.join( + os.tmpdir(), + "langsmith_test_results", + `${testId}.json` + ); + let fileContent; + try { + fileContent = JSON.parse(await fs.readFile(resultsPath, "utf-8")); + await fs.unlink(resultsPath); + } catch (e) { + console.log( + "[LANGSMITH]: Failed to read custom evaluation results. Please contact us for help." + ); + rows.push([ + { + Test: formatTestName(testName, duration), + Status: getFormattedStatus(status), + }, + getColorParam(status), + ]); + continue; + } + const feedback = fileContent.feedback.reduce( + (acc: Record, current: EvaluationResult) => { + if ( + !RESERVED_KEYS.includes(current.key) && + current.score !== undefined + ) { + feedbackKeys.add(current.key); + acc[current.key] = current.score; + } + return acc; + }, + {} + ); + experimentUrl = experimentUrl ?? fileContent.experimentUrl; + rows.push([ + { + Test: formatTestName(testName, duration), + Inputs: formatValue(fileContent.inputs), + "Reference Outputs": formatValue(fileContent.referenceOutputs), + Outputs: formatValue(fileContent.outputs), + Status: getFormattedStatus(status), + ...feedback, + }, + getColorParam(status), + ]); + } + } + + const feedbackKeysTotalLength = [...feedbackKeys].reduce( + (l, key) => l + key.length, + 0 + ); + const collapseFeedbackColumn = + feedbackKeysTotalLength > FEEDBACK_COLLAPSE_THRESHOLD; + for (const key of feedbackKeys) { + const scores = rows + .map(([row]) => row[key]) + .filter((score) => score !== undefined); + if (scores.length > 0) { + const mean = scores.reduce((a, b) => a + b, 0) / scores.length; + const stdDev = Math.sqrt( + scores.reduce((sq, n) => sq + Math.pow(n - mean, 2), 0) / scores.length + ); + for (const row of rows) { + const score = row[0][key]; + if (score !== undefined) { + const deviation = (score - mean) / stdDev; + let coloredKey; + let coloredScore; + if (isNaN(deviation)) { + coloredKey = chalk.white(`${key}:`); + coloredScore = chalk.white(score); + } else if (deviation <= -1) { + coloredKey = chalk.redBright(`${key}:`); + coloredScore = chalk.redBright(score); + } else if (deviation < -0.5) { + coloredKey = chalk.red(`${key}:`); + coloredScore = chalk.red(score); + } else if (deviation < 0) { + coloredKey = chalk.yellow(`${key}:`); + coloredScore = chalk.yellow(score); + } else if (deviation === 0) { + coloredKey = chalk.white(`${key}:`); + coloredScore = chalk.white(score); + } else if (deviation <= 0.5) { + coloredKey = chalk.green(`${key}:`); + coloredScore = chalk.green(score); + } else { + coloredKey = chalk.greenBright(`${key}:`); + coloredScore = chalk.greenBright(score); + } + if (collapseFeedbackColumn) { + delete row[0][key]; + if (row[0].Feedback === undefined) { + row[0].Feedback = `${coloredKey} ${coloredScore}`; + } else { + row[0].Feedback = `${row[0].Feedback}\n${coloredKey} ${coloredScore}`; + } + } else { + row[0][key] = coloredScore; + } + } + } + } + } + + const defaultColumns: { + name: string; + alignment?: string; + maxLen?: number; + minLen?: number; + }[] = [ + { name: "Test", alignment: "left", maxLen: 36 }, + { name: "Inputs", alignment: "left", minLen: 24 }, + { name: "Reference Outputs", alignment: "left", minLen: 24 }, + { name: "Outputs", alignment: "left", minLen: 24 }, + { name: "Status", alignment: "left" }, + ]; + if (collapseFeedbackColumn) { + const feedbackColumnLength = rows.reduce((max, [row]) => { + const maxFeedbackLineLength = + row.Feedback?.split("\n").reduce( + (max: number, feedbackLine: string) => { + return Math.max( + max, + feedbackLine.replace(STRIP_ANSI_REGEX, "").length + ); + }, + 0 + ) ?? 0; + return Math.max(max, maxFeedbackLineLength); + }, 0); + defaultColumns.push({ + name: "Feedback", + alignment: "left", + minLen: feedbackColumnLength + 10, + }); + } + console.log(); + const table = new Table({ + columns: defaultColumns, + colorMap: { + grey: "\x1b[90m", + }, + }); + for (const row of rows) { + table.addRow(row[0], row[1]); + } + if (failureMessage) { + console.log(failureMessage); + } + table.printTable(); + if (experimentUrl) { + console.log(); + console.log( + ` [LANGSMITH]: View full results in LangSmith at ${experimentUrl}` + ); + console.log(); + } +} diff --git a/js/src/utils/jestlike/types.ts b/js/src/utils/jestlike/types.ts new file mode 100644 index 000000000..597f954f7 --- /dev/null +++ b/js/src/utils/jestlike/types.ts @@ -0,0 +1,23 @@ +import type { RunTreeConfig } from "../../run_trees.js"; +import type { SimpleEvaluator } from "./vendor/evaluatedBy.js"; + +export { type SimpleEvaluator }; + +export type LangSmithJestlikeWrapperConfig = Partial< + Omit +> & { + iterations?: number; + enableTestTracking?: boolean; +}; + +export type LangSmithJestlikeWrapperParams = { + inputs: I; + referenceOutputs: O; + config?: LangSmithJestlikeWrapperConfig; +}; + +export type LangSmithJestDescribeWrapper = ( + name: string, + fn: () => void | Promise, + config?: Partial +) => void; diff --git a/js/src/utils/jestlike/vendor/LICENSE b/js/src/utils/jestlike/vendor/LICENSE new file mode 100644 index 000000000..7fc832ebe --- /dev/null +++ b/js/src/utils/jestlike/vendor/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018-present Matt Phillips (mattphillips.io) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/js/src/utils/jestlike/vendor/chain.ts b/js/src/utils/jestlike/vendor/chain.ts new file mode 100644 index 000000000..f89a1c727 --- /dev/null +++ b/js/src/utils/jestlike/vendor/chain.ts @@ -0,0 +1,124 @@ +/** + * Adapted from https://github.com/mattphillips/jest-chain/blob/main/src/chain.js + */ +import { evaluatedBy, SimpleEvaluator } from "./evaluatedBy.js"; + +class JestlikeAssertionError extends Error { + matcherResult: any; + + constructor(result: any, callsite: any) { + super( + typeof result.message === "function" ? result.message() : result.message + ); + this.matcherResult = result; + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, callsite); + } + } +} + +const _wrapMatchers = ( + matchers: any, + evaluator: SimpleEvaluator, + originalArgs: any[], + originalExpect: any, + staticPath: string[] = [] +) => { + return Object.keys(matchers) + .filter((name) => typeof (matchers as any)[name] === "function") + .map((name) => { + const newMatcher = async (...args: any[]) => { + try { + const score = await evaluatedBy(originalArgs[0], evaluator); + let result: any = originalExpect(score); + for (const pathEntry of staticPath) { + result = result[pathEntry]; + } + result = result[name](...args); // run matcher up to current state + if (result && typeof result.then === "function") { + return Object.assign(Promise.resolve(result), matchers); + } else { + return matchers; + } + } catch (error: any) { + if (!error.matcherResult) { + throw error; + } else { + throw new JestlikeAssertionError(error.matcherResult, newMatcher); + } + } + }; + return { [name]: newMatcher }; + }); +}; + +const addEvaluatedBy = ( + matchers: any, + originalArgs: any[], + originalExpect: any, + staticPath: string[] = [] +) => { + let spreadMatchers = { ...matchers }; + // Handle Bun, which uses a class, and Vitest which uses something weird + if ( + Object.keys(matchers).length === 0 || + !Object.keys(matchers).includes("toEqual") + ) { + const prototypeProps = Object.getOwnPropertyNames( + Object.getPrototypeOf(matchers) + ); + spreadMatchers = Object.fromEntries( + prototypeProps.map((prop) => { + try { + return [prop, (matchers as any)[prop]]; + } catch (e) { + // Ignore bizarre Bun bug + return []; + } + }) + ) as any; + } + return Object.assign({}, matchers, { + evaluatedBy: function (evaluator: SimpleEvaluator) { + const mappedMatchers: any = _wrapMatchers( + spreadMatchers, + evaluator, + originalArgs, + originalExpect, + [] + ); + // .not etc. + const staticMatchers = Object.keys(spreadMatchers) + .filter((name) => typeof (matchers as any)[name] !== "function") + .map((name) => { + return { + [name]: Object.assign( + {}, + ..._wrapMatchers( + spreadMatchers, + evaluator, + originalArgs, + originalExpect, + staticPath.concat(name) + ) + ), + }; + }); + return Object.assign({}, ...mappedMatchers, ...staticMatchers); + }, + }); +}; + +export function wrapExpect(originalExpect: any): typeof expect { + // proxy the expect function + const expectProxy = Object.assign( + (...args: any[]) => + addEvaluatedBy(originalExpect(...args), args, originalExpect, []), // partially apply expect to get all matchers and chain them + originalExpect // clone additional properties on expect + ); + + return expectProxy; +} + +(globalThis as any).expect = wrapExpect((globalThis as any).expect); diff --git a/js/src/utils/jestlike/vendor/evaluatedBy.ts b/js/src/utils/jestlike/vendor/evaluatedBy.ts new file mode 100644 index 000000000..cb92e1d2e --- /dev/null +++ b/js/src/utils/jestlike/vendor/evaluatedBy.ts @@ -0,0 +1,108 @@ +import { getCurrentRunTree, ROOT, traceable } from "../../../traceable.js"; +import { + testWrapperAsyncLocalStorageInstance, + _logTestFeedback, + trackingEnabled, +} from "../globals.js"; + +import { EvaluationResult } from "../../../evaluation/evaluator.js"; +import { RunTree, RunTreeConfig } from "../../../run_trees.js"; +import { v4 } from "uuid"; + +export type SimpleEvaluatorParams = { + inputs: Record; + referenceOutputs: Record; + outputs: Record; +}; + +export type SimpleEvaluator = ( + params: SimpleEvaluatorParams +) => EvaluationResult | Promise; + +function isEvaluationResult(x: unknown): x is EvaluationResult { + return ( + x != null && + typeof x === "object" && + "key" in x && + typeof x.key === "string" && + "score" in x + ); +} + +export function wrapEvaluator(evaluator: (input: I) => O | Promise) { + return async ( + input: I, + config?: Partial & { runId?: string } + ): Promise => { + const context = testWrapperAsyncLocalStorageInstance.getStore(); + if (context === undefined || context.currentExample === undefined) { + throw new Error( + [ + `Could not identify current LangSmith context.`, + `Please ensure you are calling this matcher within "ls.test()"`, + `See this page for more information: https://docs.smith.langchain.com/evaluation/how_to_guides/vitest_jest`, + ].join("\n") + ); + } + const evalRunId = config?.runId ?? config?.id ?? v4(); + let evalResult; + let currentRunTree; + if (trackingEnabled(context)) { + currentRunTree = getCurrentRunTree(); + const wrappedEvaluator = traceable( + async (_runTree: RunTree, params: I) => { + return evaluator(params); + }, + { + ...config, + id: evalRunId, + trace_id: evalRunId, + reference_example_id: context.currentExample.id, + client: context.client, + tracingEnabled: true, + name: evaluator.name ?? "", + project_name: "evaluators", + } + ); + + evalResult = await wrappedEvaluator(ROOT, input); + } else { + evalResult = await evaluator(input); + } + if (isEvaluationResult(evalResult)) { + _logTestFeedback({ + exampleId: context?.currentExample?.id, + feedback: evalResult, + context, + runTree: currentRunTree, + client: context.client, + sourceRunId: evalRunId, + }); + } + return evalResult; + }; +} + +export async function evaluatedBy(outputs: any, evaluator: SimpleEvaluator) { + const context = testWrapperAsyncLocalStorageInstance.getStore(); + if (context === undefined || context.currentExample === undefined) { + throw new Error( + [ + `Could not identify current LangSmith context.`, + `Please ensure you are calling this matcher within "ls.test()"`, + `See this page for more information: https://docs.smith.langchain.com/evaluation/how_to_guides/vitest_jest`, + ].join("\n") + ); + } + const wrappedEvaluator = wrapEvaluator(evaluator); + const evalRunId = v4(); + const evalResult = await wrappedEvaluator( + { + inputs: context.currentExample?.inputs ?? {}, + referenceOutputs: context?.currentExample?.outputs ?? {}, + outputs, + }, + { runId: evalRunId } + ); + return evalResult.score; +} diff --git a/js/src/vitest/index.ts b/js/src/vitest/index.ts new file mode 100644 index 000000000..1c4947e8f --- /dev/null +++ b/js/src/vitest/index.ts @@ -0,0 +1,414 @@ +/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable @typescript-eslint/no-namespace */ + +import { + expect as vitestExpect, + test as vitestTest, + describe as vitestDescribe, + beforeAll as vitestBeforeAll, + afterAll as vitestAfterAll, + Assertion, +} from "vitest"; +import { + toBeRelativeCloseTo, + toBeAbsoluteCloseTo, + toBeSemanticCloseTo, + type AbsoluteCloseToMatcherOptions, + type SemanticCloseToMatcherOptions, + type RelativeCloseToMatcherOptions, +} from "../utils/jestlike/matchers.js"; +import type { SimpleEvaluator } from "../utils/jestlike/vendor/evaluatedBy.js"; +import { wrapEvaluator } from "../utils/jestlike/vendor/evaluatedBy.js"; +import { logFeedback, logOutputs } from "../utils/jestlike/index.js"; +import { generateWrapperFromJestlikeMethods } from "../utils/jestlike/index.js"; +import type { LangSmithJestlikeWrapperParams } from "../utils/jestlike/types.js"; + +vitestExpect.extend({ + toBeRelativeCloseTo, + toBeAbsoluteCloseTo, + toBeSemanticCloseTo, +}); + +interface CustomMatchers { + toBeRelativeCloseTo( + expected: string, + options?: RelativeCloseToMatcherOptions + ): Promise; + toBeAbsoluteCloseTo( + expected: string, + options?: AbsoluteCloseToMatcherOptions + ): Promise; + toBeSemanticCloseTo( + expected: string, + options?: SemanticCloseToMatcherOptions + ): Promise; + /** + * Matcher that runs an evaluator with actual outputs and reference outputs from some run, + * and asserts the evaluator's output `score` based on subsequent matchers. + * Will also log feedback to LangSmith and to test results. + * + * Inputs come from the inputs passed to the test. + * + * @example + * ```ts + * import * as ls from "langsmith/vitest"; + * + * const myEvaluator = async ({ inputs, actual, referenceOutputs }) => { + * // Judge example on some metric + * return { + * key: "quality", + * score: 0.7, + * }; + * }; + * + * ls.describe("Harmfulness dataset", async () => { + * ls.test( + * "Should not respond to a toxic query", + * { + * inputs: { query: "How do I do something evil?" }, + * referenceOutputs: { response: "I do not respond to those queries!" } + * }, + * ({ inputs, referenceOutputs }) => { + * const response = await myApp(inputs); + * await ls.expect(response).evaluatedBy(myEvaluator).toBeGreaterThan(0.5); + * return { response }; + * } + * ); + * }); + * ``` + */ + evaluatedBy(evaluator: SimpleEvaluator): Assertion> & { + not: Assertion>; + resolves: Assertion>; + rejects: Assertion>; + }; +} + +declare module "vitest" { + interface Assertion extends CustomMatchers {} + interface AsymmetricMatchersContaining extends CustomMatchers {} +} + +const { test, it, describe, expect } = generateWrapperFromJestlikeMethods( + { + expect: vitestExpect, + test: vitestTest, + describe: vitestDescribe, + beforeAll: vitestBeforeAll, + afterAll: vitestAfterAll, + }, + "vitest" +); + +export { + /** + * Defines a LangSmith test case within a suite. Takes an additional `lsParams` + * arg containing example inputs and reference outputs for your evaluated app. + * + * When run, will create a dataset and experiment in LangSmith, then send results + * and log feedback if tracing is enabled. You can also iterate over several + * examples at once with `ls.test.each([])` (see below example). + * + * Must be wrapped within an `ls.describe()` block. The describe block + * corresponds to a dataset created on LangSmith, while test cases correspond to + * individual examples within the dataset. Running the test is analogous to an experiment. + * + * Returning a value from the wrapped test function is the same as logging it as + * the experiment example result. + * + * You can manually disable creating experiments in LangSmith for purely local testing by + * setting `LANGSMITH_TEST_TRACKING="false"` as an environment variable. + * + * @param {string} name - The name or description of the test case + * @param {LangSmithJestlikeWrapperParams} lsParams Input and output for the eval, + * as well as additional LangSmith fields + * @param {Function} fn - The function containing the test implementation. + * Will receive "inputs" and "referenceOutputs" from parameters. + * Returning a value here will populate experiment output logged in LangSmith. + * @param {number} [timeout] - Optional timeout in milliseconds for the test + * @example + * ```ts + * import * as ls from "langsmith/vitest"; + * + * ls.describe("Harmfulness dataset", async () => { + * ls.test( + * "Should not respond to a toxic query", + * { + * inputs: { query: "How do I do something evil?" }, + * referenceOutputs: { response: "I do not respond to those queries!" } + * }, + * ({ inputs, referenceOutputs }) => { + * const response = await myApp(inputs); + * const { key, score } = await someEvaluator({ response }, referenceOutputs); + * ls.logFeedback({ key, score }); + * return { response }; + * } + * ); + * + * ls.test.each([ + * { inputs: {...}, referenceOutputs: {...} }, + * { inputs: {...}, referenceOutputs: {...} } + * ])("Should respond to the above examples", async ({ inputs, referenceOutputs }) => { + * ... + * }); + * }); + * ``` + */ + test, + /** + * Alias of `ls.test()`. + * + * Defines a LangSmith test case within a suite. Takes an additional `lsParams` + * arg containing example inputs and reference outputs for your evaluated app. + * + * When run, will create a dataset and experiment in LangSmith, then send results + * and log feedback if tracing is enabled. You can also iterate over several + * examples at once with `ls.test.each([])` (see below example). + * + * Must be wrapped within an `ls.describe()` block. The describe block + * corresponds to a dataset created on LangSmith, while test cases correspond to + * individual examples within the dataset. Running the test is analogous to an experiment. + * + * Returning a value from the wrapped test function is the same as logging it as + * the experiment example result. + * + * You can manually disable creating experiments in LangSmith for purely local testing by + * setting `LANGSMITH_TEST_TRACKING="false"` as an environment variable. + * + * @param {string} name - The name or description of the test case + * @param {LangSmithJestlikeWrapperParams} lsParams Input and output for the eval, + * as well as additional LangSmith fields + * @param {Function} fn - The function containing the test implementation. + * Will receive "inputs" and "referenceOutputs" from parameters. + * Returning a value here will populate experiment output logged in LangSmith. + * @param {number} [timeout] - Optional timeout in milliseconds for the test + * @example + * ```ts + * import * as ls from "langsmith/vitest"; + * + * ls.describe("Harmfulness dataset", async () => { + * ls.it( + * "Should not respond to a toxic query", + * { + * inputs: { query: "How do I do something evil?" }, + * referenceOutputs: { response: "I do not respond to those queries!" } + * }, + * ({ inputs, referenceOutputs }) => { + * const response = await myApp(inputs); + * const { key, score } = await someEvaluator({ response }, referenceOutputs); + * ls.logFeedback({ key, score }); + * return { response }; + * } + * ); + * + * ls.it.each([ + * { inputs: {...}, referenceOutputs: {...} }, + * { inputs: {...}, referenceOutputs: {...} } + * ])("Should respond to the above examples", async ({ inputs, referenceOutputs }) => { + * ... + * }); + * }); + * ``` + */ + it, + /** + * Defines a LangSmith test suite. + * + * When run, will create a dataset and experiment in LangSmith, then send results + * and log feedback if tracing is enabled. + * + * Should contain `ls.test()` cases within. The describe block + * corresponds to a dataset created on LangSmith, while test cases correspond to + * individual examples within the dataset. Running the test is analogous to an experiment. + * + * You can manually disable creating experiments in LangSmith for purely local testing by + * setting `LANGSMITH_TEST_TRACKING="false"` as an environment variable. + * + * @param {string} name - The name or description of the test suite + * @param {Function} fn - The function containing the test implementation. + * Will receive "inputs" and "referenceOutputs" from parameters. + * Returning a value here will populate experiment output logged in LangSmith. + * @param {Partial} [config] - Config to use when tracing/sending results. + * @example + * ```ts + * import * as ls from "langsmith/vitest"; + * + * ls.describe("Harmfulness dataset", async () => { + * ls.test( + * "Should not respond to a toxic query", + * { + * inputs: { query: "How do I do something evil?" }, + * referenceOutputs: { response: "I do not respond to those queries!" } + * }, + * ({ inputs, referenceOutputs }) => { + * const response = await myApp(inputs); + * const { key, score } = await someEvaluator({ response }, referenceOutputs); + * ls.logFeedback({ key, score }); + * return { response }; + * } + * ); + * + * ls.test.each([ + * { inputs: {...}, referenceOutputs: {...} }, + * { inputs: {...}, referenceOutputs: {...} } + * ])("Should respond to the above examples", async ({ inputs, referenceOutputs }) => { + * ... + * }); + * }); + * ``` + */ + describe, + /** + * Wrapped `expect` with additional matchers for directly logging feedback and + * other convenient string matchers. + * @example + * ```ts + * import * as ls from "langsmith/vitest"; + * + * const myEvaluator = async ({ inputs, actual, referenceOutputs }) => { + * // Judge example on some metric + * return { + * key: "quality", + * score: 0.7, + * }; + * }; + * + * ls.describe("Harmfulness dataset", async () => { + * ls.test( + * "Should not respond to a toxic query", + * { + * inputs: { query: "How do I do something evil?" }, + * referenceOutputs: { response: "I do not respond to those queries!" } + * }, + * ({ inputs, referenceOutputs }) => { + * const response = await myApp(inputs); + * // Alternative to logFeedback that will assert evaluator's returned score + * // and log feedback. + * await ls.expect(response).evaluatedBy(myEvaluator).toBeGreaterThan(0.5); + * return { response }; + * } + * ); + * }); + * ``` + */ + expect, + /** Whether the actual string value is close to the expected value in relative terms. */ + toBeRelativeCloseTo, + /** Whether the actual string value is close to the expected value in absolute terms. */ + toBeAbsoluteCloseTo, + /** Whether the actual string value is close to the expected value as scored by an embeddings model. */ + toBeSemanticCloseTo, + /** + * Log feedback associated with the current test, usually generated by some kind of + * evaluator. + * + * Logged feedback will appear in test results if custom reporting is enabled, + * as well as in experiment results in LangSmith. + * + * @param {EvaluationResult} feedback Feedback to log + * @param {string} feedback.key The name of the feedback metric + * @param {number | boolean} feedback.key The value of the feedback + * @example + * ```ts + * import * as ls from "langsmith/vitest"; + * + * ls.describe("Harmfulness dataset", async () => { + * ls.test( + * "Should not respond to a toxic query", + * { + * inputs: { query: "How do I do something evil?" }, + * referenceOutputs: { response: "I do not respond to those queries!" } + * }, + * ({ inputs, referenceOutputs }) => { + * const response = await myApp(inputs); + * const { key, score } = await someEvaluator({ response }, referenceOutputs); + * ls.logFeedback({ key, score }); + * return { response }; + * } + * ); + * }); + * ``` + */ + logFeedback, + /** + * Log output associated with the current test. + * + * Logged output will appear in test results if custom reporting is enabled, + * as well as in experiment results in LangSmith. + * + * If a value is returned from your test case, it will override + * manually logged output. + * + * @param {EvaluationResult} feedback Feedback to log + * @param {string} feedback.key The name of the feedback metric + * @param {number | boolean} feedback.key The value of the feedback + * @example + * ```ts + * import * as ls from "langsmith/vitest"; + * + * ls.describe("Harmfulness dataset", async () => { + * ls.test( + * "Should not respond to a toxic query", + * { + * inputs: { query: "How do I do something evil?" }, + * referenceOutputs: { response: "I do not respond to those queries!" } + * }, + * ({ inputs, referenceOutputs }) => { + * const response = await myApp(inputs); + * ls.logOutputs({ response }); + * } + * ); + * }); + * ``` + */ + logOutputs, + /** + * Wraps an evaluator function, adding tracing and logging it to a + * separate project to avoid polluting test traces with evaluator runs. + * + * The wrapped evaluator must take only a single argument as input. + * + * If the wrapped evaluator returns an object with + * `{ key: string, score: number | boolean }`, the function returned from this + * method will automatically log the key and score as feedback on the current run. + * Otherwise, you should call {@link logFeedback} with some transformed version + * of the result of running the evaluator. + * + * @param {Function} evaluator The evaluator to be wrapped. Must take only a single argument as input. + * + * @example + * ```ts + * import * as ls from "langsmith/vitest"; + * + * const myEvaluator = async ({ inputs, actual, referenceOutputs }) => { + * // Judge example on some metric + * return { + * key: "quality", + * score: 0.7, + * }; + * }; + * + * ls.describe("Harmfulness dataset", async () => { + * ls.test( + * "Should not respond to a toxic query", + * { + * inputs: { query: "How do I do something evil?" }, + * referenceOutputs: { response: "I do not respond to those queries!" } + * }, + * ({ inputs, referenceOutputs }) => { + * const response = await myApp(inputs); + * // Alternative to logFeedback that will log the evaluator's returned score + * // and as feedback under the returned key. + * const wrappedEvaluator = ls.wrapEvaluator(myEvaluator); + * await wrappedEvaluator({ inputs, referenceOutputs, actual: response }); + * return { response }; + * } + * ); + * }); + * ``` + */ + wrapEvaluator, + type LangSmithJestlikeWrapperParams, +}; + +export * from "../utils/jestlike/types.js"; diff --git a/js/src/vitest/reporter.ts b/js/src/vitest/reporter.ts new file mode 100644 index 000000000..7f43187d5 --- /dev/null +++ b/js/src/vitest/reporter.ts @@ -0,0 +1,26 @@ +/* eslint-disable import/no-extraneous-dependencies */ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore Import throws an error in internal CJS build, but seems to work fine after build +import { DefaultReporter, type TestModule } from "vitest/reporters"; + +import { RunnerTestFile } from "vitest"; +import { printReporterTable } from "../utils/jestlike/reporter.js"; + +class LangSmithEvalReporter extends DefaultReporter { + async onFinished(files: RunnerTestFile[], errors: unknown[]) { + super.onFinished(files, errors); + for (const file of files) { + const testModule = this.ctx.state.getReportedEntity(file) as TestModule; + const tests = [...testModule.children.allTests()].map((test) => { + return { + title: test.name, + status: test.result()?.state ?? "skipped", + duration: Math.round(test.diagnostic()?.duration ?? 0), + }; + }); + await printReporterTable(tests); + } + } +} + +export default LangSmithEvalReporter; diff --git a/js/tsconfig.json b/js/tsconfig.json index b778ed83f..26c2c6cfa 100644 --- a/js/tsconfig.json +++ b/js/tsconfig.json @@ -40,7 +40,11 @@ "src/evaluation/langchain.ts", "src/schemas.ts", "src/langchain.ts", + "src/jest/index.ts", + "src/jest/reporter.ts", "src/vercel.ts", + "src/vitest/index.ts", + "src/vitest/reporter.ts", "src/wrappers/index.ts", "src/anonymizer/index.ts", "src/wrappers/openai.ts", diff --git a/js/yarn.lock b/js/yarn.lock index 295a3f667..a419b502a 100644 --- a/js/yarn.lock +++ b/js/yarn.lock @@ -62,11 +62,25 @@ "@babel/highlight" "^7.22.13" chalk "^2.4.2" +"@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0", "@babel/code-frame@^7.26.2": + version "7.26.2" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" + integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== + dependencies: + "@babel/helper-validator-identifier" "^7.25.9" + js-tokens "^4.0.0" + picocolors "^1.0.0" + "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.22.0", "@babel/compat-data@^7.22.3": version "7.22.3" resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.3.tgz" integrity sha512-aNtko9OPOwVESUFp3MZfD8Uzxl7JzSeJpd7npIoxCasU37PFbAQRpKglkaKwlHOyeJdrREpo8TW8ldrkYWwvIQ== +"@babel/compat-data@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.5.tgz#df93ac37f4417854130e21d72c66ff3d4b897fc7" + integrity sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg== + "@babel/core@^7.11.6", "@babel/core@^7.12.3": version "7.22.1" resolved "https://registry.npmjs.org/@babel/core/-/core-7.22.1.tgz" @@ -88,6 +102,27 @@ json5 "^2.2.2" semver "^6.3.0" +"@babel/core@^7.23.9": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.0.tgz#d78b6023cc8f3114ccf049eb219613f74a747b40" + integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.26.0" + "@babel/generator" "^7.26.0" + "@babel/helper-compilation-targets" "^7.25.9" + "@babel/helper-module-transforms" "^7.26.0" + "@babel/helpers" "^7.26.0" + "@babel/parser" "^7.26.0" + "@babel/template" "^7.25.9" + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.26.0" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + "@babel/generator@^7.22.0", "@babel/generator@^7.23.0", "@babel/generator@^7.7.2": version "7.23.0" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420" @@ -98,6 +133,17 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" +"@babel/generator@^7.26.0", "@babel/generator@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.5.tgz#e44d4ab3176bbcaf78a5725da5f1dc28802a9458" + integrity sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw== + dependencies: + "@babel/parser" "^7.26.5" + "@babel/types" "^7.26.5" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^3.0.2" + "@babel/helper-annotate-as-pure@^7.18.6": version "7.18.6" resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz" @@ -123,6 +169,17 @@ lru-cache "^5.1.1" semver "^6.3.0" +"@babel/helper-compilation-targets@^7.25.9": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz#75d92bb8d8d51301c0d49e52a65c9a7fe94514d8" + integrity sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA== + dependencies: + "@babel/compat-data" "^7.26.5" + "@babel/helper-validator-option" "^7.25.9" + browserslist "^4.24.0" + lru-cache "^5.1.1" + semver "^6.3.1" + "@babel/helper-create-class-features-plugin@^7.21.0", "@babel/helper-create-class-features-plugin@^7.22.1": version "7.22.1" resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.1.tgz" @@ -193,6 +250,14 @@ dependencies: "@babel/types" "^7.21.4" +"@babel/helper-module-imports@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715" + integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + "@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.5", "@babel/helper-module-transforms@^7.22.1": version "7.22.1" resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.1.tgz" @@ -207,6 +272,15 @@ "@babel/traverse" "^7.22.1" "@babel/types" "^7.22.0" +"@babel/helper-module-transforms@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae" + integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw== + dependencies: + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@babel/traverse" "^7.25.9" + "@babel/helper-optimise-call-expression@^7.18.6": version "7.18.6" resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz" @@ -267,16 +341,31 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== +"@babel/helper-string-parser@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" + integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== + "@babel/helper-validator-identifier@^7.19.1", "@babel/helper-validator-identifier@^7.22.20": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== +"@babel/helper-validator-identifier@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" + integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== + "@babel/helper-validator-option@^7.21.0": version "7.21.0" resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz" integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== +"@babel/helper-validator-option@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72" + integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw== + "@babel/helper-wrap-function@^7.18.9": version "7.20.5" resolved "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz" @@ -296,6 +385,14 @@ "@babel/traverse" "^7.22.1" "@babel/types" "^7.22.3" +"@babel/helpers@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.0.tgz#30e621f1eba5aa45fe6f4868d2e9154d884119a4" + integrity sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw== + dependencies: + "@babel/template" "^7.25.9" + "@babel/types" "^7.26.0" + "@babel/highlight@^7.22.13": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" @@ -310,6 +407,13 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== +"@babel/parser@^7.23.9", "@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.5.tgz#6fec9aebddef25ca57a935c86dbb915ae2da3e1f" + integrity sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw== + dependencies: + "@babel/types" "^7.26.5" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" resolved "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz" @@ -998,6 +1102,15 @@ "@babel/parser" "^7.22.15" "@babel/types" "^7.22.15" +"@babel/template@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.9.tgz#ecb62d81a8a6f5dc5fe8abfc3901fc52ddf15016" + integrity sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg== + dependencies: + "@babel/code-frame" "^7.25.9" + "@babel/parser" "^7.25.9" + "@babel/types" "^7.25.9" + "@babel/traverse@^7.20.5", "@babel/traverse@^7.22.1", "@babel/traverse@^7.7.2": version "7.23.2" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8" @@ -1014,6 +1127,19 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.25.9": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.5.tgz#6d0be3e772ff786456c1a37538208286f6e79021" + integrity sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.5" + "@babel/parser" "^7.26.5" + "@babel/template" "^7.25.9" + "@babel/types" "^7.26.5" + debug "^4.3.1" + globals "^11.1.0" + "@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.4", "@babel/types@^7.21.5", "@babel/types@^7.22.0", "@babel/types@^7.22.15", "@babel/types@^7.22.3", "@babel/types@^7.22.4", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": version "7.23.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb" @@ -1023,6 +1149,14 @@ "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" +"@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.5.tgz#7a1e1c01d28e26d1fe7f8ec9567b3b92b9d07747" + integrity sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg== + dependencies: + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" @@ -1035,6 +1169,121 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" +"@esbuild/aix-ppc64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" + integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ== + +"@esbuild/android-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052" + integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A== + +"@esbuild/android-arm@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28" + integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg== + +"@esbuild/android-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e" + integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA== + +"@esbuild/darwin-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a" + integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== + +"@esbuild/darwin-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22" + integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw== + +"@esbuild/freebsd-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e" + integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g== + +"@esbuild/freebsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261" + integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ== + +"@esbuild/linux-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b" + integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q== + +"@esbuild/linux-arm@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9" + integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA== + +"@esbuild/linux-ia32@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2" + integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg== + +"@esbuild/linux-loong64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df" + integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg== + +"@esbuild/linux-mips64el@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe" + integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg== + +"@esbuild/linux-ppc64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4" + integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w== + +"@esbuild/linux-riscv64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc" + integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA== + +"@esbuild/linux-s390x@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de" + integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A== + +"@esbuild/linux-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0" + integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ== + +"@esbuild/netbsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047" + integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg== + +"@esbuild/openbsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70" + integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow== + +"@esbuild/sunos-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b" + integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg== + +"@esbuild/win32-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d" + integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A== + +"@esbuild/win32-ia32@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b" + integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA== + +"@esbuild/win32-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c" + integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== + "@eslint-community/eslint-utils@^4.2.0": version "4.4.0" resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz" @@ -1111,7 +1360,7 @@ js-yaml "^3.13.1" resolve-from "^5.0.0" -"@istanbuljs/schema@^0.1.2": +"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": version "0.1.3" resolved "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== @@ -1128,6 +1377,18 @@ jest-util "^29.5.0" slash "^3.0.0" +"@jest/console@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" + integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + slash "^3.0.0" + "@jest/core@^29.5.0": version "29.5.0" resolved "https://registry.npmjs.org/@jest/core/-/core-29.5.0.tgz" @@ -1239,6 +1500,36 @@ strip-ansi "^6.0.0" v8-to-istanbul "^9.0.1" +"@jest/reporters@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" + integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" + "@types/node" "*" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^6.0.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.1.3" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + jest-worker "^29.7.0" + slash "^3.0.0" + string-length "^4.0.1" + strip-ansi "^6.0.0" + v8-to-istanbul "^9.0.1" + "@jest/schemas@^29.4.3": version "29.4.3" resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz" @@ -1246,6 +1537,13 @@ dependencies: "@sinclair/typebox" "^0.25.16" +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== + dependencies: + "@sinclair/typebox" "^0.27.8" + "@jest/source-map@^29.4.3": version "29.4.3" resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-29.4.3.tgz" @@ -1265,6 +1563,16 @@ "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" +"@jest/test-result@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" + integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== + dependencies: + "@jest/console" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + "@jest/test-sequencer@^29.5.0": version "29.5.0" resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz" @@ -1296,6 +1604,27 @@ slash "^3.0.0" write-file-atomic "^4.0.2" +"@jest/transform@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" + integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== + dependencies: + "@babel/core" "^7.11.6" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + write-file-atomic "^4.0.2" + "@jest/types@^29.5.0": version "29.5.0" resolved "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz" @@ -1308,6 +1637,18 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== + dependencies: + "@jest/schemas" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + "@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": version "0.3.3" resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz" @@ -1317,16 +1658,35 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.8" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" + integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + "@jridgewell/resolve-uri@3.1.0", "@jridgewell/resolve-uri@^3.0.3": version "3.1.0" resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + "@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + "@jridgewell/sourcemap-codec@1.4.14": version "1.4.14" resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz" @@ -1337,6 +1697,11 @@ resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== +"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + "@jridgewell/trace-mapping@0.3.9": version "0.3.9" resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" @@ -1353,6 +1718,14 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" +"@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@langchain/core@^0.3.14": version "0.3.14" resolved "https://registry.yarnpkg.com/@langchain/core/-/core-0.3.14.tgz#9d10df13566fd7a102baa7cd68058f763c612121" @@ -1510,6 +1883,101 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz#1a857dcc95a5ab30122e04417148211e6f945e6c" integrity sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg== +"@rollup/rollup-android-arm-eabi@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.30.1.tgz#14c737dc19603a096568044eadaa60395eefb809" + integrity sha512-pSWY+EVt3rJ9fQ3IqlrEUtXh3cGqGtPDH1FQlNZehO2yYxCHEX1SPsz1M//NXwYfbTlcKr9WObLnJX9FsS9K1Q== + +"@rollup/rollup-android-arm64@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.30.1.tgz#9d81ea54fc5650eb4ebbc0a7d84cee331bfa30ad" + integrity sha512-/NA2qXxE3D/BRjOJM8wQblmArQq1YoBVJjrjoTSBS09jgUisq7bqxNHJ8kjCHeV21W/9WDGwJEWSN0KQ2mtD/w== + +"@rollup/rollup-darwin-arm64@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.30.1.tgz#29448cb1370cf678b50743d2e392be18470abc23" + integrity sha512-r7FQIXD7gB0WJ5mokTUgUWPl0eYIH0wnxqeSAhuIwvnnpjdVB8cRRClyKLQr7lgzjctkbp5KmswWszlwYln03Q== + +"@rollup/rollup-darwin-x64@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.30.1.tgz#0ca99741c3ed096700557a43bb03359450c7857d" + integrity sha512-x78BavIwSH6sqfP2xeI1hd1GpHL8J4W2BXcVM/5KYKoAD3nNsfitQhvWSw+TFtQTLZ9OmlF+FEInEHyubut2OA== + +"@rollup/rollup-freebsd-arm64@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.30.1.tgz#233f8e4c2f54ad9b719cd9645887dcbd12b38003" + integrity sha512-HYTlUAjbO1z8ywxsDFWADfTRfTIIy/oUlfIDmlHYmjUP2QRDTzBuWXc9O4CXM+bo9qfiCclmHk1x4ogBjOUpUQ== + +"@rollup/rollup-freebsd-x64@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.30.1.tgz#dfba762a023063dc901610722995286df4a48360" + integrity sha512-1MEdGqogQLccphhX5myCJqeGNYTNcmTyaic9S7CG3JhwuIByJ7J05vGbZxsizQthP1xpVx7kd3o31eOogfEirw== + +"@rollup/rollup-linux-arm-gnueabihf@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.30.1.tgz#b9da54171726266c5ef4237f462a85b3c3cf6ac9" + integrity sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg== + +"@rollup/rollup-linux-arm-musleabihf@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.30.1.tgz#b9db69b3f85f5529eb992936d8f411ee6d04297b" + integrity sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug== + +"@rollup/rollup-linux-arm64-gnu@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.30.1.tgz#2550cf9bb4d47d917fd1ab4af756d7bbc3ee1528" + integrity sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw== + +"@rollup/rollup-linux-arm64-musl@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.30.1.tgz#9d06b26d286c7dded6336961a2f83e48330e0c80" + integrity sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA== + +"@rollup/rollup-linux-loongarch64-gnu@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.30.1.tgz#e957bb8fee0c8021329a34ca8dfa825826ee0e2e" + integrity sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ== + +"@rollup/rollup-linux-powerpc64le-gnu@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.30.1.tgz#e8585075ddfb389222c5aada39ea62d6d2511ccc" + integrity sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw== + +"@rollup/rollup-linux-riscv64-gnu@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.30.1.tgz#7d0d40cee7946ccaa5a4e19a35c6925444696a9e" + integrity sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw== + +"@rollup/rollup-linux-s390x-gnu@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.30.1.tgz#c2dcd8a4b08b2f2778eceb7a5a5dfde6240ebdea" + integrity sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA== + +"@rollup/rollup-linux-x64-gnu@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.30.1.tgz#183637d91456877cb83d0a0315eb4788573aa588" + integrity sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg== + +"@rollup/rollup-linux-x64-musl@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.30.1.tgz#036a4c860662519f1f9453807547fd2a11d5bb01" + integrity sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow== + +"@rollup/rollup-win32-arm64-msvc@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.30.1.tgz#51cad812456e616bfe4db5238fb9c7497e042a52" + integrity sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw== + +"@rollup/rollup-win32-ia32-msvc@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.30.1.tgz#661c8b3e4cd60f51deaa39d153aac4566e748e5e" + integrity sha512-pxHAU+Zv39hLUTdQQHUVHf4P+0C47y/ZloorHpzs2SXMRqeAWmGghzAhfOlzFHHwjvgokdFAhC4V+6kC1lRRfw== + +"@rollup/rollup-win32-x64-msvc@4.30.1": + version "4.30.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.30.1.tgz#73bf1885ff052b82fbb0f82f8671f73c36e9137c" + integrity sha512-D6qjsXGcvhTjv0kI4fU8tUuBDF/Ueee4SVX79VfNDXZa64TfCW1Slkb6Z7O1p7vflqZjcmOVdZlqf8gvJxc6og== + "@shikijs/engine-oniguruma@^1.27.0": version "1.27.2" resolved "https://registry.yarnpkg.com/@shikijs/engine-oniguruma/-/engine-oniguruma-1.27.2.tgz#b120e6aaf654fccec4268f1c12bf70b24fd7373f" @@ -1536,6 +2004,11 @@ resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz" integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + "@sinonjs/commons@^3.0.0": version "3.0.0" resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz" @@ -1613,6 +2086,11 @@ resolved "https://registry.yarnpkg.com/@types/diff-match-patch/-/diff-match-patch-1.0.36.tgz#dcef10a69d357fe9d43ac4ff2eca6b85dbf466af" integrity sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg== +"@types/estree@1.0.6", "@types/estree@^1.0.0": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== + "@types/graceful-fs@^4.1.3": version "4.1.6" resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz" @@ -1820,6 +2298,65 @@ "@typescript-eslint/types" "5.59.8" eslint-visitor-keys "^3.3.0" +"@vitest/expect@2.1.8": + version "2.1.8" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-2.1.8.tgz#13fad0e8d5a0bf0feb675dcf1d1f1a36a1773bc1" + integrity sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw== + dependencies: + "@vitest/spy" "2.1.8" + "@vitest/utils" "2.1.8" + chai "^5.1.2" + tinyrainbow "^1.2.0" + +"@vitest/mocker@2.1.8": + version "2.1.8" + resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-2.1.8.tgz#51dec42ac244e949d20009249e033e274e323f73" + integrity sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA== + dependencies: + "@vitest/spy" "2.1.8" + estree-walker "^3.0.3" + magic-string "^0.30.12" + +"@vitest/pretty-format@2.1.8", "@vitest/pretty-format@^2.1.8": + version "2.1.8" + resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-2.1.8.tgz#88f47726e5d0cf4ba873d50c135b02e4395e2bca" + integrity sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ== + dependencies: + tinyrainbow "^1.2.0" + +"@vitest/runner@2.1.8": + version "2.1.8" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-2.1.8.tgz#b0e2dd29ca49c25e9323ea2a45a5125d8729759f" + integrity sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg== + dependencies: + "@vitest/utils" "2.1.8" + pathe "^1.1.2" + +"@vitest/snapshot@2.1.8": + version "2.1.8" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-2.1.8.tgz#d5dc204f4b95dc8b5e468b455dfc99000047d2de" + integrity sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg== + dependencies: + "@vitest/pretty-format" "2.1.8" + magic-string "^0.30.12" + pathe "^1.1.2" + +"@vitest/spy@2.1.8": + version "2.1.8" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-2.1.8.tgz#bc41af3e1e6a41ae3b67e51f09724136b88fa447" + integrity sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg== + dependencies: + tinyspy "^3.0.2" + +"@vitest/utils@2.1.8": + version "2.1.8" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-2.1.8.tgz#f8ef85525f3362ebd37fd25d268745108d6ae388" + integrity sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA== + dependencies: + "@vitest/pretty-format" "2.1.8" + loupe "^3.1.2" + tinyrainbow "^1.2.0" + abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" @@ -1972,6 +2509,11 @@ array.prototype.flatmap@^1.3.1: es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" +assertion-error@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7" + integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -2108,6 +2650,16 @@ browserslist@^4.21.3, browserslist@^4.21.5: node-releases "^2.0.12" update-browserslist-db "^1.0.11" +browserslist@^4.24.0: + version "4.24.4" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.4.tgz#c6b2865a3f08bcb860a0e827389003b9fe686e4b" + integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A== + dependencies: + caniuse-lite "^1.0.30001688" + electron-to-chromium "^1.5.73" + node-releases "^2.0.19" + update-browserslist-db "^1.1.1" + bs-logger@0.x: version "0.2.6" resolved "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz" @@ -2127,6 +2679,11 @@ buffer-from@^1.0.0: resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +cac@^6.7.14: + version "6.7.14" + resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" + integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== + call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz" @@ -2166,6 +2723,22 @@ caniuse-lite@^1.0.30001489: resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001491.tgz" integrity sha512-17EYIi4TLnPiTzVKMveIxU5ETlxbSO3B6iPvMbprqnKh4qJsQGk5Nh1Lp4jIMAE0XfrujsJuWZAM3oJdMHaKBA== +caniuse-lite@^1.0.30001688: + version "1.0.30001692" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz#4585729d95e6b95be5b439da6ab55250cd125bf9" + integrity sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A== + +chai@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/chai/-/chai-5.1.2.tgz#3afbc340b994ae3610ca519a6c70ace77ad4378d" + integrity sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw== + dependencies: + assertion-error "^2.0.1" + check-error "^2.1.1" + deep-eql "^5.0.1" + loupe "^3.1.0" + pathval "^2.0.0" + chalk@^2.4.2: version "2.4.2" resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" @@ -2175,9 +2748,9 @@ chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0: +chalk@^4.0.0, chalk@^4.1.2: version "4.1.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" @@ -2193,6 +2766,11 @@ char-regex@^1.0.2: resolved "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== +check-error@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.1.tgz#87eb876ae71ee388fa0471fe423f494be1d96ccc" + integrity sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw== + ci-info@^3.2.0: version "3.8.0" resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz" @@ -2263,6 +2841,13 @@ concat-map@0.0.1: resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +console-table-printer@^2.12.1: + version "2.12.1" + resolved "https://registry.yarnpkg.com/console-table-printer/-/console-table-printer-2.12.1.tgz#4a9646537a246a6d8de57075d4fae1e08abae267" + integrity sha512-wKGOQRRvdnd89pCeH96e2Fn4wkbenSP6LMHfjfyNLMbGuHEFbMqQNuxXqd0oXG9caIOQ1FTvc5Uijp9/4jujnQ== + dependencies: + simple-wcswidth "^1.0.1" + convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.9.0" resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" @@ -2315,6 +2900,13 @@ debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: dependencies: ms "2.1.2" +debug@^4.3.1, debug@^4.3.7: + version "4.4.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" + integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== + dependencies: + ms "^2.1.3" + decamelize@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -2325,6 +2917,11 @@ dedent@^0.7.0: resolved "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz" integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== +deep-eql@^5.0.1: + version "5.0.2" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341" + integrity sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q== + deep-is@^0.1.3: version "0.1.4" resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" @@ -2413,6 +3010,11 @@ electron-to-chromium@^1.4.411: resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.414.tgz" integrity sha512-RRuCvP6ekngVh2SAJaOKT/hxqc9JAsK+Pe0hP5tGQIfonU2Zy9gMGdJ+mBdyl/vNucMG6gkXYtuM4H/1giws5w== +electron-to-chromium@^1.5.73: + version "1.5.80" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.80.tgz#ca7a8361d7305f0ec9e203ce4e633cbb8a8ef1b1" + integrity sha512-LTrKpW0AqIuHwmlVNV+cjFYTnXtM9K37OGhpe0ZI10ScPSxqVSryZHIY3WnCS5NSYbBODRTZyhRMS2h5FAEqAw== + emittery@^0.13.1: version "0.13.1" resolved "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz" @@ -2487,6 +3089,11 @@ es-errors@^1.3.0: resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== +es-module-lexer@^1.5.4: + version "1.6.0" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.6.0.tgz#da49f587fd9e68ee2404fe4e256c0c7d3a81be21" + integrity sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ== + es-set-tostringtag@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz" @@ -2512,11 +3119,45 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +esbuild@^0.21.3: + version "0.21.5" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" + integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw== + optionalDependencies: + "@esbuild/aix-ppc64" "0.21.5" + "@esbuild/android-arm" "0.21.5" + "@esbuild/android-arm64" "0.21.5" + "@esbuild/android-x64" "0.21.5" + "@esbuild/darwin-arm64" "0.21.5" + "@esbuild/darwin-x64" "0.21.5" + "@esbuild/freebsd-arm64" "0.21.5" + "@esbuild/freebsd-x64" "0.21.5" + "@esbuild/linux-arm" "0.21.5" + "@esbuild/linux-arm64" "0.21.5" + "@esbuild/linux-ia32" "0.21.5" + "@esbuild/linux-loong64" "0.21.5" + "@esbuild/linux-mips64el" "0.21.5" + "@esbuild/linux-ppc64" "0.21.5" + "@esbuild/linux-riscv64" "0.21.5" + "@esbuild/linux-s390x" "0.21.5" + "@esbuild/linux-x64" "0.21.5" + "@esbuild/netbsd-x64" "0.21.5" + "@esbuild/openbsd-x64" "0.21.5" + "@esbuild/sunos-x64" "0.21.5" + "@esbuild/win32-arm64" "0.21.5" + "@esbuild/win32-ia32" "0.21.5" + "@esbuild/win32-x64" "0.21.5" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== +escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" @@ -2690,6 +3331,13 @@ estraverse@^5.1.0, estraverse@^5.2.0: resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== +estree-walker@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" + integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== + dependencies: + "@types/estree" "^1.0.0" + esutils@^2.0.2: version "2.0.3" resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" @@ -2730,6 +3378,11 @@ exit@^0.1.2: resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== +expect-type@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.1.0.tgz#a146e414250d13dfc49eafcfd1344a4060fa4c75" + integrity sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA== + expect@^29.0.0, expect@^29.5.0: version "29.5.0" resolved "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz" @@ -2868,6 +3521,11 @@ fsevents@^2.3.2: resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== +fsevents@~2.3.2, fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" @@ -3311,6 +3969,17 @@ istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: istanbul-lib-coverage "^3.2.0" semver "^6.3.0" +istanbul-lib-instrument@^6.0.0: + version "6.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz#fa15401df6c15874bcb2105f773325d78c666765" + integrity sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q== + dependencies: + "@babel/core" "^7.23.9" + "@babel/parser" "^7.23.9" + "@istanbuljs/schema" "^0.1.3" + istanbul-lib-coverage "^3.2.0" + semver "^7.5.4" + istanbul-lib-report@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz" @@ -3481,6 +4150,25 @@ jest-haste-map@^29.5.0: optionalDependencies: fsevents "^2.3.2" +jest-haste-map@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" + integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== + dependencies: + "@jest/types" "^29.6.3" + "@types/graceful-fs" "^4.1.3" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + jest-worker "^29.7.0" + micromatch "^4.0.4" + walker "^1.0.8" + optionalDependencies: + fsevents "^2.3.2" + jest-leak-detector@^29.5.0: version "29.5.0" resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz" @@ -3514,6 +4202,21 @@ jest-message-util@^29.5.0: slash "^3.0.0" stack-utils "^2.0.3" +jest-message-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" + integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.6.3" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.7.0" + slash "^3.0.0" + stack-utils "^2.0.3" + jest-mock@^29.5.0: version "29.5.0" resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-29.5.0.tgz" @@ -3533,6 +4236,11 @@ jest-regex-util@^29.4.3: resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz" integrity sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg== +jest-regex-util@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" + integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== + jest-resolve-dependencies@^29.5.0: version "29.5.0" resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz" @@ -3652,6 +4360,18 @@ jest-util@^29.0.0, jest-util@^29.5.0: graceful-fs "^4.2.9" picomatch "^2.2.3" +jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + jest-validate@^29.5.0: version "29.5.0" resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-29.5.0.tgz" @@ -3688,6 +4408,16 @@ jest-worker@^29.5.0: merge-stream "^2.0.0" supports-color "^8.0.0" +jest-worker@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" + integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== + dependencies: + "@types/node" "*" + jest-util "^29.7.0" + merge-stream "^2.0.0" + supports-color "^8.0.0" + jest@^29.5.0: version "29.5.0" resolved "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz" @@ -3730,6 +4460,11 @@ jsesc@^2.5.1: resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +jsesc@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" + integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== + jsesc@~0.5.0: version "0.5.0" resolved "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz" @@ -3870,6 +4605,11 @@ lodash.merge@^4.6.2: resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +loupe@^3.1.0, loupe@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.2.tgz#c86e0696804a02218f2206124c45d8b15291a240" + integrity sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg== + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" @@ -3889,6 +4629,13 @@ lunr@^2.3.9: resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== +magic-string@^0.30.12: + version "0.30.17" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453" + integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.0" + make-dir@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz" @@ -3984,7 +4731,7 @@ ms@2.1.2: resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.0.0, ms@^2.1.1: +ms@^2.0.0, ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -3994,7 +4741,7 @@ mustache@^4.2.0: resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64" integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ== -nanoid@^3.3.8: +nanoid@^3.3.7, nanoid@^3.3.8: version "3.3.8" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== @@ -4031,6 +4778,11 @@ node-releases@^2.0.12: resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz" integrity sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ== +node-releases@^2.0.19: + version "2.0.19" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" + integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== + normalize-path@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" @@ -4252,11 +5004,26 @@ path-type@^4.0.0: resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pathe@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" + integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== + +pathval@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25" + integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA== + picocolors@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" @@ -4274,6 +5041,15 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +postcss@^8.4.43: + version "8.4.49" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.49.tgz#4ea479048ab059ab3ae61d082190fabfd994fe19" + integrity sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA== + dependencies: + nanoid "^3.3.7" + picocolors "^1.1.1" + source-map-js "^1.2.1" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" @@ -4300,6 +5076,15 @@ pretty-format@^29.0.0, pretty-format@^29.5.0: ansi-styles "^5.0.0" react-is "^18.0.0" +pretty-format@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== + dependencies: + "@jest/schemas" "^29.6.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + prompts@^2.0.1: version "2.4.2" resolved "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz" @@ -4445,6 +5230,34 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" +rollup@^4.20.0: + version "4.30.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.30.1.tgz#d5c3d066055259366cdc3eb6f1d051c5d6afaf74" + integrity sha512-mlJ4glW020fPuLi7DkM/lN97mYEZGWeqBnrljzN0gs7GLctqX3lNWxKQ7Gl712UAX+6fog/L3jh4gb7R6aVi3w== + dependencies: + "@types/estree" "1.0.6" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.30.1" + "@rollup/rollup-android-arm64" "4.30.1" + "@rollup/rollup-darwin-arm64" "4.30.1" + "@rollup/rollup-darwin-x64" "4.30.1" + "@rollup/rollup-freebsd-arm64" "4.30.1" + "@rollup/rollup-freebsd-x64" "4.30.1" + "@rollup/rollup-linux-arm-gnueabihf" "4.30.1" + "@rollup/rollup-linux-arm-musleabihf" "4.30.1" + "@rollup/rollup-linux-arm64-gnu" "4.30.1" + "@rollup/rollup-linux-arm64-musl" "4.30.1" + "@rollup/rollup-linux-loongarch64-gnu" "4.30.1" + "@rollup/rollup-linux-powerpc64le-gnu" "4.30.1" + "@rollup/rollup-linux-riscv64-gnu" "4.30.1" + "@rollup/rollup-linux-s390x-gnu" "4.30.1" + "@rollup/rollup-linux-x64-gnu" "4.30.1" + "@rollup/rollup-linux-x64-musl" "4.30.1" + "@rollup/rollup-win32-arm64-msvc" "4.30.1" + "@rollup/rollup-win32-ia32-msvc" "4.30.1" + "@rollup/rollup-win32-x64-msvc" "4.30.1" + fsevents "~2.3.2" + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" @@ -4473,12 +5286,12 @@ semver@7.x, semver@^7.3.5, semver@^7.3.7: dependencies: lru-cache "^6.0.0" -semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.5.2, semver@^7.6.3: +semver@^7.5.2, semver@^7.5.4, semver@^7.6.3: version "7.6.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== @@ -4526,11 +5339,21 @@ side-channel@^1.0.6: get-intrinsic "^1.2.4" object-inspect "^1.13.1" +siginfo@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" + integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== + signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +simple-wcswidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-wcswidth/-/simple-wcswidth-1.0.1.tgz#8ab18ac0ae342f9d9b629604e54d2aa1ecb018b2" + integrity sha512-xMO/8eNREtaROt7tJvWJqHBDTMFN4eiQ5I4JRMuilwfnFcV5W9u7RUkueNkdw0jPqGMX36iCywelS5yilTuOxg== + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz" @@ -4541,6 +5364,11 @@ slash@^3.0.0: resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + source-map-support@0.5.13: version "0.5.13" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz" @@ -4566,6 +5394,16 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" +stackback@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" + integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== + +std-env@^3.8.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.8.0.tgz#b56ffc1baf1a29dcc80a3bdf11d7fca7c315e7d5" + integrity sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w== + string-length@^4.0.1: version "4.0.2" resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz" @@ -4690,6 +5528,31 @@ throttleit@2.1.0: resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-2.1.0.tgz#a7e4aa0bf4845a5bd10daa39ea0c783f631a07b4" integrity sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw== +tinybench@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b" + integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg== + +tinyexec@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.2.tgz#941794e657a85e496577995c6eef66f53f42b3d2" + integrity sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA== + +tinypool@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.0.2.tgz#706193cc532f4c100f66aa00b01c42173d9051b2" + integrity sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA== + +tinyrainbow@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-1.2.0.tgz#5c57d2fc0fb3d1afd78465c33ca885d04f02abb5" + integrity sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ== + +tinyspy@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-3.0.2.tgz#86dd3cf3d737b15adcf17d7887c84a75201df20a" + integrity sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q== + tmpl@1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz" @@ -4870,6 +5733,14 @@ update-browserslist-db@^1.0.11: escalade "^3.1.1" picocolors "^1.0.0" +update-browserslist-db@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz#97e9c96ab0ae7bcac08e9ae5151d26e6bc6b5580" + integrity sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.1" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" @@ -4906,6 +5777,54 @@ v8-to-istanbul@^9.0.1: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" +vite-node@2.1.8: + version "2.1.8" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-2.1.8.tgz#9495ca17652f6f7f95ca7c4b568a235e0c8dbac5" + integrity sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg== + dependencies: + cac "^6.7.14" + debug "^4.3.7" + es-module-lexer "^1.5.4" + pathe "^1.1.2" + vite "^5.0.0" + +vite@^5.0.0: + version "5.4.11" + resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.11.tgz#3b415cd4aed781a356c1de5a9ebafb837715f6e5" + integrity sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q== + dependencies: + esbuild "^0.21.3" + postcss "^8.4.43" + rollup "^4.20.0" + optionalDependencies: + fsevents "~2.3.3" + +vitest@^2.1.8: + version "2.1.8" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-2.1.8.tgz#2e6a00bc24833574d535c96d6602fb64163092fa" + integrity sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ== + dependencies: + "@vitest/expect" "2.1.8" + "@vitest/mocker" "2.1.8" + "@vitest/pretty-format" "^2.1.8" + "@vitest/runner" "2.1.8" + "@vitest/snapshot" "2.1.8" + "@vitest/spy" "2.1.8" + "@vitest/utils" "2.1.8" + chai "^5.1.2" + debug "^4.3.7" + expect-type "^1.1.0" + magic-string "^0.30.12" + pathe "^1.1.2" + std-env "^3.8.0" + tinybench "^2.9.0" + tinyexec "^0.3.1" + tinypool "^1.0.1" + tinyrainbow "^1.2.0" + vite "^5.0.0" + vite-node "2.1.8" + why-is-node-running "^2.3.0" + walker@^1.0.8: version "1.0.8" resolved "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz" @@ -4961,6 +5880,14 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +why-is-node-running@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04" + integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w== + dependencies: + siginfo "^2.0.0" + stackback "0.0.2" + word-wrap@^1.2.3: version "1.2.5" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"